@nexrall/code-core 1.4.71 → 1.4.73
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/agent/agentRegistry.d.ts.map +1 -1
- package/dist/agent/agentRegistry.js +28 -10
- package/dist/agent/agentTypes.d.ts.map +1 -1
- package/dist/agent/agentTypes.js +159 -48
- package/dist/agent/askOnce.d.ts.map +1 -1
- package/dist/agent/askOnce.js +14 -5
- package/dist/agent/backgroundAgents.d.ts.map +1 -1
- package/dist/agent/backgroundAgents.js +22 -8
- package/dist/agent/claimEvidence.d.ts.map +1 -1
- package/dist/agent/claimEvidence.js +2 -1
- package/dist/agent/compaction.d.ts.map +1 -1
- package/dist/agent/compaction.js +31 -10
- package/dist/agent/crossProcessLock.d.ts.map +1 -1
- package/dist/agent/crossProcessLock.js +14 -6
- package/dist/agent/editCompleteness.d.ts.map +1 -1
- package/dist/agent/fileLocks.d.ts.map +1 -1
- package/dist/agent/fileLocks.js +14 -5
- package/dist/agent/flaky.d.ts.map +1 -1
- package/dist/agent/hooks.d.ts.map +1 -1
- package/dist/agent/hooks.js +284 -75
- package/dist/agent/iterationPolicy.d.ts.map +1 -1
- package/dist/agent/iterationPolicy.js +26 -23
- package/dist/agent/lifecycleHost.d.ts.map +1 -1
- package/dist/agent/lifecycleHost.js +35 -11
- package/dist/agent/loop.d.ts +22 -4
- package/dist/agent/loop.d.ts.map +1 -1
- package/dist/agent/loop.js +238 -51
- package/dist/agent/memory.d.ts.map +1 -1
- package/dist/agent/memory.js +13 -7
- package/dist/agent/modelCatalogue.d.ts.map +1 -1
- package/dist/agent/modelCatalogue.js +4 -1
- package/dist/agent/nestedInstructions.d.ts +122 -0
- package/dist/agent/nestedInstructions.d.ts.map +1 -0
- package/dist/agent/nestedInstructions.js +696 -0
- package/dist/agent/peerRegistry.d.ts.map +1 -1
- package/dist/agent/peerRegistry.js +44 -6
- package/dist/agent/peerTransport.d.ts.map +1 -1
- package/dist/agent/peerTransport.js +18 -6
- package/dist/agent/planMode.d.ts.map +1 -1
- package/dist/agent/planMode.js +194 -41
- package/dist/agent/readDedupe.d.ts.map +1 -1
- package/dist/agent/readDedupe.js +3 -1
- package/dist/agent/securityLint.d.ts.map +1 -1
- package/dist/agent/sharedTasks.d.ts.map +1 -1
- package/dist/agent/sharedTasks.js +20 -5
- package/dist/agent/skills.d.ts +79 -0
- package/dist/agent/skills.d.ts.map +1 -1
- package/dist/agent/skills.js +403 -14
- package/dist/agent/subAgentBudget.d.ts.map +1 -1
- package/dist/agent/subTask.d.ts.map +1 -1
- package/dist/agent/subTask.js +87 -37
- package/dist/agent/subTaskSupport.d.ts.map +1 -1
- package/dist/agent/subTaskSupport.js +43 -14
- package/dist/agent/testIntegrity.d.ts.map +1 -1
- package/dist/agent/testIntegrity.js +4 -2
- package/dist/agent/toolDescriptions.d.ts.map +1 -1
- package/dist/agent/toolPrefetch.d.ts.map +1 -1
- package/dist/agent/toolPrefetch.js +15 -11
- package/dist/agent/trust.d.ts.map +1 -1
- package/dist/agent/trust.js +6 -2
- package/dist/agent/worktree.d.ts.map +1 -1
- package/dist/agent/worktree.js +70 -16
- package/dist/agent/worktreeEnforcement.d.ts.map +1 -1
- package/dist/agent/worktreeEnforcement.js +9 -4
- package/dist/api/client.d.ts +6 -0
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +64 -31
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +3 -1
- package/dist/checkpoint/manager.d.ts.map +1 -1
- package/dist/checkpoint/manager.js +22 -7
- package/dist/commands/loader.d.ts.map +1 -1
- package/dist/commands/loader.js +6 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/mcp/client.d.ts +42 -0
- package/dist/mcp/client.d.ts.map +1 -1
- package/dist/mcp/client.js +89 -24
- package/dist/mcp/httpClient.d.ts +24 -1
- package/dist/mcp/httpClient.d.ts.map +1 -1
- package/dist/mcp/httpClient.js +80 -14
- package/dist/mcp/instructions.d.ts +11 -0
- package/dist/mcp/instructions.d.ts.map +1 -0
- package/dist/mcp/instructions.js +42 -0
- package/dist/mcp/manager.d.ts +35 -0
- package/dist/mcp/manager.d.ts.map +1 -1
- package/dist/mcp/manager.js +135 -24
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +48 -14
- package/dist/mcp/sseClient.d.ts +9 -1
- package/dist/mcp/sseClient.d.ts.map +1 -1
- package/dist/mcp/sseClient.js +43 -10
- package/dist/mcp/stats.d.ts.map +1 -1
- package/dist/permissions/bashClassify.d.ts.map +1 -1
- package/dist/permissions/bashClassify.js +25 -3
- package/dist/permissions/destructive.d.ts.map +1 -1
- package/dist/permissions/destructive.js +7 -2
- package/dist/permissions/destructiveTokens.d.ts.map +1 -1
- package/dist/permissions/destructiveTokens.js +104 -24
- package/dist/permissions/modePolicy.d.ts.map +1 -1
- package/dist/permissions/modePolicy.js +40 -17
- package/dist/permissions/rules.d.ts.map +1 -1
- package/dist/permissions/rules.js +7 -5
- package/dist/plugins/data.d.ts.map +1 -1
- package/dist/plugins/data.js +6 -2
- package/dist/plugins/index.d.ts.map +1 -1
- package/dist/plugins/index.js +18 -7
- package/dist/plugins/installer.d.ts.map +1 -1
- package/dist/plugins/installer.js +17 -10
- package/dist/plugins/sources.d.ts.map +1 -1
- package/dist/plugins/sources.js +9 -3
- package/dist/tools/executor.d.ts.map +1 -1
- package/dist/tools/executor.js +454 -168
- package/dist/tools/sandbox.d.ts.map +1 -1
- package/dist/tools/sandbox.js +5 -1
- package/dist/tools/symbols.d.ts.map +1 -1
- package/dist/tools/symbols.js +30 -8
- package/dist/tools/tsLangService.d.ts.map +1 -1
- package/dist/tools/tsLangService.js +12 -4
- package/dist/types.d.ts +17 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/util/frontmatter.d.ts.map +1 -1
- package/dist/util/miniYaml.d.ts.map +1 -1
- package/dist/util/miniYaml.js +3 -1
- package/package.json +1 -1
package/dist/agent/loop.js
CHANGED
|
@@ -5,6 +5,7 @@ exports.lastToolResults = exports.summariseSubTaskProgress = exports.capSubTaskT
|
|
|
5
5
|
exports.dispatchSubAgent = dispatchSubAgent;
|
|
6
6
|
exports.dispatchBackgroundSubAgent = dispatchBackgroundSubAgent;
|
|
7
7
|
exports.dispatchForkedSubAgent = dispatchForkedSubAgent;
|
|
8
|
+
exports.runForkedSkill = runForkedSkill;
|
|
8
9
|
exports.runAgentLoop = runAgentLoop;
|
|
9
10
|
exports.trimToResumableBoundary = trimToResumableBoundary;
|
|
10
11
|
const readDedupe_1 = require("./readDedupe");
|
|
@@ -16,6 +17,7 @@ const executor_1 = require("../tools/executor");
|
|
|
16
17
|
const sharedTasks_1 = require("./sharedTasks");
|
|
17
18
|
const agentTypes_1 = require("./agentTypes");
|
|
18
19
|
const skills_1 = require("./skills");
|
|
20
|
+
const nestedInstructions_1 = require("./nestedInstructions");
|
|
19
21
|
const rules_1 = require("../permissions/rules");
|
|
20
22
|
const modePolicy_1 = require("../permissions/modePolicy");
|
|
21
23
|
const destructive_1 = require("../permissions/destructive");
|
|
@@ -25,6 +27,7 @@ const destructive_1 = require("../permissions/destructive");
|
|
|
25
27
|
const bashClassify_1 = require("../permissions/bashClassify");
|
|
26
28
|
Object.defineProperty(exports, "bashNeedsRepoLock", { enumerable: true, get: function () { return bashClassify_1.bashNeedsRepoLock; } });
|
|
27
29
|
const planMode_1 = require("./planMode");
|
|
30
|
+
const instructions_1 = require("../mcp/instructions");
|
|
28
31
|
const worktreeEnforcement_1 = require("./worktreeEnforcement");
|
|
29
32
|
const sandbox_1 = require("../tools/sandbox");
|
|
30
33
|
const testIntegrity_1 = require("./testIntegrity");
|
|
@@ -235,8 +238,11 @@ function dispatchBackgroundSubAgent(input, options, agentTypes, hub) {
|
|
|
235
238
|
if (overBudget)
|
|
236
239
|
return { error: overBudget };
|
|
237
240
|
const agentType = (typeof input.subagent_type === 'string' && input.subagent_type) || 'general-purpose';
|
|
238
|
-
const description = (typeof input.description === 'string' && input.description.trim())
|
|
239
|
-
|
|
241
|
+
const description = (typeof input.description === 'string' && input.description.trim()) ||
|
|
242
|
+
String(input.prompt ?? '')
|
|
243
|
+
.trim()
|
|
244
|
+
.slice(0, 60) ||
|
|
245
|
+
agentType;
|
|
240
246
|
const mode = (0, modePolicy_1.parseMode)(options.mode);
|
|
241
247
|
const backgroundPermission = backgroundPermissionPolicy(options, mode);
|
|
242
248
|
const started = hub.start({ description, agentType }, async ({ abort, onToolUse }) => {
|
|
@@ -344,6 +350,59 @@ function dispatchForkedSubAgent(conversation, prompt, options, agentTypes, hub)
|
|
|
344
350
|
}
|
|
345
351
|
return { id: started.id, description, agentType, messages: conversation.length };
|
|
346
352
|
}
|
|
353
|
+
/**
|
|
354
|
+
* Resolve the skill a `use_skill` call names, returning it ONLY if it runs as a
|
|
355
|
+
* fork (`context: 'fork'`) — the signal `runAgentLoop` uses to divert the call
|
|
356
|
+
* away from the inline-injection path and into the sub-agent machinery.
|
|
357
|
+
*
|
|
358
|
+
* The snapshot is the list the catalogue was built from (loaded once per run);
|
|
359
|
+
* a MISS reloads from disk so a skill created mid-turn is found — the same
|
|
360
|
+
* trade-off `runSubTask` documents for `agentTypes`.
|
|
361
|
+
*/
|
|
362
|
+
function resolveForkSkill(skills, input, options) {
|
|
363
|
+
const name = typeof input.name === 'string' ? input.name.trim() : '';
|
|
364
|
+
if (!name)
|
|
365
|
+
return undefined;
|
|
366
|
+
const skill = (0, skills_1.findSkill)(skills, name) ??
|
|
367
|
+
(0, skills_1.resolveNestedSkill)(options.workDir, name) ?? // `<dir>:<name>` - see skills.ts
|
|
368
|
+
(0, skills_1.findSkill)((0, skills_1.loadSkillsWithWarnings)(options.workDir, options._extraSkills ?? []).skills, name);
|
|
369
|
+
return skill?.context === 'fork' ? skill : undefined;
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Run a `context: fork` skill as an ISOLATED sub-agent (Claude Code parity):
|
|
373
|
+
* the expanded body is the child's prompt and the parent conversation is NOT
|
|
374
|
+
* copied in — a forked skill must stand on its own, which is the point of the
|
|
375
|
+
* fork (the invoking context stays clean and the child cannot lean on it).
|
|
376
|
+
*
|
|
377
|
+
* Background by default (CC 2.1.x), exactly like `task(run_in_background)`: the
|
|
378
|
+
* call returns as soon as the child starts and the report arrives later as a
|
|
379
|
+
* <task-notification>; `background: false` awaits the child and returns its
|
|
380
|
+
* report as the tool result. Hosts with no hub (headless `-p`/SDK callers)
|
|
381
|
+
* always await — the same place CC waits regardless of the frontmatter.
|
|
382
|
+
*
|
|
383
|
+
* The `subagent_type` rides the ordinary `task` dispatch, so `deny:
|
|
384
|
+
* ["task(<name>)"]` rules, the depth limit and the session sub-agent budget all
|
|
385
|
+
* apply unchanged (see runSubTask / dispatchSubAgent).
|
|
386
|
+
*/
|
|
387
|
+
async function runForkedSkill(skill, args, options, agentTypes) {
|
|
388
|
+
const workDir = options.workDir ?? process.cwd();
|
|
389
|
+
const input = {
|
|
390
|
+
prompt: (0, skills_1.expandSkill)(skill, args, workDir),
|
|
391
|
+
description: `/${skill.name}`,
|
|
392
|
+
subagent_type: skill.agent?.trim() || 'general-purpose',
|
|
393
|
+
};
|
|
394
|
+
// `model:` on the skill applies to the fork exactly as it does when the skill is
|
|
395
|
+
// typed as a turn (`/name` in chat.ts): the child starts on that model. A model
|
|
396
|
+
// the agent TYPE declares still wins — the child resolves it over ours, same as
|
|
397
|
+
// any `task` call (see subTask.ts).
|
|
398
|
+
const childOptions = skill.model ? { ...options, model: (0, modelCatalogue_1.resolveSubAgentModel)(skill.model, options.model) } : options;
|
|
399
|
+
const depth = options._depth ?? 0;
|
|
400
|
+
const wantsBackground = skill.background !== false && depth === 0;
|
|
401
|
+
if (wantsBackground && childOptions.backgroundAgents) {
|
|
402
|
+
return dispatchBackgroundSubAgent(input, childOptions, agentTypes, childOptions.backgroundAgents);
|
|
403
|
+
}
|
|
404
|
+
return dispatchSubAgent(input, childOptions, agentTypes);
|
|
405
|
+
}
|
|
347
406
|
// ─── Agent Loop ───────────────────────────────────────────────────────────────
|
|
348
407
|
async function runAgentLoop(initialMessages, options) {
|
|
349
408
|
const messages = [...initialMessages];
|
|
@@ -400,7 +459,10 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
400
459
|
const addContext = (ctx) => {
|
|
401
460
|
const m = messages[lastIdx];
|
|
402
461
|
if (m && Array.isArray(m.content))
|
|
403
|
-
messages[lastIdx] = {
|
|
462
|
+
messages[lastIdx] = {
|
|
463
|
+
...m,
|
|
464
|
+
content: [...m.content, { type: 'text', text: `\n\n<hook-context>\n${ctx}\n</hook-context>` }],
|
|
465
|
+
};
|
|
404
466
|
};
|
|
405
467
|
const sid = options.sessionId || subAgentBudget_1.PROCESS_BUDGET_KEY;
|
|
406
468
|
if (isPrompt && !_sessionStarted.has(sid) && (hooks.SessionStart?.length ?? 0) > 0) {
|
|
@@ -436,7 +498,9 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
436
498
|
// the user request that caused it belong to the same run — which is precisely
|
|
437
499
|
// the chain of custody the trail exists to prove.
|
|
438
500
|
const auditCfg = options.audit
|
|
439
|
-
?
|
|
501
|
+
? options.audit.sessionId
|
|
502
|
+
? options.audit
|
|
503
|
+
: { ...options.audit, sessionId: (0, audit_1.newAuditSessionId)() }
|
|
440
504
|
: undefined;
|
|
441
505
|
const auditCtx = auditCfg
|
|
442
506
|
? {
|
|
@@ -479,7 +543,24 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
479
543
|
// just a reusable prompt template (via use_skill), not another spawn point, so
|
|
480
544
|
// sub-agents benefit from the same playbooks without the recursion concerns that
|
|
481
545
|
// gate agentsCatalogue to the top level.
|
|
482
|
-
const
|
|
546
|
+
const skillsList = (0, skills_1.loadSkillsWithWarnings)(options.workDir, options._extraSkills ?? []).skills;
|
|
547
|
+
// Nested-skill discovery (CC 2.1.x parity): as tool calls touch files, their
|
|
548
|
+
// directories get checked for their own `.nexrall/skills`, and anything found is
|
|
549
|
+
// addressed as `<dir>:<name>` (see skills.ts). The catalogue below is rebuilt
|
|
550
|
+
// PER REQUEST precisely so a discovery mid-turn reaches the model on its next
|
|
551
|
+
// round-trip without a new user message; resolutions go through findSkill /
|
|
552
|
+
// resolveNestedSkill, so a scoped name works even before its turn advertises it.
|
|
553
|
+
const nestedSkillScan = options.workDir ? new skills_1.NestedSkillScanner(options.workDir) : null;
|
|
554
|
+
// Nested instruction files (S18c): a subdirectory's `nexrall.md` applies once the
|
|
555
|
+
// agent works there — the same 2.1.x behaviour as the nested skills above, and the
|
|
556
|
+
// same per-request composition (the prompt reads the ledger's list() every call).
|
|
557
|
+
// The HOST normally owns the ledger: one scanner per session, passed to every run,
|
|
558
|
+
// because a loaded file must not silently vanish between turns. A host that passes
|
|
559
|
+
// nothing gets run-scoped discovery — the right lifetime for a one-shot run. `false`
|
|
560
|
+
// disables the feature entirely (`--bare`: no discovery).
|
|
561
|
+
const nestedInstructionScan = options._nestedInstructions === false
|
|
562
|
+
? null
|
|
563
|
+
: (options._nestedInstructions ?? (options.workDir ? new nestedInstructions_1.NestedInstructionScanner(options.workDir) : null));
|
|
483
564
|
// Plan mode instructions ride the project-instructions channel, which is
|
|
484
565
|
// authoritative in the system prompt. Prepended rather than appended: the
|
|
485
566
|
// lock has to be read before the project conventions it overrides.
|
|
@@ -491,7 +572,26 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
491
572
|
options.planMode ? planMode_1.PLAN_MODE_INSTRUCTIONS : null,
|
|
492
573
|
options.worktree ? (0, worktreeEnforcement_1.worktreeInstructions)(options.worktree) : null,
|
|
493
574
|
options.nexrallMd ?? null,
|
|
494
|
-
]
|
|
575
|
+
]
|
|
576
|
+
.filter((s) => s !== null)
|
|
577
|
+
.join('\n\n---\n\n') || undefined; // '' (nothing at all was present) must behave exactly like the old `: options.nexrallMd` fallback (undefined), not an empty-but-truthy string.
|
|
578
|
+
// MCP `initialize.instructions` — a server's own reference notes. Composed PER
|
|
579
|
+
// REQUEST so a server that connects (or is enabled) mid-session contributes
|
|
580
|
+
// without the caller rebuilding the session brief; a sub-agent only reads the
|
|
581
|
+
// servers its allowlist names (another server's notes are not its business).
|
|
582
|
+
// The size caps live in mcp/instructions.ts.
|
|
583
|
+
const mcpInstructionsBlock = () => (0, instructions_1.formatMcpInstructions)((options.mcpManager?.serverInstructions() ?? []).filter((e) => !options._mcpServerAllowlist || options._mcpServerAllowlist.has(e.server)));
|
|
584
|
+
// Per-server `alwaysLoad` (CC parity): names the user pinned in mcp.json. The
|
|
585
|
+
// backend keeps their `<server>__*` tools out of BOTH deferral paths (tool-search
|
|
586
|
+
// `defer_loading` and the cap selector's droppable set). Sub-agent runs narrow the
|
|
587
|
+
// list to servers their own allowlist can actually reach — naming a server the run
|
|
588
|
+
// cannot use would protect nothing and leak the catalogue's shape. Empty →
|
|
589
|
+
// undefined so older backends receive the exact same body as before.
|
|
590
|
+
const alwaysLoadServerNames = (() => {
|
|
591
|
+
const named = options.mcpManager?.alwaysLoadServers() ?? [];
|
|
592
|
+
const scoped = named.filter((n) => !options._mcpServerAllowlist || options._mcpServerAllowlist.has(n));
|
|
593
|
+
return scoped.length ? scoped : undefined;
|
|
594
|
+
})();
|
|
495
595
|
// The request options every main-loop call sends. Shared with the compaction
|
|
496
596
|
// summariser so its request has the SAME system prompt and tools as the conversation
|
|
497
597
|
// — that is what lets the summary call read the whole history from cache (0.10×)
|
|
@@ -502,7 +602,13 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
502
602
|
effort: options.effort,
|
|
503
603
|
env: options.env,
|
|
504
604
|
editorContext: options.editorContext,
|
|
505
|
-
nexrallMd:
|
|
605
|
+
nexrallMd: [
|
|
606
|
+
planAndWorktreeAwareNexrallMd,
|
|
607
|
+
(0, nestedInstructions_1.formatNestedInstructions)(nestedInstructionScan?.list() ?? []),
|
|
608
|
+
mcpInstructionsBlock(),
|
|
609
|
+
]
|
|
610
|
+
.filter((s) => !!s)
|
|
611
|
+
.join('\n\n---\n\n') || undefined,
|
|
506
612
|
clientType: options.clientType,
|
|
507
613
|
abortSignal: options.abortSignal,
|
|
508
614
|
// MCP tools, plus the per-agent memory tool when (and only when) this run
|
|
@@ -511,8 +617,7 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
511
617
|
// to every other run: a tool the model cannot see is one it cannot try,
|
|
512
618
|
// which is better than advertising it everywhere and refusing it at the gate.
|
|
513
619
|
extraTools: [
|
|
514
|
-
...(options.mcpManager?.getAnthropicTools() ?? [])
|
|
515
|
-
.filter((t) => !options._mcpServerAllowlist || options._mcpServerAllowlist.has(String(t.name).split('__')[0])),
|
|
620
|
+
...(options.mcpManager?.getAnthropicTools() ?? []).filter((t) => !options._mcpServerAllowlist || options._mcpServerAllowlist.has(String(t.name).split('__')[0])),
|
|
516
621
|
...(options._agentMemory ? [iterationPolicy_1.AGENT_MEMORY_TOOL_SCHEMA] : []),
|
|
517
622
|
...(options.extraToolSchemas ?? []),
|
|
518
623
|
],
|
|
@@ -522,11 +627,7 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
522
627
|
// memory_write, which no sub-agent allowlist contains.
|
|
523
628
|
// Sub-agents NEVER write the shared store (the main agent decides what to persist) —
|
|
524
629
|
// an unnamed/general-purpose child has no allowlist, which used to read as "may write".
|
|
525
|
-
canWriteSharedMemory: depth > 0
|
|
526
|
-
? false
|
|
527
|
-
: options._allowedTools
|
|
528
|
-
? options._allowedTools.has('memory_write')
|
|
529
|
-
: true, // no allowlist = main agent = may write
|
|
630
|
+
canWriteSharedMemory: depth > 0 ? false : options._allowedTools ? options._allowedTools.has('memory_write') : true, // no allowlist = main agent = may write
|
|
530
631
|
hasOwnAgentStore: !!options._agentMemory,
|
|
531
632
|
// Same derivation, same reason, for the network. The base prompt's
|
|
532
633
|
// "Web search" section tells the run to reach the network, but
|
|
@@ -543,11 +644,15 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
543
644
|
// (and gets refused on) write, office and image tools. Sorted so the tools cache
|
|
544
645
|
// prefix is identical for every run of the same agent type.
|
|
545
646
|
toolAllowlist: options._allowedTools ? [...options._allowedTools].sort() : undefined,
|
|
647
|
+
// Servers the user pinned with `alwaysLoad: true` (computed above). The backend
|
|
648
|
+
// keeps their `<server>__*` tools out of the tool cap and both deferral paths;
|
|
649
|
+
// undefined (not []) when nothing is pinned so older backends see the same body.
|
|
650
|
+
alwaysLoadServers: alwaysLoadServerNames,
|
|
546
651
|
// Withholds the `task` schema and its instructions when this run cannot
|
|
547
652
|
// delegate — see canSpawnSubAgents.
|
|
548
653
|
canSpawnSubAgents: maySpawn,
|
|
549
654
|
agents: agentsCatalogue || undefined,
|
|
550
|
-
skills:
|
|
655
|
+
skills: (0, skills_1.summariseSkills)(skillsList) || undefined,
|
|
551
656
|
});
|
|
552
657
|
// Optional OS-level bash sandbox (opt-in via settings.json "sandbox").
|
|
553
658
|
const sandboxCfg = (0, sandbox_1.parseSandboxConfig)(settings.raw.sandbox) ?? undefined;
|
|
@@ -563,7 +668,7 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
563
668
|
// Per-run: a sub-agent's own runAgentLoop gets its own, so it never sees its parent's reads.
|
|
564
669
|
const readDedupe = new readDedupe_1.ReadDedupe(options.workDir);
|
|
565
670
|
// Only the main agent owns background agents (runSubTask never passes the hub down).
|
|
566
|
-
const drainBackgroundNotifications = () => depth === 0 ? (options.backgroundAgents?.takeNotifications() ?? []) : [];
|
|
671
|
+
const drainBackgroundNotifications = () => (depth === 0 ? (options.backgroundAgents?.takeNotifications() ?? []) : []);
|
|
567
672
|
// Live prompt-size estimate, updated from usage events after every stream.
|
|
568
673
|
let lastPromptTokens = 0;
|
|
569
674
|
let compacting = false; // re-entrancy guard — compaction itself calls streamChat
|
|
@@ -734,7 +839,11 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
734
839
|
compactDisabled = false;
|
|
735
840
|
compactFailures = 0;
|
|
736
841
|
}
|
|
737
|
-
if (autoCompact &&
|
|
842
|
+
if (autoCompact &&
|
|
843
|
+
!compactDisabled &&
|
|
844
|
+
!compacting &&
|
|
845
|
+
(tokenPressure || bytePressure) &&
|
|
846
|
+
messages.length > compaction_1.COMPACT_KEEP_MIN + 2) {
|
|
738
847
|
compacting = true;
|
|
739
848
|
try {
|
|
740
849
|
// Measured BEFORE, so "did it actually help?" is a fact about bytes rather
|
|
@@ -759,9 +868,7 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
759
868
|
const reclaimed = bytesBefore - bytesAfter;
|
|
760
869
|
if (did) {
|
|
761
870
|
lastPromptTokens = 0; // stale — next usage event refreshes it
|
|
762
|
-
const reason = bytePressure
|
|
763
|
-
? `body ~${(bodyBytes / (1024 * 1024)).toFixed(1)}MB`
|
|
764
|
-
: 'context window';
|
|
871
|
+
const reason = bytePressure ? `body ~${(bodyBytes / (1024 * 1024)).toFixed(1)}MB` : 'context window';
|
|
765
872
|
// Silent, same as the prune above — routine housekeeping. Only the
|
|
766
873
|
// actionable "auto-compaction paused" warning below is surfaced.
|
|
767
874
|
void reason;
|
|
@@ -1091,8 +1198,8 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1091
1198
|
// be an example here too — as of the 2026-08-11+ provider-agnostic port it is a
|
|
1092
1199
|
// plain client tool executed by THIS process, so it no longer belongs in this set.)
|
|
1093
1200
|
const serverSideResultIds = new Set(assistantMessage.content
|
|
1094
|
-
.filter(b => b.type === 'tool_result')
|
|
1095
|
-
.map(b => b.tool_use_id)
|
|
1201
|
+
.filter((b) => b.type === 'tool_result')
|
|
1202
|
+
.map((b) => b.tool_use_id)
|
|
1096
1203
|
.filter((id) => !!id));
|
|
1097
1204
|
const toolUseBlocks = assistantMessage.content.filter((block) => block.type === 'tool_use' && !serverSideResultIds.has(block.id));
|
|
1098
1205
|
// ─── TRUNCATED-TOOL-CALL GUARD ──────────────────────────────────────────
|
|
@@ -1117,9 +1224,7 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1117
1224
|
// model retries with a smaller call, rather than either failing
|
|
1118
1225
|
// mysteriously or silently mutating a file incompletely.
|
|
1119
1226
|
const lastBlock = assistantMessage.content[assistantMessage.content.length - 1];
|
|
1120
|
-
const truncatedToolUseId = assistantMessage.stopReason === 'max_tokens' && lastBlock?.type === 'tool_use'
|
|
1121
|
-
? lastBlock.id
|
|
1122
|
-
: undefined;
|
|
1227
|
+
const truncatedToolUseId = assistantMessage.stopReason === 'max_tokens' && lastBlock?.type === 'tool_use' ? lastBlock.id : undefined;
|
|
1123
1228
|
// 4. If no tool use → agent produced a final text response.
|
|
1124
1229
|
// But if the user queued a follow-up while we were working, fold it in as the
|
|
1125
1230
|
// next user turn and keep going instead of ending (Claude-Code style).
|
|
@@ -1170,7 +1275,8 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1170
1275
|
const bullet = fresh.map((t) => ` • ${t.path}: ${t.reason}`).join('\n');
|
|
1171
1276
|
messages.push({
|
|
1172
1277
|
role: 'user',
|
|
1173
|
-
content: [
|
|
1278
|
+
content: [
|
|
1279
|
+
{
|
|
1174
1280
|
type: 'text',
|
|
1175
1281
|
text: 'STOP — test-integrity check. Before finishing, I detected that you WEAKENED test(s) this session:\n' +
|
|
1176
1282
|
bullet +
|
|
@@ -1178,7 +1284,8 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1178
1284
|
' 1. Revert the weakening and fix the actual code so the ORIGINAL test passes, or\n' +
|
|
1179
1285
|
' 2. Justify concretely why the change is a legitimate test refactor (e.g. the behaviour was intentionally removed per the task, a duplicate test, or the assertion was genuinely wrong) — and confirm the remaining tests still meaningfully cover the behaviour.\n' +
|
|
1180
1286
|
'Then re-run the test suite to prove it passes for real.',
|
|
1181
|
-
}
|
|
1287
|
+
},
|
|
1288
|
+
],
|
|
1182
1289
|
});
|
|
1183
1290
|
continue;
|
|
1184
1291
|
}
|
|
@@ -1191,15 +1298,19 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1191
1298
|
if (flaky.length) {
|
|
1192
1299
|
for (const f of flaky)
|
|
1193
1300
|
flakyNudgedCmds.add(f.cmd);
|
|
1194
|
-
const bullet = flaky
|
|
1301
|
+
const bullet = flaky
|
|
1302
|
+
.map((f) => ` • ${f.cmd} (${f.passes} pass / ${f.fails} fail on identical code)`)
|
|
1303
|
+
.join('\n');
|
|
1195
1304
|
messages.push({
|
|
1196
1305
|
role: 'user',
|
|
1197
|
-
content: [
|
|
1306
|
+
content: [
|
|
1307
|
+
{
|
|
1198
1308
|
type: 'text',
|
|
1199
1309
|
text: 'STOP — flaky-test check. These command(s) produced BOTH a pass and a fail with NO code change in between, so they are non-deterministic:\n' +
|
|
1200
1310
|
bullet +
|
|
1201
1311
|
'\n\nA green run of a flaky test proves nothing, and re-running until it passes is not a fix. Do NOT declare success on this basis. Instead: identify the source of non-determinism (unseeded RNG, timing/sleep races, test-ordering or shared-state dependence, wall-clock/timezone, network) and make the test deterministic — or, if it is out of scope, say so explicitly and flag it as pre-existing flakiness rather than treating the passing run as proof.',
|
|
1202
|
-
}
|
|
1312
|
+
},
|
|
1313
|
+
],
|
|
1203
1314
|
});
|
|
1204
1315
|
continue;
|
|
1205
1316
|
}
|
|
@@ -1213,10 +1324,12 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1213
1324
|
verificationNudgeSent = true;
|
|
1214
1325
|
messages.push({
|
|
1215
1326
|
role: 'user',
|
|
1216
|
-
content: [
|
|
1327
|
+
content: [
|
|
1328
|
+
{
|
|
1217
1329
|
type: 'text',
|
|
1218
|
-
text:
|
|
1219
|
-
}
|
|
1330
|
+
text: "Before finishing: you edited file(s) in this task but I don't see a build/test/lint/typecheck command run to confirm the change works. If this project has one, please run it now and fix any failures. If verification genuinely doesn't apply here (e.g. docs-only change, no test suite), just say so and finish.",
|
|
1331
|
+
},
|
|
1332
|
+
],
|
|
1220
1333
|
});
|
|
1221
1334
|
continue;
|
|
1222
1335
|
}
|
|
@@ -1358,6 +1471,11 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1358
1471
|
if (err instanceof subTaskSupport_1.ToolNotAllowedError)
|
|
1359
1472
|
deniedReason = err.message;
|
|
1360
1473
|
}
|
|
1474
|
+
// `context: fork` skills never reach the executor's inline injection: the
|
|
1475
|
+
// call is diverted into the same sub-agent machinery `task` uses, with the
|
|
1476
|
+
// expanded body as the child's prompt (see runForkedSkill). Resolved here —
|
|
1477
|
+
// only for an allowed `use_skill` — so a denied call pays nothing for it.
|
|
1478
|
+
const forkSkill = permitted && name === 'use_skill' ? resolveForkSkill(skillsList, input, options) : undefined;
|
|
1361
1479
|
if (!permitted) {
|
|
1362
1480
|
result = { error: deniedReason ?? 'Permission denied by user' };
|
|
1363
1481
|
}
|
|
@@ -1373,8 +1491,9 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1373
1491
|
// reimplementing depth/budget/concurrency enforcement a second time.
|
|
1374
1492
|
// Background only for the MAIN agent, and only when the host can deliver the
|
|
1375
1493
|
// result later; otherwise it quietly runs in the foreground.
|
|
1376
|
-
const wantsBackground = input.run_in_background === true
|
|
1377
|
-
|
|
1494
|
+
const wantsBackground = input.run_in_background === true ||
|
|
1495
|
+
(input.run_in_background !== false &&
|
|
1496
|
+
!!(0, agentTypes_1.findAgentType)(agentTypes, String(input.subagent_type ?? ''))?.background);
|
|
1378
1497
|
const taskOptions = { ...auditOptions, _taskToolUseId: id };
|
|
1379
1498
|
// PreToolUse/PostToolUse cover spawns too (Claude Code's matcher "Task"/"Agent"
|
|
1380
1499
|
// works the same way): a hook can veto a delegation or audit what came back.
|
|
@@ -1383,9 +1502,10 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1383
1502
|
result = { error: `Blocked by PreToolUse hook: ${preTask.reason}` };
|
|
1384
1503
|
}
|
|
1385
1504
|
else {
|
|
1386
|
-
result =
|
|
1387
|
-
|
|
1388
|
-
|
|
1505
|
+
result =
|
|
1506
|
+
wantsBackground && depth === 0 && options.backgroundAgents && !input.resume_agent_id
|
|
1507
|
+
? dispatchBackgroundSubAgent(input, taskOptions, agentTypes, options.backgroundAgents)
|
|
1508
|
+
: await dispatchSubAgent(input, taskOptions, agentTypes);
|
|
1389
1509
|
const postTask = await (0, hooks_1.runToolHooks)(hooks.PostToolUse, 'PostToolUse', name, input, options.workDir, result, (0, hooks_1.hookRunOptsFor)(options));
|
|
1390
1510
|
const injectedTask = [preTask.context, postTask.context].filter(Boolean).join('\n');
|
|
1391
1511
|
if (injectedTask) {
|
|
@@ -1399,6 +1519,28 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1399
1519
|
}
|
|
1400
1520
|
}
|
|
1401
1521
|
}
|
|
1522
|
+
else if (forkSkill) {
|
|
1523
|
+
// `use_skill` on a `context: fork` skill — the same hook pair as any call,
|
|
1524
|
+
// so a Pre can veto the fork and a Post sees its start/report.
|
|
1525
|
+
const preFork = await (0, hooks_1.runToolHooks)(hooks.PreToolUse, 'PreToolUse', name, input, options.workDir, undefined, (0, hooks_1.hookRunOptsFor)(options));
|
|
1526
|
+
if (preFork.block) {
|
|
1527
|
+
result = { error: `Blocked by PreToolUse hook: ${preFork.reason}` };
|
|
1528
|
+
}
|
|
1529
|
+
else {
|
|
1530
|
+
result = await runForkedSkill(forkSkill, typeof input.args === 'string' ? input.args : '', { ...auditOptions, _taskToolUseId: id }, agentTypes);
|
|
1531
|
+
const postFork = await (0, hooks_1.runToolHooks)(hooks.PostToolUse, 'PostToolUse', name, input, options.workDir, result, (0, hooks_1.hookRunOptsFor)(options));
|
|
1532
|
+
const injectedFork = [preFork.context, postFork.context].filter(Boolean).join('\n');
|
|
1533
|
+
if (injectedFork) {
|
|
1534
|
+
if (result.error !== undefined)
|
|
1535
|
+
result.error = `${result.error}\n\n[hook] ${injectedFork}`;
|
|
1536
|
+
else
|
|
1537
|
+
result.output = `${result.output ?? ''}\n\n[hook] ${injectedFork}`;
|
|
1538
|
+
}
|
|
1539
|
+
if (postFork.block) {
|
|
1540
|
+
result.error = `${result.error ? result.error + '\n' : ''}PostToolUse hook flagged: ${postFork.reason}`;
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1402
1544
|
else {
|
|
1403
1545
|
const pre = await (0, hooks_1.runToolHooks)(hooks.PreToolUse, 'PreToolUse', name, input, options.workDir, undefined, (0, hooks_1.hookRunOptsFor)(options));
|
|
1404
1546
|
if (pre.block) {
|
|
@@ -1407,9 +1549,7 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1407
1549
|
else {
|
|
1408
1550
|
// TaskCreated needs the id of the task the tool is ABOUT to create, which only
|
|
1409
1551
|
// the tool can know — so snapshot the ids first and diff afterwards (see below).
|
|
1410
|
-
const taskIdsBefore = name === 'create_shared_task'
|
|
1411
|
-
? new Set((0, sharedTasks_1.listSharedTasks)(options.workDir).map((t) => t.id))
|
|
1412
|
-
: null;
|
|
1552
|
+
const taskIdsBefore = name === 'create_shared_task' ? new Set((0, sharedTasks_1.listSharedTasks)(options.workDir).map((t) => t.id)) : null;
|
|
1413
1553
|
try {
|
|
1414
1554
|
// TaskCompleted (Claude Code): the hook is the GATE, so it runs BEFORE the
|
|
1415
1555
|
// update — refusing means the task simply stays open, and the reason goes
|
|
@@ -1445,9 +1585,10 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1445
1585
|
}
|
|
1446
1586
|
else if (options.mcpManager?.isMcpTool(name)) {
|
|
1447
1587
|
const mcpOutput = await (0, subTaskSupport_1.stopAwaiting)(options.mcpManager.callTool(name, input, options.abortSignal), options.abortSignal);
|
|
1448
|
-
result =
|
|
1449
|
-
|
|
1450
|
-
|
|
1588
|
+
result =
|
|
1589
|
+
mcpOutput === subTaskSupport_1.STOP_TIMEOUT_RESULT
|
|
1590
|
+
? subTaskSupport_1.STOP_TIMEOUT_RESULT
|
|
1591
|
+
: { output: (0, executor_1.capExternalOutput)(mcpOutput ?? '', `mcp-${name}`) };
|
|
1451
1592
|
// 3. Fall through to built-in executor
|
|
1452
1593
|
}
|
|
1453
1594
|
else {
|
|
@@ -1472,7 +1613,9 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1472
1613
|
// This matters far more with sub-agents than without: up to 4 run
|
|
1473
1614
|
// concurrently, each issuing its own tool calls into this same process.
|
|
1474
1615
|
const locks = name === 'bash'
|
|
1475
|
-
? (
|
|
1616
|
+
? (0, bashClassify_1.bashNeedsRepoLock)(String(input.command ?? ''))
|
|
1617
|
+
? [fileLocks_1.REPO_STATE_LOCK]
|
|
1618
|
+
: []
|
|
1476
1619
|
: (0, fileLocks_1.lockPathsFor)(name, input, options.workDir);
|
|
1477
1620
|
// Two lock layers, nested outer-then-inner:
|
|
1478
1621
|
// 1. Cross-process (OS lock file) — the ONLY layer that protects
|
|
@@ -1523,7 +1666,9 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1523
1666
|
const task = created[created.length - 1];
|
|
1524
1667
|
if (task) {
|
|
1525
1668
|
const veto = await taskHookVeto(hooks.TaskCreated, 'TaskCreated', input, options, {
|
|
1526
|
-
task_id: task.id,
|
|
1669
|
+
task_id: task.id,
|
|
1670
|
+
task_subject: task.content,
|
|
1671
|
+
task_description: task.content,
|
|
1527
1672
|
});
|
|
1528
1673
|
if (veto) {
|
|
1529
1674
|
(0, sharedTasks_1.deleteSharedTask)(options.workDir, task.id);
|
|
@@ -1531,6 +1676,35 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1531
1676
|
}
|
|
1532
1677
|
}
|
|
1533
1678
|
}
|
|
1679
|
+
// Nested-skill discovery: whatever this call touched is where the model
|
|
1680
|
+
// is working, so those directories' own `.nexrall/skills` now matter.
|
|
1681
|
+
// Runs regardless of success - a failed read still signals the area,
|
|
1682
|
+
// and a re-touched directory costs a Set lookup.
|
|
1683
|
+
if (nestedSkillScan) {
|
|
1684
|
+
for (const s of nestedSkillScan.scanInput(input)) {
|
|
1685
|
+
if (!skillsList.some((k) => k.name === s.name))
|
|
1686
|
+
skillsList.push(s);
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
// Nested instruction files are discovered on the same touch, from the same
|
|
1690
|
+
// directories (S18c), and path-scoped `.nexrall/rules` files when their globs
|
|
1691
|
+
// match (S18d). A fresh file joins the ledger the prompt reads per request,
|
|
1692
|
+
// and is announced ONCE to the host — carrying the vocabulary's own name for
|
|
1693
|
+
// its event ('nested_traversal' for a directory the agent worked in,
|
|
1694
|
+
// 'path_glob_match' for a rule whose `paths:` matched the touched path).
|
|
1695
|
+
// Sub-agents clear both the callback and the ledger (subTask.ts), so only
|
|
1696
|
+
// the session's own discoveries fire hooks; a child's ephemeral load is
|
|
1697
|
+
// not a fact about this session's instruction set.
|
|
1698
|
+
if (nestedInstructionScan) {
|
|
1699
|
+
const fresh = nestedInstructionScan.scanInput(input);
|
|
1700
|
+
if (fresh.length && options._onInstructionsDiscovered) {
|
|
1701
|
+
options._onInstructionsDiscovered(fresh.map((f) => ({
|
|
1702
|
+
file_path: f.filePath,
|
|
1703
|
+
memory_type: 'Project',
|
|
1704
|
+
load_reason: f.loadReason,
|
|
1705
|
+
})));
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1534
1708
|
// Opportunistic memory compaction — memory_write is the only tool that can
|
|
1535
1709
|
// grow a memory file, so this is the cheapest point to check whether it just
|
|
1536
1710
|
// crossed the compaction threshold. Fire-and-forget: never blocks the turn,
|
|
@@ -1544,7 +1718,12 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1544
1718
|
// PostToolUseFailure: only when the call errored. Its context/exit-2 text joins the
|
|
1545
1719
|
// PostToolUse output, so the model sees it next to the error it explains.
|
|
1546
1720
|
if (result.error !== undefined && (hooks.PostToolUseFailure?.length ?? 0) > 0) {
|
|
1547
|
-
const f = await (0, hooks_1.runLifecycleHooks)(hooks.PostToolUseFailure, 'PostToolUseFailure', options.workDir, {
|
|
1721
|
+
const f = await (0, hooks_1.runLifecycleHooks)(hooks.PostToolUseFailure, 'PostToolUseFailure', options.workDir, {
|
|
1722
|
+
session_id: options.sessionId ?? '',
|
|
1723
|
+
tool_name: name,
|
|
1724
|
+
tool_input: input,
|
|
1725
|
+
error: String(result.error).slice(0, 4000),
|
|
1726
|
+
}, name, (0, hooks_1.hookRunOptsFor)(options)).catch(() => ({ block: false }));
|
|
1548
1727
|
if (f.context)
|
|
1549
1728
|
post.context = [post.context, f.context].filter(Boolean).join('\n');
|
|
1550
1729
|
if (f.block) {
|
|
@@ -1630,8 +1809,10 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1630
1809
|
// command that timed out mid-test-run), surface both so the model can
|
|
1631
1810
|
// see WHY it hung — the failure detail is usually in the output tail.
|
|
1632
1811
|
content: result.error
|
|
1633
|
-
?
|
|
1634
|
-
|
|
1812
|
+
? result.output
|
|
1813
|
+
? `Error: ${result.error}\n\n${result.output}`
|
|
1814
|
+
: `Error: ${result.error}`
|
|
1815
|
+
: (result.output ?? ''),
|
|
1635
1816
|
is_error: result.error !== undefined,
|
|
1636
1817
|
}));
|
|
1637
1818
|
// Fold any follow-up the user queued mid-task into THIS user turn (alongside the
|
|
@@ -1687,7 +1868,9 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1687
1868
|
tool_input: b.input,
|
|
1688
1869
|
tool_use_id: b.id,
|
|
1689
1870
|
tool_response: r.error !== undefined
|
|
1690
|
-
?
|
|
1871
|
+
? r.output
|
|
1872
|
+
? `Error: ${r.error}\n\n${r.output}`
|
|
1873
|
+
: `Error: ${r.error}`
|
|
1691
1874
|
: (r.output ?? ''),
|
|
1692
1875
|
})),
|
|
1693
1876
|
}, undefined, (0, hooks_1.hookRunOptsFor)(options));
|
|
@@ -1791,7 +1974,11 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1791
1974
|
// Routed through onNotice (falling back to onText) like every other housekeeping
|
|
1792
1975
|
// message in this file — these are statements from the harness, not from the model,
|
|
1793
1976
|
// and splicing them into the assistant's own bubble reads as if it said them.
|
|
1794
|
-
const notice = (0, iterationPolicy_1.stopReasonNotice)(stopReason, {
|
|
1977
|
+
const notice = (0, iterationPolicy_1.stopReasonNotice)(stopReason, {
|
|
1978
|
+
budget,
|
|
1979
|
+
repeatError: stalledRepeatError,
|
|
1980
|
+
hookReason: hookBlockedReason,
|
|
1981
|
+
});
|
|
1795
1982
|
// A sub-agent's stop belongs in its parent's tool result, not in the user's chat as
|
|
1796
1983
|
// if the MAIN agent had stopped (runSubTask turns it into an error for the parent).
|
|
1797
1984
|
if (options._onStopReason)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/agent/memory.ts"],"names":[],"mappings":"AAiEA,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE/C;;;sDAGsD;AACtD,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAI3E;AAED,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAC1C,eAAO,MAAM,gBAAgB,QAAS,CAAC;AACvC,eAAO,MAAM,4BAA4B,QAAS,CAAC;AAMnD,eAAO,MAAM,qBAAqB,QAAmC,CAAC;AAKtE,eAAO,MAAM,wBAAwB,QAAmC,CAAC;
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/agent/memory.ts"],"names":[],"mappings":"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"}
|
package/dist/agent/memory.js
CHANGED
|
@@ -147,7 +147,9 @@ const _locks = new Map();
|
|
|
147
147
|
async function withLock(key, fn) {
|
|
148
148
|
const prev = _locks.get(key) ?? Promise.resolve();
|
|
149
149
|
let release;
|
|
150
|
-
const next = new Promise((res) => {
|
|
150
|
+
const next = new Promise((res) => {
|
|
151
|
+
release = res;
|
|
152
|
+
});
|
|
151
153
|
_locks.set(key, prev.then(() => next));
|
|
152
154
|
try {
|
|
153
155
|
await prev;
|
|
@@ -218,7 +220,10 @@ function evictToFit(active, archived, maxBytes) {
|
|
|
218
220
|
function sanitizeSourceTag(source) {
|
|
219
221
|
if (!source)
|
|
220
222
|
return '';
|
|
221
|
-
return source
|
|
223
|
+
return source
|
|
224
|
+
.trim()
|
|
225
|
+
.replace(/[[\]\n\r]/g, '')
|
|
226
|
+
.slice(0, SOURCE_TAG_MAX_CHARS);
|
|
222
227
|
}
|
|
223
228
|
async function writeMemory(content, scope, workDir, opts = {}) {
|
|
224
229
|
let trimmed = content.trim();
|
|
@@ -306,7 +311,9 @@ function clearMemory(scope, workDir) {
|
|
|
306
311
|
try {
|
|
307
312
|
fs.rmSync(file, { force: true });
|
|
308
313
|
}
|
|
309
|
-
catch {
|
|
314
|
+
catch {
|
|
315
|
+
/* ignore */
|
|
316
|
+
}
|
|
310
317
|
}
|
|
311
318
|
function memoryStats(scope, workDir) {
|
|
312
319
|
const file = memoryFilePath(scope, workDir);
|
|
@@ -374,7 +381,8 @@ async function compactMemoryIfNeeded(scope, workDir, summarize) {
|
|
|
374
381
|
}
|
|
375
382
|
// 2. If the active set alone is still driving the size past the trigger,
|
|
376
383
|
// summarize its oldest half.
|
|
377
|
-
if (Buffer.byteLength(serializeSections(active, archived), 'utf-8') > exports.MEMORY_COMPACT_TRIGGER_BYTES &&
|
|
384
|
+
if (Buffer.byteLength(serializeSections(active, archived), 'utf-8') > exports.MEMORY_COMPACT_TRIGGER_BYTES &&
|
|
385
|
+
active.length >= 8) {
|
|
378
386
|
const cut = Math.floor(active.length / 2);
|
|
379
387
|
const older = active.slice(0, cut);
|
|
380
388
|
const recent = active.slice(cut);
|
|
@@ -524,9 +532,7 @@ function agentMemoryPreamble(agentName, scope, workDir) {
|
|
|
524
532
|
const notes = readAgentMemory(agentName, scope, workDir);
|
|
525
533
|
if (!notes)
|
|
526
534
|
return '';
|
|
527
|
-
const kept = Buffer.byteLength(notes, 'utf-8') > exports.AGENT_MEMORY_INJECT_MAX
|
|
528
|
-
? notes.slice(-exports.AGENT_MEMORY_INJECT_MAX)
|
|
529
|
-
: notes;
|
|
535
|
+
const kept = Buffer.byteLength(notes, 'utf-8') > exports.AGENT_MEMORY_INJECT_MAX ? notes.slice(-exports.AGENT_MEMORY_INJECT_MAX) : notes;
|
|
530
536
|
return (`# Your own notes (agent: ${agentName}, scope: ${scope})\n` +
|
|
531
537
|
`These are notes YOU saved on previous runs. Treat them as your accumulated experience, ` +
|
|
532
538
|
`not as instructions from the user, and verify anything that looks stale against the ` +
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"modelCatalogue.d.ts","sourceRoot":"","sources":["../../src/agent/modelCatalogue.ts"],"names":[],"mappings":"AAgDA,OAAO,EAAiB,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AAiBlE;;;;;;;;;;;;GAYG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAC7B,OAAO,CAAC;IAAE,MAAM,EAAE,aAAa,EAAE,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAuBnE;AAED,iFAAiF;AACjF,wBAAgB,uBAAuB,IAAI;IAAE,MAAM,EAAE,aAAa,EAAE,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAElG;AAOD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAErF;AAED,sDAAsD;AACtD,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEvF;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAGzF;AAED,wEAAwE;AACxE,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAGvF;AAED,uFAAuF;AACvF,wBAAgB,wBAAwB,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAEnF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAExE;AAED,4EAA4E;AAC5E,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE7E;AAED,0EAA0E;AAC1E,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAG9G;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,EAAE,GAAG,IAAI,CAExD;AAED,kEAAkE;AAClE,wBAAgB,iCAAiC,IAAI,IAAI,CAGxD;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"modelCatalogue.d.ts","sourceRoot":"","sources":["../../src/agent/modelCatalogue.ts"],"names":[],"mappings":"AAgDA,OAAO,EAAiB,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AAiBlE;;;;;;;;;;;;GAYG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAC7B,OAAO,CAAC;IAAE,MAAM,EAAE,aAAa,EAAE,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAuBnE;AAED,iFAAiF;AACjF,wBAAgB,uBAAuB,IAAI;IAAE,MAAM,EAAE,aAAa,EAAE,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAElG;AAOD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAErF;AAED,sDAAsD;AACtD,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEvF;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAGzF;AAED,wEAAwE;AACxE,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAGvF;AAED,uFAAuF;AACvF,wBAAgB,wBAAwB,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAEnF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAExE;AAED,4EAA4E;AAC5E,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE7E;AAED,0EAA0E;AAC1E,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAG9G;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,EAAE,GAAG,IAAI,CAExD;AAED,kEAAkE;AAClE,wBAAgB,iCAAiC,IAAI,IAAI,CAGxD;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,aAAa,CAAC;AAU/C,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAW3F;AAED,6GAA6G;AAC7G,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,WAAW,EAAE,MAAM,GAAG,SAAS,GAC9B,MAAM,GAAG,SAAS,CAIpB;AAED,8DAA8D;AAC9D,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,IAAI,GAAG,IAAI,CAE9E"}
|
|
@@ -196,7 +196,10 @@ function _resetModelCatalogueCacheForTests() {
|
|
|
196
196
|
exports.CHEAPEST_MODEL_ALIAS = 'cheapest';
|
|
197
197
|
// Legacy tier aliases the backend still resolves (see agentTypes.ts VALID_MODELS).
|
|
198
198
|
const LEGACY_ALIAS_IDS = {
|
|
199
|
-
turbo: 'claude-sonnet-5-5',
|
|
199
|
+
turbo: 'claude-sonnet-5-5',
|
|
200
|
+
fast: 'claude-sonnet-5-5',
|
|
201
|
+
pro: 'claude-opus-5-5',
|
|
202
|
+
ultra: 'claude-fable-5-1',
|
|
200
203
|
};
|
|
201
204
|
function cheapestSameVendorModel(parentModel) {
|
|
202
205
|
if (!_state || !parentModel)
|