@nexrall/code-core 1.4.71 → 1.4.72
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 +223 -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.map +1 -1
- package/dist/api/client.js +63 -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 +34 -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 +24 -0
- package/dist/mcp/manager.d.ts.map +1 -1
- package/dist/mcp/manager.js +117 -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,15 @@ 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)));
|
|
495
584
|
// The request options every main-loop call sends. Shared with the compaction
|
|
496
585
|
// summariser so its request has the SAME system prompt and tools as the conversation
|
|
497
586
|
// — that is what lets the summary call read the whole history from cache (0.10×)
|
|
@@ -502,7 +591,13 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
502
591
|
effort: options.effort,
|
|
503
592
|
env: options.env,
|
|
504
593
|
editorContext: options.editorContext,
|
|
505
|
-
nexrallMd:
|
|
594
|
+
nexrallMd: [
|
|
595
|
+
planAndWorktreeAwareNexrallMd,
|
|
596
|
+
(0, nestedInstructions_1.formatNestedInstructions)(nestedInstructionScan?.list() ?? []),
|
|
597
|
+
mcpInstructionsBlock(),
|
|
598
|
+
]
|
|
599
|
+
.filter((s) => !!s)
|
|
600
|
+
.join('\n\n---\n\n') || undefined,
|
|
506
601
|
clientType: options.clientType,
|
|
507
602
|
abortSignal: options.abortSignal,
|
|
508
603
|
// MCP tools, plus the per-agent memory tool when (and only when) this run
|
|
@@ -511,8 +606,7 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
511
606
|
// to every other run: a tool the model cannot see is one it cannot try,
|
|
512
607
|
// which is better than advertising it everywhere and refusing it at the gate.
|
|
513
608
|
extraTools: [
|
|
514
|
-
...(options.mcpManager?.getAnthropicTools() ?? [])
|
|
515
|
-
.filter((t) => !options._mcpServerAllowlist || options._mcpServerAllowlist.has(String(t.name).split('__')[0])),
|
|
609
|
+
...(options.mcpManager?.getAnthropicTools() ?? []).filter((t) => !options._mcpServerAllowlist || options._mcpServerAllowlist.has(String(t.name).split('__')[0])),
|
|
516
610
|
...(options._agentMemory ? [iterationPolicy_1.AGENT_MEMORY_TOOL_SCHEMA] : []),
|
|
517
611
|
...(options.extraToolSchemas ?? []),
|
|
518
612
|
],
|
|
@@ -522,11 +616,7 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
522
616
|
// memory_write, which no sub-agent allowlist contains.
|
|
523
617
|
// Sub-agents NEVER write the shared store (the main agent decides what to persist) —
|
|
524
618
|
// 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
|
|
619
|
+
canWriteSharedMemory: depth > 0 ? false : options._allowedTools ? options._allowedTools.has('memory_write') : true, // no allowlist = main agent = may write
|
|
530
620
|
hasOwnAgentStore: !!options._agentMemory,
|
|
531
621
|
// Same derivation, same reason, for the network. The base prompt's
|
|
532
622
|
// "Web search" section tells the run to reach the network, but
|
|
@@ -547,7 +637,7 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
547
637
|
// delegate — see canSpawnSubAgents.
|
|
548
638
|
canSpawnSubAgents: maySpawn,
|
|
549
639
|
agents: agentsCatalogue || undefined,
|
|
550
|
-
skills:
|
|
640
|
+
skills: (0, skills_1.summariseSkills)(skillsList) || undefined,
|
|
551
641
|
});
|
|
552
642
|
// Optional OS-level bash sandbox (opt-in via settings.json "sandbox").
|
|
553
643
|
const sandboxCfg = (0, sandbox_1.parseSandboxConfig)(settings.raw.sandbox) ?? undefined;
|
|
@@ -563,7 +653,7 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
563
653
|
// Per-run: a sub-agent's own runAgentLoop gets its own, so it never sees its parent's reads.
|
|
564
654
|
const readDedupe = new readDedupe_1.ReadDedupe(options.workDir);
|
|
565
655
|
// Only the main agent owns background agents (runSubTask never passes the hub down).
|
|
566
|
-
const drainBackgroundNotifications = () => depth === 0 ? (options.backgroundAgents?.takeNotifications() ?? []) : [];
|
|
656
|
+
const drainBackgroundNotifications = () => (depth === 0 ? (options.backgroundAgents?.takeNotifications() ?? []) : []);
|
|
567
657
|
// Live prompt-size estimate, updated from usage events after every stream.
|
|
568
658
|
let lastPromptTokens = 0;
|
|
569
659
|
let compacting = false; // re-entrancy guard — compaction itself calls streamChat
|
|
@@ -734,7 +824,11 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
734
824
|
compactDisabled = false;
|
|
735
825
|
compactFailures = 0;
|
|
736
826
|
}
|
|
737
|
-
if (autoCompact &&
|
|
827
|
+
if (autoCompact &&
|
|
828
|
+
!compactDisabled &&
|
|
829
|
+
!compacting &&
|
|
830
|
+
(tokenPressure || bytePressure) &&
|
|
831
|
+
messages.length > compaction_1.COMPACT_KEEP_MIN + 2) {
|
|
738
832
|
compacting = true;
|
|
739
833
|
try {
|
|
740
834
|
// Measured BEFORE, so "did it actually help?" is a fact about bytes rather
|
|
@@ -759,9 +853,7 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
759
853
|
const reclaimed = bytesBefore - bytesAfter;
|
|
760
854
|
if (did) {
|
|
761
855
|
lastPromptTokens = 0; // stale — next usage event refreshes it
|
|
762
|
-
const reason = bytePressure
|
|
763
|
-
? `body ~${(bodyBytes / (1024 * 1024)).toFixed(1)}MB`
|
|
764
|
-
: 'context window';
|
|
856
|
+
const reason = bytePressure ? `body ~${(bodyBytes / (1024 * 1024)).toFixed(1)}MB` : 'context window';
|
|
765
857
|
// Silent, same as the prune above — routine housekeeping. Only the
|
|
766
858
|
// actionable "auto-compaction paused" warning below is surfaced.
|
|
767
859
|
void reason;
|
|
@@ -1091,8 +1183,8 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1091
1183
|
// be an example here too — as of the 2026-08-11+ provider-agnostic port it is a
|
|
1092
1184
|
// plain client tool executed by THIS process, so it no longer belongs in this set.)
|
|
1093
1185
|
const serverSideResultIds = new Set(assistantMessage.content
|
|
1094
|
-
.filter(b => b.type === 'tool_result')
|
|
1095
|
-
.map(b => b.tool_use_id)
|
|
1186
|
+
.filter((b) => b.type === 'tool_result')
|
|
1187
|
+
.map((b) => b.tool_use_id)
|
|
1096
1188
|
.filter((id) => !!id));
|
|
1097
1189
|
const toolUseBlocks = assistantMessage.content.filter((block) => block.type === 'tool_use' && !serverSideResultIds.has(block.id));
|
|
1098
1190
|
// ─── TRUNCATED-TOOL-CALL GUARD ──────────────────────────────────────────
|
|
@@ -1117,9 +1209,7 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1117
1209
|
// model retries with a smaller call, rather than either failing
|
|
1118
1210
|
// mysteriously or silently mutating a file incompletely.
|
|
1119
1211
|
const lastBlock = assistantMessage.content[assistantMessage.content.length - 1];
|
|
1120
|
-
const truncatedToolUseId = assistantMessage.stopReason === 'max_tokens' && lastBlock?.type === 'tool_use'
|
|
1121
|
-
? lastBlock.id
|
|
1122
|
-
: undefined;
|
|
1212
|
+
const truncatedToolUseId = assistantMessage.stopReason === 'max_tokens' && lastBlock?.type === 'tool_use' ? lastBlock.id : undefined;
|
|
1123
1213
|
// 4. If no tool use → agent produced a final text response.
|
|
1124
1214
|
// But if the user queued a follow-up while we were working, fold it in as the
|
|
1125
1215
|
// next user turn and keep going instead of ending (Claude-Code style).
|
|
@@ -1170,7 +1260,8 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1170
1260
|
const bullet = fresh.map((t) => ` • ${t.path}: ${t.reason}`).join('\n');
|
|
1171
1261
|
messages.push({
|
|
1172
1262
|
role: 'user',
|
|
1173
|
-
content: [
|
|
1263
|
+
content: [
|
|
1264
|
+
{
|
|
1174
1265
|
type: 'text',
|
|
1175
1266
|
text: 'STOP — test-integrity check. Before finishing, I detected that you WEAKENED test(s) this session:\n' +
|
|
1176
1267
|
bullet +
|
|
@@ -1178,7 +1269,8 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1178
1269
|
' 1. Revert the weakening and fix the actual code so the ORIGINAL test passes, or\n' +
|
|
1179
1270
|
' 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
1271
|
'Then re-run the test suite to prove it passes for real.',
|
|
1181
|
-
}
|
|
1272
|
+
},
|
|
1273
|
+
],
|
|
1182
1274
|
});
|
|
1183
1275
|
continue;
|
|
1184
1276
|
}
|
|
@@ -1191,15 +1283,19 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1191
1283
|
if (flaky.length) {
|
|
1192
1284
|
for (const f of flaky)
|
|
1193
1285
|
flakyNudgedCmds.add(f.cmd);
|
|
1194
|
-
const bullet = flaky
|
|
1286
|
+
const bullet = flaky
|
|
1287
|
+
.map((f) => ` • ${f.cmd} (${f.passes} pass / ${f.fails} fail on identical code)`)
|
|
1288
|
+
.join('\n');
|
|
1195
1289
|
messages.push({
|
|
1196
1290
|
role: 'user',
|
|
1197
|
-
content: [
|
|
1291
|
+
content: [
|
|
1292
|
+
{
|
|
1198
1293
|
type: 'text',
|
|
1199
1294
|
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
1295
|
bullet +
|
|
1201
1296
|
'\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
|
-
}
|
|
1297
|
+
},
|
|
1298
|
+
],
|
|
1203
1299
|
});
|
|
1204
1300
|
continue;
|
|
1205
1301
|
}
|
|
@@ -1213,10 +1309,12 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1213
1309
|
verificationNudgeSent = true;
|
|
1214
1310
|
messages.push({
|
|
1215
1311
|
role: 'user',
|
|
1216
|
-
content: [
|
|
1312
|
+
content: [
|
|
1313
|
+
{
|
|
1217
1314
|
type: 'text',
|
|
1218
|
-
text:
|
|
1219
|
-
}
|
|
1315
|
+
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.",
|
|
1316
|
+
},
|
|
1317
|
+
],
|
|
1220
1318
|
});
|
|
1221
1319
|
continue;
|
|
1222
1320
|
}
|
|
@@ -1358,6 +1456,11 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1358
1456
|
if (err instanceof subTaskSupport_1.ToolNotAllowedError)
|
|
1359
1457
|
deniedReason = err.message;
|
|
1360
1458
|
}
|
|
1459
|
+
// `context: fork` skills never reach the executor's inline injection: the
|
|
1460
|
+
// call is diverted into the same sub-agent machinery `task` uses, with the
|
|
1461
|
+
// expanded body as the child's prompt (see runForkedSkill). Resolved here —
|
|
1462
|
+
// only for an allowed `use_skill` — so a denied call pays nothing for it.
|
|
1463
|
+
const forkSkill = permitted && name === 'use_skill' ? resolveForkSkill(skillsList, input, options) : undefined;
|
|
1361
1464
|
if (!permitted) {
|
|
1362
1465
|
result = { error: deniedReason ?? 'Permission denied by user' };
|
|
1363
1466
|
}
|
|
@@ -1373,8 +1476,9 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1373
1476
|
// reimplementing depth/budget/concurrency enforcement a second time.
|
|
1374
1477
|
// Background only for the MAIN agent, and only when the host can deliver the
|
|
1375
1478
|
// result later; otherwise it quietly runs in the foreground.
|
|
1376
|
-
const wantsBackground = input.run_in_background === true
|
|
1377
|
-
|
|
1479
|
+
const wantsBackground = input.run_in_background === true ||
|
|
1480
|
+
(input.run_in_background !== false &&
|
|
1481
|
+
!!(0, agentTypes_1.findAgentType)(agentTypes, String(input.subagent_type ?? ''))?.background);
|
|
1378
1482
|
const taskOptions = { ...auditOptions, _taskToolUseId: id };
|
|
1379
1483
|
// PreToolUse/PostToolUse cover spawns too (Claude Code's matcher "Task"/"Agent"
|
|
1380
1484
|
// works the same way): a hook can veto a delegation or audit what came back.
|
|
@@ -1383,9 +1487,10 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1383
1487
|
result = { error: `Blocked by PreToolUse hook: ${preTask.reason}` };
|
|
1384
1488
|
}
|
|
1385
1489
|
else {
|
|
1386
|
-
result =
|
|
1387
|
-
|
|
1388
|
-
|
|
1490
|
+
result =
|
|
1491
|
+
wantsBackground && depth === 0 && options.backgroundAgents && !input.resume_agent_id
|
|
1492
|
+
? dispatchBackgroundSubAgent(input, taskOptions, agentTypes, options.backgroundAgents)
|
|
1493
|
+
: await dispatchSubAgent(input, taskOptions, agentTypes);
|
|
1389
1494
|
const postTask = await (0, hooks_1.runToolHooks)(hooks.PostToolUse, 'PostToolUse', name, input, options.workDir, result, (0, hooks_1.hookRunOptsFor)(options));
|
|
1390
1495
|
const injectedTask = [preTask.context, postTask.context].filter(Boolean).join('\n');
|
|
1391
1496
|
if (injectedTask) {
|
|
@@ -1399,6 +1504,28 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1399
1504
|
}
|
|
1400
1505
|
}
|
|
1401
1506
|
}
|
|
1507
|
+
else if (forkSkill) {
|
|
1508
|
+
// `use_skill` on a `context: fork` skill — the same hook pair as any call,
|
|
1509
|
+
// so a Pre can veto the fork and a Post sees its start/report.
|
|
1510
|
+
const preFork = await (0, hooks_1.runToolHooks)(hooks.PreToolUse, 'PreToolUse', name, input, options.workDir, undefined, (0, hooks_1.hookRunOptsFor)(options));
|
|
1511
|
+
if (preFork.block) {
|
|
1512
|
+
result = { error: `Blocked by PreToolUse hook: ${preFork.reason}` };
|
|
1513
|
+
}
|
|
1514
|
+
else {
|
|
1515
|
+
result = await runForkedSkill(forkSkill, typeof input.args === 'string' ? input.args : '', { ...auditOptions, _taskToolUseId: id }, agentTypes);
|
|
1516
|
+
const postFork = await (0, hooks_1.runToolHooks)(hooks.PostToolUse, 'PostToolUse', name, input, options.workDir, result, (0, hooks_1.hookRunOptsFor)(options));
|
|
1517
|
+
const injectedFork = [preFork.context, postFork.context].filter(Boolean).join('\n');
|
|
1518
|
+
if (injectedFork) {
|
|
1519
|
+
if (result.error !== undefined)
|
|
1520
|
+
result.error = `${result.error}\n\n[hook] ${injectedFork}`;
|
|
1521
|
+
else
|
|
1522
|
+
result.output = `${result.output ?? ''}\n\n[hook] ${injectedFork}`;
|
|
1523
|
+
}
|
|
1524
|
+
if (postFork.block) {
|
|
1525
|
+
result.error = `${result.error ? result.error + '\n' : ''}PostToolUse hook flagged: ${postFork.reason}`;
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1402
1529
|
else {
|
|
1403
1530
|
const pre = await (0, hooks_1.runToolHooks)(hooks.PreToolUse, 'PreToolUse', name, input, options.workDir, undefined, (0, hooks_1.hookRunOptsFor)(options));
|
|
1404
1531
|
if (pre.block) {
|
|
@@ -1407,9 +1534,7 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1407
1534
|
else {
|
|
1408
1535
|
// TaskCreated needs the id of the task the tool is ABOUT to create, which only
|
|
1409
1536
|
// 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;
|
|
1537
|
+
const taskIdsBefore = name === 'create_shared_task' ? new Set((0, sharedTasks_1.listSharedTasks)(options.workDir).map((t) => t.id)) : null;
|
|
1413
1538
|
try {
|
|
1414
1539
|
// TaskCompleted (Claude Code): the hook is the GATE, so it runs BEFORE the
|
|
1415
1540
|
// update — refusing means the task simply stays open, and the reason goes
|
|
@@ -1445,9 +1570,10 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1445
1570
|
}
|
|
1446
1571
|
else if (options.mcpManager?.isMcpTool(name)) {
|
|
1447
1572
|
const mcpOutput = await (0, subTaskSupport_1.stopAwaiting)(options.mcpManager.callTool(name, input, options.abortSignal), options.abortSignal);
|
|
1448
|
-
result =
|
|
1449
|
-
|
|
1450
|
-
|
|
1573
|
+
result =
|
|
1574
|
+
mcpOutput === subTaskSupport_1.STOP_TIMEOUT_RESULT
|
|
1575
|
+
? subTaskSupport_1.STOP_TIMEOUT_RESULT
|
|
1576
|
+
: { output: (0, executor_1.capExternalOutput)(mcpOutput ?? '', `mcp-${name}`) };
|
|
1451
1577
|
// 3. Fall through to built-in executor
|
|
1452
1578
|
}
|
|
1453
1579
|
else {
|
|
@@ -1472,7 +1598,9 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1472
1598
|
// This matters far more with sub-agents than without: up to 4 run
|
|
1473
1599
|
// concurrently, each issuing its own tool calls into this same process.
|
|
1474
1600
|
const locks = name === 'bash'
|
|
1475
|
-
? (
|
|
1601
|
+
? (0, bashClassify_1.bashNeedsRepoLock)(String(input.command ?? ''))
|
|
1602
|
+
? [fileLocks_1.REPO_STATE_LOCK]
|
|
1603
|
+
: []
|
|
1476
1604
|
: (0, fileLocks_1.lockPathsFor)(name, input, options.workDir);
|
|
1477
1605
|
// Two lock layers, nested outer-then-inner:
|
|
1478
1606
|
// 1. Cross-process (OS lock file) — the ONLY layer that protects
|
|
@@ -1523,7 +1651,9 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1523
1651
|
const task = created[created.length - 1];
|
|
1524
1652
|
if (task) {
|
|
1525
1653
|
const veto = await taskHookVeto(hooks.TaskCreated, 'TaskCreated', input, options, {
|
|
1526
|
-
task_id: task.id,
|
|
1654
|
+
task_id: task.id,
|
|
1655
|
+
task_subject: task.content,
|
|
1656
|
+
task_description: task.content,
|
|
1527
1657
|
});
|
|
1528
1658
|
if (veto) {
|
|
1529
1659
|
(0, sharedTasks_1.deleteSharedTask)(options.workDir, task.id);
|
|
@@ -1531,6 +1661,35 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1531
1661
|
}
|
|
1532
1662
|
}
|
|
1533
1663
|
}
|
|
1664
|
+
// Nested-skill discovery: whatever this call touched is where the model
|
|
1665
|
+
// is working, so those directories' own `.nexrall/skills` now matter.
|
|
1666
|
+
// Runs regardless of success - a failed read still signals the area,
|
|
1667
|
+
// and a re-touched directory costs a Set lookup.
|
|
1668
|
+
if (nestedSkillScan) {
|
|
1669
|
+
for (const s of nestedSkillScan.scanInput(input)) {
|
|
1670
|
+
if (!skillsList.some((k) => k.name === s.name))
|
|
1671
|
+
skillsList.push(s);
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
// Nested instruction files are discovered on the same touch, from the same
|
|
1675
|
+
// directories (S18c), and path-scoped `.nexrall/rules` files when their globs
|
|
1676
|
+
// match (S18d). A fresh file joins the ledger the prompt reads per request,
|
|
1677
|
+
// and is announced ONCE to the host — carrying the vocabulary's own name for
|
|
1678
|
+
// its event ('nested_traversal' for a directory the agent worked in,
|
|
1679
|
+
// 'path_glob_match' for a rule whose `paths:` matched the touched path).
|
|
1680
|
+
// Sub-agents clear both the callback and the ledger (subTask.ts), so only
|
|
1681
|
+
// the session's own discoveries fire hooks; a child's ephemeral load is
|
|
1682
|
+
// not a fact about this session's instruction set.
|
|
1683
|
+
if (nestedInstructionScan) {
|
|
1684
|
+
const fresh = nestedInstructionScan.scanInput(input);
|
|
1685
|
+
if (fresh.length && options._onInstructionsDiscovered) {
|
|
1686
|
+
options._onInstructionsDiscovered(fresh.map((f) => ({
|
|
1687
|
+
file_path: f.filePath,
|
|
1688
|
+
memory_type: 'Project',
|
|
1689
|
+
load_reason: f.loadReason,
|
|
1690
|
+
})));
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1534
1693
|
// Opportunistic memory compaction — memory_write is the only tool that can
|
|
1535
1694
|
// grow a memory file, so this is the cheapest point to check whether it just
|
|
1536
1695
|
// crossed the compaction threshold. Fire-and-forget: never blocks the turn,
|
|
@@ -1544,7 +1703,12 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1544
1703
|
// PostToolUseFailure: only when the call errored. Its context/exit-2 text joins the
|
|
1545
1704
|
// PostToolUse output, so the model sees it next to the error it explains.
|
|
1546
1705
|
if (result.error !== undefined && (hooks.PostToolUseFailure?.length ?? 0) > 0) {
|
|
1547
|
-
const f = await (0, hooks_1.runLifecycleHooks)(hooks.PostToolUseFailure, 'PostToolUseFailure', options.workDir, {
|
|
1706
|
+
const f = await (0, hooks_1.runLifecycleHooks)(hooks.PostToolUseFailure, 'PostToolUseFailure', options.workDir, {
|
|
1707
|
+
session_id: options.sessionId ?? '',
|
|
1708
|
+
tool_name: name,
|
|
1709
|
+
tool_input: input,
|
|
1710
|
+
error: String(result.error).slice(0, 4000),
|
|
1711
|
+
}, name, (0, hooks_1.hookRunOptsFor)(options)).catch(() => ({ block: false }));
|
|
1548
1712
|
if (f.context)
|
|
1549
1713
|
post.context = [post.context, f.context].filter(Boolean).join('\n');
|
|
1550
1714
|
if (f.block) {
|
|
@@ -1630,8 +1794,10 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1630
1794
|
// command that timed out mid-test-run), surface both so the model can
|
|
1631
1795
|
// see WHY it hung — the failure detail is usually in the output tail.
|
|
1632
1796
|
content: result.error
|
|
1633
|
-
?
|
|
1634
|
-
|
|
1797
|
+
? result.output
|
|
1798
|
+
? `Error: ${result.error}\n\n${result.output}`
|
|
1799
|
+
: `Error: ${result.error}`
|
|
1800
|
+
: (result.output ?? ''),
|
|
1635
1801
|
is_error: result.error !== undefined,
|
|
1636
1802
|
}));
|
|
1637
1803
|
// Fold any follow-up the user queued mid-task into THIS user turn (alongside the
|
|
@@ -1687,7 +1853,9 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1687
1853
|
tool_input: b.input,
|
|
1688
1854
|
tool_use_id: b.id,
|
|
1689
1855
|
tool_response: r.error !== undefined
|
|
1690
|
-
?
|
|
1856
|
+
? r.output
|
|
1857
|
+
? `Error: ${r.error}\n\n${r.output}`
|
|
1858
|
+
: `Error: ${r.error}`
|
|
1691
1859
|
: (r.output ?? ''),
|
|
1692
1860
|
})),
|
|
1693
1861
|
}, undefined, (0, hooks_1.hookRunOptsFor)(options));
|
|
@@ -1791,7 +1959,11 @@ async function runAgentLoop(initialMessages, options) {
|
|
|
1791
1959
|
// Routed through onNotice (falling back to onText) like every other housekeeping
|
|
1792
1960
|
// message in this file — these are statements from the harness, not from the model,
|
|
1793
1961
|
// and splicing them into the assistant's own bubble reads as if it said them.
|
|
1794
|
-
const notice = (0, iterationPolicy_1.stopReasonNotice)(stopReason, {
|
|
1962
|
+
const notice = (0, iterationPolicy_1.stopReasonNotice)(stopReason, {
|
|
1963
|
+
budget,
|
|
1964
|
+
repeatError: stalledRepeatError,
|
|
1965
|
+
hookReason: hookBlockedReason,
|
|
1966
|
+
});
|
|
1795
1967
|
// A sub-agent's stop belongs in its parent's tool result, not in the user's chat as
|
|
1796
1968
|
// if the MAIN agent had stopped (runSubTask turns it into an error for the parent).
|
|
1797
1969
|
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)
|