@wrongstack/core 0.308.1 → 0.308.2
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/coordination/index.d.ts +1 -0
- package/dist/coordination/index.js +108 -11
- package/dist/coordination/task-boundary.d.ts +64 -0
- package/dist/defaults/index.js +111 -15
- package/dist/execution/index.js +7 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.js +120 -15
- package/dist/infrastructure/index.js +1 -1
- package/dist/storage/index.js +1 -1
- package/dist/types/context-window.d.ts +18 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.js +9 -3
- package/instructions/coordination/director-preamble.md +9 -1
- package/instructions/coordination/subagent-baseline.md +4 -0
- package/package.json +5 -4
|
@@ -69,6 +69,7 @@ TIMEOUT_PREEMPT_FRACTION, } from './subagent-budget.js';
|
|
|
69
69
|
export { assignNickname } from './subagent-nicknames.js';
|
|
70
70
|
export { formatSubagentStructuredReport, MAX_SUBAGENT_STRUCTURED_REPORT_CHARS, makeSubagentResultTool, normalizeSubagentStructuredReport, readSubagentStructuredReport, SUBAGENT_STRUCTURED_REPORT_META_KEY, } from './subagent-result-tool.js';
|
|
71
71
|
export { startTechStackConsumer, type TechStackConsumerOptions, } from './techstack-mailbox-consumer.js';
|
|
72
|
+
export { composeBoundedTaskDescription, type TaskBoundary, parseTaskBoundary, renderTaskBoundaryBlock, taskBoundarySchemaProperties, } from './task-boundary.js';
|
|
72
73
|
export { type FleetWorktreePolicy, resolveSubagentWorktreeDecision, subagentNeedsWorktree, WorktreeIntegrationError, type WorktreeIsolationDecision, type WorktreeTaskRunnerOptions, type WorktreeTaskStateUpdate, wrapSubagentRunnerWithWorktrees, } from './worktree-task-runner.js';
|
|
73
74
|
export { collabInjectMiddleware, collabPauseMiddleware, } from '../middleware/collab-pause.js';
|
|
74
75
|
export { AdaptiveConcurrencyController, type AdaptiveConcurrencyState, } from './adaptive-concurrency.js';
|
|
@@ -10481,6 +10481,80 @@ var FLEET_ROSTER_WITHACP = {
|
|
|
10481
10481
|
...Object.fromEntries(ACP_AGENTS.map((a) => [a.role, a]))
|
|
10482
10482
|
};
|
|
10483
10483
|
|
|
10484
|
+
// src/coordination/task-boundary.ts
|
|
10485
|
+
var PLACEHOLDER_VALUES = /* @__PURE__ */ new Set([
|
|
10486
|
+
"n/a",
|
|
10487
|
+
"na",
|
|
10488
|
+
"none",
|
|
10489
|
+
"nothing",
|
|
10490
|
+
"tbd",
|
|
10491
|
+
"todo",
|
|
10492
|
+
"unknown",
|
|
10493
|
+
"unspecified",
|
|
10494
|
+
"-",
|
|
10495
|
+
"\u2014",
|
|
10496
|
+
".",
|
|
10497
|
+
"as above",
|
|
10498
|
+
"same as above",
|
|
10499
|
+
"see above",
|
|
10500
|
+
"see task",
|
|
10501
|
+
"same as task"
|
|
10502
|
+
]);
|
|
10503
|
+
var isPlaceholder = (value) => PLACEHOLDER_VALUES.has(value.trim().toLowerCase());
|
|
10504
|
+
var MIN_SCOPE_CHARS = 8;
|
|
10505
|
+
var MIN_NON_GOAL_CHARS = 3;
|
|
10506
|
+
function parseTaskBoundary(raw) {
|
|
10507
|
+
const scope = typeof raw.scope === "string" ? raw.scope.trim() : "";
|
|
10508
|
+
if (scope.length < MIN_SCOPE_CHARS) {
|
|
10509
|
+
return {
|
|
10510
|
+
ok: false,
|
|
10511
|
+
error: `\`scope\` is missing or too vague \u2014 state in one concrete sentence what work this task covers (files, components, or commands in-bounds).`,
|
|
10512
|
+
hint: 'Example \u2014 scope: "Audit packages/core/src/parser/*.ts for unhandled token errors and report findings."'
|
|
10513
|
+
};
|
|
10514
|
+
}
|
|
10515
|
+
if (!Array.isArray(raw.outOfScope) || raw.outOfScope.length === 0) {
|
|
10516
|
+
return {
|
|
10517
|
+
ok: false,
|
|
10518
|
+
error: "`outOfScope` must be an array with at least one explicit non-goal \u2014 things the worker must NOT do.",
|
|
10519
|
+
hint: 'Example \u2014 outOfScope: ["Do not modify files outside packages/core", "Do not fix the bugs you find, only report them"].'
|
|
10520
|
+
};
|
|
10521
|
+
}
|
|
10522
|
+
const concrete = raw.outOfScope.filter((item) => typeof item === "string").map((item) => item.trim()).filter((item) => item.length >= MIN_NON_GOAL_CHARS && !isPlaceholder(item));
|
|
10523
|
+
if (concrete.length === 0) {
|
|
10524
|
+
return {
|
|
10525
|
+
ok: false,
|
|
10526
|
+
error: 'Every `outOfScope` entry was a placeholder ("none", "n/a", \u2026). Name at least one concrete non-goal: files or areas not to touch, changes not to make, features not to add.',
|
|
10527
|
+
hint: 'There is always an edge worth stating \u2014 "read-only, no edits", "no dependency changes", "do not touch other packages". If truly nothing comes to mind, the task is not decomposed enough yet.'
|
|
10528
|
+
};
|
|
10529
|
+
}
|
|
10530
|
+
return { ok: true, boundary: { scope, outOfScope: concrete } };
|
|
10531
|
+
}
|
|
10532
|
+
function renderTaskBoundaryBlock(boundary) {
|
|
10533
|
+
return [
|
|
10534
|
+
"\u2500\u2500 TASK BOUNDARY (hard contract \u2014 these lines define your edges) \u2500\u2500",
|
|
10535
|
+
`Scope (what this task covers):
|
|
10536
|
+
${boundary.scope}`,
|
|
10537
|
+
`Out of scope (explicit non-goals \u2014 do NOT do any of these):
|
|
10538
|
+
${boundary.outOfScope.map((item) => `- ${item}`).join("\n")}`
|
|
10539
|
+
].join("\n");
|
|
10540
|
+
}
|
|
10541
|
+
function composeBoundedTaskDescription(objective, boundary) {
|
|
10542
|
+
return `${objective.trim()}
|
|
10543
|
+
|
|
10544
|
+
${renderTaskBoundaryBlock(boundary)}`;
|
|
10545
|
+
}
|
|
10546
|
+
var taskBoundarySchemaProperties = {
|
|
10547
|
+
scope: {
|
|
10548
|
+
type: "string",
|
|
10549
|
+
description: "REQUIRED. One concrete sentence stating what work this task covers \u2014 the in-bounds. The call is rejected without it."
|
|
10550
|
+
},
|
|
10551
|
+
outOfScope: {
|
|
10552
|
+
type: "array",
|
|
10553
|
+
items: { type: "string", minLength: 1 },
|
|
10554
|
+
description: 'REQUIRED. At least one explicit non-goal the worker must NOT do (files/areas not to touch, changes not to make, features not to add). Placeholders like "none" are rejected.'
|
|
10555
|
+
}
|
|
10556
|
+
};
|
|
10557
|
+
|
|
10484
10558
|
// src/coordination/delegate-tool.ts
|
|
10485
10559
|
function createDelegateTool(opts) {
|
|
10486
10560
|
const defaultTimeoutMs = opts.defaultTimeoutMs ?? 4 * 60 * 60 * 1e3;
|
|
@@ -10490,8 +10564,9 @@ function createDelegateTool(opts) {
|
|
|
10490
10564
|
properties: {
|
|
10491
10565
|
task: {
|
|
10492
10566
|
type: "string",
|
|
10493
|
-
description: "
|
|
10567
|
+
description: "The objective \u2014 what the subagent should do, natural language, complete sentence(s). Pair it with the required `scope` and `outOfScope` boundary fields."
|
|
10494
10568
|
},
|
|
10569
|
+
...taskBoundarySchemaProperties,
|
|
10495
10570
|
role: {
|
|
10496
10571
|
type: "string",
|
|
10497
10572
|
description: rosterIds.length > 0 ? "Roster role id. Common: bug-hunter, security-scanner, refactor-planner, critic, audit-log, executor, shadow-agent, architect." : "No roster configured \u2014 pass `name` instead."
|
|
@@ -10549,12 +10624,12 @@ function createDelegateTool(opts) {
|
|
|
10549
10624
|
description: "Max fresh-worker continuations after budget exhaustion. Default 1. Each gets the prior partial report."
|
|
10550
10625
|
}
|
|
10551
10626
|
},
|
|
10552
|
-
required: ["task"]
|
|
10627
|
+
required: ["task", "scope", "outOfScope"]
|
|
10553
10628
|
};
|
|
10554
10629
|
return {
|
|
10555
10630
|
name: "delegate",
|
|
10556
10631
|
description: "Hand a piece of work to a subagent and block until it returns. This call is synchronous: the leader's iteration pauses for the full duration of the subagent's run. (Multiple `delegate` calls fired in the same assistant turn still parallelize through the provider's parallel-tool-call surface, but each one eats wall-clock time \u2014 so for fan-out you actually control, reach for the async path below.) Use `delegate` when your next step genuinely needs the subagent's verdict \u2014 a review, a fact-check, a sign-off. Has own context, own LLM call, auto-extending budget, and a partial-completion handoff path (maxHandoffs, default 1). Workers cannot recursively spawn.\n\n**Do NOT use `delegate` for long-running work.** While `delegate` is in flight, the leader is fully blocked \u2014 it cannot act on other tools, read mail, or react to the user. If the work might run for tens of minutes or hours (multi-file refactor, monorepo audit, long-running build/test, sweeping migration), the blocking call wastes the leader's time. Use the async tool family instead: `spawn_subagent` to create each worker (returns a `subagentId` immediately), `assign_task` to queue work on it (returns a `taskId` immediately), then `await_tasks` to retrieve results later. The leader keeps doing other work while the worker churns, and a worker that realizes its task will run long can mail the leader (type `steer` or `ask` via `mail_send`) saying *\"my task is going to run long, please spawn a subagent instead\"* so the leader re-dispatches asynchronously instead of waiting.\n\n**Do NOT use `delegate` for fan-out you control.** Multiple sequential `delegate` calls each block the leader, wasting wall-clock time. For independent investigations you want to run in parallel \u2014 security scan + bug hunt + perf review on the same PR \u2014 use the async tool family: `spawn_subagent` to create each worker (returns a `subagentId` immediately), `assign_task` to queue work on it (returns a `taskId` immediately), then the `await_tasks` tool with `{mode: 'any'}` to fold the first useful result into the next decision while the rest keep churning. Reach for `delegate` only when the result gates your next move AND the work is short enough that blocking the leader is acceptable.",
|
|
10557
|
-
usageHint: "Set `task` to a
|
|
10632
|
+
usageHint: "Set `task` to the objective, then make the edges explicit: `scope` (what the work covers) and `outOfScope` (at least one concrete non-goal) are REQUIRED \u2014 the call is rejected without them, and the worker treats the rendered boundary block as a hard contract. Pick `role` from roster or pass `name` for free-form. Reach for `delegate` only when the result gates your next move AND the work is short enough that blocking the leader is acceptable (minutes, not hours). For long-running work or fan-out you control, use `spawn_subagent` + `assign_task` + `await_tasks` instead. Raise `maxHandoffs` (default 1, cap 8) for multi-day or multi-refactor tasks; pass larger `timeoutMs`/`maxIterations`/`maxToolCalls` only when needed.",
|
|
10558
10633
|
permission: "auto",
|
|
10559
10634
|
mutating: false,
|
|
10560
10635
|
managesOwnTimeout: true,
|
|
@@ -10574,6 +10649,14 @@ function createDelegateTool(opts) {
|
|
|
10574
10649
|
error: "Delegation cancelled before spawn \u2014 the run was interrupted."
|
|
10575
10650
|
};
|
|
10576
10651
|
}
|
|
10652
|
+
const boundary = parseTaskBoundary(i);
|
|
10653
|
+
if (!boundary.ok) {
|
|
10654
|
+
return {
|
|
10655
|
+
ok: false,
|
|
10656
|
+
error: `delegate rejected \u2014 task boundary incomplete: ${boundary.error}`,
|
|
10657
|
+
hint: boundary.hint
|
|
10658
|
+
};
|
|
10659
|
+
}
|
|
10577
10660
|
const target = i.role ?? i.name ?? "subagent";
|
|
10578
10661
|
const launchModePreface = [
|
|
10579
10662
|
"Launch-mode guidance (delegate): you were launched via the synchronous `delegate` tool, so the leader is blocked on this call for the full duration of your run.",
|
|
@@ -10644,7 +10727,8 @@ function createDelegateTool(opts) {
|
|
|
10644
10727
|
const dir = director;
|
|
10645
10728
|
const maxHandoffs = Math.min(8, Math.max(0, Math.floor(i.maxHandoffs ?? 1)));
|
|
10646
10729
|
const handoffs = [];
|
|
10647
|
-
|
|
10730
|
+
const baseBrief = composeBoundedTaskDescription(i.task, boundary.boundary);
|
|
10731
|
+
let delegatedTask = baseBrief;
|
|
10648
10732
|
let handoffCount = 0;
|
|
10649
10733
|
for (; ; ) {
|
|
10650
10734
|
const attemptConfig = (() => {
|
|
@@ -10784,7 +10868,7 @@ function createDelegateTool(opts) {
|
|
|
10784
10868
|
remainingWork: continuation.remainingWork
|
|
10785
10869
|
});
|
|
10786
10870
|
handoffCount += 1;
|
|
10787
|
-
delegatedTask = buildHandoffTask(
|
|
10871
|
+
delegatedTask = buildHandoffTask(baseBrief, continuation, handoffCount, maxHandoffs);
|
|
10788
10872
|
continue;
|
|
10789
10873
|
}
|
|
10790
10874
|
const incomplete = result.report?.completion === "partial";
|
|
@@ -12688,8 +12772,9 @@ function makeAssignTool(director) {
|
|
|
12688
12772
|
description: {
|
|
12689
12773
|
type: "string",
|
|
12690
12774
|
minLength: 1,
|
|
12691
|
-
description: "The
|
|
12775
|
+
description: "The objective in natural language \u2014 what you want this subagent to do. Pair it with the required `scope` and `outOfScope` boundary fields."
|
|
12692
12776
|
},
|
|
12777
|
+
...taskBoundarySchemaProperties,
|
|
12693
12778
|
maxToolCalls: {
|
|
12694
12779
|
type: "number",
|
|
12695
12780
|
minimum: 1,
|
|
@@ -12697,20 +12782,28 @@ function makeAssignTool(director) {
|
|
|
12697
12782
|
},
|
|
12698
12783
|
timeoutMs: { type: "number", minimum: 1, description: "Optional per-task timeout in ms." }
|
|
12699
12784
|
},
|
|
12700
|
-
required: ["subagentId", "description"]
|
|
12785
|
+
required: ["subagentId", "description", "scope", "outOfScope"]
|
|
12701
12786
|
};
|
|
12702
12787
|
return {
|
|
12703
12788
|
name: "assign_task",
|
|
12704
|
-
description: "Queue a task on a previously spawned subagent. NON-BLOCKING: returns a `taskId` IMMEDIATELY \u2014 the subagent processes the task on its next iteration with its own LLM budget. The `taskId` is the durable handle for retrieving the result later via `await_tasks`, `roll_up`, or `ask_result`. Many `assign_task` calls can be in flight in parallel against the same or different subagents. This is the primary tool for fan-out work; do NOT use `delegate` to spawn multiple investigations sequentially.",
|
|
12789
|
+
description: "Queue a task on a previously spawned subagent. NON-BLOCKING: returns a `taskId` IMMEDIATELY \u2014 the subagent processes the task on its next iteration with its own LLM budget. The `taskId` is the durable handle for retrieving the result later via `await_tasks`, `roll_up`, or `ask_result`. Every assignment MUST carry an explicit boundary: `scope` (what the work covers) and `outOfScope` (at least one concrete non-goal) \u2014 the call is rejected without them, and the worker treats the rendered boundary block as a hard contract. Many `assign_task` calls can be in flight in parallel against the same or different subagents. This is the primary tool for fan-out work; do NOT use `delegate` to spawn multiple investigations sequentially.",
|
|
12705
12790
|
permission: "auto",
|
|
12706
12791
|
mutating: false,
|
|
12707
12792
|
capabilities: [ToolCapabilities.SUBAGENT_SPAWN],
|
|
12708
12793
|
inputSchema,
|
|
12709
12794
|
async execute(input) {
|
|
12710
12795
|
const i = input;
|
|
12796
|
+
const boundary = parseTaskBoundary(i);
|
|
12797
|
+
if (!boundary.ok) {
|
|
12798
|
+
return {
|
|
12799
|
+
ok: false,
|
|
12800
|
+
error: `assign_task rejected \u2014 task boundary incomplete: ${boundary.error}`,
|
|
12801
|
+
hint: boundary.hint
|
|
12802
|
+
};
|
|
12803
|
+
}
|
|
12711
12804
|
const task = {
|
|
12712
12805
|
id: randomUUID8(),
|
|
12713
|
-
description: i.description,
|
|
12806
|
+
description: composeBoundedTaskDescription(i.description, boundary.boundary),
|
|
12714
12807
|
subagentId: i.subagentId,
|
|
12715
12808
|
maxToolCalls: i.maxToolCalls,
|
|
12716
12809
|
timeoutMs: i.timeoutMs
|
|
@@ -20503,8 +20596,8 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
20503
20596
|
withNickname(subagent, subagentId) {
|
|
20504
20597
|
const role = subagent.role ?? "subagent";
|
|
20505
20598
|
const name = subagent.name?.trim() ?? "";
|
|
20506
|
-
const
|
|
20507
|
-
if (!
|
|
20599
|
+
const isPlaceholder2 = name === "" || name.toLowerCase() === role.toLowerCase() || name === "subagent" || name === "adhoc" || name === "generic" || /^slot-/.test(name);
|
|
20600
|
+
if (!isPlaceholder2) return subagent;
|
|
20508
20601
|
const { key, display } = assignNickname(role, this.usedNicknames);
|
|
20509
20602
|
this.usedNicknames.add(key);
|
|
20510
20603
|
this.subagentNicknames.set(subagentId, key);
|
|
@@ -31367,6 +31460,7 @@ export {
|
|
|
31367
31460
|
clearProjectSkillAugmentation,
|
|
31368
31461
|
collabInjectMiddleware,
|
|
31369
31462
|
collabPauseMiddleware,
|
|
31463
|
+
composeBoundedTaskDescription,
|
|
31370
31464
|
composeDirectorPrompt,
|
|
31371
31465
|
composeSubagentPrompt,
|
|
31372
31466
|
consolidatedDocumentPath,
|
|
@@ -31451,6 +31545,7 @@ export {
|
|
|
31451
31545
|
parseMailboxAckInput,
|
|
31452
31546
|
parseMailboxQueryInput,
|
|
31453
31547
|
parseMailboxSendInput,
|
|
31548
|
+
parseTaskBoundary,
|
|
31454
31549
|
phaseForRole,
|
|
31455
31550
|
quarantinePath,
|
|
31456
31551
|
rankRoleSkills,
|
|
@@ -31467,6 +31562,7 @@ export {
|
|
|
31467
31562
|
refreshProjectAgentIdentity,
|
|
31468
31563
|
release,
|
|
31469
31564
|
renderSkillAugmentation,
|
|
31565
|
+
renderTaskBoundaryBlock,
|
|
31470
31566
|
resetCaptureWindow,
|
|
31471
31567
|
resetCaptureWindows,
|
|
31472
31568
|
resetProjectAgentIdentity,
|
|
@@ -31497,6 +31593,7 @@ export {
|
|
|
31497
31593
|
startPackageOutdatedWatcher,
|
|
31498
31594
|
startTechStackConsumer,
|
|
31499
31595
|
subagentNeedsWorktree,
|
|
31596
|
+
taskBoundarySchemaProperties,
|
|
31500
31597
|
terminalPolicyDecision,
|
|
31501
31598
|
unwrapWholeDocumentFence,
|
|
31502
31599
|
updatePackageOutdatedStatus,
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hard task-boundary contract for leader → subagent assignment tools.
|
|
3
|
+
*
|
|
4
|
+
* `delegate` and `assign_task` reject any assignment that does not carry an
|
|
5
|
+
* explicit in-bounds `scope` and at least one concrete out-of-scope non-goal.
|
|
6
|
+
* Prompt-only guidance ("state the scope and non-goals") degrades under
|
|
7
|
+
* pressure: a rushed leader writes "fix it", the worker guesses the edges, and
|
|
8
|
+
* the drift only surfaces at review time — the most expensive place to find
|
|
9
|
+
* it. Forcing the boundary into structured fields makes the edges
|
|
10
|
+
* machine-checkable at assignment time and lets the tool return a teaching
|
|
11
|
+
* error that names exactly what is missing, so the leader self-corrects on
|
|
12
|
+
* the next call instead of shipping a vague brief.
|
|
13
|
+
*
|
|
14
|
+
* The parsed boundary is composed into `TaskSpec.description` as a clearly
|
|
15
|
+
* delimited block. That is deliberate: the description is the canonical brief
|
|
16
|
+
* every runner, transcript, handoff continuation, and roll-up already
|
|
17
|
+
* consumes, so the boundary travels with the task everywhere without new
|
|
18
|
+
* plumbing (and `buildHandoffTask`'s "Original task:" carry-over preserves it
|
|
19
|
+
* into fresh workers for free).
|
|
20
|
+
*/
|
|
21
|
+
import type { JSONSchema } from '../types/tool.js';
|
|
22
|
+
export interface TaskBoundary {
|
|
23
|
+
/** What the work covers — the in-bounds statement. */
|
|
24
|
+
scope: string;
|
|
25
|
+
/** Explicit non-goals: things the worker must NOT do. */
|
|
26
|
+
outOfScope: string[];
|
|
27
|
+
}
|
|
28
|
+
export type TaskBoundaryParseResult = {
|
|
29
|
+
ok: true;
|
|
30
|
+
boundary: TaskBoundary;
|
|
31
|
+
} | {
|
|
32
|
+
ok: false;
|
|
33
|
+
error: string;
|
|
34
|
+
hint: string;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Validate and normalize the `scope` / `outOfScope` pair from a tool input.
|
|
38
|
+
* Returns the cleaned boundary, or an error + hint pair worded so the calling
|
|
39
|
+
* leader can fix the call in one retry.
|
|
40
|
+
*/
|
|
41
|
+
export declare function parseTaskBoundary(raw: {
|
|
42
|
+
scope?: unknown;
|
|
43
|
+
outOfScope?: unknown;
|
|
44
|
+
}): TaskBoundaryParseResult;
|
|
45
|
+
/**
|
|
46
|
+
* Render the boundary as the block appended to `TaskSpec.description`. The
|
|
47
|
+
* heading is loud on purpose: the worker treats these lines as hard edges,
|
|
48
|
+
* and review-time drift checks quote them verbatim.
|
|
49
|
+
*/
|
|
50
|
+
export declare function renderTaskBoundaryBlock(boundary: TaskBoundary): string;
|
|
51
|
+
/**
|
|
52
|
+
* Compose the leader's objective with its boundary into the canonical brief
|
|
53
|
+
* delivered to the runner. The objective stays verbatim and first; the
|
|
54
|
+
* boundary block follows so handoffs, transcripts, and roll-ups that quote
|
|
55
|
+
* the description carry the edges with them.
|
|
56
|
+
*/
|
|
57
|
+
export declare function composeBoundedTaskDescription(objective: string, boundary: TaskBoundary): string;
|
|
58
|
+
/**
|
|
59
|
+
* Shared schema property definitions so `delegate` and `assign_task` state
|
|
60
|
+
* the identical contract. Declaring the fields here (rather than inline per
|
|
61
|
+
* tool) keeps the required-field teaching text single-sourced.
|
|
62
|
+
*/
|
|
63
|
+
export declare const taskBoundarySchemaProperties: Record<string, JSONSchema>;
|
|
64
|
+
//# sourceMappingURL=task-boundary.d.ts.map
|
package/dist/defaults/index.js
CHANGED
|
@@ -5512,6 +5512,80 @@ var FLEET_ROSTER_WITHACP = {
|
|
|
5512
5512
|
...Object.fromEntries(ACP_AGENTS.map((a) => [a.role, a]))
|
|
5513
5513
|
};
|
|
5514
5514
|
|
|
5515
|
+
// src/coordination/task-boundary.ts
|
|
5516
|
+
var PLACEHOLDER_VALUES = /* @__PURE__ */ new Set([
|
|
5517
|
+
"n/a",
|
|
5518
|
+
"na",
|
|
5519
|
+
"none",
|
|
5520
|
+
"nothing",
|
|
5521
|
+
"tbd",
|
|
5522
|
+
"todo",
|
|
5523
|
+
"unknown",
|
|
5524
|
+
"unspecified",
|
|
5525
|
+
"-",
|
|
5526
|
+
"\u2014",
|
|
5527
|
+
".",
|
|
5528
|
+
"as above",
|
|
5529
|
+
"same as above",
|
|
5530
|
+
"see above",
|
|
5531
|
+
"see task",
|
|
5532
|
+
"same as task"
|
|
5533
|
+
]);
|
|
5534
|
+
var isPlaceholder = (value) => PLACEHOLDER_VALUES.has(value.trim().toLowerCase());
|
|
5535
|
+
var MIN_SCOPE_CHARS = 8;
|
|
5536
|
+
var MIN_NON_GOAL_CHARS = 3;
|
|
5537
|
+
function parseTaskBoundary(raw) {
|
|
5538
|
+
const scope = typeof raw.scope === "string" ? raw.scope.trim() : "";
|
|
5539
|
+
if (scope.length < MIN_SCOPE_CHARS) {
|
|
5540
|
+
return {
|
|
5541
|
+
ok: false,
|
|
5542
|
+
error: `\`scope\` is missing or too vague \u2014 state in one concrete sentence what work this task covers (files, components, or commands in-bounds).`,
|
|
5543
|
+
hint: 'Example \u2014 scope: "Audit packages/core/src/parser/*.ts for unhandled token errors and report findings."'
|
|
5544
|
+
};
|
|
5545
|
+
}
|
|
5546
|
+
if (!Array.isArray(raw.outOfScope) || raw.outOfScope.length === 0) {
|
|
5547
|
+
return {
|
|
5548
|
+
ok: false,
|
|
5549
|
+
error: "`outOfScope` must be an array with at least one explicit non-goal \u2014 things the worker must NOT do.",
|
|
5550
|
+
hint: 'Example \u2014 outOfScope: ["Do not modify files outside packages/core", "Do not fix the bugs you find, only report them"].'
|
|
5551
|
+
};
|
|
5552
|
+
}
|
|
5553
|
+
const concrete = raw.outOfScope.filter((item) => typeof item === "string").map((item) => item.trim()).filter((item) => item.length >= MIN_NON_GOAL_CHARS && !isPlaceholder(item));
|
|
5554
|
+
if (concrete.length === 0) {
|
|
5555
|
+
return {
|
|
5556
|
+
ok: false,
|
|
5557
|
+
error: 'Every `outOfScope` entry was a placeholder ("none", "n/a", \u2026). Name at least one concrete non-goal: files or areas not to touch, changes not to make, features not to add.',
|
|
5558
|
+
hint: 'There is always an edge worth stating \u2014 "read-only, no edits", "no dependency changes", "do not touch other packages". If truly nothing comes to mind, the task is not decomposed enough yet.'
|
|
5559
|
+
};
|
|
5560
|
+
}
|
|
5561
|
+
return { ok: true, boundary: { scope, outOfScope: concrete } };
|
|
5562
|
+
}
|
|
5563
|
+
function renderTaskBoundaryBlock(boundary) {
|
|
5564
|
+
return [
|
|
5565
|
+
"\u2500\u2500 TASK BOUNDARY (hard contract \u2014 these lines define your edges) \u2500\u2500",
|
|
5566
|
+
`Scope (what this task covers):
|
|
5567
|
+
${boundary.scope}`,
|
|
5568
|
+
`Out of scope (explicit non-goals \u2014 do NOT do any of these):
|
|
5569
|
+
${boundary.outOfScope.map((item) => `- ${item}`).join("\n")}`
|
|
5570
|
+
].join("\n");
|
|
5571
|
+
}
|
|
5572
|
+
function composeBoundedTaskDescription(objective, boundary) {
|
|
5573
|
+
return `${objective.trim()}
|
|
5574
|
+
|
|
5575
|
+
${renderTaskBoundaryBlock(boundary)}`;
|
|
5576
|
+
}
|
|
5577
|
+
var taskBoundarySchemaProperties = {
|
|
5578
|
+
scope: {
|
|
5579
|
+
type: "string",
|
|
5580
|
+
description: "REQUIRED. One concrete sentence stating what work this task covers \u2014 the in-bounds. The call is rejected without it."
|
|
5581
|
+
},
|
|
5582
|
+
outOfScope: {
|
|
5583
|
+
type: "array",
|
|
5584
|
+
items: { type: "string", minLength: 1 },
|
|
5585
|
+
description: 'REQUIRED. At least one explicit non-goal the worker must NOT do (files/areas not to touch, changes not to make, features not to add). Placeholders like "none" are rejected.'
|
|
5586
|
+
}
|
|
5587
|
+
};
|
|
5588
|
+
|
|
5515
5589
|
// src/coordination/delegate-tool.ts
|
|
5516
5590
|
function createDelegateTool(opts) {
|
|
5517
5591
|
const defaultTimeoutMs = opts.defaultTimeoutMs ?? 4 * 60 * 60 * 1e3;
|
|
@@ -5521,8 +5595,9 @@ function createDelegateTool(opts) {
|
|
|
5521
5595
|
properties: {
|
|
5522
5596
|
task: {
|
|
5523
5597
|
type: "string",
|
|
5524
|
-
description: "
|
|
5598
|
+
description: "The objective \u2014 what the subagent should do, natural language, complete sentence(s). Pair it with the required `scope` and `outOfScope` boundary fields."
|
|
5525
5599
|
},
|
|
5600
|
+
...taskBoundarySchemaProperties,
|
|
5526
5601
|
role: {
|
|
5527
5602
|
type: "string",
|
|
5528
5603
|
description: rosterIds.length > 0 ? "Roster role id. Common: bug-hunter, security-scanner, refactor-planner, critic, audit-log, executor, shadow-agent, architect." : "No roster configured \u2014 pass `name` instead."
|
|
@@ -5580,12 +5655,12 @@ function createDelegateTool(opts) {
|
|
|
5580
5655
|
description: "Max fresh-worker continuations after budget exhaustion. Default 1. Each gets the prior partial report."
|
|
5581
5656
|
}
|
|
5582
5657
|
},
|
|
5583
|
-
required: ["task"]
|
|
5658
|
+
required: ["task", "scope", "outOfScope"]
|
|
5584
5659
|
};
|
|
5585
5660
|
return {
|
|
5586
5661
|
name: "delegate",
|
|
5587
5662
|
description: "Hand a piece of work to a subagent and block until it returns. This call is synchronous: the leader's iteration pauses for the full duration of the subagent's run. (Multiple `delegate` calls fired in the same assistant turn still parallelize through the provider's parallel-tool-call surface, but each one eats wall-clock time \u2014 so for fan-out you actually control, reach for the async path below.) Use `delegate` when your next step genuinely needs the subagent's verdict \u2014 a review, a fact-check, a sign-off. Has own context, own LLM call, auto-extending budget, and a partial-completion handoff path (maxHandoffs, default 1). Workers cannot recursively spawn.\n\n**Do NOT use `delegate` for long-running work.** While `delegate` is in flight, the leader is fully blocked \u2014 it cannot act on other tools, read mail, or react to the user. If the work might run for tens of minutes or hours (multi-file refactor, monorepo audit, long-running build/test, sweeping migration), the blocking call wastes the leader's time. Use the async tool family instead: `spawn_subagent` to create each worker (returns a `subagentId` immediately), `assign_task` to queue work on it (returns a `taskId` immediately), then `await_tasks` to retrieve results later. The leader keeps doing other work while the worker churns, and a worker that realizes its task will run long can mail the leader (type `steer` or `ask` via `mail_send`) saying *\"my task is going to run long, please spawn a subagent instead\"* so the leader re-dispatches asynchronously instead of waiting.\n\n**Do NOT use `delegate` for fan-out you control.** Multiple sequential `delegate` calls each block the leader, wasting wall-clock time. For independent investigations you want to run in parallel \u2014 security scan + bug hunt + perf review on the same PR \u2014 use the async tool family: `spawn_subagent` to create each worker (returns a `subagentId` immediately), `assign_task` to queue work on it (returns a `taskId` immediately), then the `await_tasks` tool with `{mode: 'any'}` to fold the first useful result into the next decision while the rest keep churning. Reach for `delegate` only when the result gates your next move AND the work is short enough that blocking the leader is acceptable.",
|
|
5588
|
-
usageHint: "Set `task` to a
|
|
5663
|
+
usageHint: "Set `task` to the objective, then make the edges explicit: `scope` (what the work covers) and `outOfScope` (at least one concrete non-goal) are REQUIRED \u2014 the call is rejected without them, and the worker treats the rendered boundary block as a hard contract. Pick `role` from roster or pass `name` for free-form. Reach for `delegate` only when the result gates your next move AND the work is short enough that blocking the leader is acceptable (minutes, not hours). For long-running work or fan-out you control, use `spawn_subagent` + `assign_task` + `await_tasks` instead. Raise `maxHandoffs` (default 1, cap 8) for multi-day or multi-refactor tasks; pass larger `timeoutMs`/`maxIterations`/`maxToolCalls` only when needed.",
|
|
5589
5664
|
permission: "auto",
|
|
5590
5665
|
mutating: false,
|
|
5591
5666
|
managesOwnTimeout: true,
|
|
@@ -5605,6 +5680,14 @@ function createDelegateTool(opts) {
|
|
|
5605
5680
|
error: "Delegation cancelled before spawn \u2014 the run was interrupted."
|
|
5606
5681
|
};
|
|
5607
5682
|
}
|
|
5683
|
+
const boundary = parseTaskBoundary(i);
|
|
5684
|
+
if (!boundary.ok) {
|
|
5685
|
+
return {
|
|
5686
|
+
ok: false,
|
|
5687
|
+
error: `delegate rejected \u2014 task boundary incomplete: ${boundary.error}`,
|
|
5688
|
+
hint: boundary.hint
|
|
5689
|
+
};
|
|
5690
|
+
}
|
|
5608
5691
|
const target = i.role ?? i.name ?? "subagent";
|
|
5609
5692
|
const launchModePreface = [
|
|
5610
5693
|
"Launch-mode guidance (delegate): you were launched via the synchronous `delegate` tool, so the leader is blocked on this call for the full duration of your run.",
|
|
@@ -5675,7 +5758,8 @@ function createDelegateTool(opts) {
|
|
|
5675
5758
|
const dir = director;
|
|
5676
5759
|
const maxHandoffs = Math.min(8, Math.max(0, Math.floor(i.maxHandoffs ?? 1)));
|
|
5677
5760
|
const handoffs = [];
|
|
5678
|
-
|
|
5761
|
+
const baseBrief = composeBoundedTaskDescription(i.task, boundary.boundary);
|
|
5762
|
+
let delegatedTask = baseBrief;
|
|
5679
5763
|
let handoffCount = 0;
|
|
5680
5764
|
for (; ; ) {
|
|
5681
5765
|
const attemptConfig = (() => {
|
|
@@ -5815,7 +5899,7 @@ function createDelegateTool(opts) {
|
|
|
5815
5899
|
remainingWork: continuation.remainingWork
|
|
5816
5900
|
});
|
|
5817
5901
|
handoffCount += 1;
|
|
5818
|
-
delegatedTask = buildHandoffTask(
|
|
5902
|
+
delegatedTask = buildHandoffTask(baseBrief, continuation, handoffCount, maxHandoffs);
|
|
5819
5903
|
continue;
|
|
5820
5904
|
}
|
|
5821
5905
|
const incomplete = result.report?.completion === "partial";
|
|
@@ -8503,8 +8587,9 @@ function makeAssignTool(director) {
|
|
|
8503
8587
|
description: {
|
|
8504
8588
|
type: "string",
|
|
8505
8589
|
minLength: 1,
|
|
8506
|
-
description: "The
|
|
8590
|
+
description: "The objective in natural language \u2014 what you want this subagent to do. Pair it with the required `scope` and `outOfScope` boundary fields."
|
|
8507
8591
|
},
|
|
8592
|
+
...taskBoundarySchemaProperties,
|
|
8508
8593
|
maxToolCalls: {
|
|
8509
8594
|
type: "number",
|
|
8510
8595
|
minimum: 1,
|
|
@@ -8512,20 +8597,28 @@ function makeAssignTool(director) {
|
|
|
8512
8597
|
},
|
|
8513
8598
|
timeoutMs: { type: "number", minimum: 1, description: "Optional per-task timeout in ms." }
|
|
8514
8599
|
},
|
|
8515
|
-
required: ["subagentId", "description"]
|
|
8600
|
+
required: ["subagentId", "description", "scope", "outOfScope"]
|
|
8516
8601
|
};
|
|
8517
8602
|
return {
|
|
8518
8603
|
name: "assign_task",
|
|
8519
|
-
description: "Queue a task on a previously spawned subagent. NON-BLOCKING: returns a `taskId` IMMEDIATELY \u2014 the subagent processes the task on its next iteration with its own LLM budget. The `taskId` is the durable handle for retrieving the result later via `await_tasks`, `roll_up`, or `ask_result`. Many `assign_task` calls can be in flight in parallel against the same or different subagents. This is the primary tool for fan-out work; do NOT use `delegate` to spawn multiple investigations sequentially.",
|
|
8604
|
+
description: "Queue a task on a previously spawned subagent. NON-BLOCKING: returns a `taskId` IMMEDIATELY \u2014 the subagent processes the task on its next iteration with its own LLM budget. The `taskId` is the durable handle for retrieving the result later via `await_tasks`, `roll_up`, or `ask_result`. Every assignment MUST carry an explicit boundary: `scope` (what the work covers) and `outOfScope` (at least one concrete non-goal) \u2014 the call is rejected without them, and the worker treats the rendered boundary block as a hard contract. Many `assign_task` calls can be in flight in parallel against the same or different subagents. This is the primary tool for fan-out work; do NOT use `delegate` to spawn multiple investigations sequentially.",
|
|
8520
8605
|
permission: "auto",
|
|
8521
8606
|
mutating: false,
|
|
8522
8607
|
capabilities: [ToolCapabilities.SUBAGENT_SPAWN],
|
|
8523
8608
|
inputSchema,
|
|
8524
8609
|
async execute(input) {
|
|
8525
8610
|
const i = input;
|
|
8611
|
+
const boundary = parseTaskBoundary(i);
|
|
8612
|
+
if (!boundary.ok) {
|
|
8613
|
+
return {
|
|
8614
|
+
ok: false,
|
|
8615
|
+
error: `assign_task rejected \u2014 task boundary incomplete: ${boundary.error}`,
|
|
8616
|
+
hint: boundary.hint
|
|
8617
|
+
};
|
|
8618
|
+
}
|
|
8526
8619
|
const task = {
|
|
8527
8620
|
id: randomUUID6(),
|
|
8528
|
-
description: i.description,
|
|
8621
|
+
description: composeBoundedTaskDescription(i.description, boundary.boundary),
|
|
8529
8622
|
subagentId: i.subagentId,
|
|
8530
8623
|
maxToolCalls: i.maxToolCalls,
|
|
8531
8624
|
timeoutMs: i.timeoutMs
|
|
@@ -19228,8 +19321,8 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
19228
19321
|
withNickname(subagent, subagentId) {
|
|
19229
19322
|
const role = subagent.role ?? "subagent";
|
|
19230
19323
|
const name = subagent.name?.trim() ?? "";
|
|
19231
|
-
const
|
|
19232
|
-
if (!
|
|
19324
|
+
const isPlaceholder2 = name === "" || name.toLowerCase() === role.toLowerCase() || name === "subagent" || name === "adhoc" || name === "generic" || /^slot-/.test(name);
|
|
19325
|
+
if (!isPlaceholder2) return subagent;
|
|
19233
19326
|
const { key, display } = assignNickname(role, this.usedNicknames);
|
|
19234
19327
|
this.usedNicknames.add(key);
|
|
19235
19328
|
this.subagentNicknames.set(subagentId, key);
|
|
@@ -22497,6 +22590,7 @@ function normalizeTargetLoad(targetLoad, thresholds) {
|
|
|
22497
22590
|
|
|
22498
22591
|
// src/types/context-window.ts
|
|
22499
22592
|
var DEFAULT_CONTEXT_WINDOW_MODE_ID = "balanced";
|
|
22593
|
+
var LARGE_WINDOW_DEEP_MODE_THRESHOLD = 1e6;
|
|
22500
22594
|
var DEPRECATED_CONTEXT_WINDOW_MODE_ALIASES = Object.freeze({
|
|
22501
22595
|
archival: "balanced"
|
|
22502
22596
|
});
|
|
@@ -22504,7 +22598,7 @@ var CONTEXT_WINDOW_MODES = Object.freeze([
|
|
|
22504
22598
|
{
|
|
22505
22599
|
id: "balanced",
|
|
22506
22600
|
name: "Balanced",
|
|
22507
|
-
description: "Default rolling compaction: recent work stays verbatim, old tool output is trimmed.",
|
|
22601
|
+
description: "Default rolling compaction: recent work stays verbatim, old tool output is trimmed. Windows of 1M+ tokens default to Deep.",
|
|
22508
22602
|
thresholds: { warn: 0.55, soft: 0.7, hard: 0.85 },
|
|
22509
22603
|
aggressiveOn: "soft",
|
|
22510
22604
|
preserveK: 8,
|
|
@@ -22555,9 +22649,11 @@ function getContextWindowMode(id) {
|
|
|
22555
22649
|
function isContextWindowModeId(id) {
|
|
22556
22650
|
return CONTEXT_WINDOW_MODES.some((m) => m.id === id);
|
|
22557
22651
|
}
|
|
22558
|
-
function resolveContextWindowPolicy(config = {}, overrideMode) {
|
|
22652
|
+
function resolveContextWindowPolicy(config = {}, overrideMode, maxContext) {
|
|
22559
22653
|
const requested = overrideMode ?? config.mode ?? DEFAULT_CONTEXT_WINDOW_MODE_ID;
|
|
22560
|
-
const
|
|
22654
|
+
const normalized = normalizeContextWindowModeId(requested) ?? DEFAULT_CONTEXT_WINDOW_MODE_ID;
|
|
22655
|
+
const baseId = normalized === DEFAULT_CONTEXT_WINDOW_MODE_ID && typeof maxContext === "number" && maxContext >= LARGE_WINDOW_DEEP_MODE_THRESHOLD ? "deep" : normalized;
|
|
22656
|
+
const mode = expectDefined(getContextWindowMode(baseId));
|
|
22561
22657
|
return {
|
|
22562
22658
|
...mode,
|
|
22563
22659
|
thresholds: {
|
|
@@ -22972,7 +23068,7 @@ function readContextWindowPolicy(ctx) {
|
|
|
22972
23068
|
function installSubagentAutoCompaction(pipelines, ctx, contextConfig, events) {
|
|
22973
23069
|
const maxContext = ctx.provider?.capabilities?.maxContext ?? 0;
|
|
22974
23070
|
if (!(maxContext > 0)) return void 0;
|
|
22975
|
-
const policy = resolveContextWindowPolicy(contextConfig ?? {});
|
|
23071
|
+
const policy = resolveContextWindowPolicy(contextConfig ?? {}, void 0, maxContext);
|
|
22976
23072
|
ctx.meta ??= {};
|
|
22977
23073
|
ctx.meta["contextWindowPolicy"] = policy;
|
|
22978
23074
|
const compactor = new HybridCompactor({
|
package/dist/execution/index.js
CHANGED
|
@@ -21421,6 +21421,7 @@ function readPolicy(ctx) {
|
|
|
21421
21421
|
|
|
21422
21422
|
// src/types/context-window.ts
|
|
21423
21423
|
var DEFAULT_CONTEXT_WINDOW_MODE_ID = "balanced";
|
|
21424
|
+
var LARGE_WINDOW_DEEP_MODE_THRESHOLD = 1e6;
|
|
21424
21425
|
var DEPRECATED_CONTEXT_WINDOW_MODE_ALIASES = Object.freeze({
|
|
21425
21426
|
archival: "balanced"
|
|
21426
21427
|
});
|
|
@@ -21428,7 +21429,7 @@ var CONTEXT_WINDOW_MODES = Object.freeze([
|
|
|
21428
21429
|
{
|
|
21429
21430
|
id: "balanced",
|
|
21430
21431
|
name: "Balanced",
|
|
21431
|
-
description: "Default rolling compaction: recent work stays verbatim, old tool output is trimmed.",
|
|
21432
|
+
description: "Default rolling compaction: recent work stays verbatim, old tool output is trimmed. Windows of 1M+ tokens default to Deep.",
|
|
21432
21433
|
thresholds: { warn: 0.55, soft: 0.7, hard: 0.85 },
|
|
21433
21434
|
aggressiveOn: "soft",
|
|
21434
21435
|
preserveK: 8,
|
|
@@ -21470,9 +21471,11 @@ function getContextWindowMode(id) {
|
|
|
21470
21471
|
function isContextWindowModeId(id) {
|
|
21471
21472
|
return CONTEXT_WINDOW_MODES.some((m) => m.id === id);
|
|
21472
21473
|
}
|
|
21473
|
-
function resolveContextWindowPolicy(config = {}, overrideMode) {
|
|
21474
|
+
function resolveContextWindowPolicy(config = {}, overrideMode, maxContext) {
|
|
21474
21475
|
const requested = overrideMode ?? config.mode ?? DEFAULT_CONTEXT_WINDOW_MODE_ID;
|
|
21475
|
-
const
|
|
21476
|
+
const normalized = normalizeContextWindowModeId(requested) ?? DEFAULT_CONTEXT_WINDOW_MODE_ID;
|
|
21477
|
+
const baseId = normalized === DEFAULT_CONTEXT_WINDOW_MODE_ID && typeof maxContext === "number" && maxContext >= LARGE_WINDOW_DEEP_MODE_THRESHOLD ? "deep" : normalized;
|
|
21478
|
+
const mode = expectDefined(getContextWindowMode(baseId));
|
|
21476
21479
|
return {
|
|
21477
21480
|
...mode,
|
|
21478
21481
|
thresholds: {
|
|
@@ -21490,7 +21493,7 @@ function resolveContextWindowPolicy(config = {}, overrideMode) {
|
|
|
21490
21493
|
function installSubagentAutoCompaction(pipelines, ctx, contextConfig, events) {
|
|
21491
21494
|
const maxContext = ctx.provider?.capabilities?.maxContext ?? 0;
|
|
21492
21495
|
if (!(maxContext > 0)) return void 0;
|
|
21493
|
-
const policy = resolveContextWindowPolicy(contextConfig ?? {});
|
|
21496
|
+
const policy = resolveContextWindowPolicy(contextConfig ?? {}, void 0, maxContext);
|
|
21494
21497
|
ctx.meta ??= {};
|
|
21495
21498
|
ctx.meta["contextWindowPolicy"] = policy;
|
|
21496
21499
|
const compactor = new HybridCompactor({
|
package/dist/index.d.ts
CHANGED
|
@@ -134,7 +134,7 @@ export { createMcpUseTool } from './tools/mcp-use.js';
|
|
|
134
134
|
export { type CreateOneShotLLMToolOptions, createOneShotLLMTool, ONE_SHOT_LLM_TOOL_NAME, } from './tools/one-shot-llm-tool.js';
|
|
135
135
|
export { type CreatePluginManagerToolOptions, createPluginManagerTool, PLUGIN_MANAGER_TOOL_NAME, type PluginManagerCatalogEntry, type PluginManagerMutationResult, } from './tools/plugin-manager.js';
|
|
136
136
|
export type { Compactor, CompactReport } from './types/compactor.js';
|
|
137
|
-
export { CONTEXT_WINDOW_MODES, type ContextWindowAggressiveOn, type ContextWindowConfigLike, type ContextWindowMode, type ContextWindowModeId, type ContextWindowModeSelectionId, type ContextWindowPolicy, type ContextWindowThresholds, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEPRECATED_CONTEXT_WINDOW_MODE_ALIASES, type DeprecatedContextWindowModeId, formatContextWindowModeList, getContextWindowMode, isContextWindowModeId, isContextWindowModeSelectionId, isDeprecatedContextWindowModeId, listContextWindowModes, normalizeContextWindowModeId, resolveContextWindowPolicy, } from './types/context-window.js';
|
|
137
|
+
export { CONTEXT_WINDOW_MODES, type ContextWindowAggressiveOn, type ContextWindowConfigLike, type ContextWindowMode, type ContextWindowModeId, type ContextWindowModeSelectionId, type ContextWindowPolicy, type ContextWindowThresholds, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEPRECATED_CONTEXT_WINDOW_MODE_ALIASES, type DeprecatedContextWindowModeId, formatContextWindowModeList, getContextWindowMode, isContextWindowModeId, isContextWindowModeSelectionId, isDeprecatedContextWindowModeId, LARGE_WINDOW_DEEP_MODE_THRESHOLD, CONTEXT_WINDOW_MODE_PINNED_META_KEY, listContextWindowModes, normalizeContextWindowModeId, resolveContextWindowPolicy, } from './types/context-window.js';
|
|
138
138
|
export { DEFAULT_SESSION_PRUNE_DAYS } from './types/default-config.js';
|
|
139
139
|
export type { FileEventRecord } from './types/file-event-record.js';
|
|
140
140
|
export * from './types/index.js';
|
package/dist/index.js
CHANGED
|
@@ -3042,6 +3042,8 @@ function expectDefined(value, label) {
|
|
|
3042
3042
|
|
|
3043
3043
|
// src/types/context-window.ts
|
|
3044
3044
|
var DEFAULT_CONTEXT_WINDOW_MODE_ID = "balanced";
|
|
3045
|
+
var LARGE_WINDOW_DEEP_MODE_THRESHOLD = 1e6;
|
|
3046
|
+
var CONTEXT_WINDOW_MODE_PINNED_META_KEY = "contextWindowModePinned";
|
|
3045
3047
|
var DEPRECATED_CONTEXT_WINDOW_MODE_ALIASES = Object.freeze({
|
|
3046
3048
|
archival: "balanced"
|
|
3047
3049
|
});
|
|
@@ -3049,7 +3051,7 @@ var CONTEXT_WINDOW_MODES = Object.freeze([
|
|
|
3049
3051
|
{
|
|
3050
3052
|
id: "balanced",
|
|
3051
3053
|
name: "Balanced",
|
|
3052
|
-
description: "Default rolling compaction: recent work stays verbatim, old tool output is trimmed.",
|
|
3054
|
+
description: "Default rolling compaction: recent work stays verbatim, old tool output is trimmed. Windows of 1M+ tokens default to Deep.",
|
|
3053
3055
|
thresholds: { warn: 0.55, soft: 0.7, hard: 0.85 },
|
|
3054
3056
|
aggressiveOn: "soft",
|
|
3055
3057
|
preserveK: 8,
|
|
@@ -3100,9 +3102,11 @@ function getContextWindowMode(id) {
|
|
|
3100
3102
|
function isContextWindowModeId(id) {
|
|
3101
3103
|
return CONTEXT_WINDOW_MODES.some((m) => m.id === id);
|
|
3102
3104
|
}
|
|
3103
|
-
function resolveContextWindowPolicy(config = {}, overrideMode) {
|
|
3105
|
+
function resolveContextWindowPolicy(config = {}, overrideMode, maxContext) {
|
|
3104
3106
|
const requested = overrideMode ?? config.mode ?? DEFAULT_CONTEXT_WINDOW_MODE_ID;
|
|
3105
|
-
const
|
|
3107
|
+
const normalized = normalizeContextWindowModeId(requested) ?? DEFAULT_CONTEXT_WINDOW_MODE_ID;
|
|
3108
|
+
const baseId = normalized === DEFAULT_CONTEXT_WINDOW_MODE_ID && typeof maxContext === "number" && maxContext >= LARGE_WINDOW_DEEP_MODE_THRESHOLD ? "deep" : normalized;
|
|
3109
|
+
const mode = expectDefined(getContextWindowMode(baseId));
|
|
3106
3110
|
return {
|
|
3107
3111
|
...mode,
|
|
3108
3112
|
thresholds: {
|
|
@@ -31216,6 +31220,82 @@ function makeLLMClassifier(complete2) {
|
|
|
31216
31220
|
// src/coordination/director-basic-tools.ts
|
|
31217
31221
|
import { randomUUID as randomUUID12 } from "node:crypto";
|
|
31218
31222
|
init_error();
|
|
31223
|
+
|
|
31224
|
+
// src/coordination/task-boundary.ts
|
|
31225
|
+
var PLACEHOLDER_VALUES = /* @__PURE__ */ new Set([
|
|
31226
|
+
"n/a",
|
|
31227
|
+
"na",
|
|
31228
|
+
"none",
|
|
31229
|
+
"nothing",
|
|
31230
|
+
"tbd",
|
|
31231
|
+
"todo",
|
|
31232
|
+
"unknown",
|
|
31233
|
+
"unspecified",
|
|
31234
|
+
"-",
|
|
31235
|
+
"\u2014",
|
|
31236
|
+
".",
|
|
31237
|
+
"as above",
|
|
31238
|
+
"same as above",
|
|
31239
|
+
"see above",
|
|
31240
|
+
"see task",
|
|
31241
|
+
"same as task"
|
|
31242
|
+
]);
|
|
31243
|
+
var isPlaceholder = (value) => PLACEHOLDER_VALUES.has(value.trim().toLowerCase());
|
|
31244
|
+
var MIN_SCOPE_CHARS = 8;
|
|
31245
|
+
var MIN_NON_GOAL_CHARS = 3;
|
|
31246
|
+
function parseTaskBoundary(raw) {
|
|
31247
|
+
const scope = typeof raw.scope === "string" ? raw.scope.trim() : "";
|
|
31248
|
+
if (scope.length < MIN_SCOPE_CHARS) {
|
|
31249
|
+
return {
|
|
31250
|
+
ok: false,
|
|
31251
|
+
error: `\`scope\` is missing or too vague \u2014 state in one concrete sentence what work this task covers (files, components, or commands in-bounds).`,
|
|
31252
|
+
hint: 'Example \u2014 scope: "Audit packages/core/src/parser/*.ts for unhandled token errors and report findings."'
|
|
31253
|
+
};
|
|
31254
|
+
}
|
|
31255
|
+
if (!Array.isArray(raw.outOfScope) || raw.outOfScope.length === 0) {
|
|
31256
|
+
return {
|
|
31257
|
+
ok: false,
|
|
31258
|
+
error: "`outOfScope` must be an array with at least one explicit non-goal \u2014 things the worker must NOT do.",
|
|
31259
|
+
hint: 'Example \u2014 outOfScope: ["Do not modify files outside packages/core", "Do not fix the bugs you find, only report them"].'
|
|
31260
|
+
};
|
|
31261
|
+
}
|
|
31262
|
+
const concrete = raw.outOfScope.filter((item) => typeof item === "string").map((item) => item.trim()).filter((item) => item.length >= MIN_NON_GOAL_CHARS && !isPlaceholder(item));
|
|
31263
|
+
if (concrete.length === 0) {
|
|
31264
|
+
return {
|
|
31265
|
+
ok: false,
|
|
31266
|
+
error: 'Every `outOfScope` entry was a placeholder ("none", "n/a", \u2026). Name at least one concrete non-goal: files or areas not to touch, changes not to make, features not to add.',
|
|
31267
|
+
hint: 'There is always an edge worth stating \u2014 "read-only, no edits", "no dependency changes", "do not touch other packages". If truly nothing comes to mind, the task is not decomposed enough yet.'
|
|
31268
|
+
};
|
|
31269
|
+
}
|
|
31270
|
+
return { ok: true, boundary: { scope, outOfScope: concrete } };
|
|
31271
|
+
}
|
|
31272
|
+
function renderTaskBoundaryBlock(boundary) {
|
|
31273
|
+
return [
|
|
31274
|
+
"\u2500\u2500 TASK BOUNDARY (hard contract \u2014 these lines define your edges) \u2500\u2500",
|
|
31275
|
+
`Scope (what this task covers):
|
|
31276
|
+
${boundary.scope}`,
|
|
31277
|
+
`Out of scope (explicit non-goals \u2014 do NOT do any of these):
|
|
31278
|
+
${boundary.outOfScope.map((item) => `- ${item}`).join("\n")}`
|
|
31279
|
+
].join("\n");
|
|
31280
|
+
}
|
|
31281
|
+
function composeBoundedTaskDescription(objective, boundary) {
|
|
31282
|
+
return `${objective.trim()}
|
|
31283
|
+
|
|
31284
|
+
${renderTaskBoundaryBlock(boundary)}`;
|
|
31285
|
+
}
|
|
31286
|
+
var taskBoundarySchemaProperties = {
|
|
31287
|
+
scope: {
|
|
31288
|
+
type: "string",
|
|
31289
|
+
description: "REQUIRED. One concrete sentence stating what work this task covers \u2014 the in-bounds. The call is rejected without it."
|
|
31290
|
+
},
|
|
31291
|
+
outOfScope: {
|
|
31292
|
+
type: "array",
|
|
31293
|
+
items: { type: "string", minLength: 1 },
|
|
31294
|
+
description: 'REQUIRED. At least one explicit non-goal the worker must NOT do (files/areas not to touch, changes not to make, features not to add). Placeholders like "none" are rejected.'
|
|
31295
|
+
}
|
|
31296
|
+
};
|
|
31297
|
+
|
|
31298
|
+
// src/coordination/director-basic-tools.ts
|
|
31219
31299
|
function makeAssignTool(director) {
|
|
31220
31300
|
const inputSchema = {
|
|
31221
31301
|
type: "object",
|
|
@@ -31224,8 +31304,9 @@ function makeAssignTool(director) {
|
|
|
31224
31304
|
description: {
|
|
31225
31305
|
type: "string",
|
|
31226
31306
|
minLength: 1,
|
|
31227
|
-
description: "The
|
|
31307
|
+
description: "The objective in natural language \u2014 what you want this subagent to do. Pair it with the required `scope` and `outOfScope` boundary fields."
|
|
31228
31308
|
},
|
|
31309
|
+
...taskBoundarySchemaProperties,
|
|
31229
31310
|
maxToolCalls: {
|
|
31230
31311
|
type: "number",
|
|
31231
31312
|
minimum: 1,
|
|
@@ -31233,20 +31314,28 @@ function makeAssignTool(director) {
|
|
|
31233
31314
|
},
|
|
31234
31315
|
timeoutMs: { type: "number", minimum: 1, description: "Optional per-task timeout in ms." }
|
|
31235
31316
|
},
|
|
31236
|
-
required: ["subagentId", "description"]
|
|
31317
|
+
required: ["subagentId", "description", "scope", "outOfScope"]
|
|
31237
31318
|
};
|
|
31238
31319
|
return {
|
|
31239
31320
|
name: "assign_task",
|
|
31240
|
-
description: "Queue a task on a previously spawned subagent. NON-BLOCKING: returns a `taskId` IMMEDIATELY \u2014 the subagent processes the task on its next iteration with its own LLM budget. The `taskId` is the durable handle for retrieving the result later via `await_tasks`, `roll_up`, or `ask_result`. Many `assign_task` calls can be in flight in parallel against the same or different subagents. This is the primary tool for fan-out work; do NOT use `delegate` to spawn multiple investigations sequentially.",
|
|
31321
|
+
description: "Queue a task on a previously spawned subagent. NON-BLOCKING: returns a `taskId` IMMEDIATELY \u2014 the subagent processes the task on its next iteration with its own LLM budget. The `taskId` is the durable handle for retrieving the result later via `await_tasks`, `roll_up`, or `ask_result`. Every assignment MUST carry an explicit boundary: `scope` (what the work covers) and `outOfScope` (at least one concrete non-goal) \u2014 the call is rejected without them, and the worker treats the rendered boundary block as a hard contract. Many `assign_task` calls can be in flight in parallel against the same or different subagents. This is the primary tool for fan-out work; do NOT use `delegate` to spawn multiple investigations sequentially.",
|
|
31241
31322
|
permission: "auto",
|
|
31242
31323
|
mutating: false,
|
|
31243
31324
|
capabilities: [ToolCapabilities.SUBAGENT_SPAWN],
|
|
31244
31325
|
inputSchema,
|
|
31245
31326
|
async execute(input) {
|
|
31246
31327
|
const i = input;
|
|
31328
|
+
const boundary = parseTaskBoundary(i);
|
|
31329
|
+
if (!boundary.ok) {
|
|
31330
|
+
return {
|
|
31331
|
+
ok: false,
|
|
31332
|
+
error: `assign_task rejected \u2014 task boundary incomplete: ${boundary.error}`,
|
|
31333
|
+
hint: boundary.hint
|
|
31334
|
+
};
|
|
31335
|
+
}
|
|
31247
31336
|
const task = {
|
|
31248
31337
|
id: randomUUID12(),
|
|
31249
|
-
description: i.description,
|
|
31338
|
+
description: composeBoundedTaskDescription(i.description, boundary.boundary),
|
|
31250
31339
|
subagentId: i.subagentId,
|
|
31251
31340
|
maxToolCalls: i.maxToolCalls,
|
|
31252
31341
|
timeoutMs: i.timeoutMs
|
|
@@ -39083,8 +39172,8 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
39083
39172
|
withNickname(subagent, subagentId) {
|
|
39084
39173
|
const role = subagent.role ?? "subagent";
|
|
39085
39174
|
const name = subagent.name?.trim() ?? "";
|
|
39086
|
-
const
|
|
39087
|
-
if (!
|
|
39175
|
+
const isPlaceholder2 = name === "" || name.toLowerCase() === role.toLowerCase() || name === "subagent" || name === "adhoc" || name === "generic" || /^slot-/.test(name);
|
|
39176
|
+
if (!isPlaceholder2) return subagent;
|
|
39088
39177
|
const { key, display } = assignNickname(role, this.usedNicknames);
|
|
39089
39178
|
this.usedNicknames.add(key);
|
|
39090
39179
|
this.subagentNicknames.set(subagentId, key);
|
|
@@ -43320,8 +43409,9 @@ function createDelegateTool(opts) {
|
|
|
43320
43409
|
properties: {
|
|
43321
43410
|
task: {
|
|
43322
43411
|
type: "string",
|
|
43323
|
-
description: "
|
|
43412
|
+
description: "The objective \u2014 what the subagent should do, natural language, complete sentence(s). Pair it with the required `scope` and `outOfScope` boundary fields."
|
|
43324
43413
|
},
|
|
43414
|
+
...taskBoundarySchemaProperties,
|
|
43325
43415
|
role: {
|
|
43326
43416
|
type: "string",
|
|
43327
43417
|
description: rosterIds.length > 0 ? "Roster role id. Common: bug-hunter, security-scanner, refactor-planner, critic, audit-log, executor, shadow-agent, architect." : "No roster configured \u2014 pass `name` instead."
|
|
@@ -43379,12 +43469,12 @@ function createDelegateTool(opts) {
|
|
|
43379
43469
|
description: "Max fresh-worker continuations after budget exhaustion. Default 1. Each gets the prior partial report."
|
|
43380
43470
|
}
|
|
43381
43471
|
},
|
|
43382
|
-
required: ["task"]
|
|
43472
|
+
required: ["task", "scope", "outOfScope"]
|
|
43383
43473
|
};
|
|
43384
43474
|
return {
|
|
43385
43475
|
name: "delegate",
|
|
43386
43476
|
description: "Hand a piece of work to a subagent and block until it returns. This call is synchronous: the leader's iteration pauses for the full duration of the subagent's run. (Multiple `delegate` calls fired in the same assistant turn still parallelize through the provider's parallel-tool-call surface, but each one eats wall-clock time \u2014 so for fan-out you actually control, reach for the async path below.) Use `delegate` when your next step genuinely needs the subagent's verdict \u2014 a review, a fact-check, a sign-off. Has own context, own LLM call, auto-extending budget, and a partial-completion handoff path (maxHandoffs, default 1). Workers cannot recursively spawn.\n\n**Do NOT use `delegate` for long-running work.** While `delegate` is in flight, the leader is fully blocked \u2014 it cannot act on other tools, read mail, or react to the user. If the work might run for tens of minutes or hours (multi-file refactor, monorepo audit, long-running build/test, sweeping migration), the blocking call wastes the leader's time. Use the async tool family instead: `spawn_subagent` to create each worker (returns a `subagentId` immediately), `assign_task` to queue work on it (returns a `taskId` immediately), then `await_tasks` to retrieve results later. The leader keeps doing other work while the worker churns, and a worker that realizes its task will run long can mail the leader (type `steer` or `ask` via `mail_send`) saying *\"my task is going to run long, please spawn a subagent instead\"* so the leader re-dispatches asynchronously instead of waiting.\n\n**Do NOT use `delegate` for fan-out you control.** Multiple sequential `delegate` calls each block the leader, wasting wall-clock time. For independent investigations you want to run in parallel \u2014 security scan + bug hunt + perf review on the same PR \u2014 use the async tool family: `spawn_subagent` to create each worker (returns a `subagentId` immediately), `assign_task` to queue work on it (returns a `taskId` immediately), then the `await_tasks` tool with `{mode: 'any'}` to fold the first useful result into the next decision while the rest keep churning. Reach for `delegate` only when the result gates your next move AND the work is short enough that blocking the leader is acceptable.",
|
|
43387
|
-
usageHint: "Set `task` to a
|
|
43477
|
+
usageHint: "Set `task` to the objective, then make the edges explicit: `scope` (what the work covers) and `outOfScope` (at least one concrete non-goal) are REQUIRED \u2014 the call is rejected without them, and the worker treats the rendered boundary block as a hard contract. Pick `role` from roster or pass `name` for free-form. Reach for `delegate` only when the result gates your next move AND the work is short enough that blocking the leader is acceptable (minutes, not hours). For long-running work or fan-out you control, use `spawn_subagent` + `assign_task` + `await_tasks` instead. Raise `maxHandoffs` (default 1, cap 8) for multi-day or multi-refactor tasks; pass larger `timeoutMs`/`maxIterations`/`maxToolCalls` only when needed.",
|
|
43388
43478
|
permission: "auto",
|
|
43389
43479
|
mutating: false,
|
|
43390
43480
|
managesOwnTimeout: true,
|
|
@@ -43404,6 +43494,14 @@ function createDelegateTool(opts) {
|
|
|
43404
43494
|
error: "Delegation cancelled before spawn \u2014 the run was interrupted."
|
|
43405
43495
|
};
|
|
43406
43496
|
}
|
|
43497
|
+
const boundary = parseTaskBoundary(i);
|
|
43498
|
+
if (!boundary.ok) {
|
|
43499
|
+
return {
|
|
43500
|
+
ok: false,
|
|
43501
|
+
error: `delegate rejected \u2014 task boundary incomplete: ${boundary.error}`,
|
|
43502
|
+
hint: boundary.hint
|
|
43503
|
+
};
|
|
43504
|
+
}
|
|
43407
43505
|
const target = i.role ?? i.name ?? "subagent";
|
|
43408
43506
|
const launchModePreface = [
|
|
43409
43507
|
"Launch-mode guidance (delegate): you were launched via the synchronous `delegate` tool, so the leader is blocked on this call for the full duration of your run.",
|
|
@@ -43474,7 +43572,8 @@ function createDelegateTool(opts) {
|
|
|
43474
43572
|
const dir = director;
|
|
43475
43573
|
const maxHandoffs = Math.min(8, Math.max(0, Math.floor(i.maxHandoffs ?? 1)));
|
|
43476
43574
|
const handoffs = [];
|
|
43477
|
-
|
|
43575
|
+
const baseBrief = composeBoundedTaskDescription(i.task, boundary.boundary);
|
|
43576
|
+
let delegatedTask = baseBrief;
|
|
43478
43577
|
let handoffCount = 0;
|
|
43479
43578
|
for (; ; ) {
|
|
43480
43579
|
const attemptConfig = (() => {
|
|
@@ -43614,7 +43713,7 @@ function createDelegateTool(opts) {
|
|
|
43614
43713
|
remainingWork: continuation.remainingWork
|
|
43615
43714
|
});
|
|
43616
43715
|
handoffCount += 1;
|
|
43617
|
-
delegatedTask = buildHandoffTask(
|
|
43716
|
+
delegatedTask = buildHandoffTask(baseBrief, continuation, handoffCount, maxHandoffs);
|
|
43618
43717
|
continue;
|
|
43619
43718
|
}
|
|
43620
43719
|
const incomplete = result.report?.completion === "partial";
|
|
@@ -58285,7 +58384,7 @@ function readContextWindowPolicy(ctx) {
|
|
|
58285
58384
|
function installSubagentAutoCompaction(pipelines, ctx, contextConfig, events) {
|
|
58286
58385
|
const maxContext = ctx.provider?.capabilities?.maxContext ?? 0;
|
|
58287
58386
|
if (!(maxContext > 0)) return void 0;
|
|
58288
|
-
const policy = resolveContextWindowPolicy(contextConfig ?? {});
|
|
58387
|
+
const policy = resolveContextWindowPolicy(contextConfig ?? {}, void 0, maxContext);
|
|
58289
58388
|
ctx.meta ??= {};
|
|
58290
58389
|
ctx.meta["contextWindowPolicy"] = policy;
|
|
58291
58390
|
const compactor = new HybridCompactor({
|
|
@@ -97079,6 +97178,7 @@ export {
|
|
|
97079
97178
|
COMPLETED_WORK_LEDGER_MARKER,
|
|
97080
97179
|
CONFIG_BEHAVIOR_DEFAULTS,
|
|
97081
97180
|
CONTEXT_WINDOW_MODES,
|
|
97181
|
+
CONTEXT_WINDOW_MODE_PINNED_META_KEY,
|
|
97082
97182
|
CORE_RECONSTRUCT_EVENTS,
|
|
97083
97183
|
COUNCIL_JUDGE_PROMPT_PATH,
|
|
97084
97184
|
COUNCIL_REFUSAL_OPTION_ID,
|
|
@@ -97270,6 +97370,7 @@ export {
|
|
|
97270
97370
|
KNOWN_TOKEN_GROUPS,
|
|
97271
97371
|
KNOWN_TOKEN_NAMES,
|
|
97272
97372
|
KnowledgeGraph,
|
|
97373
|
+
LARGE_WINDOW_DEEP_MODE_THRESHOLD,
|
|
97273
97374
|
LAYER_1_IDENTITY,
|
|
97274
97375
|
LEADER_MODEL_SET_TOOL_NAME,
|
|
97275
97376
|
LEARNED_HARD_LIMIT,
|
|
@@ -97546,6 +97647,7 @@ export {
|
|
|
97546
97647
|
compileUserRegex,
|
|
97547
97648
|
completeBrainLlm,
|
|
97548
97649
|
completePartialObject,
|
|
97650
|
+
composeBoundedTaskDescription,
|
|
97549
97651
|
composeDirectorPrompt,
|
|
97550
97652
|
composeSubagentPrompt,
|
|
97551
97653
|
computeMessageTokens,
|
|
@@ -97963,6 +98065,7 @@ export {
|
|
|
97963
98065
|
parseReviewSeverity,
|
|
97964
98066
|
parseSkillFrontmatter,
|
|
97965
98067
|
parseSkillRef,
|
|
98068
|
+
parseTaskBoundary,
|
|
97966
98069
|
peekQueuedMessages,
|
|
97967
98070
|
pendingBtwCount,
|
|
97968
98071
|
persistReviewReport,
|
|
@@ -98021,6 +98124,7 @@ export {
|
|
|
98021
98124
|
renderPrometheus,
|
|
98022
98125
|
renderPrompt,
|
|
98023
98126
|
renderSkillAugmentation,
|
|
98127
|
+
renderTaskBoundaryBlock,
|
|
98024
98128
|
repairConfigDefaults,
|
|
98025
98129
|
repairToolUseAdjacency,
|
|
98026
98130
|
repeatedReadPressure,
|
|
@@ -98185,6 +98289,7 @@ export {
|
|
|
98185
98289
|
syncReportCompletion,
|
|
98186
98290
|
syncReportReopen,
|
|
98187
98291
|
takeHeapSample,
|
|
98292
|
+
taskBoundarySchemaProperties,
|
|
98188
98293
|
terminalPolicyDecision,
|
|
98189
98294
|
tightenHqRedactionPolicy,
|
|
98190
98295
|
toAlertMessage,
|
|
@@ -2895,7 +2895,7 @@ var CONTEXT_WINDOW_MODES = Object.freeze([
|
|
|
2895
2895
|
{
|
|
2896
2896
|
id: "balanced",
|
|
2897
2897
|
name: "Balanced",
|
|
2898
|
-
description: "Default rolling compaction: recent work stays verbatim, old tool output is trimmed.",
|
|
2898
|
+
description: "Default rolling compaction: recent work stays verbatim, old tool output is trimmed. Windows of 1M+ tokens default to Deep.",
|
|
2899
2899
|
thresholds: { warn: 0.55, soft: 0.7, hard: 0.85 },
|
|
2900
2900
|
aggressiveOn: "soft",
|
|
2901
2901
|
preserveK: 8,
|
package/dist/storage/index.js
CHANGED
|
@@ -4656,7 +4656,7 @@ var CONTEXT_WINDOW_MODES = Object.freeze([
|
|
|
4656
4656
|
{
|
|
4657
4657
|
id: "balanced",
|
|
4658
4658
|
name: "Balanced",
|
|
4659
|
-
description: "Default rolling compaction: recent work stays verbatim, old tool output is trimmed.",
|
|
4659
|
+
description: "Default rolling compaction: recent work stays verbatim, old tool output is trimmed. Windows of 1M+ tokens default to Deep.",
|
|
4660
4660
|
thresholds: { warn: 0.55, soft: 0.7, hard: 0.85 },
|
|
4661
4661
|
aggressiveOn: "soft",
|
|
4662
4662
|
preserveK: 8,
|
|
@@ -45,6 +45,23 @@ export interface ContextWindowConfigLike {
|
|
|
45
45
|
targetLoad?: number | undefined;
|
|
46
46
|
}
|
|
47
47
|
export declare const DEFAULT_CONTEXT_WINDOW_MODE_ID: ContextWindowModeId;
|
|
48
|
+
/**
|
|
49
|
+
* Windows at or above this size default to the `deep` policy instead of
|
|
50
|
+
* `balanced`: 1M-class windows exist to be filled, and balanced's hard line
|
|
51
|
+
* (0.85) would compact at ~890K of a 1.05M window, stranding the tail. Deep
|
|
52
|
+
* holds compaction until 0.96 and keeps a wider verbatim tail. An explicit
|
|
53
|
+
* `frugal`/`deep` choice, custom modes, and per-field threshold overrides are
|
|
54
|
+
* always respected as-is; only the balanced default is swapped.
|
|
55
|
+
*/
|
|
56
|
+
export declare const LARGE_WINDOW_DEEP_MODE_THRESHOLD = 1000000;
|
|
57
|
+
/**
|
|
58
|
+
* Meta key the mode-switch surfaces (`/context mode`, WebUI `context.mode.switch`,
|
|
59
|
+
* `/context thresholds`) set after the user deliberately picks a policy for the
|
|
60
|
+
* session. Window-change flows re-resolve the default policy against the new
|
|
61
|
+
* window (so a 1M↔200K model switch keeps the policy scaled to the window) but
|
|
62
|
+
* must leave a user-pinned choice alone.
|
|
63
|
+
*/
|
|
64
|
+
export declare const CONTEXT_WINDOW_MODE_PINNED_META_KEY = "contextWindowModePinned";
|
|
48
65
|
export declare const DEPRECATED_CONTEXT_WINDOW_MODE_ALIASES: Readonly<Record<DeprecatedContextWindowModeId, ContextWindowModeId>>;
|
|
49
66
|
export declare const CONTEXT_WINDOW_MODES: readonly ContextWindowMode[];
|
|
50
67
|
export declare function listContextWindowModes(): ContextWindowMode[];
|
|
@@ -53,6 +70,6 @@ export declare function isDeprecatedContextWindowModeId(id: string): id is Depre
|
|
|
53
70
|
export declare function isContextWindowModeSelectionId(id: string): id is ContextWindowModeSelectionId;
|
|
54
71
|
export declare function getContextWindowMode(id: string | null | undefined): ContextWindowMode | null;
|
|
55
72
|
export declare function isContextWindowModeId(id: string): id is ContextWindowModeId;
|
|
56
|
-
export declare function resolveContextWindowPolicy(config?: ContextWindowConfigLike, overrideMode?: string | null | undefined): ContextWindowPolicy;
|
|
73
|
+
export declare function resolveContextWindowPolicy(config?: ContextWindowConfigLike, overrideMode?: string | null | undefined, maxContext?: number | undefined): ContextWindowPolicy;
|
|
57
74
|
export declare function formatContextWindowModeList(activeId?: string | null): string;
|
|
58
75
|
//# sourceMappingURL=context-window.d.ts.map
|
package/dist/types/index.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export type { AdaptiveConcurrencyConfig, AgentLearningConfig, AutonomyConfig, Br
|
|
|
7
7
|
export { DEFAULT_TUI_THINKING_WORD, FLEET_CHAT_VERBOSITY_VALUES, MAX_TUI_THINKING_WORD_LENGTH, normalizeTokenSavingTier, normalizeTuiThinkingWord, resolveFleetChatVerbosity, resolveTokenSavingTier, THEME_PRESET_IDS, } from './config.js';
|
|
8
8
|
export type { CompletedWorkEvidence, CompletedWorkSource, ContextEvidenceState, ContextFileEvidence, ContextIntentEvidence, ContextRepeatedReadEvidence, ToolEvidenceStatus, ToolOutputMetadata, } from './context-evidence.js';
|
|
9
9
|
export type { ContextSnapshot, ContextWindowAggressiveOn, ContextWindowConfigLike, ContextWindowMode, ContextWindowModeId, ContextWindowModeSelectionId, ContextWindowPolicy, ContextWindowThresholds, DeprecatedContextWindowModeId, } from './context-window.js';
|
|
10
|
-
export { CONTEXT_WINDOW_MODES, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEPRECATED_CONTEXT_WINDOW_MODE_ALIASES, formatContextWindowModeList, getContextWindowMode, isContextWindowModeId, isContextWindowModeSelectionId, isDeprecatedContextWindowModeId, listContextWindowModes, normalizeContextWindowModeId, resolveContextWindowPolicy, } from './context-window.js';
|
|
10
|
+
export { CONTEXT_WINDOW_MODES, CONTEXT_WINDOW_MODE_PINNED_META_KEY, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEPRECATED_CONTEXT_WINDOW_MODE_ALIASES, LARGE_WINDOW_DEEP_MODE_THRESHOLD, formatContextWindowModeList, getContextWindowMode, isContextWindowModeId, isContextWindowModeSelectionId, isDeprecatedContextWindowModeId, listContextWindowModes, normalizeContextWindowModeId, resolveContextWindowPolicy, } from './context-window.js';
|
|
11
11
|
export type { CouncilDistinctness, CouncilLLMCaller, CouncilModelTarget, CouncilOption, CouncilPersona, CouncilProfileConfig, CouncilQuestion, CouncilResolutionMethod, CouncilResult, CouncilSeatConfig, CouncilUsage, CouncilVoteResult, CouncilVoteStatus, ResolvedCouncilProfile, ResolvedCouncilSeat, } from './council.js';
|
|
12
12
|
export { DEFAULT_AUTONOMY_CONFIG, DEFAULT_CIRCUIT_BREAKER_CONFIG, DEFAULT_CONTEXT_CONFIG, DEFAULT_SESSION_LOGGING_CONFIG, DEFAULT_SESSION_PRUNE_DAYS, DEFAULT_TOOLS_CONFIG, } from './default-config.js';
|
|
13
13
|
export type { DesignKitEntry, DesignKitLoader, DesignKitManifest, DesignKitTokens, DesignStack, DesignStudioState, DesignTokenSet, TokenValueKind, } from './design-kit.js';
|
package/dist/types/index.js
CHANGED
|
@@ -111,6 +111,8 @@ function expectDefined(value, label) {
|
|
|
111
111
|
|
|
112
112
|
// src/types/context-window.ts
|
|
113
113
|
var DEFAULT_CONTEXT_WINDOW_MODE_ID = "balanced";
|
|
114
|
+
var LARGE_WINDOW_DEEP_MODE_THRESHOLD = 1e6;
|
|
115
|
+
var CONTEXT_WINDOW_MODE_PINNED_META_KEY = "contextWindowModePinned";
|
|
114
116
|
var DEPRECATED_CONTEXT_WINDOW_MODE_ALIASES = Object.freeze({
|
|
115
117
|
archival: "balanced"
|
|
116
118
|
});
|
|
@@ -118,7 +120,7 @@ var CONTEXT_WINDOW_MODES = Object.freeze([
|
|
|
118
120
|
{
|
|
119
121
|
id: "balanced",
|
|
120
122
|
name: "Balanced",
|
|
121
|
-
description: "Default rolling compaction: recent work stays verbatim, old tool output is trimmed.",
|
|
123
|
+
description: "Default rolling compaction: recent work stays verbatim, old tool output is trimmed. Windows of 1M+ tokens default to Deep.",
|
|
122
124
|
thresholds: { warn: 0.55, soft: 0.7, hard: 0.85 },
|
|
123
125
|
aggressiveOn: "soft",
|
|
124
126
|
preserveK: 8,
|
|
@@ -169,9 +171,11 @@ function getContextWindowMode(id) {
|
|
|
169
171
|
function isContextWindowModeId(id) {
|
|
170
172
|
return CONTEXT_WINDOW_MODES.some((m) => m.id === id);
|
|
171
173
|
}
|
|
172
|
-
function resolveContextWindowPolicy(config = {}, overrideMode) {
|
|
174
|
+
function resolveContextWindowPolicy(config = {}, overrideMode, maxContext) {
|
|
173
175
|
const requested = overrideMode ?? config.mode ?? DEFAULT_CONTEXT_WINDOW_MODE_ID;
|
|
174
|
-
const
|
|
176
|
+
const normalized = normalizeContextWindowModeId(requested) ?? DEFAULT_CONTEXT_WINDOW_MODE_ID;
|
|
177
|
+
const baseId = normalized === DEFAULT_CONTEXT_WINDOW_MODE_ID && typeof maxContext === "number" && maxContext >= LARGE_WINDOW_DEEP_MODE_THRESHOLD ? "deep" : normalized;
|
|
178
|
+
const mode = expectDefined(getContextWindowMode(baseId));
|
|
175
179
|
return {
|
|
176
180
|
...mode,
|
|
177
181
|
thresholds: {
|
|
@@ -1313,6 +1317,7 @@ export {
|
|
|
1313
1317
|
BUILTIN_PROMPT_CATEGORIES,
|
|
1314
1318
|
CHAT_MARKER_SOURCES,
|
|
1315
1319
|
CONTEXT_WINDOW_MODES,
|
|
1320
|
+
CONTEXT_WINDOW_MODE_PINNED_META_KEY,
|
|
1316
1321
|
ConfigError,
|
|
1317
1322
|
DEFAULT_AUTONOMY_CONFIG,
|
|
1318
1323
|
DEFAULT_CIRCUIT_BREAKER_CONFIG,
|
|
@@ -1333,6 +1338,7 @@ export {
|
|
|
1333
1338
|
GOVERNED_TOOL_EXECUTOR_META_KEY,
|
|
1334
1339
|
KNOWN_TOKEN_GROUPS,
|
|
1335
1340
|
KNOWN_TOKEN_NAMES,
|
|
1341
|
+
LARGE_WINDOW_DEEP_MODE_THRESHOLD,
|
|
1336
1342
|
MALFORMED_ARG_MARKERS,
|
|
1337
1343
|
MAX_TUI_THINKING_WORD_LENGTH,
|
|
1338
1344
|
MEMORY_TYPE_LABELS,
|
|
@@ -48,7 +48,7 @@ For controlled fan-out, use `spawn_subagent` → `assign_task` →
|
|
|
48
48
|
|
|
49
49
|
## Dispatch contract
|
|
50
50
|
|
|
51
|
-
Every assigned task
|
|
51
|
+
Every assigned task must state:
|
|
52
52
|
|
|
53
53
|
- the objective and why it matters;
|
|
54
54
|
- exact scope and non-goals;
|
|
@@ -58,6 +58,14 @@ Every assigned task should state:
|
|
|
58
58
|
- the narrowest required verification;
|
|
59
59
|
- known dependencies, risks, and assumptions.
|
|
60
60
|
|
|
61
|
+
The assignment tools enforce the boundary: `delegate` and `assign_task` reject
|
|
62
|
+
any call without an explicit `scope` (what the work covers) and at least one
|
|
63
|
+
concrete `outOfScope` non-goal (what the worker must not do). Treat a
|
|
64
|
+
rejection as a design checkpoint, not paperwork — if you cannot name what is
|
|
65
|
+
out of scope, the task is not decomposed enough yet. The boundary is rendered
|
|
66
|
+
into the worker's brief as a hard contract and survives into handoff
|
|
67
|
+
continuations, so write it once, precisely.
|
|
68
|
+
|
|
61
69
|
Match role and model to the work: use economical workers for bounded discovery
|
|
62
70
|
and capable workers for ambiguous implementation or synthesis. Provider
|
|
63
71
|
diversity is useful for independent review, not an end in itself.
|
|
@@ -7,6 +7,10 @@ self-contained handoff; do not take over fleet orchestration.
|
|
|
7
7
|
- Treat the assigned objective, scope, write authority, non-goals, and
|
|
8
8
|
completion criteria as your boundary. Later role, task, and per-spawn
|
|
9
9
|
instructions may narrow this baseline.
|
|
10
|
+
- Your brief carries an explicit "TASK BOUNDARY" block (scope plus
|
|
11
|
+
out-of-scope non-goals). It is a hard contract, not a suggestion: stay
|
|
12
|
+
inside it even when an out-of-scope change looks quick or obviously right —
|
|
13
|
+
report it back instead of doing it.
|
|
10
14
|
- Inspect before editing. Resolve discoverable context yourself and use the
|
|
11
15
|
project's existing conventions, tests, and tooling.
|
|
12
16
|
- Make only task-relevant changes. Preserve unrelated work and avoid broad
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wrongstack/core",
|
|
3
|
-
"version": "0.308.
|
|
3
|
+
"version": "0.308.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "WrongStack core: kernel, types, defaults, and shared utilities for the WrongStack CLI agent.",
|
|
6
6
|
"repository": {
|
|
@@ -94,7 +94,8 @@
|
|
|
94
94
|
},
|
|
95
95
|
"./storage": {
|
|
96
96
|
"types": "./dist/storage/index.d.ts",
|
|
97
|
-
"import": "./dist/storage/index.js"
|
|
97
|
+
"import": "./dist/storage/index.js",
|
|
98
|
+
"require": "./dist/storage/index.js"
|
|
98
99
|
},
|
|
99
100
|
"./session-catalog": {
|
|
100
101
|
"types": "./dist/session-catalog/index.d.ts",
|
|
@@ -181,8 +182,8 @@
|
|
|
181
182
|
"wrongstackApiVersion": "0.1.10",
|
|
182
183
|
"dependencies": {
|
|
183
184
|
"zod": "4.4.3",
|
|
184
|
-
"@wrongstack/
|
|
185
|
-
"@wrongstack/
|
|
185
|
+
"@wrongstack/persistence": "0.308.2",
|
|
186
|
+
"@wrongstack/kanban": "0.308.2"
|
|
186
187
|
},
|
|
187
188
|
"devDependencies": {
|
|
188
189
|
"@types/node": "^26.2.0",
|