@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.
- package/README.md +13 -0
- package/dist/acpx-engine/ephemeral-session-environment.d.ts +4 -0
- package/dist/acpx-engine/ephemeral-session-environment.d.ts.map +1 -0
- package/dist/acpx-engine/ephemeral-session-environment.js +29 -0
- package/dist/acpx-engine/ephemeral-session-environment.js.map +1 -0
- package/dist/acpx-engine/ephemeral-session-environment.test.d.ts +2 -0
- package/dist/acpx-engine/ephemeral-session-environment.test.d.ts.map +1 -0
- package/dist/acpx-engine/ephemeral-session-environment.test.js +177 -0
- package/dist/acpx-engine/ephemeral-session-environment.test.js.map +1 -0
- package/dist/acpx-engine/execute.d.ts +2 -6
- package/dist/acpx-engine/execute.d.ts.map +1 -1
- package/dist/acpx-engine/execute.js +125 -62
- package/dist/acpx-engine/execute.js.map +1 -1
- package/dist/acpx-engine/execute.test.js +299 -3
- package/dist/acpx-engine/execute.test.js.map +1 -1
- package/dist/acpx-engine/session-codec.d.ts.map +1 -1
- package/dist/acpx-engine/session-codec.js +1 -0
- package/dist/acpx-engine/session-codec.js.map +1 -1
- package/dist/acpx-engine/settlement-characterization.test.js +105 -0
- package/dist/acpx-engine/settlement-characterization.test.js.map +1 -1
- package/dist/acpx-engine/spawn-smoke.test.js +16 -7
- package/dist/acpx-engine/spawn-smoke.test.js.map +1 -1
- package/dist/acpx-engine/startup-cancellation.d.ts +1 -0
- package/dist/acpx-engine/startup-cancellation.d.ts.map +1 -1
- package/dist/acpx-engine/startup-cancellation.js +7 -2
- package/dist/acpx-engine/startup-cancellation.js.map +1 -1
- package/dist/acpx-engine/terminal-session-failure.d.ts +18 -0
- package/dist/acpx-engine/terminal-session-failure.d.ts.map +1 -0
- package/dist/acpx-engine/terminal-session-failure.js +106 -0
- package/dist/acpx-engine/terminal-session-failure.js.map +1 -0
- package/dist/acpx-engine/terminal-session-failure.test.d.ts +2 -0
- package/dist/acpx-engine/terminal-session-failure.test.d.ts.map +1 -0
- package/dist/acpx-engine/terminal-session-failure.test.js +139 -0
- package/dist/acpx-engine/terminal-session-failure.test.js.map +1 -0
- package/dist/acpx-engine/turn-characterization.test.js +4 -3
- package/dist/acpx-engine/turn-characterization.test.js.map +1 -1
- package/dist/command-managed-runtime.d.ts +2 -0
- package/dist/command-managed-runtime.d.ts.map +1 -1
- package/dist/command-managed-runtime.js +2 -0
- package/dist/command-managed-runtime.js.map +1 -1
- package/dist/command-redaction.d.ts +2 -0
- package/dist/command-redaction.d.ts.map +1 -1
- package/dist/command-redaction.js +20 -10
- package/dist/command-redaction.js.map +1 -1
- package/dist/command-redaction.test.js +20 -1
- package/dist/command-redaction.test.js.map +1 -1
- package/dist/directory-merge-lock.test.d.ts +2 -0
- package/dist/directory-merge-lock.test.d.ts.map +1 -0
- package/dist/directory-merge-lock.test.js +166 -0
- package/dist/directory-merge-lock.test.js.map +1 -0
- package/dist/execution-phase.d.ts +10 -0
- package/dist/execution-phase.d.ts.map +1 -0
- package/dist/execution-phase.js +27 -0
- package/dist/execution-phase.js.map +1 -0
- package/dist/execution-phase.test.d.ts +2 -0
- package/dist/execution-phase.test.d.ts.map +1 -0
- package/dist/execution-phase.test.js +26 -0
- package/dist/execution-phase.test.js.map +1 -0
- package/dist/execution-target-sandbox.test.js +49 -7
- package/dist/execution-target-sandbox.test.js.map +1 -1
- package/dist/execution-target-stdin-race.test.js +245 -2
- package/dist/execution-target-stdin-race.test.js.map +1 -1
- package/dist/execution-target.d.ts +6 -0
- package/dist/execution-target.d.ts.map +1 -1
- package/dist/execution-target.js +74 -22
- package/dist/execution-target.js.map +1 -1
- package/dist/git-workspace-sync.d.ts +15 -5
- package/dist/git-workspace-sync.d.ts.map +1 -1
- package/dist/git-workspace-sync.js +267 -124
- package/dist/git-workspace-sync.js.map +1 -1
- package/dist/git-workspace-sync.test.js +296 -20
- package/dist/git-workspace-sync.test.js.map +1 -1
- package/dist/github-launcher.d.ts.map +1 -1
- package/dist/github-launcher.js +26 -11
- package/dist/github-launcher.js.map +1 -1
- package/dist/github-launcher.test.js +34 -1
- package/dist/github-launcher.test.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/paperclip-runner-permissions.d.ts +28 -1
- package/dist/paperclip-runner-permissions.d.ts.map +1 -1
- package/dist/paperclip-runner-permissions.js +9 -1
- package/dist/paperclip-runner-permissions.js.map +1 -1
- package/dist/prompt-sections.test.d.ts +2 -0
- package/dist/prompt-sections.test.d.ts.map +1 -0
- package/dist/prompt-sections.test.js +53 -0
- package/dist/prompt-sections.test.js.map +1 -0
- package/dist/provider-stopped-boundary.d.ts +7 -0
- package/dist/provider-stopped-boundary.d.ts.map +1 -0
- package/dist/provider-stopped-boundary.js +22 -0
- package/dist/provider-stopped-boundary.js.map +1 -0
- package/dist/provider-stopped-boundary.test.d.ts +2 -0
- package/dist/provider-stopped-boundary.test.d.ts.map +1 -0
- package/dist/provider-stopped-boundary.test.js +76 -0
- package/dist/provider-stopped-boundary.test.js.map +1 -0
- package/dist/remote-managed-runtime.d.ts +2 -0
- package/dist/remote-managed-runtime.d.ts.map +1 -1
- package/dist/remote-managed-runtime.js +3 -1
- package/dist/remote-managed-runtime.js.map +1 -1
- package/dist/remote-managed-runtime.test.js +25 -0
- package/dist/remote-managed-runtime.test.js.map +1 -1
- package/dist/sandbox-callback-bridge.d.ts +2 -0
- package/dist/sandbox-callback-bridge.d.ts.map +1 -1
- package/dist/sandbox-callback-bridge.js +34 -10
- package/dist/sandbox-callback-bridge.js.map +1 -1
- package/dist/sandbox-callback-bridge.test.js +106 -3
- package/dist/sandbox-callback-bridge.test.js.map +1 -1
- package/dist/sandbox-managed-runtime.d.ts +3 -0
- package/dist/sandbox-managed-runtime.d.ts.map +1 -1
- package/dist/sandbox-managed-runtime.js +749 -718
- package/dist/sandbox-managed-runtime.js.map +1 -1
- package/dist/sandbox-managed-runtime.test.js +171 -2
- package/dist/sandbox-managed-runtime.test.js.map +1 -1
- package/dist/server-utils.d.ts +43 -1
- package/dist/server-utils.d.ts.map +1 -1
- package/dist/server-utils.js +103 -31
- package/dist/server-utils.js.map +1 -1
- package/dist/server-utils.test.js +235 -45
- package/dist/server-utils.test.js.map +1 -1
- package/dist/session-compaction.d.ts.map +1 -1
- package/dist/session-compaction.js +6 -0
- package/dist/session-compaction.js.map +1 -1
- package/dist/ssh-fixture.test.js +22 -0
- package/dist/ssh-fixture.test.js.map +1 -1
- package/dist/ssh.d.ts +2 -0
- package/dist/ssh.d.ts.map +1 -1
- package/dist/ssh.js +2 -2
- package/dist/ssh.js.map +1 -1
- package/dist/test-fixtures/prompt-context.d.ts +54 -0
- package/dist/test-fixtures/prompt-context.d.ts.map +1 -0
- package/dist/test-fixtures/prompt-context.js +55 -0
- package/dist/test-fixtures/prompt-context.js.map +1 -0
- package/dist/types.d.ts +12 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/workspace-git-stream.d.ts +19 -0
- package/dist/workspace-git-stream.d.ts.map +1 -0
- package/dist/workspace-git-stream.js +99 -0
- package/dist/workspace-git-stream.js.map +1 -0
- package/dist/workspace-git-stream.test.d.ts +2 -0
- package/dist/workspace-git-stream.test.d.ts.map +1 -0
- package/dist/workspace-git-stream.test.js +71 -0
- package/dist/workspace-git-stream.test.js.map +1 -0
- package/dist/workspace-manifest.d.ts +61 -0
- package/dist/workspace-manifest.d.ts.map +1 -0
- package/dist/workspace-manifest.js +257 -0
- package/dist/workspace-manifest.js.map +1 -0
- package/dist/workspace-manifest.test.d.ts +2 -0
- package/dist/workspace-manifest.test.d.ts.map +1 -0
- package/dist/workspace-manifest.test.js +160 -0
- package/dist/workspace-manifest.test.js.map +1 -0
- package/dist/workspace-restore-diagnostics.d.ts +34 -0
- package/dist/workspace-restore-diagnostics.d.ts.map +1 -0
- package/dist/workspace-restore-diagnostics.js +238 -0
- package/dist/workspace-restore-diagnostics.js.map +1 -0
- package/dist/workspace-restore-diagnostics.test.d.ts +2 -0
- package/dist/workspace-restore-diagnostics.test.d.ts.map +1 -0
- package/dist/workspace-restore-diagnostics.test.js +258 -0
- package/dist/workspace-restore-diagnostics.test.js.map +1 -0
- package/dist/workspace-restore-merge.d.ts +47 -6
- package/dist/workspace-restore-merge.d.ts.map +1 -1
- package/dist/workspace-restore-merge.js +382 -114
- package/dist/workspace-restore-merge.js.map +1 -1
- package/dist/workspace-restore-merge.test.js +253 -4
- package/dist/workspace-restore-merge.test.js.map +1 -1
- package/dist/workspace-restore-result.d.ts +6 -0
- package/dist/workspace-restore-result.d.ts.map +1 -0
- package/dist/workspace-restore-result.js +72 -0
- package/dist/workspace-restore-result.js.map +1 -0
- package/dist/workspace-restore-result.test.d.ts +2 -0
- package/dist/workspace-restore-result.test.d.ts.map +1 -0
- package/dist/workspace-restore-result.test.js +74 -0
- package/dist/workspace-restore-result.test.js.map +1 -0
- package/dist/workspace-restore-teardown.d.ts.map +1 -1
- package/dist/workspace-restore-teardown.js +5 -3
- package/dist/workspace-restore-teardown.js.map +1 -1
- package/dist/workspace-restore-teardown.test.js +24 -0
- package/dist/workspace-restore-teardown.test.js.map +1 -1
- package/node_modules/acpx/dist/live-checkpoint-ClPCSdrW.js +4 -3
- 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
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
expect(
|
|
1128
|
-
expect(
|
|
1129
|
-
expect(
|
|
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(
|
|
1134
|
-
expect(
|
|
1135
|
-
expect(
|
|
1136
|
-
expect(
|
|
1137
|
-
expect(
|
|
1138
|
-
expect(
|
|
1139
|
-
expect(
|
|
1140
|
-
expect(
|
|
1141
|
-
expect(
|
|
1142
|
-
});
|
|
1143
|
-
it("
|
|
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("
|
|
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
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
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
|
|
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
|
|
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
|