@tickernelz/paperclip-pro-adapter-utils 2026.1006.0 → 2026.1006.2

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 (181) hide show
  1. package/README.md +13 -0
  2. package/dist/acpx-engine/ephemeral-session-environment.d.ts +4 -0
  3. package/dist/acpx-engine/ephemeral-session-environment.d.ts.map +1 -0
  4. package/dist/acpx-engine/ephemeral-session-environment.js +29 -0
  5. package/dist/acpx-engine/ephemeral-session-environment.js.map +1 -0
  6. package/dist/acpx-engine/ephemeral-session-environment.test.d.ts +2 -0
  7. package/dist/acpx-engine/ephemeral-session-environment.test.d.ts.map +1 -0
  8. package/dist/acpx-engine/ephemeral-session-environment.test.js +177 -0
  9. package/dist/acpx-engine/ephemeral-session-environment.test.js.map +1 -0
  10. package/dist/acpx-engine/execute.d.ts +2 -6
  11. package/dist/acpx-engine/execute.d.ts.map +1 -1
  12. package/dist/acpx-engine/execute.js +125 -62
  13. package/dist/acpx-engine/execute.js.map +1 -1
  14. package/dist/acpx-engine/execute.test.js +299 -3
  15. package/dist/acpx-engine/execute.test.js.map +1 -1
  16. package/dist/acpx-engine/session-codec.d.ts.map +1 -1
  17. package/dist/acpx-engine/session-codec.js +1 -0
  18. package/dist/acpx-engine/session-codec.js.map +1 -1
  19. package/dist/acpx-engine/settlement-characterization.test.js +105 -0
  20. package/dist/acpx-engine/settlement-characterization.test.js.map +1 -1
  21. package/dist/acpx-engine/spawn-smoke.test.js +16 -7
  22. package/dist/acpx-engine/spawn-smoke.test.js.map +1 -1
  23. package/dist/acpx-engine/startup-cancellation.d.ts +1 -0
  24. package/dist/acpx-engine/startup-cancellation.d.ts.map +1 -1
  25. package/dist/acpx-engine/startup-cancellation.js +7 -2
  26. package/dist/acpx-engine/startup-cancellation.js.map +1 -1
  27. package/dist/acpx-engine/terminal-session-failure.d.ts +18 -0
  28. package/dist/acpx-engine/terminal-session-failure.d.ts.map +1 -0
  29. package/dist/acpx-engine/terminal-session-failure.js +106 -0
  30. package/dist/acpx-engine/terminal-session-failure.js.map +1 -0
  31. package/dist/acpx-engine/terminal-session-failure.test.d.ts +2 -0
  32. package/dist/acpx-engine/terminal-session-failure.test.d.ts.map +1 -0
  33. package/dist/acpx-engine/terminal-session-failure.test.js +139 -0
  34. package/dist/acpx-engine/terminal-session-failure.test.js.map +1 -0
  35. package/dist/acpx-engine/turn-characterization.test.js +4 -3
  36. package/dist/acpx-engine/turn-characterization.test.js.map +1 -1
  37. package/dist/command-managed-runtime.d.ts +2 -0
  38. package/dist/command-managed-runtime.d.ts.map +1 -1
  39. package/dist/command-managed-runtime.js +2 -0
  40. package/dist/command-managed-runtime.js.map +1 -1
  41. package/dist/command-redaction.d.ts +2 -0
  42. package/dist/command-redaction.d.ts.map +1 -1
  43. package/dist/command-redaction.js +20 -10
  44. package/dist/command-redaction.js.map +1 -1
  45. package/dist/command-redaction.test.js +20 -1
  46. package/dist/command-redaction.test.js.map +1 -1
  47. package/dist/directory-merge-lock.test.d.ts +2 -0
  48. package/dist/directory-merge-lock.test.d.ts.map +1 -0
  49. package/dist/directory-merge-lock.test.js +166 -0
  50. package/dist/directory-merge-lock.test.js.map +1 -0
  51. package/dist/execution-phase.d.ts +10 -0
  52. package/dist/execution-phase.d.ts.map +1 -0
  53. package/dist/execution-phase.js +27 -0
  54. package/dist/execution-phase.js.map +1 -0
  55. package/dist/execution-phase.test.d.ts +2 -0
  56. package/dist/execution-phase.test.d.ts.map +1 -0
  57. package/dist/execution-phase.test.js +26 -0
  58. package/dist/execution-phase.test.js.map +1 -0
  59. package/dist/execution-target-sandbox.test.js +49 -7
  60. package/dist/execution-target-sandbox.test.js.map +1 -1
  61. package/dist/execution-target-stdin-race.test.js +245 -2
  62. package/dist/execution-target-stdin-race.test.js.map +1 -1
  63. package/dist/execution-target.d.ts +6 -0
  64. package/dist/execution-target.d.ts.map +1 -1
  65. package/dist/execution-target.js +74 -22
  66. package/dist/execution-target.js.map +1 -1
  67. package/dist/git-workspace-sync.d.ts +15 -5
  68. package/dist/git-workspace-sync.d.ts.map +1 -1
  69. package/dist/git-workspace-sync.js +267 -124
  70. package/dist/git-workspace-sync.js.map +1 -1
  71. package/dist/git-workspace-sync.test.js +296 -20
  72. package/dist/git-workspace-sync.test.js.map +1 -1
  73. package/dist/github-launcher.d.ts.map +1 -1
  74. package/dist/github-launcher.js +26 -11
  75. package/dist/github-launcher.js.map +1 -1
  76. package/dist/github-launcher.test.js +34 -1
  77. package/dist/github-launcher.test.js.map +1 -1
  78. package/dist/index.d.ts +1 -1
  79. package/dist/index.d.ts.map +1 -1
  80. package/dist/index.js +1 -1
  81. package/dist/index.js.map +1 -1
  82. package/dist/paperclip-runner-permissions.d.ts +28 -1
  83. package/dist/paperclip-runner-permissions.d.ts.map +1 -1
  84. package/dist/paperclip-runner-permissions.js +9 -1
  85. package/dist/paperclip-runner-permissions.js.map +1 -1
  86. package/dist/prompt-sections.test.d.ts +2 -0
  87. package/dist/prompt-sections.test.d.ts.map +1 -0
  88. package/dist/prompt-sections.test.js +53 -0
  89. package/dist/prompt-sections.test.js.map +1 -0
  90. package/dist/provider-stopped-boundary.d.ts +7 -0
  91. package/dist/provider-stopped-boundary.d.ts.map +1 -0
  92. package/dist/provider-stopped-boundary.js +22 -0
  93. package/dist/provider-stopped-boundary.js.map +1 -0
  94. package/dist/provider-stopped-boundary.test.d.ts +2 -0
  95. package/dist/provider-stopped-boundary.test.d.ts.map +1 -0
  96. package/dist/provider-stopped-boundary.test.js +76 -0
  97. package/dist/provider-stopped-boundary.test.js.map +1 -0
  98. package/dist/remote-managed-runtime.d.ts +2 -0
  99. package/dist/remote-managed-runtime.d.ts.map +1 -1
  100. package/dist/remote-managed-runtime.js +3 -1
  101. package/dist/remote-managed-runtime.js.map +1 -1
  102. package/dist/remote-managed-runtime.test.js +25 -0
  103. package/dist/remote-managed-runtime.test.js.map +1 -1
  104. package/dist/sandbox-callback-bridge.d.ts +2 -0
  105. package/dist/sandbox-callback-bridge.d.ts.map +1 -1
  106. package/dist/sandbox-callback-bridge.js +34 -10
  107. package/dist/sandbox-callback-bridge.js.map +1 -1
  108. package/dist/sandbox-callback-bridge.test.js +106 -3
  109. package/dist/sandbox-callback-bridge.test.js.map +1 -1
  110. package/dist/sandbox-managed-runtime.d.ts +3 -0
  111. package/dist/sandbox-managed-runtime.d.ts.map +1 -1
  112. package/dist/sandbox-managed-runtime.js +749 -718
  113. package/dist/sandbox-managed-runtime.js.map +1 -1
  114. package/dist/sandbox-managed-runtime.test.js +171 -2
  115. package/dist/sandbox-managed-runtime.test.js.map +1 -1
  116. package/dist/server-utils.d.ts +43 -1
  117. package/dist/server-utils.d.ts.map +1 -1
  118. package/dist/server-utils.js +103 -31
  119. package/dist/server-utils.js.map +1 -1
  120. package/dist/server-utils.test.js +235 -45
  121. package/dist/server-utils.test.js.map +1 -1
  122. package/dist/session-compaction.d.ts.map +1 -1
  123. package/dist/session-compaction.js +6 -0
  124. package/dist/session-compaction.js.map +1 -1
  125. package/dist/ssh-fixture.test.js +22 -0
  126. package/dist/ssh-fixture.test.js.map +1 -1
  127. package/dist/ssh.d.ts +2 -0
  128. package/dist/ssh.d.ts.map +1 -1
  129. package/dist/ssh.js +2 -2
  130. package/dist/ssh.js.map +1 -1
  131. package/dist/test-fixtures/prompt-context.d.ts +54 -0
  132. package/dist/test-fixtures/prompt-context.d.ts.map +1 -0
  133. package/dist/test-fixtures/prompt-context.js +55 -0
  134. package/dist/test-fixtures/prompt-context.js.map +1 -0
  135. package/dist/types.d.ts +12 -2
  136. package/dist/types.d.ts.map +1 -1
  137. package/dist/workspace-git-stream.d.ts +19 -0
  138. package/dist/workspace-git-stream.d.ts.map +1 -0
  139. package/dist/workspace-git-stream.js +99 -0
  140. package/dist/workspace-git-stream.js.map +1 -0
  141. package/dist/workspace-git-stream.test.d.ts +2 -0
  142. package/dist/workspace-git-stream.test.d.ts.map +1 -0
  143. package/dist/workspace-git-stream.test.js +71 -0
  144. package/dist/workspace-git-stream.test.js.map +1 -0
  145. package/dist/workspace-manifest.d.ts +61 -0
  146. package/dist/workspace-manifest.d.ts.map +1 -0
  147. package/dist/workspace-manifest.js +257 -0
  148. package/dist/workspace-manifest.js.map +1 -0
  149. package/dist/workspace-manifest.test.d.ts +2 -0
  150. package/dist/workspace-manifest.test.d.ts.map +1 -0
  151. package/dist/workspace-manifest.test.js +160 -0
  152. package/dist/workspace-manifest.test.js.map +1 -0
  153. package/dist/workspace-restore-diagnostics.d.ts +34 -0
  154. package/dist/workspace-restore-diagnostics.d.ts.map +1 -0
  155. package/dist/workspace-restore-diagnostics.js +238 -0
  156. package/dist/workspace-restore-diagnostics.js.map +1 -0
  157. package/dist/workspace-restore-diagnostics.test.d.ts +2 -0
  158. package/dist/workspace-restore-diagnostics.test.d.ts.map +1 -0
  159. package/dist/workspace-restore-diagnostics.test.js +258 -0
  160. package/dist/workspace-restore-diagnostics.test.js.map +1 -0
  161. package/dist/workspace-restore-merge.d.ts +47 -6
  162. package/dist/workspace-restore-merge.d.ts.map +1 -1
  163. package/dist/workspace-restore-merge.js +382 -114
  164. package/dist/workspace-restore-merge.js.map +1 -1
  165. package/dist/workspace-restore-merge.test.js +253 -4
  166. package/dist/workspace-restore-merge.test.js.map +1 -1
  167. package/dist/workspace-restore-result.d.ts +6 -0
  168. package/dist/workspace-restore-result.d.ts.map +1 -0
  169. package/dist/workspace-restore-result.js +72 -0
  170. package/dist/workspace-restore-result.js.map +1 -0
  171. package/dist/workspace-restore-result.test.d.ts +2 -0
  172. package/dist/workspace-restore-result.test.d.ts.map +1 -0
  173. package/dist/workspace-restore-result.test.js +74 -0
  174. package/dist/workspace-restore-result.test.js.map +1 -0
  175. package/dist/workspace-restore-teardown.d.ts.map +1 -1
  176. package/dist/workspace-restore-teardown.js +5 -3
  177. package/dist/workspace-restore-teardown.js.map +1 -1
  178. package/dist/workspace-restore-teardown.test.js +24 -0
  179. package/dist/workspace-restore-teardown.test.js.map +1 -1
  180. package/node_modules/acpx/dist/live-checkpoint-ClPCSdrW.js +4 -3
  181. package/package.json +2 -2
