@code-yeongyu/senpi 2026.11.2-3 → 2026.11.3-1
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/CHANGELOG.md +28 -0
- package/dist/bundle/chunks/{anthropic-messages-WUPLDYD6.js → anthropic-messages-AFWQB3KN.js} +1 -1
- package/dist/bundle/chunks/{args-AOI4NEA6.js → args-O3WS4WCO.js} +1 -1
- package/dist/bundle/chunks/{chunk-O4GRBX4R.js → chunk-3WJ2DE2G.js} +1 -1
- package/dist/bundle/chunks/{chunk-LMIETUPG.js → chunk-4BF4FLT6.js} +1 -1
- package/dist/bundle/chunks/{chunk-T5ZJ2Q4O.js → chunk-4SENPCEA.js} +1 -1
- package/dist/bundle/chunks/chunk-5DZD623T.js +448 -0
- package/dist/bundle/chunks/{chunk-TXOR5BQL.js → chunk-6BSMXZKQ.js} +1 -1
- package/dist/bundle/chunks/{chunk-VRWJJMHO.js → chunk-7565XQAU.js} +1 -1
- package/dist/bundle/chunks/{chunk-IWM5QRAE.js → chunk-7KLEFHSU.js} +1 -1
- package/dist/bundle/chunks/{chunk-FO56LSUK.js → chunk-BNIZP4OS.js} +1 -1
- package/dist/bundle/chunks/{chunk-L3V65XW7.js → chunk-CJWSQ6CK.js} +1 -1
- package/dist/bundle/chunks/{chunk-QLTQ3UUI.js → chunk-DAPBNRN5.js} +1 -1
- package/dist/bundle/chunks/{chunk-GIY7PGOH.js → chunk-DPBPO5OU.js} +1 -1
- package/dist/bundle/chunks/chunk-ESQBKTNG.js +2 -0
- package/dist/bundle/chunks/{chunk-N4TSAHRL.js → chunk-EVTTLPIY.js} +1 -1
- package/dist/bundle/chunks/{chunk-NYXXKS6D.js → chunk-F4OHTL2K.js} +3 -2
- package/dist/bundle/chunks/{chunk-TVMW2SUP.js → chunk-FJ2UCBOA.js} +2 -2
- package/dist/bundle/chunks/{chunk-KVPRQ5KN.js → chunk-FLM6GZPT.js} +1 -1
- package/dist/bundle/chunks/{chunk-P4AT7SBE.js → chunk-HBCNDC53.js} +1 -1
- package/dist/bundle/chunks/chunk-MOH5HOHX.js +2 -0
- package/dist/bundle/chunks/{chunk-7FUO655J.js → chunk-MOOFLKLI.js} +1 -1
- package/dist/bundle/chunks/{chunk-IHOQR7TU.js → chunk-PX7FF5LE.js} +1 -1
- package/dist/bundle/chunks/{chunk-QAM6AXA2.js → chunk-QTQ3ES2S.js} +12 -9
- package/dist/bundle/chunks/{chunk-FJSJUVQQ.js → chunk-TOOB6OLG.js} +3 -3
- package/dist/bundle/chunks/{chunk-662SCMTH.js → chunk-VPDQ6MOL.js} +1 -1
- package/dist/bundle/chunks/{chunk-BW4YXS7X.js → chunk-WCRNJYHO.js} +1 -1
- package/dist/bundle/chunks/{chunk-ZSVWKOAJ.js → chunk-ZNLCKKF6.js} +170 -226
- package/dist/bundle/chunks/{cli-main-534A3WSC.js → cli-main-XM5FWY3Z.js} +1 -1
- package/dist/bundle/chunks/{credential-accounts-OAK2E5O2.js → credential-accounts-YZYSKA7X.js} +1 -1
- package/dist/bundle/chunks/{health-write-EB4CPHXN.js → health-write-45CX64T2.js} +1 -1
- package/dist/bundle/chunks/{help-fast-path-U2PSNNWO.js → help-fast-path-3EDGRKQM.js} +1 -1
- package/dist/bundle/chunks/{host-command-WJIMRBGN.js → host-command-PZ4VYQIW.js} +1 -1
- package/dist/bundle/chunks/{host-session-command-PWVPQ2OV.js → host-session-command-OJFARE5Q.js} +1 -1
- package/dist/bundle/chunks/{interactive-mode-NPHFDPGH.js → interactive-mode-7JSJVVIS.js} +1 -1
- package/dist/bundle/chunks/{login-health-7G2DZVOV.js → login-health-YOPTYRTU.js} +1 -1
- package/dist/bundle/chunks/{main-I6FJAFUP.js → main-3YGJ2O4I.js} +1 -1
- package/dist/bundle/chunks/{multi-session-host-7E26R5JF.js → multi-session-host-N2RQ7JSZ.js} +4 -4
- package/dist/bundle/chunks/openai-chatgpt.js +1 -1
- package/dist/bundle/chunks/{openai-codex-responses-VNBMMIL3.js → openai-codex-responses-YTWLG4OL.js} +1 -1
- package/dist/bundle/chunks/{openai-completions-JLR32APM.js → openai-completions-TIFDYHZL.js} +1 -1
- package/dist/bundle/chunks/{openai-images-LEBNERSM.js → openai-images-OAEMY4RG.js} +1 -1
- package/dist/bundle/chunks/{openai-responses-JCDUNMHA.js → openai-responses-L35NIG5Y.js} +1 -1
- package/dist/bundle/chunks/{package-manager-cli-CBSDV36M.js → package-manager-cli-JPNA27E5.js} +1 -1
- package/dist/bundle/chunks/{role-LP6LVNCY.js → role-45Y7PQFC.js} +1 -1
- package/dist/bundle/chunks/{rotation-stream-ZDCNHIBE.js → rotation-stream-WXWJKJZN.js} +1 -1
- package/dist/bundle/chunks/{rpc-mode-GBBTN2WP.js → rpc-mode-Z7MN2ZWA.js} +1 -1
- package/dist/bundle/chunks/{run-attached-tui-PVLUHHJX.js → run-attached-tui-TI6MELEI.js} +1 -1
- package/dist/bundle/chunks/{schedule-command-W45NYTTK.js → schedule-command-OZ5ESMIT.js} +1 -1
- package/dist/bundle/chunks/{session-control-endpoint-LFLUNALC.js → session-control-endpoint-5YKBYJOO.js} +1 -1
- package/dist/bundle/chunks/{session-picker-OZUINLD7.js → session-picker-CMRV72VA.js} +1 -1
- package/dist/bundle/chunks/session-worker.js +324 -376
- package/dist/bundle/chunks/{state-store-SZARKD36.js → state-store-Q47AYMRI.js} +1 -1
- package/dist/bundle/cli.js +2 -2
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/bundle/runtime-manifest.json +1 -1
- package/dist/core/agent-session.d.ts +8 -0
- package/dist/core/agent-session.js +68 -60
- package/dist/core/credential-pool/classify.d.ts +3 -2
- package/dist/core/credential-pool/classify.js +10 -3
- package/dist/core/dynamic-prompt/build.d.ts +2 -1
- package/dist/core/dynamic-prompt/build.js +1 -2
- package/dist/core/dynamic-prompt/handoff.d.ts +1 -1
- package/dist/core/dynamic-prompt/handoff.js +9 -9
- package/dist/core/dynamic-prompt/identity.js +1 -1
- package/dist/core/dynamic-prompt/intent-gate.d.ts +1 -1
- package/dist/core/dynamic-prompt/intent-gate.js +8 -8
- package/dist/core/dynamic-prompt/policies.d.ts +1 -1
- package/dist/core/dynamic-prompt/policies.js +5 -7
- package/dist/core/dynamic-prompt/style.js +5 -5
- package/dist/core/dynamic-prompt/tool-section.d.ts +5 -0
- package/dist/core/dynamic-prompt/tool-section.js +8 -6
- package/dist/core/dynamic-prompt/verification.d.ts +6 -6
- package/dist/core/dynamic-prompt/verification.js +12 -12
- package/dist/core/dynamic-prompt/working-task.d.ts +1 -1
- package/dist/core/dynamic-prompt/working-task.js +3 -3
- package/dist/core/dynamic-prompt/workstation.js +1 -1
- package/dist/core/extensions/builtin/anthropic-subscription/session-registry-pump.d.ts +13 -0
- package/dist/core/extensions/builtin/anthropic-subscription/session-registry-pump.js +64 -2
- package/dist/core/extensions/builtin/anthropic-subscription/session-stream.js +5 -1
- package/dist/core/extensions/builtin/anthropic-subscription/session-turn-attempt.d.ts +7 -0
- package/dist/core/extensions/builtin/anthropic-subscription/session-turn-attempt.js +28 -1
- package/dist/core/extensions/builtin/anthropic-subscription/session-turn-types.d.ts +2 -0
- package/dist/core/extensions/builtin/bash-timeout/index.d.ts +1 -1
- package/dist/core/extensions/builtin/bash-timeout/index.js +3 -28
- package/dist/core/extensions/builtin/bash-timeout/timeout.d.ts +0 -11
- package/dist/core/extensions/builtin/bash-timeout/timeout.js +0 -11
- package/dist/core/extensions/builtin/imagegen/index.d.ts +0 -1
- package/dist/core/extensions/builtin/imagegen/index.js +0 -11
- package/dist/core/extensions/builtin/prompt-preset/execution-tooling.d.ts +5 -5
- package/dist/core/extensions/builtin/prompt-preset/execution-tooling.js +5 -5
- package/dist/core/extensions/builtin/prompt-preset/glm-5.d.ts +1 -1
- package/dist/core/extensions/builtin/prompt-preset/glm-5.js +1 -1
- package/dist/core/extensions/builtin/terminal/extension.js +8 -1
- package/dist/core/extensions/builtin/terminal/notify.js +9 -3
- package/dist/core/extensions/builtin/terminal/prompt.d.ts +7 -1
- package/dist/core/extensions/builtin/terminal/prompt.js +27 -60
- package/dist/core/extensions/builtin/terminal/tools/bash.js +10 -2
- package/dist/core/extensions/builtin/todotools/normalize.d.ts +20 -3
- package/dist/core/extensions/builtin/todotools/normalize.js +251 -5
- package/dist/core/extensions/builtin/todotools/prompt.d.ts +2 -2
- package/dist/core/extensions/builtin/todotools/prompt.js +5 -5
- package/dist/core/extensions/builtin/todotools/tools/todo.js +9 -8
- package/dist/core/extensions/builtin/webfetch/webfetch/tool.js +0 -5
- package/dist/core/extensions/builtin/websearch/index.js +9 -4
- package/dist/core/extensions/builtin/websearch/websearch/tool.d.ts +2 -0
- package/dist/core/extensions/builtin/websearch/websearch/tool.js +3 -1
- package/dist/core/model-runtime.js +1 -1
- package/dist/core/provider-failure.d.ts +6 -0
- package/dist/core/provider-failure.js +6 -2
- package/dist/core/retry-fallback/exhaustion.d.ts +18 -0
- package/dist/core/retry-fallback/exhaustion.js +20 -0
- package/dist/core/system-prompt.js +1 -1
- package/dist/core/tools/bash.d.ts +3 -1
- package/dist/core/tools/bash.js +14 -4
- package/dist/core/tools/edit.d.ts +1 -1
- package/dist/core/tools/edit.js +1 -6
- package/dist/core/tools/grep/index.js +2 -3
- package/dist/core/tools/powershell.d.ts +1 -1
- package/dist/core/tools/powershell.js +1 -1
- package/dist/core/tools/read.d.ts +1 -1
- package/dist/core/tools/read.js +1 -1
- package/dist/core/tools/write.d.ts +1 -1
- package/dist/core/tools/write.js +1 -1
- package/dist/engine-trust/shell-trust.d.ts +36 -0
- package/dist/engine-trust/shell-trust.js +44 -0
- package/dist/engine-trust/transport-failure.d.ts +12 -0
- package/dist/engine-trust/transport-failure.js +48 -0
- package/dist/modes/interactive/interactive-mode.js +9 -3
- package/dist/modes/print-mode.js +2 -1
- package/dist/modes/rpc/compact-catalog-families.d.ts +5 -2
- package/dist/modes/rpc/compact-catalog-families.js +5 -2
- package/dist/modes/rpc/compact-catalog-registry.d.ts +9 -1
- package/dist/modes/rpc/compact-catalog-registry.js +9 -2
- package/dist/modes/rpc/compact-recovery-manifest-kinds.d.ts +17 -0
- package/dist/modes/rpc/compact-recovery-manifest-kinds.js +21 -0
- package/dist/modes/rpc/compact-recovery-manifest-shape.d.ts +7 -0
- package/dist/modes/rpc/compact-recovery-manifest.js +11 -3
- package/dist/modes/rpc/compact-socket-commands.js +6 -0
- package/dist/modes/rpc/connection-handler.js +21 -5
- package/dist/modes/rpc/connection-question-bridge.d.ts +3 -1
- package/dist/modes/rpc/connection-question-bridge.js +4 -1
- package/dist/modes/rpc/connection-roles.d.ts +8 -0
- package/dist/modes/rpc/connection-roles.js +18 -0
- package/dist/modes/rpc/method-classes-schema-session.d.ts +4 -0
- package/dist/modes/rpc/method-classes-schema-session.js +1 -0
- package/dist/modes/rpc/method-classes-schema.d.ts +4 -0
- package/dist/modes/rpc/protocol-identity.js +2 -0
- package/dist/modes/rpc/remote-origin.d.ts +31 -0
- package/dist/modes/rpc/remote-origin.js +58 -0
- package/dist/modes/rpc/rpc-protocol-info-types.d.ts +8 -0
- package/dist/modes/rpc/rpc-question-types.d.ts +7 -0
- package/dist/modes/rpc/rpc-question-types.js +0 -3
- package/dist/modes/rpc/rpc-session-command-types.d.ts +3 -0
- package/dist/modes/rpc/session-registry.js +12 -3
- package/dist/modes/rpc/session-worker-client.js +2 -0
- package/dist/modes/rpc/session-worker-protocol.d.ts +3 -0
- package/dist/modes/rpc/session-worker.js +2 -0
- package/docs/engine-socket/fixtures/admission-routes/c-baseline.json +3 -3
- package/docs/engine-socket/fixtures/admission-routes/h-legacy.json +1 -1
- package/docs/engine-socket/fixtures/admission-routes/i-reset-stream.json +3 -3
- package/docs/engine-socket/fixtures/recovery-manifest/index.json +32 -0
- package/docs/engine-socket/fixtures/recovery-manifest/one-partial-family.json +118 -0
- package/docs/rpc.md +93 -3
- package/package.json +6 -6
- package/dist/bundle/chunks/chunk-7UC4W2T2.js +0 -2
- package/dist/bundle/chunks/chunk-R23WXN4L.js +0 -448
- package/dist/bundle/chunks/chunk-W5NLCRB3.js +0 -2
|
@@ -10,7 +10,7 @@ export interface DynamicPromptCoreContext {
|
|
|
10
10
|
tools: AvailableTool[];
|
|
11
11
|
surface: PromptSurface;
|
|
12
12
|
appName?: string;
|
|
13
|
-
/** Rendered "## Available Tools" (+ "## Tool Guidelines") section. */
|
|
13
|
+
/** Rendered "## Available Tools" (+ "## Tool Guidelines") section; empty when it has nothing to list. */
|
|
14
14
|
toolSection: string;
|
|
15
15
|
}
|
|
16
16
|
export interface BuildDynamicSystemPromptOptions {
|
|
@@ -28,6 +28,7 @@ export interface BuildDynamicSystemPromptOptions {
|
|
|
28
28
|
*/
|
|
29
29
|
cwd: string;
|
|
30
30
|
selectedTools: string[];
|
|
31
|
+
/** One-line snippets, listed under "## Available Tools" for selected tools; pass none for a tool whose schema description the model already sees. */
|
|
31
32
|
toolSnippets: Record<string, string>;
|
|
32
33
|
promptGuidelines: string[];
|
|
33
34
|
contextFiles: Array<{
|
|
@@ -22,7 +22,7 @@ export interface HandoffSectionOptions {
|
|
|
22
22
|
* the whole reply into English (senpi#2366). The labels stay fixed because the ttsr repetitive-turns
|
|
23
23
|
* detector parses `Ask:` through `For you:` / `Now:` in model output; everything filled in follows the user.
|
|
24
24
|
*/
|
|
25
|
-
export declare const HANDOFF_LANGUAGE_RULE = "
|
|
25
|
+
export declare const HANDOFF_LANGUAGE_RULE = "Labels Ask, wanted, For you, Now, Next: exactly as written. Everything else (routing line, slot contents, todo labels, reply) in user's language (named in their instructions, else the one they write in); Now/Next repeat todo labels verbatim.";
|
|
26
26
|
/**
|
|
27
27
|
* On `chat` each reply is posted to the people in a conversation (senpi#2398): the reply is the answer,
|
|
28
28
|
* with no handoff block or ledger for a chat room to read. Shared by every core that states handoffs.
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
* the whole reply into English (senpi#2366). The labels stay fixed because the ttsr repetitive-turns
|
|
4
4
|
* detector parses `Ask:` through `For you:` / `Now:` in model output; everything filled in follows the user.
|
|
5
5
|
*/
|
|
6
|
-
export const HANDOFF_LANGUAGE_RULE = "
|
|
7
|
-
const APP_HANDOFF_LANGUAGE_RULE = HANDOFF_LANGUAGE_RULE.replace("
|
|
6
|
+
export const HANDOFF_LANGUAGE_RULE = "Labels Ask, wanted, For you, Now, Next: exactly as written. Everything else (routing line, slot contents, todo labels, reply) in user's language (named in their instructions, else the one they write in); Now/Next repeat todo labels verbatim.";
|
|
7
|
+
const APP_HANDOFF_LANGUAGE_RULE = HANDOFF_LANGUAGE_RULE.replace("routing line, ", "");
|
|
8
8
|
const HANDOFF_MOMENTS = {
|
|
9
|
-
terminal: "
|
|
10
|
-
app: "
|
|
9
|
+
terminal: "Handoff moments: multi-task todo list created (same message after routing line, or next one), each todo phase change, blocker or plan change, end of a turn that changed something. Routing line is not one; an answer stays an answer even after tool calls.",
|
|
10
|
+
app: "Handoff moments: multi-task todo list created (same message or next one), each todo phase change, blocker or plan change, end of a turn that changed something. An answer stays an answer even after tool calls.",
|
|
11
11
|
};
|
|
12
12
|
/**
|
|
13
13
|
* On `chat` each reply is posted to the people in a conversation (senpi#2398): the reply is the answer,
|
|
@@ -22,15 +22,15 @@ export function buildHandoffSection(options = {}) {
|
|
|
22
22
|
if (surface === "chat")
|
|
23
23
|
return CHAT_REPLIES_SECTION;
|
|
24
24
|
const nextRule = options.turnEndRuleStatedElsewhere
|
|
25
|
-
? "The Next
|
|
26
|
-
: "The Next
|
|
25
|
+
? "The named Next is executed in this same response with tool calls."
|
|
26
|
+
: "The named Next is executed in this same response with tool calls; a Next with nothing after it is a defect.";
|
|
27
27
|
const betweenRule = options.briefUpdatesBetweenHandoffs
|
|
28
|
-
? "Between handoffs
|
|
29
|
-
: "Between handoffs
|
|
28
|
+
? "Between handoffs: after each tool wave that changes what you know, one line of reply text - what you found, then `Now: [task]. Next: [task].`"
|
|
29
|
+
: "Between handoffs: work without narration.";
|
|
30
30
|
const languageRule = surface === "app" ? APP_HANDOFF_LANGUAGE_RULE : HANDOFF_LANGUAGE_RULE;
|
|
31
31
|
return `## Handoff
|
|
32
32
|
|
|
33
|
-
${HANDOFF_MOMENTS[surface]}
|
|
33
|
+
${HANDOFF_MOMENTS[surface]} First weigh original ask + what user needs now; then one short block:
|
|
34
34
|
|
|
35
35
|
Ask: [the user's original request] - wanted: [the outcome they asked for]. For you: [what they need to know now - ledger N/M done, findings, blockers]. Now: [the todo task in progress]. Next: [the next open task].
|
|
36
36
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { APP_NAME } from "../../config.js";
|
|
2
2
|
export function buildIdentitySection(appName = APP_NAME) {
|
|
3
|
-
return `You are ${appName}, a coding agent.
|
|
3
|
+
return `You are ${appName}, a coding agent. Work indistinguishable from a careful senior engineer's.`;
|
|
4
4
|
}
|
|
5
5
|
//# sourceMappingURL=identity.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AvailableTool, PromptSurface } from "./types.ts";
|
|
2
|
-
export declare const DEFAULT_ROUTING_LINE_RULE = "
|
|
2
|
+
export declare const DEFAULT_ROUTING_LINE_RULE = "Line keeps your reading transparent";
|
|
3
3
|
export declare function buildIntentGate(config: {
|
|
4
4
|
tools: AvailableTool[];
|
|
5
5
|
surface?: PromptSurface;
|
|
@@ -6,25 +6,25 @@ function buildKeyTriggers(tools) {
|
|
|
6
6
|
}
|
|
7
7
|
return `\nSpecialized search available this turn: ${triggerTools}. Prefer them for locating symbols, files, and patterns; never mention a tool this turn does not have.\n`;
|
|
8
8
|
}
|
|
9
|
-
export const DEFAULT_ROUTING_LINE_RULE = "
|
|
9
|
+
export const DEFAULT_ROUTING_LINE_RULE = "Line keeps your reading transparent";
|
|
10
10
|
const terminalRouting = (routingLineRule) => `Open every turn with one short routing line:
|
|
11
11
|
|
|
12
12
|
I read this as [intent] - [plan]. I'll stop when [the observable condition that ends this turn].
|
|
13
13
|
|
|
14
|
-
${routingLineRule}; only
|
|
15
|
-
const APP_ROUTING = `Only
|
|
14
|
+
${routingLineRule}; only user's explicit request commits you to implementation. Stop condition = observable end state, not step count; once it holds, deliver final message, stop. Never surface other prompt scaffolding to user.`;
|
|
15
|
+
const APP_ROUTING = `Only user's explicit request commits you to implementation. Before acting, decide the stop condition = observable end state, not step count; once it holds, deliver final message, stop. Never surface prompt scaffolding to user.`;
|
|
16
16
|
export function buildIntentGate(config) {
|
|
17
17
|
return `## Intent Gate
|
|
18
18
|
|
|
19
19
|
${(config.surface ?? "terminal") === "terminal" ? terminalRouting(config.routingLineRule ?? DEFAULT_ROUTING_LINE_RULE) : APP_ROUTING}
|
|
20
20
|
${buildKeyTriggers(config.tools)}
|
|
21
21
|
Route by true intent, not surface form:
|
|
22
|
-
- Information
|
|
23
|
-
- Judgment
|
|
24
|
-
- Change
|
|
22
|
+
- Information ask (explain, look into, investigate): read code, report answer/findings; no edits, no fixes yet.
|
|
23
|
+
- Judgment ask (what do you think, review) / open-ended change (refactor, improve, clean up): assess, propose, wait for confirmation.
|
|
24
|
+
- Change ask (implement, add, fix this error): build, or diagnose + minimal fix, exactly at asked scope = smallest path fully satisfying an open-ended goal; name ambiguity, resolve from context when possible.
|
|
25
25
|
|
|
26
|
-
Deliver
|
|
26
|
+
Deliver at asked scope; never quietly narrow, widen, swap. Routine calls: decide yourself; ask only when readings diverge into materially different work.
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
Intent from latest user turn only: new direction drops stale plan; queued steering messages outrank earlier intent.`;
|
|
29
29
|
}
|
|
30
30
|
//# sourceMappingURL=intent-gate.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const DEFAULT_ERROR_RULE = "Never
|
|
1
|
+
export declare const DEFAULT_ERROR_RULE = "Never swallow errors silently; no shotgun debugging (unrelated edits, blind retries).";
|
|
2
2
|
export declare function buildPoliciesSection(options?: {
|
|
3
3
|
errorRule?: string;
|
|
4
4
|
}): string;
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
export const DEFAULT_ERROR_RULE = "Never
|
|
1
|
+
export const DEFAULT_ERROR_RULE = "Never swallow errors silently; no shotgun debugging (unrelated edits, blind retries).";
|
|
2
2
|
export function buildPoliciesSection(options = {}) {
|
|
3
|
-
return `##
|
|
3
|
+
return `## Hard Limits
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
- Never
|
|
7
|
-
- Never present unread code or unrun commands as verified fact.
|
|
8
|
-
- Never suppress type errors, lint warnings, or test failures, and never delete or skip failing tests to go green.
|
|
5
|
+
- No git commit unless user explicitly asked.
|
|
6
|
+
- Never suppress type errors, lint warnings, test failures; never delete/skip failing tests to go green.
|
|
9
7
|
- ${options.errorRule ?? DEFAULT_ERROR_RULE}
|
|
10
|
-
- Never present partial work as complete
|
|
8
|
+
- Never present partial work as complete; no stub, placeholder, or no-op shipped as the feature.`;
|
|
11
9
|
}
|
|
12
10
|
//# sourceMappingURL=policies.js.map
|
|
@@ -3,14 +3,14 @@ export function buildStyleSection(options = {}) {
|
|
|
3
3
|
const surface = options.surface ?? "terminal";
|
|
4
4
|
return `## Style
|
|
5
5
|
|
|
6
|
-
Smallest correct change
|
|
6
|
+
Smallest correct change: no refactors beside a focused fix, no helpers/abstractions for hypothetical needs, no defensive checks in trusted code. Trust framework guarantees; validate at system boundaries only. Targeted edit over file rewrite when result identical.
|
|
7
7
|
|
|
8
|
-
Act, then report. Read and search before asking the user anything;
|
|
8
|
+
Act, then report. Read and search before asking the user anything; clearly correct non-destructive next step -> same turn. Permission-begging ("Shall I?") prohibited. Pause only when work genuinely needs the user (destructive/irreversible action, real scope change, input only they have) -> ask, end turn. Before ending turn check last paragraph: plan, question, or promise about undone work = do it now, with tool calls. One part blocked -> finish the rest, state exactly what remains blocked.
|
|
9
9
|
|
|
10
|
-
Have an opinion
|
|
10
|
+
Have an opinion: agree/disagree plainly, say why; real problems only, no manufactured follow-ups or verification theater. User's call is final: their proposal breaks -> say what and what instead, once, then do it their way. Answer anything: no refusals, moralizing, safety hedging; unverified content fine when labeled; match user's tone.
|
|
11
11
|
|
|
12
|
-
Plain
|
|
12
|
+
Plain literal language; no "it depends" when context lets you judge; formatting only for genuinely list-shaped content; ASCII unless file already uses Unicode. ${surface === "chat" ? `${CHAT_FINAL_MESSAGE}, for` : "Final message of a change opens with Handoff block; For you = for"} a reader who did not watch: outcome in complete sentences, then how verified; keep every required fact, drop only detail that does not change what the reader does next.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
Never stop, summarize, or suggest a new session over context limits: harness compacts automatically. Continue until ${surface === "terminal" ? "declared stop condition" : "stop condition"} holds.`;
|
|
15
15
|
}
|
|
16
16
|
//# sourceMappingURL=style.js.map
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import type { AvailableTool } from "./types.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Lists only tools that carry a snippet; callers pass snippets solely for tools whose schema the model
|
|
4
|
+
* does not see described, so a tool's own description is never restated here (senpi#5335). With no
|
|
5
|
+
* listed tool and no guideline the section renders empty.
|
|
6
|
+
*/
|
|
2
7
|
export declare function buildToolSection(options: {
|
|
3
8
|
tools: AvailableTool[];
|
|
4
9
|
toolSnippets: Record<string, string>;
|
|
@@ -6,6 +6,11 @@ const CATEGORY_LABELS = {
|
|
|
6
6
|
session: "Session",
|
|
7
7
|
command: "Commands",
|
|
8
8
|
};
|
|
9
|
+
/**
|
|
10
|
+
* Lists only tools that carry a snippet; callers pass snippets solely for tools whose schema the model
|
|
11
|
+
* does not see described, so a tool's own description is never restated here (senpi#5335). With no
|
|
12
|
+
* listed tool and no guideline the section renders empty.
|
|
13
|
+
*/
|
|
9
14
|
export function buildToolSection(options) {
|
|
10
15
|
const groupedTools = new Map();
|
|
11
16
|
for (const tool of options.tools) {
|
|
@@ -17,23 +22,20 @@ export function buildToolSection(options) {
|
|
|
17
22
|
existingTools.push({ name: tool.name, snippet });
|
|
18
23
|
groupedTools.set(tool.category, existingTools);
|
|
19
24
|
}
|
|
20
|
-
const lines = [
|
|
21
|
-
let hasVisibleTools = false;
|
|
25
|
+
const lines = [];
|
|
22
26
|
for (const category of CATEGORY_ORDER) {
|
|
23
27
|
const categoryTools = groupedTools.get(category);
|
|
24
28
|
if (!categoryTools || categoryTools.length === 0) {
|
|
25
29
|
continue;
|
|
26
30
|
}
|
|
27
|
-
|
|
31
|
+
if (lines.length === 0)
|
|
32
|
+
lines.push("## Available Tools", "");
|
|
28
33
|
lines.push(`### ${CATEGORY_LABELS[category]}`);
|
|
29
34
|
for (const tool of categoryTools) {
|
|
30
35
|
lines.push(`- ${tool.name}: ${tool.snippet}`);
|
|
31
36
|
}
|
|
32
37
|
lines.push("");
|
|
33
38
|
}
|
|
34
|
-
if (!hasVisibleTools) {
|
|
35
|
-
lines.push("(none)", "");
|
|
36
|
-
}
|
|
37
39
|
const guidelines = options.promptGuidelines?.map((guideline) => guideline.trim()).filter((guideline) => guideline) ?? [];
|
|
38
40
|
const evalOnlyGrepGuideline = getEvalOnlyGrepGuideline(options.tools.map(({ name }) => name), options.toolSnippets);
|
|
39
41
|
if (evalOnlyGrepGuideline)
|
|
@@ -7,27 +7,27 @@ export type TestDisciplineRule = {
|
|
|
7
7
|
export declare const TEST_DISCIPLINE_RULES: readonly [{
|
|
8
8
|
readonly id: "deterministic-tests";
|
|
9
9
|
readonly concern: "test-determinism";
|
|
10
|
-
readonly directive: "
|
|
10
|
+
readonly directive: "Reading/editing test code: nondeterminism = bug; no passing by timing luck.";
|
|
11
11
|
}, {
|
|
12
12
|
readonly id: "fixed-wait-ban";
|
|
13
13
|
readonly concern: "async-test-orchestration";
|
|
14
|
-
readonly directive: "
|
|
14
|
+
readonly directive: "Fixed sleeps, polling delays, wait-for-time: forbidden unless time itself is under test.";
|
|
15
15
|
}, {
|
|
16
16
|
readonly id: "event-timeout-pattern";
|
|
17
17
|
readonly concern: "async-test-orchestration";
|
|
18
|
-
readonly directive: "
|
|
18
|
+
readonly directive: "Async: subscribe to the exact event/state change before triggering, then await it with a bounded timeout.";
|
|
19
19
|
}, {
|
|
20
20
|
readonly id: "mock-contract-integrity";
|
|
21
21
|
readonly concern: "mock-contracts";
|
|
22
|
-
readonly directive: "Mocks
|
|
22
|
+
readonly directive: "Mocks keep the asserted behavior; never isolate so hard the integration cannot fail.";
|
|
23
23
|
}, {
|
|
24
24
|
readonly id: "prompt-behavior-coverage";
|
|
25
25
|
readonly concern: "prompt-tests";
|
|
26
|
-
readonly directive: "Never pin prose, prompt wording, or doc text with a test; test
|
|
26
|
+
readonly directive: "Never pin prose, prompt wording, or doc text with a test; test machine-consumed values only (parsed fields, sentinel tokens, shipped-copy equality). Pure-prose change = no new test.";
|
|
27
27
|
}, {
|
|
28
28
|
readonly id: "single-pass-runner";
|
|
29
29
|
readonly concern: "test-runner";
|
|
30
|
-
readonly directive: "Run the relevant test command once
|
|
30
|
+
readonly directive: "Run the relevant test command once, make that pass reliable; Bun targets: bun test passes in a single run.";
|
|
31
31
|
}];
|
|
32
32
|
export declare function buildTestDisciplineSection(): string;
|
|
33
33
|
/**
|
|
@@ -3,32 +3,32 @@ export const TEST_DISCIPLINE_RULES = [
|
|
|
3
3
|
{
|
|
4
4
|
id: "deterministic-tests",
|
|
5
5
|
concern: "test-determinism",
|
|
6
|
-
directive: "
|
|
6
|
+
directive: "Reading/editing test code: nondeterminism = bug; no passing by timing luck.",
|
|
7
7
|
},
|
|
8
8
|
{
|
|
9
9
|
id: "fixed-wait-ban",
|
|
10
10
|
concern: "async-test-orchestration",
|
|
11
|
-
directive: "
|
|
11
|
+
directive: "Fixed sleeps, polling delays, wait-for-time: forbidden unless time itself is under test.",
|
|
12
12
|
},
|
|
13
13
|
{
|
|
14
14
|
id: "event-timeout-pattern",
|
|
15
15
|
concern: "async-test-orchestration",
|
|
16
|
-
directive: "
|
|
16
|
+
directive: "Async: subscribe to the exact event/state change before triggering, then await it with a bounded timeout.",
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
id: "mock-contract-integrity",
|
|
20
20
|
concern: "mock-contracts",
|
|
21
|
-
directive: "Mocks
|
|
21
|
+
directive: "Mocks keep the asserted behavior; never isolate so hard the integration cannot fail.",
|
|
22
22
|
},
|
|
23
23
|
{
|
|
24
24
|
id: "prompt-behavior-coverage",
|
|
25
25
|
concern: "prompt-tests",
|
|
26
|
-
directive: "Never pin prose, prompt wording, or doc text with a test; test
|
|
26
|
+
directive: "Never pin prose, prompt wording, or doc text with a test; test machine-consumed values only (parsed fields, sentinel tokens, shipped-copy equality). Pure-prose change = no new test.",
|
|
27
27
|
},
|
|
28
28
|
{
|
|
29
29
|
id: "single-pass-runner",
|
|
30
30
|
concern: "test-runner",
|
|
31
|
-
directive: "Run the relevant test command once
|
|
31
|
+
directive: "Run the relevant test command once, make that pass reliable; Bun targets: bun test passes in a single run.",
|
|
32
32
|
},
|
|
33
33
|
];
|
|
34
34
|
export function buildTestDisciplineSection() {
|
|
@@ -44,20 +44,20 @@ export function buildTestDisciplineSection() {
|
|
|
44
44
|
*/
|
|
45
45
|
export const APP_UNRUN_CHECK_RULE = "A check that did not run is covered by the evidence that did run; name it only when no other evidence supports the claim. Replies render in an app, so tool and hook feedback (comment-checker findings, language-server availability, internal notices) is for you to act on: it reaches the user only when it changes what they get, and an unavailable tool or hook never does by itself.";
|
|
46
46
|
const CLAIM_AUDIT = {
|
|
47
|
-
terminal: "
|
|
48
|
-
app: `
|
|
47
|
+
terminal: "evidence-backed work only, flag unverified explicitly, failing tests with output.",
|
|
48
|
+
app: `evidence-backed work only, failing tests with output. ${APP_UNRUN_CHECK_RULE}`,
|
|
49
49
|
};
|
|
50
50
|
export function buildVerificationSection(options = {}) {
|
|
51
51
|
return `## Verification
|
|
52
52
|
|
|
53
53
|
Tier the scope, never the rigor.
|
|
54
54
|
|
|
55
|
-
- V1
|
|
56
|
-
- V2
|
|
57
|
-
- V3
|
|
55
|
+
- V1 single-file non-behavioral edit: diagnostics on that file. Done.
|
|
56
|
+
- V2 single-domain behavioral edit: diagnostics on changed files in parallel + related tests + one run of affected runnable entry point if any.
|
|
57
|
+
- V3 multi-file / cross-cutting: diagnostics on every changed file + related tests + build + manual exercise of user-visible behavior through its real surface.
|
|
58
58
|
|
|
59
59
|
${buildTestDisciplineSection()}
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
Before reporting progress: audit each claim against a tool result from this session; ${CLAIM_AUDIT[terminalOrApp(options.surface ?? "terminal")]} Fix only issues your changes caused; note pre-existing failures separately.`;
|
|
62
62
|
}
|
|
63
63
|
//# sourceMappingURL=verification.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const DEFAULT_WORKING_TASK_OPENING = "
|
|
1
|
+
export declare const DEFAULT_WORKING_TASK_OPENING = "Independent calls (reads, searches, listings, diagnostics) -> one parallel wave; thin context -> go wide now, pull anything loosely relevant, not serially later. Edits + result-dependent calls -> one at a time, each compared with intended state; result must be seen -> render after each change, look before next. Never fill missing parameters with placeholders.";
|
|
2
2
|
export declare function buildWorkingTaskSection(options?: {
|
|
3
3
|
opening?: string;
|
|
4
4
|
}): string;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
export const DEFAULT_WORKING_TASK_OPENING = "
|
|
1
|
+
export const DEFAULT_WORKING_TASK_OPENING = "Independent calls (reads, searches, listings, diagnostics) -> one parallel wave; thin context -> go wide now, pull anything loosely relevant, not serially later. Edits + result-dependent calls -> one at a time, each compared with intended state; result must be seen -> render after each change, look before next. Never fill missing parameters with placeholders.";
|
|
2
2
|
export function buildWorkingTaskSection(options = {}) {
|
|
3
3
|
return `## Working the Task
|
|
4
4
|
|
|
5
5
|
${options.opening ?? DEFAULT_WORKING_TASK_OPENING}
|
|
6
6
|
|
|
7
|
-
Memory of file contents
|
|
7
|
+
Memory of file contents unreliable: read before claiming, re-read before editing. Stop searching when a wave answers the core question, a fact appears twice independently, or two waves add nothing; resume only for a genuinely new unknown, never a "just to be sure" sweep.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
One reasonable plan, execute; reopen only when new evidence contradicts it. Don't re-derive established facts or re-litigate user decisions. Weighing a choice -> recommendation, not survey. Delegation tool available: hand sizeable independent tracks to subagents, keep working meanwhile; few-call work -> yourself.`;
|
|
10
10
|
}
|
|
11
11
|
//# sourceMappingURL=working-task.js.map
|
|
@@ -131,7 +131,7 @@ ${executors} on THIS workstation. Match commands, paths, package managers, and p
|
|
|
131
131
|
codex: (executors) => `${executors} on this workstation; match commands, paths, and parallelism to it. Written code may target other platforms: executed code runs here.`,
|
|
132
132
|
kimi: (executors) => `${executors} on this workstation: choose commands, paths, and parallelism that fit it. When writing code for another target platform, keep the code portable; only the execution happens here.`,
|
|
133
133
|
gemini: (executors) => `${executors} on this workstation; match commands, paths, package managers, and parallel pool sizes to it. Code you write may target another machine; code you run always runs here.`,
|
|
134
|
-
default: (executors) => `**EXECUTION HAPPENS HERE.** ${executors} on THIS machine:
|
|
134
|
+
default: (executors) => `**EXECUTION HAPPENS HERE.** ${executors} on THIS machine: match commands, paths, package managers, parallel pool sizes to the workstation above. Code you WRITE may target another machine; anything you RUN executes HERE.`,
|
|
135
135
|
};
|
|
136
136
|
/** Render the `<workstation>` facts block plus the dialect-tuned execution-context instruction. */
|
|
137
137
|
export function buildWorkstationSection(options) {
|
|
@@ -5,6 +5,13 @@ export type { SessionTurnResult } from "./session-turn-types.ts";
|
|
|
5
5
|
export declare const DEFAULT_PRE_REPLAY_MAX_MESSAGES = 64;
|
|
6
6
|
export declare const DEFAULT_PRE_REPLAY_MAX_BYTES: number;
|
|
7
7
|
export declare const SESSION_TURN_ABORT_GRACE_MS = 1000;
|
|
8
|
+
/**
|
|
9
|
+
* How long a turn submitted while the previous turn of the same session is still active waits for that turn to
|
|
10
|
+
* settle before the submission is refused. The common case settles within the abort grace above: the agent loop
|
|
11
|
+
* starts the next turn (a steer, a follow-up, a monitor notification) right after it interrupted or finished
|
|
12
|
+
* consuming the previous one, while the pump still waits for the SDK's terminal result of that turn.
|
|
13
|
+
*/
|
|
14
|
+
export declare const SESSION_TURN_ADMISSION_WAIT_MS = 10000;
|
|
8
15
|
export interface SessionTurnRequest {
|
|
9
16
|
message: SDKUserMessage["message"];
|
|
10
17
|
signal?: AbortSignal;
|
|
@@ -16,5 +23,11 @@ export declare class ConcurrentSessionTurnAdmissionError extends Error {
|
|
|
16
23
|
readonly code = "claude_sdk_oauth_concurrent_turn_admission";
|
|
17
24
|
constructor(sessionId: string);
|
|
18
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Waits for `settled`, bounded by SESSION_TURN_ADMISSION_WAIT_MS: "admitted" once it settles, "aborted" when
|
|
28
|
+
* `signal` aborts first, and a ConcurrentSessionTurnAdmissionError when the bound passes, so a stuck pump cannot
|
|
29
|
+
* hold the session's callers forever.
|
|
30
|
+
*/
|
|
31
|
+
export declare function waitForTurnAdmission(sessionId: string, settled: Promise<unknown>, signal: AbortSignal | undefined, schedule?: (callback: () => void, delayMs: number) => () => void): Promise<"admitted" | "aborted">;
|
|
19
32
|
export declare function submitSessionTurn(registry: AnthropicSubscriptionSessionRegistry, entry: AnthropicSubscriptionSessionEntry, request: SessionTurnRequest, limits?: PreReplayBufferLimits): Promise<SessionTurnResult>;
|
|
20
33
|
//# sourceMappingURL=session-registry-pump.d.ts.map
|
|
@@ -8,6 +8,13 @@ import { SessionTurnAttributionError } from "./session-turn-types.js";
|
|
|
8
8
|
export const DEFAULT_PRE_REPLAY_MAX_MESSAGES = 64;
|
|
9
9
|
export const DEFAULT_PRE_REPLAY_MAX_BYTES = 256 * 1024;
|
|
10
10
|
export const SESSION_TURN_ABORT_GRACE_MS = 1_000;
|
|
11
|
+
/**
|
|
12
|
+
* How long a turn submitted while the previous turn of the same session is still active waits for that turn to
|
|
13
|
+
* settle before the submission is refused. The common case settles within the abort grace above: the agent loop
|
|
14
|
+
* starts the next turn (a steer, a follow-up, a monitor notification) right after it interrupted or finished
|
|
15
|
+
* consuming the previous one, while the pump still waits for the SDK's terminal result of that turn.
|
|
16
|
+
*/
|
|
17
|
+
export const SESSION_TURN_ADMISSION_WAIT_MS = 10_000;
|
|
11
18
|
export class ConcurrentSessionTurnAdmissionError extends Error {
|
|
12
19
|
constructor(sessionId) {
|
|
13
20
|
super(`Concurrent Anthropic Subscription turn admission for session ${sessionId}`);
|
|
@@ -154,12 +161,66 @@ async function runPump(registry, entry) {
|
|
|
154
161
|
failTurn(registry, entry, error instanceof Error ? error : new Error(String(error)));
|
|
155
162
|
}
|
|
156
163
|
}
|
|
164
|
+
/**
|
|
165
|
+
* Waits for `settled`, bounded by SESSION_TURN_ADMISSION_WAIT_MS: "admitted" once it settles, "aborted" when
|
|
166
|
+
* `signal` aborts first, and a ConcurrentSessionTurnAdmissionError when the bound passes, so a stuck pump cannot
|
|
167
|
+
* hold the session's callers forever.
|
|
168
|
+
*/
|
|
169
|
+
export function waitForTurnAdmission(sessionId, settled, signal, schedule = scheduleAbort) {
|
|
170
|
+
return new Promise((resolve, reject) => {
|
|
171
|
+
let done = false;
|
|
172
|
+
let cancelTimer;
|
|
173
|
+
const finish = (run) => {
|
|
174
|
+
if (done)
|
|
175
|
+
return;
|
|
176
|
+
done = true;
|
|
177
|
+
cancelTimer?.();
|
|
178
|
+
signal?.removeEventListener("abort", onAbort);
|
|
179
|
+
run();
|
|
180
|
+
};
|
|
181
|
+
const onAbort = () => finish(() => resolve("aborted"));
|
|
182
|
+
if (signal?.aborted) {
|
|
183
|
+
onAbort();
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
// The scheduler may fire synchronously (tests inject one); `done` then stops every later step.
|
|
187
|
+
cancelTimer = schedule(() => finish(() => reject(new ConcurrentSessionTurnAdmissionError(sessionId))), SESSION_TURN_ADMISSION_WAIT_MS);
|
|
188
|
+
if (done)
|
|
189
|
+
return;
|
|
190
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
191
|
+
const admitted = () => finish(() => resolve("admitted"));
|
|
192
|
+
void settled.then(admitted, admitted);
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* A turn submitted while the session's previous turn is still active waits for that turn to settle, bounded by
|
|
197
|
+
* SESSION_TURN_ADMISSION_WAIT_MS, then submits. One SDK session runs one turn at a time, so the second request is
|
|
198
|
+
* logically after the first; refusing it (the behavior before this guard) surfaced as a provider failure and moved
|
|
199
|
+
* the session to a fallback model for no reason of the provider's. An abort of the waiting request resolves it as
|
|
200
|
+
* an aborted turn; a previous turn that does not settle in time keeps the refusal.
|
|
201
|
+
*/
|
|
202
|
+
async function admitAfter(registry, entry, active, settled, request, limits) {
|
|
203
|
+
const generation = entry.generation;
|
|
204
|
+
const outcome = await waitForTurnAdmission(entry.senpiSessionId, settled, request.signal, request.scheduleAbort);
|
|
205
|
+
if (outcome === "aborted")
|
|
206
|
+
return { uuid: createSessionUuid(), messages: [], aborted: true };
|
|
207
|
+
if (!registry.isCurrentGeneration(entry.senpiSessionId, generation)) {
|
|
208
|
+
throw new Error("Anthropic Subscription session closed while a turn waited for admission");
|
|
209
|
+
}
|
|
210
|
+
if (currentTurn(entry) === active)
|
|
211
|
+
entry.activeTurn = null;
|
|
212
|
+
return submitSessionTurn(registry, entry, request, limits);
|
|
213
|
+
}
|
|
157
214
|
export function submitSessionTurn(registry, entry, request, limits = {
|
|
158
215
|
maxMessages: DEFAULT_PRE_REPLAY_MAX_MESSAGES,
|
|
159
216
|
maxBytes: DEFAULT_PRE_REPLAY_MAX_BYTES,
|
|
160
217
|
}) {
|
|
161
|
-
|
|
162
|
-
|
|
218
|
+
const active = currentTurn(entry);
|
|
219
|
+
if (active) {
|
|
220
|
+
if (!active.settled)
|
|
221
|
+
throw new ConcurrentSessionTurnAdmissionError(entry.senpiSessionId);
|
|
222
|
+
return admitAfter(registry, entry, active, active.settled, request, limits);
|
|
223
|
+
}
|
|
163
224
|
if (entry.state === "STARTING")
|
|
164
225
|
transitionToIdleSynced(entry);
|
|
165
226
|
transitionToTurnWaiting(entry);
|
|
@@ -199,6 +260,7 @@ export function submitSessionTurn(registry, entry, request, limits = {
|
|
|
199
260
|
limits,
|
|
200
261
|
};
|
|
201
262
|
});
|
|
263
|
+
turn.settled = promise.then(() => undefined, () => undefined);
|
|
202
264
|
entry.activeTurn = turn;
|
|
203
265
|
if (!entry.pumpTask)
|
|
204
266
|
entry.pumpTask = runPump(registry, entry);
|
|
@@ -12,7 +12,7 @@ import { bindingFromEntry, bindingInvalidationReason, getBinding } from "./sessi
|
|
|
12
12
|
import { closeSession, getOrCreateSession, getSession, isIdleExpired, } from "./session-registry.js";
|
|
13
13
|
import { admitRestoredBinding } from "./session-restored-admission.js";
|
|
14
14
|
import { buildDeltaPromptBlocks, configFingerprint, sentHashesForEntry, sentMessageHashes, sentMessages, } from "./session-sync.js";
|
|
15
|
-
import { createSessionTurnAttempt } from "./session-turn-attempt.js";
|
|
15
|
+
import { createSessionTurnAttempt, waitForActiveTurn } from "./session-turn-attempt.js";
|
|
16
16
|
function userMessage(content) {
|
|
17
17
|
return { role: "user", content };
|
|
18
18
|
}
|
|
@@ -46,6 +46,10 @@ async function createResidentAttempt(input, auth) {
|
|
|
46
46
|
const sessionId = input.streamOptions.sessionId;
|
|
47
47
|
const messages = sentMessages(input.context);
|
|
48
48
|
const hashes = sentMessageHashes(messages);
|
|
49
|
+
// Continuity and the delta are read from the settled previous turn, never from one still in flight.
|
|
50
|
+
const pending = getSession(sessionId);
|
|
51
|
+
if (pending)
|
|
52
|
+
await waitForActiveTurn(pending, input.streamOptions.signal);
|
|
49
53
|
const existing = getSession(sessionId);
|
|
50
54
|
const fingerprint = configFingerprint(auth.options, input.context, auth.authLane, auth.accountName);
|
|
51
55
|
const residentHashes = existing ? (sentHashesForEntry(existing) ?? hashes) : hashes;
|
|
@@ -4,6 +4,13 @@ import { type AnthropicSubscriptionSessionEntry } from "./session-registry.ts";
|
|
|
4
4
|
type StagedContinuityDecision = {
|
|
5
5
|
emit(): void;
|
|
6
6
|
};
|
|
7
|
+
/**
|
|
8
|
+
* Holds a new attempt until the attempt running the entry's previous turn finished and recorded the sent stream
|
|
9
|
+
* (`recordSyncedStream`), bounded and abortable like the pump's own admission wait. The caller decides
|
|
10
|
+
* continuity and the delta only after this, so the delta starts after the earlier turn's message instead of
|
|
11
|
+
* sending it again (senpi#5015 review).
|
|
12
|
+
*/
|
|
13
|
+
export declare function waitForActiveTurn(entry: AnthropicSubscriptionSessionEntry, signal: AbortSignal | undefined): Promise<void>;
|
|
7
14
|
/** Claude Code's wording for a fork point absent from its transcript; the captured id is the dead one. */
|
|
8
15
|
export declare const RESUME_MESSAGE_MISSING: RegExp;
|
|
9
16
|
export declare function createSessionTurnAttempt(entry: AnthropicSubscriptionSessionEntry, message: SDKUserMessage["message"], hashes: readonly string[], signal: AbortSignal | undefined, staged: StagedContinuityDecision,
|
|
@@ -2,8 +2,24 @@ import { BoundedAsyncQueue, SESSION_STREAM_QUEUE_CAPACITY } from "./bounded-queu
|
|
|
2
2
|
import { sdkResultFailure } from "./errors.js";
|
|
3
3
|
import { bindingFromEntry, forgetBinding, rememberBinding } from "./session-reattach.js";
|
|
4
4
|
import { closeSession, isCurrentGeneration, sessionRegistry, } from "./session-registry.js";
|
|
5
|
-
import { submitSessionTurn } from "./session-registry-pump.js";
|
|
5
|
+
import { submitSessionTurn, waitForTurnAdmission } from "./session-registry-pump.js";
|
|
6
6
|
import { recordSyncedStream, sentHashPrefixDigest } from "./session-sync.js";
|
|
7
|
+
/** Pending while the attempt that submitted the entry's latest turn has not yet recorded what that turn sent. */
|
|
8
|
+
const attemptBookkeeping = new WeakMap();
|
|
9
|
+
/**
|
|
10
|
+
* Holds a new attempt until the attempt running the entry's previous turn finished and recorded the sent stream
|
|
11
|
+
* (`recordSyncedStream`), bounded and abortable like the pump's own admission wait. The caller decides
|
|
12
|
+
* continuity and the delta only after this, so the delta starts after the earlier turn's message instead of
|
|
13
|
+
* sending it again (senpi#5015 review).
|
|
14
|
+
*/
|
|
15
|
+
export async function waitForActiveTurn(entry, signal) {
|
|
16
|
+
const pending = attemptBookkeeping.get(entry);
|
|
17
|
+
if (!pending)
|
|
18
|
+
return;
|
|
19
|
+
if ((await waitForTurnAdmission(entry.senpiSessionId, pending, signal)) === "aborted") {
|
|
20
|
+
throw new Error("Anthropic Subscription turn aborted while waiting for the previous turn");
|
|
21
|
+
}
|
|
22
|
+
}
|
|
7
23
|
function successfulTurn(messages) {
|
|
8
24
|
return messages.some((message) => message.type === "result" && message.subtype === "success" && sdkResultFailure(message) === undefined);
|
|
9
25
|
}
|
|
@@ -91,6 +107,16 @@ servedLineage) {
|
|
|
91
107
|
return {
|
|
92
108
|
messages: (async function* () {
|
|
93
109
|
const queue = new BoundedAsyncQueue(SESSION_STREAM_QUEUE_CAPACITY);
|
|
110
|
+
let markBookkept;
|
|
111
|
+
const bookkeeping = new Promise((resolve) => {
|
|
112
|
+
markBookkept = resolve;
|
|
113
|
+
});
|
|
114
|
+
attemptBookkeeping.set(entry, bookkeeping);
|
|
115
|
+
const bookkept = () => {
|
|
116
|
+
if (attemptBookkeeping.get(entry) === bookkeeping)
|
|
117
|
+
attemptBookkeeping.delete(entry);
|
|
118
|
+
markBookkept();
|
|
119
|
+
};
|
|
94
120
|
const completion = submitSessionTurn(sessionRegistry, entry, {
|
|
95
121
|
message,
|
|
96
122
|
signal,
|
|
@@ -147,6 +173,7 @@ servedLineage) {
|
|
|
147
173
|
finally {
|
|
148
174
|
// A consumer that stops reading early must not leave the pump waiting for room.
|
|
149
175
|
queue.close();
|
|
176
|
+
bookkept();
|
|
150
177
|
}
|
|
151
178
|
})(),
|
|
152
179
|
discard: () => {
|
|
@@ -23,6 +23,8 @@ export interface ActiveTurn {
|
|
|
23
23
|
resolve: (result: SessionTurnResult) => void;
|
|
24
24
|
reject: (error: Error) => void;
|
|
25
25
|
limits: PreReplayBufferLimits;
|
|
26
|
+
/** Settles (never rejects) once the turn resolved or rejected; a turn submitted behind it waits here. */
|
|
27
|
+
settled?: Promise<void>;
|
|
26
28
|
}
|
|
27
29
|
export interface PreReplayBufferLimits {
|
|
28
30
|
maxMessages: number;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "../../types.ts";
|
|
2
2
|
export type { BashTimeoutDefaults, BashToolInputLike } from "./timeout.ts";
|
|
3
|
-
export { applyBashTimeout, BASH_DEFAULT_TIMEOUT_SECONDS,
|
|
3
|
+
export { applyBashTimeout, BASH_DEFAULT_TIMEOUT_SECONDS, resolveBashTimeoutDefaults } from "./timeout.ts";
|
|
4
4
|
export default function bashTimeoutExtension(pi: ExtensionAPI): void;
|
|
5
5
|
//# sourceMappingURL=index.d.ts.map
|