@code-yeongyu/senpi 2026.9.6 → 2026.9.7

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.
Files changed (67) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/core/agent-session.d.ts +12 -2
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +50 -26
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/compaction/stuck-overflow.d.ts +11 -0
  7. package/dist/core/compaction/stuck-overflow.d.ts.map +1 -0
  8. package/dist/core/compaction/stuck-overflow.js +17 -0
  9. package/dist/core/compaction/stuck-overflow.js.map +1 -0
  10. package/dist/core/extensions/builtin/goal/continuation-recovery.d.ts +1 -0
  11. package/dist/core/extensions/builtin/goal/continuation-recovery.d.ts.map +1 -1
  12. package/dist/core/extensions/builtin/goal/continuation-recovery.js +2 -0
  13. package/dist/core/extensions/builtin/goal/continuation-recovery.js.map +1 -1
  14. package/dist/core/extensions/builtin/goal/continuation.d.ts +3 -1
  15. package/dist/core/extensions/builtin/goal/continuation.d.ts.map +1 -1
  16. package/dist/core/extensions/builtin/goal/continuation.js +4 -0
  17. package/dist/core/extensions/builtin/goal/continuation.js.map +1 -1
  18. package/dist/core/extensions/builtin/goal/lifecycle-helpers.d.ts +3 -0
  19. package/dist/core/extensions/builtin/goal/lifecycle-helpers.d.ts.map +1 -1
  20. package/dist/core/extensions/builtin/goal/lifecycle-helpers.js +14 -3
  21. package/dist/core/extensions/builtin/goal/lifecycle-helpers.js.map +1 -1
  22. package/dist/core/extensions/builtin/goal/monitor-continuation.d.ts.map +1 -1
  23. package/dist/core/extensions/builtin/goal/monitor-continuation.js +2 -1
  24. package/dist/core/extensions/builtin/goal/monitor-continuation.js.map +1 -1
  25. package/dist/core/extensions/builtin/permission-system/external-dir.d.ts.map +1 -1
  26. package/dist/core/extensions/builtin/permission-system/external-dir.js +42 -13
  27. package/dist/core/extensions/builtin/permission-system/external-dir.js.map +1 -1
  28. package/dist/core/package-identity.d.ts +12 -0
  29. package/dist/core/package-identity.d.ts.map +1 -0
  30. package/dist/core/package-identity.js +57 -0
  31. package/dist/core/package-identity.js.map +1 -0
  32. package/dist/core/resource-loader.d.ts +0 -2
  33. package/dist/core/resource-loader.d.ts.map +1 -1
  34. package/dist/core/resource-loader.js +8 -60
  35. package/dist/core/resource-loader.js.map +1 -1
  36. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  37. package/dist/modes/interactive/interactive-mode.js +1 -0
  38. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  39. package/docs/compaction-guide.md +2 -2
  40. package/docs/compaction.md +2 -2
  41. package/docs/rpc.md +1 -1
  42. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +12 -0
  43. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  44. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/jl/kernel.ts +2 -2
  45. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/inline-worker.ts +2 -2
  46. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-startup.ts +2 -2
  47. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/transport.ts +2 -2
  48. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/rb/kernel.ts +2 -2
  49. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/runtime-asset.ts +40 -14
  50. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  51. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  52. package/node_modules/@earendil-works/pi-ai/dist/providers/data/amazon-bedrock.json +1 -1
  53. package/node_modules/@earendil-works/pi-ai/dist/providers/data/azure-openai-responses.json +1 -1
  54. package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-ai-gateway.json +1 -1
  55. package/node_modules/@earendil-works/pi-ai/dist/providers/data/github-copilot.json +1 -1
  56. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openai-codex.json +1 -1
  57. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openai.json +1 -1
  58. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode-go.json +1 -1
  59. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode.json +1 -1
  60. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opengateway.json +1 -1
  61. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  62. package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
  63. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  64. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  65. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  66. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  67. package/package.json +7 -7
@@ -0,0 +1,11 @@
1
+ import { type AssistantMessage } from "@earendil-works/pi-ai";
2
+ /**
3
+ * A turn that context size prevented from producing anything usable: the
4
+ * provider rejected the request outright, or it accepted the request but had no
5
+ * room left for output (truncating providers report a zero-output "length" stop).
6
+ * Such a session cannot move by re-sending the same context, so its recovery
7
+ * runs even when the user switched proactive auto-compaction off (#1422).
8
+ * A completed answer whose usage merely exceeds the window is not stuck.
9
+ */
10
+ export declare function isTurnStuckOnContextOverflow(message: AssistantMessage, contextWindow: number): boolean;
11
+ //# sourceMappingURL=stuck-overflow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stuck-overflow.d.ts","sourceRoot":"","sources":["../../../src/core/compaction/stuck-overflow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,gBAAgB,EAAqB,MAAM,uBAAuB,CAAC;AAEjF;;;;;;;GAOG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAItG"}
@@ -0,0 +1,17 @@
1
+ import { isContextOverflow } from "@earendil-works/pi-ai";
2
+ /**
3
+ * A turn that context size prevented from producing anything usable: the
4
+ * provider rejected the request outright, or it accepted the request but had no
5
+ * room left for output (truncating providers report a zero-output "length" stop).
6
+ * Such a session cannot move by re-sending the same context, so its recovery
7
+ * runs even when the user switched proactive auto-compaction off (#1422).
8
+ * A completed answer whose usage merely exceeds the window is not stuck.
9
+ */
10
+ export function isTurnStuckOnContextOverflow(message, contextWindow) {
11
+ if (!isContextOverflow(message, contextWindow))
12
+ return false;
13
+ if (message.stopReason === "error")
14
+ return true;
15
+ return message.stopReason === "length" && message.usage.output === 0;
16
+ }
17
+ //# sourceMappingURL=stuck-overflow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stuck-overflow.js","sourceRoot":"","sources":["../../../src/core/compaction/stuck-overflow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAEjF;;;;;;;GAOG;AACH,MAAM,UAAU,4BAA4B,CAAC,OAAyB,EAAE,aAAqB;IAC5F,IAAI,CAAC,iBAAiB,CAAC,OAAO,EAAE,aAAa,CAAC;QAAE,OAAO,KAAK,CAAC;IAC7D,IAAI,OAAO,CAAC,UAAU,KAAK,OAAO;QAAE,OAAO,IAAI,CAAC;IAChD,OAAO,OAAO,CAAC,UAAU,KAAK,QAAQ,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC;AACtE,CAAC","sourcesContent":["import { type AssistantMessage, isContextOverflow } from \"@earendil-works/pi-ai\";\n\n/**\n * A turn that context size prevented from producing anything usable: the\n * provider rejected the request outright, or it accepted the request but had no\n * room left for output (truncating providers report a zero-output \"length\" stop).\n * Such a session cannot move by re-sending the same context, so its recovery\n * runs even when the user switched proactive auto-compaction off (#1422).\n * A completed answer whose usage merely exceeds the window is not stuck.\n */\nexport function isTurnStuckOnContextOverflow(message: AssistantMessage, contextWindow: number): boolean {\n\tif (!isContextOverflow(message, contextWindow)) return false;\n\tif (message.stopReason === \"error\") return true;\n\treturn message.stopReason === \"length\" && message.usage.output === 0;\n}\n"]}
@@ -3,6 +3,7 @@ export declare const REPETITION_BLOCKED_REASON = "repeated assistant output";
3
3
  export declare const LENGTH_EXHAUSTED_BLOCKED_REASON = "output truncation repeated";
4
4
  export declare const UNATTENDED_CONTINUATION_BLOCKED_REASON = "unattended continuation limit reached";
5
5
  export declare const PROVIDER_ERROR_BLOCKED_REASON = "provider error ended the turn (retries exhausted)";
6
+ export declare const CONTEXT_OVERFLOW_BLOCKED_REASON = "context overflow ended the turn (compaction did not recover)";
6
7
  export declare function isMechanicalContinuationBlock(blockedReason: string | undefined): boolean;
7
8
  export declare function continuationCapRecoveryHint(blockedReason: string): string;
8
9
  //# sourceMappingURL=continuation-recovery.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"continuation-recovery.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/continuation-recovery.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,+BAA+B,6BAA6B,CAAC;AAC1E,eAAO,MAAM,yBAAyB,8BAA8B,CAAC;AACrE,eAAO,MAAM,+BAA+B,+BAA+B,CAAC;AAC5E,eAAO,MAAM,sCAAsC,0CAA0C,CAAC;AAC9F,eAAO,MAAM,6BAA6B,sDAAsD,CAAC;AAiBjG,wBAAgB,6BAA6B,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAGxF;AAED,wBAAgB,2BAA2B,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAGzE"}
1
+ {"version":3,"file":"continuation-recovery.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/continuation-recovery.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,+BAA+B,6BAA6B,CAAC;AAC1E,eAAO,MAAM,yBAAyB,8BAA8B,CAAC;AACrE,eAAO,MAAM,+BAA+B,+BAA+B,CAAC;AAC5E,eAAO,MAAM,sCAAsC,0CAA0C,CAAC;AAC9F,eAAO,MAAM,6BAA6B,sDAAsD,CAAC;AACjG,eAAO,MAAM,+BAA+B,iEAAiE,CAAC;AAkB9G,wBAAgB,6BAA6B,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAGxF;AAED,wBAAgB,2BAA2B,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAGzE"}
@@ -3,6 +3,7 @@ export const REPETITION_BLOCKED_REASON = "repeated assistant output";
3
3
  export const LENGTH_EXHAUSTED_BLOCKED_REASON = "output truncation repeated";
4
4
  export const UNATTENDED_CONTINUATION_BLOCKED_REASON = "unattended continuation limit reached";
5
5
  export const PROVIDER_ERROR_BLOCKED_REASON = "provider error ended the turn (retries exhausted)";
6
+ export const CONTEXT_OVERFLOW_BLOCKED_REASON = "context overflow ended the turn (compaction did not recover)";
6
7
  // Mechanical blocks are stops the runtime imposed on itself, not decisions the
7
8
  // user or the model made. A terminal provider error belongs here: the provider
8
9
  // failing is infrastructure, and the user's next message is exactly the retry