@@ -5,7 +5,7 @@ import os from "node:os";
5
5
  import path from "node:path";
6
6
  import { describe, expect, it } from "vitest";
7
7
  import { CONNECTION_INTENT_AGENT_GUIDANCE } from "@tickernelz/paperclip-pro-shared";
8
- import { readPaperclipRuntimeSkillEntries, applyPaperclipWorkspaceEnv, appendWithByteCap, buildPersistentSkillSnapshot, buildRuntimeMountedSkillSnapshot, buildInvocationEnvForLogs, buildPaperclipEnv, buildRuntimeToolsEnv, DEFAULT_PAPERCLIP_AGENT_PROMPT_TEMPLATE, DEFAULT_PAPERCLIP_CONVERSATION_PROMPT_TEMPLATE, isPaperclipExternalChatContractTurn, isPaperclipExternalChatQuestionResponseTurn, isPaperclipExternalChatTurn, materializePaperclipSkillCopy, PAPERCLIP_OPERATIONAL_SKILL_KEY, refreshPaperclipWorkspaceEnvForExecution, renderPaperclipWakePrompt, resolveLegacyPaperclipDesiredSkillNames, resolvePaperclipDesiredSkillNames, selectPaperclipTaskMarkdown, selectInitialCommunicationGuidance, runningProcesses, runChildProcess, sanitizeSshRemoteEnv, signalRunningProcess, shapePaperclipWorkspaceEnvForExecution, rewriteWorkspaceCwdEnvVarsForExecution, stringifyPaperclipWakePayload, ensurePaperclipSkillSymlink, relinkPaperclipSkillsFromOtherInstalls, UNMANAGED_BACKGROUND_TASK_LIVENESS_REASON, UNMANAGED_BACKGROUND_TASK_STOP_REASON, WATCHDOG_DEFAULT_MANDATE, } from "./server-utils.js";
8
+ import { readPaperclipRuntimeSkillEntries, applyPaperclipWorkspaceEnv, appendWithByteCap, buildPersistentSkillSnapshot, buildRuntimeMountedSkillSnapshot, buildInvocationEnvForLogs, buildPaperclipEnv, buildRuntimeToolsEnv, DEFAULT_PAPERCLIP_AGENT_PROMPT_TEMPLATE, DEFAULT_PAPERCLIP_CONVERSATION_PROMPT_TEMPLATE, paperclipAgentPromptTemplate, isPaperclipExternalChatContractTurn, isPaperclipExternalChatQuestionResponseTurn, isPaperclipExternalChatTurn, materializePaperclipSkillCopy, PAPERCLIP_OPERATIONAL_SKILL_KEY, refreshPaperclipWorkspaceEnvForExecution, renderPaperclipWakePrompt, resolveLegacyPaperclipDesiredSkillNames, resolvePaperclipDesiredSkillNames, selectPaperclipTaskMarkdown, selectInitialCommunicationGuidance, hydrateFreshSessionHandoff, runningProcesses, runChildProcess, sanitizeSshRemoteEnv, signalRunningProcess, shapePaperclipWorkspaceEnvForExecution, rewriteWorkspaceCwdEnvVarsForExecution, stringifyPaperclipWakePayload, ensurePaperclipSkillSymlink, relinkPaperclipSkillsFromOtherInstalls, UNMANAGED_BACKGROUND_TASK_LIVENESS_REASON, UNMANAGED_BACKGROUND_TASK_STOP_REASON, WATCHDOG_DEFAULT_MANDATE, } from "./server-utils.js";
9
9
  describe("ensurePaperclipSkillSymlink", () => {
10
10
  const bundled = (root, version, skill) => path.join(root, "installs", version, "node_modules", "@tickernelz", "paperclip-pro-server", "skills", skill);
11
11
  it("moves a skill link from an older Paperclip install to the current one and leaves foreign links alone", async () => {
@@ -723,7 +723,7 @@ describe("renderPaperclipWakePrompt", () => {
723
723
  fallbackFetchNeeded: false,
724
724
  };
725
725
  const ordinary = renderPaperclipWakePrompt(payload, { resumedSession: true });
726
- expect(ordinary).toContain("Execution contract:");
726
+ expect(ordinary).not.toContain("Execution contract:");
727
727
  expect(ordinary).toContain("Create child issues from the approved plan");
728
728
  for (const resumedSession of [false, true]) {
729
729
  const chat = renderPaperclipWakePrompt(payload, {
@@ -930,7 +930,7 @@ describe("renderPaperclipWakePrompt", () => {
930
930
  }, { resumedSession: true });
931
931
  expect(incompleteResumePrompt).not.toContain("## External chat response contract");
932
932
  expect(incompleteResumePrompt).toContain("[continuation summary truncated]");
933
- expect(incompleteResumePrompt).toContain("a successful process exit or final response is not sufficient");
933
+ expect(incompleteResumePrompt).not.toContain("a successful process exit or final response is not sufficient");
934
934
  });
935
935
  it("uses only the run-scoped reader when an authenticated chat wake does not fit inline", () => {
936
936
  const readerWake = {
@@ -1114,33 +1114,42 @@ describe("renderPaperclipWakePrompt", () => {
1114
1114
  });
1115
1115
  expect(renderPaperclipWakePrompt(payload)).not.toContain("Issue description:");
1116
1116
  });
1117
- it("keeps the default local-agent prompt action-oriented", () => {
1118
- expect(DEFAULT_PAPERCLIP_AGENT_PROMPT_TEMPLATE).toContain("Start actionable work in this heartbeat");
1119
- expect(DEFAULT_PAPERCLIP_AGENT_PROMPT_TEMPLATE).toContain("do not stop at a plan");
1120
- expect(DEFAULT_PAPERCLIP_AGENT_PROMPT_TEMPLATE).toContain("clear final disposition");
1121
- expect(DEFAULT_PAPERCLIP_AGENT_PROMPT_TEMPLATE).toContain("evidence, not valid liveness paths by themselves");
1122
- expect(DEFAULT_PAPERCLIP_AGENT_PROMPT_TEMPLATE).toContain("keep `in_progress` only when a live continuation path exists");
1123
- expect(DEFAULT_PAPERCLIP_AGENT_PROMPT_TEMPLATE).toContain("Prefer the smallest verification that proves the change");
1124
- expect(DEFAULT_PAPERCLIP_AGENT_PROMPT_TEMPLATE).toContain("After 2 consecutive failures of the same control-plane write");
1125
- expect(DEFAULT_PAPERCLIP_AGENT_PROMPT_TEMPLATE).toContain("adapter/runtime status channel as the sanctioned fallback");
1126
- expect(DEFAULT_PAPERCLIP_AGENT_PROMPT_TEMPLATE).toContain("Use child issues");
1127
- expect(DEFAULT_PAPERCLIP_AGENT_PROMPT_TEMPLATE).toContain("instead of polling agents, sessions, or processes");
1128
- expect(DEFAULT_PAPERCLIP_AGENT_PROMPT_TEMPLATE).toContain("Create child issues directly when you know what needs to be done");
1129
- expect(DEFAULT_PAPERCLIP_AGENT_PROMPT_TEMPLATE).toContain("using `issueId: $PAPERCLIP_TASK_ID`");
1117
+ it("keeps task and chat defaults to identity and connection guidance", () => {
1118
+ for (const template of [
1119
+ DEFAULT_PAPERCLIP_AGENT_PROMPT_TEMPLATE,
1120
+ DEFAULT_PAPERCLIP_CONVERSATION_PROMPT_TEMPLATE,
1121
+ ]) {
1122
+ expect(template).toBe(`You are agent {{agent.id}} ({{agent.name}}).\n\n${CONNECTION_INTENT_AGENT_GUIDANCE}`);
1123
+ }
1124
+ });
1125
+ it("keeps the explicit Paperclip MCP contract action-oriented", () => {
1126
+ const template = paperclipAgentPromptTemplate("mcp");
1127
+ expect(template).toContain("Start actionable work in this heartbeat");
1128
+ expect(template).toContain("do not stop at a plan");
1129
+ expect(template).toContain("clear final disposition");
1130
+ expect(template).toContain("evidence, not valid liveness paths by themselves");
1131
+ expect(template).toContain("keep `in_progress` only when a live continuation path exists");
1132
+ expect(template).toContain("Prefer the smallest verification that proves the change");
1133
+ expect(template).toContain("After 2 consecutive failures of the same control-plane write");
1134
+ expect(template).toContain("adapter/runtime status channel as the sanctioned fallback");
1135
+ expect(template).toContain("Use child issues");
1136
+ expect(template).toContain("instead of polling agents, sessions, or processes");
1137
+ expect(template).toContain("Create child issues directly when you know what needs to be done");
1138
+ expect(template).toContain("using `issueId: $PAPERCLIP_TASK_ID`");
1130
1139
  // URL paths in prompt text carry real ids or env vars, never brace
1131
1140
  // placeholders: agents paste these lines verbatim, and a literal {issueId}
1132
1141
  // reaches the server as /api/issues/%7BissueId%7D and 404s.
1133
- expect(DEFAULT_PAPERCLIP_AGENT_PROMPT_TEMPLATE).not.toContain("/api/issues/{issueId}");
1134
- expect(DEFAULT_PAPERCLIP_AGENT_PROMPT_TEMPLATE).not.toContain("/api/issues/{id}");
1135
- expect(DEFAULT_PAPERCLIP_AGENT_PROMPT_TEMPLATE).toContain("`paperclipSuggestTasks`, `paperclipAskUserQuestions`, or `paperclipRequestConfirmation`");
1136
- expect(DEFAULT_PAPERCLIP_AGENT_PROMPT_TEMPLATE).toContain("Use continuationPolicy wake_assignee when you need to resume after a response (it wakes on acceptance and rejection alike; only expiry does not wake); use wake_assignee_on_accept when you want to resume only after acceptance");
1137
- expect(DEFAULT_PAPERCLIP_AGENT_PROMPT_TEMPLATE).not.toContain("for request_confirmation this resumes only after acceptance");
1138
- expect(DEFAULT_PAPERCLIP_AGENT_PROMPT_TEMPLATE).toContain("Never create probe or throwaway issue-thread interactions to discover the interactions API shape or your permissions");
1139
- expect(DEFAULT_PAPERCLIP_AGENT_PROMPT_TEMPLATE).toContain("confirmation:{issueId}:plan:{revisionId}");
1140
- expect(DEFAULT_PAPERCLIP_AGENT_PROMPT_TEMPLATE).toContain("Wait for acceptance before creating implementation subtasks");
1141
- expect(DEFAULT_PAPERCLIP_AGENT_PROMPT_TEMPLATE).toContain("Respect budget, pause/cancel, approval gates, and company boundaries");
1142
- });
1143
- it("leaves the execution contract to the heartbeat template on fresh scoped wake prompts", () => {
1142
+ expect(template).not.toContain("/api/issues/{issueId}");
1143
+ expect(template).not.toContain("/api/issues/{id}");
1144
+ expect(template).toContain("`paperclipSuggestTasks`, `paperclipAskUserQuestions`, or `paperclipRequestConfirmation`");
1145
+ expect(template).toContain("Use continuationPolicy wake_assignee when you need to resume after a response (it wakes on acceptance and rejection alike; only expiry does not wake); use wake_assignee_on_accept when you want to resume only after acceptance");
1146
+ expect(template).not.toContain("for request_confirmation this resumes only after acceptance");
1147
+ expect(template).toContain("Never create probe or throwaway issue-thread interactions to discover the interactions API shape or your permissions");
1148
+ expect(template).toContain("confirmation:{issueId}:plan:{revisionId}");
1149
+ expect(template).toContain("Wait for acceptance before creating implementation subtasks");
1150
+ expect(template).toContain("Respect budget, pause/cancel, approval gates, and company boundaries");
1151
+ });
1152
+ it("keeps current task data without generic procedures on fresh scoped wake prompts", () => {
1144
1153
  const prompt = renderPaperclipWakePrompt({
1145
1154
  reason: "issue_assigned",
1146
1155
  issue: {
@@ -1159,9 +1168,9 @@ describe("renderPaperclipWakePrompt", () => {
1159
1168
  });
1160
1169
  expect(prompt).toContain("## Paperclip Wake Payload");
1161
1170
  expect(prompt).not.toContain("Execution contract:");
1162
- expect(DEFAULT_PAPERCLIP_AGENT_PROMPT_TEMPLATE).toContain("Execution contract:");
1171
+ expect(DEFAULT_PAPERCLIP_AGENT_PROMPT_TEMPLATE).not.toContain("Execution contract:");
1163
1172
  });
1164
- it("adds the execution contract to resume delta prompts and opted-in fresh prompts", () => {
1173
+ it("does not restore generic procedures on resume or with the legacy opt-in", () => {
1165
1174
  const payload = {
1166
1175
  reason: "issue_assigned",
1167
1176
  issue: {
@@ -1178,20 +1187,19 @@ describe("renderPaperclipWakePrompt", () => {
1178
1187
  comments: [],
1179
1188
  fallbackFetchNeeded: false,
1180
1189
  };
1181
- for (const prompt of [
1182
- renderPaperclipWakePrompt(payload, { resumedSession: true }),
1183
- renderPaperclipWakePrompt(payload, { includeExecutionContract: true }),
1184
- ]) {
1185
- expect(prompt).toContain("Execution contract: take concrete action in this heartbeat");
1186
- expect(prompt).toContain("clear final disposition");
1187
- expect(prompt).toContain("Immediately before returning, verify that Paperclip records one of those dispositions");
1188
- expect(prompt).toContain("a successful process exit or final response is not sufficient");
1189
- expect(prompt).toContain("If no valid disposition is recorded, record it now and do not end the run");
1190
- expect(prompt).toContain("After 2 consecutive failures of the same control-plane write");
1191
- expect(prompt).toContain("adapter/runtime status channel as the sanctioned fallback");
1192
- expect(prompt).toContain("evidence, not valid liveness paths by themselves");
1193
- expect(prompt).toContain("Use child issues for long or parallel delegated work instead of polling");
1194
- expect(prompt).toContain("named unblock owner/action");
1190
+ for (const resumedSession of [false, true]) {
1191
+ for (const includeExecutionContract of [undefined, false, true]) {
1192
+ const prompt = renderPaperclipWakePrompt(payload, {
1193
+ resumedSession, includeExecutionContract,
1194
+ });
1195
+ expect(prompt).toContain(resumedSession ? "## Paperclip Resume Delta" : "## Paperclip Wake Payload");
1196
+ expect(prompt).toContain("- reason: issue_assigned");
1197
+ expect(prompt).toContain("- issue: PAP-1580 Update prompts");
1198
+ expect(prompt).toContain("- issue status: in_progress");
1199
+ expect(prompt).not.toContain("Execution contract:");
1200
+ expect(prompt).not.toContain("clear final disposition");
1201
+ expect(prompt).not.toContain("do not end the run");
1202
+ }
1195
1203
  }
1196
1204
  });
1197
1205
  it.each(["answered", "accepted"])("preserves exact-output constraints for an external %s interaction continuation", (interactionStatus) => {
@@ -1501,7 +1509,7 @@ describe("renderPaperclipWakePrompt", () => {
1501
1509
  });
1502
1510
  expect(prompt).toContain("Do not narrate the recovery in your next comment — at most one short sentence; lead with the work.");
1503
1511
  });
1504
- it("keeps exactly one execution contract in a composed fresh heartbeat prompt", () => {
1512
+ it("keeps connection guidance without a generic manual in a composed fresh prompt", () => {
1505
1513
  const wakePrompt = renderPaperclipWakePrompt({
1506
1514
  reason: "issue_assigned",
1507
1515
  issue: {
@@ -1519,7 +1527,8 @@ describe("renderPaperclipWakePrompt", () => {
1519
1527
  fallbackFetchNeeded: false,
1520
1528
  });
1521
1529
  const composed = [wakePrompt, DEFAULT_PAPERCLIP_AGENT_PROMPT_TEMPLATE].join("\n\n");
1522
- expect(composed.match(/Execution contract/g)).toHaveLength(1);
1530
+ expect(composed).toContain(CONNECTION_INTENT_AGENT_GUIDANCE);
1531
+ expect(composed).not.toContain("Execution contract:");
1523
1532
  });
1524
1533
  it("trims comment-batch boilerplate on fresh wakes with zero pending comments", () => {
1525
1534
  const base = {
@@ -2320,6 +2329,16 @@ describe("renderPaperclipWakePrompt", () => {
2320
2329
  expect(prompt).toContain("Please focus on edge cases and leave a short risk summary.");
2321
2330
  expect(prompt).toContain("You are waking as the active reviewer for this issue.");
2322
2331
  });
2332
+ it("delivers typed disposition repair instructions without liveness classification", () => {
2333
+ const payload = { reason: "issue_disposition_repair", issue: { id: "issue-1", status: "in_progress" },
2334
+ dispositionRepair: { attempt: 1, maxAttempts: 2, sourceRunId: "source-1", instruction: "Record completion or a durable waiting path through the API." } };
2335
+ const prompt = renderPaperclipWakePrompt(payload);
2336
+ expect(prompt).toContain("Task disposition repair:");
2337
+ expect(prompt).toContain("- attempt: 1/2");
2338
+ expect(prompt).toContain(payload.dispositionRepair.instruction);
2339
+ expect(prompt).not.toContain("liveness state:");
2340
+ expect(JSON.parse(stringifyPaperclipWakePayload(payload))).toMatchObject({ dispositionRepair: payload.dispositionRepair });
2341
+ });
2323
2342
  it("includes continuation and child issue summaries in structured wake context", () => {
2324
2343
  const payload = {
2325
2344
  reason: "issue_children_completed",
@@ -2433,6 +2452,18 @@ describe("selectPaperclipTaskMarkdown", () => {
2433
2452
  expect(selectPaperclipTaskMarkdown(context)).toBe(fullMarkdown);
2434
2453
  expect(selectPaperclipTaskMarkdown({ ...context, paperclipWake: wake("issue_assigned") }, { resumedSession: true })).toBe(fullMarkdown);
2435
2454
  });
2455
+ it("prefers assignment-only fields while preserving historical fallback fields", () => {
2456
+ const context = {
2457
+ paperclipTaskMarkdown: `${fullMarkdown}\nHistorical wake comment`,
2458
+ paperclipTaskMarkdownCompact: `${compactMarkdown}\nHistorical compact comment`,
2459
+ paperclipTaskMarkdownAssignment: fullMarkdown,
2460
+ paperclipTaskMarkdownAssignmentCompact: compactMarkdown,
2461
+ paperclipWake: wake("issue_commented"),
2462
+ };
2463
+ expect(selectPaperclipTaskMarkdown(context)).toBe(fullMarkdown);
2464
+ expect(selectPaperclipTaskMarkdown(context, { resumedSession: true })).toBe(compactMarkdown);
2465
+ expect(selectPaperclipTaskMarkdown({ paperclipTaskMarkdown: "legacy", paperclipWake: wake("issue_commented") })).toBe("legacy");
2466
+ });
2436
2467
  it("returns the compact markdown for non-assignment resume deltas", () => {
2437
2468
  expect(selectPaperclipTaskMarkdown({
2438
2469
  paperclipTaskMarkdown: fullMarkdown,
@@ -2451,12 +2482,28 @@ describe("selectPaperclipTaskMarkdown", () => {
2451
2482
  expect(selectInitialCommunicationGuidance({ paperclipTaskCommunicationGuidance: " Slack preference " })).toBe("Slack preference");
2452
2483
  expect(selectInitialCommunicationGuidance(context, { resumedSession: true })).toBe("");
2453
2484
  expect(selectInitialCommunicationGuidance({})).toBe("");
2485
+ const handoffContext = { ...context, paperclipFreshSessionHandoffMarkdown: "Prior goal and approved decisions" };
2486
+ expect(selectInitialCommunicationGuidance(handoffContext, { resumedSession: true })).toBe("");
2487
+ expect(selectInitialCommunicationGuidance(handoffContext, { resumedSession: false })).toContain("Prior goal and approved decisions");
2454
2488
  expect(selectPaperclipTaskMarkdown(context, { resumedSession: true })).toBe(compactMarkdown);
2455
2489
  expect(selectPaperclipTaskMarkdown(context, { includeCommunicationGuidance: false })).toBe(fullMarkdown);
2456
2490
  context.paperclipWake = { ...wake("issue_monitor_recovery"), recovery: { cause: "process_lost" } };
2457
2491
  expect(selectPaperclipTaskMarkdown(context, { resumedSession: true })).toBe(fullMarkdown);
2458
2492
  expect(selectPaperclipTaskMarkdown(context, { resumedSession: false }).match(/Saved initial guidance/g)).toHaveLength(1);
2459
2493
  });
2494
+ it("reads history only at a fresh provider attempt, including resume fallback", async () => {
2495
+ let reads = 0;
2496
+ const ctx = { context: {}, getFreshSessionHandoff: async () => {
2497
+ reads += 1;
2498
+ return "Original goal and prior answer";
2499
+ } };
2500
+ await hydrateFreshSessionHandoff(ctx, { resumedSession: true });
2501
+ expect(reads).toBe(0);
2502
+ expect(ctx.context.paperclipFreshSessionHandoffMarkdown).toBeUndefined();
2503
+ await hydrateFreshSessionHandoff(ctx, { resumedSession: false });
2504
+ expect(reads).toBe(1);
2505
+ expect(selectInitialCommunicationGuidance(ctx.context)).toContain("Original goal and prior answer");
2506
+ });
2460
2507
  it("falls back to the full markdown when no compact variant exists", () => {
2461
2508
  expect(selectPaperclipTaskMarkdown({
2462
2509
  paperclipTaskMarkdown: fullMarkdown,
@@ -3112,4 +3159,147 @@ describe("runtime skill assignment boundaries", () => {
3112
3159
  }
3113
3160
  });
3114
3161
  });
3162
+ describe("wake continuation comment ownership", () => {
3163
+ const continuation = (messages, resumeDelta) => ({
3164
+ version: 1,
3165
+ companyId: "company-1",
3166
+ issueId: "issue-1",
3167
+ trigger: { reason: "issue_commented", interactionId: null, sourceRunId: null },
3168
+ originCommentIds: [],
3169
+ objective: "Continue the task.",
3170
+ messages,
3171
+ ...(resumeDelta ? { resumeDelta: { baseRunId: "run-old", messages: resumeDelta } } : {}),
3172
+ interactionOutcomes: [],
3173
+ completedWork: null,
3174
+ unresolvedInteractionIds: [],
3175
+ coverage: { kind: "full_task_history", throughCommentId: null, summaryThroughCommentId: null },
3176
+ });
3177
+ const message = (id, body) => ({
3178
+ id,
3179
+ authorType: "user",
3180
+ authorId: "user-1",
3181
+ body,
3182
+ createdAt: "2026-09-21T00:00:00.000Z",
3183
+ updatedAt: "2026-09-21T00:00:00.000Z",
3184
+ deleted: false,
3185
+ sourceTrust: "human",
3186
+ });
3187
+ it("suppresses only an exact continuation owner and keeps same-body distinct IDs", () => {
3188
+ const prompt = renderPaperclipWakePrompt({
3189
+ reason: "issue_commented",
3190
+ issue: { id: "issue-1", identifier: "PAP-1", title: "Task", description: null },
3191
+ comments: [
3192
+ { id: "comment-a", body: "Repeat body" },
3193
+ { id: "comment-b", body: "Repeat body" },
3194
+ { id: "comment-edited", body: "Edited current body" },
3195
+ ],
3196
+ commentWindow: { requestedCount: 3, includedCount: 3, missingCount: 0 },
3197
+ fallbackFetchNeeded: false,
3198
+ executionContinuation: continuation([
3199
+ message("comment-a", "Repeat body"),
3200
+ message("comment-edited", "Original body"),
3201
+ ]),
3202
+ });
3203
+ expect(prompt).toContain("comment-b");
3204
+ expect(prompt).toContain("comment-edited");
3205
+ expect(prompt).not.toContain("comment-a at");
3206
+ });
3207
+ it("does not suppress a current comment absent from the rendered resume delta", () => {
3208
+ const prompt = renderPaperclipWakePrompt({
3209
+ reason: "issue_commented",
3210
+ issue: { id: "issue-1", identifier: "PAP-1", title: "Task", description: null },
3211
+ comments: [{ id: "comment-new", body: "Current delta body" }],
3212
+ commentWindow: { requestedCount: 1, includedCount: 1, missingCount: 0 },
3213
+ fallbackFetchNeeded: false,
3214
+ executionContinuation: continuation([message("comment-new", "Current delta body")], []),
3215
+ }, { resumedSession: true });
3216
+ expect(prompt).toContain("comment-new");
3217
+ expect(prompt).toContain("Current delta body");
3218
+ });
3219
+ it("does not repeat the shared issue brief as continuation objective on a fresh owned wake", () => {
3220
+ const objective = "Assignment brief owned by task markdown.";
3221
+ const prompt = renderPaperclipWakePrompt({
3222
+ reason: "issue_assigned",
3223
+ issue: { id: "issue-1", identifier: "PAP-1", title: "Task", description: objective },
3224
+ comments: [],
3225
+ commentWindow: { requestedCount: 0, includedCount: 0, missingCount: 0 },
3226
+ fallbackFetchNeeded: false,
3227
+ executionContinuation: { ...continuation([]), objective, objectiveSource: { kind: "description", id: "issue-1", revision: "3afeec397239f147627f99ddaa883639fa50d191fc2bbce0dc7515a0f05deedb" } },
3228
+ }, { suppressIssueDescription: true });
3229
+ expect(prompt).not.toContain(`"objective":"${objective}"`);
3230
+ });
3231
+ it("keeps a changed continuation objective on an ordinary compact resume", () => {
3232
+ const objective = "Changed objective must reach the compact resumed turn.";
3233
+ const prompt = renderPaperclipWakePrompt({
3234
+ reason: "issue_commented",
3235
+ issue: { id: "issue-1", identifier: "PAP-1", title: "Task", description: "Original brief" },
3236
+ comments: [],
3237
+ commentWindow: { requestedCount: 0, includedCount: 0, missingCount: 0 },
3238
+ fallbackFetchNeeded: false,
3239
+ executionContinuation: { ...continuation([]), objective, objectiveSource: { kind: "description", id: "issue-1", revision: "3fdd2539337403e2e9551085a24ce3737978e6f0d2fc129d71d6a27c2befb523" } },
3240
+ }, { resumedSession: true, suppressIssueDescription: true });
3241
+ expect(prompt).toContain(`"objective":"${objective}"`);
3242
+ });
3243
+ it("keeps continuation objective when its issue identity does not match the assignment", () => {
3244
+ const objective = "Mismatched continuation objective remains visible.";
3245
+ const prompt = renderPaperclipWakePrompt({
3246
+ reason: "issue_assigned",
3247
+ issue: { id: "issue-1", identifier: "PAP-1", title: "Task", description: "Assignment brief" },
3248
+ comments: [],
3249
+ commentWindow: { requestedCount: 0, includedCount: 0, missingCount: 0 },
3250
+ fallbackFetchNeeded: false,
3251
+ executionContinuation: { ...continuation([]), issueId: "issue-2", objective, objectiveSource: { kind: "description", id: "issue-2", revision: "ba02348ecb9f87dd102e0faf7ae65731c856c8a13ba64874394a873cc529c63f" } },
3252
+ }, { suppressIssueDescription: true });
3253
+ expect(prompt).toContain(`"objective":"${objective}"`);
3254
+ });
3255
+ it("suppresses a latest comment objective only when its exact revised message is displayed", () => {
3256
+ const objective = "Latest user direction.";
3257
+ const source = { ...message("comment-latest", objective), updatedAt: "2026-09-21T00:02:00.000Z" };
3258
+ const full = renderPaperclipWakePrompt({
3259
+ reason: "issue_commented",
3260
+ issue: { id: "issue-1", identifier: "PAP-1", title: "Task", description: "Assignment brief" },
3261
+ comments: [{ id: "comment-latest", body: objective }],
3262
+ commentWindow: { requestedCount: 1, includedCount: 1, missingCount: 0 },
3263
+ fallbackFetchNeeded: false,
3264
+ executionContinuation: { ...continuation([source]), objective, objectiveSource: { kind: "comment", id: source.id, revision: source.updatedAt } },
3265
+ }, { suppressIssueDescription: true });
3266
+ expect(full).not.toContain(`"objective":"${objective}"`);
3267
+ const stale = renderPaperclipWakePrompt({
3268
+ reason: "issue_commented",
3269
+ issue: { id: "issue-1", identifier: "PAP-1", title: "Task", description: "Assignment brief" },
3270
+ comments: [{ id: "comment-latest", body: objective }],
3271
+ commentWindow: { requestedCount: 1, includedCount: 1, missingCount: 0 },
3272
+ fallbackFetchNeeded: false,
3273
+ executionContinuation: { ...continuation([source]), objective, objectiveSource: { kind: "comment", id: source.id, revision: "stale-revision" } },
3274
+ }, { suppressIssueDescription: true });
3275
+ expect(stale).toContain(`"objective":"${objective}"`);
3276
+ const wrongSource = renderPaperclipWakePrompt({
3277
+ reason: "issue_commented",
3278
+ issue: { id: "issue-1", identifier: "PAP-1", title: "Task", description: "Assignment brief" },
3279
+ comments: [{ id: "comment-latest", body: objective }],
3280
+ commentWindow: { requestedCount: 1, includedCount: 1, missingCount: 0 },
3281
+ fallbackFetchNeeded: false,
3282
+ executionContinuation: { ...continuation([source]), objective, objectiveSource: { kind: "comment", id: "comment-other", revision: source.updatedAt } },
3283
+ }, { suppressIssueDescription: true });
3284
+ expect(wrongSource).toContain(`"objective":"${objective}"`);
3285
+ const missingDelta = renderPaperclipWakePrompt({
3286
+ reason: "issue_commented",
3287
+ issue: { id: "issue-1", identifier: "PAP-1", title: "Task", description: "Assignment brief" },
3288
+ comments: [{ id: "comment-latest", body: objective }],
3289
+ commentWindow: { requestedCount: 1, includedCount: 1, missingCount: 0 },
3290
+ fallbackFetchNeeded: false,
3291
+ executionContinuation: { ...continuation([source], []), objective, objectiveSource: { kind: "comment", id: source.id, revision: source.updatedAt } },
3292
+ }, { resumedSession: true, suppressIssueDescription: true });
3293
+ expect(missingDelta).toContain(`"objective":"${objective}"`);
3294
+ const legacy = renderPaperclipWakePrompt({
3295
+ reason: "issue_commented",
3296
+ issue: { id: "issue-1", identifier: "PAP-1", title: "Task", description: "Assignment brief" },
3297
+ comments: [],
3298
+ commentWindow: { requestedCount: 0, includedCount: 0, missingCount: 0 },
3299
+ fallbackFetchNeeded: false,
3300
+ executionContinuation: { ...continuation([]), objective },
3301
+ }, { suppressIssueDescription: true });
3302
+ expect(legacy).toContain(`"objective":"${objective}"`);
3303
+ });
3304
+ });
3115
3305
  //# sourceMappingURL=server-utils.test.js.map