@mindfullabai/onda-mcp 0.3.2 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +248 -0
- package/package.json +1 -1
- package/skills/onda-mcp-usage/SKILL.md +140 -7
package/dist/index.js
CHANGED
|
@@ -577,6 +577,195 @@ const TOOLS = [
|
|
|
577
577
|
required: ['workspace', 'bin'],
|
|
578
578
|
},
|
|
579
579
|
},
|
|
580
|
+
// --- Agent delegation (preferred over onda_launch_session) ---
|
|
581
|
+
// These three tools (`spawn` / `wait` / `close`) are the canonical
|
|
582
|
+
// entry points for "Claude session A delegates work to Claude session
|
|
583
|
+
// B via Onda mosaic". They expose the same composer pipeline as the
|
|
584
|
+
// legacy onda_launch_session but with three improvements:
|
|
585
|
+
// 1. Three placement modes (workspace / pane-split / same-workspace)
|
|
586
|
+
// so the caller can decide whether to splice a new tile, split a
|
|
587
|
+
// pane in the current workspace, or just add a terminal to an
|
|
588
|
+
// already-mounted workspace.
|
|
589
|
+
// 2. The mount path goes through the post-fix `mountWorkspaceInTile`
|
|
590
|
+
// flow, so the tile actually appears in the UI (the legacy
|
|
591
|
+
// launchSession path only set the active workspace, which after
|
|
592
|
+
// the B1 fix in Onda 1.9.1 was no longer enough on its own).
|
|
593
|
+
// 3. Optional `subscribe + sessionId` so the caller can immediately
|
|
594
|
+
// hand the sessionId to `onda_agent_wait` without a separate
|
|
595
|
+
// `terminal_subscribe` round-trip.
|
|
596
|
+
//
|
|
597
|
+
// The legacy `onda_launch_session` tool still works (it maps to the
|
|
598
|
+
// same backend handler) but is kept only for backward compatibility.
|
|
599
|
+
// New code should prefer `onda_agent_spawn`.
|
|
600
|
+
{
|
|
601
|
+
name: 'onda_agent_spawn',
|
|
602
|
+
description: 'Boot an agent in a workspace+pane and return once the TUI is ready to accept a prompt. Does NOT submit a task — use `onda_agent_prompt` after spawn for that. Splitting bootstrap and prompt delivery makes the flow resilient: you can retry the prompt without re-paying the agent cold-start (5-10s for Claude), and you can intercept unexpected TUI states (trust dialog, model picker) between the two steps. Placement modes: "workspace" (default — full create+mount+add), "pane-split" (split active pane of current workspace), "same-workspace" (add a terminal to an already-mounted workspace). For non-TUI binaries (bash, scripts), pass the full command in `agentArgs` and use `execMode: "run"`; `onda_agent_prompt` is then unnecessary.',
|
|
603
|
+
inputSchema: {
|
|
604
|
+
type: 'object',
|
|
605
|
+
properties: {
|
|
606
|
+
placement: {
|
|
607
|
+
type: 'string',
|
|
608
|
+
enum: ['workspace', 'pane-split', 'same-workspace'],
|
|
609
|
+
description: 'Where to host the new agent terminal. Default "workspace": create-or-locate workspace + mount + add terminal. "pane-split": split the active pane of the focused workspace. "same-workspace": add a terminal pane to an existing mounted workspace (requires `workspaceId`).',
|
|
610
|
+
},
|
|
611
|
+
// --- placement=workspace ---
|
|
612
|
+
workspaceRootPath: {
|
|
613
|
+
type: 'string',
|
|
614
|
+
description: 'placement=workspace: filesystem path used to locate (or create) the workspace. If the workspace does not exist and rootPath is set, it will be auto-created (name = basename of rootPath unless `workspaceName` is given).',
|
|
615
|
+
},
|
|
616
|
+
workspaceName: {
|
|
617
|
+
type: 'string',
|
|
618
|
+
description: 'placement=workspace: optional override for the auto-created workspace name. Ignored if a workspace already matches `workspaceRootPath`.',
|
|
619
|
+
},
|
|
620
|
+
targetWindowId: {
|
|
621
|
+
type: 'string',
|
|
622
|
+
description: 'placement=workspace: window to mount the workspace in. Default: focused window.',
|
|
623
|
+
},
|
|
624
|
+
mountDirection: {
|
|
625
|
+
type: 'string',
|
|
626
|
+
enum: ['down', 'right'],
|
|
627
|
+
description: 'placement=workspace: where to splice the new tile in the mosaic. Default "down" (matches the Cmd+P picker default).',
|
|
628
|
+
},
|
|
629
|
+
// --- placement=pane-split ---
|
|
630
|
+
splitDirection: {
|
|
631
|
+
type: 'string',
|
|
632
|
+
enum: ['right', 'down', 'left', 'up', 'horizontal', 'vertical'],
|
|
633
|
+
description: 'placement=pane-split: how to split the active pane. Default "down".',
|
|
634
|
+
},
|
|
635
|
+
splitCwd: {
|
|
636
|
+
type: 'string',
|
|
637
|
+
description: 'placement=pane-split: cwd of the new pane (typically the project root the delegated agent should work in).',
|
|
638
|
+
},
|
|
639
|
+
// --- placement=same-workspace ---
|
|
640
|
+
workspaceId: {
|
|
641
|
+
type: 'string',
|
|
642
|
+
description: 'placement=same-workspace: id of an already-mounted workspace to add a terminal to.',
|
|
643
|
+
},
|
|
644
|
+
addDirection: {
|
|
645
|
+
type: 'string',
|
|
646
|
+
enum: ['right', 'down'],
|
|
647
|
+
description: 'placement=same-workspace: split direction for the new pane. Default "down".',
|
|
648
|
+
},
|
|
649
|
+
// --- universal ---
|
|
650
|
+
agentName: {
|
|
651
|
+
type: 'string',
|
|
652
|
+
description: 'Listener label shown in the pane header (e.g. "kai-2026-05-23"). Recommended: include a date or session identifier so the user can tell concurrent delegations apart.',
|
|
653
|
+
},
|
|
654
|
+
agentBin: {
|
|
655
|
+
type: 'string',
|
|
656
|
+
description: 'Binary to exec inside the pane. Default "claude". Any PATH-resolvable executable works (claude, qwen, codex, bash, etc.).',
|
|
657
|
+
},
|
|
658
|
+
agentArgs: {
|
|
659
|
+
type: 'array',
|
|
660
|
+
items: { type: 'string' },
|
|
661
|
+
description: 'Extra argv entries appended after the binary. For TUI agents (claude/qwen): use flags only (e.g. ["--dangerously-skip-permissions"]) — DO NOT include the task prompt here; submit it separately via onda_agent_prompt once readyPattern has matched. For non-TUI bins (bash/sh): include the full command line, e.g. ["-lc", "build.sh && echo DONE"].',
|
|
662
|
+
},
|
|
663
|
+
execMode: {
|
|
664
|
+
type: 'string',
|
|
665
|
+
enum: ['exec', 'run'],
|
|
666
|
+
description: 'How to hand the binary to the pane\'s existing shell. Default "exec": writes `exec bin args` so the binary REPLACES the shell (right for long-lived TUI agents like claude/qwen — they own the pane, no shell prompt visible). Set to "run" for one-shot commands (build, script, quick check): writes `bin args` plainly, the shell runs it as a child and re-prompts when done, so the PTY stays alive and `agent.wait` subscribers survive even for short tasks. Wrong choice for short tasks under "exec" causes the PTY to exit the moment the command completes, wiping any pending wait subscriber.',
|
|
667
|
+
},
|
|
668
|
+
readyPattern: {
|
|
669
|
+
type: 'string',
|
|
670
|
+
description: 'JavaScript regex (string form). When set, agent.spawn blocks until the pattern matches new output before returning. Use it to synchronize on "TUI is ready for a prompt" — for Claude TUI a good default is "❯|Welcome back|Try \\"|bypass permissions"; for qwen "qwen>|Ready"; for bash this is unnecessary. The response includes `ready: true` on match, `ready: false` on timeout (caller decides whether to proceed).',
|
|
671
|
+
},
|
|
672
|
+
readyTimeoutMs: {
|
|
673
|
+
type: 'number',
|
|
674
|
+
description: 'Max wait for readyPattern in ms. Default 15000 (15s — enough for Claude cold-start on a busy machine).',
|
|
675
|
+
},
|
|
676
|
+
subscribe: {
|
|
677
|
+
type: 'boolean',
|
|
678
|
+
description: 'Attach a listener and return a `sessionId` ready for `onda_agent_wait`. Default true. Also lights up the presence badge in the pane header.',
|
|
679
|
+
},
|
|
680
|
+
snapshotDelay: {
|
|
681
|
+
type: 'number',
|
|
682
|
+
description: 'Milliseconds to wait before reading the first chunk of buffer for the `firstChunk` field in the response. Default 500. Set to 0 to skip the snapshot. Ignored if readyPattern matched and provided a sync point already.',
|
|
683
|
+
},
|
|
684
|
+
},
|
|
685
|
+
required: ['agentBin'],
|
|
686
|
+
},
|
|
687
|
+
},
|
|
688
|
+
{
|
|
689
|
+
name: 'onda_agent_prompt',
|
|
690
|
+
description: 'Submit a task prompt to an agent that was previously spawned with `onda_agent_spawn`. Use this once `onda_agent_spawn` has returned with `ready: true` (TUI textbox is up and accepting keystrokes). Writes the prompt as raw text into the PTY, then optionally sends Enter to submit. Idempotent in the sense that you can call it multiple times — useful for multi-line drafts (each call with `submitWith: "none"` except the last) or for retry-on-blocker scenarios.',
|
|
691
|
+
inputSchema: {
|
|
692
|
+
type: 'object',
|
|
693
|
+
properties: {
|
|
694
|
+
terminalId: {
|
|
695
|
+
type: 'string',
|
|
696
|
+
description: 'Terminal id from `onda_agent_spawn` response (paneId === terminalId for terminal-content panes).',
|
|
697
|
+
},
|
|
698
|
+
prompt: {
|
|
699
|
+
type: 'string',
|
|
700
|
+
description: 'The text to write. For TUI agents this populates the textbox. No shell-quoting — the caller talks to a TUI, not a shell. Caller is responsible for any agent-bus preamble (e.g. "# STATUS:", "# DONE" sentinels, "/send kai brief: ..." vocabulary). Onda stays opinion-free about protocol semantics.',
|
|
701
|
+
},
|
|
702
|
+
submitWith: {
|
|
703
|
+
type: 'string',
|
|
704
|
+
enum: ['enter', 'none'],
|
|
705
|
+
description: 'How to submit the prompt. "enter" (default) writes a CR after the text — this is what most TUIs need to actually submit. "none" leaves the text in the buffer without submitting; use this when chaining multiple writes (e.g. multi-line draft, each call writing one line with submitWith=none, the final one with submitWith=enter).',
|
|
706
|
+
},
|
|
707
|
+
readyPattern: {
|
|
708
|
+
type: 'string',
|
|
709
|
+
description: 'Optional defensive ready-sync regex. If provided, `agent.prompt` first runs a quick `waitForPattern` (default 5s timeout) before writing. Cheap when the buffer already matches; safety net when the caller is racing TUI cold-start. Generally redundant if `agent.spawn` already synced on the same pattern.',
|
|
710
|
+
},
|
|
711
|
+
readyTimeoutMs: {
|
|
712
|
+
type: 'number',
|
|
713
|
+
description: 'Max wait for `readyPattern` in ms. Default 5000.',
|
|
714
|
+
},
|
|
715
|
+
preWriteDelay: {
|
|
716
|
+
type: 'number',
|
|
717
|
+
description: 'Optional ms delay between ready-sync and the actual write. Some TUIs need a beat to finish layout transitions even after the textbox is technically present.',
|
|
718
|
+
},
|
|
719
|
+
postSubmitDelay: {
|
|
720
|
+
type: 'number',
|
|
721
|
+
description: 'Optional ms delay after sending Enter, before returning. Useful if you want to give the agent a moment to start streaming output that the next caller-side step (terminal_read snapshot, agent_wait) will need.',
|
|
722
|
+
},
|
|
723
|
+
},
|
|
724
|
+
required: ['terminalId', 'prompt'],
|
|
725
|
+
},
|
|
726
|
+
},
|
|
727
|
+
{
|
|
728
|
+
name: 'onda_agent_wait',
|
|
729
|
+
description: 'Block on a subscriber session until a regex matches new terminal output, or timeout. Use this after `onda_agent_spawn` to wait for a sentinel pattern that indicates the delegated agent is done (or has hit a blocker). Returns { matched, match?, bufferedChunks, tailContent? } where tailContent is the last ~4KB of accumulated output (useful for surfacing a summary to the user or for debugging mismatched expectations).',
|
|
730
|
+
inputSchema: {
|
|
731
|
+
type: 'object',
|
|
732
|
+
properties: {
|
|
733
|
+
sessionId: {
|
|
734
|
+
type: 'string',
|
|
735
|
+
description: 'Session id returned by `onda_agent_spawn` (or by a manual `onda_terminal_subscribe`).',
|
|
736
|
+
},
|
|
737
|
+
doneRegex: {
|
|
738
|
+
type: 'string',
|
|
739
|
+
description: 'JavaScript regex (string form). Default matches `^# DONE`, `^# BLOCKED`, or `Esc to interrupt`. Pick a sentinel that matches whatever the agent-bus protocol used by your team prescribes.',
|
|
740
|
+
},
|
|
741
|
+
timeoutMs: {
|
|
742
|
+
type: 'number',
|
|
743
|
+
description: 'Max wait in ms. Default 300000 (5 min). Cap to whatever your task complexity warrants.',
|
|
744
|
+
},
|
|
745
|
+
flags: {
|
|
746
|
+
type: 'string',
|
|
747
|
+
description: 'Regex flags. Default "m" (multiline).',
|
|
748
|
+
},
|
|
749
|
+
},
|
|
750
|
+
required: ['sessionId'],
|
|
751
|
+
},
|
|
752
|
+
},
|
|
753
|
+
{
|
|
754
|
+
name: 'onda_agent_close',
|
|
755
|
+
description: 'Escalation cleanup after an agent finishes (or to abandon a delegation). Three levels: SOFT — only `sessionId` set → unsubscribe (keeps PTY alive, lets you reattach or let the user inspect). MEDIUM — `+ killPty + terminalId` → terminates the agent process. HARD — `+ unmountWorkspace + workspaceId` (and optionally `+ closeWindow + windowId`) → full teardown of an ephemeral delegation. All fields are optional; the tool does only what you ask.',
|
|
756
|
+
inputSchema: {
|
|
757
|
+
type: 'object',
|
|
758
|
+
properties: {
|
|
759
|
+
sessionId: { type: 'string', description: 'Subscriber session to detach. Idempotent.' },
|
|
760
|
+
terminalId: { type: 'string', description: 'Terminal to kill. Required when `killPty` is true.' },
|
|
761
|
+
workspaceId: { type: 'string', description: 'Workspace to unmount. Required when `unmountWorkspace` is true.' },
|
|
762
|
+
killPty: { type: 'boolean', description: 'Default false. When true, the underlying PTY process is terminated (UI shows [Process exited]).' },
|
|
763
|
+
unmountWorkspace: { type: 'boolean', description: 'Default false. When true, the workspace is dropped from its window\'s mosaic. The workspace itself still exists in the global list.' },
|
|
764
|
+
closeWindow: { type: 'boolean', description: 'Default false. When true, the host window is closed via `BrowserWindow.close()`. Requires `windowId`. Only use when YOU spawned this window via `onda_window_new`.' },
|
|
765
|
+
windowId: { type: 'string', description: 'Required when `closeWindow` is true.' },
|
|
766
|
+
},
|
|
767
|
+
},
|
|
768
|
+
},
|
|
580
769
|
// --- System ---
|
|
581
770
|
{
|
|
582
771
|
name: 'onda_context',
|
|
@@ -695,6 +884,65 @@ const TOOL_MAP = {
|
|
|
695
884
|
}),
|
|
696
885
|
},
|
|
697
886
|
onda_launch_session: { method: 'launchSession' },
|
|
887
|
+
// Agent delegation: forward EVERY declared param to the backend. Do
|
|
888
|
+
// not silently drop fields (the old `workspace.addTerminal` mapParams
|
|
889
|
+
// bug was caused exactly by that: schema declared `direction`, mapper
|
|
890
|
+
// omitted it, calls silently fell back to default placement).
|
|
891
|
+
onda_agent_spawn: {
|
|
892
|
+
method: 'agent.spawn',
|
|
893
|
+
mapParams: (args) => ({
|
|
894
|
+
placement: args.placement,
|
|
895
|
+
workspaceRootPath: args.workspaceRootPath,
|
|
896
|
+
workspaceName: args.workspaceName,
|
|
897
|
+
targetWindowId: args.targetWindowId,
|
|
898
|
+
mountDirection: args.mountDirection,
|
|
899
|
+
splitDirection: args.splitDirection,
|
|
900
|
+
splitCwd: args.splitCwd,
|
|
901
|
+
workspaceId: args.workspaceId,
|
|
902
|
+
addDirection: args.addDirection,
|
|
903
|
+
agentName: args.agentName,
|
|
904
|
+
agentBin: args.agentBin,
|
|
905
|
+
agentArgs: args.agentArgs,
|
|
906
|
+
execMode: args.execMode,
|
|
907
|
+
subscribe: args.subscribe,
|
|
908
|
+
snapshotDelay: args.snapshotDelay,
|
|
909
|
+
readyPattern: args.readyPattern,
|
|
910
|
+
readyTimeoutMs: args.readyTimeoutMs,
|
|
911
|
+
}),
|
|
912
|
+
},
|
|
913
|
+
onda_agent_prompt: {
|
|
914
|
+
method: 'agent.prompt',
|
|
915
|
+
mapParams: (args) => ({
|
|
916
|
+
terminalId: args.terminalId,
|
|
917
|
+
prompt: args.prompt,
|
|
918
|
+
submitWith: args.submitWith,
|
|
919
|
+
readyPattern: args.readyPattern,
|
|
920
|
+
readyTimeoutMs: args.readyTimeoutMs,
|
|
921
|
+
preWriteDelay: args.preWriteDelay,
|
|
922
|
+
postSubmitDelay: args.postSubmitDelay,
|
|
923
|
+
}),
|
|
924
|
+
},
|
|
925
|
+
onda_agent_wait: {
|
|
926
|
+
method: 'agent.wait',
|
|
927
|
+
mapParams: (args) => ({
|
|
928
|
+
sessionId: args.sessionId,
|
|
929
|
+
doneRegex: args.doneRegex,
|
|
930
|
+
timeoutMs: args.timeoutMs,
|
|
931
|
+
flags: args.flags,
|
|
932
|
+
}),
|
|
933
|
+
},
|
|
934
|
+
onda_agent_close: {
|
|
935
|
+
method: 'agent.close',
|
|
936
|
+
mapParams: (args) => ({
|
|
937
|
+
sessionId: args.sessionId,
|
|
938
|
+
terminalId: args.terminalId,
|
|
939
|
+
workspaceId: args.workspaceId,
|
|
940
|
+
killPty: args.killPty,
|
|
941
|
+
unmountWorkspace: args.unmountWorkspace,
|
|
942
|
+
closeWindow: args.closeWindow,
|
|
943
|
+
windowId: args.windowId,
|
|
944
|
+
}),
|
|
945
|
+
},
|
|
698
946
|
// System (onda_context handled separately - it's local, not RPC)
|
|
699
947
|
onda_status: { method: 'session.current' },
|
|
700
948
|
onda_app_info: { method: 'app.info' },
|
package/package.json
CHANGED
|
@@ -2,22 +2,30 @@
|
|
|
2
2
|
name: onda-mcp-usage
|
|
3
3
|
description: Best practices for driving Onda (terminal emulator) from Claude Code via the `onda` MCP server (`mcp__onda__*`). Covers workspaces/windows/panes/terminals + buffer reading (read/subscribe/poll/wait_for/send_keys) + spatial awareness (layout/screenshot). Use whenever you call any `mcp__onda__*` tool or the user mentions Onda, pane, workspace, terminal listener, presence badge, inception loop (Claude controlling Onda from inside Onda). Triggers — onda mcp, drive onda, control onda terminal, show pane, workspace layout, onda screenshot, kai watcher, terminal listener.
|
|
4
4
|
metadata:
|
|
5
|
-
version: 0.
|
|
5
|
+
version: 0.5.0
|
|
6
6
|
source: '@mindfullabai/onda-mcp'
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Onda MCP usage skill
|
|
10
10
|
|
|
11
|
-
This skill tells you **how to correctly use the `mcp__onda__*` tools** exposed by the `onda-mcp` server (~
|
|
11
|
+
This skill tells you **how to correctly use the `mcp__onda__*` tools** exposed by the `onda-mcp` server (~42 tools as of 0.4.0). It is installed alongside the MCP server. Without this guide you miss non-obvious patterns (subscribe vs read, direction semantics, multi-window orchestration, agent delegation atomic macro, inception loop).
|
|
12
12
|
|
|
13
13
|
## Compatibility note
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
Breaking change in `@mindfullabai/onda-mcp@0.5.0`:
|
|
16
|
+
- `onda_agent_spawn` no longer accepts a `prompt` argument. Prompt delivery is now a separate call, `onda_agent_prompt`, gated on a `readyPattern` sync. See "Pattern: agent delegation" below for the new four-tool flow. The split makes the pipeline resilient against TUI cold-start race conditions (trust dialog, splash screen, model picker) that previously caused the Enter keystroke to land on the wrong control.
|
|
17
|
+
- `submitDelay` is removed from `onda_agent_spawn` (the spawn never auto-submits anymore). Use `agent_prompt` with `preWriteDelay` if you really need a beat between ready-sync and the write.
|
|
18
|
+
- The legacy `onda_launch_session` is unchanged and still accepts the v2 `prompt` argv + auto-submit semantics; use it if you have existing callers you don't want to touch.
|
|
19
|
+
|
|
20
|
+
New in `@mindfullabai/onda-mcp@0.4.0`:
|
|
21
|
+
- **Agent delegation tools**: `onda_agent_spawn` + `onda_agent_wait` + `onda_agent_close` (0.5.0 added `onda_agent_prompt`). Replaces the 6-step manual dance. Three placement modes (workspace / pane-split / same-workspace).
|
|
22
|
+
|
|
23
|
+
Bug fixes shipped in `@mindfullabai/onda-mcp@0.3.1` + Onda 1.9.1+:
|
|
16
24
|
- `terminal_read` now returns the full buffer even on first call (eager tap attach at PTY spawn). Pre-0.3.1 a fresh terminal returned an empty buffer until `subscribe` was active.
|
|
17
25
|
- `workspace_add_terminal` now respects `direction='down'` (was silently rewritten to `'row'` whenever the anchor was already in a row split).
|
|
18
26
|
- `window_mount_workspace` now actually splices the workspace into the tiled mosaic (pre-fix the registry was updated but the UI tab/tile never appeared). Accepts `direction` ('down'|'right', default 'down') and `anchorWorkspaceId` mirroring the Cmd+P picker.
|
|
19
27
|
|
|
20
|
-
If you are talking to an older host, fall back to the legacy patterns: subscribe before run, avoid direction='down' on add_terminal, click-mount via the user.
|
|
28
|
+
If you are talking to an older host (Onda < 1.9.1), fall back to the legacy patterns: subscribe before run, avoid direction='down' on add_terminal, click-mount via the user. The `agent_spawn` tool will respond with `{error: "Unknown method"}` if the host pre-dates 0.4.0 — fall back to `onda_launch_session` in that case.
|
|
21
29
|
|
|
22
30
|
## Mental model
|
|
23
31
|
|
|
@@ -43,6 +51,10 @@ Internal workspace layout = mosaic-component tree. Read it via `onda_workspace_l
|
|
|
43
51
|
| "Move workspace W from one window to another" | `onda_window_mount_workspace` with target `windowId` (atomic transfer, returns `transferred: true`) |
|
|
44
52
|
| "Drop workspace from its window without deleting it" | `onda_workspace_unmount` (registry slot released, PTYs survive) |
|
|
45
53
|
| "Bring a window to front" | `onda_window_focus` (omit `windowId` to focus primary) |
|
|
54
|
+
| "Boot an agent in a workspace" | `onda_agent_spawn` (mount + add terminal + exec bin + readyPattern sync) |
|
|
55
|
+
| "Submit a task prompt to a TUI agent" | `onda_agent_prompt` (after spawn returned ready:true) |
|
|
56
|
+
| "Wait until the delegated agent reports done" | `onda_agent_wait` with `doneRegex` |
|
|
57
|
+
| "Clean up after a delegation" | `onda_agent_close` (escalation: soft → kill → unmount → close window) |
|
|
46
58
|
| "Read what the terminal has printed so far" | `onda_terminal_read` (no subscribe needed) |
|
|
47
59
|
| "I want to receive every new output chunk" | `subscribe` + `poll` loop |
|
|
48
60
|
| "Wait until the build finishes" | `onda_terminal_wait_for` with regex |
|
|
@@ -162,7 +174,121 @@ screenshot = window_screenshot(windowId, format="jpeg", quality=72, dataUrl=fals
|
|
|
162
174
|
|
|
163
175
|
Default `dataUrl=true` returns inline base64 (~1-2 MB), `dataUrl=false` writes a tempfile that `Read` mounts as multimodal image — preferred to avoid wasting context.
|
|
164
176
|
|
|
165
|
-
## Pattern:
|
|
177
|
+
## Pattern: agent delegation (PREFERRED, two-call for TUIs)
|
|
178
|
+
|
|
179
|
+
When you need to delegate work to another Claude session (or any TUI agent) — e.g. Alita @ work-hub asks Kai to refactor something in Orbit — use the four-tool flow: `onda_agent_spawn` → `onda_agent_prompt` → `onda_agent_wait` → `onda_agent_close`. The first two are the meat; `wait` and `close` are observation + cleanup.
|
|
180
|
+
|
|
181
|
+
The flow is **split into spawn + prompt** intentionally. Booting a TUI like Claude costs 5-10s and runs through several transient states (trust dialog → splash → welcome → textbox). Submitting the prompt while the TUI is still in trust dialog state means the Enter goes to the dialog, not your prompt. Splitting the two phases gives you a sync point (`readyPattern`) and lets you retry just the prompt if delivery fails, without re-paying the cold start.
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
# 1. Spawn (boot only — no prompt yet)
|
|
185
|
+
spawn = agent_spawn(
|
|
186
|
+
placement="workspace", # create-or-locate workspace + mount + add terminal
|
|
187
|
+
workspaceRootPath="~/Projects/04-Production/Orbit",
|
|
188
|
+
mountDirection="right", # splice the new tile to the right
|
|
189
|
+
agentName="kai-2026-05-23", # listener label in pane header
|
|
190
|
+
agentBin="claude",
|
|
191
|
+
agentArgs=["--dangerously-skip-permissions"], # flags only, NOT the task prompt
|
|
192
|
+
readyPattern="❯|Welcome back|Try \"", # block until Claude TUI textbox is up
|
|
193
|
+
readyTimeoutMs=20000, # generous: Claude cold-start
|
|
194
|
+
subscribe=True, # default; returns sessionId for agent_wait
|
|
195
|
+
)
|
|
196
|
+
# → {sessionId, terminalId, paneId, workspaceId, windowId, ready: True, firstChunk, completedSteps:[...,'ready_sync']}
|
|
197
|
+
|
|
198
|
+
if not spawn.ready:
|
|
199
|
+
# readyPattern didn't match — TUI is in an unexpected state.
|
|
200
|
+
# Inspect spawn.firstChunk to see where it got stuck (trust dialog?
|
|
201
|
+
# model picker? bin not found?). Decide whether to retry, send keystrokes
|
|
202
|
+
# to navigate the dialog, or give up and call agent_close.
|
|
203
|
+
...
|
|
204
|
+
|
|
205
|
+
# 2. Submit the task prompt
|
|
206
|
+
agent_prompt(
|
|
207
|
+
terminalId=spawn.terminalId,
|
|
208
|
+
prompt="/send kai brief: refactor Authenticator.swift; target test file Tests/AuthTests.swift; report '# DONE' on its own line when green",
|
|
209
|
+
submitWith="enter", # default; press Enter to submit
|
|
210
|
+
)
|
|
211
|
+
# → {success: True, bytesWritten, completedSteps:['write','submit']}
|
|
212
|
+
|
|
213
|
+
# 3. Wait for the agent to signal completion
|
|
214
|
+
result = agent_wait(
|
|
215
|
+
sessionId=spawn.sessionId,
|
|
216
|
+
doneRegex="DONE|BLOCKED", # see "doneRegex pattern" below
|
|
217
|
+
timeoutMs=15000, # short window, loop if needed
|
|
218
|
+
timeoutMs=600000, # 10 min budget
|
|
219
|
+
)
|
|
220
|
+
# → {matched: True, match: "# DONE", tailContent: "...last 4KB..."}
|
|
221
|
+
|
|
222
|
+
# 4. Clean up. SOFT (default): unsubscribe only, leave the PTY alive so the
|
|
223
|
+
# user can inspect. MEDIUM (+killPty + terminalId): terminate the agent.
|
|
224
|
+
# HARD (+unmountWorkspace + workspaceId): drop the workspace tile.
|
|
225
|
+
agent_close(sessionId=spawn.sessionId)
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### doneRegex pattern: match across renderers
|
|
229
|
+
|
|
230
|
+
Don't be too literal with `# DONE`. TUI agents re-render markdown: Claude turns `# DONE` into a styled bullet `⏺ DONE` (with bold + italic + underline ANSI), qwen prefixes timestamps, codex may emit `[DONE]`. Pick a pattern that survives the visual layer.
|
|
231
|
+
|
|
232
|
+
Recommended generic patterns for delegated agents:
|
|
233
|
+
|
|
234
|
+
```
|
|
235
|
+
# Most resilient — matches DONE/BLOCKED regardless of bullet, prefix, or styling
|
|
236
|
+
doneRegex = "(DONE|BLOCKED|FAILED|ERROR)\\b"
|
|
237
|
+
|
|
238
|
+
# Conservative — matches the literal sentinel words preceded by whitespace, bullet, or markdown header
|
|
239
|
+
doneRegex = "(^|\\s|[#⏺\\-\\*•])\\s*(DONE|BLOCKED|FAILED)\\b"
|
|
240
|
+
|
|
241
|
+
# Strict — your team enforces "# DONE" / "# BLOCKED" exactly (works when the agent runs in a non-TUI shell, e.g. bash one-shot)
|
|
242
|
+
doneRegex = "^# (DONE|BLOCKED)"
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
For Claude TUI specifically, the markdown `# DONE` in the agent's reply becomes `⏺ DONE` on screen. The bullet glyph `⏺` (U+23FA) is the most reliable marker because Claude always renders it before H1 sentinels. Combined patterns work well: `"(⏺|^|^#)\\s*DONE"`.
|
|
246
|
+
|
|
247
|
+
### Long-running tasks: chunked polling
|
|
248
|
+
|
|
249
|
+
`agent_wait` blocks server-side until the regex matches or `timeoutMs` elapses. **But the MCP JSON-RPC client has its own timeout (~60s)**: a single `agent_wait(timeoutMs=300000)` call will fail client-side with "Connection timeout" long before the backend would have returned, even if the agent eventually completes in time.
|
|
250
|
+
|
|
251
|
+
For any task likely to take >30s, do **chunked polling**: loop with a short timeout, check `matched`, repeat until done or your budget is spent.
|
|
252
|
+
|
|
253
|
+
```
|
|
254
|
+
budget = 600 # 10 minutes
|
|
255
|
+
elapsed = 0
|
|
256
|
+
while elapsed < budget:
|
|
257
|
+
r = agent_wait(sessionId, doneRegex=..., timeoutMs=15000)
|
|
258
|
+
if r.matched:
|
|
259
|
+
break
|
|
260
|
+
elapsed += 15
|
|
261
|
+
else:
|
|
262
|
+
# timed out
|
|
263
|
+
...
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
The session cursor advances on each poll, so you don't re-scan bytes from the previous loop iteration; only new emissions are evaluated.
|
|
267
|
+
|
|
268
|
+
### Why split spawn + prompt?
|
|
269
|
+
|
|
270
|
+
Concretely: the alternative (single `agent_spawn` call that also submits the prompt) was tried — `claude --prompt` as argv mostly works for short prompts, but a multi-line / long prompt can land in the textbox while the trust dialog is still active, and the Enter goes to the dialog instead of submitting. The split flow has two side benefits:
|
|
271
|
+
|
|
272
|
+
1. **Retry-on-failure is cheap**: if `agent_prompt` reports `errorCode: "not_ready"`, you re-call `agent_prompt` (potentially after `terminal_read` to see what's blocking) without ever touching the PTY-spawn cost.
|
|
273
|
+
2. **TUI navigation between phases**: occasionally claude shows a model picker or "select profile" screen before the textbox. Between `agent_spawn` (which can target `readyPattern="select profile"`) and `agent_prompt`, the caller can send `send_keys(["Enter"])` to dismiss it, then proceed.
|
|
274
|
+
|
|
275
|
+
### Placement modes
|
|
276
|
+
|
|
277
|
+
| Mode | When to use |
|
|
278
|
+
|------|-------------|
|
|
279
|
+
| `workspace` (default) | Long isolated delegation. New tile alongside yours in the same window. Survives across delegations. |
|
|
280
|
+
| `pane-split` | Quick parallel work in the SAME workspace you're already in. The agent's cwd is `splitCwd`. Nothing to clean up at the workspace level. |
|
|
281
|
+
| `same-workspace` | The target workspace is already mounted (`workspaceId`). Just add another terminal pane to it — useful when several agents share one repo. |
|
|
282
|
+
|
|
283
|
+
### What `agent.spawn` does NOT do
|
|
284
|
+
|
|
285
|
+
- **Protocol preamble**: the `prompt` field is sent verbatim (modulo shell-quote). If your team has agent-bus conventions like `# STATUS:`, `/send`, `# DONE` markers — compose them into the prompt yourself before calling. This is intentional: Onda stays opinion-free about agent communication semantics, the agent-bus skill is the source of truth for that vocabulary.
|
|
286
|
+
- **Result aggregation**: `agent_wait` returns raw `tailContent`. Parsing structured output (extracting commit hashes, PR URLs, etc.) lives in the caller.
|
|
287
|
+
- **Workspace limit handling**: in free/dev builds Onda caps at 2 workspaces. When the cap is reached `agent_spawn` returns `{success: false, errorCode: "workspace_limit_reached", needsUserAction: true, hint: "..."}` — surface that to the user, do not retry blindly.
|
|
288
|
+
|
|
289
|
+
## Pattern: launching Claude Code in a pane (legacy, manual)
|
|
290
|
+
|
|
291
|
+
Pre-`agent_spawn` (0.3.x and earlier) pattern. Still works, but verbose. New code should prefer the section above.
|
|
166
292
|
|
|
167
293
|
```
|
|
168
294
|
pane = workspace_add_terminal(workspaceId="...", direction="right")
|
|
@@ -303,6 +429,7 @@ Con: prompt cache miss every 15 min (cache threshold 5 min), non-zero token cost
|
|
|
303
429
|
3. **`workspace_add_terminal` without prior `workspace_layout`**: blind placement. Append-right quickly becomes unreadable.
|
|
304
430
|
4. **Listener without unsubscribe**: leak for 30 min TTL. UI badge stays. Clean up.
|
|
305
431
|
5. **`window_new`** when the user just wants "one more workspace on the right": NO. Use `window_mount_workspace(workspaceId, windowId=current, direction='right')` to splice it into the existing window as a side-by-side tile. Open a new window only when the user actually wants two separate windows.
|
|
432
|
+
9. **Manual 6-step delegation dance** (`workspace_locate` → `workspace_create` → `window_mount_workspace` → `workspace_add_terminal` → `terminal_spawn` → `send_keys Enter` → `terminal_subscribe`): NO. Use `onda_agent_spawn` — it composes all of these atomically and avoids the race conditions where a fail mid-sequence leaves orphaned state.
|
|
306
433
|
6. **Assuming `app_info.name === "Onda-dev"`** distinguishes dev vs prod: NO. Use `app_info.path` or an explicit flag.
|
|
307
434
|
7. **`terminal_run` alone to submit a prompt to Claude TUI**: NO. The TUI treats `\n` as newline buffer. You must always follow with `send_keys(["Enter"])` to actually submit. (Validated 21 May 2026 spawning a dedicated Kai.)
|
|
308
435
|
8. **Spawning Kai and then waiting for Mario to click and type**: anti-pattern. If you spawned the session, you drive it to "Kai is working" (pattern "drive a remote Claude Code session"). Mario shouldn't be a human postman in your agent team.
|
|
@@ -314,7 +441,7 @@ Con: prompt cache miss every 15 min (cache threshold 5 min), non-zero token cost
|
|
|
314
441
|
- Onda uses a **single UDS socket**, `~/.config/onda/onda.sock`. Only one Onda instance answers MCP at a time (the first to bind). If `app_info` shows odd data, you may be talking to a different instance.
|
|
315
442
|
- The MCP server holds the socket binding until the master Onda app exits — restarting Onda via Cmd+Q + relaunch is how you "promote" another instance to master.
|
|
316
443
|
|
|
317
|
-
## Tool reference (
|
|
444
|
+
## Tool reference (~42 total)
|
|
318
445
|
|
|
319
446
|
Application:
|
|
320
447
|
- `onda_app_info` — version, pid, paths
|
|
@@ -364,7 +491,13 @@ Terminal (read/listen plane, M+1 2026-05-21):
|
|
|
364
491
|
|
|
365
492
|
Session:
|
|
366
493
|
- `onda_session_current` — current CLI consumer session
|
|
367
|
-
- `
|
|
494
|
+
- `onda_launch_session` — atomic macro "open workspace + add terminal + spawn bin" (DEPRECATED, use `onda_agent_spawn`)
|
|
495
|
+
|
|
496
|
+
Agent delegation (preferred since 0.4.0, split into spawn+prompt in 0.5.0):
|
|
497
|
+
- `onda_agent_spawn` — boot an agent in a workspace+pane and return when `readyPattern` matches (or timeout). Returns `sessionId` + `firstChunk`. Does NOT submit a prompt.
|
|
498
|
+
- `onda_agent_prompt` — write a prompt into the spawned agent's TUI textbox and submit it. Has its own optional `readyPattern` defensive sync.
|
|
499
|
+
- `onda_agent_wait` — block on the spawned agent's sessionId until `doneRegex` matches; returns matched chunk + tail content.
|
|
500
|
+
- `onda_agent_close` — escalation cleanup: unsubscribe → kill PTY → unmount workspace → close window. Each step opt-in via boolean flags.
|
|
368
501
|
|
|
369
502
|
## Versioning
|
|
370
503
|
|