@runtypelabs/sdk 4.10.0 → 4.11.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.cjs +563 -57
- package/dist/index.d.cts +312 -2
- package/dist/index.d.ts +312 -2
- package/dist/index.mjs +545 -56
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -4310,6 +4310,261 @@ function sanitizeTaskSlug(taskName) {
|
|
|
4310
4310
|
return taskName.toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
|
|
4311
4311
|
}
|
|
4312
4312
|
|
|
4313
|
+
// src/workflows/hook-registry.ts
|
|
4314
|
+
var BUILTIN_NAMESPACE = "builtin";
|
|
4315
|
+
var HOOK_REF_PATTERN = /^[a-z0-9_-]+:[a-z0-9_-]+$/;
|
|
4316
|
+
function isWorkflowHookRef(value) {
|
|
4317
|
+
return typeof value === "string" && HOOK_REF_PATTERN.test(value);
|
|
4318
|
+
}
|
|
4319
|
+
var registry = /* @__PURE__ */ new Map();
|
|
4320
|
+
function registerWorkflowHook(name, entry) {
|
|
4321
|
+
if (!isWorkflowHookRef(name)) {
|
|
4322
|
+
throw new Error(
|
|
4323
|
+
`Invalid workflow hook name "${name}": must be "<namespace>:<id>" using lowercase letters, digits, "-" or "_" (e.g. "acme:my-completion").`
|
|
4324
|
+
);
|
|
4325
|
+
}
|
|
4326
|
+
if (name.startsWith(`${BUILTIN_NAMESPACE}:`)) {
|
|
4327
|
+
throw new Error(
|
|
4328
|
+
`Cannot register "${name}": the "builtin:" namespace is reserved. Register under your own namespace and reference it from the workflow config instead.`
|
|
4329
|
+
);
|
|
4330
|
+
}
|
|
4331
|
+
registry.set(name, entry);
|
|
4332
|
+
}
|
|
4333
|
+
function registerBuiltinWorkflowHook(name, entry) {
|
|
4334
|
+
if (!name.startsWith(`${BUILTIN_NAMESPACE}:`) || !isWorkflowHookRef(name)) {
|
|
4335
|
+
throw new Error(`Builtin workflow hooks must be named "builtin:<id>" (got "${name}").`);
|
|
4336
|
+
}
|
|
4337
|
+
if (registry.has(name)) return;
|
|
4338
|
+
registry.set(name, entry);
|
|
4339
|
+
}
|
|
4340
|
+
function resolveWorkflowHook(name, expectedKind) {
|
|
4341
|
+
const entry = registry.get(name);
|
|
4342
|
+
if (!entry) {
|
|
4343
|
+
const known = listWorkflowHooks().filter((hook) => hook.kind === expectedKind).map((hook) => hook.name);
|
|
4344
|
+
throw new Error(
|
|
4345
|
+
`Unknown workflow hook "${name}". ` + (known.length > 0 ? `Registered '${expectedKind}' hooks: ${known.join(", ")}.` : `No '${expectedKind}' hooks are registered.`) + " Custom hooks must be registered (e.g. via a playbook plugin) before the workflow is compiled."
|
|
4346
|
+
);
|
|
4347
|
+
}
|
|
4348
|
+
if (entry.kind !== expectedKind) {
|
|
4349
|
+
throw new Error(
|
|
4350
|
+
`Workflow hook "${name}" is registered as '${entry.kind}' but referenced from a '${expectedKind}' slot.`
|
|
4351
|
+
);
|
|
4352
|
+
}
|
|
4353
|
+
return entry.fn;
|
|
4354
|
+
}
|
|
4355
|
+
function listWorkflowHooks() {
|
|
4356
|
+
return [...registry.entries()].map(([name, entry]) => ({ name, kind: entry.kind }));
|
|
4357
|
+
}
|
|
4358
|
+
function unregisterWorkflowHook(name) {
|
|
4359
|
+
if (name.startsWith(`${BUILTIN_NAMESPACE}:`)) return false;
|
|
4360
|
+
return registry.delete(name);
|
|
4361
|
+
}
|
|
4362
|
+
|
|
4363
|
+
// src/workflows/workflow-config.ts
|
|
4364
|
+
var DISCOVERY_TOOLS = /* @__PURE__ */ new Set([
|
|
4365
|
+
"search_repo",
|
|
4366
|
+
"glob_files",
|
|
4367
|
+
"tree_directory",
|
|
4368
|
+
"list_directory"
|
|
4369
|
+
]);
|
|
4370
|
+
var DEFAULT_RECOVERY_AFTER_EMPTY_SESSIONS = 2;
|
|
4371
|
+
function definePlaybook(playbook) {
|
|
4372
|
+
return playbook;
|
|
4373
|
+
}
|
|
4374
|
+
function interpolateWorkflowTemplate(template, state) {
|
|
4375
|
+
return template.replace(/\{\{(\w+)\}\}/g, (_match, key) => {
|
|
4376
|
+
const value = state[key];
|
|
4377
|
+
if (value === void 0 || value === null) return `{{${key}}}`;
|
|
4378
|
+
return String(value);
|
|
4379
|
+
});
|
|
4380
|
+
}
|
|
4381
|
+
function buildIsComplete(criteria, configName, milestoneName) {
|
|
4382
|
+
if (!criteria) return () => false;
|
|
4383
|
+
switch (criteria.type) {
|
|
4384
|
+
case "evidence":
|
|
4385
|
+
return (ctx) => {
|
|
4386
|
+
const minFiles = criteria.minReadFiles ?? 1;
|
|
4387
|
+
return (ctx.state.recentReadPaths?.length ?? 0) >= minFiles;
|
|
4388
|
+
};
|
|
4389
|
+
case "sessions": {
|
|
4390
|
+
let baselineSessionCount;
|
|
4391
|
+
return (ctx) => {
|
|
4392
|
+
const minSessions = criteria.minSessions ?? 1;
|
|
4393
|
+
if (baselineSessionCount === void 0) {
|
|
4394
|
+
baselineSessionCount = ctx.state.sessions.length;
|
|
4395
|
+
}
|
|
4396
|
+
return ctx.state.sessions.length - baselineSessionCount >= minSessions;
|
|
4397
|
+
};
|
|
4398
|
+
}
|
|
4399
|
+
case "planWritten":
|
|
4400
|
+
return (ctx) => {
|
|
4401
|
+
return ctx.trace.planWritten;
|
|
4402
|
+
};
|
|
4403
|
+
case "never":
|
|
4404
|
+
return () => false;
|
|
4405
|
+
default: {
|
|
4406
|
+
if (isWorkflowHookRef(criteria.type)) {
|
|
4407
|
+
return resolveWorkflowHook(criteria.type, "completion");
|
|
4408
|
+
}
|
|
4409
|
+
throw new Error(
|
|
4410
|
+
`Workflow config '${configName}': milestone '${milestoneName}' has unknown completionCriteria.type "${criteria.type}" (expected evidence | sessions | planWritten | never, or a 'completion' hook reference).`
|
|
4411
|
+
);
|
|
4412
|
+
}
|
|
4413
|
+
}
|
|
4414
|
+
}
|
|
4415
|
+
function buildPolicyIntercept(policy, configName, deps) {
|
|
4416
|
+
if (!policy.blockedTools?.length && !policy.blockDiscoveryTools && !policy.allowedReadGlobs?.length && !policy.allowedWriteGlobs?.length && !policy.requirePlanBeforeWrite) {
|
|
4417
|
+
return void 0;
|
|
4418
|
+
}
|
|
4419
|
+
const blockedSet = new Set(
|
|
4420
|
+
(policy.blockedTools ?? []).map((t) => t.trim()).filter(Boolean)
|
|
4421
|
+
);
|
|
4422
|
+
const readGlobs = policy.allowedReadGlobs ?? [];
|
|
4423
|
+
const writeGlobs = policy.allowedWriteGlobs ?? [];
|
|
4424
|
+
const matchPathGlobs = deps.matchPathGlobs;
|
|
4425
|
+
if ((readGlobs.length > 0 || writeGlobs.length > 0) && !matchPathGlobs) {
|
|
4426
|
+
throw new Error(
|
|
4427
|
+
`Workflow config '${configName}': policy uses allowedReadGlobs/allowedWriteGlobs but no glob matcher was provided to compileWorkflowConfig (pass deps.matchPathGlobs).`
|
|
4428
|
+
);
|
|
4429
|
+
}
|
|
4430
|
+
return (toolName, args, ctx) => {
|
|
4431
|
+
if (blockedSet.has(toolName)) {
|
|
4432
|
+
return `Blocked by playbook policy: ${toolName} is not allowed for this task.`;
|
|
4433
|
+
}
|
|
4434
|
+
if (policy.blockDiscoveryTools && DISCOVERY_TOOLS.has(toolName)) {
|
|
4435
|
+
return `Blocked by playbook policy: discovery tools are disabled for this task.`;
|
|
4436
|
+
}
|
|
4437
|
+
const pathArg = typeof args.path === "string" && args.path.trim() ? ctx.normalizePath(String(args.path)) : void 0;
|
|
4438
|
+
if (pathArg) {
|
|
4439
|
+
const isWrite = toolName === "write_file" || toolName === "restore_file_checkpoint";
|
|
4440
|
+
const isRead = toolName === "read_file";
|
|
4441
|
+
if (isRead && readGlobs.length > 0) {
|
|
4442
|
+
const allowed = matchPathGlobs(pathArg, readGlobs);
|
|
4443
|
+
if (!allowed) {
|
|
4444
|
+
return `Blocked by playbook policy: ${toolName} path "${pathArg}" is outside allowed read globs: ${readGlobs.join(", ")}`;
|
|
4445
|
+
}
|
|
4446
|
+
}
|
|
4447
|
+
if (isWrite && writeGlobs.length > 0) {
|
|
4448
|
+
const planPath = ctx.state.planPath ? ctx.normalizePath(ctx.state.planPath) : void 0;
|
|
4449
|
+
if (planPath && pathArg === planPath) {
|
|
4450
|
+
} else {
|
|
4451
|
+
const allowed = matchPathGlobs(pathArg, writeGlobs);
|
|
4452
|
+
if (!allowed) {
|
|
4453
|
+
return `Blocked by playbook policy: ${toolName} path "${pathArg}" is outside allowed write globs: ${writeGlobs.join(", ")}`;
|
|
4454
|
+
}
|
|
4455
|
+
}
|
|
4456
|
+
}
|
|
4457
|
+
if (isWrite && policy.requirePlanBeforeWrite && !ctx.state.planWritten && !ctx.trace.planWritten) {
|
|
4458
|
+
const planPath = ctx.state.planPath ? ctx.normalizePath(ctx.state.planPath) : void 0;
|
|
4459
|
+
if (!planPath || pathArg !== planPath) {
|
|
4460
|
+
return `Blocked by playbook policy: write the plan before creating other files.`;
|
|
4461
|
+
}
|
|
4462
|
+
}
|
|
4463
|
+
}
|
|
4464
|
+
return void 0;
|
|
4465
|
+
};
|
|
4466
|
+
}
|
|
4467
|
+
function buildPolicyGuidance(policy) {
|
|
4468
|
+
if (!policy) return [];
|
|
4469
|
+
const lines = [];
|
|
4470
|
+
if (policy.requirePlanBeforeWrite) {
|
|
4471
|
+
lines.push(
|
|
4472
|
+
"Policy: write the plan file before any other file. Once the plan is written, other writes are allowed in the same turn."
|
|
4473
|
+
);
|
|
4474
|
+
}
|
|
4475
|
+
if (policy.allowedWriteGlobs?.length) {
|
|
4476
|
+
lines.push(
|
|
4477
|
+
`Policy: file writes are only allowed for paths matching: ${policy.allowedWriteGlobs.join(", ")} (the plan file is always allowed).`
|
|
4478
|
+
);
|
|
4479
|
+
}
|
|
4480
|
+
if (policy.outputRoot) {
|
|
4481
|
+
lines.push(`Policy: create new files under "${policy.outputRoot.replace(/\/$/, "")}/".`);
|
|
4482
|
+
}
|
|
4483
|
+
if (policy.allowedReadGlobs?.length) {
|
|
4484
|
+
lines.push(
|
|
4485
|
+
`Policy: file reads are only allowed for paths matching: ${policy.allowedReadGlobs.join(", ")}.`
|
|
4486
|
+
);
|
|
4487
|
+
}
|
|
4488
|
+
if (policy.blockDiscoveryTools) {
|
|
4489
|
+
lines.push(
|
|
4490
|
+
"Policy: broad discovery tools (search_repo, glob_files, tree_directory, list_directory) are disabled for this task."
|
|
4491
|
+
);
|
|
4492
|
+
}
|
|
4493
|
+
if (policy.blockedTools?.length) {
|
|
4494
|
+
lines.push(`Policy: these tools are disabled for this task: ${policy.blockedTools.join(", ")}.`);
|
|
4495
|
+
}
|
|
4496
|
+
return lines;
|
|
4497
|
+
}
|
|
4498
|
+
function resolveSlotHook(value, kind) {
|
|
4499
|
+
if (value === void 0) return void 0;
|
|
4500
|
+
if (typeof value === "function") return value;
|
|
4501
|
+
return resolveWorkflowHook(value, kind);
|
|
4502
|
+
}
|
|
4503
|
+
function compileMilestone(milestone, config, policyIntercept, policyGuidance) {
|
|
4504
|
+
const buildInstructions = typeof milestone.instructions === "function" ? milestone.instructions : isWorkflowHookRef(milestone.instructions) ? resolveWorkflowHook(milestone.instructions, "instructions") : (state) => {
|
|
4505
|
+
const header = `--- Workflow Phase: ${milestone.name} ---`;
|
|
4506
|
+
const desc = milestone.description ? `
|
|
4507
|
+
${milestone.description}` : "";
|
|
4508
|
+
const instructions = interpolateWorkflowTemplate(
|
|
4509
|
+
milestone.instructions,
|
|
4510
|
+
state
|
|
4511
|
+
);
|
|
4512
|
+
return `${header}${desc}
|
|
4513
|
+
${instructions}`;
|
|
4514
|
+
};
|
|
4515
|
+
const guidanceHook = typeof milestone.toolGuidance === "function" ? milestone.toolGuidance : milestone.toolGuidance !== void 0 && isWorkflowHookRef(milestone.toolGuidance) ? resolveWorkflowHook(milestone.toolGuidance, "toolGuidance") : void 0;
|
|
4516
|
+
const buildToolGuidance = (state) => {
|
|
4517
|
+
const base = guidanceHook ? guidanceHook(state) : milestone.toolGuidance ?? [];
|
|
4518
|
+
return policyGuidance.length > 0 ? [...base, ...policyGuidance] : base;
|
|
4519
|
+
};
|
|
4520
|
+
const customIntercept = resolveSlotHook(milestone.intercept, "intercept");
|
|
4521
|
+
const interceptToolCall = policyIntercept && customIntercept ? (toolName, args, ctx) => policyIntercept(toolName, args, ctx) ?? customIntercept(toolName, args, ctx) : policyIntercept ?? customIntercept;
|
|
4522
|
+
const transitionHook = typeof milestone.transitionSummary === "function" ? milestone.transitionSummary : milestone.transitionSummary !== void 0 && isWorkflowHookRef(milestone.transitionSummary) ? resolveWorkflowHook(milestone.transitionSummary, "transitionSummary") : void 0;
|
|
4523
|
+
const buildTransitionSummary = milestone.transitionSummary === void 0 ? void 0 : transitionHook ?? ((state, nextPhaseName) => interpolateWorkflowTemplate(milestone.transitionSummary, state).replace(
|
|
4524
|
+
/\{\{nextPhase\}\}/g,
|
|
4525
|
+
nextPhaseName
|
|
4526
|
+
));
|
|
4527
|
+
const recoveryHook = typeof milestone.recovery === "function" ? milestone.recovery : milestone.recovery !== void 0 && isWorkflowHookRef(milestone.recovery) ? resolveWorkflowHook(milestone.recovery, "recovery") : void 0;
|
|
4528
|
+
const buildRecoveryMessage = milestone.recovery === void 0 ? void 0 : recoveryHook ?? ((state) => {
|
|
4529
|
+
const inline = milestone.recovery;
|
|
4530
|
+
const threshold = inline.afterEmptySessions ?? DEFAULT_RECOVERY_AFTER_EMPTY_SESSIONS;
|
|
4531
|
+
if ((state.consecutiveEmptySessions ?? 0) < threshold) return void 0;
|
|
4532
|
+
return interpolateWorkflowTemplate(inline.message, state);
|
|
4533
|
+
});
|
|
4534
|
+
const canAcceptCompletion = milestone.canAcceptCompletion === void 0 ? void 0 : typeof milestone.canAcceptCompletion === "function" ? milestone.canAcceptCompletion : isWorkflowHookRef(milestone.canAcceptCompletion) ? resolveWorkflowHook(milestone.canAcceptCompletion, "acceptCompletion") : () => milestone.canAcceptCompletion;
|
|
4535
|
+
const isComplete = typeof milestone.completionCriteria === "function" ? milestone.completionCriteria : buildIsComplete(milestone.completionCriteria, config.name, milestone.name);
|
|
4536
|
+
return {
|
|
4537
|
+
name: milestone.name,
|
|
4538
|
+
description: milestone.description,
|
|
4539
|
+
buildInstructions,
|
|
4540
|
+
buildToolGuidance,
|
|
4541
|
+
isComplete,
|
|
4542
|
+
...interceptToolCall ? { interceptToolCall } : {},
|
|
4543
|
+
...buildTransitionSummary ? { buildTransitionSummary } : {},
|
|
4544
|
+
...buildRecoveryMessage ? { buildRecoveryMessage } : {},
|
|
4545
|
+
...milestone.forceEndTurn ? { shouldForceEndTurn: resolveSlotHook(milestone.forceEndTurn, "forceEndTurn") } : {},
|
|
4546
|
+
...canAcceptCompletion ? { canAcceptCompletion } : {}
|
|
4547
|
+
};
|
|
4548
|
+
}
|
|
4549
|
+
function compileWorkflowConfig(config, deps = {}) {
|
|
4550
|
+
const policyIntercept = config.policy ? buildPolicyIntercept(config.policy, config.name, deps) : void 0;
|
|
4551
|
+
const policyGuidance = buildPolicyGuidance(config.policy);
|
|
4552
|
+
const phases = config.milestones.map(
|
|
4553
|
+
(milestone) => compileMilestone(milestone, config, policyIntercept, policyGuidance)
|
|
4554
|
+
);
|
|
4555
|
+
const classifyVariant4 = resolveSlotHook(config.classifyVariant, "classify");
|
|
4556
|
+
const generateBootstrapContext2 = resolveSlotHook(config.bootstrap, "bootstrap");
|
|
4557
|
+
const buildCandidateBlock2 = resolveSlotHook(config.candidateBlock, "candidateBlock");
|
|
4558
|
+
return {
|
|
4559
|
+
name: config.name,
|
|
4560
|
+
phases,
|
|
4561
|
+
...config.stallPolicy ? { stallPolicy: config.stallPolicy } : {},
|
|
4562
|
+
...classifyVariant4 ? { classifyVariant: classifyVariant4 } : {},
|
|
4563
|
+
...generateBootstrapContext2 ? { generateBootstrapContext: generateBootstrapContext2 } : {},
|
|
4564
|
+
...buildCandidateBlock2 ? { buildCandidateBlock: buildCandidateBlock2 } : {}
|
|
4565
|
+
};
|
|
4566
|
+
}
|
|
4567
|
+
|
|
4313
4568
|
// src/workflows/default-workflow.ts
|
|
4314
4569
|
function isExternalTask(state) {
|
|
4315
4570
|
return state.workflowVariant === "external";
|
|
@@ -4459,6 +4714,45 @@ function summarizeTextBlock(value, maxLines = 4) {
|
|
|
4459
4714
|
if (!text) return "";
|
|
4460
4715
|
return text.split("\n").map((line) => line.trim()).filter(Boolean).slice(0, maxLines).join(" | ").slice(0, 240);
|
|
4461
4716
|
}
|
|
4717
|
+
function interceptProductWriteTarget(toolName, normalizedPathArg, ctx, guardLabel) {
|
|
4718
|
+
const normalizedPlanPath = ctx.state.planPath ? ctx.normalizePath(ctx.state.planPath) : void 0;
|
|
4719
|
+
const normalizedBestCandidatePath = ctx.state.bestCandidatePath ? ctx.normalizePath(ctx.state.bestCandidatePath) : void 0;
|
|
4720
|
+
if (!ctx.state.isCreationTask && normalizedPathArg && normalizedPathArg !== normalizedPlanPath) {
|
|
4721
|
+
const allowedWriteTargets = new Set(
|
|
4722
|
+
[
|
|
4723
|
+
normalizedPlanPath,
|
|
4724
|
+
normalizedBestCandidatePath,
|
|
4725
|
+
...(ctx.state.recentReadPaths || []).map((readPath) => ctx.normalizePath(readPath)),
|
|
4726
|
+
...ctx.trace.readPaths.map((readPath) => ctx.normalizePath(readPath))
|
|
4727
|
+
].filter((value) => Boolean(value))
|
|
4728
|
+
);
|
|
4729
|
+
if (!allowedWriteTargets.has(normalizedPathArg)) {
|
|
4730
|
+
return [
|
|
4731
|
+
`Blocked by marathon ${guardLabel}: ${toolName} is limited to the confirmed target, the plan file, or files already discovered/read for this task.`,
|
|
4732
|
+
`Do not create scratch files like "${normalizedPathArg}".`,
|
|
4733
|
+
normalizedBestCandidatePath ? `Edit "${normalizedBestCandidatePath}" or another previously discovered repo file instead.` : "Read the current target file before writing."
|
|
4734
|
+
].join(" ");
|
|
4735
|
+
}
|
|
4736
|
+
}
|
|
4737
|
+
if (ctx.state.isCreationTask && normalizedPathArg && normalizedPathArg !== normalizedPlanPath) {
|
|
4738
|
+
const outputRoot = ctx.state.outputRoot ? ctx.state.outputRoot.trim().replace(/\\/g, "/").replace(/\/+/g, "/").replace(/\/$/, "") || void 0 : void 0;
|
|
4739
|
+
if (!outputRoot) {
|
|
4740
|
+
return [
|
|
4741
|
+
`Blocked by marathon ${guardLabel}: creation tasks require outputRoot. Writes outside the plan are not allowed.`,
|
|
4742
|
+
`Plan path: "${normalizedPlanPath}". Create files only under the configured output root.`
|
|
4743
|
+
].join(" ");
|
|
4744
|
+
}
|
|
4745
|
+
const rootPrefix = outputRoot + "/";
|
|
4746
|
+
const isUnderRoot = normalizedPathArg === outputRoot || normalizedPathArg.startsWith(rootPrefix);
|
|
4747
|
+
if (!isUnderRoot) {
|
|
4748
|
+
return [
|
|
4749
|
+
`Blocked by marathon ${guardLabel}: ${toolName} must target the plan or paths under outputRoot "${outputRoot}/".`,
|
|
4750
|
+
`"${normalizedPathArg}" is outside the allowed output root.`
|
|
4751
|
+
].join(" ");
|
|
4752
|
+
}
|
|
4753
|
+
}
|
|
4754
|
+
return void 0;
|
|
4755
|
+
}
|
|
4462
4756
|
var researchPhase = {
|
|
4463
4757
|
name: "research",
|
|
4464
4758
|
description: "Inspect the repo and identify the correct target file",
|
|
@@ -4543,11 +4837,15 @@ var researchPhase = {
|
|
|
4543
4837
|
const normalizedPathArg2 = typeof _args.path === "string" && _args.path.trim() ? ctx.normalizePath(String(_args.path)) : void 0;
|
|
4544
4838
|
const normalizedPlanPath = ctx.state.planPath ? ctx.normalizePath(ctx.state.planPath) : void 0;
|
|
4545
4839
|
if (normalizedPathArg2 && normalizedPlanPath && normalizedPathArg2 !== normalizedPlanPath) {
|
|
4546
|
-
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
|
|
4550
|
-
|
|
4840
|
+
const planWritten = ctx.trace.planWritten || Boolean(ctx.state.planWritten);
|
|
4841
|
+
if (!planWritten) {
|
|
4842
|
+
return [
|
|
4843
|
+
`Blocked by marathon research guard: ${toolName} cannot create product files during the research phase.`,
|
|
4844
|
+
"Complete research first, then the system will advance you to planning.",
|
|
4845
|
+
`You may write the plan to "${normalizedPlanPath}" once research is complete.`
|
|
4846
|
+
].join(" ");
|
|
4847
|
+
}
|
|
4848
|
+
return interceptProductWriteTarget(toolName, normalizedPathArg2, ctx, "research guard");
|
|
4551
4849
|
}
|
|
4552
4850
|
}
|
|
4553
4851
|
return void 0;
|
|
@@ -4670,19 +4968,24 @@ var planningPhase = {
|
|
|
4670
4968
|
"Research is complete. Write the implementation plan for building this from scratch.",
|
|
4671
4969
|
`Write the plan markdown to exactly: ${planPath}`,
|
|
4672
4970
|
"List the files you will create, their locations, purpose, and any dependencies to install.",
|
|
4971
|
+
...state.outputRoot ? [
|
|
4972
|
+
`All new files must be created under "${state.outputRoot}" \u2014 writes outside that directory are blocked, so plan every file location inside it.`
|
|
4973
|
+
] : [],
|
|
4673
4974
|
'Include a "Verification steps" section listing the concrete checks you will run before TASK_COMPLETE.',
|
|
4674
|
-
"If the plan already exists, update that same plan file instead of creating a different one."
|
|
4975
|
+
"If the plan already exists, update that same plan file instead of creating a different one.",
|
|
4976
|
+
"Once the plan is written, you may begin creating the planned files in the same turn."
|
|
4675
4977
|
].join("\n");
|
|
4676
4978
|
}
|
|
4677
4979
|
return [
|
|
4678
4980
|
"--- Workflow Phase: Planning ---",
|
|
4679
4981
|
"Research is complete. Your current job is to write the implementation plan before any product-file edits.",
|
|
4680
4982
|
`Write the plan markdown to exactly: ${planPath}`,
|
|
4681
|
-
"Do NOT edit the target product file
|
|
4983
|
+
"Do NOT edit the target product file before the plan exists.",
|
|
4682
4984
|
"The plan should summarize UX findings, explain why the current best candidate is the right file, and list concrete execution steps.",
|
|
4683
4985
|
'The plan must include a "Preserve existing functionality" section that lists current behaviors, linked files, integrations, and constraints that must keep working.',
|
|
4684
4986
|
'The plan must include a "Verification steps" section listing the concrete checks you will run before TASK_COMPLETE.',
|
|
4685
|
-
"If the plan already exists, update that same plan file instead of creating a different one."
|
|
4987
|
+
"If the plan already exists, update that same plan file instead of creating a different one.",
|
|
4988
|
+
"Once the plan is written, you may begin editing the target file in the same turn."
|
|
4686
4989
|
].join("\n");
|
|
4687
4990
|
},
|
|
4688
4991
|
buildToolGuidance(state) {
|
|
@@ -4710,10 +5013,14 @@ var planningPhase = {
|
|
|
4710
5013
|
const normalizedPlanPath = ctx.state.planPath ? ctx.normalizePath(ctx.state.planPath) : void 0;
|
|
4711
5014
|
const isWriteLikeTool = toolName === "write_file" || toolName === "edit_file" || toolName === "restore_file_checkpoint";
|
|
4712
5015
|
if (isWriteLikeTool && normalizedPathArg && normalizedPlanPath && normalizedPathArg !== normalizedPlanPath) {
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
5016
|
+
const planWritten = ctx.trace.planWritten || Boolean(ctx.state.planWritten);
|
|
5017
|
+
if (!planWritten) {
|
|
5018
|
+
return [
|
|
5019
|
+
`Blocked by marathon planning guard: ${toolName} must target the exact plan path during planning.`,
|
|
5020
|
+
`Write the plan to "${normalizedPlanPath}" before editing any product files.`
|
|
5021
|
+
].join(" ");
|
|
5022
|
+
}
|
|
5023
|
+
return interceptProductWriteTarget(toolName, normalizedPathArg, ctx, "planning guard");
|
|
4717
5024
|
}
|
|
4718
5025
|
return void 0;
|
|
4719
5026
|
},
|
|
@@ -4773,6 +5080,9 @@ var executionPhase = {
|
|
|
4773
5080
|
},
|
|
4774
5081
|
buildToolGuidance(state) {
|
|
4775
5082
|
return [
|
|
5083
|
+
...state.isCreationTask && state.outputRoot ? [
|
|
5084
|
+
`Creation guard: create new files under "${state.outputRoot}". Writes outside it are blocked \u2014 the plan file is the only exception.`
|
|
5085
|
+
] : [],
|
|
4776
5086
|
...state.bestCandidatePath ? [
|
|
4777
5087
|
`Execution-phase guard: broad discovery tools (search_repo, glob_files, tree_directory, list_directory) are locked while executing against "${state.bestCandidatePath}".`
|
|
4778
5088
|
] : [
|
|
@@ -4814,40 +5124,13 @@ var executionPhase = {
|
|
|
4814
5124
|
`After that, you may update "${normalizedPlanPath}" with progress.`
|
|
4815
5125
|
].join(" ");
|
|
4816
5126
|
}
|
|
4817
|
-
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
].filter((value) => Boolean(value))
|
|
4825
|
-
);
|
|
4826
|
-
if (!allowedWriteTargets.has(normalizedPathArg)) {
|
|
4827
|
-
return [
|
|
4828
|
-
`Blocked by marathon execution guard: ${toolName} is limited to the confirmed target, the plan file, or files already discovered/read for this task.`,
|
|
4829
|
-
`Do not create scratch files like "${normalizedPathArg}".`,
|
|
4830
|
-
normalizedBestCandidatePath ? `Edit "${normalizedBestCandidatePath}" or another previously discovered repo file instead.` : "Read the current target file before writing."
|
|
4831
|
-
].join(" ");
|
|
4832
|
-
}
|
|
4833
|
-
}
|
|
4834
|
-
if (ctx.state.isCreationTask && normalizedPathArg && normalizedPathArg !== normalizedPlanPath) {
|
|
4835
|
-
const outputRoot = ctx.state.outputRoot ? ctx.state.outputRoot.trim().replace(/\\/g, "/").replace(/\/+/g, "/").replace(/\/$/, "") || void 0 : void 0;
|
|
4836
|
-
if (!outputRoot) {
|
|
4837
|
-
return [
|
|
4838
|
-
`Blocked by marathon execution guard: creation tasks require outputRoot. Writes outside the plan are not allowed.`,
|
|
4839
|
-
`Plan path: "${normalizedPlanPath}". Create files only under the configured output root.`
|
|
4840
|
-
].join(" ");
|
|
4841
|
-
}
|
|
4842
|
-
const rootPrefix = outputRoot + "/";
|
|
4843
|
-
const isUnderRoot = normalizedPathArg === outputRoot || normalizedPathArg.startsWith(rootPrefix);
|
|
4844
|
-
if (!isUnderRoot) {
|
|
4845
|
-
return [
|
|
4846
|
-
`Blocked by marathon execution guard: ${toolName} must target the plan or paths under outputRoot "${outputRoot}/".`,
|
|
4847
|
-
`"${normalizedPathArg}" is outside the allowed output root.`
|
|
4848
|
-
].join(" ");
|
|
4849
|
-
}
|
|
4850
|
-
}
|
|
5127
|
+
const writeTargetBlock = interceptProductWriteTarget(
|
|
5128
|
+
toolName,
|
|
5129
|
+
normalizedPathArg,
|
|
5130
|
+
ctx,
|
|
5131
|
+
"execution guard"
|
|
5132
|
+
);
|
|
5133
|
+
if (writeTargetBlock) return writeTargetBlock;
|
|
4851
5134
|
}
|
|
4852
5135
|
return void 0;
|
|
4853
5136
|
},
|
|
@@ -5080,13 +5363,163 @@ function buildCandidateBlock(state) {
|
|
|
5080
5363
|
...state.bestCandidateReason ? [`Why: ${state.bestCandidateReason}`] : []
|
|
5081
5364
|
].join("\n");
|
|
5082
5365
|
}
|
|
5083
|
-
var
|
|
5366
|
+
var builtinHooksRegistered = false;
|
|
5367
|
+
function ensureDefaultWorkflowHooks() {
|
|
5368
|
+
if (builtinHooksRegistered) return;
|
|
5369
|
+
builtinHooksRegistered = true;
|
|
5370
|
+
registerBuiltinWorkflowHook("builtin:classify-task-variant", {
|
|
5371
|
+
kind: "classify",
|
|
5372
|
+
fn: classifyVariant
|
|
5373
|
+
});
|
|
5374
|
+
registerBuiltinWorkflowHook("builtin:repo-bootstrap-discovery", {
|
|
5375
|
+
kind: "bootstrap",
|
|
5376
|
+
fn: generateBootstrapContext
|
|
5377
|
+
});
|
|
5378
|
+
registerBuiltinWorkflowHook("builtin:best-candidate-block", {
|
|
5379
|
+
kind: "candidateBlock",
|
|
5380
|
+
fn: buildCandidateBlock
|
|
5381
|
+
});
|
|
5382
|
+
registerBuiltinWorkflowHook("builtin:research-instructions", {
|
|
5383
|
+
kind: "instructions",
|
|
5384
|
+
fn: researchPhase.buildInstructions
|
|
5385
|
+
});
|
|
5386
|
+
registerBuiltinWorkflowHook("builtin:research-tool-guidance", {
|
|
5387
|
+
kind: "toolGuidance",
|
|
5388
|
+
fn: researchPhase.buildToolGuidance
|
|
5389
|
+
});
|
|
5390
|
+
registerBuiltinWorkflowHook("builtin:research-complete", {
|
|
5391
|
+
kind: "completion",
|
|
5392
|
+
fn: researchPhase.isComplete
|
|
5393
|
+
});
|
|
5394
|
+
registerBuiltinWorkflowHook("builtin:research-transition-summary", {
|
|
5395
|
+
kind: "transitionSummary",
|
|
5396
|
+
fn: researchPhase.buildTransitionSummary
|
|
5397
|
+
});
|
|
5398
|
+
registerBuiltinWorkflowHook("builtin:research-guard", {
|
|
5399
|
+
kind: "intercept",
|
|
5400
|
+
fn: researchPhase.interceptToolCall
|
|
5401
|
+
});
|
|
5402
|
+
registerBuiltinWorkflowHook("builtin:research-recovery", {
|
|
5403
|
+
kind: "recovery",
|
|
5404
|
+
fn: researchPhase.buildRecoveryMessage
|
|
5405
|
+
});
|
|
5406
|
+
registerBuiltinWorkflowHook("builtin:research-force-end-turn", {
|
|
5407
|
+
kind: "forceEndTurn",
|
|
5408
|
+
fn: researchPhase.shouldForceEndTurn
|
|
5409
|
+
});
|
|
5410
|
+
registerBuiltinWorkflowHook("builtin:research-accept-completion", {
|
|
5411
|
+
kind: "acceptCompletion",
|
|
5412
|
+
fn: researchPhase.canAcceptCompletion
|
|
5413
|
+
});
|
|
5414
|
+
registerBuiltinWorkflowHook("builtin:planning-instructions", {
|
|
5415
|
+
kind: "instructions",
|
|
5416
|
+
fn: planningPhase.buildInstructions
|
|
5417
|
+
});
|
|
5418
|
+
registerBuiltinWorkflowHook("builtin:planning-tool-guidance", {
|
|
5419
|
+
kind: "toolGuidance",
|
|
5420
|
+
fn: planningPhase.buildToolGuidance
|
|
5421
|
+
});
|
|
5422
|
+
registerBuiltinWorkflowHook("builtin:planning-complete", {
|
|
5423
|
+
kind: "completion",
|
|
5424
|
+
fn: planningPhase.isComplete
|
|
5425
|
+
});
|
|
5426
|
+
registerBuiltinWorkflowHook("builtin:planning-transition-summary", {
|
|
5427
|
+
kind: "transitionSummary",
|
|
5428
|
+
fn: planningPhase.buildTransitionSummary
|
|
5429
|
+
});
|
|
5430
|
+
registerBuiltinWorkflowHook("builtin:planning-guard", {
|
|
5431
|
+
kind: "intercept",
|
|
5432
|
+
fn: planningPhase.interceptToolCall
|
|
5433
|
+
});
|
|
5434
|
+
registerBuiltinWorkflowHook("builtin:planning-recovery", {
|
|
5435
|
+
kind: "recovery",
|
|
5436
|
+
fn: planningPhase.buildRecoveryMessage
|
|
5437
|
+
});
|
|
5438
|
+
registerBuiltinWorkflowHook("builtin:planning-force-end-turn", {
|
|
5439
|
+
kind: "forceEndTurn",
|
|
5440
|
+
fn: planningPhase.shouldForceEndTurn
|
|
5441
|
+
});
|
|
5442
|
+
registerBuiltinWorkflowHook("builtin:execution-instructions", {
|
|
5443
|
+
kind: "instructions",
|
|
5444
|
+
fn: executionPhase.buildInstructions
|
|
5445
|
+
});
|
|
5446
|
+
registerBuiltinWorkflowHook("builtin:execution-tool-guidance", {
|
|
5447
|
+
kind: "toolGuidance",
|
|
5448
|
+
fn: executionPhase.buildToolGuidance
|
|
5449
|
+
});
|
|
5450
|
+
registerBuiltinWorkflowHook("builtin:execution-guard", {
|
|
5451
|
+
kind: "intercept",
|
|
5452
|
+
fn: executionPhase.interceptToolCall
|
|
5453
|
+
});
|
|
5454
|
+
registerBuiltinWorkflowHook("builtin:execution-recovery", {
|
|
5455
|
+
kind: "recovery",
|
|
5456
|
+
fn: executionPhase.buildRecoveryMessage
|
|
5457
|
+
});
|
|
5458
|
+
registerBuiltinWorkflowHook("builtin:execution-force-end-turn", {
|
|
5459
|
+
kind: "forceEndTurn",
|
|
5460
|
+
fn: executionPhase.shouldForceEndTurn
|
|
5461
|
+
});
|
|
5462
|
+
registerBuiltinWorkflowHook("builtin:execution-accept-completion", {
|
|
5463
|
+
kind: "acceptCompletion",
|
|
5464
|
+
fn: executionPhase.canAcceptCompletion
|
|
5465
|
+
});
|
|
5466
|
+
}
|
|
5467
|
+
var defaultWorkflowConfig = {
|
|
5084
5468
|
name: "default",
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5469
|
+
// Empty-session escalation. The counter only counts tool actions, so
|
|
5470
|
+
// narration-only sessions ("I'll create the files now" with no tool calls)
|
|
5471
|
+
// escalate here even though the phase recovery conditions keyed on
|
|
5472
|
+
// hadTextOutput skip them: nudge after the first actionless session, signal
|
|
5473
|
+
// model escalation after the second (a no-op unless the caller configured a
|
|
5474
|
+
// fallback model), and stop as 'stalled' after the third — the same total
|
|
5475
|
+
// session budget as before stallPolicy existed.
|
|
5476
|
+
stallPolicy: { nudgeAfter: 1, escalateModelAfter: 2, stopAfter: 3 },
|
|
5477
|
+
classifyVariant: "builtin:classify-task-variant",
|
|
5478
|
+
bootstrap: "builtin:repo-bootstrap-discovery",
|
|
5479
|
+
candidateBlock: "builtin:best-candidate-block",
|
|
5480
|
+
milestones: [
|
|
5481
|
+
{
|
|
5482
|
+
name: "research",
|
|
5483
|
+
description: "Inspect the repo and identify the correct target file",
|
|
5484
|
+
instructions: "builtin:research-instructions",
|
|
5485
|
+
toolGuidance: "builtin:research-tool-guidance",
|
|
5486
|
+
completionCriteria: { type: "builtin:research-complete" },
|
|
5487
|
+
intercept: "builtin:research-guard",
|
|
5488
|
+
transitionSummary: "builtin:research-transition-summary",
|
|
5489
|
+
recovery: "builtin:research-recovery",
|
|
5490
|
+
forceEndTurn: "builtin:research-force-end-turn",
|
|
5491
|
+
canAcceptCompletion: "builtin:research-accept-completion"
|
|
5492
|
+
},
|
|
5493
|
+
{
|
|
5494
|
+
name: "planning",
|
|
5495
|
+
description: "Write the implementation plan before editing product files",
|
|
5496
|
+
instructions: "builtin:planning-instructions",
|
|
5497
|
+
toolGuidance: "builtin:planning-tool-guidance",
|
|
5498
|
+
completionCriteria: { type: "builtin:planning-complete" },
|
|
5499
|
+
intercept: "builtin:planning-guard",
|
|
5500
|
+
transitionSummary: "builtin:planning-transition-summary",
|
|
5501
|
+
recovery: "builtin:planning-recovery",
|
|
5502
|
+
forceEndTurn: "builtin:planning-force-end-turn"
|
|
5503
|
+
// canAcceptCompletion intentionally absent: the hand-written planning
|
|
5504
|
+
// phase never defined it, and the SDK accepts completion when the slot
|
|
5505
|
+
// is undefined. Keep parity.
|
|
5506
|
+
},
|
|
5507
|
+
{
|
|
5508
|
+
name: "execution",
|
|
5509
|
+
description: "Execute the plan by editing target files",
|
|
5510
|
+
instructions: "builtin:execution-instructions",
|
|
5511
|
+
toolGuidance: "builtin:execution-tool-guidance",
|
|
5512
|
+
// Execution never auto-advances; completion is agent-driven via TASK_COMPLETE
|
|
5513
|
+
completionCriteria: { type: "never" },
|
|
5514
|
+
intercept: "builtin:execution-guard",
|
|
5515
|
+
recovery: "builtin:execution-recovery",
|
|
5516
|
+
forceEndTurn: "builtin:execution-force-end-turn",
|
|
5517
|
+
canAcceptCompletion: "builtin:execution-accept-completion"
|
|
5518
|
+
}
|
|
5519
|
+
]
|
|
5089
5520
|
};
|
|
5521
|
+
ensureDefaultWorkflowHooks();
|
|
5522
|
+
var defaultWorkflow = compileWorkflowConfig(defaultWorkflowConfig);
|
|
5090
5523
|
|
|
5091
5524
|
// src/workflows/deploy-workflow.ts
|
|
5092
5525
|
var scaffoldPhase = {
|
|
@@ -5498,6 +5931,34 @@ var gameWorkflow = {
|
|
|
5498
5931
|
}
|
|
5499
5932
|
};
|
|
5500
5933
|
|
|
5934
|
+
// src/workflows/stall-policy.ts
|
|
5935
|
+
var DEFAULT_STALL_STOP_AFTER = 3;
|
|
5936
|
+
function isPositiveInteger(value) {
|
|
5937
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 1;
|
|
5938
|
+
}
|
|
5939
|
+
function resolveStallStopAfter(policy) {
|
|
5940
|
+
return isPositiveInteger(policy?.stopAfter) ? policy.stopAfter : DEFAULT_STALL_STOP_AFTER;
|
|
5941
|
+
}
|
|
5942
|
+
function shouldRequestModelEscalation(policy, consecutiveEmptySessions) {
|
|
5943
|
+
const threshold = policy?.escalateModelAfter;
|
|
5944
|
+
if (!isPositiveInteger(threshold)) return false;
|
|
5945
|
+
return consecutiveEmptySessions === threshold;
|
|
5946
|
+
}
|
|
5947
|
+
function shouldInjectEmptySessionNudge(policy, consecutiveEmptySessions) {
|
|
5948
|
+
const threshold = policy?.nudgeAfter;
|
|
5949
|
+
if (!isPositiveInteger(threshold)) return false;
|
|
5950
|
+
return consecutiveEmptySessions >= threshold;
|
|
5951
|
+
}
|
|
5952
|
+
function buildEmptySessionNudge(consecutiveEmptySessions) {
|
|
5953
|
+
const sessionPhrase = consecutiveEmptySessions === 1 ? "Your previous session ended" : `Your previous ${consecutiveEmptySessions} sessions ended`;
|
|
5954
|
+
return [
|
|
5955
|
+
"Recovery instruction:",
|
|
5956
|
+
`${sessionPhrase} without a single tool call. Describing what you plan to do does nothing \u2014 only tool calls make progress.`,
|
|
5957
|
+
"Your next response MUST include at least one tool call (for example write_file, edit_file, read_file, or run_check) that advances the task.",
|
|
5958
|
+
"If a previous tool call was blocked, re-read the block message and satisfy its requirement instead of ending the turn."
|
|
5959
|
+
].join("\n");
|
|
5960
|
+
}
|
|
5961
|
+
|
|
5501
5962
|
// src/endpoints.ts
|
|
5502
5963
|
var FlowsEndpoint = class {
|
|
5503
5964
|
constructor(client) {
|
|
@@ -8044,8 +8505,11 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
8044
8505
|
}
|
|
8045
8506
|
buildStuckTurnRecoveryMessage(state, workflow) {
|
|
8046
8507
|
const currentPhase = workflow.phases.find((p) => p.name === state.workflowPhase);
|
|
8047
|
-
|
|
8048
|
-
|
|
8508
|
+
const phaseMessage = currentPhase?.buildRecoveryMessage?.(state);
|
|
8509
|
+
if (phaseMessage) return phaseMessage;
|
|
8510
|
+
const emptySessions = state.consecutiveEmptySessions || 0;
|
|
8511
|
+
if (shouldInjectEmptySessionNudge(workflow.stallPolicy, emptySessions)) {
|
|
8512
|
+
return buildEmptySessionNudge(emptySessions);
|
|
8049
8513
|
}
|
|
8050
8514
|
return void 0;
|
|
8051
8515
|
}
|
|
@@ -8171,6 +8635,10 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
8171
8635
|
state.originalMessage = options.message;
|
|
8172
8636
|
}
|
|
8173
8637
|
const queuedSteeringMessages = options.getQueuedUserMessages?.() ?? [];
|
|
8638
|
+
if (queuedSteeringMessages.length > 0) {
|
|
8639
|
+
state.consecutiveEmptySessions = 0;
|
|
8640
|
+
state.stallEscalationRequested = void 0;
|
|
8641
|
+
}
|
|
8174
8642
|
const preparedSession = await this.prepareSessionContext(
|
|
8175
8643
|
options.message,
|
|
8176
8644
|
state,
|
|
@@ -8411,11 +8879,15 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
8411
8879
|
} else {
|
|
8412
8880
|
state.lastCompletionRejectionReason = void 0;
|
|
8413
8881
|
}
|
|
8414
|
-
const sessionHadActions = sessionTrace.wroteFiles || sessionTrace.readFiles || sessionTrace.discoveryPerformed || sessionTrace.verificationAttempted;
|
|
8882
|
+
const sessionHadActions = sessionTrace.wroteFiles || sessionTrace.readFiles || sessionTrace.discoveryPerformed || sessionTrace.verificationAttempted || options.hasQueuedUserMessages?.() === true;
|
|
8415
8883
|
if (sessionHadActions) {
|
|
8416
8884
|
state.consecutiveEmptySessions = 0;
|
|
8885
|
+
state.stallEscalationRequested = void 0;
|
|
8417
8886
|
} else {
|
|
8418
8887
|
state.consecutiveEmptySessions = (state.consecutiveEmptySessions || 0) + 1;
|
|
8888
|
+
if (shouldRequestModelEscalation(workflow.stallPolicy, state.consecutiveEmptySessions)) {
|
|
8889
|
+
state.stallEscalationRequested = true;
|
|
8890
|
+
}
|
|
8419
8891
|
}
|
|
8420
8892
|
if (sessionResult.stopReason === "complete" && !detectedTaskCompletion) {
|
|
8421
8893
|
const currentPhase = workflow.phases.find((p) => p.name === state.workflowPhase);
|
|
@@ -8444,7 +8916,7 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
8444
8916
|
state.status = "budget_exceeded";
|
|
8445
8917
|
} else if (acceptedTaskCompletion) {
|
|
8446
8918
|
state.status = "complete";
|
|
8447
|
-
} else if ((state.consecutiveEmptySessions || 0) >=
|
|
8919
|
+
} else if ((state.consecutiveEmptySessions || 0) >= resolveStallStopAfter(workflow.stallPolicy)) {
|
|
8448
8920
|
state.status = "stalled";
|
|
8449
8921
|
} else if (maxCost && state.totalCost >= maxCost) {
|
|
8450
8922
|
state.status = "budget_exceeded";
|
|
@@ -10993,6 +11465,8 @@ export {
|
|
|
10993
11465
|
ClientTokensEndpoint,
|
|
10994
11466
|
ContextTemplatesEndpoint,
|
|
10995
11467
|
ConversationsEndpoint,
|
|
11468
|
+
DEFAULT_RECOVERY_AFTER_EMPTY_SESSIONS,
|
|
11469
|
+
DEFAULT_STALL_STOP_AFTER,
|
|
10996
11470
|
DispatchEndpoint,
|
|
10997
11471
|
EvalBuilder,
|
|
10998
11472
|
EvalEndpoint,
|
|
@@ -11030,25 +11504,34 @@ export {
|
|
|
11030
11504
|
UsersEndpoint,
|
|
11031
11505
|
applyGeneratedRuntimeToolProposalToDispatchRequest,
|
|
11032
11506
|
attachRuntimeToolsToDispatchRequest,
|
|
11507
|
+
buildEmptySessionNudge,
|
|
11033
11508
|
buildGeneratedRuntimeToolGateOutput,
|
|
11034
11509
|
buildLedgerOffloadReference,
|
|
11510
|
+
buildPolicyGuidance,
|
|
11035
11511
|
buildSendViewOffloadMarker,
|
|
11512
|
+
compileWorkflowConfig,
|
|
11036
11513
|
computeAgentContentHash,
|
|
11037
11514
|
computeFlowContentHash,
|
|
11038
11515
|
createClient,
|
|
11039
11516
|
createExternalTool,
|
|
11040
11517
|
defaultWorkflow,
|
|
11518
|
+
defaultWorkflowConfig,
|
|
11041
11519
|
defineAgent,
|
|
11042
11520
|
defineFlow,
|
|
11521
|
+
definePlaybook,
|
|
11043
11522
|
deployWorkflow,
|
|
11523
|
+
ensureDefaultWorkflowHooks,
|
|
11044
11524
|
evaluateGeneratedRuntimeToolProposal,
|
|
11045
11525
|
extractDeclaredToolResultChars,
|
|
11046
11526
|
gameWorkflow,
|
|
11047
11527
|
getDefaultPlanPath,
|
|
11048
11528
|
getLikelySupportingCandidatePaths,
|
|
11529
|
+
interpolateWorkflowTemplate,
|
|
11049
11530
|
isDiscoveryToolName,
|
|
11050
11531
|
isMarathonArtifactPath,
|
|
11051
11532
|
isPreservationSensitiveTask,
|
|
11533
|
+
isWorkflowHookRef,
|
|
11534
|
+
listWorkflowHooks,
|
|
11052
11535
|
normalizeAgentDefinition,
|
|
11053
11536
|
normalizeCandidatePath,
|
|
11054
11537
|
parseFinalBuffer,
|
|
@@ -11056,6 +11539,12 @@ export {
|
|
|
11056
11539
|
parseOffloadedOutputId,
|
|
11057
11540
|
parseSSEChunk,
|
|
11058
11541
|
processStream,
|
|
11542
|
+
registerWorkflowHook,
|
|
11543
|
+
resolveStallStopAfter,
|
|
11544
|
+
resolveWorkflowHook,
|
|
11059
11545
|
sanitizeTaskSlug,
|
|
11060
|
-
|
|
11546
|
+
shouldInjectEmptySessionNudge,
|
|
11547
|
+
shouldRequestModelEscalation,
|
|
11548
|
+
streamEvents,
|
|
11549
|
+
unregisterWorkflowHook
|
|
11061
11550
|
};
|