@pentoshi/clai 3.11.36 → 3.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/agent/compaction-summary.d.ts +8 -7
- package/dist/agent/compaction-summary.js +16 -13
- package/dist/agent/compaction-summary.js.map +1 -1
- package/dist/agent/runner.js +55 -4
- package/dist/agent/runner.js.map +1 -1
- package/dist/agent/tool-call-parser.js +115 -16
- package/dist/agent/tool-call-parser.js.map +1 -1
- package/dist/app/controllers/session-context-limits.js +1 -1
- package/dist/app/controllers/session-context-limits.js.map +1 -1
- package/dist/commands/providers.js +2 -0
- package/dist/commands/providers.js.map +1 -1
- package/dist/llm/anthropic.js +2 -0
- package/dist/llm/anthropic.js.map +1 -1
- package/dist/llm/capabilities.js +4 -0
- package/dist/llm/capabilities.js.map +1 -1
- package/dist/llm/gemini.js +2 -1
- package/dist/llm/gemini.js.map +1 -1
- package/dist/llm/http.d.ts +1 -1
- package/dist/llm/http.js +17 -3
- package/dist/llm/http.js.map +1 -1
- package/dist/llm/meta.d.ts +2 -0
- package/dist/llm/meta.js +87 -0
- package/dist/llm/meta.js.map +1 -0
- package/dist/llm/provider.js +56 -0
- package/dist/llm/provider.js.map +1 -1
- package/dist/llm/router.js +5 -2
- package/dist/llm/router.js.map +1 -1
- package/dist/llm/token-usage.js +1 -0
- package/dist/llm/token-usage.js.map +1 -1
- package/dist/prompts/embedded.js +2 -2
- package/dist/prompts/embedded.js.map +1 -1
- package/dist/prompts/system.agent.md +7 -0
- package/dist/prompts/system.ask.md +2 -0
- package/dist/repl/slash-commands.js +2 -2
- package/dist/repl/slash-commands.js.map +1 -1
- package/dist/repl.js +3 -2
- package/dist/repl.js.map +1 -1
- package/dist/store/config.js +1 -0
- package/dist/store/config.js.map +1 -1
- package/dist/tools/registry.d.ts +0 -8
- package/dist/tools/registry.js +36 -0
- package/dist/tools/registry.js.map +1 -1
- package/dist/tui-v2/app/App.js +8 -1
- package/dist/tui-v2/app/App.js.map +1 -1
- package/dist/tui-v2/app/commands/picker-commands.js +3 -2
- package/dist/tui-v2/app/commands/picker-commands.js.map +1 -1
- package/dist/tui-v2/components/status/context-limit-chip.js +17 -3
- package/dist/tui-v2/components/status/context-limit-chip.js.map +1 -1
- package/dist/tui-v2/components/transcript/transcript-view.js +2 -0
- package/dist/tui-v2/components/transcript/transcript-view.js.map +1 -1
- package/dist/tui-v2/controllers/focus-controller.d.ts +3 -0
- package/dist/tui-v2/controllers/focus-controller.js +7 -0
- package/dist/tui-v2/controllers/focus-controller.js.map +1 -1
- package/dist/tui-v2/rendering/incremental-strip.js +1 -1
- package/dist/tui-v2/rendering/incremental-strip.js.map +1 -1
- package/dist/tui-v2/rendering/strip-tool-surfaces.js +5 -5
- package/dist/tui-v2/rendering/strip-tool-surfaces.js.map +1 -1
- package/dist/types.d.ts +2 -2
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/dist/ui/mentions.d.ts +0 -7
- package/dist/ui/mentions.js +15 -75
- package/dist/ui/mentions.js.map +1 -1
- package/dist/version.generated.d.ts +2 -2
- package/dist/version.generated.js +2 -2
- package/dist/version.generated.js.map +1 -1
- package/package.json +1 -2
package/README.md
CHANGED
|
@@ -498,7 +498,7 @@ npm run compile # native binaries (Bun)
|
|
|
498
498
|
|
|
499
499
|
## Releasing
|
|
500
500
|
|
|
501
|
-
Tag-driven CI (`.github/workflows/release.yml`): validate (typecheck + tests +
|
|
501
|
+
Tag-driven CI (`.github/workflows/release.yml`): validate (typecheck + tests + release checks) → multi-platform binaries → GitHub Release → npm `@pentoshi/clai` → Homebrew tap.
|
|
502
502
|
|
|
503
503
|
```sh
|
|
504
504
|
# package.json "version" is the single source of truth.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Prompts and helpers for LLM-based context compaction.
|
|
3
3
|
* The model produces structured continuation memory — not a raw transcript dump.
|
|
4
4
|
*/
|
|
5
|
-
export declare const COMPACTION_SYSTEM_PROMPT = "You are a session-memory compressor for an autonomous coding and security agent.\n\nWrite a dense, accurate CONTINUATION MEMORY for another assistant that will resume this work with no other history.\n\nYou are SUMMARIZING a past session, NOT continuing it. Do not answer the user, do not perform the next task, and do not role-play the agent. Never emit tool calls or invent tool results, file-write receipts (bytes/lines/sha256), exit codes, or \"TOOL:\" / \"[tools: \u2026]\" transcript lines \u2014 describe what already happened in your own words.\n\nRules:\n- Fidelity over style. Never invent tool results, file contents, findings, URLs, ports, or completions.\n- Prefer concrete artifacts: absolute paths, commands (short form), exit outcomes, HTTP status, plan task ids/states, job ids, open ports, confirmed vs unconfirmed findings.\n- LENGTH: aim for ~
|
|
5
|
+
export declare const COMPACTION_SYSTEM_PROMPT = "You are a session-memory compressor for an autonomous coding and security agent.\n\nWrite a dense, accurate CONTINUATION MEMORY for another assistant that will resume this work with no other history.\n\nYou are SUMMARIZING a past session, NOT continuing it. Do not answer the user, do not perform the next task, and do not role-play the agent. Never emit tool calls or invent tool results, file-write receipts (bytes/lines/sha256), exit codes, or \"TOOL:\" / \"[tools: \u2026]\" transcript lines \u2014 describe what already happened in your own words.\n\nRules:\n- Fidelity over style. Never invent tool results, file contents, findings, URLs, ports, or completions.\n- Prefer concrete artifacts: absolute paths, commands (short form), exit outcomes, HTTP status, plan task ids/states, job ids, open ports, confirmed vs unconfirmed findings.\n- LENGTH: aim for ~1400\u20132400 tokens of dense structured bullets \u2014 a richer, complete memory beats a terse one, but finish within budget rather than cutting mid-sentence. No full tool transcripts or HTML bodies.\n- DETAIL LEVEL: mechanism-level specificity. For code changes name the file path with line anchors, what changed, the before\u2192after behavior, and the verification evidence (which tests/typecheck/build status prove it). For debugging name the root cause, each failed approach, and why it failed. For research name exact findings with their evidence. For pending work give the next concrete step a cold reader can execute immediately. A reader resuming with no other context must not need to re-discover anything recorded below.\n- NO DUPLICATION: state each fact exactly once, in its best section. This memory is prepended to a live context that ALSO re-injects fresh ACTIVE PLAN, SESSION STATE, and ENGAGEMENT SCOPE \u2014 do not restate the full plan or every task, and do not reproduce long user prompts verbatim; capture goals/deltas concisely.\n- Omit secrets, API keys, passwords, tokens, and full credential material. Say \"[redacted]\" if present.\n- Omit progress bars, repeated failures, and decorative chatter \u2014 but list genuinely informative failures (with cause and attempt count) under Open risks / failures.\n- Do not wrap the whole answer in markdown code fences.\n- If something is unknown, write \"(unknown)\" rather than guessing.\n- PHASE AWARENESS: Temporary UI/mode gates (plan-mode gather-only, \"await accept\", \"do not implement yet\") are HISTORICAL context \u2014 never rewrite them as permanent forever-rules for the resuming agent. Durable user/engagement policy (scope, non-destructive default, stay on remote target when that is the engagement) may still apply.";
|
|
6
6
|
export interface CompactionPromptParts {
|
|
7
7
|
visualTranscript?: string | undefined;
|
|
8
8
|
messageTranscript: string;
|
|
@@ -19,13 +19,14 @@ export declare function buildCompactionUserPrompt(parts: CompactionPromptParts):
|
|
|
19
19
|
/** Soft cap for transcript fed to the summarizer (chars). */
|
|
20
20
|
export declare const COMPACTION_TRANSCRIPT_CHAR_BUDGET = 48000;
|
|
21
21
|
/**
|
|
22
|
-
* A compaction is a compression operation, not a reasoning task.
|
|
23
|
-
*
|
|
24
|
-
*
|
|
22
|
+
* A compaction is a compression operation, not a reasoning task. The final
|
|
23
|
+
* memory is required to carry mechanism-level detail (per the prompts above),
|
|
24
|
+
* so its allowance must comfortably exceed the ~1400–2400 token target while
|
|
25
|
+
* still discouraging hidden reasoning blowout.
|
|
25
26
|
*/
|
|
26
|
-
export declare const COMPACTION_MAX_COMPLETION_TOKENS =
|
|
27
|
-
/** Map passes
|
|
28
|
-
export declare const COMPACTION_MAP_MAX_COMPLETION_TOKENS =
|
|
27
|
+
export declare const COMPACTION_MAX_COMPLETION_TOKENS = 3072;
|
|
28
|
+
/** Map passes extract facts at the same detail level but for one region only. */
|
|
29
|
+
export declare const COMPACTION_MAP_MAX_COMPLETION_TOKENS = 1536;
|
|
29
30
|
/** Chars per map-stage chunk when the transcript exceeds one summarizer call. */
|
|
30
31
|
export declare const COMPACTION_CHUNK_CHAR_BUDGET = 96000;
|
|
31
32
|
/**
|
|
@@ -11,12 +11,13 @@ You are SUMMARIZING a past session, NOT continuing it. Do not answer the user, d
|
|
|
11
11
|
Rules:
|
|
12
12
|
- Fidelity over style. Never invent tool results, file contents, findings, URLs, ports, or completions.
|
|
13
13
|
- Prefer concrete artifacts: absolute paths, commands (short form), exit outcomes, HTTP status, plan task ids/states, job ids, open ports, confirmed vs unconfirmed findings.
|
|
14
|
-
- LENGTH: aim for ~
|
|
14
|
+
- LENGTH: aim for ~1400–2400 tokens of dense structured bullets — a richer, complete memory beats a terse one, but finish within budget rather than cutting mid-sentence. No full tool transcripts or HTML bodies.
|
|
15
|
+
- DETAIL LEVEL: mechanism-level specificity. For code changes name the file path with line anchors, what changed, the before→after behavior, and the verification evidence (which tests/typecheck/build status prove it). For debugging name the root cause, each failed approach, and why it failed. For research name exact findings with their evidence. For pending work give the next concrete step a cold reader can execute immediately. A reader resuming with no other context must not need to re-discover anything recorded below.
|
|
15
16
|
- NO DUPLICATION: state each fact exactly once, in its best section. This memory is prepended to a live context that ALSO re-injects fresh ACTIVE PLAN, SESSION STATE, and ENGAGEMENT SCOPE — do not restate the full plan or every task, and do not reproduce long user prompts verbatim; capture goals/deltas concisely.
|
|
16
17
|
- Omit secrets, API keys, passwords, tokens, and full credential material. Say "[redacted]" if present.
|
|
17
|
-
- Omit progress bars, repeated failures, and decorative chatter.
|
|
18
|
+
- Omit progress bars, repeated failures, and decorative chatter — but list genuinely informative failures (with cause and attempt count) under Open risks / failures.
|
|
18
19
|
- Do not wrap the whole answer in markdown code fences.
|
|
19
|
-
- If something is unknown,
|
|
20
|
+
- If something is unknown, write "(unknown)" rather than guessing.
|
|
20
21
|
- PHASE AWARENESS: Temporary UI/mode gates (plan-mode gather-only, "await accept", "do not implement yet") are HISTORICAL context — never rewrite them as permanent forever-rules for the resuming agent. Durable user/engagement policy (scope, non-destructive default, stay on remote target when that is the engagement) may still apply.`;
|
|
21
22
|
/** User message fed to the summarizer model. */
|
|
22
23
|
export function buildCompactionUserPrompt(parts) {
|
|
@@ -36,9 +37,10 @@ export function buildCompactionUserPrompt(parts) {
|
|
|
36
37
|
sections.push("Create a complete but compact HANDOFF MEMORY from PLAN-MODE RESEARCH for an agent that will EXECUTE the accepted plan next.", "Do not add another framing paragraph; the memory wrapper already labels this as PLAN MODE HANDOFF and ACTIVE PLAN is re-injected separately.", "The material below was gathered under plan mode to design that plan/tasks. The next phase is agent implement — not gather-only.", "The live ACTIVE PLAN (goal/detail/tasks) will be re-injected separately — your job is ACTIONABLE research memory so implement does not re-discover or miss coverage.", "", "Use these section headings as applicable. For coding/general work, omit empty security-only sections instead of writing repeated `none` bullets; for pentest handoffs retain the coverage/finding sections:", "## User goals", "## Research evidence (facts from plan-mode tools)", "## Coverage ledger (do not blindly re-do)", "## Confirmed findings", "## Negative / tested-OK results", "## Untested / open classes", "## Artifacts & paths", "## Durable engagement rules (still apply in agent mode)", "## Plan-mode-only notes (historical — not current agent gates)", "## Commands/tools and key results", "## Current state", "## Remaining work (post-accept tasks)", "## Open risks / failures", "", "How to fill sections (agent-performance critical):", "- DEDUPLICATE: state each fact once in the best section. Do not repeat destination/tool versions/config contents under evidence, findings, commands, and current state.", "- For coding handoffs, target ~600–1000 tokens. Security handoffs may use the full 800–1500 only when needed to preserve findings/coverage.", "- Research evidence: concrete verified facts only — project state/stack/config for coding; hosts/IPs, ports, stack fingerprint, auth surfaces, headers, endpoints, versions for security. Complete every bullet.", "- Coverage ledger: expensive/stable research already done so execution deepens rather than restarts. This does NOT forbid one concise revalidation of mutable workspace/manifest/tool state after handoff.", "- Confirmed findings: each as severity + one-line evidence + impact/repro if known (F1, F2, … or short titles). If none yet, say so.", "- Negative / tested-OK: things checked and not issues (SSRF guard, CORS locked, etc.) so implement does not re-burn steps.", "- Untested / open classes: authz/IDOR, RBAC, payment, injection, etc. still open — especially anything blocked on credentials.", "- Artifacts & paths: include only reusable artifacts the implementer may need (reports, downloads, extracted specs, scan outputs). Omit routine fs.list/fs.read/tool.check receipt paths and transient temp logs whose result is already summarized.", "- Durable engagement rules ONLY if they still bind implement (remote target/scope, authorized testing, non-destructive default, do not treat clai workspace as the target, no local dev server for remote assessments).", "- Plan-mode-only notes: gather-only / await-accept / no-exploit-yet gates that applied BEFORE accept — label historical; implementer is past that phase.", "- Commands/tools: only non-obvious or reusable commands and their outcomes. Do not repeat routine list/read/tool-version facts already captured above.", "- Current state: describe the last tool-observed state at compaction time. Do not claim plan acceptance/approval unless DURABLE STATE confirms it; distinguish transition assumptions from evidence.", "- Remaining work: ACTIVE PLAN is injected separately, so do not restate every task. Record only the next task, dependency/order caveats, blockers, or plan details not present in durable state.", "- Resolve contradictions explicitly (for example, a non-empty destination is not simultaneously guaranteed safe for an in-place scaffolder). If evidence conflicts, preserve the uncertainty and safest next check.", "- Do NOT invent findings. Do NOT omit confirmed issues that appear in the material. Prefer denser bullets over long prose.", "- Prefer COMPLETE short memory over a long memory that cuts off mid-sentence.");
|
|
37
38
|
}
|
|
38
39
|
else {
|
|
39
|
-
sections.push("Create a complete but compact continuation memory of the session below.", "Treat resumed history and newer turns as one continuous conversation.", "Open with a single ORIENTATION line — current objective plus status (done / in progress / blocked) — so the resuming agent reorients in one read. Keep it to one line; the details belong in the sections below, not restated here.", "", "Organize under these exact section headings (skip a section only if empty):", "## User goals", "## Decisions and constraints", "## Work completed", "## Commands/tools and results", "## Current state", "## Remaining work", "## Open risks / failures", "", "Preserve: user intentions, decisions, constraints, requested execution boundary (entire program/all phases versus a named phase versus unspecified), referenced roadmap/plan/task/index paths, stack/package manager,", "commands and key results, plan task states/hierarchy, errors and failed approaches,", "Responder notification ids, linked task/parent ids, job/PID/status, durable artifact paths, and the authoritative consumed/analyzed state from RESPONDER RESULT LEDGER entries. Never describe consumed=true ledger entries as unread, pending, or needing another artifact read.", "servers/jobs still running, and exactly what remains.", "", "AVOID BLOAT — this memory is prepended to a context that re-injects fresh ACTIVE PLAN, SESSION STATE, and ENGAGEMENT SCOPE after compaction:", "- DEDUPLICATE: state each fact once in its best section. Never repeat the same path, command, decision, or finding across multiple sections.", "- Do NOT restate the full plan or list every task under Remaining work — the live ACTIVE PLAN is re-injected separately. Record only deltas: the next task, blockers, dependency/order caveats, and states not obvious from the plan.", "- Do NOT reproduce long user prompts verbatim. Capture the goal and hard constraints concisely under User goals.", "- Omit routine fs.list / fs.read / tool.check receipts and transient logs whose result is already captured. Prefer dense bullets over prose.", "", "PHASE AWARENESS under Decisions and constraints:", "- If earlier turns were plan-mode research, do not elevate gather-only / await-accept / no-implement rules as permanent forever-constraints for agent execution.", "- Durable engagement policy (scope, remote target, non-destructive default) may still be listed as current.", "- Prefer labeling historical plan-mode gates as \"(plan-mode only; superseded after accept)\" when they appear in the material.");
|
|
40
|
+
sections.push("Create a complete but compact continuation memory of the session below.", "Treat resumed history and newer turns as one continuous conversation.", "Open with a single ORIENTATION line — current objective plus status (done / in progress / blocked) — so the resuming agent reorients in one read. Keep it to one line; the details belong in the sections below, not restated here.", "", "Organize under these exact section headings (skip a section only if empty):", "## User goals", "## Key facts and environment", "## Decisions and constraints", "## Work completed", "## In flight / blocked", "## Commands/tools and results", "## Current state", "## Remaining work", "## Relevant files", "## Open risks / failures", "", "How to fill sections (resume-quality critical — write for a reader with zero prior context):", "- User goals: the objective plus hard constraints the user imposed verbatim or near-verbatim (style rules, forbiddens, scope limits), and the requested execution boundary (entire program/all phases versus a named phase versus unspecified).", "- Key facts and environment: mechanism-level truths discovered during the session — API/data shapes and field semantics, regex or parser behaviors, gate conditions and flag interactions, environment quirks (broken credentials, unavailable services, tool versions), and anything verified empirically. Each bullet must let the resumer act without re-verifying.", "- Decisions and constraints: each decision with its rationale and source (user directive, discovered evidence, tool constraint), not just the decision itself.", "- Work completed: one bullet per change/result with file path and line anchors, what changed, before→after behavior, and explicit verification evidence (test counts, exit codes, commands that passed, or 'not yet verified'). Group as Completed, then any reverted/abandoned changes with why.", "- In flight / blocked: edits made but not verified, designs decided but not applied, and blocked items with the exact missing piece — including the concrete next edit already determined but not yet performed.", "- Commands/tools and results: key commands with outcomes; notable failures with root cause and how many attempts before success/abandonment so the resumer does not retry dead ends.", "- Current state: what is true at compaction time — running servers/jobs (ids, ports), dirty worktree state, open handles, last observed outputs.", "- Remaining work: an ordered, numbered list of concrete next steps — each step names the action, the file/line or artifact it applies to, and the verification command that proves it done. Do not restate plan tasks already recorded in the live plan; record the deltas and execution detail.", "- Relevant files: path → role, what changed there, and line anchors worth resuming from. Include test files with their pass/fail state.", "- Open risks / failures: unresolved bugs, known-broken neighbors left untouched, suspicious observations not yet explained.", "", "Preserve: user intentions, decisions, constraints, requested execution boundary (entire program/all phases versus a named phase versus unspecified), referenced roadmap/plan/task/index paths, stack/package manager,", "commands and key results, plan task states/hierarchy, errors and failed approaches,", "Responder notification ids, linked task/parent ids, job/PID/status, durable artifact paths, and the authoritative consumed/analyzed state from RESPONDER RESULT LEDGER entries. Never describe consumed=true ledger entries as unread, pending, or needing another artifact read.", "servers/jobs still running, and exactly what remains.", "", "AVOID BLOAT — this memory is prepended to a context that re-injects fresh ACTIVE PLAN, SESSION STATE, and ENGAGEMENT SCOPE after compaction:", "- DEDUPLICATE: state each fact once in its best section. Never repeat the same path, command, decision, or finding across multiple sections.", "- Do NOT restate the full plan or list every task under Remaining work — the live ACTIVE PLAN is re-injected separately. Record only deltas: the next task, blockers, dependency/order caveats, and states not obvious from the plan.", "- Do NOT reproduce long user prompts verbatim. Capture the goal and hard constraints concisely under User goals.", "- Omit routine fs.list / fs.read / tool.check receipts and transient logs whose result is already captured. Prefer dense bullets over prose.", "", "PHASE AWARENESS under Decisions and constraints:", "- If earlier turns were plan-mode research, do not elevate gather-only / await-accept / no-implement rules as permanent forever-constraints for agent execution.", "- Durable engagement policy (scope, remote target, non-destructive default) may still be listed as current.", "- Prefer labeling historical plan-mode gates as \"(plan-mode only; superseded after accept)\" when they appear in the material.");
|
|
40
41
|
}
|
|
41
|
-
|
|
42
|
+
const target = "Be specific over short. Target ~1400–2400 tokens of memory (~800–1200 for plan-mode handoffs). Dense bullets over prose. No secrets. No fabricated successes. No full tool dumps.";
|
|
43
|
+
sections.push(target);
|
|
42
44
|
if (durable) {
|
|
43
45
|
sections.push("", "DURABLE STATE (trust this over older chatter):", durable);
|
|
44
46
|
}
|
|
@@ -48,13 +50,14 @@ export function buildCompactionUserPrompt(parts) {
|
|
|
48
50
|
/** Soft cap for transcript fed to the summarizer (chars). */
|
|
49
51
|
export const COMPACTION_TRANSCRIPT_CHAR_BUDGET = 48_000;
|
|
50
52
|
/**
|
|
51
|
-
* A compaction is a compression operation, not a reasoning task.
|
|
52
|
-
*
|
|
53
|
-
*
|
|
53
|
+
* A compaction is a compression operation, not a reasoning task. The final
|
|
54
|
+
* memory is required to carry mechanism-level detail (per the prompts above),
|
|
55
|
+
* so its allowance must comfortably exceed the ~1400–2400 token target while
|
|
56
|
+
* still discouraging hidden reasoning blowout.
|
|
54
57
|
*/
|
|
55
|
-
export const COMPACTION_MAX_COMPLETION_TOKENS =
|
|
56
|
-
/** Map passes
|
|
57
|
-
export const COMPACTION_MAP_MAX_COMPLETION_TOKENS =
|
|
58
|
+
export const COMPACTION_MAX_COMPLETION_TOKENS = 3_072;
|
|
59
|
+
/** Map passes extract facts at the same detail level but for one region only. */
|
|
60
|
+
export const COMPACTION_MAP_MAX_COMPLETION_TOKENS = 1_536;
|
|
58
61
|
/** Chars per map-stage chunk when the transcript exceeds one summarizer call. */
|
|
59
62
|
export const COMPACTION_CHUNK_CHAR_BUDGET = 96_000;
|
|
60
63
|
/**
|
|
@@ -98,12 +101,12 @@ export function chunkTranscriptForCompaction(transcript, chunkChars = COMPACTION
|
|
|
98
101
|
export function buildCompactionChunkPrompt(input) {
|
|
99
102
|
const focus = input.purpose === "plan-implement"
|
|
100
103
|
? "Preserve targets, stack, confirmed findings, negative results, untested classes, artifact paths, commands and remaining work."
|
|
101
|
-
: "Preserve goals, decisions, file paths, commands and their results, task states, failures, running jobs and remaining work.";
|
|
104
|
+
: "Preserve goals, decisions, file paths with line anchors, before→after behavior of each change, verification evidence, environment quirks, commands and their results, task states, failures (with causes), running jobs and remaining work.";
|
|
102
105
|
return [
|
|
103
106
|
`Summarize region ${input.index + 1} of ${input.total} of one continuous session.`,
|
|
104
107
|
"This is a partial region: do not write an orientation line, do not speculate about regions you cannot see, and do not answer the user.",
|
|
105
108
|
focus,
|
|
106
|
-
"Dense fact bullets only. Transform the material into findings; never copy transcript lines, headings, tool-call JSON, file-write receipts, or long output verbatim. Preserve completed work, negative results, decisions, paths, commands, blockers, and remaining work so they are not repeated after resume.",
|
|
109
|
+
"Dense fact bullets only, at mechanism level. Transform the material into findings; never copy transcript lines, headings, tool-call JSON, file-write receipts, or long output verbatim. Preserve completed work, negative results, decisions, paths, commands, blockers, and remaining work so they are not repeated after resume.",
|
|
107
110
|
"",
|
|
108
111
|
"SESSION MATERIAL (REGION):",
|
|
109
112
|
"",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compaction-summary.js","sourceRoot":"","sources":["../../src/agent/compaction-summary.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,CAAC,MAAM,wBAAwB,GAAG
|
|
1
|
+
{"version":3,"file":"compaction-summary.js","sourceRoot":"","sources":["../../src/agent/compaction-summary.ts"],"names":[],"mappings":"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"}
|
package/dist/agent/runner.js
CHANGED
|
@@ -880,6 +880,8 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
880
880
|
});
|
|
881
881
|
};
|
|
882
882
|
const loopGuard = new LoopGuard();
|
|
883
|
+
let lastExactPromptTokens = 0;
|
|
884
|
+
let consecutiveSynthesizedRounds = 0;
|
|
883
885
|
const engagementPolicy = new EngagementPolicyEngine();
|
|
884
886
|
const probeStateKey = (call) => {
|
|
885
887
|
const project = (job) => job
|
|
@@ -2785,6 +2787,7 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
2785
2787
|
: COMPACTION_MAX_COMPLETION_TOKENS,
|
|
2786
2788
|
thinking: { enabled: false, effort: "none" },
|
|
2787
2789
|
signal: options.signal,
|
|
2790
|
+
allowModelFallback: true,
|
|
2788
2791
|
};
|
|
2789
2792
|
const response = await streamWithProvider(request, (token) => deltaParser?.push(token), { onStatus: () => undefined, maxRetries: 0 });
|
|
2790
2793
|
deltaParser?.finish();
|
|
@@ -2805,6 +2808,7 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
2805
2808
|
? COMPACTION_MAP_MAX_COMPLETION_TOKENS
|
|
2806
2809
|
: COMPACTION_MAX_COMPLETION_TOKENS,
|
|
2807
2810
|
thinking: { enabled: false, effort: "none" },
|
|
2811
|
+
allowModelFallback: true,
|
|
2808
2812
|
}, { maxRetries: 0 });
|
|
2809
2813
|
const retryVisible = stripThinking(retry.text).visible.trim();
|
|
2810
2814
|
if (retryVisible && compactionId) {
|
|
@@ -2873,7 +2877,7 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
2873
2877
|
});
|
|
2874
2878
|
}
|
|
2875
2879
|
async function maybeAutoCompact(reason, force = false) {
|
|
2876
|
-
const beforeTokens = estimateNextRequestTokens(messages);
|
|
2880
|
+
const beforeTokens = Math.max(estimateNextRequestTokens(messages), lastExactPromptTokens);
|
|
2877
2881
|
const contextLimitTokens = currentContextLimitTokens();
|
|
2878
2882
|
const compactTrigger = autoCompactTriggerTokens(getReliabilityPolicy(), {
|
|
2879
2883
|
provider,
|
|
@@ -2939,6 +2943,7 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
2939
2943
|
messages.splice(0, messages.length, ...result.messages);
|
|
2940
2944
|
compactionAttempts.recordSuccess(attemptKey);
|
|
2941
2945
|
loopGuard.resetReadOnly();
|
|
2946
|
+
lastExactPromptTokens = 0;
|
|
2942
2947
|
// Token stats use the same complete request estimate as the trigger.
|
|
2943
2948
|
const compactedTokens = estimateNextRequestTokens(messages);
|
|
2944
2949
|
// Re-inject the live plan so the model keeps full plan awareness even
|
|
@@ -3496,6 +3501,9 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
3496
3501
|
provider = completion.provider;
|
|
3497
3502
|
model = completion.model;
|
|
3498
3503
|
if (completion.usage) {
|
|
3504
|
+
if (completion.usage.exact && completion.usage.promptTokens > 0) {
|
|
3505
|
+
lastExactPromptTokens = completion.usage.promptTokens;
|
|
3506
|
+
}
|
|
3499
3507
|
emit({
|
|
3500
3508
|
type: "token-usage",
|
|
3501
3509
|
usage: completion.usage,
|
|
@@ -3861,7 +3869,7 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
3861
3869
|
}
|
|
3862
3870
|
// Exhausted retries — fall through to the normal answer path.
|
|
3863
3871
|
}
|
|
3864
|
-
if (/<\|tool_call(?:s_section)?_begin\|>|<\|tool_call_argument_begin\|>|<[||]DSML[||](?:tool_calls|invoke|parameter)\b/i.test(assistantText.visible)) {
|
|
3872
|
+
if (/<\|tool_call(?:s_section)?_begin\|>|<\|tool_call_argument_begin\|>|<[||]+DSML[||]+(?:tool_calls|invoke|parameter)\b/i.test(assistantText.visible)) {
|
|
3865
3873
|
writeNotice("warn", "tool call was malformed or cut off — asking the model to retry in JSON form", chalk.yellow(" ⚠ tool call was malformed or cut off — asking the model to retry in JSON form\n"));
|
|
3866
3874
|
commitAssistantRetry(assistantText.visible);
|
|
3867
3875
|
messages.push(recoveryUserMessage(toolsAttached
|
|
@@ -4294,6 +4302,32 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
4294
4302
|
writeToolOutput(eventId, output, chalk.dim(` ${output}`));
|
|
4295
4303
|
emitToolResult(eventId, result, resultReason);
|
|
4296
4304
|
}
|
|
4305
|
+
const suppressedCallList = suppressedResults
|
|
4306
|
+
.map(({ b }) => `${b.call.name} ${formatToolArgs(b.call)}`)
|
|
4307
|
+
.join("; ");
|
|
4308
|
+
const deniedContent = (b, resultReason) => `Tool ${b.call.name} result (exit=130, ok=false):\n` +
|
|
4309
|
+
`NOT EXECUTED — suppressed repeat. ${resultReason}\n\n` +
|
|
4310
|
+
`Suppressed call: ${b.call.name} ${formatToolArgs(b.call)}. ` +
|
|
4311
|
+
"This exact call is blocked for the rest of the turn; its earlier result is already in context — use it, or choose a different action.";
|
|
4312
|
+
if (historyNativeCalls.length) {
|
|
4313
|
+
appendAssistantWithTools(messages, beforeTool ?? "", historyNativeCalls, completion.reasoningBlock ??
|
|
4314
|
+
(assistantText.hasThinking && assistantText.thinkContent
|
|
4315
|
+
? { text: assistantText.thinkContent }
|
|
4316
|
+
: undefined));
|
|
4317
|
+
for (const { b, resultReason } of suppressedResults) {
|
|
4318
|
+
appendToolResult(messages, b.id, deniedContent(b, resultReason), b.call.name, false);
|
|
4319
|
+
}
|
|
4320
|
+
}
|
|
4321
|
+
else {
|
|
4322
|
+
const standardizedContent = (beforeTool ? beforeTool.trim() + "\n\n" : "") +
|
|
4323
|
+
bound
|
|
4324
|
+
.map((b) => `\`\`\`tool\n${JSON.stringify(b.call)}\n\`\`\``)
|
|
4325
|
+
.join("\n\n");
|
|
4326
|
+
pushAssistantHistory(standardizedContent);
|
|
4327
|
+
for (const { b, resultReason } of suppressedResults) {
|
|
4328
|
+
messages.push({ role: "tool", content: deniedContent(b, resultReason) });
|
|
4329
|
+
}
|
|
4330
|
+
}
|
|
4297
4331
|
if (sequenceDecision.terminal) {
|
|
4298
4332
|
const remainingCriteria = unreadResponderNotificationIds.size > 0
|
|
4299
4333
|
? ["Analyze and acknowledge the delivered Responder result without repeating completed foreground work."]
|
|
@@ -4301,12 +4335,13 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
4301
4335
|
outcomeState.outcome.status = "partial";
|
|
4302
4336
|
await saveOutcomeState(outcomeState);
|
|
4303
4337
|
moveTurn("partial", "repeated identical action sequence");
|
|
4304
|
-
return finishTurn(
|
|
4338
|
+
return finishTurn(`Stopped an identical action cycle before it could execute again. Blocked this turn: ${suppressedCallList}. Their earlier results are in context — continue from those, do not re-issue the same calls.`, productiveSteps, "partial", remainingCriteria, "The model repeated an identical action sequence without a new premise or state change.");
|
|
4305
4339
|
}
|
|
4306
4340
|
upsertActionCycleRecovery(reason +
|
|
4341
|
+
` The repeated calls were: ${suppressedCallList}.` +
|
|
4307
4342
|
(unreadResponderNotificationIds.size > 0
|
|
4308
4343
|
? " A delivered Responder result is still unread: analyze the available result, gather only genuinely necessary bounded evidence, then call job.read before returning to foreground work."
|
|
4309
|
-
: " The original successful tool
|
|
4344
|
+
: " The original successful tool results remain in context. Reassess that evidence and either finish or select a materially different action; do not replay completed work."));
|
|
4310
4345
|
continue;
|
|
4311
4346
|
}
|
|
4312
4347
|
if (sequenceDecision.warn && sequenceDecision.warnMessage) {
|
|
@@ -4369,6 +4404,7 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
4369
4404
|
/** True after a successful plan.create this turn (activePlan is turn-start snapshot). */
|
|
4370
4405
|
let planCreatedThisTurn = Boolean(activePlan && activePlan.tasks.length > 0);
|
|
4371
4406
|
let actionSequenceExecuted = 0;
|
|
4407
|
+
let roundSuppressedCount = 0;
|
|
4372
4408
|
let actionSequenceEligible = allCalls.length > 0;
|
|
4373
4409
|
const actionSequenceOutcomes = new Map();
|
|
4374
4410
|
/**
|
|
@@ -4381,6 +4417,8 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
4381
4417
|
consecutiveModelOnlyRounds = 0;
|
|
4382
4418
|
recordedNativeIds.add(boundCall.id);
|
|
4383
4419
|
actionSequenceExecuted += 1;
|
|
4420
|
+
if (res.suppressedRepeat)
|
|
4421
|
+
roundSuppressedCount += 1;
|
|
4384
4422
|
const sequenceObservation = res.suppressedRepeat
|
|
4385
4423
|
? loopGuard.getPriorObservation(res.call.name, res.call.args) ??
|
|
4386
4424
|
res.contextOutput
|
|
@@ -4739,6 +4777,19 @@ export async function runAgentTurn(prompt, options = {}) {
|
|
|
4739
4777
|
!aborted &&
|
|
4740
4778
|
!awaitingPlanApproval &&
|
|
4741
4779
|
!governorPauseReason, actionSequenceOutcome);
|
|
4780
|
+
consecutiveSynthesizedRounds =
|
|
4781
|
+
!aborted && actionSequenceExecuted > 0 && roundSuppressedCount === actionSequenceExecuted
|
|
4782
|
+
? consecutiveSynthesizedRounds + 1
|
|
4783
|
+
: 0;
|
|
4784
|
+
if (consecutiveSynthesizedRounds >= 2) {
|
|
4785
|
+
const repeatedList = bound
|
|
4786
|
+
.map((b) => `${b.call.name} ${formatToolArgs(b.call)}`)
|
|
4787
|
+
.join("; ");
|
|
4788
|
+
outcomeState.outcome.status = "partial";
|
|
4789
|
+
await saveOutcomeState(outcomeState);
|
|
4790
|
+
moveTurn("partial", "repeated identical action cycle");
|
|
4791
|
+
return finishTurn(`Stopped an identical action cycle: consecutive rounds re-issued calls whose results are already in context (${repeatedList}). Continue from those results or take a materially different action.`, productiveSteps, "partial", ["Continue with a materially different action that can produce new evidence."], "Every call in consecutive rounds repeated already-answered work.");
|
|
4792
|
+
}
|
|
4742
4793
|
// Keep ledger system rows outside the native assistant→tool group so
|
|
4743
4794
|
// protocol repair preserves the real successful job.read body.
|
|
4744
4795
|
for (const notification of deferredResponderLedgerNotifications.splice(0)) {
|