feat(maestro): wrap dispatch, session list/show, send --tab/--no-system-prompt#44
Conversation
…em-prompt
Adds wrappers for the four CLI surfaces that landed in upstream Maestro
since the kernel refactor and that target relay-style external consumers:
- maestro.dispatch(agentId, message, { newTab?, tabId?, force? }) —
hand a prompt to the desktop app and return { agentId, sessionId,
tabId } so callers can address the same tab on follow-up calls.
- maestro.sessionList() — enumerate every open AI tab across every
agent in the running desktop.
- maestro.sessionShow(tabId, { since?, tail? }) — fetch a tab's
conversation history; --since auto-detects ISO-8601 vs epoch ms/sec.
- maestro.send() options bag now exposes openTab (-t, focus the tab
after delivery) and noSystemPrompt (--no-system-prompt, opt out of
the Maestro system context the CLI appends by default).
send()'s positional signature is folded into an options bag — the
queue is the only caller, updated in the same commit. Defaults
preserve current behavior: system prompt included, tab not opened.
Error envelopes from the CLI ({ success: false, error, code }) are
unwrapped into native Errors so the new methods throw with the same
ergonomics as the existing wrappers. dispatch reuses send's
stdout-recovery fall-through for the non-zero-exit-with-JSON case.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthroughThis PR refactors the maestro service API and adds new functionality for desktop dispatch delivery and session management. It introduces four data type contracts, refactors ChangesMaestro Service API Expansion
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/core/maestro.ts`:
- Around line 47-56: The DispatchResult path currently returns { success: false,
error, code } for parsed error envelopes which makes await callers treat
failures as fulfilled; update the dispatch function so that after parsing the
dispatch envelope (the branch that currently produces DispatchResult) you detect
error/code fields and throw an Error (or a specialized exception carrying
error/code) instead of returning success: false, and apply the same behavior to
the stdout: fallback path so both parsed and fallback error envelopes reject;
keep returning DispatchResult only for truly successful envelopes (including
agentId/sessionId/tabId). Also mirror this change at the other dispatch handling
block referenced (lines around 371-385) so both code paths consistently throw on
parsed errors.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 9c548ce1-8db7-498f-819a-48b552f699c9
📒 Files selected for processing (2)
src/core/maestro.tssrc/core/queue.ts
| export interface DispatchResult { | ||
| success: boolean; | ||
| agentId?: string; | ||
| /** Tab id the prompt was delivered to. Identical to `tabId` — the CLI emits | ||
| * both keys so polling consumers can use either. */ | ||
| sessionId?: string | null; | ||
| tabId?: string | null; | ||
| error?: string; | ||
| code?: string; | ||
| } |
There was a problem hiding this comment.
Throw on parsed dispatch error envelopes.
dispatch() currently resolves with { success: false, error, code } from both the normal parse path and the stdout: fallback path. That breaks the wrapper contract the PR describes and makes a failed dispatch look like a successful await, leaving consumers with nullable IDs on the resolved path.
💡 Suggested fix
export interface DispatchResult {
- success: boolean;
- agentId?: string;
+ success: true;
+ agentId: string;
/** Tab id the prompt was delivered to. Identical to `tabId` — the CLI emits
* both keys so polling consumers can use either. */
- sessionId?: string | null;
- tabId?: string | null;
- error?: string;
- code?: string;
+ sessionId: string | null;
+ tabId: string | null;
}
@@
async dispatch(
@@
): Promise<DispatchResult> {
@@
try {
const raw = await runSpawn(args);
- return JSON.parse(raw) as DispatchResult;
+ const parsed = JSON.parse(raw) as
+ | DispatchResult
+ | { success: false; error?: string; code?: string };
+ if (parsed.success === false) {
+ throw new Error(`dispatch failed: ${parsed.error ?? 'unknown'} (${parsed.code ?? 'UNKNOWN'})`);
+ }
+ return parsed;
} catch (err: unknown) {
@@
if (stdoutMatch) {
try {
- return JSON.parse(stdoutMatch[1]) as DispatchResult;
+ const parsed = JSON.parse(stdoutMatch[1]) as
+ | DispatchResult
+ | { success: false; error?: string; code?: string };
+ if (parsed.success === false) {
+ throw new Error(`dispatch failed: ${parsed.error ?? 'unknown'} (${parsed.code ?? 'UNKNOWN'})`);
+ }
+ return parsed;
} catch {
/* fall through */
}
}
throw err;Also applies to: 371-385
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/core/maestro.ts` around lines 47 - 56, The DispatchResult path currently
returns { success: false, error, code } for parsed error envelopes which makes
await callers treat failures as fulfilled; update the dispatch function so that
after parsing the dispatch envelope (the branch that currently produces
DispatchResult) you detect error/code fields and throw an Error (or a
specialized exception carrying error/code) instead of returning success: false,
and apply the same behavior to the stdout: fallback path so both parsed and
fallback error envelopes reject; keep returning DispatchResult only for truly
successful envelopes (including agentId/sessionId/tabId). Also mirror this
change at the other dispatch handling block referenced (lines around 371-385) so
both code paths consistently throw on parsed errors.
Summary
Wraps the four
maestro-clisurfaces that landed upstream since the kernel refactor and that target relay-style external consumers (the dispatch + session PRs explicitly name Maestro-Discord/relay as the intended caller):maestro.dispatch(agentId, message, { newTab?, tabId?, force? })— hand a prompt to the running desktop app and get back{ agentId, sessionId, tabId }so the caller can address the same tab on follow-up calls. (upstreamdfdb77262)maestro.sessionList()— enumerate every open AI tab across every agent. (upstreamac51048fa)maestro.sessionShow(tabId, { since?, tail? })— fetch a tab's conversation history;--sinceauto-detects ISO-8601 vs epoch ms/sec, so a previous response'smessages[].timestampround-trips directly. (upstreamac51048fa)send()options bag — addsopenTab(-t, focuses the tab after delivery) andnoSystemPrompt(--no-system-prompt, opt out of the Maestro system context the CLI appends by default for parity with the desktop app — agent identity, git branch, history-file pointer, conductor profile).Behavioral notes
send()'s positional signature is folded into an options bag — the queue is the only caller and is updated in the same commit. Defaults preserve current behavior: system prompt included, tab not opened.{ success: false, error, code }envelope is unwrapped into nativeErrors so the new methods throw with the same ergonomics as the existing wrappers (createGist,directorSynopsis, etc.).dispatchreusessend's stdout-recovery fall-through for the case where the CLI exits non-zero but still emits a parseable JSON error on stdout.--no-system-promptflag exists on the RC branches the user is running (PR #1003 onmaestro-p-rcand friends). Once it lands in upstreammain, default behavior is "include the prompt" — matching what we set here.Pairing pattern this unlocks
Test plan
npx tsc --noEmit— cleannpm test— 211/211 passdispatch --new-tabagainst a live desktop, thensessionShowwith the returnedtabId.🤖 Generated with Claude Code
Summary by CodeRabbit
Release Notes