-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Add user orchestrator for agent messaging and observation #207
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
86 commits
Select commit
Hold shift + click to select a range
68a4507
Add user orchestrator agent messaging and observe
sethkarten 08aa1f1
Add orchestration heartbeat skill
sethkarten 44d137a
Address user orchestrator PR review comments
sethkarten da7d873
Tighten agent message rate limiting
sethkarten 9cff30b
Harden agent message queue safety
sethkarten bb78549
Preserve agent message reservations on queue clear
sethkarten 1706020
Serialize agent messages per target
sethkarten 16b1c76
Handle queued agent message edge cases
sethkarten f4d471c
Preserve orchestration heartbeat schedule on refresh
sethkarten 1ba6cd7
Merge main into user orchestrator
sethkarten c5aa8e6
Merge remote-tracking branch 'origin/main' into feature/user-orchestr…
sethkarten eae5588
Fix agent message queue handling
sethkarten d29f863
Fix agent message review issues
sethkarten 180e727
Defer agent message acknowledgment past stream start
sethkarten b14752f
Fix agent message acceptance race
sethkarten 96ec881
Avoid blocking agent message send on pre-prompt work
sethkarten d942a14
Avoid blocking agent messages on pre-prompt hooks
sethkarten 6e7f71d
Hide daemon prompt fast path from public options
sethkarten 45dec5f
fix(coding-agent): harden agent message delivery
sethkarten 102bd3f
fix(coding-agent): keep failed agent messages from consuming limits
sethkarten 5839208
fix(coding-agent): address agent orchestrator review issues
sethkarten f126ff3
fix(coding-agent): address agent orchestrator review issues
sethkarten 4519211
merge remote feature/user-orchestrator
sethkarten e30af89
merge remote feature/user-orchestrator
sethkarten fd55d2b
fix(coding-agent): queue sends during accepted retries
sethkarten d7fe172
merge remote feature/user-orchestrator
sethkarten e6f7040
fix(coding-agent): address remaining orchestrator bot findings
sethkarten 5735cc4
merge main into user orchestrator branch
sethkarten 4189986
test(coding-agent): update streaming prompt guard expectation
sethkarten dd0442f
test(coding-agent): remove duplicate daemon queue case
sethkarten c493acd
fix(coding-agent): harden prompt acceptance races
sethkarten fb077c0
fix(coding-agent): serialize agent message clearing
sethkarten 03dd462
fix(coding-agent): lock agent message safety controls
sethkarten a54a99e
fix(coding-agent): harden agent message pause
sethkarten b53f038
fix(coding-agent): queue agent messages internally
sethkarten 56e280a
fix(coding-agent): release prompt lock when streaming starts
sethkarten b7d81f7
fix(coding-agent): reject messages paused while locked
sethkarten e11ca91
fix(coding-agent): reject messages after session switch
sethkarten 0006acc
fix(coding-agent): serialize session close with messages
sethkarten 78f1af8
fix(coding-agent): reject messages for closing sessions
sethkarten 502bca3
Merge remote-tracking branch 'origin/main' into feature/user-orchestr…
sethkarten ea5d0bd
fix(coding-agent): queue messages during compaction
sethkarten 7f9e886
fix(coding-agent): avoid blocking messages during prompt prep
sethkarten 951da7d
fix(coding-agent): track queued agent messages
sethkarten c7e6d6d
fix(coding-agent): preserve agent message queue identity
sethkarten 7375570
fix(coding-agent): parse canonical agent message ids
sethkarten e1cb216
fix(coding-agent): clear accepted agent messages
sethkarten 3df464c
fix(coding-agent): sanitize agent message metadata
sethkarten b89b4fa
fix(coding-agent): suppress cleared accepted messages
sethkarten c8b583b
fix(coding-agent): clear accepted prompt state on failure
sethkarten e5ca7fe
fix(coding-agent): account for accepted message delivery
sethkarten 807fa06
fix(coding-agent): close accepted prompt busy gap
sethkarten 43a6648
fix(coding-agent): harden accepted message cleanup
sethkarten 5a409c2
fix(coding-agent): report accepted messages as pending
sethkarten d10b2cb
fix(coding-agent): wait for accepted message delivery
sethkarten 27638aa
fix(coding-agent): report duplicate follow-up preflight
sethkarten 63746d5
fix(coding-agent): reject cleared accepted messages
sethkarten 4e8e4ee
fix(coding-agent): reject failed agent message delivery
sethkarten 0cb8af8
fix(coding-agent): emit cleared accepted agent end
sethkarten 8a3140f
fix(coding-agent): unblock idle prompts after agent delivery
sethkarten 4c8d2e3
fix(coding-agent): wait for agent message delivery
sethkarten 48468ac
fix(coding-agent): avoid direct delivery waiter hang
sethkarten f65a8c6
fix(coding-agent): settle late agent message waits
sethkarten 9136eb3
fix(coding-agent): reject delivery waits on dispose
sethkarten 94e8b6f
fix(coding-agent): acknowledge queued agent messages
sethkarten 5c456af
Merge remote-tracking branch 'origin/main' into feature/user-orchestr…
sethkarten 33759eb
fix(coding-agent): review fixes for agent messaging admission, capaci…
kevinjosethomas aa83217
fix(coding-agent): drain agent messages after bash
sethkarten cb362b5
fix(coding-agent): preserve queued message order after bash
sethkarten 6023e56
fix(coding-agent): resolve queued agent-message sends without blockin…
kevinjosethomas f7928ec
Merge remote-tracking branch 'origin/feature/user-orchestrator' into …
kevinjosethomas 3381a91
fix(coding-agent): report busy observed sessions
sethkarten 7d0cdc6
fix(coding-agent): settle agent-message lifecycle edge cases
sethkarten c28c53d
fix(coding-agent): keep deferred agent messages ordered
sethkarten 5d8535c
fix(coding-agent): harden queued message cleanup
sethkarten d53e443
fix(coding-agent): report queued direct accepts
sethkarten cb192f8
fix(coding-agent): harden agent message delivery state
sethkarten 12f3a0d
fix(coding-agent): restore next-turn context after failed accept
sethkarten d53aef0
fix(coding-agent): defer cron while agent accepts messages
sethkarten 0575e9e
fix(coding-agent): queue cron during agent-message accept
sethkarten 52719ef
fix(coding-agent): normalize orchestration heartbeat intervals
sethkarten 009ef1c
Merge remote-tracking branch 'origin/main' into feature/user-orchestr…
sethkarten 7ca681b
fix(coding-agent): recheck busy state before direct accept
sethkarten 7bed563
fix(coding-agent): preserve next-turn context in queued accepts
sethkarten c6c4ad9
fix(coding-agent): settle queued accept edge cases
sethkarten 9454cba
fix(coding-agent): serialize daemon prompts with agent messages
sethkarten File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| --- | ||
| name: agent-message | ||
| description: Message other active Prime Agent sessions through the daemon. Use to discover active agents and send a direct text message without spoofing sender identity. | ||
| --- | ||
|
|
||
| # Agent Message | ||
|
|
||
| Send direct messages to other active Prime Agent sessions through the local | ||
| daemon. The daemon derives your sender identity from the current session; do | ||
| not try to include a `from` field. | ||
|
|
||
| Call directly from the kernel: | ||
|
|
||
| ```python | ||
| agents = await agent_message.list_agents() | ||
| receipt = await agent_message.send("worker", "Please inspect the latest result.", mode="auto") | ||
| ``` | ||
|
|
||
| ## API | ||
|
|
||
| - `await agent_message.list_agents()` — returns `current` and `agents`, where | ||
| each agent includes active session id, session id, optional name, runtime | ||
| kind, cwd, streaming state, and pending message count. | ||
| - `await agent_message.send(target, message, mode="auto")` — sends one direct | ||
| text message to an active session. `target` is resolved by the daemon like | ||
| other live-session selectors. `mode` is `"auto"`, `"follow_up"`, or | ||
| `"steer"`. Returns a receipt with a `deliveryStatus` field: `"delivered"` | ||
| means the message reached an idle target's context; `"queued"` means it was | ||
| accepted and will deliver when the target's current work allows (`send` | ||
| does not block waiting for that). Delivered receipts carry `deliveredAt`, | ||
| queued receipts carry `queuedAt`. | ||
|
|
||
| ## Safety | ||
|
|
||
| - Broadcast sends are not supported. | ||
| - Sender identity is daemon-derived and cannot be spoofed from Python. | ||
| - The daemon enforces message size, rate, and pending-queue limits before | ||
| accepting delivery. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| [project] | ||
| name = "agent-message" | ||
| version = "0.1.0" | ||
| description = "Prime Agent session-to-session messaging skill" | ||
| requires-python = ">=3.10" | ||
| dependencies = [] | ||
|
|
||
| [build-system] | ||
| requires = ["hatchling"] | ||
| build-backend = "hatchling.build" | ||
|
|
||
| [tool.hatch.build.targets.wheel] | ||
| packages = ["src/agent_message"] |
48 changes: 48 additions & 0 deletions
48
packages/coding-agent/skills/agent-message/src/agent_message/__init__.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| """Prime Agent session-to-session messaging skill. | ||
|
|
||
| All routing and sender identity live in the TypeScript daemon. These functions | ||
| only call the host bridge exposed inside the Prime Agent IPython kernel. | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| from typing import Any, Literal | ||
|
|
||
| from rlm import host_request | ||
|
|
||
| MessageMode = Literal["auto", "follow_up", "steer"] | ||
|
|
||
|
|
||
| async def list_agents() -> dict[str, Any]: | ||
| """List active daemon sessions addressable by agent_message.send().""" | ||
| return await host_request("agent_message.list") | ||
|
|
||
|
|
||
| async def send(target: str, message: str, mode: MessageMode = "auto") -> dict[str, Any]: | ||
| """Send one direct text message to another active Prime Agent session. | ||
|
|
||
| Args: | ||
| target: Active session id, session id/name, or unambiguous suffix. | ||
| message: Text payload to deliver. | ||
| mode: "auto" queues as follow-up only if the target is streaming; | ||
| "follow_up" always uses follow-up when the target is streaming; | ||
| "steer" interrupts a streaming target. | ||
|
|
||
| Returns a receipt dict whose "deliveryStatus" is "delivered" (reached an | ||
| idle target's context) or "queued" (accepted; delivers when the target's | ||
| current work allows -- this call does not block waiting for that). | ||
| """ | ||
| if not isinstance(target, str): | ||
| raise TypeError(f"target must be str, got {type(target).__name__}") | ||
| if not isinstance(message, str): | ||
| raise TypeError(f"message must be str, got {type(message).__name__}") | ||
| if mode not in ("auto", "follow_up", "steer"): | ||
| raise ValueError('mode must be "auto", "follow_up", or "steer"') | ||
| return await host_request( | ||
| "agent_message.send", | ||
| { | ||
| "target": target, | ||
| "message": message, | ||
| "mode": mode, | ||
| }, | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| --- | ||
| name: agent-observe | ||
| description: Read-only observation of active Prime Agent sessions through the local daemon. Use to list agents, inspect session status, and read bounded recent-message previews without mutating other sessions. | ||
| --- | ||
|
|
||
| # Agent Observe | ||
|
|
||
| Observe active Prime Agent sessions through the local daemon. This skill is | ||
| read-only: it can list sessions, inspect one session, and fetch bounded recent | ||
| message previews. It cannot prompt, steer, clear, kill, rename, or otherwise | ||
| mutate another session. | ||
|
|
||
| Call directly from the kernel: | ||
|
|
||
| ```python | ||
| agents = await agent_observe.list_agents() | ||
| worker = await agent_observe.get_agent("worker") | ||
| recent = await agent_observe.recent_messages("worker", limit=6) | ||
| ``` | ||
|
|
||
| ## API | ||
|
|
||
| - `await agent_observe.list_agents()` returns `current` and `agents`. Each | ||
| agent includes active session id, session id, optional name, runtime kind, | ||
| cwd, status, streaming state, message count, pending count, and a latest | ||
| message preview. | ||
| - `await agent_observe.get_agent(target)` returns one agent summary. `target` | ||
| is resolved like other live-session selectors: active id, session id/name, or | ||
| unambiguous suffix. | ||
| - `await agent_observe.recent_messages(target, limit=8, max_chars=800)` | ||
| returns up to `limit` recent bounded message previews for the target session. | ||
| `limit` must be 1-50, and `max_chars` must be 80-2000. | ||
|
|
||
| ## Safety | ||
|
|
||
| - This skill is read-only and exposes no mutation commands. | ||
| - Message access is bounded by count and per-message character limit. | ||
| - Prefer status and recent previews for orchestration. Ask the user before | ||
| using observed context to steer or message another session. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| [project] | ||
| name = "agent-observe" | ||
| version = "0.1.0" | ||
| description = "Read-only Prime Agent session observation skill" | ||
| requires-python = ">=3.10" | ||
|
|
||
| [build-system] | ||
| requires = ["hatchling"] | ||
| build-backend = "hatchling.build" | ||
|
|
||
| [tool.hatch.build.targets.wheel] | ||
| packages = ["src/agent_observe"] | ||
|
|
||
| [tool.prime_agent.skill] | ||
| import = "agent_observe" | ||
52 changes: 52 additions & 0 deletions
52
packages/coding-agent/skills/agent-observe/src/agent_observe/__init__.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| """Read-only Prime Agent session observation skill. | ||
|
|
||
| All session lookup and data access live in the TypeScript daemon. These | ||
| functions only call the host bridge exposed inside the Prime Agent IPython | ||
| kernel. | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| from typing import Any | ||
|
|
||
| from rlm import host_request | ||
|
|
||
|
|
||
| async def list_agents() -> dict[str, Any]: | ||
| """List active daemon sessions visible to this agent.""" | ||
| return await host_request("agent_observe.list") | ||
|
|
||
|
|
||
| async def get_agent(target: str) -> dict[str, Any]: | ||
| """Read one active session summary by active id, session id/name, or suffix.""" | ||
| if not isinstance(target, str): | ||
| raise TypeError(f"target must be str, got {type(target).__name__}") | ||
| return await host_request("agent_observe.get", {"target": target}) | ||
|
|
||
|
|
||
| async def recent_messages( | ||
| target: str, | ||
| limit: int = 8, | ||
| max_chars: int = 800, | ||
| ) -> dict[str, Any]: | ||
| """Read bounded recent message previews from an active session. | ||
|
|
||
| Args: | ||
| target: Active session id, session id/name, or unambiguous suffix. | ||
| limit: Number of recent messages to return. Host validates 1-50. | ||
| max_chars: Per-message preview size. Host validates 80-2000. | ||
| """ | ||
| if not isinstance(target, str): | ||
| raise TypeError(f"target must be str, got {type(target).__name__}") | ||
| if not isinstance(limit, int): | ||
| raise TypeError(f"limit must be int, got {type(limit).__name__}") | ||
| if not isinstance(max_chars, int): | ||
| raise TypeError(f"max_chars must be int, got {type(max_chars).__name__}") | ||
| return await host_request( | ||
| "agent_observe.recent", | ||
| { | ||
| "target": target, | ||
| "limit": limit, | ||
| "max_chars": max_chars, | ||
| }, | ||
| ) |
41 changes: 41 additions & 0 deletions
41
packages/coding-agent/skills/orchestration-heartbeat/SKILL.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| --- | ||
| name: orchestration-heartbeat | ||
| description: Initialize or refresh an internal orchestrator heartbeat that observes active Prime Agent sessions, summarizes progress and blockers, and recommends next actions or messages. | ||
| --- | ||
|
|
||
| # Orchestration Heartbeat | ||
|
|
||
| Use this skill from an orchestrator session to create or refresh a recurring | ||
| internal heartbeat for supervising multiple active Prime Agent sessions. | ||
|
|
||
| The heartbeat is an RLM heartbeat, not the user's visible `/heartbeat`. It | ||
| cannot read, replace, pause, resume, or clear the user-level heartbeat. | ||
|
|
||
| Call directly from IPython: | ||
|
|
||
| ```python | ||
| await orchestration_heartbeat.initialize() | ||
| await orchestration_heartbeat.initialize(interval="10m", focus="EmulatorBench and AutoEnv") | ||
| ``` | ||
|
|
||
| ## API | ||
|
|
||
| - `await orchestration_heartbeat.initialize(interval="5m", label="orchestrator", | ||
| focus=None, require_user_approval=True)` creates or updates the labeled | ||
| orchestrator heartbeat for the current session. | ||
| - `await orchestration_heartbeat.ensure(...)` is an alias for `initialize`. | ||
| - `orchestration_heartbeat.build_instruction(...)` returns the prompt text | ||
| without creating or updating a heartbeat. | ||
|
|
||
| ## Heartbeat Behavior | ||
|
|
||
| Each recurring orchestration heartbeat should: | ||
|
|
||
| - Inspect active sessions with `agent_observe`. | ||
| - Summarize each relevant session as `active`, `waiting`, `blocked`, `error`, | ||
| or `completed`. | ||
| - Include current progress and explicit blockers. | ||
| - Recommend the next action and, when useful, draft a target message. | ||
| - Ask for user approval before sending cross-session messages unless the user | ||
| has already approved that specific messaging policy. | ||
| - Keep the update compact and operational instead of dumping logs. |
13 changes: 13 additions & 0 deletions
13
packages/coding-agent/skills/orchestration-heartbeat/pyproject.toml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| [project] | ||
| name = "orchestration-heartbeat" | ||
| version = "0.1.0" | ||
| description = "Prime Agent orchestration heartbeat skill" | ||
| requires-python = ">=3.10" | ||
| dependencies = ["agent-observe", "rlm-heartbeat"] | ||
|
|
||
| [build-system] | ||
| requires = ["hatchling"] | ||
| build-backend = "hatchling.build" | ||
|
|
||
| [tool.hatch.build.targets.wheel] | ||
| packages = ["src/orchestration_heartbeat"] |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.