@prestyj/cli 5.30.2 → 5.30.3
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/app-sidecar/app-settings.d.ts +6 -2
- package/dist/app-sidecar/app-settings.js +39 -20
- package/dist/app-sidecar/app-settings.js.map +1 -1
- package/dist/app-sidecar/git-target.d.ts +17 -0
- package/dist/app-sidecar/git-target.js +31 -0
- package/dist/app-sidecar/git-target.js.map +1 -0
- package/dist/app-sidecar/goal-routes.d.ts +26 -0
- package/dist/app-sidecar/goal-routes.js +445 -0
- package/dist/app-sidecar/goal-routes.js.map +1 -0
- package/dist/app-sidecar/history-routes.d.ts +13 -0
- package/dist/app-sidecar/history-routes.js +36 -25
- package/dist/app-sidecar/history-routes.js.map +1 -1
- package/dist/app-sidecar/mcp-routes.js +1 -2
- package/dist/app-sidecar/mcp-routes.js.map +1 -1
- package/dist/app-sidecar/route-context.d.ts +12 -0
- package/dist/app-sidecar/run-control-routes.js +2 -1
- package/dist/app-sidecar/run-control-routes.js.map +1 -1
- package/dist/app-sidecar/session-routes.js +41 -0
- package/dist/app-sidecar/session-routes.js.map +1 -1
- package/dist/app-sidecar/session-types.d.ts +2 -0
- package/dist/app-sidecar/workspace-routes.js +13 -95
- package/dist/app-sidecar/workspace-routes.js.map +1 -1
- package/dist/app-sidecar.js +213 -60
- package/dist/app-sidecar.js.map +1 -1
- package/dist/cli/shared.js +1 -1
- package/dist/cli/shared.js.map +1 -1
- package/dist/core/acceptance-checks.js +17 -4
- package/dist/core/acceptance-checks.js.map +1 -1
- package/dist/core/agent-notifications.d.ts +1 -1
- package/dist/core/agent-session.d.ts +83 -5
- package/dist/core/agent-session.js +174 -59
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/ask-user.d.ts +11 -0
- package/dist/core/ask-user.js +6 -1
- package/dist/core/ask-user.js.map +1 -1
- package/dist/core/auth-providers.d.ts +2 -80
- package/dist/core/auth-providers.js +4 -183
- package/dist/core/auth-providers.js.map +1 -1
- package/dist/core/cache-diagnostics.d.ts +1 -1
- package/dist/core/cache-diagnostics.js +2 -1
- package/dist/core/cache-diagnostics.js.map +1 -1
- package/dist/core/compaction/tool-result-pruner.d.ts +10 -0
- package/dist/core/compaction/tool-result-pruner.js +12 -0
- package/dist/core/compaction/tool-result-pruner.js.map +1 -1
- package/dist/core/context-files.d.ts +8 -0
- package/dist/core/context-files.js +46 -0
- package/dist/core/context-files.js.map +1 -0
- package/dist/core/env-delta.d.ts +9 -1
- package/dist/core/env-delta.js +20 -0
- package/dist/core/env-delta.js.map +1 -1
- package/dist/core/goals-store.d.ts +208 -0
- package/dist/core/goals-store.js +521 -0
- package/dist/core/goals-store.js.map +1 -0
- package/dist/core/local-endpoint-store.js +15 -11
- package/dist/core/local-endpoint-store.js.map +1 -1
- package/dist/core/project-discovery.d.ts +7 -0
- package/dist/core/project-discovery.js +9 -3
- package/dist/core/project-discovery.js.map +1 -1
- package/dist/core/radio.d.ts +2 -3
- package/dist/core/radio.js.map +1 -1
- package/dist/core/session-manager.d.ts +7 -1
- package/dist/core/session-manager.js +17 -7
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-storage.d.ts +3 -0
- package/dist/core/session-storage.js +8 -2
- package/dist/core/session-storage.js.map +1 -1
- package/dist/core/steroids.d.ts +8 -0
- package/dist/core/steroids.js +19 -3
- package/dist/core/steroids.js.map +1 -1
- package/dist/core/subagent-manager.d.ts +51 -0
- package/dist/core/subagent-manager.js +103 -16
- package/dist/core/subagent-manager.js.map +1 -1
- package/dist/core/subagent-store.js +2 -1
- package/dist/core/subagent-store.js.map +1 -1
- package/dist/goals-agent/goal-team.d.ts +2 -0
- package/dist/goals-agent/goal-team.js +83 -0
- package/dist/goals-agent/goal-team.js.map +1 -0
- package/dist/goals-agent/goals-agent.d.ts +90 -0
- package/dist/goals-agent/goals-agent.js +246 -0
- package/dist/goals-agent/goals-agent.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/interactive.js +4 -3
- package/dist/interactive.js.map +1 -1
- package/dist/modes/subagent-worker-mode.d.ts +14 -0
- package/dist/modes/subagent-worker-mode.js +32 -1
- package/dist/modes/subagent-worker-mode.js.map +1 -1
- package/dist/system-prompt.d.ts +8 -0
- package/dist/system-prompt.js +21 -9
- package/dist/system-prompt.js.map +1 -1
- package/dist/tools/bash-read-evidence.d.ts +13 -0
- package/dist/tools/bash-read-evidence.js +48 -1
- package/dist/tools/bash-read-evidence.js.map +1 -1
- package/dist/tools/index.d.ts +7 -1
- package/dist/tools/index.js +10 -2
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/read-tracker.d.ts +6 -12
- package/dist/tools/read-tracker.js +21 -36
- package/dist/tools/read-tracker.js.map +1 -1
- package/dist/tools/read.js +2 -2
- package/dist/tools/read.js.map +1 -1
- package/dist/tools/subagent-control.js +22 -3
- package/dist/tools/subagent-control.js.map +1 -1
- package/dist/tools/write.js +110 -8
- package/dist/tools/write.js.map +1 -1
- package/dist/ui/components/ActivityIndicator.d.ts +5 -5
- package/dist/ui/components/InputArea.d.ts +2 -2
- package/dist/ui/components/InputArea.js +3 -3
- package/dist/ui/components/InputArea.js.map +1 -1
- package/dist/ui/components/ToolExecution.d.ts +2 -2
- package/dist/ui/hooks/useContextCompaction.js +38 -31
- package/dist/ui/hooks/useContextCompaction.js.map +1 -1
- package/dist/utils/format.d.ts +0 -4
- package/dist/utils/format.js +0 -7
- package/dist/utils/format.js.map +1 -1
- package/package.json +4 -4
|
@@ -27,7 +27,7 @@ import { partitionToolsByTier } from "../tools/tool-tiers.js";
|
|
|
27
27
|
import { formatImpactForVerification } from "./test-impact.js";
|
|
28
28
|
import { buildProcessCompletionFollowUp } from "./process-gate.js";
|
|
29
29
|
import { PLAN_SUBMISSION_NUDGE, shouldNudgePlanSubmission } from "./plan-submission-gate.js";
|
|
30
|
-
import { buildSubAgentCompletionFollowUp } from "./subagent-manager.js";
|
|
30
|
+
import { buildSubAgentCompletionFollowUp, } from "./subagent-manager.js";
|
|
31
31
|
import { applyAsyncSubagentPolicy } from "./subagent-policy.js";
|
|
32
32
|
import { resolveResponsesLite, resolveStrictTools, } from "./codex-request-shape.js";
|
|
33
33
|
import { subAgentDescription } from "../tools/subagent.js";
|
|
@@ -46,14 +46,14 @@ import { decidePlanStepCompaction, DEFAULT_CACHE_WRITE_READ_RATIO, PLAN_STEP_KEE
|
|
|
46
46
|
import { extractPlanSteps, findCompletedMarkers } from "../utils/plan-steps.js";
|
|
47
47
|
import { readFileSync } from "node:fs";
|
|
48
48
|
import { clampThinkingForPlanMode } from "./thinking-level.js";
|
|
49
|
-
import { pruneStaleToolResults } from "./compaction/tool-result-pruner.js";
|
|
49
|
+
import { pruneStaleToolResults, shouldPruneToolResults } from "./compaction/tool-result-pruner.js";
|
|
50
50
|
import { discoverAgents } from "./agents.js";
|
|
51
51
|
import { enhancePrompt } from "../utils/prompt-enhancer.js";
|
|
52
52
|
import { detectLanguages, detectProjectStack } from "./language-detector.js";
|
|
53
53
|
import { buildLoopBreakMessage } from "./loop-breaker.js";
|
|
54
54
|
import { buildRegroundingMessage, requestTextForRegrounding } from "./regrounding.js";
|
|
55
55
|
import { buildSemanticLoopJudgePrompt, buildSemanticLoopMessage, parseSemanticLoopVerdict, SEMANTIC_LOOP_JUDGE_TIMEOUT_MS, withJudgeTimeout, } from "./semantic-loop-check.js";
|
|
56
|
-
import { buildEnvDeltaMessage } from "./env-delta.js";
|
|
56
|
+
import { buildEnvDeltaMessage, buildLanguageUpdateMessage } from "./env-delta.js";
|
|
57
57
|
import { wrapSteeringText, buildNotificationSteeringText, STEERING_PREFIX } from "./steering.js";
|
|
58
58
|
import { AgentNotificationQueue } from "./agent-notifications.js";
|
|
59
59
|
import { VerificationGate, VERIFICATION_STATE_KIND } from "./verification-gate.js";
|
|
@@ -220,6 +220,9 @@ export class AgentSession {
|
|
|
220
220
|
* for no benefit.
|
|
221
221
|
*/
|
|
222
222
|
activeLanguages = new Set();
|
|
223
|
+
/** Languages the model has already been given guidance for, via the system
|
|
224
|
+
* prompt or an appended language update. */
|
|
225
|
+
announcedLanguages = new Set();
|
|
223
226
|
/** Shared with the tool layer so plan-mode restrictions read live state. */
|
|
224
227
|
planModeRef = { current: false };
|
|
225
228
|
/** Path of the approved plan currently being implemented, or undefined. When
|
|
@@ -309,7 +312,10 @@ export class AgentSession {
|
|
|
309
312
|
// Session manager. Agent-specific roots keep chat and coder histories isolated.
|
|
310
313
|
this.sessionManager = new SessionManager(this.opts.sessionRootDir ?? paths.sessionsDir);
|
|
311
314
|
const projectCustomization = this.opts.projectCustomization !== false;
|
|
312
|
-
|
|
315
|
+
// A sub-agent worker never creates them: the session that owns the
|
|
316
|
+
// project does. EZ Goals' workers work in the user's attached folders,
|
|
317
|
+
// which mustn't gain empty .ezcoder folders as a side effect.
|
|
318
|
+
if (projectCustomization && !this.opts.subagentWorker) {
|
|
313
319
|
// Ensure project-local .ezcoder directories exist.
|
|
314
320
|
const localEzDir = path.join(this.cwd, ".ezcoder");
|
|
315
321
|
await fs.mkdir(path.join(localEzDir, "skills"), { recursive: true });
|
|
@@ -330,16 +336,20 @@ export class AgentSession {
|
|
|
330
336
|
}
|
|
331
337
|
// Discover agents and create tools (with sub-agent support). Chat sessions
|
|
332
338
|
// can retain bundled workers without loading project/global customization.
|
|
333
|
-
const agents =
|
|
334
|
-
?
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
:
|
|
339
|
+
const agents = this.opts.subAgents
|
|
340
|
+
? [...this.opts.subAgents]
|
|
341
|
+
: projectCustomization
|
|
342
|
+
? await discoverAgents({
|
|
343
|
+
globalAgentsDir: paths.agentsDir,
|
|
344
|
+
projectDir: this.cwd,
|
|
345
|
+
})
|
|
346
|
+
: this.opts.globalSubagents
|
|
347
|
+
? await discoverAgents({ globalAgentsDir: paths.agentsDir })
|
|
348
|
+
: [];
|
|
341
349
|
const { tools: builtInTools, processManager, rebuildReadTool, clearReadTracker, recordBashReads, lspManager, testImpact, debugManager, subAgentManager, } = await createTools(this.cwd, {
|
|
342
350
|
agents,
|
|
351
|
+
shareWorkspaceRoots: this.opts.shareRootsWithSubAgents,
|
|
352
|
+
defaultSubAgent: this.opts.defaultSubAgent,
|
|
343
353
|
skills: this.skills,
|
|
344
354
|
contextLimits: this.contextLimits,
|
|
345
355
|
provider: this.provider,
|
|
@@ -381,6 +391,7 @@ export class AgentSession {
|
|
|
381
391
|
getBaseUrl: () => this.baseUrl,
|
|
382
392
|
getCacheKey: () => this.getPromptCacheKey(),
|
|
383
393
|
getMaxPerModel: () => this.settingsManager.get("subagentMaxPerModel"),
|
|
394
|
+
getChildModel: this.opts.getChildModel,
|
|
384
395
|
// A persistent child is already the single allowed fan-out level. Blocking
|
|
385
396
|
// `subagent` here created a timeout sandwich: the nested call could consume
|
|
386
397
|
// the child's entire 10-minute turn budget, discarding all nested results.
|
|
@@ -408,6 +419,16 @@ export class AgentSession {
|
|
|
408
419
|
// a hallucinated call can't mutate the repo — and buildSystemPrompt below is
|
|
409
420
|
// fed the same filtered names so the Tools section matches exactly.
|
|
410
421
|
this.tools = this.opts.allowedTools ? tools.filter((t) => this.isToolAllowed(t.name)) : tools;
|
|
422
|
+
const without = new Set(this.opts.withoutTools ?? []);
|
|
423
|
+
if (without.size > 0)
|
|
424
|
+
this.tools = this.tools.filter((t) => !without.has(t.name));
|
|
425
|
+
// A host may wrap built-ins (EZ Goals holds back its own editing once a
|
|
426
|
+
// goal is underway). Same names and schemas, so the cached tool block and
|
|
427
|
+
// the system prompt's Tools section are unchanged.
|
|
428
|
+
if (this.opts.wrapTool) {
|
|
429
|
+
const wrap = this.opts.wrapTool;
|
|
430
|
+
this.tools = this.tools.map((t) => wrap(t));
|
|
431
|
+
}
|
|
411
432
|
// Both delegation tools carry the agent roster; when both survive the
|
|
412
433
|
// allow-list, `subagent` points at `spawn_agent`'s copy instead.
|
|
413
434
|
if (this.tools.some((t) => t.name === "spawn_agent")) {
|
|
@@ -422,7 +443,14 @@ export class AgentSession {
|
|
|
422
443
|
// catalog must hold the wrapped tool so a promoted spawn still loads
|
|
423
444
|
// wait_agent.
|
|
424
445
|
this.promoteFollowUpTools();
|
|
425
|
-
const
|
|
446
|
+
const partition = partitionToolsByTier(this.tools);
|
|
447
|
+
// A session can keep chosen built-ins in its core set (EZ Goals
|
|
448
|
+
// orchestrates, so its agent tools are always at hand). Fixed for the
|
|
449
|
+
// session, so the cached tool block stays byte-stable.
|
|
450
|
+
const keep = new Set(this.opts.coreTools ?? []);
|
|
451
|
+
const deferred = partition.deferred.filter((t) => !keep.has(t.name));
|
|
452
|
+
// Everything not deferred, in the original relative order.
|
|
453
|
+
const core = keep.size > 0 ? this.tools.filter((t) => !deferred.includes(t)) : partition.core;
|
|
426
454
|
if (deferred.length > 0) {
|
|
427
455
|
// Append-only: `core` preserves the original relative order and
|
|
428
456
|
// tool_search is pushed after it, so the serialized tool block that
|
|
@@ -454,6 +482,9 @@ export class AgentSession {
|
|
|
454
482
|
else {
|
|
455
483
|
await this.connectMcpServers();
|
|
456
484
|
}
|
|
485
|
+
// Detect languages before the first render so the opening prompt already
|
|
486
|
+
// carries their packs and only a genuinely new language needs an update.
|
|
487
|
+
this.refreshActiveLanguages();
|
|
457
488
|
const basePrompt = await this.buildBasePrompt(false, undefined);
|
|
458
489
|
this.baseSystemPrompt = basePrompt;
|
|
459
490
|
this.messages = [{ role: "system", content: this.withSystemPromptTail(basePrompt) }];
|
|
@@ -786,6 +817,7 @@ export class AgentSession {
|
|
|
786
817
|
if (slash?.kind === "template") {
|
|
787
818
|
// Prompt templates remain human-originated: the user invoked the command.
|
|
788
819
|
const userMessage = { role: "user", content: slash.fullPrompt, provenance };
|
|
820
|
+
await this.announceNewLanguages();
|
|
789
821
|
this.messages.push(userMessage);
|
|
790
822
|
await this.persistMessage(userMessage);
|
|
791
823
|
this.lastPersistedIndex = this.messages.length;
|
|
@@ -802,6 +834,7 @@ export class AgentSession {
|
|
|
802
834
|
}
|
|
803
835
|
// Push user message
|
|
804
836
|
const userMessage = { role: "user", content, provenance };
|
|
837
|
+
await this.announceNewLanguages();
|
|
805
838
|
this.messages.push(userMessage);
|
|
806
839
|
await this.persistMessage(userMessage);
|
|
807
840
|
this.lastPersistedIndex = this.messages.length;
|
|
@@ -826,6 +859,7 @@ export class AgentSession {
|
|
|
826
859
|
content: parts,
|
|
827
860
|
provenance: { source: "human", kind: "prompt", visibility: "transcript" },
|
|
828
861
|
};
|
|
862
|
+
await this.announceNewLanguages();
|
|
829
863
|
this.messages.push(userMessage);
|
|
830
864
|
await this.persistMessage(userMessage);
|
|
831
865
|
this.lastPersistedIndex = this.messages.length;
|
|
@@ -1713,36 +1747,6 @@ export class AgentSession {
|
|
|
1713
1747
|
if (!force) {
|
|
1714
1748
|
if (!this.settingsManager.get("autoCompact"))
|
|
1715
1749
|
return messages;
|
|
1716
|
-
// Cheap stale-tool-output pruning before the expensive LLM
|
|
1717
|
-
// compaction check. In-place mutation preserves anchors; drop the
|
|
1718
|
-
// retained usage afterwards since it counted the pruned content.
|
|
1719
|
-
const pruneResult = pruneStaleToolResults(messages);
|
|
1720
|
-
if (pruneResult.pruned) {
|
|
1721
|
-
this.cacheDiagnostics.noteEdit("tool_prune", pruneResult.freedTokens);
|
|
1722
|
-
this.providerContext = null;
|
|
1723
|
-
log("INFO", "compaction", "Pruned stale tool outputs", {
|
|
1724
|
-
prunedResults: String(pruneResult.prunedResults),
|
|
1725
|
-
compactedToolCalls: String(pruneResult.compactedToolCalls),
|
|
1726
|
-
freedTokens: String(pruneResult.freedTokens),
|
|
1727
|
-
});
|
|
1728
|
-
}
|
|
1729
|
-
if (Date.now() < this.compactionRetryAfter)
|
|
1730
|
-
return messages;
|
|
1731
|
-
// The turn's own usage also counted the pruned content — after a
|
|
1732
|
-
// prune, fall back to estimating the (now smaller) history so the
|
|
1733
|
-
// freed tokens actually defer the LLM compaction.
|
|
1734
|
-
let usage = pruneResult.pruned ? undefined : transformOpts.usage;
|
|
1735
|
-
let pendingMessages = transformOpts.pendingMessages;
|
|
1736
|
-
if (!usage && this.providerContext) {
|
|
1737
|
-
const anchorIndex = messages.lastIndexOf(this.providerContext.anchor);
|
|
1738
|
-
if (anchorIndex >= 0) {
|
|
1739
|
-
usage = this.providerContext.usage;
|
|
1740
|
-
pendingMessages = messages.slice(anchorIndex + 1);
|
|
1741
|
-
}
|
|
1742
|
-
else {
|
|
1743
|
-
this.providerContext = null;
|
|
1744
|
-
}
|
|
1745
|
-
}
|
|
1746
1750
|
const contextWindow = getContextWindow(this.model, {
|
|
1747
1751
|
provider: this.provider,
|
|
1748
1752
|
accountId,
|
|
@@ -1755,10 +1759,48 @@ export class AgentSession {
|
|
|
1755
1759
|
accountId,
|
|
1756
1760
|
approvedPlanPath: this.approvedPlanPath,
|
|
1757
1761
|
});
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
+
// Provider usage when there is some, else an estimate of the history.
|
|
1763
|
+
const measureActiveTokens = () => {
|
|
1764
|
+
let usage = transformOpts.usage;
|
|
1765
|
+
let pendingMessages = transformOpts.pendingMessages;
|
|
1766
|
+
if (!usage && this.providerContext) {
|
|
1767
|
+
const anchorIndex = messages.lastIndexOf(this.providerContext.anchor);
|
|
1768
|
+
if (anchorIndex >= 0) {
|
|
1769
|
+
usage = this.providerContext.usage;
|
|
1770
|
+
pendingMessages = messages.slice(anchorIndex + 1);
|
|
1771
|
+
}
|
|
1772
|
+
else {
|
|
1773
|
+
this.providerContext = null;
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
return calculateActiveContextTokens(messages, { usage, pendingMessages });
|
|
1777
|
+
};
|
|
1778
|
+
let activeTokens = measureActiveTokens();
|
|
1779
|
+
// Cheap stale-tool-output pruning before the expensive LLM
|
|
1780
|
+
// compaction check, once the context is big enough for the freed
|
|
1781
|
+
// tokens to be worth a cache rewrite. In-place mutation preserves
|
|
1782
|
+
// anchors; drop the retained usage afterwards since it counted the
|
|
1783
|
+
// pruned content.
|
|
1784
|
+
if (shouldPruneToolResults(activeTokens, policy.targetTokens)) {
|
|
1785
|
+
const pruneResult = pruneStaleToolResults(messages);
|
|
1786
|
+
if (pruneResult.pruned) {
|
|
1787
|
+
this.cacheDiagnostics.noteEdit("tool_prune", pruneResult.freedTokens);
|
|
1788
|
+
this.providerContext = null;
|
|
1789
|
+
log("INFO", "compaction", "Pruned stale tool outputs", {
|
|
1790
|
+
prunedResults: String(pruneResult.prunedResults),
|
|
1791
|
+
compactedToolCalls: String(pruneResult.compactedToolCalls),
|
|
1792
|
+
freedTokens: String(pruneResult.freedTokens),
|
|
1793
|
+
});
|
|
1794
|
+
// The turn's own usage also counted the pruned content — estimate
|
|
1795
|
+
// the (now smaller) history so the freed tokens actually defer
|
|
1796
|
+
// the LLM compaction.
|
|
1797
|
+
activeTokens = calculateActiveContextTokens(messages, {
|
|
1798
|
+
pendingMessages: transformOpts.pendingMessages,
|
|
1799
|
+
});
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
if (Date.now() < this.compactionRetryAfter)
|
|
1803
|
+
return messages;
|
|
1762
1804
|
// An approved plan runs as ONE run, so step boundaries are only
|
|
1763
1805
|
// visible here, between model steps — not after the run ends.
|
|
1764
1806
|
const planStep = this.observePlanStepProgress(messages, contextWindow, activeTokens);
|
|
@@ -2510,6 +2552,9 @@ export class AgentSession {
|
|
|
2510
2552
|
// new session's /history, and get re-persisted into the new file by the
|
|
2511
2553
|
// next compaction — the cross-session duplicate-marker propagation bug.
|
|
2512
2554
|
this.markers.clear();
|
|
2555
|
+
// Detect languages before the first render so the opening prompt already
|
|
2556
|
+
// carries their packs and only a genuinely new language needs an update.
|
|
2557
|
+
this.refreshActiveLanguages();
|
|
2513
2558
|
const basePrompt = await this.buildBasePrompt(false, undefined);
|
|
2514
2559
|
this.baseSystemPrompt = basePrompt;
|
|
2515
2560
|
this.messages = [{ role: "system", content: this.withSystemPromptTail(basePrompt) }];
|
|
@@ -2713,6 +2758,19 @@ export class AgentSession {
|
|
|
2713
2758
|
.map((m) => m.text)
|
|
2714
2759
|
.join("\n\n");
|
|
2715
2760
|
}
|
|
2761
|
+
/**
|
|
2762
|
+
* Tell the agent a new fact at its next turn, as a short hidden message
|
|
2763
|
+
* appended after the cached prompt (the same channel as a finished worker),
|
|
2764
|
+
* so a mid-session change reaches it without re-rendering the prompt. A
|
|
2765
|
+
* later note with the same id replaces one not yet delivered.
|
|
2766
|
+
*/
|
|
2767
|
+
notify(id, text) {
|
|
2768
|
+
this.notifications.enqueue("goal", id, text, { terminal: true });
|
|
2769
|
+
}
|
|
2770
|
+
/** This session's sub-agents (workers), newest-state, including ones restored after a restart. */
|
|
2771
|
+
listSubAgents() {
|
|
2772
|
+
return this.subAgentManager?.list() ?? [];
|
|
2773
|
+
}
|
|
2716
2774
|
/** Snapshot of background processes (bash run_in_background), newest-state. */
|
|
2717
2775
|
listBackgroundProcesses() {
|
|
2718
2776
|
return this.processManager?.list() ?? [];
|
|
@@ -2811,7 +2869,7 @@ export class AgentSession {
|
|
|
2811
2869
|
return relative.startsWith("..") || path.isAbsolute(relative);
|
|
2812
2870
|
});
|
|
2813
2871
|
this.additionalRoots.push(resolved);
|
|
2814
|
-
await this.
|
|
2872
|
+
await this.refreshPromptForRoots();
|
|
2815
2873
|
return { ok: true, root: resolved };
|
|
2816
2874
|
}
|
|
2817
2875
|
/** Remove an exact root previously added with `/add-dir`. */
|
|
@@ -2822,9 +2880,20 @@ export class AgentSession {
|
|
|
2822
2880
|
return { ok: false, error: `Not an additional workspace root: ${resolved}` };
|
|
2823
2881
|
}
|
|
2824
2882
|
this.additionalRoots.splice(index, 1);
|
|
2825
|
-
await this.
|
|
2883
|
+
await this.refreshPromptForRoots();
|
|
2826
2884
|
return { ok: true, root: resolved };
|
|
2827
2885
|
}
|
|
2886
|
+
/**
|
|
2887
|
+
* Name a root change in the prompt only while there is no conversation yet.
|
|
2888
|
+
* Mid-conversation the env-delta note tells the model instead: the roots sit
|
|
2889
|
+
* in the Environment section, and rewriting it would invalidate every cached
|
|
2890
|
+
* message after it (a goal attaching a folder re-sent ~75k tokens).
|
|
2891
|
+
*/
|
|
2892
|
+
async refreshPromptForRoots() {
|
|
2893
|
+
if (this.messages.some((message) => message.role !== "system"))
|
|
2894
|
+
return;
|
|
2895
|
+
await this.rebuildSystemPromptInPlace();
|
|
2896
|
+
}
|
|
2828
2897
|
/**
|
|
2829
2898
|
* Names to advertise as available-on-demand. A tool the model already
|
|
2830
2899
|
* promoted lives in `this.tools` and carries its own schema, so it drops out
|
|
@@ -2865,6 +2934,8 @@ export class AgentSession {
|
|
|
2865
2934
|
return this.customSystemPrompt;
|
|
2866
2935
|
const toolNames = this.tools.map((tool) => tool.name);
|
|
2867
2936
|
const deferredToolNames = this.deferredToolNamesForPrompt(toolNames);
|
|
2937
|
+
// Whatever is rendered now is what the model has been told.
|
|
2938
|
+
this.announcedLanguages = new Set(this.activeLanguages);
|
|
2868
2939
|
if (this.agentPrompt !== undefined) {
|
|
2869
2940
|
return buildSubAgentSystemPrompt(this.agentPrompt, {
|
|
2870
2941
|
cwd: this.cwd,
|
|
@@ -2874,29 +2945,73 @@ export class AgentSession {
|
|
|
2874
2945
|
role: this.opts.agentRole,
|
|
2875
2946
|
environment: this.recordRenderedEnvironment(),
|
|
2876
2947
|
contextLimits: this.contextLimits,
|
|
2948
|
+
...(this.languagesApply() ? { activeLanguages: this.activeLanguages } : {}),
|
|
2877
2949
|
});
|
|
2878
2950
|
}
|
|
2879
2951
|
return buildSystemPrompt(this.cwd, this.skills, planMode, approvedPlanPath, toolNames, this.activeLanguages, this.provider, this.recordRenderedEnvironment(), deferredToolNames, this.contextLimits);
|
|
2880
2952
|
}
|
|
2881
|
-
/**
|
|
2882
|
-
* Add newly detected project languages. Returns true when the set grew and
|
|
2883
|
-
* the standard prompt therefore needs a rebuild. Custom and sub-agent
|
|
2884
|
-
* prompts never render packs, so detection is skipped for them.
|
|
2885
|
-
*/
|
|
2886
2953
|
/**
|
|
2887
2954
|
* Bring the system prompt to the exact state the next provider request will
|
|
2888
2955
|
* send. Shared by real runs and {@link prewarm} so both see one prefix.
|
|
2956
|
+
*
|
|
2957
|
+
* Languages are re-detected at each task boundary. Before the first reply
|
|
2958
|
+
* nothing is cached worth keeping, so a new language simply rebuilds the
|
|
2959
|
+
* prompt. After that the prompt is a cached prefix and is left alone: the
|
|
2960
|
+
* new language is announced by {@link announceNewLanguages} instead.
|
|
2889
2961
|
*/
|
|
2890
2962
|
async prepareSystemPromptForRequest() {
|
|
2891
|
-
|
|
2892
|
-
// during the previous turn gets its packs; the prompt is rebuilt only when
|
|
2893
|
-
// the set grows, keeping the cached prefix stable otherwise.
|
|
2894
|
-
if (this.refreshActiveLanguages())
|
|
2963
|
+
if (this.refreshActiveLanguages() && !this.hasReply()) {
|
|
2895
2964
|
await this.rebuildSystemPromptInPlace();
|
|
2965
|
+
}
|
|
2896
2966
|
this.refreshSystemPromptTail();
|
|
2897
2967
|
}
|
|
2968
|
+
hasReply() {
|
|
2969
|
+
return this.messages.some((m) => m.role === "assistant");
|
|
2970
|
+
}
|
|
2971
|
+
/**
|
|
2972
|
+
* Give the model style packs for languages that appeared after its prompt
|
|
2973
|
+
* was cached (e.g. the previous task scaffolded a TypeScript project), as a
|
|
2974
|
+
* hidden message appended to the history rather than a prompt re-render that
|
|
2975
|
+
* would throw away the whole cache. Runs just before a new user message is
|
|
2976
|
+
* added, so the user's request stays the last thing the model reads and the
|
|
2977
|
+
* update is saved in order with the rest of the conversation.
|
|
2978
|
+
*/
|
|
2979
|
+
async announceNewLanguages() {
|
|
2980
|
+
if (!this.hasReply())
|
|
2981
|
+
return; // the next request rebuilds the prompt instead
|
|
2982
|
+
this.refreshActiveLanguages();
|
|
2983
|
+
const fresh = new Set([...this.activeLanguages].filter((id) => !this.announcedLanguages.has(id)));
|
|
2984
|
+
if (fresh.size === 0)
|
|
2985
|
+
return;
|
|
2986
|
+
for (const id of fresh)
|
|
2987
|
+
this.announcedLanguages.add(id);
|
|
2988
|
+
const update = buildLanguageUpdateMessage(fresh, this.cwd);
|
|
2989
|
+
if (!update)
|
|
2990
|
+
return;
|
|
2991
|
+
this.messages.push(update);
|
|
2992
|
+
await this.persistMessage(update);
|
|
2993
|
+
this.lastPersistedIndex = this.messages.length;
|
|
2994
|
+
log("INFO", "language", "Appended style packs for new languages", {
|
|
2995
|
+
added: [...fresh].join(","),
|
|
2996
|
+
});
|
|
2997
|
+
}
|
|
2998
|
+
/**
|
|
2999
|
+
* Whether this session renders language packs: the standard prompt, and a
|
|
3000
|
+
* code-writing child (a sub-agent prompt with project context). A verbatim
|
|
3001
|
+
* custom prompt never does, nor do user-facing specialist agents (Motion,
|
|
3002
|
+
* goals) or recon children without project context.
|
|
3003
|
+
*/
|
|
3004
|
+
languagesApply() {
|
|
3005
|
+
if (this.customSystemPrompt)
|
|
3006
|
+
return false;
|
|
3007
|
+
if (this.agentPrompt === undefined)
|
|
3008
|
+
return true;
|
|
3009
|
+
return ((this.opts.agentRole ?? "subagent") === "subagent" &&
|
|
3010
|
+
(this.opts.agentContext ?? "project") === "project");
|
|
3011
|
+
}
|
|
3012
|
+
/** Add newly detected project languages. Returns true when the set grew. */
|
|
2898
3013
|
refreshActiveLanguages() {
|
|
2899
|
-
if (this.
|
|
3014
|
+
if (!this.languagesApply())
|
|
2900
3015
|
return false;
|
|
2901
3016
|
let grew = false;
|
|
2902
3017
|
try {
|