@multiclawcli/core 0.4.0 → 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.
Files changed (47) hide show
  1. package/dist/agents/arch-design-reviewer.d.ts +4 -0
  2. package/dist/agents/architect.d.ts +4 -0
  3. package/dist/agents/backend-developer.d.ts +4 -0
  4. package/dist/agents/code-reviewer.d.ts +4 -0
  5. package/dist/agents/devops.d.ts +4 -0
  6. package/dist/agents/frontend-developer.d.ts +4 -0
  7. package/dist/agents/index.d.ts +26 -0
  8. package/dist/agents/leader.d.ts +4 -0
  9. package/dist/agents/product-manager.d.ts +4 -0
  10. package/dist/agents/reviewer.d.ts +4 -0
  11. package/dist/agents/tester.d.ts +4 -0
  12. package/dist/agents/ui-design-reviewer.d.ts +4 -0
  13. package/dist/agents/ui-designer.d.ts +4 -0
  14. package/dist/config/defineConfig.d.ts +2 -0
  15. package/dist/event/EventBus.d.ts +7 -0
  16. package/dist/index.d.ts +11 -720
  17. package/dist/index.js +463 -165
  18. package/dist/orchestrator/AgentRunner.d.ts +19 -0
  19. package/dist/orchestrator/ContextManager.d.ts +7 -0
  20. package/dist/orchestrator/Orchestrator.d.ts +109 -0
  21. package/dist/orchestrator/StageBuilder.d.ts +2 -0
  22. package/dist/providers/bridge.d.ts +29 -0
  23. package/dist/providers/catalog.d.ts +73 -0
  24. package/dist/providers/detect.d.ts +34 -0
  25. package/dist/providers/index.d.ts +4 -0
  26. package/dist/providers/resolve.d.ts +26 -0
  27. package/dist/runtime/AcpRuntime.d.ts +17 -0
  28. package/dist/runtime/RuntimeBase.d.ts +6 -0
  29. package/dist/runtime/index.d.ts +12 -0
  30. package/dist/types/agent.d.ts +96 -0
  31. package/dist/types/config.d.ts +126 -0
  32. package/dist/types/event.d.ts +115 -0
  33. package/dist/types/index.d.ts +5 -0
  34. package/dist/types/leader.d.ts +36 -0
  35. package/dist/types/runtime.d.ts +146 -0
  36. package/dist/utils/abort.d.ts +4 -0
  37. package/dist/utils/conversation.d.ts +29 -0
  38. package/dist/utils/errors.d.ts +16 -0
  39. package/dist/utils/json.d.ts +10 -0
  40. package/dist/utils/leaderDecision.d.ts +17 -0
  41. package/dist/utils/prompt.d.ts +1 -0
  42. package/dist/utils/resumePlan.d.ts +35 -0
  43. package/dist/utils/retry.d.ts +36 -0
  44. package/dist/utils/timeout.d.ts +1 -0
  45. package/dist/utils/toolSummary.d.ts +13 -0
  46. package/dist/utils/upstream.d.ts +69 -0
  47. package/package.json +7 -7
