@multiclawcli/core 0.5.0 → 0.6.0
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.d.ts +2 -0
- package/dist/index.js +1182 -277
- package/dist/orchestrator/AgentRunner.d.ts +4 -2
- package/dist/orchestrator/AgentSessionManager.d.ts +40 -0
- package/dist/orchestrator/Orchestrator.d.ts +5 -1
- package/dist/runtime/AcpRuntime.d.ts +4 -10
- package/dist/types/config.d.ts +35 -0
- package/dist/types/leader.d.ts +2 -0
- package/dist/types/runtime.d.ts +112 -1
- package/dist/utils/timeout.d.ts +3 -1
- package/package.json +3 -3
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import type { EventBus } from "../event/EventBus";
|
|
2
2
|
import type { AgentDefinition, AgentResult } from "../types/agent";
|
|
3
3
|
import type { MultiClawConfig } from "../types/config";
|
|
4
|
+
import type { AgentSessionManager } from "./AgentSessionManager";
|
|
4
5
|
import type { ContextManager } from "./ContextManager";
|
|
5
6
|
export declare class AgentRunner {
|
|
6
7
|
private eventBus;
|
|
7
8
|
private contextManager;
|
|
8
9
|
private globalWorkDir;
|
|
9
10
|
private config;
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
private sessionManager?;
|
|
12
|
+
constructor(eventBus: EventBus, contextManager: ContextManager, globalWorkDir: string, config: MultiClawConfig, sessionManager?: AgentSessionManager | undefined);
|
|
13
|
+
run(agent: AgentDefinition, signal?: AbortSignal, instruction?: string): Promise<AgentResult>;
|
|
12
14
|
/**
|
|
13
15
|
* Publish one chunk of agent output.
|
|
14
16
|
*
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { AgentOutput, AgentRuntime, AgentTask } from "../types/runtime";
|
|
2
|
+
/** What one agent did with a mid-turn message. */
|
|
3
|
+
export interface SupplementReport {
|
|
4
|
+
agentId: string;
|
|
5
|
+
/**
|
|
6
|
+
* `injected` landed in the running turn; `idle` had no turn to land in;
|
|
7
|
+
* `unsupported` cannot take mid-turn input at all; `error` failed.
|
|
8
|
+
*/
|
|
9
|
+
status: "injected" | "idle" | "unsupported" | "error";
|
|
10
|
+
error?: string;
|
|
11
|
+
}
|
|
12
|
+
/** Owns reusable agent sessions across independent orchestration turns. */
|
|
13
|
+
export declare class AgentSessionManager {
|
|
14
|
+
private entries;
|
|
15
|
+
private resetting?;
|
|
16
|
+
private closing?;
|
|
17
|
+
private closed;
|
|
18
|
+
execute(runtime: AgentRuntime, task: AgentTask): Promise<AgentOutput>;
|
|
19
|
+
/**
|
|
20
|
+
* Deliver a message into every session that is running a turn right now.
|
|
21
|
+
*
|
|
22
|
+
* Only `running` sessions are candidates, and that is the whole safety
|
|
23
|
+
* argument: a session between turns has nothing to inject into, and asking a
|
|
24
|
+
* provider to steer an idle session makes it *start* a turn that this manager
|
|
25
|
+
* never scheduled — one whose output belongs to no turn and which nothing
|
|
26
|
+
* would ever settle. Sessions that are idle are therefore not reported as
|
|
27
|
+
* failures but simply absent, and the caller sees an empty list.
|
|
28
|
+
*
|
|
29
|
+
* One agent failing never stops the others: each report stands alone, because
|
|
30
|
+
* the caller has to be able to say "architect took it, qa did not" rather than
|
|
31
|
+
* "steering failed".
|
|
32
|
+
*/
|
|
33
|
+
supplement(text: string): Promise<SupplementReport[]>;
|
|
34
|
+
/** Closes all sessions and permits a fresh conversation after cleanup completes. */
|
|
35
|
+
reset(): Promise<void>;
|
|
36
|
+
/** Terminal, idempotent cleanup barrier. No new executions may start. */
|
|
37
|
+
close(): Promise<void>;
|
|
38
|
+
private drain;
|
|
39
|
+
private closeEntry;
|
|
40
|
+
}
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import { EventBus } from "../event/EventBus";
|
|
2
2
|
import type { MultiClawConfig, OrchestrationPreview, OrchestratorResult } from "../types/config";
|
|
3
|
+
import type { AgentSessionManager } from "./AgentSessionManager";
|
|
3
4
|
export declare class Orchestrator {
|
|
4
5
|
private config;
|
|
5
6
|
readonly eventBus: EventBus;
|
|
6
7
|
private contextManager;
|
|
7
8
|
private runner;
|
|
8
9
|
private workDir;
|
|
9
|
-
constructor(config: MultiClawConfig);
|
|
10
|
+
constructor(config: MultiClawConfig, sessions?: AgentSessionManager);
|
|
10
11
|
preview(options?: {
|
|
11
12
|
signal?: AbortSignal;
|
|
12
13
|
}): Promise<OrchestrationPreview>;
|
|
13
14
|
run(options?: {
|
|
14
15
|
signal?: AbortSignal;
|
|
15
16
|
}): Promise<OrchestratorResult>;
|
|
17
|
+
private reviewPolicyError;
|
|
18
|
+
private reviewDelivery;
|
|
16
19
|
private stageInfos;
|
|
17
20
|
/**
|
|
18
21
|
* Order the agents into stages, degrading to declaration order if the graph is
|
|
@@ -98,6 +101,7 @@ export declare class Orchestrator {
|
|
|
98
101
|
*/
|
|
99
102
|
private finishWithMessage;
|
|
100
103
|
private warn;
|
|
104
|
+
private finishCancelled;
|
|
101
105
|
/**
|
|
102
106
|
* Close out a turn that scheduled no agents.
|
|
103
107
|
*
|
|
@@ -1,17 +1,11 @@
|
|
|
1
|
-
import type { AgentOutput, AgentTask, RuntimeInfo, RuntimeQuery } from "../types/runtime";
|
|
1
|
+
import type { AgentOutput, AgentTask, RuntimeInfo, RuntimeQuery, RuntimeSession, RuntimeSessionOptions } from "../types/runtime";
|
|
2
2
|
import { RuntimeBase } from "./RuntimeBase";
|
|
3
|
-
/**
|
|
4
|
-
* Grace period between SIGTERM and SIGKILL when an agent will not stop.
|
|
5
|
-
*
|
|
6
|
-
* This is time a single attempt can still consume *after* its `timeout`: the
|
|
7
|
-
* inner timeout fires a SIGTERM, and a child that ignores it is not reaped until
|
|
8
|
-
* this grace elapses — the reject itself lands on the child's `close`. A caller
|
|
9
|
-
* that budgets for a whole retry sequence must therefore add this to the
|
|
10
|
-
* per-attempt timeout, or its "upper bound" is not one (see `retryBudgetMs`).
|
|
11
|
-
*/
|
|
3
|
+
/** Grace period between SIGTERM and SIGKILL during process cleanup. */
|
|
12
4
|
export declare const KILL_ESCALATION_MS = 3000;
|
|
5
|
+
export declare function validateRuntimeQuestionContent(schema: unknown, content: unknown): content is Record<string, string | number | boolean | string[]>;
|
|
13
6
|
export declare class AcpRuntime extends RuntimeBase {
|
|
14
7
|
readonly name = "acp";
|
|
15
8
|
describe(options?: RuntimeQuery): Promise<RuntimeInfo>;
|
|
16
9
|
execute(task: AgentTask): Promise<AgentOutput>;
|
|
10
|
+
openSession(options: RuntimeSessionOptions): Promise<RuntimeSession>;
|
|
17
11
|
}
|
package/dist/types/config.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AgentDefinition } from "./agent";
|
|
2
2
|
import type { LeaderMessage } from "./leader";
|
|
3
|
+
import type { RuntimePermissionHandler, RuntimeQuestionHandler } from "./runtime";
|
|
3
4
|
/**
|
|
4
5
|
* One finished turn of a session, as dialogue the leader can read back.
|
|
5
6
|
*
|
|
@@ -14,6 +15,26 @@ export interface TurnRecord {
|
|
|
14
15
|
/** How the turn ended in words, when it ended that way. */
|
|
15
16
|
reply?: LeaderMessage;
|
|
16
17
|
}
|
|
18
|
+
export interface ReviewPolicy {
|
|
19
|
+
/** Configured agent reserved for final review, outside the normal pipeline. */
|
|
20
|
+
reviewer: string;
|
|
21
|
+
/** Configured workers allowed to repair, in dependency order. */
|
|
22
|
+
repairAgents: string[];
|
|
23
|
+
/** Maximum repair passes (0–5); defaults to 1. Review runs once more after each pass. */
|
|
24
|
+
maxRepairRounds?: number;
|
|
25
|
+
}
|
|
26
|
+
export interface ReviewVerdict {
|
|
27
|
+
verdict: "approved" | "changes_required";
|
|
28
|
+
summary: string;
|
|
29
|
+
issues: string[];
|
|
30
|
+
}
|
|
31
|
+
export interface AcceptanceResult {
|
|
32
|
+
status: "approved" | "changes_required" | "failed" | "cancelled";
|
|
33
|
+
reviewer: string;
|
|
34
|
+
repairRounds: number;
|
|
35
|
+
reviews: ReviewVerdict[];
|
|
36
|
+
reason?: string;
|
|
37
|
+
}
|
|
17
38
|
export interface MultiClawConfig {
|
|
18
39
|
name: string;
|
|
19
40
|
workDir: string;
|
|
@@ -48,6 +69,17 @@ export interface MultiClawConfig {
|
|
|
48
69
|
continueOnError?: boolean;
|
|
49
70
|
maxConcurrency?: number;
|
|
50
71
|
useLeader?: boolean;
|
|
72
|
+
/** Opt-in delivery gate. Execution success alone does not satisfy this policy. */
|
|
73
|
+
review?: ReviewPolicy;
|
|
74
|
+
/** Caller-owned live approvals. Ask mode without a handler fails closed. */
|
|
75
|
+
permissions?: {
|
|
76
|
+
mode: "auto" | "ask" | "deny";
|
|
77
|
+
onRequest?: RuntimePermissionHandler;
|
|
78
|
+
};
|
|
79
|
+
/** Caller-owned structured questions; headless callers cancel explicitly. */
|
|
80
|
+
questions?: {
|
|
81
|
+
onRequest: RuntimeQuestionHandler;
|
|
82
|
+
};
|
|
51
83
|
/**
|
|
52
84
|
* Project-level defaults applied to every agent that does not override them.
|
|
53
85
|
* Keeps generated configs readable: one line instead of 12 repeated parameters.
|
|
@@ -77,7 +109,10 @@ export interface DeliverableSummary {
|
|
|
77
109
|
}
|
|
78
110
|
export interface OrchestratorResult {
|
|
79
111
|
name: string;
|
|
112
|
+
/** With review enabled, true only when execution and delivery acceptance both succeed. */
|
|
80
113
|
success: boolean;
|
|
114
|
+
/** Present only for a review-gated pipeline, never for a leader reply/ask. */
|
|
115
|
+
acceptance?: AcceptanceResult;
|
|
81
116
|
totalDuration: number;
|
|
82
117
|
agentResults: import("./agent").AgentResult[];
|
|
83
118
|
startTime: string;
|
package/dist/types/leader.d.ts
CHANGED
|
@@ -10,6 +10,8 @@ export type LeaderMode = "run" | "reply" | "ask";
|
|
|
10
10
|
export type LeaderDecision = {
|
|
11
11
|
mode: "run";
|
|
12
12
|
run: string[];
|
|
13
|
+
/** Complete task combining the original goal with clarification constraints. */
|
|
14
|
+
requirement?: string;
|
|
13
15
|
/**
|
|
14
16
|
* Why these agents, in the leader's own words.
|
|
15
17
|
*
|
package/dist/types/runtime.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Everything a runtime emits about a run carries the same envelope. `seq` is a
|
|
3
3
|
* per-run monotonic counter so a consumer can order events without trusting the
|
|
4
|
-
* clock; `runId` is fresh for every `execute()` call.
|
|
4
|
+
* clock; `runId` is fresh for every `execute()` call or session `prompt()` turn.
|
|
5
5
|
*/
|
|
6
6
|
export interface RuntimeEventBase {
|
|
7
7
|
runId: string;
|
|
@@ -94,6 +94,38 @@ export interface RuntimeQuery {
|
|
|
94
94
|
/** Binary this runtime would launch, when the caller knows it. */
|
|
95
95
|
command?: string;
|
|
96
96
|
}
|
|
97
|
+
export interface RuntimePermissionRequest extends RuntimeEventBase {
|
|
98
|
+
requestId: string;
|
|
99
|
+
sessionId: string;
|
|
100
|
+
toolCallId: string;
|
|
101
|
+
title: string;
|
|
102
|
+
toolName?: string;
|
|
103
|
+
kind?: string;
|
|
104
|
+
input?: unknown;
|
|
105
|
+
/** Only choices permitted by the configured tools policy are offered. */
|
|
106
|
+
options: Array<{
|
|
107
|
+
optionId: string;
|
|
108
|
+
name: string;
|
|
109
|
+
kind: "allow_once" | "allow_always" | "reject_once" | "reject_always";
|
|
110
|
+
}>;
|
|
111
|
+
}
|
|
112
|
+
/** Return an offered option id, or undefined to cancel/deny. Never grant outside the tools policy. */
|
|
113
|
+
export type RuntimePermissionHandler = (request: RuntimePermissionRequest, signal: AbortSignal) => Promise<string | undefined>;
|
|
114
|
+
export interface RuntimeQuestionRequest extends RuntimeEventBase {
|
|
115
|
+
requestId: string;
|
|
116
|
+
sessionId: string;
|
|
117
|
+
toolCallId?: string;
|
|
118
|
+
message: string;
|
|
119
|
+
requestedSchema: import("@agentclientprotocol/sdk").ElicitationSchema;
|
|
120
|
+
}
|
|
121
|
+
export type RuntimeQuestionResponse = {
|
|
122
|
+
action: "accept";
|
|
123
|
+
content: Record<string, string | number | boolean | string[]>;
|
|
124
|
+
} | {
|
|
125
|
+
action: "decline" | "cancel";
|
|
126
|
+
};
|
|
127
|
+
/** Structured form input only; the signal aborts when the requesting turn ends. */
|
|
128
|
+
export type RuntimeQuestionHandler = (request: RuntimeQuestionRequest, signal: AbortSignal) => Promise<RuntimeQuestionResponse>;
|
|
97
129
|
export interface AgentTask {
|
|
98
130
|
agentId: string;
|
|
99
131
|
systemPrompt: string;
|
|
@@ -103,6 +135,10 @@ export interface AgentTask {
|
|
|
103
135
|
timeout: number;
|
|
104
136
|
signal?: AbortSignal;
|
|
105
137
|
onEvent?: (event: RuntimeEvent) => void;
|
|
138
|
+
/** Automatic tools policy by default; ask without a handler denies. */
|
|
139
|
+
permissionMode?: "auto" | "ask" | "deny";
|
|
140
|
+
onPermission?: RuntimePermissionHandler;
|
|
141
|
+
onQuestion?: RuntimeQuestionHandler;
|
|
106
142
|
/**
|
|
107
143
|
* Non-fatal diagnostics the runtime wants the caller to surface.
|
|
108
144
|
*
|
|
@@ -139,8 +175,83 @@ export interface AgentOutput {
|
|
|
139
175
|
costUsd?: number;
|
|
140
176
|
stopReason?: string;
|
|
141
177
|
}
|
|
178
|
+
/** Immutable agent configuration shared by all turns in an open session. */
|
|
179
|
+
export type RuntimeSessionOptions = Omit<AgentTask, "prompt" | "onEvent" | "signal" | "timeout" | "permissionMode" | "onPermission" | "onQuestion"> & {
|
|
180
|
+
/** Immutable negotiated form capability; callbacks remain turn-local. */
|
|
181
|
+
questionForms?: boolean;
|
|
182
|
+
/** Lifetime cancellation, including startup and idle time; closes the session. */
|
|
183
|
+
signal?: AbortSignal;
|
|
184
|
+
/** Startup deadline and default per-turn timeout in ms (ACP defaults to 600,000). */
|
|
185
|
+
timeout?: number;
|
|
186
|
+
};
|
|
187
|
+
export interface RuntimePromptOptions {
|
|
188
|
+
/** Cancels only this turn. A cooperative provider leaves the session reusable. */
|
|
189
|
+
signal?: AbortSignal;
|
|
190
|
+
/** Overrides the session's default per-turn deadline. */
|
|
191
|
+
timeout?: number;
|
|
192
|
+
/** Turn-local events, with a fresh runId and sequence counter. */
|
|
193
|
+
onEvent?: (event: RuntimeEvent) => void;
|
|
194
|
+
/** Waiting uses the existing turn timeout; it does not pause the deadline. */
|
|
195
|
+
permissionMode?: "auto" | "ask" | "deny";
|
|
196
|
+
onPermission?: RuntimePermissionHandler;
|
|
197
|
+
onQuestion?: RuntimeQuestionHandler;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* What a mid-turn message did.
|
|
201
|
+
*
|
|
202
|
+
* `injected` means the running turn took it — the agent will see it before it
|
|
203
|
+
* finishes. `idle` means no turn was running, so nothing was applied and the
|
|
204
|
+
* caller still owns the text; deciding to queue it is the caller's job, not the
|
|
205
|
+
* runtime's.
|
|
206
|
+
*/
|
|
207
|
+
export type RuntimeSupplementResult = {
|
|
208
|
+
status: "injected";
|
|
209
|
+
} | {
|
|
210
|
+
status: "idle";
|
|
211
|
+
};
|
|
212
|
+
/** Caller-owned live session. Always await close() in a finally block. */
|
|
213
|
+
export interface RuntimeSession {
|
|
214
|
+
readonly sessionId: string;
|
|
215
|
+
readonly state: "idle" | "running" | "cancelling" | "closing" | "closed";
|
|
216
|
+
/**
|
|
217
|
+
* What this session can do beyond a single turn.
|
|
218
|
+
*
|
|
219
|
+
* `supplement` is negotiated with the provider rather than assumed: it is true
|
|
220
|
+
* only when the agent advertised mid-turn input during `initialize` (ACP
|
|
221
|
+
* `_meta.steering.supported`), so a bridge that does not implement it is
|
|
222
|
+
* reported as unable instead of being sent a method it will ignore.
|
|
223
|
+
*/
|
|
224
|
+
readonly capabilities: {
|
|
225
|
+
continuation: boolean;
|
|
226
|
+
supplement: boolean;
|
|
227
|
+
};
|
|
228
|
+
/** Rejects overlapping turns; it does not implicitly queue or inject input. */
|
|
229
|
+
prompt(text: string, options?: RuntimePromptOptions): Promise<AgentOutput>;
|
|
230
|
+
/**
|
|
231
|
+
* Delivers a message into the turn that is running *now*, when
|
|
232
|
+
* {@link RuntimeSession.capabilities} allows it.
|
|
233
|
+
*
|
|
234
|
+
* Only meaningful mid-turn. An idle session resolves `idle` and must not start
|
|
235
|
+
* a turn from it: `prompt` is how a turn starts, and a turn the caller never
|
|
236
|
+
* started has no owner to settle it, cancel it or show its output. Rejects on
|
|
237
|
+
* provider errors — "did it land?" must never be a guess.
|
|
238
|
+
*/
|
|
239
|
+
supplement?(text: string, options?: {
|
|
240
|
+
signal?: AbortSignal;
|
|
241
|
+
}): Promise<RuntimeSupplementResult>;
|
|
242
|
+
/**
|
|
243
|
+
* Idempotently cancels the current turn and waits for its terminal response.
|
|
244
|
+
* ACP closes uncooperative providers after a grace period; cleanup failure rejects.
|
|
245
|
+
* Idle cancellation is a no-op. The prompt may reject independently.
|
|
246
|
+
*/
|
|
247
|
+
cancelTurn(): Promise<void>;
|
|
248
|
+
/** Idempotent cleanup barrier; rejects if process cleanup cannot be acknowledged. */
|
|
249
|
+
close(): Promise<void>;
|
|
250
|
+
}
|
|
142
251
|
export interface AgentRuntime {
|
|
143
252
|
readonly name: string;
|
|
144
253
|
describe(options?: RuntimeQuery): Promise<RuntimeInfo>;
|
|
145
254
|
execute(task: AgentTask): Promise<AgentOutput>;
|
|
255
|
+
/** Optional live-session API; existing execute-only runtimes remain compatible. */
|
|
256
|
+
openSession?(options: RuntimeSessionOptions): Promise<RuntimeSession>;
|
|
146
257
|
}
|
package/dist/utils/timeout.d.ts
CHANGED
|
@@ -1 +1,3 @@
|
|
|
1
|
-
export declare function withTimeout<T>(factory: (signal: AbortSignal) => Promise<T>, ms: number, label: string, signal?: AbortSignal
|
|
1
|
+
export declare function withTimeout<T>(factory: (signal: AbortSignal) => Promise<T>, ms: number, label: string, signal?: AbortSignal, options?: {
|
|
2
|
+
waitForCleanup?: boolean;
|
|
3
|
+
}): Promise<T>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@multiclawcli/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
"vitest": "^5.0.4"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@agentclientprotocol/claude-agent-acp": "^0.
|
|
29
|
+
"@agentclientprotocol/claude-agent-acp": "^0.95.0",
|
|
30
30
|
"@agentclientprotocol/codex-acp": "^2.2.2",
|
|
31
|
-
"@agentclientprotocol/sdk": "^
|
|
31
|
+
"@agentclientprotocol/sdk": "^2.0.2"
|
|
32
32
|
},
|
|
33
33
|
"scripts": {
|
|
34
34
|
"build": "tsup && tsc -p tsconfig.build.json --emitDeclarationOnly --outDir dist",
|