fix: Prevent agents page from scrolling — lock layout to viewport
Body: height 100vh + overflow hidden instead of min-height 100vh, so the browser never shows a scrollbar on html/body. AppLayout: h-screen flex column with shrink-0 header and flex-1 min-h-0 overflow-auto main. Pages like initiatives scroll within main; agents page uses h-full with internal panel scrollers.
This commit is contained in:
@@ -143,15 +143,27 @@ Agents can communicate with each other via the `conversations` table, coordinate
|
|||||||
|
|
||||||
### Prompt Integration
|
### Prompt Integration
|
||||||
`INTER_AGENT_COMMUNICATION` constant in `prompts/shared.ts` is appended to all 5 agent mode prompts. It instructs agents to:
|
`INTER_AGENT_COMMUNICATION` constant in `prompts/shared.ts` is appended to all 5 agent mode prompts. It instructs agents to:
|
||||||
1. Start `cw listen --agent-id <ID> &` as a background process at session start
|
1. Set up a background listener via temp-file redirect: `cw listen > $CW_LISTEN_FILE &`
|
||||||
2. Handle incoming questions by answering via `cw answer` and restarting the listener
|
2. Periodically check the temp file for incoming questions between work steps
|
||||||
3. Ask questions to peers via `cw ask --from <ID> --agent-id|--phase-id|--task-id`
|
3. Answer via `cw answer`, clear the file, restart the listener
|
||||||
4. Kill the listener before writing `signal.json`
|
4. Ask questions to peers via `cw ask --from <ID> --agent-id|--task-id|--phase-id`
|
||||||
|
5. Kill the listener and clean up the temp file before writing `signal.json`
|
||||||
|
|
||||||
### Agent Identity
|
### Agent Identity
|
||||||
`manifest.json` now includes `agentId` and `agentName` fields. The manager passes these from the DB record after agent creation.
|
`manifest.json` now includes `agentId` and `agentName` fields. The manager passes these from the DB record after agent creation.
|
||||||
|
|
||||||
### CLI Commands
|
### CLI Commands
|
||||||
- `cw listen`: Polls `getPendingConversations`, prints first pending as JSON, exits
|
|
||||||
- `cw ask`: Creates conversation, polls `getConversation` until answered, prints answer
|
**`cw listen --agent-id <id> [--timeout <ms>] [--poll-interval <ms>]`**
|
||||||
- `cw answer`: Calls `answerConversation`, prints confirmation JSON
|
- Polls `getPendingConversations`, prints first pending as JSON, exits with code 0
|
||||||
|
- `--timeout`: max wait in ms (default 0=forever)
|
||||||
|
- `--poll-interval`: polling frequency in ms (default 2000)
|
||||||
|
- Output: `{ conversationId, fromAgentId, question, phaseId?, taskId? }`
|
||||||
|
|
||||||
|
**`cw ask <question> --from <agentId> --agent-id|--task-id|--phase-id <target> [--timeout <ms>] [--poll-interval <ms>]`**
|
||||||
|
- Creates conversation, polls `getConversation` until answered, prints answer text to stdout
|
||||||
|
- Target resolution: `--agent-id` (direct), `--task-id` (find agent running task), `--phase-id` (find agent in phase)
|
||||||
|
- `--timeout` / `--poll-interval`: same defaults as listen
|
||||||
|
|
||||||
|
**`cw answer <answer> --conversation-id <id>`**
|
||||||
|
- Calls `answerConversation`, prints `{ conversationId, status: "answered" }`
|
||||||
|
|||||||
@@ -57,7 +57,8 @@
|
|||||||
@apply bg-background text-foreground;
|
@apply bg-background text-foreground;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
min-width: 320px;
|
min-width: 320px;
|
||||||
min-height: 100vh;
|
height: 100vh;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ const navItems = [
|
|||||||
|
|
||||||
export function AppLayout({ children }: { children: React.ReactNode }) {
|
export function AppLayout({ children }: { children: React.ReactNode }) {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="flex h-screen flex-col bg-background">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<header className="sticky top-0 z-50 border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
<header className="shrink-0 border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
||||||
<div className="mx-auto flex h-14 max-w-7xl items-center justify-between px-6">
|
<div className="mx-auto flex h-14 max-w-7xl items-center justify-between px-6">
|
||||||
<Link to="/initiatives" className="text-lg font-bold tracking-tight">
|
<Link to="/initiatives" className="text-lg font-bold tracking-tight">
|
||||||
Codewalk District
|
Codewalk District
|
||||||
@@ -35,7 +35,7 @@ export function AppLayout({ children }: { children: React.ReactNode }) {
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
{/* Page content */}
|
{/* Page content */}
|
||||||
<main className="mx-auto max-w-7xl px-6 py-6">
|
<main className="mx-auto flex-1 min-h-0 w-full max-w-7xl overflow-auto px-6 py-6">
|
||||||
{children}
|
{children}
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -89,10 +89,7 @@ function AgentsPage() {
|
|||||||
// Loading state
|
// Loading state
|
||||||
if (agentsQuery.isLoading) {
|
if (agentsQuery.isLoading) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div className="flex h-full flex-col gap-4">
|
||||||
style={{ height: "calc(100vh - 7rem)" }}
|
|
||||||
className="flex flex-col gap-4"
|
|
||||||
>
|
|
||||||
<div className="flex items-center justify-between shrink-0">
|
<div className="flex items-center justify-between shrink-0">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Skeleton className="h-6 w-20" />
|
<Skeleton className="h-6 w-20" />
|
||||||
@@ -179,10 +176,7 @@ function AgentsPage() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className="flex h-full flex-col gap-4">
|
||||||
style={{ height: "calc(100vh - 7rem)" }}
|
|
||||||
className="flex flex-col gap-4"
|
|
||||||
>
|
|
||||||
{/* Header + Filters */}
|
{/* Header + Filters */}
|
||||||
<div className="shrink-0 space-y-3">
|
<div className="shrink-0 space-y-3">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
|
|||||||
@@ -47,53 +47,80 @@ Use the output as the filename (e.g., \`{id}.md\`).`;
|
|||||||
export const INTER_AGENT_COMMUNICATION = `
|
export const INTER_AGENT_COMMUNICATION = `
|
||||||
## Inter-Agent Communication
|
## Inter-Agent Communication
|
||||||
|
|
||||||
You are working in a multi-agent parallel environment. Other agents may be working on related tasks simultaneously.
|
You are working in a multi-agent parallel environment. Other agents may be working on related tasks simultaneously. You can exchange questions and answers with peer agents via CLI commands.
|
||||||
|
|
||||||
### Your Identity
|
### Your Identity
|
||||||
Read \`.cw/input/manifest.json\` — it contains \`agentId\` and \`agentName\` fields identifying you.
|
Read \`.cw/input/manifest.json\` — it contains \`agentId\` and \`agentName\` fields identifying you. You'll need your \`agentId\` for all communication commands.
|
||||||
|
|
||||||
### Listening for Questions
|
### CLI Commands
|
||||||
At the START of your session, start a background listener:
|
|
||||||
|
**\`cw listen\`** — Poll for incoming questions. Prints the first pending question as JSON and exits.
|
||||||
|
\`\`\`
|
||||||
|
cw listen --agent-id <YOUR_AGENT_ID> [--timeout <ms>] [--poll-interval <ms>]
|
||||||
|
\`\`\`
|
||||||
|
- \`--agent-id\` (required): Your agent ID
|
||||||
|
- \`--timeout\`: Max wait in ms. Default: 0 (wait forever). Use a value like 120000 (2 min) to avoid hanging.
|
||||||
|
- \`--poll-interval\`: Polling frequency in ms. Default: 2000
|
||||||
|
- Output (JSON): \`{ "conversationId": "...", "fromAgentId": "...", "question": "...", "phaseId": "...", "taskId": "..." }\`
|
||||||
|
- Exit code 0 if a question was found, 1 on timeout or error.
|
||||||
|
|
||||||
|
**\`cw ask\`** — Ask another agent a question. Blocks until the answer arrives, then prints the answer text to stdout.
|
||||||
|
\`\`\`
|
||||||
|
cw ask "<question>" --from <YOUR_AGENT_ID> --agent-id <TARGET_AGENT_ID> [--timeout <ms>] [--poll-interval <ms>]
|
||||||
|
\`\`\`
|
||||||
|
- \`--from\` (required): Your agent ID (the asker)
|
||||||
|
- Target (exactly one required):
|
||||||
|
- \`--agent-id <id>\`: Ask a specific agent directly
|
||||||
|
- \`--task-id <id>\`: Ask whichever agent is running that task
|
||||||
|
- \`--phase-id <id>\`: Ask whichever agent is running a task in that phase
|
||||||
|
- \`--timeout\`: Max wait in ms. Default: 0 (wait forever). Use 120000+ for safety.
|
||||||
|
- \`--poll-interval\`: Polling frequency in ms. Default: 2000
|
||||||
|
- Output: The answer text (plain text, not JSON).
|
||||||
|
- Exit code 0 if answered, 1 on timeout or error.
|
||||||
|
|
||||||
|
**\`cw answer\`** — Answer a pending question.
|
||||||
|
\`\`\`
|
||||||
|
cw answer "<answer>" --conversation-id <CONVERSATION_ID>
|
||||||
|
\`\`\`
|
||||||
|
- \`--conversation-id\` (required): The conversation ID from the listen output
|
||||||
|
- Output (JSON): \`{ "conversationId": "...", "status": "answered" }\`
|
||||||
|
|
||||||
|
### Background Listener Pattern
|
||||||
|
|
||||||
|
At the START of your session, set up a background listener that writes to a temp file:
|
||||||
\`\`\`bash
|
\`\`\`bash
|
||||||
cw listen --agent-id <YOUR_AGENT_ID> &
|
CW_LISTEN_FILE=$(mktemp /tmp/cw-listen-XXXXXX.txt)
|
||||||
|
cw listen --agent-id <YOUR_AGENT_ID> --timeout 120000 > "$CW_LISTEN_FILE" 2>&1 &
|
||||||
LISTEN_PID=$!
|
LISTEN_PID=$!
|
||||||
\`\`\`
|
\`\`\`
|
||||||
|
|
||||||
When the listener prints JSON to stdout, another agent is asking you a question:
|
Periodically check for incoming questions between your work steps:
|
||||||
\`\`\`json
|
\`\`\`bash
|
||||||
{ "conversationId": "...", "fromAgentId": "...", "question": "..." }
|
LISTEN_CONTENT=$(cat "$CW_LISTEN_FILE" 2>/dev/null)
|
||||||
|
if [ -n "$LISTEN_CONTENT" ]; then
|
||||||
|
echo "$LISTEN_CONTENT"
|
||||||
|
fi
|
||||||
\`\`\`
|
\`\`\`
|
||||||
|
|
||||||
Answer it:
|
When a question arrives, parse the JSON, answer, then restart the listener:
|
||||||
\`\`\`bash
|
\`\`\`bash
|
||||||
|
# Parse conversationId and question from the JSON
|
||||||
cw answer "<your answer>" --conversation-id <conversationId>
|
cw answer "<your answer>" --conversation-id <conversationId>
|
||||||
\`\`\`
|
# Clear and restart
|
||||||
|
> "$CW_LISTEN_FILE"
|
||||||
Then restart the listener:
|
cw listen --agent-id <YOUR_AGENT_ID> --timeout 120000 > "$CW_LISTEN_FILE" 2>&1 &
|
||||||
\`\`\`bash
|
|
||||||
cw listen --agent-id <YOUR_AGENT_ID> &
|
|
||||||
LISTEN_PID=$!
|
LISTEN_PID=$!
|
||||||
\`\`\`
|
\`\`\`
|
||||||
|
|
||||||
### Asking Questions
|
|
||||||
To ask another agent a question (blocks until answered):
|
|
||||||
\`\`\`bash
|
|
||||||
cw ask "What interface does the user service expose?" --from <YOUR_AGENT_ID> --agent-id <TARGET_AGENT_ID>
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
You can also target by task or phase:
|
|
||||||
\`\`\`bash
|
|
||||||
cw ask "What port does the API run on?" --from <YOUR_AGENT_ID> --task-id <TASK_ID>
|
|
||||||
cw ask "What schema are you using?" --from <YOUR_AGENT_ID> --phase-id <PHASE_ID>
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
### When to Communicate
|
### When to Communicate
|
||||||
- You need interface/schema/contract info from another agent's work
|
- You need interface, schema, or API contract info from another agent's work
|
||||||
- You're about to modify a shared resource and want to coordinate
|
- You're about to modify a shared resource and want to coordinate
|
||||||
- You have a dependency on work another agent is doing
|
- You have a dependency on work another agent is doing
|
||||||
|
- Do NOT ask questions that you can answer by reading the codebase yourself
|
||||||
|
|
||||||
### Cleanup
|
### Cleanup
|
||||||
Before writing \`.cw/output/signal.json\`, kill your listener:
|
Before writing \`.cw/output/signal.json\`, kill your listener:
|
||||||
\`\`\`bash
|
\`\`\`bash
|
||||||
kill $LISTEN_PID 2>/dev/null
|
kill $LISTEN_PID 2>/dev/null
|
||||||
|
rm -f "$CW_LISTEN_FILE"
|
||||||
\`\`\``;
|
\`\`\``;
|
||||||
|
|||||||
@@ -73,6 +73,8 @@ function createMockEventBus(): EventBus & { emitted: DomainEvent[] } {
|
|||||||
emitted.push(event);
|
emitted.push(event);
|
||||||
}),
|
}),
|
||||||
on: vi.fn(),
|
on: vi.fn(),
|
||||||
|
off: vi.fn(),
|
||||||
|
once: vi.fn(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user