@@ -0,0 +1,115 @@
1
+ import type { AgentResult, OrchestratorResult } from "./index";
2
+ export interface StageInfo {
3
+ stageIndex: number;
4
+ agents: Array<{
5
+ id: string;
6
+ name: string;
7
+ icon?: string;
8
+ model?: string;
9
+ description?: string;
10
+ taskTitle?: string;
11
+ }>;
12
+ }
13
+ export type MultiClawEvent = {
14
+ type: "orchestration:start";
15
+ timestamp: string;
16
+ payload: {
17
+ name: string;
18
+ requirement?: string;
19
+ totalAgents: number;
20
+ stages: StageInfo[];
21
+ /** The leader's explanation for the agent set, when it gave one. */
22
+ selectionReason?: string;
23
+ };
24
+ } | {
25
+ /**
26
+ * The stage list changed after the turn had already started.
27
+ *
28
+ * The leader plans first, so `orchestration:start` necessarily carries the
29
+ * *pre-plan* grouping. Once the architect's task plan is applied the team is
30
+ * regrouped, and consumers that drew the first grouping need to be told —
31
+ * otherwise the console shows a pipeline that never ran.
32
+ */
33
+ type: "orchestration:stages";
34
+ timestamp: string;
35
+ payload: {
36
+ totalAgents: number;
37
+ stages: StageInfo[];
38
+ };
39
+ } | {
40
+ type: "orchestration:complete";
41
+ timestamp: string;
42
+ payload: OrchestratorResult;
43
+ } | {
44
+ type: "orchestration:warning";
45
+ timestamp: string;
46
+ payload: {
47
+ message: string;
48
+ };
49
+ } | {
50
+ type: "stage:start";
51
+ timestamp: string;
52
+ payload: {
53
+ stageIndex: number;
54
+ agentIds: string[];
55
+ };
56
+ } | {
57
+ type: "stage:complete";
58
+ timestamp: string;
59
+ payload: {
60
+ stageIndex: number;
61
+ };
62
+ } | {
63
+ type: "agent:start";
64
+ timestamp: string;
65
+ payload: {
66
+ agentId: string;
67
+ agentName: string;
68
+ icon?: string;
69
+ model?: string;
70
+ taskTitle?: string;
71
+ };
72
+ } | {
73
+ type: "agent:output";
74
+ timestamp: string;
75
+ payload: {
76
+ agentId: string;
77
+ chunk: string;
78
+ /**
79
+ * What kind of output this chunk is. Absent means `"text"`, so every
80
+ * producer and consumer written before this field existed is unchanged.
81
+ *
82
+ * - `"text"` — the agent's natural-language output (default)
83
+ * - `"tool"` — a summary line of tool activity, shown by default
84
+ * - `"tool-detail"` — a successful tool's detail, shown only in verbose mode
85
+ */
86
+ kind?: "text" | "tool" | "tool-detail";
87
+ };
88
+ } | {
89
+ type: "agent:complete";
90
+ timestamp: string;
91
+ payload: AgentResult;
92
+ } | {
93
+ type: "agent:failed";
94
+ timestamp: string;
95
+ payload: AgentResult;
96
+ } | {
97
+ type: "agent:skipped";
98
+ timestamp: string;
99
+ payload: {
100
+ agentId: string;
101
+ agentName: string;
102
+ icon?: string;
103
+ };
104
+ } | {
105
+ type: "agent:retrying";
106
+ timestamp: string;
107
+ payload: {
108
+ agentId: string;
109
+ agentName: string;
110
+ attempt: number;
111
+ maxAttempts: number;
112
+ error: string;
113
+ };
114
+ };
115
+ export type EventListener = (event: MultiClawEvent) => void;
@@ -0,0 +1,5 @@
1
+ export * from "./agent";
2
+ export * from "./config";
3
+ export * from "./event";
4
+ export * from "./leader";
5
+ export * from "./runtime";
@@ -0,0 +1,36 @@
1
+ /**
2
+ * What the leader decided a request needs.
3
+ *
4
+ * The leader is the only agent that runs before the pipeline exists, which makes
5
+ * it the only place where "does this even need the team?" can be answered. `run`
6
+ * is the familiar case; the other two exist so that a greeting or a question is
7
+ * answered instead of spinning up every agent in the config.
8
+ */
9
+ export type LeaderMode = "run" | "reply" | "ask";
10
+ export type LeaderDecision = {
11
+ mode: "run";
12
+ run: string[];
13
+ /**
14
+ * Why these agents, in the leader's own words.
15
+ *
16
+ * Optional because the field was added after the decision shape was: a
17
+ * model that omits it still gets its plan honoured. It exists so a user
18
+ * can ask "why did this run only two agents?" and get an answer from the
19
+ * run itself rather than from a re-run of the leader.
20
+ */
21
+ reason?: string;
22
+ } | {
23
+ mode: "reply" | "ask";
24
+ message: string;
25
+ };
26
+ /**
27
+ * A turn that ended in words rather than in a pipeline.
28
+ *
29
+ * Carried on {@link OrchestratorResult} so a caller can tell "the team ran and
30
+ * succeeded" from "the leader answered you" — both of which are `success: true`
31
+ * with an empty `agentResults`.
32
+ */
33
+ export interface LeaderMessage {
34
+ mode: "reply" | "ask";
35
+ message: string;
36
+ }
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Everything a runtime emits about a run carries the same envelope. `seq` is a
3
+ * per-run monotonic counter so a consumer can order events without trusting the
4
+ * clock; `runId` is fresh for every `execute()` call.
5
+ */
6
+ export interface RuntimeEventBase {
7
+ runId: string;
8
+ agentId: string;
9
+ seq: number;
10
+ }
11
+ /**
12
+ * The structured stream a runtime produces while an agent runs.
13
+ *
14
+ * This is deliberately wider than any single adapter implements: a runtime only
15
+ * emits the variants it can actually produce, and `RuntimeInfo.capabilities`
16
+ * tells the caller which those are. `raw` is the escape hatch for vendor output
17
+ * we recognise the shape of but have not modelled yet — it must never be the
18
+ * reason a run fails.
19
+ */
20
+ export type RuntimeEvent = (RuntimeEventBase & {
21
+ type: "run_started";
22
+ model?: string;
23
+ }) | (RuntimeEventBase & {
24
+ type: "message_delta";
25
+ role: "agent" | "thought" | "user";
26
+ text: string;
27
+ }) | (RuntimeEventBase & {
28
+ type: "message";
29
+ role: "agent" | "thought" | "user";
30
+ text: string;
31
+ }) | (RuntimeEventBase & {
32
+ type: "tool_call";
33
+ id: string;
34
+ name: string;
35
+ input: unknown;
36
+ }) | (RuntimeEventBase & {
37
+ type: "tool_result";
38
+ id: string;
39
+ output: string;
40
+ isError: boolean;
41
+ }) | (RuntimeEventBase & {
42
+ type: "plan";
43
+ entries: string[];
44
+ }) | (RuntimeEventBase & {
45
+ type: "usage";
46
+ inputTokens?: number;
47
+ outputTokens?: number;
48
+ contextTokens?: number;
49
+ contextSize?: number;
50
+ }) | (RuntimeEventBase & {
51
+ type: "cost";
52
+ costUsd?: number;
53
+ modelBreakdown?: Record<string, unknown>;
54
+ amount?: number;
55
+ currency?: string;
56
+ }) | (RuntimeEventBase & {
57
+ type: "raw";
58
+ vendor: string;
59
+ payload: unknown;
60
+ }) | (RuntimeEventBase & {
61
+ type: "run_finished";
62
+ stopReason: "success" | "max_turns" | "cancelled" | "timeout" | "error";
63
+ }) | (RuntimeEventBase & {
64
+ type: "run_error";
65
+ message: string;
66
+ code?: string;
67
+ });
68
+ /**
69
+ * What a *runtime adapter* implements — not what the upstream binary is capable
70
+ * of. A flag is only `true` when this adapter actually wires the behaviour; an
71
+ * unhonoured `true` would make callers pass options that are silently dropped.
72
+ */
73
+ export interface RuntimeCapabilities {
74
+ streaming: boolean;
75
+ sessions: boolean;
76
+ resume: boolean;
77
+ permissions: boolean;
78
+ tools: boolean;
79
+ models: boolean;
80
+ images: boolean;
81
+ usage: boolean;
82
+ cost: boolean;
83
+ sandbox: boolean;
84
+ }
85
+ /** The result of capability negotiation, returned by `AgentRuntime.describe()`. */
86
+ export interface RuntimeInfo {
87
+ name: string;
88
+ version?: string;
89
+ available: boolean;
90
+ capabilities: RuntimeCapabilities;
91
+ }
92
+ /** Caller-supplied context for {@link AgentRuntime.describe}. */
93
+ export interface RuntimeQuery {
94
+ /** Binary this runtime would launch, when the caller knows it. */
95
+ command?: string;
96
+ }
97
+ export interface AgentTask {
98
+ agentId: string;
99
+ systemPrompt: string;
100
+ prompt: string;
101
+ tools: string[];
102
+ workDir: string;
103
+ timeout: number;
104
+ signal?: AbortSignal;
105
+ onEvent?: (event: RuntimeEvent) => void;
106
+ /**
107
+ * Non-fatal diagnostics the runtime wants the caller to surface.
108
+ *
109
+ * A runtime runs under a TUI's alternate screen, where anything written
110
+ * straight to stderr is invisible — so a warning the user needs to see has to
111
+ * travel on a channel the caller can render. Optional, so a direct caller that
112
+ * supplies nothing keeps the old behaviour (the runtime falls back to stderr).
113
+ */
114
+ onWarning?: (message: string) => void;
115
+ /** Ask the runtime to run under a specific session id, if it supports sessions. */
116
+ sessionId?: string;
117
+ /** How to treat an existing session: continue it in place, or fork a copy. */
118
+ resume?: "continue" | "fork";
119
+ model?: string;
120
+ /**
121
+ * Command to launch, for runtimes that drive an external process (e.g. ACP).
122
+ * Generic — it names a binary, not a protocol — so runtimes that do not spawn
123
+ * anything ignore it.
124
+ */
125
+ command?: string;
126
+ /** Arguments for {@link command}. */
127
+ args?: string[];
128
+ }
129
+ export interface AgentOutput {
130
+ output: string;
131
+ exitCode: number;
132
+ sessionId?: string;
133
+ usage?: {
134
+ inputTokens?: number;
135
+ outputTokens?: number;
136
+ contextTokens?: number;
137
+ contextSize?: number;
138
+ };
139
+ costUsd?: number;
140
+ stopReason?: string;
141
+ }
142
+ export interface AgentRuntime {
143
+ readonly name: string;
144
+ describe(options?: RuntimeQuery): Promise<RuntimeInfo>;
145
+ execute(task: AgentTask): Promise<AgentOutput>;
146
+ }
@@ -0,0 +1,4 @@
1
+ /** Error name used for cooperative cancellation across the orchestrator. */
2
+ export declare const ABORT_ERROR_NAME = "AbortError";
3
+ export declare function abortError(message?: string): Error;
4
+ export declare function isAbortError(err: unknown): boolean;
@@ -0,0 +1,29 @@
1
+ import type { TurnRecord } from "../types/config";
2
+ /**
3
+ * How many earlier turns the leader is shown.
4
+ *
5
+ * Deliberately small. The point is to resolve an anaphoric answer — "Postgres",
6
+ * "the second one", "yes" — and that only ever needs the turn or two behind it.
7
+ * A long transcript would spend the leader's attention on work that is already
8
+ * done and written to the workspace, which it can read instead.
9
+ */
10
+ export declare const MAX_CONVERSATION_TURNS = 6;
11
+ /**
12
+ * Ceiling on the rendered block, in characters.
13
+ *
14
+ * Oldest turns are dropped to stay under it. A budget rather than a turn count
15
+ * alone because a requirement can be a pasted stack trace; six of those would
16
+ * otherwise bury the one turn that actually matters.
17
+ */
18
+ export declare const MAX_CONVERSATION_CHARS = 4000;
19
+ /**
20
+ * Render earlier turns into a block for the leader's prompt, or "" when there
21
+ * are none.
22
+ *
23
+ * Empty rather than a placeholder: the leader's template carries `{{…}}` for it,
24
+ * and an unresolved placeholder would be handed to the model as literal text.
25
+ *
26
+ * The newlines belong to the block, not to the template — a section that is not
27
+ * there must not leave its blank lines behind.
28
+ */
29
+ export declare function renderConversation(history: readonly TurnRecord[] | undefined): string;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * An error that will not be fixed by trying again.
3
+ *
4
+ * `withRetry` retries any error that is not an abort, which is the right default
5
+ * — a flaky connection, a transient non-zero exit, a slow turn are all worth a
6
+ * second attempt. But some failures are permanent properties of the setup: a
7
+ * binary that is not installed (ENOENT), one that is not executable (EACCES), or
8
+ * a capability the agent does not advertise (no `session/load`). Retrying those
9
+ * only stretches a doomed run by the whole backoff budget, so the sites that can
10
+ * detect them throw a `PermanentError` and the retry loop fails fast.
11
+ */
12
+ export declare class PermanentError extends Error {
13
+ constructor(message: string);
14
+ }
15
+ /** True when `err` was classified as non-retryable. */
16
+ export declare function isPermanent(err: unknown): boolean;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Extract every top-level JSON object found in `text`.
3
+ *
4
+ * Leader / architect agents are instructed to emit a bare JSON object, but real
5
+ * output frequently wraps it in prose or markdown fences. A regular expression
6
+ * cannot cope with nested braces or strings that contain `}` — this scans with
7
+ * brace and string awareness instead and simply skips spans that are not valid
8
+ * JSON, so a stray `{` in prose does not poison the whole parse.
9
+ */
10
+ export declare function extractJsonObjects(text: string): unknown[];
@@ -0,0 +1,17 @@
1
+ import type { LeaderDecision } from "../types/leader";
2
+ /**
3
+ * Read the leader's decision out of its raw output.
4
+ *
5
+ * The leader is asked for a bare JSON object, but a model will happily wrap it in
6
+ * prose or markdown fences, so every object in the text is considered in order
7
+ * and the first one that reads as a decision wins.
8
+ *
9
+ * `{"run": [...]}` with no `mode` is still accepted as a run: the field was added
10
+ * after the shape was, and a model that forgets it has named the agents it wants
11
+ * all the same.
12
+ *
13
+ * Returns `undefined` when nothing parses. That is deliberately *not* an error —
14
+ * the caller treats it as the leader having answered in prose, which is exactly
15
+ * what a reply looks like.
16
+ */
17
+ export declare function parseLeaderDecision(text: string): LeaderDecision | undefined;
@@ -0,0 +1 @@
1
+ export declare function renderPrompt(template: string, context: Record<string, string>, workDir: string): Promise<string>;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * What a resume or retry will actually do, in words.
3
+ *
4
+ * `config.resume` runs the agents it names and nothing else, which changes two
5
+ * things silently: an agent continues the ACP session it reported last time
6
+ * only when a session id is handed back for it, and a `dependsOn` that points
7
+ * outside the resumed set is pruned so the agent can run at all. Retrying one
8
+ * failed agent therefore re-runs a *different* pipeline from the one that
9
+ * failed, with nothing saying so — this is that sentence.
10
+ *
11
+ * The pruning rule mirrors `Orchestrator.selectAgents`: a dependency counts as
12
+ * dropped when it names a *configured* agent that is not being resumed. A
13
+ * dependency on an id that is not configured at all is left in place by core
14
+ * and breaks the stage ordering in every run equally — a config bug, not
15
+ * something a retry changes, so it is not reported here.
16
+ */
17
+ export interface ResumePlan {
18
+ /** Ids that will run, in the order the caller named them. */
19
+ agents: readonly string[];
20
+ /** Agent id → the session id to continue, for the agents that reported one. */
21
+ sessions?: Readonly<Record<string, string>>;
22
+ /** The team the ids were chosen from; supplies each agent's dependencies. */
23
+ team: readonly {
24
+ id: string;
25
+ dependsOn?: readonly string[];
26
+ }[];
27
+ }
28
+ /**
29
+ * One clause per agent — `dev starts fresh`, `reviewer continues its session` —
30
+ * followed by one clause per agent whose dependencies were pruned.
31
+ *
32
+ * Deliberately a description, not a plan: what actually runs stays core's
33
+ * decision (`selectAgents`), and this only reports it.
34
+ */
35
+ export declare function describeResumePlan({ agents, sessions, team }: ResumePlan): string;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Default backoff unit for {@link withRetry}.
3
+ *
4
+ * Exported so a caller that must budget for a whole retry sequence — see
5
+ * {@link retryBudgetMs} — sleeps the same amount the loop actually sleeps. A
6
+ * second literal `2000` elsewhere would silently drift from this one.
7
+ */
8
+ export declare const DEFAULT_RETRY_DELAY_MS = 2000;
9
+ /**
10
+ * Sleep, interrupted by cancellation.
11
+ *
12
+ * Without the signal, cancelling during backoff leaves the timer running for the
13
+ * rest of the delay. The retry loop already refuses to invoke `fn` again, so the
14
+ * work is safe — but the continuation lingers for up to `ms`, keeping a turn
15
+ * alive after the user asked it to stop.
16
+ */
17
+ export declare function sleep(ms: number, signal?: AbortSignal): Promise<void>;
18
+ export declare function withRetry<T>(fn: (attempt: number) => Promise<T>, retries: number, onRetry?: (failedAttempt: number, error: unknown) => void, delayMs?: number, signal?: AbortSignal, shouldRetry?: (error: unknown, attempt: number) => boolean): Promise<T>;
19
+ /**
20
+ * Wall-clock upper bound for one full retry sequence.
21
+ *
22
+ * `withRetry` allows `retries + 1` attempts and sleeps `delayMs * attempt`
23
+ * between them (`attempt` runs 1..retries), so the worst case is every attempt
24
+ * spending its whole per-attempt budget plus the sum of the backoffs.
25
+ *
26
+ * `perAttemptMs` is the *worst-case wall-clock time of one attempt*, not the
27
+ * nominal timeout the runtime is handed: an attempt can overrun its own timeout
28
+ * by whatever cleanup grace the runtime needs after the deadline (for ACP, the
29
+ * SIGTERM→SIGKILL grace — `KILL_ESCALATION_MS`). A caller that passes the bare
30
+ * timeout gets an "upper bound" that is not one, and the outer timer then races
31
+ * — and can beat — the final attempt.
32
+ *
33
+ * Kept beside the loop above on purpose: the loop is the only definition of this
34
+ * arithmetic, and the two would drift apart if they lived in separate files.
35
+ */
36
+ export declare function retryBudgetMs(perAttemptMs: number, retries: number, delayMs?: number): number;
@@ -0,0 +1 @@
1
+ export declare function withTimeout<T>(factory: (signal: AbortSignal) => Promise<T>, ms: number, label: string, signal?: AbortSignal): Promise<T>;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * One-line summaries of an agent's tool activity.
3
+ *
4
+ * A tool call or result is a glance, not a transcript: the stream pane wants a
5
+ * single scannable row per tool, and the raw payloads are frequently multi-line
6
+ * (a whole file, a command's output). These helpers reduce any payload to one
7
+ * truncated line and are deliberately total — a summary is diagnostics and must
8
+ * never be the reason a run fails.
9
+ */
10
+ /** One-line summary of a tool call's input. `input` is untrusted and may be circular. */
11
+ export declare function summarizeToolInput(input: unknown): string;
12
+ /** One-line summary of a tool result's output, which may be a string or a value. */
13
+ export declare function summarizeToolOutput(output: unknown): string;
@@ -0,0 +1,69 @@
1
+ /**
2
+ * One agent's finished output, as a downstream agent should see it.
3
+ *
4
+ * `agentId` and `name` are carried alongside the text so the block can address
5
+ * the producer by name — a bare paragraph with no attribution reads as the
6
+ * reader's own thought.
7
+ */
8
+ export interface UpstreamOutput {
9
+ agentId: string;
10
+ name: string;
11
+ output: string;
12
+ }
13
+ /**
14
+ * Ceiling on a single rendered entry, in characters.
15
+ *
16
+ * The entry is the whole bullet — its attribution line, its body, and the
17
+ * 2-space indent on every continuation line — not just the body. A budget that
18
+ * excluded its own framing would let the rendered entry exceed the constant,
19
+ * exactly as the block budget counts the heading and trailing newline.
20
+ *
21
+ * The ceiling is enforced by compacting the body, so it holds while the
22
+ * attribution line is small relative to the constant — the practical case. As
23
+ * the label grows, compaction has less body to work with, and past the point
24
+ * where it can keep nothing the entry is bounded by `label.length + markerLen`
25
+ * instead: there the label, not this constant, is what sets the size.
26
+ *
27
+ * An output past this is compacted rather than dropped: it is the one piece of
28
+ * upstream work that exists, and throwing it away would leave the downstream
29
+ * agent with nothing to build on.
30
+ */
31
+ export declare const MAX_UPSTREAM_ENTRY_CHARS = 600;
32
+ /**
33
+ * Ceiling on the whole rendered block, in characters.
34
+ *
35
+ * The block is what is injected into the prompt, so the heading and the
36
+ * trailing newline count against this — not just the entries. A budget that
37
+ * excluded its own framing would let the returned string exceed the constant.
38
+ *
39
+ * A budget rather than a count of agents because one agent's output can be a
40
+ * full document; a dozen of those would bury the requirement underneath them.
41
+ * Oldest entries are dropped to stay under it.
42
+ */
43
+ export declare const MAX_UPSTREAM_CHARS = 4000;
44
+ /**
45
+ * Render what earlier agents produced into a block for a downstream prompt, or
46
+ * "" when there is nothing.
47
+ *
48
+ * Empty rather than a placeholder: every built-in task template carries
49
+ * `{{upstreamOutputs}}`, and an unresolved placeholder would be handed to the
50
+ * model as literal text.
51
+ *
52
+ * The newlines belong to the block, not to the template — a section that is not
53
+ * there must not leave its blank lines behind. That is why the leading and
54
+ * trailing `\n` live here, and why the template places the placeholder on its
55
+ * own line with no section heading: an empty value then leaves nothing dangling.
56
+ *
57
+ * Oldest-first is the order worth reading, so the block is built forwards and
58
+ * trimmed from the front. The newest output is the most relevant to the stage
59
+ * about to run, so it is the last thing that may be dropped.
60
+ *
61
+ * Known limitation, recorded rather than fixed: `renderPrompt` substitutes
62
+ * `{{file:…}}` and then every context key, and `AgentRunner` layers
63
+ * `agentScope`/`agentPlan` *after* the snapshot. So if an upstream output's body
64
+ * literally contains `{{agentScope}}`, the downstream agent's own scope will
65
+ * replace it. This is a pre-existing property of the substitution order that
66
+ * `{{conversation}}` shares; cleaning agent output would corrupt legitimate text,
67
+ * so it is out of scope here.
68
+ */
69
+ export declare function renderUpstreamOutputs(outputs: readonly UpstreamOutput[]): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@multiclawcli/core",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -21,17 +21,17 @@
21
21
  "access": "public"
22
22
  },
23
23
  "devDependencies": {
24
- "@types/node": "^26.6.4",
25
- "typescript": "^5.4.0",
26
- "vitest": "^4.1.11"
24
+ "@types/node": "^26.6.5",
25
+ "typescript": "^7.0.2",
26
+ "vitest": "^5.0.4"
27
27
  },
28
28
  "dependencies": {
29
- "@agentclientprotocol/claude-agent-acp": "^0.89.1",
29
+ "@agentclientprotocol/claude-agent-acp": "^0.93.0",
30
30
  "@agentclientprotocol/codex-acp": "^2.2.2",
31
- "@agentclientprotocol/sdk": "^1.8.0"
31
+ "@agentclientprotocol/sdk": "^1.9.0"
32
32
  },
33
33
  "scripts": {
34
- "build": "tsup",
34
+ "build": "tsup && tsc -p tsconfig.build.json --emitDeclarationOnly --outDir dist",
35
35
  "typecheck": "tsc -p tsconfig.json --noEmit",
36
36
  "test": "vitest run",
37
37
  "test:watch": "vitest",