@deepagents/context 5.0.0 → 6.1.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/README.md +22 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +490 -104
- package/dist/index.js.map +4 -4
- package/dist/lib/agent.d.ts.map +1 -1
- package/dist/lib/engine.d.ts +12 -14
- package/dist/lib/engine.d.ts.map +1 -1
- package/dist/lib/fragments/plan.d.ts +13 -0
- package/dist/lib/fragments/plan.d.ts.map +1 -0
- package/dist/lib/fragments/reminders/src/predicates/tool.d.ts +8 -0
- package/dist/lib/fragments/reminders/src/predicates/tool.d.ts.map +1 -1
- package/dist/lib/fragments/reminders/src/types.d.ts +3 -0
- package/dist/lib/fragments/reminders/src/types.d.ts.map +1 -1
- package/dist/lib/sandbox/bash-tool.d.ts.map +1 -1
- package/dist/lib/sandbox/virtual-sandbox.d.ts +2 -1
- package/dist/lib/sandbox/virtual-sandbox.d.ts.map +1 -1
- package/dist/lib/stream/stream-manager.d.ts.map +1 -1
- package/package.json +20 -20
package/dist/index.js
CHANGED
|
@@ -380,7 +380,8 @@ var Agent = class _Agent {
|
|
|
380
380
|
}),
|
|
381
381
|
stopWhen: this.#options.stopWhen ?? DEFAULT_STOP_WHEN,
|
|
382
382
|
prepareStep: this.#options.context.createPrepareStep({
|
|
383
|
-
steer: false
|
|
383
|
+
steer: false,
|
|
384
|
+
sandbox: this.#options.sandbox
|
|
384
385
|
}),
|
|
385
386
|
tools: this.tools,
|
|
386
387
|
runtimeContext: contextVariables,
|
|
@@ -417,7 +418,8 @@ var Agent = class _Agent {
|
|
|
417
418
|
throw new Error(`Agent ${this.#options.name} is missing a model.`);
|
|
418
419
|
}
|
|
419
420
|
const prepareStep = this.#options.context.createPrepareStep({
|
|
420
|
-
additionalInput: this.#options.prepareStepInput
|
|
421
|
+
additionalInput: this.#options.prepareStepInput,
|
|
422
|
+
sandbox: this.#options.sandbox
|
|
421
423
|
});
|
|
422
424
|
const result = await this.#createRawStream(
|
|
423
425
|
contextVariables,
|
|
@@ -462,7 +464,7 @@ var Agent = class _Agent {
|
|
|
462
464
|
}),
|
|
463
465
|
repairToolCall: createRepairToolCall(model, config?.abortSignal),
|
|
464
466
|
stopWhen: this.#options.stopWhen ?? DEFAULT_STOP_WHEN,
|
|
465
|
-
prepareStep: prepareStep ?? context.createPrepareStep(),
|
|
467
|
+
prepareStep: prepareStep ?? context.createPrepareStep({ sandbox: this.#options.sandbox }),
|
|
466
468
|
experimental_transform: config?.transform ?? smoothStream(),
|
|
467
469
|
tools: this.tools,
|
|
468
470
|
runtimeContext: contextVariables,
|
|
@@ -2302,6 +2304,16 @@ function toolCallCount(name, spec) {
|
|
|
2302
2304
|
return checkCount(count, spec);
|
|
2303
2305
|
};
|
|
2304
2306
|
}
|
|
2307
|
+
function everyNToolCalls(n) {
|
|
2308
|
+
if (!Number.isInteger(n) || n < 1) {
|
|
2309
|
+
throw new Error("everyNToolCalls(n) requires a positive integer");
|
|
2310
|
+
}
|
|
2311
|
+
return (ctx) => toolPartsOf(ctx.lastAssistantMessage).filter(
|
|
2312
|
+
(part) => ["output-available", "output-error", "output-denied"].includes(
|
|
2313
|
+
part.state
|
|
2314
|
+
)
|
|
2315
|
+
).length >= n;
|
|
2316
|
+
}
|
|
2305
2317
|
function toolFailureStreak(ctx, name) {
|
|
2306
2318
|
const parts = (ctx.lastAssistantMessages ?? []).flatMap(
|
|
2307
2319
|
(message2) => toolPartsOf(message2).filter((part) => matchesName(name, toolNameOf(part)))
|
|
@@ -3838,6 +3850,42 @@ function estimateMessageContent(data) {
|
|
|
3838
3850
|
function isLanguageModelUsage(value) {
|
|
3839
3851
|
return typeof value === "object" && value !== null && "totalTokens" in value;
|
|
3840
3852
|
}
|
|
3853
|
+
function cloneFragmentData(data, seen) {
|
|
3854
|
+
if (typeof data !== "object" || data === null) return data;
|
|
3855
|
+
const existing = seen.get(data);
|
|
3856
|
+
if (existing !== void 0) return existing;
|
|
3857
|
+
if (isFragment(data)) {
|
|
3858
|
+
const clone = { ...data };
|
|
3859
|
+
seen.set(data, clone);
|
|
3860
|
+
if ("data" in data) {
|
|
3861
|
+
clone.data = cloneFragmentData(data.data, seen);
|
|
3862
|
+
}
|
|
3863
|
+
return clone;
|
|
3864
|
+
}
|
|
3865
|
+
if (Array.isArray(data)) {
|
|
3866
|
+
const clone = [];
|
|
3867
|
+
seen.set(data, clone);
|
|
3868
|
+
clone.push(...data.map((item) => cloneFragmentData(item, seen)));
|
|
3869
|
+
return clone;
|
|
3870
|
+
}
|
|
3871
|
+
if (isFragmentObject(data)) {
|
|
3872
|
+
const clone = {};
|
|
3873
|
+
seen.set(data, clone);
|
|
3874
|
+
for (const [key, value] of Object.entries(data)) {
|
|
3875
|
+
clone[key] = cloneFragmentData(value, seen);
|
|
3876
|
+
}
|
|
3877
|
+
return clone;
|
|
3878
|
+
}
|
|
3879
|
+
return data;
|
|
3880
|
+
}
|
|
3881
|
+
function cloneFragment(fragment2) {
|
|
3882
|
+
const clone = { ...fragment2 };
|
|
3883
|
+
const seen = new WeakMap([[fragment2, clone]]);
|
|
3884
|
+
if ("data" in fragment2) {
|
|
3885
|
+
clone.data = cloneFragmentData(fragment2.data, seen);
|
|
3886
|
+
}
|
|
3887
|
+
return clone;
|
|
3888
|
+
}
|
|
3841
3889
|
function addUsageValue(current, next) {
|
|
3842
3890
|
if (current === void 0 && next === void 0) {
|
|
3843
3891
|
return void 0;
|
|
@@ -3911,13 +3959,6 @@ function toolOutcomesFromStep(content) {
|
|
|
3911
3959
|
}
|
|
3912
3960
|
return outcomes;
|
|
3913
3961
|
}
|
|
3914
|
-
function stepStartPartIndices(parts) {
|
|
3915
|
-
const indices = [];
|
|
3916
|
-
for (let i = 0; i < parts.length; i++) {
|
|
3917
|
-
if (parts[i].type === "step-start") indices.push(i);
|
|
3918
|
-
}
|
|
3919
|
-
return indices;
|
|
3920
|
-
}
|
|
3921
3962
|
var ContextEngine = class _ContextEngine {
|
|
3922
3963
|
/** Non-message fragments (role, hints, etc.) - not persisted in graph */
|
|
3923
3964
|
#fragments = [];
|
|
@@ -4072,7 +4113,7 @@ var ContextEngine = class _ContextEngine {
|
|
|
4072
4113
|
* Count user turns in the conversation and return the previous saved user message context.
|
|
4073
4114
|
* Includes persisted messages and pending messages in the turn count.
|
|
4074
4115
|
*/
|
|
4075
|
-
async #getChainContext() {
|
|
4116
|
+
async #getChainContext(pending = this.#pendingMessages) {
|
|
4076
4117
|
await this.#ensureInitialized();
|
|
4077
4118
|
const builder = new ChainSummaryBuilder();
|
|
4078
4119
|
if (this.#branch?.headMessageId) {
|
|
@@ -4081,8 +4122,7 @@ var ContextEngine = class _ContextEngine {
|
|
|
4081
4122
|
);
|
|
4082
4123
|
for (const msg of chain) builder.ingestStored(msg);
|
|
4083
4124
|
}
|
|
4084
|
-
for (const fragment2 of
|
|
4085
|
-
builder.ingestPending(fragment2);
|
|
4125
|
+
for (const fragment2 of pending) builder.ingestPending(fragment2);
|
|
4086
4126
|
return builder.build();
|
|
4087
4127
|
}
|
|
4088
4128
|
async getTurnCount() {
|
|
@@ -4229,14 +4269,14 @@ var ContextEngine = class _ContextEngine {
|
|
|
4229
4269
|
* Add fragments to the context.
|
|
4230
4270
|
*
|
|
4231
4271
|
* - Message fragments (user/assistant) are queued for persistence
|
|
4232
|
-
* - Non-message fragments
|
|
4272
|
+
* - Non-message fragments are cloned so lazy resolution stays engine-local
|
|
4233
4273
|
*/
|
|
4234
4274
|
set(...fragments) {
|
|
4235
4275
|
for (const fragment2 of fragments) {
|
|
4236
4276
|
if (isMessageFragment(fragment2)) {
|
|
4237
4277
|
this.#pendingMessages.push(fragment2);
|
|
4238
4278
|
} else {
|
|
4239
|
-
this.#fragments.push(fragment2);
|
|
4279
|
+
this.#fragments.push(cloneFragment(fragment2));
|
|
4240
4280
|
}
|
|
4241
4281
|
}
|
|
4242
4282
|
return this;
|
|
@@ -4299,12 +4339,16 @@ var ContextEngine = class _ContextEngine {
|
|
|
4299
4339
|
return { headMessageId: this.#branch?.headMessageId ?? void 0 };
|
|
4300
4340
|
}
|
|
4301
4341
|
const pending = this.#pendingMessages;
|
|
4302
|
-
const pipeline = new SavePipeline(this.#asSavePipelineEngine(), pending);
|
|
4303
|
-
await pipeline.applyUpdateBranching(options?.branch ?? true);
|
|
4304
|
-
await this.#foldUserReminders(pending);
|
|
4305
|
-
const result = await pipeline.persist();
|
|
4306
4342
|
this.#pendingMessages = [];
|
|
4307
|
-
|
|
4343
|
+
const pipeline = new SavePipeline(this.#asSavePipelineEngine(), pending);
|
|
4344
|
+
try {
|
|
4345
|
+
await pipeline.applyUpdateBranching(options?.branch ?? true);
|
|
4346
|
+
await this.#foldUserReminders(pending);
|
|
4347
|
+
return await pipeline.persist();
|
|
4348
|
+
} catch (error) {
|
|
4349
|
+
this.#pendingMessages.unshift(...pending);
|
|
4350
|
+
throw error;
|
|
4351
|
+
}
|
|
4308
4352
|
}
|
|
4309
4353
|
#currentReminderSession;
|
|
4310
4354
|
/**
|
|
@@ -4318,27 +4362,19 @@ var ContextEngine = class _ContextEngine {
|
|
|
4318
4362
|
* latch is desired. AI SDK v7 carries prepared message overrides forward to
|
|
4319
4363
|
* subsequent steps.
|
|
4320
4364
|
*
|
|
4321
|
-
*
|
|
4322
|
-
*
|
|
4323
|
-
*
|
|
4365
|
+
* Each injected input is persisted with a following assistant placeholder
|
|
4366
|
+
* before prepareStep returns. writeAssistantSegment fills that placeholder,
|
|
4367
|
+
* so process loss cannot make a delivered reminder eligible again and later
|
|
4368
|
+
* requests reproduce the exact prompt prefix the model saw.
|
|
4324
4369
|
*/
|
|
4325
4370
|
createPrepareStep(options = {}) {
|
|
4326
4371
|
const enableSteer = options.steer ?? true;
|
|
4327
4372
|
const session = {
|
|
4328
4373
|
firedOnceIds: /* @__PURE__ */ new Set(),
|
|
4329
|
-
|
|
4330
|
-
currentSegStart: 0,
|
|
4331
|
-
materialized: 0
|
|
4374
|
+
currentSegStart: 0
|
|
4332
4375
|
};
|
|
4333
4376
|
this.#currentReminderSession = session;
|
|
4334
|
-
let stepOffset = 0;
|
|
4335
|
-
let previousStepNumber;
|
|
4336
4377
|
return async ({ steps, stepNumber, messages }) => {
|
|
4337
|
-
if (previousStepNumber !== void 0 && stepNumber <= previousStepNumber) {
|
|
4338
|
-
stepOffset += previousStepNumber + 1;
|
|
4339
|
-
}
|
|
4340
|
-
previousStepNumber = stepNumber;
|
|
4341
|
-
const cumulativeStepNumber = stepOffset + stepNumber;
|
|
4342
4378
|
const priorStep = stepNumber >= 1 ? steps[stepNumber - 1] : void 0;
|
|
4343
4379
|
const hasSafeBoundary = (priorStep?.content?.length ?? 0) > 0;
|
|
4344
4380
|
const canFire = enableSteer && hasSafeBoundary;
|
|
@@ -4346,13 +4382,18 @@ var ContextEngine = class _ContextEngine {
|
|
|
4346
4382
|
const outcomes = toolOutcomesFromStep(steps.at(-1)?.content ?? []);
|
|
4347
4383
|
const needsChain = steerConfigs.length > 0 || outcomes.length > 0 && this.#remindersFor("tool-output").length > 0;
|
|
4348
4384
|
const chain = needsChain ? await this.#getChainContext() : void 0;
|
|
4349
|
-
const toolReminders = chain ? await this.#evaluateToolOutputReminders(
|
|
4385
|
+
const toolReminders = chain ? await this.#evaluateToolOutputReminders(
|
|
4386
|
+
outcomes,
|
|
4387
|
+
session,
|
|
4388
|
+
chain,
|
|
4389
|
+
options.sandbox
|
|
4390
|
+
) : { texts: [], onceIds: [] };
|
|
4350
4391
|
const additionalInput = await options.additionalInput?.();
|
|
4351
4392
|
let reminderInput;
|
|
4352
4393
|
if (canFire && chain) {
|
|
4353
4394
|
const configs = steerConfigs;
|
|
4354
4395
|
if (configs.length > 0) {
|
|
4355
|
-
const whenCtx = this.#steerWhenCtx(chain, session);
|
|
4396
|
+
const whenCtx = this.#steerWhenCtx(chain, session, options.sandbox);
|
|
4356
4397
|
const matched = await evaluateFiredReminders(configs, whenCtx);
|
|
4357
4398
|
if (matched.length > 0) {
|
|
4358
4399
|
const onceIds = [
|
|
@@ -4388,10 +4429,35 @@ var ContextEngine = class _ContextEngine {
|
|
|
4388
4429
|
const inputModel = await convertToModelMessages2(inputs, {
|
|
4389
4430
|
ignoreIncompleteToolCalls: true
|
|
4390
4431
|
});
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4432
|
+
const head = await this.headMessage();
|
|
4433
|
+
if (head?.name !== "assistant") {
|
|
4434
|
+
throw new Error(
|
|
4435
|
+
"createPrepareStep: expected an assistant message at chain head."
|
|
4436
|
+
);
|
|
4437
|
+
}
|
|
4438
|
+
const currentSegmentId = session.currentSegId ?? head.id;
|
|
4439
|
+
const currentSegment = await this.#store.getMessage(currentSegmentId);
|
|
4440
|
+
if (!currentSegment || currentSegment.name !== "assistant") {
|
|
4441
|
+
throw new Error(
|
|
4442
|
+
"createPrepareStep: expected the open assistant segment to be persisted."
|
|
4443
|
+
);
|
|
4444
|
+
}
|
|
4445
|
+
const currentMessage = requireUIMessage(
|
|
4446
|
+
currentSegment.data,
|
|
4447
|
+
`Stored assistant message "${currentSegment.id}"`
|
|
4448
|
+
);
|
|
4449
|
+
const nextSegmentId = generateId5();
|
|
4450
|
+
for (const input of inputs) this.set(user(input));
|
|
4451
|
+
this.set(
|
|
4452
|
+
assistant({
|
|
4453
|
+
id: nextSegmentId,
|
|
4454
|
+
role: "assistant",
|
|
4455
|
+
parts: []
|
|
4456
|
+
})
|
|
4457
|
+
);
|
|
4458
|
+
await this.save({ branch: false });
|
|
4459
|
+
session.currentSegId = nextSegmentId;
|
|
4460
|
+
session.currentSegStart += currentMessage.parts.length;
|
|
4395
4461
|
return {
|
|
4396
4462
|
messages: [...messages, ...inputModel]
|
|
4397
4463
|
};
|
|
@@ -4400,16 +4466,15 @@ var ContextEngine = class _ContextEngine {
|
|
|
4400
4466
|
};
|
|
4401
4467
|
}
|
|
4402
4468
|
/**
|
|
4403
|
-
* Persist the streamed assistant message
|
|
4469
|
+
* Persist the streamed assistant message into its reserved segment.
|
|
4404
4470
|
*
|
|
4405
4471
|
* Called from chat()'s onStepEnd/onEnd (and the guardrail path) with the
|
|
4406
|
-
* cumulative response message.
|
|
4407
|
-
*
|
|
4408
|
-
*
|
|
4409
|
-
*
|
|
4410
|
-
* write of the whole message to the reserved head.
|
|
4472
|
+
* cumulative response message. prepareStep has already persisted reminder
|
|
4473
|
+
* boundaries and reserved the open segment id, so this only updates that
|
|
4474
|
+
* segment in place with the cumulative prefix removed. With no fired reminder
|
|
4475
|
+
* it writes the whole message to the original reserved head.
|
|
4411
4476
|
*/
|
|
4412
|
-
async writeAssistantSegment(message2
|
|
4477
|
+
async writeAssistantSegment(message2) {
|
|
4413
4478
|
const head = await this.headMessage();
|
|
4414
4479
|
if (head?.name !== "assistant") {
|
|
4415
4480
|
throw new Error(
|
|
@@ -4417,37 +4482,11 @@ var ContextEngine = class _ContextEngine {
|
|
|
4417
4482
|
);
|
|
4418
4483
|
}
|
|
4419
4484
|
const session = this.#currentReminderSession;
|
|
4420
|
-
if (!session || session.fired.length === 0) {
|
|
4421
|
-
this.set(assistant({ ...message2, id: head.id }));
|
|
4422
|
-
await this.save({ branch: false });
|
|
4423
|
-
return;
|
|
4424
|
-
}
|
|
4425
|
-
if (session.currentSegId === void 0) {
|
|
4426
|
-
session.currentSegId = head.id;
|
|
4427
|
-
session.currentSegStart = 0;
|
|
4428
|
-
}
|
|
4429
|
-
const stepStarts = stepStartPartIndices(message2.parts);
|
|
4430
|
-
while (session.materialized < session.fired.length) {
|
|
4431
|
-
const fire = session.fired[session.materialized];
|
|
4432
|
-
const boundary = stepStarts[fire.afterStep + 1] ?? (options.final ? message2.parts.length : void 0);
|
|
4433
|
-
if (boundary === void 0) break;
|
|
4434
|
-
this.set(
|
|
4435
|
-
assistant({
|
|
4436
|
-
id: session.currentSegId,
|
|
4437
|
-
role: "assistant",
|
|
4438
|
-
parts: message2.parts.slice(session.currentSegStart, boundary)
|
|
4439
|
-
})
|
|
4440
|
-
);
|
|
4441
|
-
for (const input of fire.messages) this.set(user(input));
|
|
4442
|
-
session.currentSegId = generateId5();
|
|
4443
|
-
session.currentSegStart = boundary;
|
|
4444
|
-
session.materialized++;
|
|
4445
|
-
}
|
|
4446
4485
|
this.set(
|
|
4447
4486
|
assistant({
|
|
4448
4487
|
...message2,
|
|
4449
|
-
id: session.
|
|
4450
|
-
parts: message2.parts.slice(session
|
|
4488
|
+
id: session?.currentSegId ?? head.id,
|
|
4489
|
+
parts: message2.parts.slice(session?.currentSegStart ?? 0)
|
|
4451
4490
|
})
|
|
4452
4491
|
);
|
|
4453
4492
|
await this.save({ branch: false });
|
|
@@ -4503,7 +4542,7 @@ var ContextEngine = class _ContextEngine {
|
|
|
4503
4542
|
fragment2.codec.encode(),
|
|
4504
4543
|
`Pending user fragment "${fragment2.name}"`
|
|
4505
4544
|
);
|
|
4506
|
-
const chain = await this.#getChainContext();
|
|
4545
|
+
const chain = await this.#getChainContext(pending);
|
|
4507
4546
|
const matched = await evaluateFiredReminders(configs, {
|
|
4508
4547
|
...this.#buildWhenCtx(chain, message2),
|
|
4509
4548
|
firedOnceIds: chain.firedOnceIds
|
|
@@ -4544,7 +4583,7 @@ var ContextEngine = class _ContextEngine {
|
|
|
4544
4583
|
* elapsedExceeds keeps firing every step once crossed — by design; compose
|
|
4545
4584
|
* once() for control.
|
|
4546
4585
|
*/
|
|
4547
|
-
#steerWhenCtx(chain, session) {
|
|
4586
|
+
#steerWhenCtx(chain, session, sandbox) {
|
|
4548
4587
|
const currentMessage = chain.lastMessage;
|
|
4549
4588
|
if (!currentMessage) {
|
|
4550
4589
|
throw new Error(
|
|
@@ -4553,10 +4592,11 @@ var ContextEngine = class _ContextEngine {
|
|
|
4553
4592
|
}
|
|
4554
4593
|
return {
|
|
4555
4594
|
...this.#buildWhenCtx(chain, currentMessage),
|
|
4595
|
+
sandbox,
|
|
4556
4596
|
firedOnceIds: /* @__PURE__ */ new Set([...chain.firedOnceIds, ...session.firedOnceIds])
|
|
4557
4597
|
};
|
|
4558
4598
|
}
|
|
4559
|
-
async #evaluateToolOutputReminders(outcomes, session, chain) {
|
|
4599
|
+
async #evaluateToolOutputReminders(outcomes, session, chain, sandbox) {
|
|
4560
4600
|
const configs = this.#remindersFor("tool-output");
|
|
4561
4601
|
if (configs.length === 0 || outcomes.length === 0) {
|
|
4562
4602
|
return { texts: [], onceIds: [] };
|
|
@@ -4567,6 +4607,7 @@ var ContextEngine = class _ContextEngine {
|
|
|
4567
4607
|
const onceIds = /* @__PURE__ */ new Set();
|
|
4568
4608
|
for (const outcome of outcomes) {
|
|
4569
4609
|
const whenCtx = this.#buildWhenCtx(chain, currentMessage);
|
|
4610
|
+
whenCtx.sandbox = sandbox;
|
|
4570
4611
|
whenCtx.toolOutcome = outcome;
|
|
4571
4612
|
whenCtx.firedOnceIds = /* @__PURE__ */ new Set([
|
|
4572
4613
|
...chain.firedOnceIds,
|
|
@@ -5540,7 +5581,7 @@ async function chat(agent2, options = {}) {
|
|
|
5540
5581
|
if (Object.keys(mergedMetadata).length > 0) {
|
|
5541
5582
|
message2 = { ...message2, metadata: mergedMetadata };
|
|
5542
5583
|
}
|
|
5543
|
-
await context.writeAssistantSegment(message2
|
|
5584
|
+
await context.writeAssistantSegment(message2);
|
|
5544
5585
|
const usage = await result.usage;
|
|
5545
5586
|
if (usage !== void 0) {
|
|
5546
5587
|
await context.trackUsage(usage);
|
|
@@ -5888,6 +5929,300 @@ function fromFragment(fragment2, options) {
|
|
|
5888
5929
|
throw new Error(`Fragment "${fragment2.name}" is missing codec`);
|
|
5889
5930
|
}
|
|
5890
5931
|
|
|
5932
|
+
// packages/context/src/lib/fragments/plan.ts
|
|
5933
|
+
import dedent from "dedent";
|
|
5934
|
+
import z3 from "zod";
|
|
5935
|
+
var PLAN_FILE_PATH = "/workspace/.deepagents/plan.json";
|
|
5936
|
+
var PLAN_INSTRUCTIONS = dedent`
|
|
5937
|
+
You own the plan. The user is not required to provide a structured objective, success criteria, constraints, or task breakdown.
|
|
5938
|
+
|
|
5939
|
+
Build and maintain the plan from the best available evidence, in this order:
|
|
5940
|
+
1. The user's request and later corrections.
|
|
5941
|
+
2. Higher-priority system, developer, and workspace instructions.
|
|
5942
|
+
3. Workspace guidance such as AGENTS.md.
|
|
5943
|
+
4. Existing source, public contracts, tests, documentation, issues, and generated artifacts.
|
|
5944
|
+
5. The current branch, working-tree changes, and pre-existing work.
|
|
5945
|
+
6. Runtime observations, reproduction results, errors, logs, and tool output.
|
|
5946
|
+
7. Sandbox, permission, and available-tool constraints.
|
|
5947
|
+
8. Existing plan state when resuming.
|
|
5948
|
+
9. Dependencies and risks discovered during execution.
|
|
5949
|
+
|
|
5950
|
+
Describe the objective as the outcome the user is trying to achieve, not merely the activity requested. Derive observable success criteria from that outcome and the workspace's real validation mechanisms.
|
|
5951
|
+
|
|
5952
|
+
Preserve the basis of every important plan statement:
|
|
5953
|
+
- Explicit requirement: stated by the user or a higher-priority instruction.
|
|
5954
|
+
- Discovered constraint: demonstrated by source, a public contract, a test, or runtime evidence.
|
|
5955
|
+
- Inference: supported by available evidence but not directly stated.
|
|
5956
|
+
- Assumption: unresolved and potentially requiring confirmation.
|
|
5957
|
+
|
|
5958
|
+
Never silently promote an assumption into a requirement or constraint. Ask the user only when an unresolved choice materially changes the outcome, cannot be answered from the workspace or runtime, or requires authority beyond the request. Do not ask merely because the user did not provide structured plan fields.
|
|
5959
|
+
|
|
5960
|
+
Maintain the authoritative plan as JSON at ${PLAN_FILE_PATH}. Read the latest file before changing it and write the complete document in one operation. The plan has this shape:
|
|
5961
|
+
- revision: a positive integer, incremented whenever the objective, criteria, constraints, assumptions, tasks, or dependencies change; recording lastReview alone does not increment it;
|
|
5962
|
+
- objective: { text, basis, source };
|
|
5963
|
+
- successCriteria: [{ id, text, basis, source, evidence: [{ summary, source }] }];
|
|
5964
|
+
- constraints and assumptions: [{ text, basis, source }];
|
|
5965
|
+
- tasks: [{ id, title, status, blockedBy, evidence }], where status is pending, in_progress, or completed;
|
|
5966
|
+
- lastReview, when present: { revision, decision, summary }.
|
|
5967
|
+
|
|
5968
|
+
Use basis values explicit_requirement, discovered_constraint, inference, or assumption. Store only blockedBy dependency edges. Never store blocks, readiness, or a blocked task status; those are derived when the plan is read. A task may be in_progress only when every blocker is completed, and a completed task must include concrete evidence.
|
|
5969
|
+
|
|
5970
|
+
Whenever plan review fires, record lastReview before continuing. Its revision is the plan revision that was evaluated. If the decision is revise or replace, increment the plan revision when applying the resulting changes while preserving the evaluated revision in lastReview.
|
|
5971
|
+
|
|
5972
|
+
Revise the plan when new evidence invalidates its objective, criteria, constraints, assumptions, sequencing, or dependencies. Before recording a complete decision or claiming completion, map every success criterion to concrete evidence. Completed task statuses alone do not prove completion.
|
|
5973
|
+
`;
|
|
5974
|
+
var REVIEW_REMINDER = [
|
|
5975
|
+
"Re-read the current plan and consider the evidence gathered since the previous review.",
|
|
5976
|
+
"",
|
|
5977
|
+
"Is the current plan still valid given the latest evidence?",
|
|
5978
|
+
"",
|
|
5979
|
+
"If yes, continue. If not, revise it before taking another action.",
|
|
5980
|
+
"Before claiming completion, verify the success criteria against evidence."
|
|
5981
|
+
].join("\n");
|
|
5982
|
+
var nonEmptyString = z3.string().trim().min(1);
|
|
5983
|
+
var statementBasis = z3.enum([
|
|
5984
|
+
"explicit_requirement",
|
|
5985
|
+
"discovered_constraint",
|
|
5986
|
+
"inference",
|
|
5987
|
+
"assumption"
|
|
5988
|
+
]);
|
|
5989
|
+
var statement = z3.strictObject({
|
|
5990
|
+
text: nonEmptyString,
|
|
5991
|
+
basis: statementBasis,
|
|
5992
|
+
source: nonEmptyString
|
|
5993
|
+
});
|
|
5994
|
+
var evidence = z3.strictObject({
|
|
5995
|
+
summary: nonEmptyString,
|
|
5996
|
+
source: nonEmptyString
|
|
5997
|
+
});
|
|
5998
|
+
var successCriterion = statement.extend({
|
|
5999
|
+
id: nonEmptyString,
|
|
6000
|
+
evidence: z3.array(evidence)
|
|
6001
|
+
});
|
|
6002
|
+
var task = z3.strictObject({
|
|
6003
|
+
id: nonEmptyString,
|
|
6004
|
+
title: nonEmptyString,
|
|
6005
|
+
status: z3.enum(["pending", "in_progress", "completed"]),
|
|
6006
|
+
blockedBy: z3.array(nonEmptyString),
|
|
6007
|
+
evidence: z3.array(evidence)
|
|
6008
|
+
});
|
|
6009
|
+
var lastReview = z3.strictObject({
|
|
6010
|
+
revision: z3.number().int().positive(),
|
|
6011
|
+
decision: z3.enum(["continue", "revise", "replace", "complete", "blocked"]),
|
|
6012
|
+
summary: nonEmptyString
|
|
6013
|
+
});
|
|
6014
|
+
var planState = z3.strictObject({
|
|
6015
|
+
revision: z3.number().int().positive(),
|
|
6016
|
+
objective: statement,
|
|
6017
|
+
successCriteria: z3.array(successCriterion).min(1),
|
|
6018
|
+
constraints: z3.array(statement),
|
|
6019
|
+
assumptions: z3.array(statement),
|
|
6020
|
+
tasks: z3.array(task),
|
|
6021
|
+
lastReview: lastReview.optional()
|
|
6022
|
+
});
|
|
6023
|
+
function uniqueIds(items, label) {
|
|
6024
|
+
const byId = /* @__PURE__ */ new Map();
|
|
6025
|
+
for (const item of items) {
|
|
6026
|
+
if (byId.has(item.id)) {
|
|
6027
|
+
throw new Error(`duplicate ${label} id "${item.id}"`);
|
|
6028
|
+
}
|
|
6029
|
+
byId.set(item.id, item);
|
|
6030
|
+
}
|
|
6031
|
+
return byId;
|
|
6032
|
+
}
|
|
6033
|
+
function validatePlan(state) {
|
|
6034
|
+
uniqueIds(state.successCriteria, "success criterion");
|
|
6035
|
+
const tasks = uniqueIds(state.tasks, "task");
|
|
6036
|
+
const blocks = new Map(state.tasks.map(({ id }) => [id, []]));
|
|
6037
|
+
for (const item of state.tasks) {
|
|
6038
|
+
const blockers = /* @__PURE__ */ new Set();
|
|
6039
|
+
for (const blocker of item.blockedBy) {
|
|
6040
|
+
if (blocker === item.id) {
|
|
6041
|
+
throw new Error(`task "${item.id}" cannot block itself`);
|
|
6042
|
+
}
|
|
6043
|
+
if (!tasks.has(blocker)) {
|
|
6044
|
+
throw new Error(
|
|
6045
|
+
`task "${item.id}" references missing blocker "${blocker}"`
|
|
6046
|
+
);
|
|
6047
|
+
}
|
|
6048
|
+
if (blockers.has(blocker)) {
|
|
6049
|
+
throw new Error(`task "${item.id}" repeats blocker "${blocker}"`);
|
|
6050
|
+
}
|
|
6051
|
+
blockers.add(blocker);
|
|
6052
|
+
blocks.get(blocker)?.push(item.id);
|
|
6053
|
+
}
|
|
6054
|
+
}
|
|
6055
|
+
const visited = /* @__PURE__ */ new Set();
|
|
6056
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
6057
|
+
const stack = [];
|
|
6058
|
+
const visit = (id) => {
|
|
6059
|
+
if (visited.has(id)) return;
|
|
6060
|
+
if (visiting.has(id)) {
|
|
6061
|
+
const cycleStart = stack.indexOf(id);
|
|
6062
|
+
throw new Error(
|
|
6063
|
+
`dependency cycle: ${[...stack.slice(cycleStart), id].join(" -> ")}`
|
|
6064
|
+
);
|
|
6065
|
+
}
|
|
6066
|
+
visiting.add(id);
|
|
6067
|
+
stack.push(id);
|
|
6068
|
+
for (const blocker of tasks.get(id)?.blockedBy ?? []) visit(blocker);
|
|
6069
|
+
stack.pop();
|
|
6070
|
+
visiting.delete(id);
|
|
6071
|
+
visited.add(id);
|
|
6072
|
+
};
|
|
6073
|
+
for (const { id } of state.tasks) visit(id);
|
|
6074
|
+
for (const item of state.tasks) {
|
|
6075
|
+
const incompleteBlockers = item.blockedBy.filter(
|
|
6076
|
+
(id) => tasks.get(id)?.status !== "completed"
|
|
6077
|
+
);
|
|
6078
|
+
if (item.status !== "pending" && incompleteBlockers.length > 0) {
|
|
6079
|
+
throw new Error(
|
|
6080
|
+
`task "${item.id}" is ${item.status} while blocked by ${incompleteBlockers.join(", ")}`
|
|
6081
|
+
);
|
|
6082
|
+
}
|
|
6083
|
+
if (item.status === "completed" && item.evidence.length === 0) {
|
|
6084
|
+
throw new Error(`completed task "${item.id}" has no evidence`);
|
|
6085
|
+
}
|
|
6086
|
+
}
|
|
6087
|
+
if (state.lastReview && state.lastReview.revision > state.revision) {
|
|
6088
|
+
throw new Error(
|
|
6089
|
+
`last review references future revision ${state.lastReview.revision}`
|
|
6090
|
+
);
|
|
6091
|
+
}
|
|
6092
|
+
if (state.lastReview?.decision === "complete" && state.successCriteria.some(({ evidence: evidence2 }) => evidence2.length === 0)) {
|
|
6093
|
+
throw new Error(
|
|
6094
|
+
"a complete review requires evidence for every success criterion"
|
|
6095
|
+
);
|
|
6096
|
+
}
|
|
6097
|
+
return blocks;
|
|
6098
|
+
}
|
|
6099
|
+
function parsePlan(raw) {
|
|
6100
|
+
let parsed;
|
|
6101
|
+
try {
|
|
6102
|
+
parsed = JSON.parse(raw);
|
|
6103
|
+
} catch (error) {
|
|
6104
|
+
const message2 = error instanceof Error ? error.message : String(error);
|
|
6105
|
+
throw new Error(`invalid JSON: ${message2}`, { cause: error });
|
|
6106
|
+
}
|
|
6107
|
+
const result = planState.safeParse(parsed);
|
|
6108
|
+
if (!result.success) {
|
|
6109
|
+
const issues = result.error.issues.slice(0, 5).map(({ path: path3, message: message2 }) => `${path3.join(".") || "plan"}: ${message2}`);
|
|
6110
|
+
const remaining = result.error.issues.length - issues.length;
|
|
6111
|
+
throw new Error(
|
|
6112
|
+
`schema errors: ${issues.join("; ")}${remaining > 0 ? `; ${remaining} more` : ""}`
|
|
6113
|
+
);
|
|
6114
|
+
}
|
|
6115
|
+
return { state: result.data, blocks: validatePlan(result.data) };
|
|
6116
|
+
}
|
|
6117
|
+
function lineItems(items) {
|
|
6118
|
+
return items.length > 0 ? items.map((item) => `- ${item}`) : ["- none"];
|
|
6119
|
+
}
|
|
6120
|
+
function latestEvidence(items) {
|
|
6121
|
+
const latest = items.at(-1);
|
|
6122
|
+
return latest ? ` Evidence: ${latest.summary} (${latest.source})` : "";
|
|
6123
|
+
}
|
|
6124
|
+
function formatTask(item, blocks) {
|
|
6125
|
+
const blocked = blocks.get(item.id) ?? [];
|
|
6126
|
+
const suffix = blocked.length > 0 ? `; blocks ${blocked.join(", ")}` : "";
|
|
6127
|
+
return `${item.id}: ${item.title}${suffix}${latestEvidence(item.evidence)}`;
|
|
6128
|
+
}
|
|
6129
|
+
function formatPlanReview(state, blocks) {
|
|
6130
|
+
const tasks = new Map(state.tasks.map((item) => [item.id, item]));
|
|
6131
|
+
const active = state.tasks.filter(({ status }) => status === "in_progress");
|
|
6132
|
+
const ready = state.tasks.filter(
|
|
6133
|
+
({ status, blockedBy }) => status === "pending" && blockedBy.every((id) => tasks.get(id)?.status === "completed")
|
|
6134
|
+
);
|
|
6135
|
+
const waiting = state.tasks.filter(
|
|
6136
|
+
({ status, blockedBy }) => status === "pending" && blockedBy.some((id) => tasks.get(id)?.status !== "completed")
|
|
6137
|
+
);
|
|
6138
|
+
const completed = state.tasks.filter(({ status }) => status === "completed");
|
|
6139
|
+
return [
|
|
6140
|
+
`Current plan (revision ${state.revision})`,
|
|
6141
|
+
`Objective: ${state.objective.text} [${state.objective.basis}; ${state.objective.source}]`,
|
|
6142
|
+
"",
|
|
6143
|
+
"Success criteria:",
|
|
6144
|
+
...lineItems(
|
|
6145
|
+
state.successCriteria.map(
|
|
6146
|
+
(criterion) => `${criterion.evidence.length > 0 ? "[supported]" : "[unsupported]"} ${criterion.id}: ${criterion.text}${latestEvidence(criterion.evidence)}`
|
|
6147
|
+
)
|
|
6148
|
+
),
|
|
6149
|
+
"",
|
|
6150
|
+
"Constraints:",
|
|
6151
|
+
...lineItems(
|
|
6152
|
+
state.constraints.map(
|
|
6153
|
+
(item) => `${item.text} [${item.basis}; ${item.source}]`
|
|
6154
|
+
)
|
|
6155
|
+
),
|
|
6156
|
+
"",
|
|
6157
|
+
"Assumptions:",
|
|
6158
|
+
...lineItems(
|
|
6159
|
+
state.assumptions.map(
|
|
6160
|
+
(item) => `${item.text} [${item.basis}; ${item.source}]`
|
|
6161
|
+
)
|
|
6162
|
+
),
|
|
6163
|
+
"",
|
|
6164
|
+
"Active tasks:",
|
|
6165
|
+
...lineItems(active.map((item) => formatTask(item, blocks))),
|
|
6166
|
+
"",
|
|
6167
|
+
"Ready tasks:",
|
|
6168
|
+
...lineItems(ready.map((item) => formatTask(item, blocks))),
|
|
6169
|
+
"",
|
|
6170
|
+
"Waiting tasks:",
|
|
6171
|
+
...lineItems(
|
|
6172
|
+
waiting.map(
|
|
6173
|
+
(item) => `${formatTask(item, blocks)}; waiting for ${item.blockedBy.filter((id) => tasks.get(id)?.status !== "completed").join(", ")}`
|
|
6174
|
+
)
|
|
6175
|
+
),
|
|
6176
|
+
"",
|
|
6177
|
+
"Completed tasks:",
|
|
6178
|
+
...lineItems(completed.map((item) => formatTask(item, blocks))),
|
|
6179
|
+
...state.lastReview ? [
|
|
6180
|
+
"",
|
|
6181
|
+
`Last review: ${state.lastReview.decision} at revision ${state.lastReview.revision} \u2014 ${state.lastReview.summary}`
|
|
6182
|
+
] : [],
|
|
6183
|
+
"",
|
|
6184
|
+
REVIEW_REMINDER
|
|
6185
|
+
].join("\n");
|
|
6186
|
+
}
|
|
6187
|
+
function errorMessage(error) {
|
|
6188
|
+
const message2 = error instanceof Error ? error.message : String(error);
|
|
6189
|
+
return message2.length > 500 ? `${message2.slice(0, 497)}...` : message2;
|
|
6190
|
+
}
|
|
6191
|
+
async function resolvePlanReview(sandbox) {
|
|
6192
|
+
let raw;
|
|
6193
|
+
try {
|
|
6194
|
+
raw = await sandbox.sandbox.readFile(PLAN_FILE_PATH);
|
|
6195
|
+
} catch (error) {
|
|
6196
|
+
return [
|
|
6197
|
+
`The plan file at ${PLAN_FILE_PATH} could not be read: ${errorMessage(error)}`,
|
|
6198
|
+
"Create or repair it before taking another action.",
|
|
6199
|
+
"",
|
|
6200
|
+
REVIEW_REMINDER
|
|
6201
|
+
].join("\n");
|
|
6202
|
+
}
|
|
6203
|
+
try {
|
|
6204
|
+
const { state, blocks } = parsePlan(raw);
|
|
6205
|
+
return formatPlanReview(state, blocks);
|
|
6206
|
+
} catch (error) {
|
|
6207
|
+
return [
|
|
6208
|
+
`The plan file at ${PLAN_FILE_PATH} is invalid: ${errorMessage(error)}`,
|
|
6209
|
+
"Repair the plan before taking another action.",
|
|
6210
|
+
"",
|
|
6211
|
+
REVIEW_REMINDER
|
|
6212
|
+
].join("\n");
|
|
6213
|
+
}
|
|
6214
|
+
}
|
|
6215
|
+
function instructions() {
|
|
6216
|
+
return fragment("plan_instructions", PLAN_INSTRUCTIONS);
|
|
6217
|
+
}
|
|
6218
|
+
function review(options) {
|
|
6219
|
+
return reminder(({ sandbox }) => resolvePlanReview(sandbox), {
|
|
6220
|
+
when: options.when,
|
|
6221
|
+
target: "steer"
|
|
6222
|
+
});
|
|
6223
|
+
}
|
|
6224
|
+
var plan = { path: PLAN_FILE_PATH, instructions, review };
|
|
6225
|
+
|
|
5891
6226
|
// packages/context/src/lib/fragments/reasoning.ts
|
|
5892
6227
|
function reasoningFramework() {
|
|
5893
6228
|
return [
|
|
@@ -7520,7 +7855,7 @@ import fg from "fast-glob";
|
|
|
7520
7855
|
import { BashTransformPipeline, TeePlugin } from "just-bash";
|
|
7521
7856
|
import { readFile } from "node:fs/promises";
|
|
7522
7857
|
import * as path from "node:path";
|
|
7523
|
-
import
|
|
7858
|
+
import z4 from "zod";
|
|
7524
7859
|
import { withHostOnlyToolMetadata as withHostOnlyToolMetadata2 } from "@deepagents/agent";
|
|
7525
7860
|
var DEFAULT_DESTINATION = "/workspace";
|
|
7526
7861
|
var DEFAULT_MAX_FILES = 1e3;
|
|
@@ -7543,6 +7878,7 @@ var KNOWN_TOOLS = /* @__PURE__ */ new Set([
|
|
|
7543
7878
|
"head",
|
|
7544
7879
|
"html-to-markdown",
|
|
7545
7880
|
"iconv",
|
|
7881
|
+
"js-exec",
|
|
7546
7882
|
"join",
|
|
7547
7883
|
"jq",
|
|
7548
7884
|
"node",
|
|
@@ -7687,6 +8023,7 @@ async function createToolPrompt(sandbox, filenames, customPrompt) {
|
|
|
7687
8023
|
const available = new Set(
|
|
7688
8024
|
result.stdout.split("\n").filter((entry) => KNOWN_TOOLS.has(entry))
|
|
7689
8025
|
);
|
|
8026
|
+
if (available.has("js-exec")) available.delete("node");
|
|
7690
8027
|
if (available.size === 0) return "";
|
|
7691
8028
|
const lines = [
|
|
7692
8029
|
`Available tools: ${[...available].sort().join(", ")}, and more`
|
|
@@ -7764,9 +8101,9 @@ async function createBashTool(options) {
|
|
|
7764
8101
|
extraInstructions,
|
|
7765
8102
|
experimentalTeeTransform
|
|
7766
8103
|
}),
|
|
7767
|
-
inputSchema:
|
|
7768
|
-
command:
|
|
7769
|
-
reasoning:
|
|
8104
|
+
inputSchema: z4.object({
|
|
8105
|
+
command: z4.string().describe("The bash command to execute"),
|
|
8106
|
+
reasoning: z4.string().trim().min(1).describe("Brief reason for executing this command")
|
|
7770
8107
|
}),
|
|
7771
8108
|
execute: async ({ command: originalCommand }, executionOptions) => {
|
|
7772
8109
|
let command = originalCommand;
|
|
@@ -7808,8 +8145,8 @@ async function createBashTool(options) {
|
|
|
7808
8145
|
});
|
|
7809
8146
|
const readFile2 = tool2({
|
|
7810
8147
|
description: "Read the contents of a file from the sandbox.",
|
|
7811
|
-
inputSchema:
|
|
7812
|
-
path:
|
|
8148
|
+
inputSchema: z4.object({
|
|
8149
|
+
path: z4.string().describe("The path to the file to read")
|
|
7813
8150
|
}),
|
|
7814
8151
|
execute: async ({ path: filePath }) => ({
|
|
7815
8152
|
content: await sandbox.readFile(
|
|
@@ -7819,9 +8156,9 @@ async function createBashTool(options) {
|
|
|
7819
8156
|
});
|
|
7820
8157
|
const writeFile2 = tool2({
|
|
7821
8158
|
description: "Write content to a file in the sandbox. Creates parent directories if needed.",
|
|
7822
|
-
inputSchema:
|
|
7823
|
-
path:
|
|
7824
|
-
content:
|
|
8159
|
+
inputSchema: z4.object({
|
|
8160
|
+
path: z4.string().describe("The path where the file should be written"),
|
|
8161
|
+
content: z4.string().describe("The content to write to the file")
|
|
7825
8162
|
}),
|
|
7826
8163
|
execute: async ({ path: filePath, content }) => {
|
|
7827
8164
|
try {
|
|
@@ -9902,12 +10239,15 @@ function buildSubcommandRepair(name, subcommands) {
|
|
|
9902
10239
|
}
|
|
9903
10240
|
|
|
9904
10241
|
// packages/context/src/lib/sandbox/virtual-sandbox.ts
|
|
9905
|
-
import {
|
|
10242
|
+
import {
|
|
10243
|
+
Bash
|
|
10244
|
+
} from "just-bash";
|
|
9906
10245
|
async function createVirtualSandbox(options) {
|
|
9907
10246
|
const bash = new Bash({
|
|
9908
10247
|
fs: options.fs,
|
|
9909
10248
|
cwd: options.cwd,
|
|
9910
10249
|
env: options.env,
|
|
10250
|
+
javascript: options.javascript,
|
|
9911
10251
|
customCommands: options.customCommands
|
|
9912
10252
|
});
|
|
9913
10253
|
const sandbox = {
|
|
@@ -9952,7 +10292,7 @@ async function createVirtualSandbox(options) {
|
|
|
9952
10292
|
}
|
|
9953
10293
|
|
|
9954
10294
|
// packages/context/src/lib/skills/fragments.ts
|
|
9955
|
-
import
|
|
10295
|
+
import dedent2 from "dedent";
|
|
9956
10296
|
function skills(availableSkills) {
|
|
9957
10297
|
const skillSnapshot = availableSkills.map(({ name, description, path: path3 }) => ({
|
|
9958
10298
|
name,
|
|
@@ -9983,7 +10323,7 @@ function skills(availableSkills) {
|
|
|
9983
10323
|
metadata: { skills: skillSnapshot }
|
|
9984
10324
|
};
|
|
9985
10325
|
}
|
|
9986
|
-
var SKILLS_INSTRUCTIONS =
|
|
10326
|
+
var SKILLS_INSTRUCTIONS = dedent2`A skill is a set of local instructions to follow that is stored in a \`SKILL.md\` file. Below is the list of skills that can be used. Each entry includes a name, description, and file path so you can open the source for full instructions when using a specific skill.
|
|
9987
10327
|
|
|
9988
10328
|
### How to use skills
|
|
9989
10329
|
- Discovery: The list below shows the skills available in this session (name + description + file path). Skill bodies live on disk at the listed paths.
|
|
@@ -13152,29 +13492,55 @@ var StreamManager = class {
|
|
|
13152
13492
|
const detect = async () => {
|
|
13153
13493
|
if (detected) return;
|
|
13154
13494
|
detected = true;
|
|
13155
|
-
await this.#notifyCancellation(streamId, options);
|
|
13156
13495
|
ac.abort();
|
|
13496
|
+
await this.#notifyCancellation(streamId, options);
|
|
13157
13497
|
};
|
|
13498
|
+
const retryState = createAdaptivePollingState(DEFAULT_CANCEL_POLLING);
|
|
13158
13499
|
const listeners = this.#localCancellationListeners.get(streamId) ?? /* @__PURE__ */ new Set();
|
|
13159
13500
|
listeners.add(detect);
|
|
13160
13501
|
this.#localCancellationListeners.set(streamId, listeners);
|
|
13161
13502
|
try {
|
|
13162
|
-
|
|
13163
|
-
|
|
13164
|
-
|
|
13165
|
-
|
|
13166
|
-
|
|
13167
|
-
|
|
13168
|
-
|
|
13169
|
-
|
|
13170
|
-
|
|
13503
|
+
while (!ac.signal.aborted && !detected) {
|
|
13504
|
+
try {
|
|
13505
|
+
const initialStatus = await this.#store.getStreamStatus(streamId);
|
|
13506
|
+
if (initialStatus === void 0) {
|
|
13507
|
+
ac.abort();
|
|
13508
|
+
return;
|
|
13509
|
+
}
|
|
13510
|
+
if (initialStatus === "cancelled") {
|
|
13511
|
+
await detect();
|
|
13512
|
+
return;
|
|
13513
|
+
}
|
|
13514
|
+
if (isTerminal3(initialStatus)) return;
|
|
13515
|
+
const subscribedAt = Date.now();
|
|
13516
|
+
let subscriptionStabilized = false;
|
|
13517
|
+
for await (const change of this.#changeSource.subscribe(
|
|
13518
|
+
streamId,
|
|
13519
|
+
ac.signal
|
|
13520
|
+
)) {
|
|
13521
|
+
if (!subscriptionStabilized && Date.now() - subscribedAt >= DEFAULT_CANCEL_POLLING.maxMs) {
|
|
13522
|
+
resetAdaptivePolling(retryState);
|
|
13523
|
+
subscriptionStabilized = true;
|
|
13524
|
+
}
|
|
13525
|
+
if (change.kind === "chunks") continue;
|
|
13526
|
+
const status = await this.#store.getStreamStatus(streamId);
|
|
13527
|
+
if (status === void 0) {
|
|
13528
|
+
ac.abort();
|
|
13529
|
+
return;
|
|
13530
|
+
}
|
|
13531
|
+
if (status === "cancelled") {
|
|
13532
|
+
await detect();
|
|
13533
|
+
return;
|
|
13534
|
+
}
|
|
13535
|
+
if (isTerminal3(status)) return;
|
|
13536
|
+
}
|
|
13537
|
+
} catch {
|
|
13538
|
+
if (ac.signal.aborted || detected) return;
|
|
13171
13539
|
}
|
|
13172
|
-
if (
|
|
13173
|
-
await detect();
|
|
13540
|
+
if (!await waitForDelay2(nextAdaptivePollingDelay(retryState), ac.signal)) {
|
|
13174
13541
|
return;
|
|
13175
13542
|
}
|
|
13176
13543
|
}
|
|
13177
|
-
} catch {
|
|
13178
13544
|
} finally {
|
|
13179
13545
|
listeners.delete(detect);
|
|
13180
13546
|
if (listeners.size === 0) {
|
|
@@ -13188,7 +13554,11 @@ var StreamManager = class {
|
|
|
13188
13554
|
await Promise.all([...listeners].map((listener) => listener()));
|
|
13189
13555
|
}
|
|
13190
13556
|
async #notifyCancellation(streamId, options) {
|
|
13191
|
-
|
|
13557
|
+
let current;
|
|
13558
|
+
try {
|
|
13559
|
+
current = await this.#store.getStream(streamId);
|
|
13560
|
+
} catch {
|
|
13561
|
+
}
|
|
13192
13562
|
const latencyMs = current?.cancelRequestedAt != null ? Math.max(0, Date.now() - current.cancelRequestedAt) : null;
|
|
13193
13563
|
this.#emit({ type: "persist:cancel-detected", streamId, latencyMs });
|
|
13194
13564
|
if (!options?.onCancelDetected) return;
|
|
@@ -13303,6 +13673,20 @@ var StreamManager = class {
|
|
|
13303
13673
|
}
|
|
13304
13674
|
}
|
|
13305
13675
|
};
|
|
13676
|
+
function waitForDelay2(ms, signal) {
|
|
13677
|
+
if (signal.aborted) return Promise.resolve(false);
|
|
13678
|
+
return new Promise((resolve3) => {
|
|
13679
|
+
const timeout = globalThis.setTimeout(() => {
|
|
13680
|
+
signal.removeEventListener("abort", onAbort);
|
|
13681
|
+
resolve3(true);
|
|
13682
|
+
}, ms);
|
|
13683
|
+
const onAbort = () => {
|
|
13684
|
+
globalThis.clearTimeout(timeout);
|
|
13685
|
+
resolve3(false);
|
|
13686
|
+
};
|
|
13687
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
13688
|
+
});
|
|
13689
|
+
}
|
|
13306
13690
|
async function drainAvailable(controller, store, streamId, lastSeqRef, pageSize, onPage) {
|
|
13307
13691
|
let total = 0;
|
|
13308
13692
|
while (true) {
|
|
@@ -13530,6 +13914,7 @@ export {
|
|
|
13530
13914
|
errorRecoveryGuardrail,
|
|
13531
13915
|
estimate,
|
|
13532
13916
|
evaluateFiredReminders,
|
|
13917
|
+
everyNToolCalls,
|
|
13533
13918
|
everyNTurns,
|
|
13534
13919
|
everyOfLastN,
|
|
13535
13920
|
example,
|
|
@@ -13585,6 +13970,7 @@ export {
|
|
|
13585
13970
|
persona,
|
|
13586
13971
|
pip,
|
|
13587
13972
|
pkg,
|
|
13973
|
+
plan,
|
|
13588
13974
|
policy,
|
|
13589
13975
|
preference,
|
|
13590
13976
|
principle,
|