@@ -14,6 +15,7 @@ const MECHANICAL_CONTINUATION_BLOCKS = [
14
15
  LENGTH_EXHAUSTED_BLOCKED_REASON,
15
16
  UNATTENDED_CONTINUATION_BLOCKED_REASON,
16
17
  PROVIDER_ERROR_BLOCKED_REASON,
18
+ CONTEXT_OVERFLOW_BLOCKED_REASON,
17
19
  ];
18
20
  const RESUME_GUIDANCE = "Send any message to resume.";
19
21
  export function isMechanicalContinuationBlock(blockedReason) {
@@ -1 +1 @@
1
- {"version":3,"file":"continuation-recovery.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/continuation-recovery.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,+BAA+B,GAAG,0BAA0B,CAAC;AAC1E,MAAM,CAAC,MAAM,yBAAyB,GAAG,2BAA2B,CAAC;AACrE,MAAM,CAAC,MAAM,+BAA+B,GAAG,4BAA4B,CAAC;AAC5E,MAAM,CAAC,MAAM,sCAAsC,GAAG,uCAAuC,CAAC;AAC9F,MAAM,CAAC,MAAM,6BAA6B,GAAG,mDAAmD,CAAC;AAEjG,+EAA+E;AAC/E,+EAA+E;AAC/E,8EAA8E;AAC9E,4EAA4E;AAC5E,8BAA8B;AAC9B,MAAM,8BAA8B,GAAsB;IACzD,+BAA+B;IAC/B,yBAAyB;IACzB,+BAA+B;IAC/B,sCAAsC;IACtC,6BAA6B;CAC7B,CAAC;AAEF,MAAM,eAAe,GAAG,6BAA6B,CAAC;AAEtD,MAAM,UAAU,6BAA6B,CAAC,aAAiC;IAC9E,IAAI,aAAa,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAC9C,OAAO,8BAA8B,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,aAAqB;IAChE,IAAI,CAAC,6BAA6B,CAAC,aAAa,CAAC;QAAE,OAAO,8BAA8B,aAAa,EAAE,CAAC;IACxG,OAAO,8BAA8B,aAAa,KAAK,eAAe,EAAE,CAAC;AAC1E,CAAC","sourcesContent":["export const CONTINUATION_CAP_BLOCKED_REASON = \"continuation cap reached\";\nexport const REPETITION_BLOCKED_REASON = \"repeated assistant output\";\nexport const LENGTH_EXHAUSTED_BLOCKED_REASON = \"output truncation repeated\";\nexport const UNATTENDED_CONTINUATION_BLOCKED_REASON = \"unattended continuation limit reached\";\nexport const PROVIDER_ERROR_BLOCKED_REASON = \"provider error ended the turn (retries exhausted)\";\n\n// Mechanical blocks are stops the runtime imposed on itself, not decisions the\n// user or the model made. A terminal provider error belongs here: the provider\n// failing is infrastructure, and the user's next message is exactly the retry\n// signal, so the goal resumes instead of stranding behind a block that only\n// `/goal resume` could clear.\nconst MECHANICAL_CONTINUATION_BLOCKS: readonly string[] = [\n\tCONTINUATION_CAP_BLOCKED_REASON,\n\tREPETITION_BLOCKED_REASON,\n\tLENGTH_EXHAUSTED_BLOCKED_REASON,\n\tUNATTENDED_CONTINUATION_BLOCKED_REASON,\n\tPROVIDER_ERROR_BLOCKED_REASON,\n];\n\nconst RESUME_GUIDANCE = \"Send any message to resume.\";\n\nexport function isMechanicalContinuationBlock(blockedReason: string | undefined): boolean {\n\tif (blockedReason === undefined) return false;\n\treturn MECHANICAL_CONTINUATION_BLOCKS.includes(blockedReason);\n}\n\nexport function continuationCapRecoveryHint(blockedReason: string): string {\n\tif (!isMechanicalContinuationBlock(blockedReason)) return `Goal continuation blocked: ${blockedReason}`;\n\treturn `Goal continuation blocked: ${blockedReason}. ${RESUME_GUIDANCE}`;\n}\n"]}
1
+ {"version":3,"file":"continuation-recovery.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/continuation-recovery.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,+BAA+B,GAAG,0BAA0B,CAAC;AAC1E,MAAM,CAAC,MAAM,yBAAyB,GAAG,2BAA2B,CAAC;AACrE,MAAM,CAAC,MAAM,+BAA+B,GAAG,4BAA4B,CAAC;AAC5E,MAAM,CAAC,MAAM,sCAAsC,GAAG,uCAAuC,CAAC;AAC9F,MAAM,CAAC,MAAM,6BAA6B,GAAG,mDAAmD,CAAC;AACjG,MAAM,CAAC,MAAM,+BAA+B,GAAG,8DAA8D,CAAC;AAE9G,+EAA+E;AAC/E,+EAA+E;AAC/E,8EAA8E;AAC9E,4EAA4E;AAC5E,8BAA8B;AAC9B,MAAM,8BAA8B,GAAsB;IACzD,+BAA+B;IAC/B,yBAAyB;IACzB,+BAA+B;IAC/B,sCAAsC;IACtC,6BAA6B;IAC7B,+BAA+B;CAC/B,CAAC;AAEF,MAAM,eAAe,GAAG,6BAA6B,CAAC;AAEtD,MAAM,UAAU,6BAA6B,CAAC,aAAiC;IAC9E,IAAI,aAAa,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAC9C,OAAO,8BAA8B,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,aAAqB;IAChE,IAAI,CAAC,6BAA6B,CAAC,aAAa,CAAC;QAAE,OAAO,8BAA8B,aAAa,EAAE,CAAC;IACxG,OAAO,8BAA8B,aAAa,KAAK,eAAe,EAAE,CAAC;AAC1E,CAAC","sourcesContent":["export const CONTINUATION_CAP_BLOCKED_REASON = \"continuation cap reached\";\nexport const REPETITION_BLOCKED_REASON = \"repeated assistant output\";\nexport const LENGTH_EXHAUSTED_BLOCKED_REASON = \"output truncation repeated\";\nexport const UNATTENDED_CONTINUATION_BLOCKED_REASON = \"unattended continuation limit reached\";\nexport const PROVIDER_ERROR_BLOCKED_REASON = \"provider error ended the turn (retries exhausted)\";\nexport const CONTEXT_OVERFLOW_BLOCKED_REASON = \"context overflow ended the turn (compaction did not recover)\";\n\n// Mechanical blocks are stops the runtime imposed on itself, not decisions the\n// user or the model made. A terminal provider error belongs here: the provider\n// failing is infrastructure, and the user's next message is exactly the retry\n// signal, so the goal resumes instead of stranding behind a block that only\n// `/goal resume` could clear.\nconst MECHANICAL_CONTINUATION_BLOCKS: readonly string[] = [\n\tCONTINUATION_CAP_BLOCKED_REASON,\n\tREPETITION_BLOCKED_REASON,\n\tLENGTH_EXHAUSTED_BLOCKED_REASON,\n\tUNATTENDED_CONTINUATION_BLOCKED_REASON,\n\tPROVIDER_ERROR_BLOCKED_REASON,\n\tCONTEXT_OVERFLOW_BLOCKED_REASON,\n];\n\nconst RESUME_GUIDANCE = \"Send any message to resume.\";\n\nexport function isMechanicalContinuationBlock(blockedReason: string | undefined): boolean {\n\tif (blockedReason === undefined) return false;\n\treturn MECHANICAL_CONTINUATION_BLOCKS.includes(blockedReason);\n}\n\nexport function continuationCapRecoveryHint(blockedReason: string): string {\n\tif (!isMechanicalContinuationBlock(blockedReason)) return `Goal continuation blocked: ${blockedReason}`;\n\treturn `Goal continuation blocked: ${blockedReason}. ${RESUME_GUIDANCE}`;\n}\n"]}
@@ -29,6 +29,8 @@ export type GoalContinuationInput = {
29
29
  readonly recentNormalizedOutputHashes: readonly string[];
30
30
  readonly toollessContinuationStreak: number;
31
31
  readonly continuationPending: boolean;
32
+ /** The last turn was rejected (or silently starved) by context size and recovery did not shrink it. */
33
+ readonly lastTurnStuckOnContextOverflow: boolean;
32
34
  };
33
35
  export type GoalContinuationVerdict = {
34
36
  kind: "continue";
@@ -36,7 +38,7 @@ export type GoalContinuationVerdict = {
36
38
  stallNotice: boolean;
37
39
  } | {
38
40
  kind: "deny";
39
- reason: "not-eligible" | "single-flight" | "cap" | "stale" | "repetition" | "length-exhausted" | "unattended";
41
+ reason: "not-eligible" | "single-flight" | "cap" | "stale" | "repetition" | "length-exhausted" | "unattended" | "context-overflow";
40
42
  };
41
43
  export declare function shouldQueueGoalContinuationWhenIdle(goal: Goal | null, isIdle: boolean, hasPendingMessages: boolean): goal is Goal;
42
44
  export declare function shouldQueueGoalContinuationAfterAgentEnd(goal: Goal | null, hasPendingMessages: boolean, messages: readonly AgentMessage[]): goal is Goal;
@@ -1 +1 @@
1
- {"version":3,"file":"continuation.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/continuation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"continuation.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/continuation.ts"],"names":[],"mappings":"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"}
@@ -48,6 +48,10 @@ function isAbortedToolResult(message) {
48
48
  return message.content.some((content) => content.type === "text" && /\babort(?:ed)?\b/i.test(content.text));
49
49
  }
50
50
  export function evaluateGoalContinuation(input) {
51
+ // Re-sending an overflowed context fails identically on every path (#1422).
52
+ if (input.goal?.status === "active" && input.lastTurnStuckOnContextOverflow) {
53
+ return { kind: "deny", reason: "context-overflow" };
54
+ }
51
55
  if (!isEligibleForGoalContinuation(input))
52
56
  return { kind: "deny", reason: "not-eligible" };
53
57
  if (input.continuationPending)
@@ -1 +1 @@
1
- {"version":3,"file":"continuation.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/continuation.ts"],"names":[],"mappings":"AAMA,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC;AACvC,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,CAAC;AAC/C,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC;AAC7C,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,CAAC;AAC5C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kCAAkC,GAAG,GAAG,CAAC;AACtD,MAAM,CAAC,MAAM,wBAAwB,GAAG,MAAM,CAAC;AAgC/C,MAAM,UAAU,mCAAmC,CAClD,IAAiB,EACjB,MAAe,EACf,kBAA2B;IAE3B,OAAO,IAAI,EAAE,MAAM,KAAK,QAAQ,IAAI,MAAM,IAAI,CAAC,kBAAkB,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,wCAAwC,CACvD,IAAiB,EACjB,kBAA2B,EAC3B,QAAiC;IAEjC,OAAO,IAAI,EAAE,MAAM,KAAK,QAAQ,IAAI,CAAC,kBAAkB,IAAI,kBAAkB,CAAC,QAAQ,CAAC,CAAC;AACzF,CAAC;AAED,SAAS,kBAAkB,CAAC,QAAiC;IAC5D,MAAM,kBAAkB,GAAG,6BAA6B,CAAC,QAAQ,CAAC,CAAC;IACnE,IAAI,kBAAkB,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAEnD,MAAM,aAAa,GAAG,QAAQ,CAAC,kBAAkB,CAAC,CAAC;IACnD,IAAI,aAAa,EAAE,IAAI,KAAK,WAAW,IAAI,CAAC,uBAAuB,CAAC,aAAa,CAAC,UAAU,CAAC;QAAE,OAAO,KAAK,CAAC;IAE5G,KAAK,IAAI,KAAK,GAAG,kBAAkB,GAAG,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QAC3E,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;QAChC,IAAI,OAAO,EAAE,IAAI,KAAK,YAAY,IAAI,mBAAmB,CAAC,OAAO,CAAC;YAAE,OAAO,KAAK,CAAC;IAClF,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC;AAED,SAAS,6BAA6B,CAAC,QAAiC;IACvE,KAAK,IAAI,KAAK,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;QAC3D,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;QAChC,IAAI,OAAO,EAAE,IAAI,KAAK,WAAW,EAAE,CAAC;YACnC,OAAO,KAAK,CAAC;QACd,CAAC;IACF,CAAC;IACD,OAAO,SAAS,CAAC;AAClB,CAAC;AAED,SAAS,uBAAuB,CAAC,UAA+C;IAC/E,OAAO,UAAU,KAAK,MAAM,IAAI,UAAU,KAAK,QAAQ,CAAC;AACzD,CAAC;AAED,SAAS,mBAAmB,CAAC,OAA+B;IAC3D,IAAI,CAAC,OAAO,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC;IACnC,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,MAAM,IAAI,mBAAmB,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;AAC7G,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,KAA4B;IACpE,IAAI,CAAC,6BAA6B,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC;IAC3F,IAAI,KAAK,CAAC,mBAAmB;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC;IAChF,IAAI,+BAA+B,CAAC,KAAK,CAAC,4BAA4B,CAAC;QACtE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;IAC/C,IAAI,2BAA2B,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;IACtF,IAAI,KAAK,CAAC,wBAAwB,IAAI,qBAAqB,EAAE,CAAC;QAC7D,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IACxC,CAAC;IACD,IACC,CAAC,KAAK,CAAC,IAAI,KAAK,WAAW,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW,CAAC;QAC1D,KAAK,CAAC,yBAAyB,KAAK,SAAS;QAC7C,KAAK,CAAC,yBAAyB,KAAK,KAAK,CAAC,gBAAgB,EACzD,CAAC;QACF,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;IAC1C,CAAC;IACD,IAAI,KAAK,CAAC,cAAc,KAAK,QAAQ,EAAE,CAAC;QACvC,IAAI,KAAK,CAAC,2BAA2B,IAAI,0BAA0B,EAAE,CAAC;YACrE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC;QACrD,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,qBAAqB,CAAC,KAAK,CAAC,EAAE,CAAC;IAC3F,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,qBAAqB,CAAC,KAAK,CAAC,EAAE,CAAC;AACxF,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,sBAAsB,CAAC,IAAY;IAClD,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;AACvD,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC7C,MAAM,UAAU,GAAG,sBAAsB,CAAC,IAAI,CAAC,CAAC;IAChD,IAAI,IAAI,GAAG,UAAU,CAAC;IACtB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,UAAU,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QACxD,IAAI,IAAI,UAAU,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QACrC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IACpC,CAAC;IACD,OAAO,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AACnD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,8BAA8B,CAC7C,IAAsB,EACtB,SAAiB,EACjB,UAAkB,EAClB,qBAA6B;IAE7B,OAAO,GAAG,IAAI,CAAC,EAAE,IAAI,SAAS,IAAI,UAAU,IAAI,qBAAqB,EAAE,CAAC;AACzE,CAAC;AAED,MAAM,UAAU,2BAA2B,CAC1C,KAAoF;IAEpF,OAAO,CACN,KAAK,CAAC,yBAAyB,KAAK,SAAS;QAC7C,KAAK,CAAC,gBAAgB,KAAK,SAAS;QACpC,KAAK,CAAC,yBAAyB,KAAK,KAAK,CAAC,gBAAgB,CAC1D,CAAC;AACH,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,QAAiC;IAC1E,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE;QAChC,IAAI,OAAO,EAAE,IAAI,KAAK,YAAY;YAAE,OAAO,IAAI,CAAC;QAChD,OAAO,OAAO,EAAE,IAAI,KAAK,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC;IACxG,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,6BAA6B,CAAC,KAA4B;IAClE,IAAI,KAAK,CAAC,IAAI,EAAE,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,kBAAkB;QAAE,OAAO,KAAK,CAAC;IAC9E,IAAI,KAAK,CAAC,IAAI,KAAK,gBAAgB,IAAI,KAAK,CAAC,IAAI,KAAK,kBAAkB;QAAE,OAAO,IAAI,CAAC;IACtF,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;QAChC,OAAO,KAAK,CAAC,cAAc,KAAK,SAAS,IAAI,uBAAuB,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IAC5F,CAAC;IACD,OAAO,KAAK,CAAC,MAAM,CAAC;AACrB,CAAC;AAED,wHAAwH;AACxH,SAAS,2BAA2B,CAAC,KAA4B;IAChE,IAAI,KAAK,CAAC,IAAI,KAAK,gBAAgB;QAAE,OAAO,KAAK,CAAC;IAClD,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,uBAAuB,IAAI,CAAC,CAAC,IAAI,kCAAkC,CAAC;AACzF,CAAC;AAED,SAAS,+BAA+B,CAAC,MAAyB;IACjE,MAAM,UAAU,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACjC,IAAI,UAAU,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAE3C,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,IAAI,KAAK,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,UAAU,EAAE,KAAK,EAAE,EAAE,CAAC;QACzF,MAAM,IAAI,CAAC,CAAC;IACb,CAAC;IACD,OAAO,MAAM,IAAI,2BAA2B,CAAC;AAC9C,CAAC;AAED,SAAS,qBAAqB,CAAC,KAA4B;IAC1D,OAAO,KAAK,CAAC,0BAA0B,IAAI,6BAA6B,CAAC;AAC1E,CAAC","sourcesContent":["import type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport type { Goal } from \"./types.ts\";\n\ntype AssistantAgentMessage = Extract<AgentMessage, { role: \"assistant\" }>;\ntype ToolResultAgentMessage = Extract<AgentMessage, { role: \"toolResult\" }>;\n\nexport const GOAL_CONTINUATION_CAP = 8;\nexport const GOAL_STALL_TOOLLESS_THRESHOLD = 3;\nexport const GOAL_REPETITION_HASH_STREAK = 3;\nexport const GOAL_LENGTH_RECOVERY_LIMIT = 1;\n/**\n * Hard budget of automatic continuations without accepted direct user input (#1139).\n * Progress and tool use never refill it: only direct input does. Stays above the\n * #447 distinct-progress pin (50) and an 8-hour monitor-backstop cadence\n * (~120 deliveries at 240s), below the observed 289-continuation incident run.\n */\nexport const GOAL_UNATTENDED_CONTINUATION_LIMIT = 150;\nexport const GOAL_USER_GRACE_DELAY_MS = 10_000;\n\nexport type GoalContinuationPath =\n\t| \"immediate\"\n\t| \"monitorDelayed\"\n\t| \"userGrace\"\n\t| \"sessionStart\"\n\t| \"systemRecovery\"\n\t| \"providerRecovery\";\n\nexport type GoalContinuationInput = {\n\treadonly goal: Goal | null;\n\treadonly isIdle: boolean;\n\treadonly hasPendingMessages: boolean;\n\treadonly path: GoalContinuationPath;\n\treadonly lastStopReason: AssistantAgentMessage[\"stopReason\"] | undefined;\n\treadonly consecutiveContinuations: number;\n\treadonly lastContinuationSignature: string | undefined;\n\treadonly currentSignature: string | undefined;\n\treadonly consecutiveLengthRecoveries: number;\n\treadonly recentNormalizedOutputHashes: readonly string[];\n\treadonly toollessContinuationStreak: number;\n\treadonly continuationPending: boolean;\n};\n\nexport type GoalContinuationVerdict =\n\t| { kind: \"continue\"; prompt: \"full\" | \"minimal\"; stallNotice: boolean }\n\t| {\n\t\t\tkind: \"deny\";\n\t\t\treason: \"not-eligible\" | \"single-flight\" | \"cap\" | \"stale\" | \"repetition\" | \"length-exhausted\" | \"unattended\";\n\t };\n\nexport function shouldQueueGoalContinuationWhenIdle(\n\tgoal: Goal | null,\n\tisIdle: boolean,\n\thasPendingMessages: boolean,\n): goal is Goal {\n\treturn goal?.status === \"active\" && isIdle && !hasPendingMessages;\n}\n\nexport function shouldQueueGoalContinuationAfterAgentEnd(\n\tgoal: Goal | null,\n\thasPendingMessages: boolean,\n\tmessages: readonly AgentMessage[],\n): goal is Goal {\n\treturn goal?.status === \"active\" && !hasPendingMessages && didAgentEndCleanly(messages);\n}\n\nfunction didAgentEndCleanly(messages: readonly AgentMessage[]): boolean {\n\tconst lastAssistantIndex = findLastAssistantMessageIndex(messages);\n\tif (lastAssistantIndex === undefined) return false;\n\n\tconst lastAssistant = messages[lastAssistantIndex];\n\tif (lastAssistant?.role !== \"assistant\" || !isContinuableStopReason(lastAssistant.stopReason)) return false;\n\n\tfor (let index = lastAssistantIndex + 1; index < messages.length; index++) {\n\t\tconst message = messages[index];\n\t\tif (message?.role === \"toolResult\" && isAbortedToolResult(message)) return false;\n\t}\n\treturn true;\n}\n\nfunction findLastAssistantMessageIndex(messages: readonly AgentMessage[]): number | undefined {\n\tfor (let index = messages.length - 1; index >= 0; index--) {\n\t\tconst message = messages[index];\n\t\tif (message?.role === \"assistant\") {\n\t\t\treturn index;\n\t\t}\n\t}\n\treturn undefined;\n}\n\nfunction isContinuableStopReason(stopReason: AssistantAgentMessage[\"stopReason\"]): boolean {\n\treturn stopReason === \"stop\" || stopReason === \"length\";\n}\n\nfunction isAbortedToolResult(message: ToolResultAgentMessage): boolean {\n\tif (!message.isError) return false;\n\treturn message.content.some((content) => content.type === \"text\" && /\\babort(?:ed)?\\b/i.test(content.text));\n}\n\nexport function evaluateGoalContinuation(input: GoalContinuationInput): GoalContinuationVerdict {\n\tif (!isEligibleForGoalContinuation(input)) return { kind: \"deny\", reason: \"not-eligible\" };\n\tif (input.continuationPending) return { kind: \"deny\", reason: \"single-flight\" };\n\tif (hasRepeatedNormalizedOutputHash(input.recentNormalizedOutputHashes))\n\t\treturn { kind: \"deny\", reason: \"repetition\" };\n\tif (isUnattendedBudgetExhausted(input)) return { kind: \"deny\", reason: \"unattended\" };\n\tif (input.consecutiveContinuations >= GOAL_CONTINUATION_CAP) {\n\t\treturn { kind: \"deny\", reason: \"cap\" };\n\t}\n\tif (\n\t\t(input.path === \"immediate\" || input.path === \"userGrace\") &&\n\t\tinput.lastContinuationSignature !== undefined &&\n\t\tinput.lastContinuationSignature === input.currentSignature\n\t) {\n\t\treturn { kind: \"deny\", reason: \"stale\" };\n\t}\n\tif (input.lastStopReason === \"length\") {\n\t\tif (input.consecutiveLengthRecoveries >= GOAL_LENGTH_RECOVERY_LIMIT) {\n\t\t\treturn { kind: \"deny\", reason: \"length-exhausted\" };\n\t\t}\n\t\treturn { kind: \"continue\", prompt: \"minimal\", stallNotice: shouldShowStallNotice(input) };\n\t}\n\treturn { kind: \"continue\", prompt: \"full\", stallNotice: shouldShowStallNotice(input) };\n}\n\n/** Produces a stable, whitespace-insensitive representation for repetition detection. */\nexport function normalizeAssistantText(text: string): string {\n\treturn text.toLowerCase().replace(/\\s+/g, \" \").trim();\n}\n\n/** Hashes normalized assistant text with the deterministic 32-bit FNV-1a algorithm. */\nexport function hashAssistantText(text: string): string {\n\tconst normalized = normalizeAssistantText(text);\n\tlet hash = 0x811c9dc5;\n\tfor (let index = 0; index < normalized.length; index++) {\n\t\thash ^= normalized.charCodeAt(index);\n\t\thash = Math.imul(hash, 0x01000193);\n\t}\n\treturn (hash >>> 0).toString(16).padStart(8, \"0\");\n}\n\n/**\n * Identifies the observable goal progress for stale-continuation admission.\n * Deliberately excludes goal.updatedAt because usage accounting updates it after every agent turn.\n */\nexport function buildGoalContinuationSignature(\n\tgoal: Pick<Goal, \"id\">,\n\topenTodos: number,\n\ttotalTodos: number,\n\tlastAssistantTextHash: string,\n): string {\n\treturn `${goal.id}:${openTodos}/${totalTodos}:${lastAssistantTextHash}`;\n}\n\nexport function hasGoalContinuationProgress(\n\tinput: Pick<GoalContinuationInput, \"lastContinuationSignature\" | \"currentSignature\">,\n): boolean {\n\treturn (\n\t\tinput.lastContinuationSignature !== undefined &&\n\t\tinput.currentSignature !== undefined &&\n\t\tinput.lastContinuationSignature !== input.currentSignature\n\t);\n}\n\nexport function continuationTurnUsedTools(messages: readonly AgentMessage[]): boolean {\n\treturn messages.some((message) => {\n\t\tif (message?.role === \"toolResult\") return true;\n\t\treturn message?.role === \"assistant\" && message.content.some((content) => content.type === \"toolCall\");\n\t});\n}\n\nfunction isEligibleForGoalContinuation(input: GoalContinuationInput): boolean {\n\tif (input.goal?.status !== \"active\" || input.hasPendingMessages) return false;\n\tif (input.path === \"systemRecovery\" || input.path === \"providerRecovery\") return true;\n\tif (input.path === \"immediate\") {\n\t\treturn input.lastStopReason !== undefined && isContinuableStopReason(input.lastStopReason);\n\t}\n\treturn input.isIdle;\n}\n\n/** Monitor-delayed deliveries are exempt: armed-wake waiting is by-design and rate-limited by the cache-aware timer. */\nfunction isUnattendedBudgetExhausted(input: GoalContinuationInput): boolean {\n\tif (input.path === \"monitorDelayed\") return false;\n\treturn (input.goal?.unattendedContinuations ?? 0) >= GOAL_UNATTENDED_CONTINUATION_LIMIT;\n}\n\nfunction hasRepeatedNormalizedOutputHash(hashes: readonly string[]): boolean {\n\tconst latestHash = hashes.at(-1);\n\tif (latestHash === undefined) return false;\n\n\tlet streak = 0;\n\tfor (let index = hashes.length - 1; index >= 0 && hashes[index] === latestHash; index--) {\n\t\tstreak += 1;\n\t}\n\treturn streak >= GOAL_REPETITION_HASH_STREAK;\n}\n\nfunction shouldShowStallNotice(input: GoalContinuationInput): boolean {\n\treturn input.toollessContinuationStreak >= GOAL_STALL_TOOLLESS_THRESHOLD;\n}\n"]}
1
+ {"version":3,"file":"continuation.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/continuation.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport type { Goal } from \"./types.ts\";\n\ntype AssistantAgentMessage = Extract<AgentMessage, { role: \"assistant\" }>;\ntype ToolResultAgentMessage = Extract<AgentMessage, { role: \"toolResult\" }>;\n\nexport const GOAL_CONTINUATION_CAP = 8;\nexport const GOAL_STALL_TOOLLESS_THRESHOLD = 3;\nexport const GOAL_REPETITION_HASH_STREAK = 3;\nexport const GOAL_LENGTH_RECOVERY_LIMIT = 1;\n/**\n * Hard budget of automatic continuations without accepted direct user input (#1139).\n * Progress and tool use never refill it: only direct input does. Stays above the\n * #447 distinct-progress pin (50) and an 8-hour monitor-backstop cadence\n * (~120 deliveries at 240s), below the observed 289-continuation incident run.\n */\nexport const GOAL_UNATTENDED_CONTINUATION_LIMIT = 150;\nexport const GOAL_USER_GRACE_DELAY_MS = 10_000;\n\nexport type GoalContinuationPath =\n\t| \"immediate\"\n\t| \"monitorDelayed\"\n\t| \"userGrace\"\n\t| \"sessionStart\"\n\t| \"systemRecovery\"\n\t| \"providerRecovery\";\n\nexport type GoalContinuationInput = {\n\treadonly goal: Goal | null;\n\treadonly isIdle: boolean;\n\treadonly hasPendingMessages: boolean;\n\treadonly path: GoalContinuationPath;\n\treadonly lastStopReason: AssistantAgentMessage[\"stopReason\"] | undefined;\n\treadonly consecutiveContinuations: number;\n\treadonly lastContinuationSignature: string | undefined;\n\treadonly currentSignature: string | undefined;\n\treadonly consecutiveLengthRecoveries: number;\n\treadonly recentNormalizedOutputHashes: readonly string[];\n\treadonly toollessContinuationStreak: number;\n\treadonly continuationPending: boolean;\n\t/** The last turn was rejected (or silently starved) by context size and recovery did not shrink it. */\n\treadonly lastTurnStuckOnContextOverflow: boolean;\n};\n\nexport type GoalContinuationVerdict =\n\t| { kind: \"continue\"; prompt: \"full\" | \"minimal\"; stallNotice: boolean }\n\t| {\n\t\t\tkind: \"deny\";\n\t\t\treason:\n\t\t\t\t| \"not-eligible\"\n\t\t\t\t| \"single-flight\"\n\t\t\t\t| \"cap\"\n\t\t\t\t| \"stale\"\n\t\t\t\t| \"repetition\"\n\t\t\t\t| \"length-exhausted\"\n\t\t\t\t| \"unattended\"\n\t\t\t\t| \"context-overflow\";\n\t };\n\nexport function shouldQueueGoalContinuationWhenIdle(\n\tgoal: Goal | null,\n\tisIdle: boolean,\n\thasPendingMessages: boolean,\n): goal is Goal {\n\treturn goal?.status === \"active\" && isIdle && !hasPendingMessages;\n}\n\nexport function shouldQueueGoalContinuationAfterAgentEnd(\n\tgoal: Goal | null,\n\thasPendingMessages: boolean,\n\tmessages: readonly AgentMessage[],\n): goal is Goal {\n\treturn goal?.status === \"active\" && !hasPendingMessages && didAgentEndCleanly(messages);\n}\n\nfunction didAgentEndCleanly(messages: readonly AgentMessage[]): boolean {\n\tconst lastAssistantIndex = findLastAssistantMessageIndex(messages);\n\tif (lastAssistantIndex === undefined) return false;\n\n\tconst lastAssistant = messages[lastAssistantIndex];\n\tif (lastAssistant?.role !== \"assistant\" || !isContinuableStopReason(lastAssistant.stopReason)) return false;\n\n\tfor (let index = lastAssistantIndex + 1; index < messages.length; index++) {\n\t\tconst message = messages[index];\n\t\tif (message?.role === \"toolResult\" && isAbortedToolResult(message)) return false;\n\t}\n\treturn true;\n}\n\nfunction findLastAssistantMessageIndex(messages: readonly AgentMessage[]): number | undefined {\n\tfor (let index = messages.length - 1; index >= 0; index--) {\n\t\tconst message = messages[index];\n\t\tif (message?.role === \"assistant\") {\n\t\t\treturn index;\n\t\t}\n\t}\n\treturn undefined;\n}\n\nfunction isContinuableStopReason(stopReason: AssistantAgentMessage[\"stopReason\"]): boolean {\n\treturn stopReason === \"stop\" || stopReason === \"length\";\n}\n\nfunction isAbortedToolResult(message: ToolResultAgentMessage): boolean {\n\tif (!message.isError) return false;\n\treturn message.content.some((content) => content.type === \"text\" && /\\babort(?:ed)?\\b/i.test(content.text));\n}\n\nexport function evaluateGoalContinuation(input: GoalContinuationInput): GoalContinuationVerdict {\n\t// Re-sending an overflowed context fails identically on every path (#1422).\n\tif (input.goal?.status === \"active\" && input.lastTurnStuckOnContextOverflow) {\n\t\treturn { kind: \"deny\", reason: \"context-overflow\" };\n\t}\n\tif (!isEligibleForGoalContinuation(input)) return { kind: \"deny\", reason: \"not-eligible\" };\n\tif (input.continuationPending) return { kind: \"deny\", reason: \"single-flight\" };\n\tif (hasRepeatedNormalizedOutputHash(input.recentNormalizedOutputHashes))\n\t\treturn { kind: \"deny\", reason: \"repetition\" };\n\tif (isUnattendedBudgetExhausted(input)) return { kind: \"deny\", reason: \"unattended\" };\n\tif (input.consecutiveContinuations >= GOAL_CONTINUATION_CAP) {\n\t\treturn { kind: \"deny\", reason: \"cap\" };\n\t}\n\tif (\n\t\t(input.path === \"immediate\" || input.path === \"userGrace\") &&\n\t\tinput.lastContinuationSignature !== undefined &&\n\t\tinput.lastContinuationSignature === input.currentSignature\n\t) {\n\t\treturn { kind: \"deny\", reason: \"stale\" };\n\t}\n\tif (input.lastStopReason === \"length\") {\n\t\tif (input.consecutiveLengthRecoveries >= GOAL_LENGTH_RECOVERY_LIMIT) {\n\t\t\treturn { kind: \"deny\", reason: \"length-exhausted\" };\n\t\t}\n\t\treturn { kind: \"continue\", prompt: \"minimal\", stallNotice: shouldShowStallNotice(input) };\n\t}\n\treturn { kind: \"continue\", prompt: \"full\", stallNotice: shouldShowStallNotice(input) };\n}\n\n/** Produces a stable, whitespace-insensitive representation for repetition detection. */\nexport function normalizeAssistantText(text: string): string {\n\treturn text.toLowerCase().replace(/\\s+/g, \" \").trim();\n}\n\n/** Hashes normalized assistant text with the deterministic 32-bit FNV-1a algorithm. */\nexport function hashAssistantText(text: string): string {\n\tconst normalized = normalizeAssistantText(text);\n\tlet hash = 0x811c9dc5;\n\tfor (let index = 0; index < normalized.length; index++) {\n\t\thash ^= normalized.charCodeAt(index);\n\t\thash = Math.imul(hash, 0x01000193);\n\t}\n\treturn (hash >>> 0).toString(16).padStart(8, \"0\");\n}\n\n/**\n * Identifies the observable goal progress for stale-continuation admission.\n * Deliberately excludes goal.updatedAt because usage accounting updates it after every agent turn.\n */\nexport function buildGoalContinuationSignature(\n\tgoal: Pick<Goal, \"id\">,\n\topenTodos: number,\n\ttotalTodos: number,\n\tlastAssistantTextHash: string,\n): string {\n\treturn `${goal.id}:${openTodos}/${totalTodos}:${lastAssistantTextHash}`;\n}\n\nexport function hasGoalContinuationProgress(\n\tinput: Pick<GoalContinuationInput, \"lastContinuationSignature\" | \"currentSignature\">,\n): boolean {\n\treturn (\n\t\tinput.lastContinuationSignature !== undefined &&\n\t\tinput.currentSignature !== undefined &&\n\t\tinput.lastContinuationSignature !== input.currentSignature\n\t);\n}\n\nexport function continuationTurnUsedTools(messages: readonly AgentMessage[]): boolean {\n\treturn messages.some((message) => {\n\t\tif (message?.role === \"toolResult\") return true;\n\t\treturn message?.role === \"assistant\" && message.content.some((content) => content.type === \"toolCall\");\n\t});\n}\n\nfunction isEligibleForGoalContinuation(input: GoalContinuationInput): boolean {\n\tif (input.goal?.status !== \"active\" || input.hasPendingMessages) return false;\n\tif (input.path === \"systemRecovery\" || input.path === \"providerRecovery\") return true;\n\tif (input.path === \"immediate\") {\n\t\treturn input.lastStopReason !== undefined && isContinuableStopReason(input.lastStopReason);\n\t}\n\treturn input.isIdle;\n}\n\n/** Monitor-delayed deliveries are exempt: armed-wake waiting is by-design and rate-limited by the cache-aware timer. */\nfunction isUnattendedBudgetExhausted(input: GoalContinuationInput): boolean {\n\tif (input.path === \"monitorDelayed\") return false;\n\treturn (input.goal?.unattendedContinuations ?? 0) >= GOAL_UNATTENDED_CONTINUATION_LIMIT;\n}\n\nfunction hasRepeatedNormalizedOutputHash(hashes: readonly string[]): boolean {\n\tconst latestHash = hashes.at(-1);\n\tif (latestHash === undefined) return false;\n\n\tlet streak = 0;\n\tfor (let index = hashes.length - 1; index >= 0 && hashes[index] === latestHash; index--) {\n\t\tstreak += 1;\n\t}\n\treturn streak >= GOAL_REPETITION_HASH_STREAK;\n}\n\nfunction shouldShowStallNotice(input: GoalContinuationInput): boolean {\n\treturn input.toollessContinuationStreak >= GOAL_STALL_TOOLLESS_THRESHOLD;\n}\n"]}
@@ -27,6 +27,9 @@ export declare function admitAndQueueGoalContinuation(pi: ExtensionAPI, ctx: Ext
27
27
  export declare function queueGoalContinuation(pi: ExtensionAPI, ctx: ExtensionContext, goal: Goal, options: SessionStartContinuationOptions): Promise<Goal | null>;
28
28
  export declare function buildCurrentGoalContinuationSignature(ctx: ExtensionContext, goal: Goal, lastAssistantText: string): string;
29
29
  export declare function lastAssistantText(messages: readonly AgentMessage[]): string;
30
+ export declare function isLastTurnStuckOnContextOverflow(ctx: ExtensionContext, lastAssistant: Extract<AgentMessage, {
31
+ role: "assistant";
32
+ }> | undefined): boolean;
30
33
  export declare function queueHiddenGoalPrompt(pi: ExtensionAPI, content: string): void;
31
34
  export {};
32
35
  //# sourceMappingURL=lifecycle-helpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle-helpers.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/lifecycle-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAGlE,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAErE,OAAO,EAGN,KAAK,qBAAqB,EAC1B,KAAK,uBAAuB,EAE5B,MAAM,mBAAmB,CAAC;AAY3B,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC,KAAK,iCAAiC,GAAG,OAAO,CAAC,uBAAuB,EAAE;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC,CAAC;AAEhG,KAAK,+BAA+B,GAAG;IACtC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;IACpD,QAAQ,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,iCAAiC,KAAK,MAAM,CAAC;IACzE,QAAQ,CAAC,uBAAuB,EAAE,MAAM,IAAI,CAAC;CAC7C,CAAC;AAEF,MAAM,MAAM,+BAA+B,GAAG;IAC7C,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;IACtC,QAAQ,CAAC,uBAAuB,EAAE,MAAM,IAAI,CAAC;CAC7C,CAAC;AAQF;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACpC,GAAG,EAAE,gBAAgB,EACrB,kBAAkB,EAAE,MAAM,EAC1B,IAAI,EAAE,IAAI,GAAG,IAAI,GACf,IAAI,IAAI,IAAI,CASd;AAED,sFAAsF;AACtF,wBAAsB,6BAA6B,CAClD,EAAE,EAAE,YAAY,EAChB,GAAG,EAAE,gBAAgB,EACrB,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,+BAA+B,GACtC,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAiBtB;AAED,mHAAmH;AACnH,wBAAsB,qBAAqB,CAC1C,EAAE,EAAE,YAAY,EAChB,GAAG,EAAE,gBAAgB,EACrB,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,+BAA+B,GACtC,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAuBtB;AAED,wBAAgB,qCAAqC,CACpD,GAAG,EAAE,gBAAgB,EACrB,IAAI,EAAE,IAAI,EACV,iBAAiB,EAAE,MAAM,GACvB,MAAM,CAKR;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,GAAG,MAAM,CAM3E;AA6DD,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAK7E"}
1
+ {"version":3,"file":"lifecycle-helpers.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/lifecycle-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAIlE,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAErE,OAAO,EAGN,KAAK,qBAAqB,EAC1B,KAAK,uBAAuB,EAE5B,MAAM,mBAAmB,CAAC;AAa3B,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC,KAAK,iCAAiC,GAAG,OAAO,CAAC,uBAAuB,EAAE;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC,CAAC;AAEhG,KAAK,+BAA+B,GAAG;IACtC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;IACpD,QAAQ,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,iCAAiC,KAAK,MAAM,CAAC;IACzE,QAAQ,CAAC,uBAAuB,EAAE,MAAM,IAAI,CAAC;CAC7C,CAAC;AAEF,MAAM,MAAM,+BAA+B,GAAG;IAC7C,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;IACtC,QAAQ,CAAC,uBAAuB,EAAE,MAAM,IAAI,CAAC;CAC7C,CAAC;AAQF;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACpC,GAAG,EAAE,gBAAgB,EACrB,kBAAkB,EAAE,MAAM,EAC1B,IAAI,EAAE,IAAI,GAAG,IAAI,GACf,IAAI,IAAI,IAAI,CASd;AAED,sFAAsF;AACtF,wBAAsB,6BAA6B,CAClD,EAAE,EAAE,YAAY,EAChB,GAAG,EAAE,gBAAgB,EACrB,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,+BAA+B,GACtC,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAiBtB;AAED,mHAAmH;AACnH,wBAAsB,qBAAqB,CAC1C,EAAE,EAAE,YAAY,EAChB,GAAG,EAAE,gBAAgB,EACrB,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,+BAA+B,GACtC,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CA2BtB;AAED,wBAAgB,qCAAqC,CACpD,GAAG,EAAE,gBAAgB,EACrB,IAAI,EAAE,IAAI,EACV,iBAAiB,EAAE,MAAM,GACvB,MAAM,CAKR;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,GAAG,MAAM,CAM3E;AAiBD,wBAAgB,gCAAgC,CAC/C,GAAG,EAAE,gBAAgB,EACrB,aAAa,EAAE,OAAO,CAAC,YAAY,EAAE;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,CAAC,GAAG,SAAS,GACrE,OAAO,CAET;AAuDD,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAK7E"}
@@ -1,7 +1,8 @@
1
+ import { isTurnStuckOnContextOverflow } from "../../../compaction/stuck-overflow.js";
1
2
  import { GOAL_CONTINUATION_MESSAGE_TYPE } from "../../../messages.js";
2
3
  import { getLatestPhasesFromBranchEntries } from "../todotools/state.js";
3
4
  import { buildGoalContinuationSignature, evaluateGoalContinuation, hashAssistantText, } from "./continuation.js";
4
- import { CONTINUATION_CAP_BLOCKED_REASON, continuationCapRecoveryHint, LENGTH_EXHAUSTED_BLOCKED_REASON, REPETITION_BLOCKED_REASON, UNATTENDED_CONTINUATION_BLOCKED_REASON, } from "./continuation-recovery.js";
5
+ import { CONTEXT_OVERFLOW_BLOCKED_REASON, CONTINUATION_CAP_BLOCKED_REASON, continuationCapRecoveryHint, LENGTH_EXHAUSTED_BLOCKED_REASON, REPETITION_BLOCKED_REASON, UNATTENDED_CONTINUATION_BLOCKED_REASON, } from "./continuation-recovery.js";
5
6
  import { buildContinuationPrompt } from "./prompt.js";
6
7
  import { recordContinuationDelivered, updateGoal } from "./store.js";
7
8
  import { goalStoreRef } from "./store-ref.js";
@@ -56,6 +57,7 @@ export async function queueGoalContinuation(pi, ctx, goal, options) {
56
57
  recentNormalizedOutputHashes: [],
57
58
  toollessContinuationStreak: 0,
58
59
  continuationPending: options.continuationPending,
60
+ lastTurnStuckOnContextOverflow: isLastTurnStuckOnContextOverflow(ctx, lastAssistantFromEntries(ctx.sessionManager.getBranch())),
59
61
  },
60
62
  content: () => buildContinuationPrompt(goal),
61
63
  markContinuationPending: options.markContinuationPending,
@@ -76,12 +78,19 @@ export function lastAssistantText(messages) {
76
78
  return "";
77
79
  }
78
80
  function lastAssistantTextFromEntries(entries) {
81
+ const assistant = lastAssistantFromEntries(entries);
82
+ return assistant === undefined ? "" : textContent(assistant);
83
+ }
84
+ function lastAssistantFromEntries(entries) {
79
85
  for (let index = entries.length - 1; index >= 0; index--) {
80
86
  const entry = entries[index];
81
87
  if (entry?.type === "message" && entry.message.role === "assistant")
82
- return textContent(entry.message);
88
+ return entry.message;
83
89
  }
84
- return "";
90
+ return undefined;
91
+ }
92
+ export function isLastTurnStuckOnContextOverflow(ctx, lastAssistant) {
93
+ return lastAssistant !== undefined && isTurnStuckOnContextOverflow(lastAssistant, ctx.model?.contextWindow ?? 0);
85
94
  }
86
95
  function textContent(message) {
87
96
  return message.content
@@ -114,6 +123,8 @@ function blockedReasonForContinuationGuard(reason) {
114
123
  return REPETITION_BLOCKED_REASON;
115
124
  case "length-exhausted":
116
125
  return LENGTH_EXHAUSTED_BLOCKED_REASON;
126
+ case "context-overflow":
127
+ return CONTEXT_OVERFLOW_BLOCKED_REASON;
117
128
  case "not-eligible":
118
129
  case "single-flight":
119
130
  case "stale":
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle-helpers.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/lifecycle-helpers.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport { GOAL_CONTINUATION_MESSAGE_TYPE } from \"../../../messages.ts\";\nimport type { SessionEntry } from \"../../../session-manager.ts\";\nimport type { ExtensionAPI, ExtensionContext } from \"../../types.ts\";\nimport { getLatestPhasesFromBranchEntries } from \"../todotools/state.ts\";\nimport {\n\tbuildGoalContinuationSignature,\n\tevaluateGoalContinuation,\n\ttype GoalContinuationInput,\n\ttype GoalContinuationVerdict,\n\thashAssistantText,\n} from \"./continuation.ts\";\nimport {\n\tCONTINUATION_CAP_BLOCKED_REASON,\n\tcontinuationCapRecoveryHint,\n\tLENGTH_EXHAUSTED_BLOCKED_REASON,\n\tREPETITION_BLOCKED_REASON,\n\tUNATTENDED_CONTINUATION_BLOCKED_REASON,\n} from \"./continuation-recovery.ts\";\nimport { buildContinuationPrompt } from \"./prompt.ts\";\nimport { recordContinuationDelivered, updateGoal } from \"./store.ts\";\nimport { goalStoreRef } from \"./store-ref.ts\";\nimport { openTodoTaskContents } from \"./todo-gate.ts\";\nimport type { Goal } from \"./types.ts\";\n\ntype ContinuingGoalContinuationVerdict = Extract<GoalContinuationVerdict, { kind: \"continue\" }>;\n\ntype GoalContinuationDeliveryOptions = {\n\treadonly input: Omit<GoalContinuationInput, \"goal\">;\n\treadonly content: (verdict: ContinuingGoalContinuationVerdict) => string;\n\treadonly markContinuationPending: () => void;\n};\n\nexport type SessionStartContinuationOptions = {\n\treadonly continuationPending: boolean;\n\treadonly markContinuationPending: () => void;\n};\n\n/**\n * Statuses that leave a goal stopped without finishing it. A restart is the only\n * moment the user can act on them, so both earn the resume prompt.\n */\nconst STOPPED_UNFINISHED_GOAL_STATUSES: readonly Goal[\"status\"][] = [\"paused\", \"blocked\"];\n\n/**\n * Mirrors codex `maybe_prompt_resume_paused_goal_after_resume`\n * (codex-rs/tui/src/app/thread_goal_actions.rs), which prompts on resume for every\n * stopped-but-unfinished status. senpi is budget-free, so codex's `UsageLimited`\n * has no counterpart here and the stopped set is `paused | blocked`.\n */\nexport function isResumeOfStoppedGoal(\n\tctx: ExtensionContext,\n\tsessionStartReason: string,\n\tgoal: Goal | null,\n): goal is Goal {\n\treturn (\n\t\tsessionStartReason === \"resume\" &&\n\t\tgoal !== null &&\n\t\tSTOPPED_UNFINISHED_GOAL_STATUSES.includes(goal.status) &&\n\t\tctx.hasUI &&\n\t\tctx.isIdle() &&\n\t\t!ctx.hasPendingMessages()\n\t);\n}\n\n/** Evaluates and delivers a continuation only after all guardrail checks admit it. */\nexport async function admitAndQueueGoalContinuation(\n\tpi: ExtensionAPI,\n\tctx: ExtensionContext,\n\tgoal: Goal,\n\toptions: GoalContinuationDeliveryOptions,\n): Promise<Goal | null> {\n\tconst verdict = evaluateGoalContinuation({ goal, ...options.input });\n\tif (verdict.kind === \"deny\") return handleDeniedContinuation(pi, ctx, goal, options.input, verdict.reason);\n\n\tif (options.input.currentSignature === undefined) {\n\t\tthrow new Error(\"Cannot queue a goal continuation without a progress signature\");\n\t}\n\tconst recordedGoal = await recordContinuationDelivered(\n\t\tgoalStoreRef(ctx.sessionManager, ctx.cwd),\n\t\toptions.input.currentSignature,\n\t\tgoal.id,\n\t\t{ countUnattended: options.input.path !== \"monitorDelayed\" },\n\t);\n\tif (recordedGoal === null) return null;\n\toptions.markContinuationPending();\n\tqueueHiddenGoalPrompt(pi, options.content(verdict));\n\treturn recordedGoal;\n}\n\n/** Routes startup and resume continuations through the same verdict and delivery accounting as agent-end paths. */\nexport async function queueGoalContinuation(\n\tpi: ExtensionAPI,\n\tctx: ExtensionContext,\n\tgoal: Goal,\n\toptions: SessionStartContinuationOptions,\n): Promise<Goal | null> {\n\tconst signature = buildCurrentGoalContinuationSignature(\n\t\tctx,\n\t\tgoal,\n\t\tlastAssistantTextFromEntries(ctx.sessionManager.getBranch()),\n\t);\n\treturn admitAndQueueGoalContinuation(pi, ctx, goal, {\n\t\tinput: {\n\t\t\tisIdle: ctx.isIdle(),\n\t\t\thasPendingMessages: ctx.hasPendingMessages(),\n\t\t\tpath: \"sessionStart\",\n\t\t\tlastStopReason: undefined,\n\t\t\tconsecutiveContinuations: goal.consecutiveContinuations ?? 0,\n\t\t\tlastContinuationSignature: goal.lastContinuationSignature,\n\t\t\tcurrentSignature: signature,\n\t\t\tconsecutiveLengthRecoveries: 0,\n\t\t\trecentNormalizedOutputHashes: [],\n\t\t\ttoollessContinuationStreak: 0,\n\t\t\tcontinuationPending: options.continuationPending,\n\t\t},\n\t\tcontent: () => buildContinuationPrompt(goal),\n\t\tmarkContinuationPending: options.markContinuationPending,\n\t});\n}\n\nexport function buildCurrentGoalContinuationSignature(\n\tctx: ExtensionContext,\n\tgoal: Goal,\n\tlastAssistantText: string,\n): string {\n\tconst entries = ctx.sessionManager.getBranch();\n\tconst openTodos = openTodoTaskContents(entries).length;\n\tconst totalTodos = getLatestPhasesFromBranchEntries(entries).reduce((count, phase) => count + phase.tasks.length, 0);\n\treturn buildGoalContinuationSignature(goal, openTodos, totalTodos, hashAssistantText(lastAssistantText));\n}\n\nexport function lastAssistantText(messages: readonly AgentMessage[]): string {\n\tfor (let index = messages.length - 1; index >= 0; index--) {\n\t\tconst message = messages[index];\n\t\tif (message?.role === \"assistant\") return textContent(message);\n\t}\n\treturn \"\";\n}\n\nfunction lastAssistantTextFromEntries(entries: readonly SessionEntry[]): string {\n\tfor (let index = entries.length - 1; index >= 0; index--) {\n\t\tconst entry = entries[index];\n\t\tif (entry?.type === \"message\" && entry.message.role === \"assistant\") return textContent(entry.message);\n\t}\n\treturn \"\";\n}\n\nfunction textContent(message: Extract<AgentMessage, { role: \"assistant\" }>): string {\n\treturn message.content\n\t\t.filter((content) => content.type === \"text\")\n\t\t.map((content) => content.text)\n\t\t.join(\"\\n\");\n}\n\nasync function handleDeniedContinuation(\n\tpi: ExtensionAPI,\n\tctx: ExtensionContext,\n\tgoal: Goal,\n\tinput: Omit<GoalContinuationInput, \"goal\">,\n\treason: Extract<GoalContinuationVerdict, { kind: \"deny\" }>[\"reason\"],\n): Promise<Goal> {\n\tconst blockedReason = blockedReasonForContinuationGuard(reason);\n\tif (blockedReason === undefined) return goal;\n\n\tconst blocked = await updateGoal(\n\t\tgoalStoreRef(ctx.sessionManager, ctx.cwd),\n\t\t{ status: \"blocked\", reason: blockedReason },\n\t\t\"model\",\n\t);\n\tif (ctx.hasUI) ctx.ui.notify(continuationCapRecoveryHint(blockedReason), \"warning\");\n\tpi.events?.emit(\"goal_continuation_guard_tripped\", {\n\t\tgoalId: goal.id,\n\t\treason,\n\t\tcount: input.consecutiveContinuations,\n\t\tunattendedContinuations: goal.unattendedContinuations ?? 0,\n\t});\n\treturn blocked;\n}\n\nfunction blockedReasonForContinuationGuard(\n\treason: Extract<GoalContinuationVerdict, { kind: \"deny\" }>[\"reason\"],\n): string | undefined {\n\tswitch (reason) {\n\t\tcase \"cap\":\n\t\t\treturn CONTINUATION_CAP_BLOCKED_REASON;\n\t\tcase \"unattended\":\n\t\t\treturn UNATTENDED_CONTINUATION_BLOCKED_REASON;\n\t\tcase \"repetition\":\n\t\t\treturn REPETITION_BLOCKED_REASON;\n\t\tcase \"length-exhausted\":\n\t\t\treturn LENGTH_EXHAUSTED_BLOCKED_REASON;\n\t\tcase \"not-eligible\":\n\t\tcase \"single-flight\":\n\t\tcase \"stale\":\n\t\t\treturn undefined;\n\t}\n}\n\nexport function queueHiddenGoalPrompt(pi: ExtensionAPI, content: string): void {\n\tpi.sendMessage(\n\t\t{ customType: GOAL_CONTINUATION_MESSAGE_TYPE, content, display: false },\n\t\t{ triggerTurn: true, deliverAs: \"followUp\" },\n\t);\n}\n"]}
1
+ {"version":3,"file":"lifecycle-helpers.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/lifecycle-helpers.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport { isTurnStuckOnContextOverflow } from \"../../../compaction/stuck-overflow.ts\";\nimport { GOAL_CONTINUATION_MESSAGE_TYPE } from \"../../../messages.ts\";\nimport type { SessionEntry } from \"../../../session-manager.ts\";\nimport type { ExtensionAPI, ExtensionContext } from \"../../types.ts\";\nimport { getLatestPhasesFromBranchEntries } from \"../todotools/state.ts\";\nimport {\n\tbuildGoalContinuationSignature,\n\tevaluateGoalContinuation,\n\ttype GoalContinuationInput,\n\ttype GoalContinuationVerdict,\n\thashAssistantText,\n} from \"./continuation.ts\";\nimport {\n\tCONTEXT_OVERFLOW_BLOCKED_REASON,\n\tCONTINUATION_CAP_BLOCKED_REASON,\n\tcontinuationCapRecoveryHint,\n\tLENGTH_EXHAUSTED_BLOCKED_REASON,\n\tREPETITION_BLOCKED_REASON,\n\tUNATTENDED_CONTINUATION_BLOCKED_REASON,\n} from \"./continuation-recovery.ts\";\nimport { buildContinuationPrompt } from \"./prompt.ts\";\nimport { recordContinuationDelivered, updateGoal } from \"./store.ts\";\nimport { goalStoreRef } from \"./store-ref.ts\";\nimport { openTodoTaskContents } from \"./todo-gate.ts\";\nimport type { Goal } from \"./types.ts\";\n\ntype ContinuingGoalContinuationVerdict = Extract<GoalContinuationVerdict, { kind: \"continue\" }>;\n\ntype GoalContinuationDeliveryOptions = {\n\treadonly input: Omit<GoalContinuationInput, \"goal\">;\n\treadonly content: (verdict: ContinuingGoalContinuationVerdict) => string;\n\treadonly markContinuationPending: () => void;\n};\n\nexport type SessionStartContinuationOptions = {\n\treadonly continuationPending: boolean;\n\treadonly markContinuationPending: () => void;\n};\n\n/**\n * Statuses that leave a goal stopped without finishing it. A restart is the only\n * moment the user can act on them, so both earn the resume prompt.\n */\nconst STOPPED_UNFINISHED_GOAL_STATUSES: readonly Goal[\"status\"][] = [\"paused\", \"blocked\"];\n\n/**\n * Mirrors codex `maybe_prompt_resume_paused_goal_after_resume`\n * (codex-rs/tui/src/app/thread_goal_actions.rs), which prompts on resume for every\n * stopped-but-unfinished status. senpi is budget-free, so codex's `UsageLimited`\n * has no counterpart here and the stopped set is `paused | blocked`.\n */\nexport function isResumeOfStoppedGoal(\n\tctx: ExtensionContext,\n\tsessionStartReason: string,\n\tgoal: Goal | null,\n): goal is Goal {\n\treturn (\n\t\tsessionStartReason === \"resume\" &&\n\t\tgoal !== null &&\n\t\tSTOPPED_UNFINISHED_GOAL_STATUSES.includes(goal.status) &&\n\t\tctx.hasUI &&\n\t\tctx.isIdle() &&\n\t\t!ctx.hasPendingMessages()\n\t);\n}\n\n/** Evaluates and delivers a continuation only after all guardrail checks admit it. */\nexport async function admitAndQueueGoalContinuation(\n\tpi: ExtensionAPI,\n\tctx: ExtensionContext,\n\tgoal: Goal,\n\toptions: GoalContinuationDeliveryOptions,\n): Promise<Goal | null> {\n\tconst verdict = evaluateGoalContinuation({ goal, ...options.input });\n\tif (verdict.kind === \"deny\") return handleDeniedContinuation(pi, ctx, goal, options.input, verdict.reason);\n\n\tif (options.input.currentSignature === undefined) {\n\t\tthrow new Error(\"Cannot queue a goal continuation without a progress signature\");\n\t}\n\tconst recordedGoal = await recordContinuationDelivered(\n\t\tgoalStoreRef(ctx.sessionManager, ctx.cwd),\n\t\toptions.input.currentSignature,\n\t\tgoal.id,\n\t\t{ countUnattended: options.input.path !== \"monitorDelayed\" },\n\t);\n\tif (recordedGoal === null) return null;\n\toptions.markContinuationPending();\n\tqueueHiddenGoalPrompt(pi, options.content(verdict));\n\treturn recordedGoal;\n}\n\n/** Routes startup and resume continuations through the same verdict and delivery accounting as agent-end paths. */\nexport async function queueGoalContinuation(\n\tpi: ExtensionAPI,\n\tctx: ExtensionContext,\n\tgoal: Goal,\n\toptions: SessionStartContinuationOptions,\n): Promise<Goal | null> {\n\tconst signature = buildCurrentGoalContinuationSignature(\n\t\tctx,\n\t\tgoal,\n\t\tlastAssistantTextFromEntries(ctx.sessionManager.getBranch()),\n\t);\n\treturn admitAndQueueGoalContinuation(pi, ctx, goal, {\n\t\tinput: {\n\t\t\tisIdle: ctx.isIdle(),\n\t\t\thasPendingMessages: ctx.hasPendingMessages(),\n\t\t\tpath: \"sessionStart\",\n\t\t\tlastStopReason: undefined,\n\t\t\tconsecutiveContinuations: goal.consecutiveContinuations ?? 0,\n\t\t\tlastContinuationSignature: goal.lastContinuationSignature,\n\t\t\tcurrentSignature: signature,\n\t\t\tconsecutiveLengthRecoveries: 0,\n\t\t\trecentNormalizedOutputHashes: [],\n\t\t\ttoollessContinuationStreak: 0,\n\t\t\tcontinuationPending: options.continuationPending,\n\t\t\tlastTurnStuckOnContextOverflow: isLastTurnStuckOnContextOverflow(\n\t\t\t\tctx,\n\t\t\t\tlastAssistantFromEntries(ctx.sessionManager.getBranch()),\n\t\t\t),\n\t\t},\n\t\tcontent: () => buildContinuationPrompt(goal),\n\t\tmarkContinuationPending: options.markContinuationPending,\n\t});\n}\n\nexport function buildCurrentGoalContinuationSignature(\n\tctx: ExtensionContext,\n\tgoal: Goal,\n\tlastAssistantText: string,\n): string {\n\tconst entries = ctx.sessionManager.getBranch();\n\tconst openTodos = openTodoTaskContents(entries).length;\n\tconst totalTodos = getLatestPhasesFromBranchEntries(entries).reduce((count, phase) => count + phase.tasks.length, 0);\n\treturn buildGoalContinuationSignature(goal, openTodos, totalTodos, hashAssistantText(lastAssistantText));\n}\n\nexport function lastAssistantText(messages: readonly AgentMessage[]): string {\n\tfor (let index = messages.length - 1; index >= 0; index--) {\n\t\tconst message = messages[index];\n\t\tif (message?.role === \"assistant\") return textContent(message);\n\t}\n\treturn \"\";\n}\n\nfunction lastAssistantTextFromEntries(entries: readonly SessionEntry[]): string {\n\tconst assistant = lastAssistantFromEntries(entries);\n\treturn assistant === undefined ? \"\" : textContent(assistant);\n}\n\nfunction lastAssistantFromEntries(\n\tentries: readonly SessionEntry[],\n): Extract<AgentMessage, { role: \"assistant\" }> | undefined {\n\tfor (let index = entries.length - 1; index >= 0; index--) {\n\t\tconst entry = entries[index];\n\t\tif (entry?.type === \"message\" && entry.message.role === \"assistant\") return entry.message;\n\t}\n\treturn undefined;\n}\n\nexport function isLastTurnStuckOnContextOverflow(\n\tctx: ExtensionContext,\n\tlastAssistant: Extract<AgentMessage, { role: \"assistant\" }> | undefined,\n): boolean {\n\treturn lastAssistant !== undefined && isTurnStuckOnContextOverflow(lastAssistant, ctx.model?.contextWindow ?? 0);\n}\n\nfunction textContent(message: Extract<AgentMessage, { role: \"assistant\" }>): string {\n\treturn message.content\n\t\t.filter((content) => content.type === \"text\")\n\t\t.map((content) => content.text)\n\t\t.join(\"\\n\");\n}\n\nasync function handleDeniedContinuation(\n\tpi: ExtensionAPI,\n\tctx: ExtensionContext,\n\tgoal: Goal,\n\tinput: Omit<GoalContinuationInput, \"goal\">,\n\treason: Extract<GoalContinuationVerdict, { kind: \"deny\" }>[\"reason\"],\n): Promise<Goal> {\n\tconst blockedReason = blockedReasonForContinuationGuard(reason);\n\tif (blockedReason === undefined) return goal;\n\n\tconst blocked = await updateGoal(\n\t\tgoalStoreRef(ctx.sessionManager, ctx.cwd),\n\t\t{ status: \"blocked\", reason: blockedReason },\n\t\t\"model\",\n\t);\n\tif (ctx.hasUI) ctx.ui.notify(continuationCapRecoveryHint(blockedReason), \"warning\");\n\tpi.events?.emit(\"goal_continuation_guard_tripped\", {\n\t\tgoalId: goal.id,\n\t\treason,\n\t\tcount: input.consecutiveContinuations,\n\t\tunattendedContinuations: goal.unattendedContinuations ?? 0,\n\t});\n\treturn blocked;\n}\n\nfunction blockedReasonForContinuationGuard(\n\treason: Extract<GoalContinuationVerdict, { kind: \"deny\" }>[\"reason\"],\n): string | undefined {\n\tswitch (reason) {\n\t\tcase \"cap\":\n\t\t\treturn CONTINUATION_CAP_BLOCKED_REASON;\n\t\tcase \"unattended\":\n\t\t\treturn UNATTENDED_CONTINUATION_BLOCKED_REASON;\n\t\tcase \"repetition\":\n\t\t\treturn REPETITION_BLOCKED_REASON;\n\t\tcase \"length-exhausted\":\n\t\t\treturn LENGTH_EXHAUSTED_BLOCKED_REASON;\n\t\tcase \"context-overflow\":\n\t\t\treturn CONTEXT_OVERFLOW_BLOCKED_REASON;\n\t\tcase \"not-eligible\":\n\t\tcase \"single-flight\":\n\t\tcase \"stale\":\n\t\t\treturn undefined;\n\t}\n}\n\nexport function queueHiddenGoalPrompt(pi: ExtensionAPI, content: string): void {\n\tpi.sendMessage(\n\t\t{ customType: GOAL_CONTINUATION_MESSAGE_TYPE, content, display: false },\n\t\t{ triggerTurn: true, deliverAs: \"followUp\" },\n\t);\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"monitor-continuation.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/monitor-continuation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAarE,OAAO,EAAE,2CAA2C,EAAE,MAAM,iBAAiB,CAAC;AAkB9E,OAAO,KAAK,EACX,eAAe,EAIf,uBAAuB,EAEvB,kBAAkB,EAClB,MAAM,iCAAiC,CAAC;AAMzC,OAAO,KAAK,EAAE,IAAI,EAAsB,MAAM,YAAY,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,eAAO,MAAM,iCAAiC,gCAAgC,CAAC;AAC/E,eAAO,MAAM,+BAA+B,8BAA8B,CAAC;AAC3E,eAAO,MAAM,mCAAmC,kCAAkC,CAAC;AACnF,eAAO,MAAM,wBAAwB,oCAAoC,CAAC;AAE1E,qBAAa,4BAA4B;;gBAiCvC,EAAE,EAAE,YAAY,EAChB,qBAAqB,GAAE,MAAM,OAAqB,EAClD,uBAAuB,GAAE,MAAM,IAAe,EAC9C,UAAU,CAAC,EAAE,cAAc;IAS5B,KAAK,CAAC,GAAG,EAAE,gBAAgB,GAAG,IAAI;IAW5B,aAAa,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IA0D7D,gBAAgB,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAoBnE,oBAAoB,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAgB5E,iBAAiB,IAAI,OAAO,CAAC,IAAI,GAAG,IAAI,GAAG,SAAS,CAAC;IA2B3D,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI;IASjC,sGAAsG;IACtG,oBAAoB,IAAI,OAAO;IAI/B;;;;;OAKG;IACH,oBAAoB,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAMtC,mGAAmG;IACnG,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAwBtC,6FAA6F;IAC7F,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,IAAI;IAc5D,sEAAsE;IACtE,cAAc,IAAI,IAAI;IAOtB,mGAAmG;IACnG,uBAAuB,IAAI,IAAI;IAI/B,OAAO,IAAI,IAAI;CAkWf"}
1
+ {"version":3,"file":"monitor-continuation.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/monitor-continuation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAarE,OAAO,EAAE,2CAA2C,EAAE,MAAM,iBAAiB,CAAC;AAmB9E,OAAO,KAAK,EACX,eAAe,EAIf,uBAAuB,EAEvB,kBAAkB,EAClB,MAAM,iCAAiC,CAAC;AAMzC,OAAO,KAAK,EAAE,IAAI,EAAsB,MAAM,YAAY,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,eAAO,MAAM,iCAAiC,gCAAgC,CAAC;AAC/E,eAAO,MAAM,+BAA+B,8BAA8B,CAAC;AAC3E,eAAO,MAAM,mCAAmC,kCAAkC,CAAC;AACnF,eAAO,MAAM,wBAAwB,oCAAoC,CAAC;AAE1E,qBAAa,4BAA4B;;gBAiCvC,EAAE,EAAE,YAAY,EAChB,qBAAqB,GAAE,MAAM,OAAqB,EAClD,uBAAuB,GAAE,MAAM,IAAe,EAC9C,UAAU,CAAC,EAAE,cAAc;IAS5B,KAAK,CAAC,GAAG,EAAE,gBAAgB,GAAG,IAAI;IAW5B,aAAa,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IA0D7D,gBAAgB,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAoBnE,oBAAoB,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAgB5E,iBAAiB,IAAI,OAAO,CAAC,IAAI,GAAG,IAAI,GAAG,SAAS,CAAC;IA2B3D,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI;IASjC,sGAAsG;IACtG,oBAAoB,IAAI,OAAO;IAI/B;;;;;OAKG;IACH,oBAAoB,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAMtC,mGAAmG;IACnG,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAwBtC,6FAA6F;IAC7F,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,IAAI;IAc5D,sEAAsE;IACtE,cAAc,IAAI,IAAI;IAOtB,mGAAmG;IACnG,uBAAuB,IAAI,IAAI;IAI/B,OAAO,IAAI,IAAI;CAmWf"}
@@ -3,7 +3,7 @@ import { subscribeGoalChannelState } from "./channel-state-subscriptions.js";
3
3
  export { GOAL_MONITOR_CONTINUATION_FALLBACK_DELAY_MS } from "./cache-warm.js";
4
4
  import { continuationTurnUsedTools, evaluateGoalContinuation, GOAL_USER_GRACE_DELAY_MS, hasGoalContinuationProgress, hashAssistantText, normalizeAssistantText, } from "./continuation.js";
5
5
  import { lastAssistantMessage } from "./last-assistant-message.js";
6
- import { admitAndQueueGoalContinuation, buildCurrentGoalContinuationSignature, lastAssistantText, } from "./lifecycle-helpers.js";
6
+ import { admitAndQueueGoalContinuation, buildCurrentGoalContinuationSignature, isLastTurnStuckOnContextOverflow, lastAssistantText, } from "./lifecycle-helpers.js";
7
7
  import { buildContinuationPrompt, buildGoalStallNotice, buildTruncationRecoveryPrompt } from "./prompt.js";
8
8
  import { isStaleExtensionContextError } from "./stale-context.js";
9
9
  import { resetContinuationStreak } from "./store.js";
@@ -432,6 +432,7 @@ export class MonitorAwareGoalContinuation {
432
432
  recentNormalizedOutputHashes: this.#recentNormalizedOutputHashes,
433
433
  toollessContinuationStreak: this.#toollessContinuationStreak,
434
434
  continuationPending: this.#isContinuationPending(),
435
+ lastTurnStuckOnContextOverflow: isLastTurnStuckOnContextOverflow(ctx, lastAssistant),
435
436
  };
436
437
  }
437
438
  #buildContinuationContent(ctx, goal, verdict) {