@ziggs-ai/agent-sdk 0.38.16 → 0.39.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/CHANGELOG.md +11 -2
- package/README.md +37 -0
- package/dist/Agent.d.ts +16 -0
- package/dist/Agent.js +74 -4
- package/dist/brain/ClaudeBrain.d.ts +7 -1
- package/dist/brain/ClaudeBrain.js +24 -1
- package/dist/brain/ZiggsBrain.d.ts +9 -0
- package/dist/brain/toolManagerToSdkMcp.js +1 -0
- package/dist/checkout/box/server.d.ts +1 -0
- package/dist/checkout/box/server.js +581 -0
- package/dist/checkout/boxClient.d.ts +86 -0
- package/dist/checkout/boxClient.js +320 -0
- package/dist/checkout/index.d.ts +10 -0
- package/dist/checkout/index.js +12 -0
- package/dist/checkout/intake.d.ts +15 -0
- package/dist/checkout/intake.js +65 -0
- package/dist/checkout/open.d.ts +49 -0
- package/dist/checkout/open.js +190 -0
- package/dist/checkout/tools.d.ts +24 -0
- package/dist/checkout/tools.js +65 -0
- package/dist/checkout/types.d.ts +88 -0
- package/dist/checkout/types.js +12 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -0
- package/dist/platform/ZiggsEffectHandler.js +2 -0
- package/dist/tools/needs.d.ts +13 -6
- package/dist/tools/needs.js +18 -1
- package/dist/tools/toolCreds.d.ts +5 -1
- package/dist/tools/toolCreds.js +7 -1
- package/dist/types.d.ts +2 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## 0.39.0
|
|
4
|
+
|
|
5
|
+
- A manifest may declare `needs: [{ checkout: 'git' }]`. The host provides a throwaway box per wake (`checkout` option, a `CheckoutProvider`); the task names the repository as owner/name and may name a base branch; the brain gets `checkout_read`, `checkout_write`, `checkout_run` and `checkout_push`; the change is pushed to `agent/<task id>` for a pull request. Without a GitHub connection grant on the hire the task is refused by name, like a connection need. The box holds no credential: its git traffic is relayed by the host through the backend git door under the hire's grant. The box server ships as `dist/checkout/box/server.js` (`checkoutBoxServerPath()`).
|
|
6
|
+
|
|
7
|
+
- Every repository subprocess, including Git filters, runs with the same box confinement. File reads preserve the beginning, report actual byte counts and truncation, and build output is bounded while it arrives.
|
|
8
|
+
- Checkout providers may supply private checkpoint storage. Without it, later wakes resume the pushed task branch. Mutating tools and wake cleanup save staged, unstaged and untracked work; later wakes restore it without making or pushing a commit. Storage keys isolate agents, hire lanes, tasks, repositories and base branches. Save failures return warnings without replacing command results or failing completed wakes; task-read and checkout-open errors leave task status unchanged.
|
|
9
|
+
- Task intake preserves dotted repository names and reads the explicit base branch from either title or description. Relay shutdown cancels outstanding requests and cleanup stops the box even when checkpoint storage fails.
|
|
10
|
+
|
|
11
|
+
## 0.38.17
|
|
12
|
+
|
|
13
|
+
- Hosted-agent agreement tools carry the shorter shared terms text from api-client 0.37.20.
|
|
4
14
|
|
|
5
15
|
## 0.38.16
|
|
6
16
|
|
|
@@ -29,7 +39,6 @@
|
|
|
29
39
|
- `createMcpAgent` returns `agent`; the selected decision engine is `agent.brain`.
|
|
30
40
|
- Selecting `claudeBrain()` uses the five-minute Claude wake timeout.
|
|
31
41
|
|
|
32
|
-
|
|
33
42
|
## Unreleased
|
|
34
43
|
|
|
35
44
|
### Changed (breaking)
|
package/README.md
CHANGED
|
@@ -120,6 +120,43 @@ capability bundles include `DISCOVERY_TOOLS`, `CONTEXT_TOOLS`, `CONNECTION_TOOLS
|
|
|
120
120
|
Use `defineTool` for your own tools. Declared `needs` provide their access tools
|
|
121
121
|
and enforce missing grants at task start.
|
|
122
122
|
|
|
123
|
+
A need can also be a checkout: `needs: [{ checkout: 'git' }]`. The host
|
|
124
|
+
provides a throwaway box per wake (the `checkout` option, a `CheckoutProvider`),
|
|
125
|
+
the task names the repository as owner/name and may name a base branch, and
|
|
126
|
+
the agent gets `checkout_read`, `checkout_write`, `checkout_run` and
|
|
127
|
+
`checkout_push`. The change is pushed to `agent/<task id>` for a pull request;
|
|
128
|
+
the default branch is never pushed. Without a GitHub connection grant the task
|
|
129
|
+
is refused by name. The box holds no credential: its git traffic is relayed by
|
|
130
|
+
the host through the backend under the grant. The box server ships as
|
|
131
|
+
`dist/checkout/box/server.js` (`checkoutBoxServerPath()`) and needs only Node
|
|
132
|
+
and git.
|
|
133
|
+
|
|
134
|
+
A provider may supply `checkpoints: CheckoutCheckpointStore` with `load(key)`
|
|
135
|
+
and atomic `save(key, bytes)` methods. Without storage, later wakes resume the
|
|
136
|
+
pushed task branch; tool results explicitly warn that unpushed work is temporary.
|
|
137
|
+
Checkpoints are private host storage, never a Git push. When configured, the SDK
|
|
138
|
+
saves after mutating tools and during wake cleanup. Keys include agent, hire lane,
|
|
139
|
+
task, repository and base branch. Archives are capped at 64 MiB compressed and
|
|
140
|
+
omit `node_modules` and `.npm`; reinstall dependencies after a restore.
|
|
141
|
+
|
|
142
|
+
A failed save preserves the tool's original result and adds `checkpointWarning`.
|
|
143
|
+
Further saves are disabled for that wake, so an oversized repository or storage
|
|
144
|
+
outage does not repeatedly delay every tool or fail an already completed wake.
|
|
145
|
+
Only the last successful checkpoint or pushed branch is recoverable. Loading a
|
|
146
|
+
configured store must succeed before work proceeds, to avoid overwriting saved
|
|
147
|
+
work. A task-read or checkout-open error leaves task status unchanged.
|
|
148
|
+
|
|
149
|
+
The reference Mac host retains local snapshots for seven days. The Fargate host
|
|
150
|
+
has no checkpoint storage by default and this rollout provisions no S3 snapshot
|
|
151
|
+
bucket. A production host can explicitly supply a store after establishing its
|
|
152
|
+
own retention policy and dedicated access controls.
|
|
153
|
+
|
|
154
|
+
All subprocesses, including Git and archive extraction, use `BOX_RUN_AS` or
|
|
155
|
+
`BOX_EXEC_PREFIX`. Providers must enforce outbound isolation independently of
|
|
156
|
+
proxy environment variables. The reference fleet uses subnets without an internet
|
|
157
|
+
route and a package proxy; the local host allows only the Git relay and that proxy.
|
|
158
|
+
|
|
159
|
+
|
|
123
160
|
Configure an alternate backend before constructing an agent:
|
|
124
161
|
|
|
125
162
|
```ts
|
package/dist/Agent.d.ts
CHANGED
|
@@ -30,6 +30,7 @@ import { ToolManager } from './tools/ToolManager.js';
|
|
|
30
30
|
import { type ToolDefinition } from './tools/defineTool.js';
|
|
31
31
|
import { OpenAIAdapter } from './adapters/OpenAIAdapter.js';
|
|
32
32
|
import { type AgentNeed } from './tools/needs.js';
|
|
33
|
+
import type { CheckoutProvider } from './checkout/types.js';
|
|
33
34
|
/**
|
|
34
35
|
* Given a normalized event, return the sessionId for this agent. Return
|
|
35
36
|
* `null` to skip processing. Lets each Agent define what "session" means —
|
|
@@ -89,6 +90,12 @@ export interface AgentOptions {
|
|
|
89
90
|
* missing access named, before the tick spends anything.
|
|
90
91
|
*/
|
|
91
92
|
needs?: AgentNeed[];
|
|
93
|
+
/**
|
|
94
|
+
* Who provides the checkout a `{ checkout: 'git' }` need asks for: the host.
|
|
95
|
+
* A fleet starts a container per wake, a lab a confined local process. Left
|
|
96
|
+
* unset, a task that needs a checkout is refused naming this host.
|
|
97
|
+
*/
|
|
98
|
+
checkout?: CheckoutProvider;
|
|
92
99
|
taskTools?: 'all' | 'none' | string[];
|
|
93
100
|
/**
|
|
94
101
|
* Brain selection — preferred over legacy `cognition`.
|
|
@@ -265,6 +272,8 @@ export declare class Agent {
|
|
|
265
272
|
private readonly outboundMessages;
|
|
266
273
|
private readonly confirmedPlanVersions;
|
|
267
274
|
private grantProviderCache;
|
|
275
|
+
/** The checkout box each lane holds while its wake runs. */
|
|
276
|
+
private checkoutByLane;
|
|
268
277
|
constructor(options?: AgentOptions);
|
|
269
278
|
private _validate;
|
|
270
279
|
private _setup;
|
|
@@ -383,6 +392,13 @@ export declare class Agent {
|
|
|
383
392
|
*/
|
|
384
393
|
private canAskForAccess;
|
|
385
394
|
private _processEvent;
|
|
395
|
+
/**
|
|
396
|
+
* The checkout a wake needs, opened before the brain runs. Null when this
|
|
397
|
+
* wake needs none; `'refused'` when the task was failed here by name.
|
|
398
|
+
*/
|
|
399
|
+
private _openCheckoutForWake;
|
|
400
|
+
/** Destroy the lane's box, if it still holds one, and say what it cost. */
|
|
401
|
+
private _closeCheckoutForWake;
|
|
386
402
|
/**
|
|
387
403
|
* Re-run one recorded wake against THIS agent, and nothing else.
|
|
388
404
|
*
|
package/dist/Agent.js
CHANGED
|
@@ -33,7 +33,10 @@ import { marketplaceViewTool } from './platform/tools/tier2/marketplaceTools.js'
|
|
|
33
33
|
import { OpenAIAdapter } from './adapters/OpenAIAdapter.js';
|
|
34
34
|
import { AnthropicAdapter } from './adapters/AnthropicAdapter.js';
|
|
35
35
|
import { ArtifactsClient, ConnectionsClient, ContextReadClient, TelemetryClient, sendChatMessage, presentSendResult, } from '@ziggs-ai/api-client';
|
|
36
|
-
import { normalizeNeeds, unmetNeeds, unmetNeedsMessage, } from './tools/needs.js';
|
|
36
|
+
import { isCheckoutNeed, normalizeNeeds, unmetNeeds, unmetNeedsMessage, } from './tools/needs.js';
|
|
37
|
+
import { openCheckout, closeCheckout } from './checkout/open.js';
|
|
38
|
+
import { resolveBackendUrl } from './shared/backendUrl.js';
|
|
39
|
+
import { laneUnderAgreement } from '@ziggs-ai/api-client';
|
|
37
40
|
import { TaskService } from './platform/tasks/TaskService.js';
|
|
38
41
|
import { AgreementService } from './platform/agreements/AgreementService.js';
|
|
39
42
|
import { INBOX_STOP_TIMEOUT_MS, runInboxLoop } from './platform/inboxLoop.js';
|
|
@@ -69,6 +72,11 @@ export function laneSessionIdForTask(task) {
|
|
|
69
72
|
}
|
|
70
73
|
return 'default';
|
|
71
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* A wake that holds a checkout installs, builds and tests a repository, which
|
|
77
|
+
* takes longer than a conversation turn. Its watchdog is at least this.
|
|
78
|
+
*/
|
|
79
|
+
const CHECKOUT_TICK_TIMEOUT_MS = Number(process.env.CLAUDE_SDK_CHECKOUT_TICK_TIMEOUT_MS ?? 40 * 60_000);
|
|
72
80
|
function createToolManager(options) {
|
|
73
81
|
const toolManager = new ToolManager();
|
|
74
82
|
const userTools = options.tools || options.services?.tools || [];
|
|
@@ -217,6 +225,8 @@ export class Agent {
|
|
|
217
225
|
outboundMessages = new SeenMessages();
|
|
218
226
|
confirmedPlanVersions = new SeenMessages();
|
|
219
227
|
grantProviderCache = null;
|
|
228
|
+
/** The checkout box each lane holds while its wake runs. */
|
|
229
|
+
checkoutByLane = new Map();
|
|
220
230
|
constructor(options = {}) {
|
|
221
231
|
const resolved = applyBrainToAgentOptions(options);
|
|
222
232
|
this.options = {
|
|
@@ -238,6 +248,7 @@ export class Agent {
|
|
|
238
248
|
// agents — the declaration existed and nothing carried it. Same for
|
|
239
249
|
// `requestMatchTags` (always undefined at triage) and `telemetryIngest`.
|
|
240
250
|
needs: resolved.needs,
|
|
251
|
+
checkout: resolved.checkout,
|
|
241
252
|
taskTools: resolved.taskTools,
|
|
242
253
|
states: resolved.states,
|
|
243
254
|
initial: resolved.initial,
|
|
@@ -894,7 +905,7 @@ export class Agent {
|
|
|
894
905
|
if (this.canAskForAccess() && !this.askedForAccessLanes.has(_laneKey)) {
|
|
895
906
|
this.askedForAccessLanes.add(_laneKey);
|
|
896
907
|
this.unmetTasksPendingAsk.set(_laneKey, mine.map((t) => String(t.taskId)));
|
|
897
|
-
runtimeLog.info(this.options.name || 'agent', `no access for ${unmet.map((n) => n.connection).join('/')} on ${_laneKey} — letting this wake run once so it can ask, then failing whatever it leaves`);
|
|
908
|
+
runtimeLog.info(this.options.name || 'agent', `no access for ${unmet.map((n) => ('connection' in n ? n.connection : 'checkout')).join('/')} on ${_laneKey} — letting this wake run once so it can ask, then failing whatever it leaves`);
|
|
898
909
|
return false;
|
|
899
910
|
}
|
|
900
911
|
for (const task of mine) {
|
|
@@ -1057,6 +1068,13 @@ export class Agent {
|
|
|
1057
1068
|
frame = runnable.length === 1 ? runnable[0] : { type: 'batch', events: runnable };
|
|
1058
1069
|
const memory = this.memoryForWake(session, frame);
|
|
1059
1070
|
const wakeTaskId = workScopeFromWake(frame, session.sessionId).taskId;
|
|
1071
|
+
// A checkout agent gets its box before the brain runs: the task names the
|
|
1072
|
+
// repository, the hire's GitHub grant backs the clone, and a task that
|
|
1073
|
+
// names nothing or holds no grant is refused here by name, like the needs
|
|
1074
|
+
// gate above.
|
|
1075
|
+
const checkout = await this._openCheckoutForWake(laneKey, session.sessionId, frame, wakeTaskId);
|
|
1076
|
+
if (checkout === 'refused')
|
|
1077
|
+
return;
|
|
1060
1078
|
// Recorded here rather than after the tick: this is the last point where the
|
|
1061
1079
|
// input is still exactly what the brain will see, and a wake that goes on to
|
|
1062
1080
|
// throw is the one most worth replaying.
|
|
@@ -1182,19 +1200,24 @@ export class Agent {
|
|
|
1182
1200
|
};
|
|
1183
1201
|
try {
|
|
1184
1202
|
await checkpoint();
|
|
1185
|
-
tickWork = this.wakeEffects.run(marks, () => this.brain.tick({ event: frame, ctx: session.ctx, state: session.state, identity, eff, memory, transitionTap: this.options.transitionTap, signal: tickAbort.signal }));
|
|
1203
|
+
tickWork = this.wakeEffects.run(marks, () => this.brain.tick({ event: frame, ctx: session.ctx, state: session.state, identity, eff, memory, transitionTap: this.options.transitionTap, signal: tickAbort.signal, ...(checkout ? { checkout: checkout.session } : {}) }));
|
|
1186
1204
|
const result = await Promise.race([
|
|
1187
1205
|
tickWork,
|
|
1188
1206
|
new Promise((_, reject) => {
|
|
1189
1207
|
tickWatchdog = setTimeout(() => {
|
|
1190
1208
|
tickAbort.abort(new Error('tick timeout'));
|
|
1191
1209
|
reject(new Error('tick timeout'));
|
|
1192
|
-
}, this.tickTimeoutMs);
|
|
1210
|
+
}, checkout ? Math.max(this.tickTimeoutMs, CHECKOUT_TICK_TIMEOUT_MS) : this.tickTimeoutMs);
|
|
1193
1211
|
}),
|
|
1194
1212
|
]);
|
|
1195
1213
|
session.ctx = result.ctx;
|
|
1196
1214
|
session.state = result.state;
|
|
1197
1215
|
this.sessions.set(laneKey, session);
|
|
1216
|
+
// The box dies with the wake, and what it cost rides the same cost line
|
|
1217
|
+
// as the wake's tokens.
|
|
1218
|
+
const boxCost = await this._closeCheckoutForWake(laneKey);
|
|
1219
|
+
if (boxCost && result.metrics)
|
|
1220
|
+
result.metrics = { ...result.metrics, box: boxCost };
|
|
1198
1221
|
this.recordWakeOutcome(session, classifyWakeOutcome({
|
|
1199
1222
|
skippedReason: result.skippedReason,
|
|
1200
1223
|
llmCalls: result.metrics?.llmCalls ?? 0,
|
|
@@ -1328,6 +1351,53 @@ export class Agent {
|
|
|
1328
1351
|
// work it was refused for sitting active.
|
|
1329
1352
|
if (!heldTask)
|
|
1330
1353
|
await this.settleUnmetAfterAsk(laneKey).catch(() => { });
|
|
1354
|
+
// A tick that threw still must not leave its box running.
|
|
1355
|
+
await this._closeCheckoutForWake(laneKey).catch(() => { });
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
/**
|
|
1359
|
+
* The checkout a wake needs, opened before the brain runs. Null when this
|
|
1360
|
+
* wake needs none; `'refused'` when the task was failed here by name.
|
|
1361
|
+
*/
|
|
1362
|
+
async _openCheckoutForWake(laneKey, sessionId, frame, taskId) {
|
|
1363
|
+
const needs = normalizeNeeds(this.options.needs);
|
|
1364
|
+
if (!needs.some(isCheckoutNeed) || !taskId)
|
|
1365
|
+
return null;
|
|
1366
|
+
const operatorKey = this.options.operatorKey;
|
|
1367
|
+
const agentId = this.options.agentId;
|
|
1368
|
+
if (!operatorKey || !agentId || !this.taskService)
|
|
1369
|
+
return null;
|
|
1370
|
+
const laneId = laneUnderAgreement(sessionId, workScopeFromWake(frame, sessionId).agreementId);
|
|
1371
|
+
const open = await openCheckout({
|
|
1372
|
+
provider: this.options.checkout,
|
|
1373
|
+
needs,
|
|
1374
|
+
taskId,
|
|
1375
|
+
getTask: (id) => this.taskService.getTask(id),
|
|
1376
|
+
listConnections: () => new ConnectionsClient(operatorKey, agentId, undefined, laneId).listForHolder(),
|
|
1377
|
+
door: { backendUrl: resolveBackendUrl(), operatorKey, agentId, laneId },
|
|
1378
|
+
failTask: async (id, reason) => {
|
|
1379
|
+
await this.taskService.updateState(id, 'failed', { summary: reason, status: 'failed' });
|
|
1380
|
+
},
|
|
1381
|
+
log: (message) => runtimeLog.info(this.options.name || 'agent', message),
|
|
1382
|
+
});
|
|
1383
|
+
if (open && open !== 'refused')
|
|
1384
|
+
this.checkoutByLane.set(laneKey, open);
|
|
1385
|
+
return open;
|
|
1386
|
+
}
|
|
1387
|
+
/** Destroy the lane's box, if it still holds one, and say what it cost. */
|
|
1388
|
+
async _closeCheckoutForWake(laneKey) {
|
|
1389
|
+
const open = this.checkoutByLane.get(laneKey);
|
|
1390
|
+
if (!open)
|
|
1391
|
+
return null;
|
|
1392
|
+
this.checkoutByLane.delete(laneKey);
|
|
1393
|
+
try {
|
|
1394
|
+
const cost = await closeCheckout(open);
|
|
1395
|
+
runtimeLog.info(this.options.name || 'agent', `checkout closed: ${cost.kind} box held ${cost.seconds}s`);
|
|
1396
|
+
return cost;
|
|
1397
|
+
}
|
|
1398
|
+
catch (err) {
|
|
1399
|
+
runtimeLog.warn(this.options.name || 'agent', `could not save/close the checkout box: ${err?.message ?? err}`);
|
|
1400
|
+
throw err;
|
|
1331
1401
|
}
|
|
1332
1402
|
}
|
|
1333
1403
|
/**
|
|
@@ -84,6 +84,12 @@ export declare function isMissingSessionError(err: unknown): boolean;
|
|
|
84
84
|
* the loop: instead of the FSM + `runTurn`, a wake drives a headless Claude
|
|
85
85
|
* `query()` (see "host is the product; the brain is how it decides").
|
|
86
86
|
*/
|
|
87
|
+
/**
|
|
88
|
+
* The agreement every delivery in this wake is under, or undefined when none
|
|
89
|
+
* names one or they disagree. A batch across agreements names nothing, and the
|
|
90
|
+
* backend holds that wake to its room.
|
|
91
|
+
*/
|
|
92
|
+
export declare function wakeAgreementOf(event: RawEvent | undefined): string | undefined;
|
|
87
93
|
export declare class ClaudeBrain implements Brain {
|
|
88
94
|
readonly kind: "claude";
|
|
89
95
|
workflow: Workflow;
|
|
@@ -110,5 +116,5 @@ export declare class ClaudeBrain implements Brain {
|
|
|
110
116
|
private toolContextByLane;
|
|
111
117
|
private sdkMcpByLane;
|
|
112
118
|
constructor(opts: ClaudeBrainOptions);
|
|
113
|
-
tick({ event, ctx, state, identity, eff, memory, signal }: TickInput): Promise<TickOutput>;
|
|
119
|
+
tick({ event, ctx, state, identity, eff, memory, signal, checkout }: TickInput): Promise<TickOutput>;
|
|
114
120
|
}
|
|
@@ -177,6 +177,23 @@ function extractMetrics(message) {
|
|
|
177
177
|
* the loop: instead of the FSM + `runTurn`, a wake drives a headless Claude
|
|
178
178
|
* `query()` (see "host is the product; the brain is how it decides").
|
|
179
179
|
*/
|
|
180
|
+
/**
|
|
181
|
+
* The agreement every delivery in this wake is under, or undefined when none
|
|
182
|
+
* names one or they disagree. A batch across agreements names nothing, and the
|
|
183
|
+
* backend holds that wake to its room.
|
|
184
|
+
*/
|
|
185
|
+
export function wakeAgreementOf(event) {
|
|
186
|
+
if (!event)
|
|
187
|
+
return undefined;
|
|
188
|
+
const items = Array.isArray(event.events) && event.events.length > 0 ? event.events : [event];
|
|
189
|
+
const ids = new Set(items.map((item) => {
|
|
190
|
+
const id = item.resource?.agreementId;
|
|
191
|
+
return typeof id === 'string' && id ? id : null;
|
|
192
|
+
}));
|
|
193
|
+
if (ids.size !== 1)
|
|
194
|
+
return undefined;
|
|
195
|
+
return [...ids][0] ?? undefined;
|
|
196
|
+
}
|
|
180
197
|
export class ClaudeBrain {
|
|
181
198
|
kind = 'claude';
|
|
182
199
|
workflow;
|
|
@@ -230,7 +247,7 @@ export class ClaudeBrain {
|
|
|
230
247
|
this.actsOnHireActivation = opts.actsOnHireActivation === true;
|
|
231
248
|
this.askWakeGate = opts.askWakeGate ?? null;
|
|
232
249
|
}
|
|
233
|
-
async tick({ event, ctx, state, identity, eff, memory, signal }) {
|
|
250
|
+
async tick({ event, ctx, state, identity, eff, memory, signal, checkout }) {
|
|
234
251
|
if (!identity?.sessionId)
|
|
235
252
|
throw new Error('ClaudeBrain.tick: identity.sessionId is required');
|
|
236
253
|
if (!eff)
|
|
@@ -397,6 +414,9 @@ export class ClaudeBrain {
|
|
|
397
414
|
eff,
|
|
398
415
|
senderId: rawEvent?.senderId != null ? String(rawEvent.senderId) : undefined,
|
|
399
416
|
senderType: rawEvent?.senderType != null ? String(rawEvent.senderType) : undefined,
|
|
417
|
+
// The agreement the delivery itself is under, so the backend scopes this
|
|
418
|
+
// wake to it. Read off the delivery, never a lane's remembered agreement.
|
|
419
|
+
wakeAgreementId: wakeAgreementOf(rawEvent),
|
|
400
420
|
///832: agreement this wake acts under → lets a delegated per-user
|
|
401
421
|
// MCP call resolve the originating end-user (payer) rather than the
|
|
402
422
|
// delegating agent (senderId). Resolved from every reliable wire source,
|
|
@@ -412,6 +432,9 @@ export class ClaudeBrain {
|
|
|
412
432
|
// Hire-partitioned store. The host already scoped this wake;
|
|
413
433
|
// Claude tools that persist notes must use this, not a process-wide bag.
|
|
414
434
|
memory,
|
|
435
|
+
// The wake's checkout, when the host opened one: the checkout_* tools
|
|
436
|
+
// read it here and refuse on a wake that has none.
|
|
437
|
+
...(checkout ? { checkout } : {}),
|
|
415
438
|
};
|
|
416
439
|
const lane = identity.laneKey;
|
|
417
440
|
this.toolContextByLane.set(lane, toolContext);
|
|
@@ -27,6 +27,8 @@ export interface TickInput {
|
|
|
27
27
|
* the agent's choice — `counterparty:<id>`, `agreement:<id>:notes`, etc.
|
|
28
28
|
*/
|
|
29
29
|
memory?: MemoryStore;
|
|
30
|
+
/** The checkout the host opened for this wake, when the agent declares the need and the task names a repository. */
|
|
31
|
+
checkout?: import('../checkout/tools.js').CheckoutSession;
|
|
30
32
|
/** Optional tap for FSM transition events. */
|
|
31
33
|
transitionTap?: (event: TransitionEvent) => void;
|
|
32
34
|
/**
|
|
@@ -63,6 +65,13 @@ export interface TickOutput {
|
|
|
63
65
|
cacheRead?: number;
|
|
64
66
|
cacheCreation?: number;
|
|
65
67
|
};
|
|
68
|
+
/** The checkout box this wake held, when it held one: what kind, for how long, how big. */
|
|
69
|
+
box?: {
|
|
70
|
+
kind: string;
|
|
71
|
+
seconds: number;
|
|
72
|
+
vcpu?: number;
|
|
73
|
+
memoryMb?: number;
|
|
74
|
+
};
|
|
66
75
|
};
|
|
67
76
|
/** Tick skipped the model; host records a no-op naming why. */
|
|
68
77
|
skippedReason?: string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|