@pellux/goodvibes-agent 1.1.7 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (274) hide show
  1. package/CHANGELOG.md +329 -0
  2. package/README.md +46 -30
  3. package/dist/package/{ast-grep-napi.linux-x64-gnu-mkk8xwww.node → ast-grep-napi.linux-x64-gnu-swtppvy9.node} +0 -0
  4. package/dist/package/{ast-grep-napi.linux-x64-musl-ryqtgdv6.node → ast-grep-napi.linux-x64-musl-ttfcdtap.node} +0 -0
  5. package/dist/package/main.js +138903 -91477
  6. package/docs/README.md +14 -7
  7. package/docs/channels-remote-and-api.md +8 -5
  8. package/docs/connected-host.md +14 -12
  9. package/docs/getting-started.md +58 -30
  10. package/docs/knowledge-artifacts-and-multimodal.md +7 -5
  11. package/docs/project-planning.md +2 -2
  12. package/docs/providers-and-routing.md +11 -3
  13. package/docs/tools-and-commands.md +116 -45
  14. package/docs/voice-and-live-tts.md +3 -1
  15. package/package.json +3 -2
  16. package/release/release-notes.md +142 -5
  17. package/release/release-readiness.json +124 -94
  18. package/src/agent/assistant-cockpit.ts +247 -0
  19. package/src/agent/autonomy-schedule-format.ts +96 -0
  20. package/src/agent/autonomy-schedule.ts +514 -0
  21. package/src/agent/channel-delivery-receipts.ts +292 -0
  22. package/src/agent/competitive-feature-inventory.ts +294 -0
  23. package/src/agent/document-registry-types.ts +106 -0
  24. package/src/agent/document-registry.ts +734 -0
  25. package/src/agent/harness-control.ts +2 -2
  26. package/src/agent/memory-prompt.ts +2 -2
  27. package/src/agent/operator-actions.ts +50 -6
  28. package/src/agent/persona-registry.ts +8 -0
  29. package/src/agent/project-context-files.ts +273 -0
  30. package/src/agent/prompt-context-receipts.ts +502 -0
  31. package/src/agent/reminder-schedule-format.ts +16 -2
  32. package/src/agent/research-run-registry.ts +582 -0
  33. package/src/agent/research-source-registry.ts +441 -0
  34. package/src/agent/routine-registry.ts +19 -3
  35. package/src/agent/routine-schedule-format.ts +27 -2
  36. package/src/agent/runtime-profile-starters.ts +7 -0
  37. package/src/agent/runtime-profile.ts +106 -4
  38. package/src/agent/schedule-edit-format.ts +129 -0
  39. package/src/agent/schedule-edit.ts +496 -0
  40. package/src/agent/schedule-next-routes.ts +42 -0
  41. package/src/agent/setup-wizard-artifact-receipts.ts +366 -0
  42. package/src/agent/setup-wizard-checkpoint.ts +140 -0
  43. package/src/agent/setup-wizard.ts +683 -0
  44. package/src/agent/skill-registry-types.ts +102 -0
  45. package/src/agent/skill-registry.ts +81 -107
  46. package/src/agent/vibe-confirmation-routes.ts +62 -0
  47. package/src/agent/vibe-file.ts +285 -0
  48. package/src/cli/agent-knowledge-command.ts +45 -2
  49. package/src/cli/help.ts +3 -0
  50. package/src/cli/profiles-command.ts +25 -11
  51. package/src/config/agent-settings-policy.ts +1 -18
  52. package/src/input/agent-workspace-activation.ts +53 -0
  53. package/src/input/agent-workspace-artifact-browser-editor.ts +494 -0
  54. package/src/input/agent-workspace-artifact-metadata.ts +17 -0
  55. package/src/input/agent-workspace-basic-command-editors.ts +5 -1
  56. package/src/input/agent-workspace-categories.ts +153 -155
  57. package/src/input/agent-workspace-channel-triage.ts +481 -0
  58. package/src/input/agent-workspace-channels.ts +263 -0
  59. package/src/input/agent-workspace-command-editor.ts +152 -0
  60. package/src/input/agent-workspace-context-snapshot.ts +349 -0
  61. package/src/input/agent-workspace-delegation-editor-submission.ts +8 -0
  62. package/src/input/agent-workspace-document-editor.ts +502 -0
  63. package/src/input/agent-workspace-document-ops-editor.ts +523 -0
  64. package/src/input/agent-workspace-host-category.ts +1 -1
  65. package/src/input/agent-workspace-local-library-snapshot.ts +167 -0
  66. package/src/input/agent-workspace-model-compare-editor.ts +376 -0
  67. package/src/input/agent-workspace-model-compare-prompt-submission.ts +552 -0
  68. package/src/input/agent-workspace-model-compare-types.ts +82 -0
  69. package/src/input/agent-workspace-model-compare-utils.ts +35 -0
  70. package/src/input/agent-workspace-onboarding-categories.ts +141 -0
  71. package/src/input/agent-workspace-operations-command-editor-submission.ts +53 -0
  72. package/src/input/agent-workspace-operations-command-editors.ts +22 -0
  73. package/src/input/agent-workspace-research-report-editor.ts +212 -0
  74. package/src/input/agent-workspace-research-run-editor.ts +144 -0
  75. package/src/input/agent-workspace-research-source-editor.ts +167 -0
  76. package/src/input/agent-workspace-review-packet-snapshot.ts +593 -0
  77. package/src/input/agent-workspace-review-packet-utils.ts +265 -0
  78. package/src/input/agent-workspace-settings.ts +313 -30
  79. package/src/input/agent-workspace-setup-checkpoint-action.ts +127 -0
  80. package/src/input/agent-workspace-setup-snapshot.ts +217 -0
  81. package/src/input/agent-workspace-setup.ts +61 -1
  82. package/src/input/agent-workspace-snapshot.ts +184 -149
  83. package/src/input/agent-workspace-types.ts +287 -2
  84. package/src/input/agent-workspace.ts +23 -1
  85. package/src/input/command-registry.ts +9 -2
  86. package/src/input/commands/agent-runtime-profile-runtime.ts +28 -13
  87. package/src/input/commands/channels-runtime.ts +102 -114
  88. package/src/input/commands/delegation-runtime.ts +87 -24
  89. package/src/input/commands/knowledge-browser-flags.ts +12 -0
  90. package/src/input/commands/knowledge.ts +28 -14
  91. package/src/input/commands/operator-actions-runtime.ts +6 -3
  92. package/src/input/commands/schedule-runtime.ts +71 -14
  93. package/src/input/commands/session-content.ts +2 -2
  94. package/src/input/commands/shell-core.ts +2 -0
  95. package/src/input/commands/vibe-runtime.ts +140 -0
  96. package/src/input/commands.ts +2 -0
  97. package/src/input/connected-host-routes.ts +123 -0
  98. package/src/input/settings-modal-types.ts +14 -0
  99. package/src/input/setup-wizard-live-receipts.ts +76 -0
  100. package/src/main.ts +15 -15
  101. package/src/panels/qr-panel.ts +2 -2
  102. package/src/renderer/agent-workspace-context-lines.ts +701 -0
  103. package/src/renderer/agent-workspace.ts +68 -421
  104. package/src/renderer/settings-modal-helpers.ts +12 -0
  105. package/src/renderer/settings-modal.ts +12 -0
  106. package/src/runtime/bootstrap-command-context.ts +11 -1
  107. package/src/runtime/bootstrap-command-parts.ts +14 -3
  108. package/src/runtime/bootstrap-core.ts +39 -1
  109. package/src/runtime/bootstrap-shell.ts +6 -0
  110. package/src/runtime/bootstrap.ts +113 -30
  111. package/src/runtime/connected-host-auth.ts +3 -2
  112. package/src/runtime/execution-ledger.ts +231 -0
  113. package/src/runtime/services.ts +88 -10
  114. package/src/runtime/tool-permission-safety.ts +199 -5
  115. package/src/shell/session-continuity-hints.ts +39 -0
  116. package/src/tools/agent-artifacts-tool.ts +606 -0
  117. package/src/tools/agent-audit-tool.ts +155 -0
  118. package/src/tools/agent-autonomy-schedule-tool.ts +238 -0
  119. package/src/tools/agent-autonomy-tool.ts +140 -0
  120. package/src/tools/agent-channel-send-tool.ts +24 -2
  121. package/src/tools/agent-channels-tool.ts +140 -0
  122. package/src/tools/agent-computer-tool.ts +168 -0
  123. package/src/tools/agent-context-policy.ts +137 -7
  124. package/src/tools/agent-context-tool.ts +143 -0
  125. package/src/tools/agent-delegation-tool.ts +128 -0
  126. package/src/tools/agent-device-tool.ts +240 -0
  127. package/src/tools/agent-documents-tool.ts +684 -0
  128. package/src/tools/agent-execution-tool.ts +230 -0
  129. package/src/tools/agent-harness-agent-orchestration-policy.ts +75 -0
  130. package/src/tools/agent-harness-agent-orchestration.ts +791 -0
  131. package/src/tools/agent-harness-autonomy-intake.ts +785 -0
  132. package/src/tools/agent-harness-autonomy-live-records.ts +742 -0
  133. package/src/tools/agent-harness-autonomy-queue-types.ts +88 -0
  134. package/src/tools/agent-harness-autonomy-queue.ts +571 -0
  135. package/src/tools/agent-harness-autonomy-watcher-read-models.ts +509 -0
  136. package/src/tools/agent-harness-background-processes-types.ts +35 -0
  137. package/src/tools/agent-harness-background-processes.ts +798 -0
  138. package/src/tools/agent-harness-browser-cockpit-route.ts +373 -0
  139. package/src/tools/agent-harness-browser-control.ts +346 -0
  140. package/src/tools/agent-harness-browser-pwa-read-models.ts +622 -0
  141. package/src/tools/agent-harness-channel-metadata.ts +168 -10
  142. package/src/tools/agent-harness-cli-command-policy.ts +110 -0
  143. package/src/tools/agent-harness-command-catalog.ts +4 -2
  144. package/src/tools/agent-harness-command-runner.ts +42 -0
  145. package/src/tools/agent-harness-connected-host-status.ts +7 -4
  146. package/src/tools/agent-harness-delegation-posture.ts +234 -19
  147. package/src/tools/agent-harness-device-live-read-models.ts +366 -0
  148. package/src/tools/agent-harness-document-ops-reviewer-readiness.ts +234 -0
  149. package/src/tools/agent-harness-document-ops-types.ts +72 -0
  150. package/src/tools/agent-harness-document-ops.ts +671 -0
  151. package/src/tools/agent-harness-execution-history.ts +489 -0
  152. package/src/tools/agent-harness-execution-posture.ts +385 -0
  153. package/src/tools/agent-harness-file-recovery.ts +135 -0
  154. package/src/tools/agent-harness-interactive-runtime-records.ts +421 -0
  155. package/src/tools/agent-harness-keybinding-metadata.ts +16 -12
  156. package/src/tools/agent-harness-learning-curator-common.ts +102 -0
  157. package/src/tools/agent-harness-learning-curator-consolidation.ts +295 -0
  158. package/src/tools/agent-harness-learning-curator-proposals.ts +606 -0
  159. package/src/tools/agent-harness-learning-curator-types.ts +151 -0
  160. package/src/tools/agent-harness-learning-curator.ts +417 -0
  161. package/src/tools/agent-harness-local-model-benchmarks.ts +269 -0
  162. package/src/tools/agent-harness-local-model-cookbook.ts +238 -0
  163. package/src/tools/agent-harness-local-model-endpoints.ts +788 -0
  164. package/src/tools/agent-harness-local-model-smoke.ts +277 -0
  165. package/src/tools/agent-harness-local-model-url.ts +78 -0
  166. package/src/tools/agent-harness-media-posture.ts +223 -2
  167. package/src/tools/agent-harness-memory-external-providers.ts +796 -0
  168. package/src/tools/agent-harness-memory-posture.ts +672 -0
  169. package/src/tools/agent-harness-memory-provider-certification.ts +219 -0
  170. package/src/tools/agent-harness-memory-refinement.ts +340 -0
  171. package/src/tools/agent-harness-metadata.ts +100 -168
  172. package/src/tools/agent-harness-mode-catalog.ts +86 -33
  173. package/src/tools/agent-harness-model-catalog.ts +162 -0
  174. package/src/tools/agent-harness-model-provider-health.ts +283 -0
  175. package/src/tools/agent-harness-model-readiness.ts +432 -0
  176. package/src/tools/agent-harness-model-routing-types.ts +327 -0
  177. package/src/tools/agent-harness-model-routing-utils.ts +30 -0
  178. package/src/tools/agent-harness-model-routing.ts +165 -193
  179. package/src/tools/agent-harness-operator-methods.ts +115 -133
  180. package/src/tools/agent-harness-pairing-posture.ts +452 -18
  181. package/src/tools/agent-harness-personal-ops-certification.ts +116 -0
  182. package/src/tools/agent-harness-personal-ops-discovery.ts +533 -0
  183. package/src/tools/agent-harness-personal-ops-intake.ts +385 -0
  184. package/src/tools/agent-harness-personal-ops-lanes.ts +270 -0
  185. package/src/tools/agent-harness-personal-ops-operations.ts +225 -0
  186. package/src/tools/agent-harness-personal-ops-provider-records.ts +358 -0
  187. package/src/tools/agent-harness-personal-ops-provider-task-records.ts +321 -0
  188. package/src/tools/agent-harness-personal-ops-records.ts +684 -0
  189. package/src/tools/agent-harness-personal-ops-runner.ts +637 -0
  190. package/src/tools/agent-harness-personal-ops-types.ts +240 -0
  191. package/src/tools/agent-harness-personal-ops.ts +613 -0
  192. package/src/tools/agent-harness-project-context.ts +144 -0
  193. package/src/tools/agent-harness-prompt-context.ts +589 -0
  194. package/src/tools/agent-harness-provider-account-metadata.ts +2 -2
  195. package/src/tools/agent-harness-release-evidence.ts +9 -7
  196. package/src/tools/agent-harness-release-readiness.ts +9 -7
  197. package/src/tools/agent-harness-remote-read-models.ts +541 -0
  198. package/src/tools/agent-harness-research-briefing.ts +446 -0
  199. package/src/tools/agent-harness-research-live-read-models.ts +500 -0
  200. package/src/tools/agent-harness-research-queue.ts +250 -0
  201. package/src/tools/agent-harness-research-runs.ts +307 -0
  202. package/src/tools/agent-harness-research-workflow.ts +395 -0
  203. package/src/tools/agent-harness-security-posture.ts +2 -2
  204. package/src/tools/agent-harness-service-posture.ts +9 -9
  205. package/src/tools/agent-harness-session-metadata.ts +3 -3
  206. package/src/tools/agent-harness-setup-connected-host.ts +501 -0
  207. package/src/tools/agent-harness-setup-describe.ts +557 -0
  208. package/src/tools/agent-harness-setup-handoffs.ts +502 -0
  209. package/src/tools/agent-harness-setup-model-helpers.ts +102 -0
  210. package/src/tools/agent-harness-setup-plan.ts +253 -0
  211. package/src/tools/agent-harness-setup-posture-types.ts +218 -0
  212. package/src/tools/agent-harness-setup-posture-utils.ts +173 -0
  213. package/src/tools/agent-harness-setup-posture.ts +449 -221
  214. package/src/tools/agent-harness-setup-smoke.ts +533 -0
  215. package/src/tools/agent-harness-sudo-posture.ts +114 -0
  216. package/src/tools/agent-harness-tool-schema.ts +142 -7
  217. package/src/tools/agent-harness-tool-types.ts +83 -0
  218. package/src/tools/agent-harness-tool-utils.ts +43 -0
  219. package/src/tools/agent-harness-tool.ts +319 -370
  220. package/src/tools/agent-harness-ui-surface-metadata.ts +76 -69
  221. package/src/tools/agent-harness-ui-surface-types.ts +46 -0
  222. package/src/tools/agent-harness-vibe-health.ts +105 -0
  223. package/src/tools/agent-harness-workspace-action-runner.ts +149 -0
  224. package/src/tools/agent-harness-workspace-actions.ts +114 -8
  225. package/src/tools/agent-harness-workspace-editor-execution.ts +214 -0
  226. package/src/tools/agent-harness-workspace-editor-runner.ts +755 -0
  227. package/src/tools/agent-host-tool.ts +159 -0
  228. package/src/tools/agent-knowledge-ingest-tool.ts +34 -1
  229. package/src/tools/agent-learning-consolidation-core.ts +327 -0
  230. package/src/tools/agent-learning-consolidation-tool.ts +758 -0
  231. package/src/tools/agent-memory-tool.ts +253 -0
  232. package/src/tools/agent-model-compare-export.ts +315 -0
  233. package/src/tools/agent-model-compare-handoff.ts +592 -0
  234. package/src/tools/agent-model-compare-judgment.ts +633 -0
  235. package/src/tools/agent-model-compare-run.ts +735 -0
  236. package/src/tools/agent-model-compare-tool.ts +698 -0
  237. package/src/tools/agent-model-compare-types.ts +191 -0
  238. package/src/tools/agent-model-compare-utils.ts +93 -0
  239. package/src/tools/agent-models-tool.ts +208 -0
  240. package/src/tools/agent-operator-action-tool.ts +1 -1
  241. package/src/tools/agent-operator-method-tool.ts +643 -0
  242. package/src/tools/agent-personal-ops-tool.ts +197 -0
  243. package/src/tools/agent-policy-explanation.ts +415 -0
  244. package/src/tools/agent-research-report-tool.ts +608 -0
  245. package/src/tools/agent-research-runner.ts +367 -0
  246. package/src/tools/agent-research-runs-tool.ts +434 -0
  247. package/src/tools/agent-research-sources-tool.ts +443 -0
  248. package/src/tools/agent-research-tool.ts +515 -0
  249. package/src/tools/agent-review-packet-presets-core.ts +757 -0
  250. package/src/tools/agent-review-packet-presets-tool.ts +466 -0
  251. package/src/tools/agent-review-packet-share-tool.ts +305 -0
  252. package/src/tools/agent-route-planner-candidates-setup.ts +411 -0
  253. package/src/tools/agent-route-planner-candidates-surfaces.ts +432 -0
  254. package/src/tools/agent-route-planner-candidates-work.ts +251 -0
  255. package/src/tools/agent-route-planner-helpers.ts +667 -0
  256. package/src/tools/agent-route-planner.ts +185 -0
  257. package/src/tools/agent-route-tool.ts +105 -0
  258. package/src/tools/agent-schedule-edit-tool.ts +210 -0
  259. package/src/tools/agent-schedule-tool.ts +282 -0
  260. package/src/tools/agent-security-tool.ts +145 -0
  261. package/src/tools/agent-sessions-tool.ts +122 -0
  262. package/src/tools/agent-settings-import-tool.ts +104 -0
  263. package/src/tools/agent-settings-tool.ts +176 -0
  264. package/src/tools/agent-setup-tool.ts +226 -0
  265. package/src/tools/agent-support-tool.ts +122 -0
  266. package/src/tools/agent-terminal-process-tools.ts +167 -0
  267. package/src/tools/agent-tool-policy-guard-types.ts +77 -0
  268. package/src/tools/agent-tool-policy-guard.ts +106 -85
  269. package/src/tools/agent-vibe-tool.ts +297 -0
  270. package/src/tools/agent-work-plan-tool.ts +265 -6
  271. package/src/tools/agent-workspace-tool.ts +305 -0
  272. package/src/tools/artifact-archive.ts +169 -0
  273. package/src/tools/tool-definition-compaction.ts +36 -0
  274. package/src/version.ts +1 -1
@@ -0,0 +1,757 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import type { ArtifactCreateInput, ArtifactDescriptor, ArtifactRecord, ArtifactStore } from '@pellux/goodvibes-sdk/platform/artifacts';
3
+ export interface AgentReviewPacketPresetsToolArgs {
4
+ readonly mode?: unknown;
5
+ readonly artifactId?: unknown;
6
+ readonly name?: unknown;
7
+ readonly documentId?: unknown;
8
+ readonly documentTitle?: unknown;
9
+ readonly documentExportArtifactId?: unknown;
10
+ readonly comparisonArtifactId?: unknown;
11
+ readonly judgmentArtifactId?: unknown;
12
+ readonly revealedJudgmentArtifactId?: unknown;
13
+ readonly routeDecisionArtifactId?: unknown;
14
+ readonly routeDecision?: unknown;
15
+ readonly handoffArtifactId?: unknown;
16
+ readonly handoffArchiveArtifactId?: unknown;
17
+ readonly relatedArtifactIds?: unknown;
18
+ readonly summary?: unknown;
19
+ readonly limit?: unknown;
20
+ readonly confirm?: unknown;
21
+ readonly explicitUserRequest?: unknown;
22
+ }
23
+
24
+ export type AgentReviewPacketPresetArtifactStore = Pick<ArtifactStore, 'create'> & Partial<Pick<ArtifactStore, 'get' | 'list' | 'readContent'>>;
25
+
26
+ export type AgentReviewPacketPresetMode = 'list' | 'show' | 'save' | 'refresh';
27
+ export type ReviewPacketArtifactRole =
28
+ | 'documentExport'
29
+ | 'comparison'
30
+ | 'judgment'
31
+ | 'revealedJudgment'
32
+ | 'routeDecision'
33
+ | 'handoff'
34
+ | 'handoffArchive'
35
+ | 'related';
36
+
37
+ export interface ReviewPacketPresetPacket {
38
+ readonly documentId?: string;
39
+ readonly documentTitle?: string;
40
+ readonly documentExportArtifactId?: string;
41
+ readonly comparisonArtifactId?: string;
42
+ readonly judgmentArtifactId?: string;
43
+ readonly revealedJudgmentArtifactId?: string;
44
+ readonly routeDecisionArtifactId?: string;
45
+ readonly routeDecision?: string;
46
+ readonly handoffArtifactId?: string;
47
+ readonly handoffArchiveArtifactId?: string;
48
+ readonly relatedArtifactIds: readonly string[];
49
+ readonly summary: string;
50
+ }
51
+
52
+ export interface ReviewPacketPresetRecord {
53
+ readonly schema: 'goodvibes-agent.review-packet-preset';
54
+ readonly schemaVersion: 1;
55
+ readonly presetId: string;
56
+ readonly name: string;
57
+ readonly createdAt: string;
58
+ readonly explicitUserRequest: string;
59
+ readonly packet: ReviewPacketPresetPacket;
60
+ readonly refresh?: {
61
+ readonly sourceArtifactId: string;
62
+ readonly sourcePresetId?: string;
63
+ readonly missingCount: number;
64
+ readonly supersededCount: number;
65
+ readonly unresolvedCount: number;
66
+ };
67
+ readonly policy: {
68
+ readonly effect: 'save-local-preset-artifact-only';
69
+ readonly documentsChanged: false;
70
+ readonly modelRouteChanged: false;
71
+ readonly handoffArchiveCreated: false;
72
+ };
73
+ }
74
+
75
+ export interface LoadedReviewPacketPreset {
76
+ readonly descriptor: ArtifactDescriptor;
77
+ readonly record?: ArtifactRecord;
78
+ readonly body?: ReviewPacketPresetRecord;
79
+ }
80
+
81
+ export interface ReviewPacketArtifactReference {
82
+ readonly role: ReviewPacketArtifactRole;
83
+ readonly label: string;
84
+ readonly id: string;
85
+ }
86
+
87
+ export interface ReviewPacketFreshnessMissing {
88
+ readonly role: ReviewPacketArtifactRole;
89
+ readonly label: string;
90
+ readonly id: string;
91
+ readonly replacementId?: string;
92
+ readonly reason?: string;
93
+ }
94
+
95
+ export interface ReviewPacketFreshnessSuperseded {
96
+ readonly role: ReviewPacketArtifactRole;
97
+ readonly label: string;
98
+ readonly id: string;
99
+ readonly replacementId: string;
100
+ readonly reason: string;
101
+ }
102
+
103
+ export interface ReviewPacketFreshnessAudit {
104
+ readonly available: boolean;
105
+ readonly scannedArtifacts: number;
106
+ readonly status: 'current' | 'needs-review' | 'unchecked';
107
+ readonly missing: readonly ReviewPacketFreshnessMissing[];
108
+ readonly superseded: readonly ReviewPacketFreshnessSuperseded[];
109
+ readonly recommendedPacket: ReviewPacketPresetPacket;
110
+ }
111
+
112
+ export const REVIEW_PACKET_PRESET_PURPOSE = 'agent-review-packet-preset';
113
+ export const REVIEW_PACKET_PRESET_SCHEMA = 'goodvibes-agent.review-packet-preset';
114
+ export const DEFAULT_LIST_LIMIT = 20;
115
+ export const MAX_LIST_LIMIT = 100;
116
+ export const MAX_RELATED_ARTIFACTS = 30;
117
+ export const MAX_FRESHNESS_ARTIFACT_SCAN = 500;
118
+ export const PURPOSE_DOCUMENT_EXPORT = 'agent-document-export';
119
+ export const PURPOSE_MODEL_COMPARE = 'agent-model-compare';
120
+ export const PURPOSE_MODEL_COMPARE_JUDGMENT = 'agent-model-compare-judgment';
121
+ export const PURPOSE_MODEL_COMPARE_ROUTE_DECISION = 'agent-model-compare-route-decision';
122
+ export const PURPOSE_MODEL_COMPARE_HANDOFF = 'agent-model-compare-handoff';
123
+ export const PURPOSE_MODEL_COMPARE_HANDOFF_ARCHIVE = 'agent-model-compare-handoff-archive';
124
+
125
+ export function readString(value: unknown): string {
126
+ return typeof value === 'string' ? value.trim() : '';
127
+ }
128
+
129
+ export function readBoolean(value: unknown): boolean {
130
+ return value === true || value === 'true' || value === 'yes';
131
+ }
132
+
133
+ export function readNumber(value: unknown, fallback: number): number {
134
+ const parsed = typeof value === 'string' && value.trim() ? Number(value) : value;
135
+ if (typeof parsed !== 'number' || !Number.isFinite(parsed)) return fallback;
136
+ return Math.trunc(parsed);
137
+ }
138
+
139
+ export function readStringList(value: unknown): readonly string[] {
140
+ const values = Array.isArray(value)
141
+ ? value
142
+ : readString(value)
143
+ .split(/[,\n]/)
144
+ .map((entry) => entry.trim());
145
+ const seen = new Set<string>();
146
+ const result: string[] = [];
147
+ for (const valueEntry of values) {
148
+ const entry = readString(valueEntry);
149
+ if (!entry || seen.has(entry)) continue;
150
+ seen.add(entry);
151
+ result.push(entry);
152
+ if (result.length >= MAX_RELATED_ARTIFACTS) break;
153
+ }
154
+ return result;
155
+ }
156
+
157
+ export function clamp(value: number, min: number, max: number): number {
158
+ return Math.max(min, Math.min(max, value));
159
+ }
160
+
161
+ export function compactText(value: string, maxLength = 96): string {
162
+ const normalized = value.replace(/\s+/g, ' ').trim();
163
+ if (normalized.length <= maxLength) return normalized;
164
+ return `${normalized.slice(0, Math.max(0, maxLength - 3)).trimEnd()}...`;
165
+ }
166
+
167
+ export function filenamePart(value: string): string {
168
+ const normalized = value
169
+ .toLowerCase()
170
+ .replace(/[^a-z0-9._-]+/g, '-')
171
+ .replace(/^-+|-+$/g, '')
172
+ .slice(0, 48);
173
+ return normalized || 'review-packet';
174
+ }
175
+
176
+ export function parseMode(value: unknown): AgentReviewPacketPresetMode {
177
+ const mode = readString(value).toLowerCase();
178
+ if (!mode || mode === 'list') return 'list';
179
+ if (mode === 'show' || mode === 'get') return 'show';
180
+ if (mode === 'save' || mode === 'create') return 'save';
181
+ if (mode === 'refresh' || mode === 'update') return 'refresh';
182
+ throw new Error(`Unknown agent_review_packet_presets mode: ${mode}. Use mode:"list", mode:"show", mode:"save", or mode:"refresh".`);
183
+ }
184
+
185
+ export function defaultPacketSummary(input: {
186
+ readonly documentId?: string;
187
+ readonly documentExportArtifactId?: string;
188
+ readonly comparisonArtifactId?: string;
189
+ readonly judgmentArtifactId?: string;
190
+ readonly revealedJudgmentArtifactId?: string;
191
+ readonly handoffArtifactId?: string;
192
+ readonly handoffArchiveArtifactId?: string;
193
+ readonly relatedArtifactIds?: readonly string[];
194
+ }): string {
195
+ const sourceArtifactId = input.revealedJudgmentArtifactId
196
+ || input.judgmentArtifactId
197
+ || input.comparisonArtifactId
198
+ || input.documentExportArtifactId;
199
+ const relatedArtifactIds = input.relatedArtifactIds ?? [];
200
+ return [
201
+ input.documentId ? `document ${input.documentId}` : '',
202
+ sourceArtifactId ? `source ${sourceArtifactId}` : '',
203
+ input.handoffArtifactId ? `handoff ${input.handoffArtifactId}` : '',
204
+ input.handoffArchiveArtifactId ? `archive ${input.handoffArchiveArtifactId}` : '',
205
+ relatedArtifactIds.length > 0 ? `${relatedArtifactIds.length} related` : '',
206
+ ].filter(Boolean).join('; ') || 'review packet preset';
207
+ }
208
+
209
+ export function readPresetPacket(args: AgentReviewPacketPresetsToolArgs): ReviewPacketPresetPacket {
210
+ const documentId = readString(args.documentId);
211
+ const documentTitle = readString(args.documentTitle);
212
+ const documentExportArtifactId = readString(args.documentExportArtifactId);
213
+ const comparisonArtifactId = readString(args.comparisonArtifactId);
214
+ const judgmentArtifactId = readString(args.judgmentArtifactId);
215
+ const revealedJudgmentArtifactId = readString(args.revealedJudgmentArtifactId);
216
+ const routeDecisionArtifactId = readString(args.routeDecisionArtifactId);
217
+ const routeDecision = readString(args.routeDecision);
218
+ const handoffArtifactId = readString(args.handoffArtifactId);
219
+ const handoffArchiveArtifactId = readString(args.handoffArchiveArtifactId);
220
+ const relatedArtifactIds = readStringList(args.relatedArtifactIds);
221
+ const explicitSummary = readString(args.summary);
222
+ const summary = explicitSummary || defaultPacketSummary({
223
+ documentId,
224
+ documentExportArtifactId,
225
+ comparisonArtifactId,
226
+ judgmentArtifactId,
227
+ revealedJudgmentArtifactId,
228
+ handoffArtifactId,
229
+ handoffArchiveArtifactId,
230
+ relatedArtifactIds,
231
+ });
232
+
233
+ if (!documentId
234
+ && !documentExportArtifactId
235
+ && !comparisonArtifactId
236
+ && !judgmentArtifactId
237
+ && !revealedJudgmentArtifactId
238
+ && !routeDecisionArtifactId
239
+ && !handoffArtifactId
240
+ && !handoffArchiveArtifactId
241
+ && relatedArtifactIds.length === 0
242
+ ) {
243
+ throw new Error('save mode requires at least one document, comparison, judgment, handoff, archive, route-decision, or related artifact id.');
244
+ }
245
+
246
+ return {
247
+ ...(documentId ? { documentId } : {}),
248
+ ...(documentTitle ? { documentTitle } : {}),
249
+ ...(documentExportArtifactId ? { documentExportArtifactId } : {}),
250
+ ...(comparisonArtifactId ? { comparisonArtifactId } : {}),
251
+ ...(judgmentArtifactId ? { judgmentArtifactId } : {}),
252
+ ...(revealedJudgmentArtifactId ? { revealedJudgmentArtifactId } : {}),
253
+ ...(routeDecisionArtifactId ? { routeDecisionArtifactId } : {}),
254
+ ...(routeDecision ? { routeDecision } : {}),
255
+ ...(handoffArtifactId ? { handoffArtifactId } : {}),
256
+ ...(handoffArchiveArtifactId ? { handoffArchiveArtifactId } : {}),
257
+ relatedArtifactIds,
258
+ summary,
259
+ };
260
+ }
261
+
262
+ export function presetName(args: AgentReviewPacketPresetsToolArgs, packet: ReviewPacketPresetPacket): string {
263
+ const requested = readString(args.name);
264
+ if (requested) return requested;
265
+ if (packet.documentTitle) return `${packet.documentTitle} reviewer packet`;
266
+ if (packet.documentId) return `${packet.documentId} reviewer packet`;
267
+ if (packet.handoffArtifactId) return `${packet.handoffArtifactId} reviewer packet`;
268
+ return 'Reviewer packet preset';
269
+ }
270
+
271
+ export function buildPresetRecord(args: AgentReviewPacketPresetsToolArgs): ReviewPacketPresetRecord {
272
+ const explicitUserRequest = readString(args.explicitUserRequest);
273
+ if (!explicitUserRequest) {
274
+ throw new Error('explicitUserRequest is required so saved packet presets stay tied to a direct user request.');
275
+ }
276
+ const packet = readPresetPacket(args);
277
+ return {
278
+ schema: REVIEW_PACKET_PRESET_SCHEMA,
279
+ schemaVersion: 1,
280
+ presetId: `packet_preset_${randomUUID()}`,
281
+ name: presetName(args, packet),
282
+ createdAt: new Date().toISOString(),
283
+ explicitUserRequest,
284
+ packet,
285
+ policy: {
286
+ effect: 'save-local-preset-artifact-only',
287
+ documentsChanged: false,
288
+ modelRouteChanged: false,
289
+ handoffArchiveCreated: false,
290
+ },
291
+ };
292
+ }
293
+
294
+ export function metadataValue(value: string | undefined): string | undefined {
295
+ const trimmed = value?.trim();
296
+ return trimmed ? trimmed : undefined;
297
+ }
298
+
299
+ export function presetMetadata(record: ReviewPacketPresetRecord): Record<string, unknown> {
300
+ return {
301
+ purpose: REVIEW_PACKET_PRESET_PURPOSE,
302
+ schema: REVIEW_PACKET_PRESET_SCHEMA,
303
+ schemaVersion: record.schemaVersion,
304
+ presetId: record.presetId,
305
+ name: record.name,
306
+ summary: record.packet.summary,
307
+ documentId: metadataValue(record.packet.documentId),
308
+ documentTitle: metadataValue(record.packet.documentTitle),
309
+ documentExportArtifactId: metadataValue(record.packet.documentExportArtifactId),
310
+ comparisonArtifactId: metadataValue(record.packet.comparisonArtifactId),
311
+ judgmentArtifactId: metadataValue(record.packet.judgmentArtifactId),
312
+ revealedJudgmentArtifactId: metadataValue(record.packet.revealedJudgmentArtifactId),
313
+ routeDecisionArtifactId: metadataValue(record.packet.routeDecisionArtifactId),
314
+ routeDecision: metadataValue(record.packet.routeDecision),
315
+ handoffArtifactId: metadataValue(record.packet.handoffArtifactId),
316
+ handoffArchiveArtifactId: metadataValue(record.packet.handoffArchiveArtifactId),
317
+ relatedArtifactIds: record.packet.relatedArtifactIds,
318
+ refreshOfArtifactId: metadataValue(record.refresh?.sourceArtifactId),
319
+ refreshOfPresetId: metadataValue(record.refresh?.sourcePresetId),
320
+ freshnessMissingCount: record.refresh?.missingCount,
321
+ freshnessSupersededCount: record.refresh?.supersededCount,
322
+ freshnessUnresolvedCount: record.refresh?.unresolvedCount,
323
+ source: 'agent-review-packet-presets',
324
+ createdAt: record.createdAt,
325
+ explicitUserRequest: record.explicitUserRequest,
326
+ };
327
+ }
328
+
329
+ export function presetCreateInput(record: ReviewPacketPresetRecord): ArtifactCreateInput {
330
+ return {
331
+ kind: 'data',
332
+ mimeType: 'application/json',
333
+ filename: `review-packet-preset-${filenamePart(record.name)}.json`,
334
+ text: `${JSON.stringify(record, null, 2)}\n`,
335
+ metadata: presetMetadata(record),
336
+ };
337
+ }
338
+
339
+ export function readMetadataString(metadata: Readonly<Record<string, unknown>>, key: string): string {
340
+ const value = metadata[key];
341
+ return typeof value === 'string' ? value.trim() : '';
342
+ }
343
+
344
+ export function readMetadataStringList(metadata: Readonly<Record<string, unknown>>, key: string): readonly string[] {
345
+ const value = metadata[key];
346
+ if (!Array.isArray(value)) return [];
347
+ return value.filter((entry): entry is string => typeof entry === 'string' && entry.trim().length > 0);
348
+ }
349
+
350
+ export function isPresetArtifact(artifact: ArtifactDescriptor): boolean {
351
+ return readMetadataString(artifact.metadata, 'purpose') === REVIEW_PACKET_PRESET_PURPOSE;
352
+ }
353
+
354
+ export function artifactPurpose(artifact: ArtifactDescriptor): string {
355
+ return readMetadataString(artifact.metadata, 'purpose');
356
+ }
357
+
358
+ export function hasPurpose(artifact: ArtifactDescriptor, purpose: string): boolean {
359
+ return artifactPurpose(artifact) === purpose;
360
+ }
361
+
362
+ export function isRevealedJudgmentArtifact(artifact: ArtifactDescriptor): boolean {
363
+ return hasPurpose(artifact, PURPOSE_MODEL_COMPARE_JUDGMENT)
364
+ && artifact.metadata.revealIncludedInJudgment === true
365
+ && readMetadataString(artifact.metadata, 'winnerModel').length > 0;
366
+ }
367
+
368
+ export function artifactIndex(artifacts: readonly ArtifactDescriptor[]): Map<string, ArtifactDescriptor> {
369
+ return new Map(artifacts.map((artifact) => [artifact.id, artifact]));
370
+ }
371
+
372
+ export function artifactFromIndex(
373
+ store: AgentReviewPacketPresetArtifactStore | undefined,
374
+ index: ReadonlyMap<string, ArtifactDescriptor>,
375
+ id: string | undefined,
376
+ ): ArtifactDescriptor | null {
377
+ if (!id) return null;
378
+ const indexed = index.get(id);
379
+ if (indexed) return indexed;
380
+ try {
381
+ return store?.get?.(id) ?? null;
382
+ } catch {
383
+ return null;
384
+ }
385
+ }
386
+
387
+ export function latestArtifact(
388
+ artifacts: readonly ArtifactDescriptor[],
389
+ predicate: (artifact: ArtifactDescriptor) => boolean,
390
+ ): ArtifactDescriptor | null {
391
+ return artifacts
392
+ .filter(predicate)
393
+ .sort((left, right) => right.createdAt - left.createdAt || left.id.localeCompare(right.id))[0] ?? null;
394
+ }
395
+
396
+ export function firstMetadataString(
397
+ store: AgentReviewPacketPresetArtifactStore | undefined,
398
+ index: ReadonlyMap<string, ArtifactDescriptor>,
399
+ ids: readonly (string | undefined)[],
400
+ keys: readonly string[],
401
+ ): string {
402
+ for (const id of ids) {
403
+ const artifact = artifactFromIndex(store, index, id);
404
+ if (!artifact) continue;
405
+ for (const key of keys) {
406
+ const value = readMetadataString(artifact.metadata, key);
407
+ if (value) return value;
408
+ }
409
+ }
410
+ return '';
411
+ }
412
+
413
+ export function packetDocumentId(
414
+ packet: ReviewPacketPresetPacket,
415
+ store: AgentReviewPacketPresetArtifactStore | undefined,
416
+ index: ReadonlyMap<string, ArtifactDescriptor>,
417
+ ): string {
418
+ return packet.documentId
419
+ || firstMetadataString(store, index, [
420
+ packet.documentExportArtifactId,
421
+ packet.comparisonArtifactId,
422
+ packet.judgmentArtifactId,
423
+ packet.revealedJudgmentArtifactId,
424
+ packet.routeDecisionArtifactId,
425
+ packet.handoffArtifactId,
426
+ packet.handoffArchiveArtifactId,
427
+ ], ['documentId', 'sourceDocumentId']);
428
+ }
429
+
430
+ export function packetComparisonId(
431
+ packet: ReviewPacketPresetPacket,
432
+ store: AgentReviewPacketPresetArtifactStore | undefined,
433
+ index: ReadonlyMap<string, ArtifactDescriptor>,
434
+ ): string {
435
+ return firstMetadataString(store, index, [
436
+ packet.routeDecisionArtifactId,
437
+ packet.handoffArchiveArtifactId,
438
+ packet.handoffArtifactId,
439
+ packet.revealedJudgmentArtifactId,
440
+ packet.judgmentArtifactId,
441
+ packet.comparisonArtifactId,
442
+ ], ['comparisonId']);
443
+ }
444
+
445
+ export function packetRunSourceArtifactId(
446
+ packet: ReviewPacketPresetPacket,
447
+ store: AgentReviewPacketPresetArtifactStore | undefined,
448
+ index: ReadonlyMap<string, ArtifactDescriptor>,
449
+ ): string {
450
+ return firstMetadataString(store, index, [
451
+ packet.comparisonArtifactId,
452
+ packet.judgmentArtifactId,
453
+ packet.revealedJudgmentArtifactId,
454
+ packet.handoffArtifactId,
455
+ packet.handoffArchiveArtifactId,
456
+ ], ['sourceArtifactId']) || packet.documentExportArtifactId || '';
457
+ }
458
+
459
+ export function packetJudgmentArtifactId(
460
+ packet: ReviewPacketPresetPacket,
461
+ store: AgentReviewPacketPresetArtifactStore | undefined,
462
+ index: ReadonlyMap<string, ArtifactDescriptor>,
463
+ ): string {
464
+ return packet.revealedJudgmentArtifactId
465
+ || packet.judgmentArtifactId
466
+ || firstMetadataString(store, index, [packet.routeDecisionArtifactId], ['judgmentArtifactId']);
467
+ }
468
+
469
+ export function packetHandoffSourceArtifactId(packet: ReviewPacketPresetPacket): string {
470
+ return sourceArtifactId(packet);
471
+ }
472
+
473
+ export function freshnessReferences(packet: ReviewPacketPresetPacket): readonly ReviewPacketArtifactReference[] {
474
+ const references: ReviewPacketArtifactReference[] = [];
475
+ const add = (role: ReviewPacketArtifactRole, label: string, id: string | undefined): void => {
476
+ if (id) references.push({ role, label, id });
477
+ };
478
+ add('documentExport', 'document export', packet.documentExportArtifactId);
479
+ add('comparison', 'comparison', packet.comparisonArtifactId);
480
+ add('judgment', 'judgment', packet.judgmentArtifactId);
481
+ add('revealedJudgment', 'revealed judgment', packet.revealedJudgmentArtifactId);
482
+ add('routeDecision', 'route decision', packet.routeDecisionArtifactId);
483
+ add('handoff', 'reviewer handoff', packet.handoffArtifactId);
484
+ add('handoffArchive', 'handoff archive', packet.handoffArchiveArtifactId);
485
+ for (const id of packet.relatedArtifactIds) add('related', 'related artifact', id);
486
+ return references;
487
+ }
488
+
489
+ export function replacementForRole(input: {
490
+ readonly role: ReviewPacketArtifactRole;
491
+ readonly packet: ReviewPacketPresetPacket;
492
+ readonly current: ArtifactDescriptor | null;
493
+ readonly artifacts: readonly ArtifactDescriptor[];
494
+ readonly store?: AgentReviewPacketPresetArtifactStore;
495
+ readonly index: ReadonlyMap<string, ArtifactDescriptor>;
496
+ }): { readonly artifact: ArtifactDescriptor; readonly reason: string } | null {
497
+ const documentId = packetDocumentId(input.packet, input.store, input.index);
498
+ const comparisonId = packetComparisonId(input.packet, input.store, input.index);
499
+ const runSourceArtifactId = packetRunSourceArtifactId(input.packet, input.store, input.index);
500
+ const judgmentArtifactId = packetJudgmentArtifactId(input.packet, input.store, input.index);
501
+ const handoffSourceArtifactId = packetHandoffSourceArtifactId(input.packet);
502
+ const handoffArtifactId = input.packet.handoffArtifactId
503
+ || firstMetadataString(input.store, input.index, [input.packet.handoffArchiveArtifactId], ['handoffArtifactId']);
504
+
505
+ const currentCreatedAt = input.current?.createdAt ?? -1;
506
+ const newest = (predicate: (artifact: ArtifactDescriptor) => boolean): ArtifactDescriptor | null => {
507
+ const candidate = latestArtifact(input.artifacts, (artifact) => (
508
+ artifact.id !== input.current?.id
509
+ && artifact.createdAt > currentCreatedAt
510
+ && predicate(artifact)
511
+ ));
512
+ return candidate;
513
+ };
514
+
515
+ if (input.role === 'documentExport') {
516
+ if (!documentId) return null;
517
+ const artifact = newest((candidate) => (
518
+ hasPurpose(candidate, PURPOSE_DOCUMENT_EXPORT)
519
+ && readMetadataString(candidate.metadata, 'documentId') === documentId
520
+ ));
521
+ return artifact ? { artifact, reason: `newer document export for ${documentId}` } : null;
522
+ }
523
+
524
+ if (input.role === 'comparison') {
525
+ const artifact = newest((candidate) => {
526
+ if (!hasPurpose(candidate, PURPOSE_MODEL_COMPARE)) return false;
527
+ if (documentId && readMetadataString(candidate.metadata, 'documentId') === documentId) return true;
528
+ return Boolean(runSourceArtifactId && readMetadataString(candidate.metadata, 'sourceArtifactId') === runSourceArtifactId);
529
+ });
530
+ return artifact ? { artifact, reason: documentId ? `newer comparison for ${documentId}` : `newer comparison for source ${runSourceArtifactId}` } : null;
531
+ }
532
+
533
+ if (input.role === 'judgment' || input.role === 'revealedJudgment') {
534
+ const artifact = newest((candidate) => {
535
+ if (!hasPurpose(candidate, PURPOSE_MODEL_COMPARE_JUDGMENT)) return false;
536
+ if (input.role === 'revealedJudgment' && !isRevealedJudgmentArtifact(candidate)) return false;
537
+ if (comparisonId && readMetadataString(candidate.metadata, 'comparisonId') === comparisonId) return true;
538
+ if (documentId && readMetadataString(candidate.metadata, 'documentId') === documentId) return true;
539
+ return Boolean(runSourceArtifactId && readMetadataString(candidate.metadata, 'sourceArtifactId') === runSourceArtifactId);
540
+ });
541
+ return artifact ? { artifact, reason: comparisonId ? `newer judgment for ${comparisonId}` : 'newer matching judgment' } : null;
542
+ }
543
+
544
+ if (input.role === 'routeDecision') {
545
+ const artifact = newest((candidate) => (
546
+ hasPurpose(candidate, PURPOSE_MODEL_COMPARE_ROUTE_DECISION)
547
+ && (
548
+ Boolean(judgmentArtifactId && readMetadataString(candidate.metadata, 'judgmentArtifactId') === judgmentArtifactId)
549
+ || Boolean(comparisonId && readMetadataString(candidate.metadata, 'comparisonId') === comparisonId)
550
+ )
551
+ ));
552
+ return artifact ? { artifact, reason: comparisonId ? `newer route decision for ${comparisonId}` : 'newer route decision for judgment' } : null;
553
+ }
554
+
555
+ if (input.role === 'handoff') {
556
+ const artifact = newest((candidate) => (
557
+ hasPurpose(candidate, PURPOSE_MODEL_COMPARE_HANDOFF)
558
+ && (
559
+ Boolean(handoffSourceArtifactId && readMetadataString(candidate.metadata, 'sourceArtifactId') === handoffSourceArtifactId)
560
+ || Boolean(comparisonId && readMetadataString(candidate.metadata, 'comparisonId') === comparisonId)
561
+ )
562
+ ));
563
+ return artifact ? { artifact, reason: comparisonId ? `newer reviewer handoff for ${comparisonId}` : `newer reviewer handoff for source ${handoffSourceArtifactId}` } : null;
564
+ }
565
+
566
+ if (input.role === 'handoffArchive') {
567
+ const artifact = newest((candidate) => (
568
+ hasPurpose(candidate, PURPOSE_MODEL_COMPARE_HANDOFF_ARCHIVE)
569
+ && (
570
+ Boolean(handoffArtifactId && readMetadataString(candidate.metadata, 'handoffArtifactId') === handoffArtifactId)
571
+ || Boolean(comparisonId && readMetadataString(candidate.metadata, 'comparisonId') === comparisonId)
572
+ )
573
+ ));
574
+ return artifact ? { artifact, reason: comparisonId ? `newer handoff archive for ${comparisonId}` : 'newer handoff archive for handoff' } : null;
575
+ }
576
+
577
+ return null;
578
+ }
579
+
580
+ export function applyFreshnessReplacements(
581
+ packet: ReviewPacketPresetPacket,
582
+ replacements: ReadonlyMap<string, string>,
583
+ roleReplacements: ReadonlyMap<ReviewPacketArtifactRole, string>,
584
+ ): ReviewPacketPresetPacket {
585
+ const replace = (value: string | undefined, role: ReviewPacketArtifactRole): string | undefined => {
586
+ if (!value) return undefined;
587
+ return roleReplacements.get(role) ?? replacements.get(value) ?? value;
588
+ };
589
+ const relatedArtifactIds = readStringList(packet.relatedArtifactIds.map((id) => replacements.get(id) ?? id));
590
+ return {
591
+ ...packet,
592
+ ...(replace(packet.documentExportArtifactId, 'documentExport') ? { documentExportArtifactId: replace(packet.documentExportArtifactId, 'documentExport') } : {}),
593
+ ...(replace(packet.comparisonArtifactId, 'comparison') ? { comparisonArtifactId: replace(packet.comparisonArtifactId, 'comparison') } : {}),
594
+ ...(replace(packet.judgmentArtifactId, 'judgment') ? { judgmentArtifactId: replace(packet.judgmentArtifactId, 'judgment') } : {}),
595
+ ...(replace(packet.revealedJudgmentArtifactId, 'revealedJudgment') ? { revealedJudgmentArtifactId: replace(packet.revealedJudgmentArtifactId, 'revealedJudgment') } : {}),
596
+ ...(replace(packet.routeDecisionArtifactId, 'routeDecision') ? { routeDecisionArtifactId: replace(packet.routeDecisionArtifactId, 'routeDecision') } : {}),
597
+ ...(replace(packet.handoffArtifactId, 'handoff') ? { handoffArtifactId: replace(packet.handoffArtifactId, 'handoff') } : {}),
598
+ ...(replace(packet.handoffArchiveArtifactId, 'handoffArchive') ? { handoffArchiveArtifactId: replace(packet.handoffArchiveArtifactId, 'handoffArchive') } : {}),
599
+ relatedArtifactIds,
600
+ };
601
+ }
602
+
603
+ export function auditPresetFreshness(input: {
604
+ readonly packet: ReviewPacketPresetPacket;
605
+ readonly descriptor: ArtifactDescriptor;
606
+ readonly artifacts: readonly ArtifactDescriptor[] | null;
607
+ readonly store?: AgentReviewPacketPresetArtifactStore;
608
+ }): ReviewPacketFreshnessAudit {
609
+ if (!input.artifacts) {
610
+ return {
611
+ available: false,
612
+ scannedArtifacts: 0,
613
+ status: 'unchecked',
614
+ missing: [],
615
+ superseded: [],
616
+ recommendedPacket: input.packet,
617
+ };
618
+ }
619
+ const allArtifacts = input.artifacts.some((artifact) => artifact.id === input.descriptor.id)
620
+ ? input.artifacts
621
+ : [input.descriptor, ...input.artifacts];
622
+ const index = artifactIndex(allArtifacts);
623
+ const missing: ReviewPacketFreshnessMissing[] = [];
624
+ const superseded: ReviewPacketFreshnessSuperseded[] = [];
625
+ const replacements = new Map<string, string>();
626
+ const roleReplacements = new Map<ReviewPacketArtifactRole, string>();
627
+ const seenReferences = new Set<string>();
628
+
629
+ for (const reference of freshnessReferences(input.packet)) {
630
+ const key = `${reference.role}:${reference.id}`;
631
+ if (seenReferences.has(key)) continue;
632
+ seenReferences.add(key);
633
+ const current = artifactFromIndex(input.store, index, reference.id);
634
+ const replacement = reference.role === 'related'
635
+ ? null
636
+ : replacementForRole({
637
+ role: reference.role,
638
+ packet: input.packet,
639
+ current,
640
+ artifacts: allArtifacts,
641
+ store: input.store,
642
+ index,
643
+ });
644
+ if (!current) {
645
+ missing.push({
646
+ role: reference.role,
647
+ label: reference.label,
648
+ id: reference.id,
649
+ ...(replacement ? { replacementId: replacement.artifact.id, reason: replacement.reason } : {}),
650
+ });
651
+ if (replacement) {
652
+ replacements.set(reference.id, replacement.artifact.id);
653
+ roleReplacements.set(reference.role, replacement.artifact.id);
654
+ }
655
+ continue;
656
+ }
657
+ if (replacement) {
658
+ superseded.push({
659
+ role: reference.role,
660
+ label: reference.label,
661
+ id: reference.id,
662
+ replacementId: replacement.artifact.id,
663
+ reason: replacement.reason,
664
+ });
665
+ replacements.set(reference.id, replacement.artifact.id);
666
+ roleReplacements.set(reference.role, replacement.artifact.id);
667
+ }
668
+ }
669
+
670
+ return {
671
+ available: true,
672
+ scannedArtifacts: allArtifacts.length,
673
+ status: missing.length > 0 || superseded.length > 0 ? 'needs-review' : 'current',
674
+ missing,
675
+ superseded,
676
+ recommendedPacket: applyFreshnessReplacements(input.packet, replacements, roleReplacements),
677
+ };
678
+ }
679
+
680
+ export function packetFromMetadata(artifact: ArtifactDescriptor): ReviewPacketPresetPacket {
681
+ return {
682
+ ...(readMetadataString(artifact.metadata, 'documentId') ? { documentId: readMetadataString(artifact.metadata, 'documentId') } : {}),
683
+ ...(readMetadataString(artifact.metadata, 'documentTitle') ? { documentTitle: readMetadataString(artifact.metadata, 'documentTitle') } : {}),
684
+ ...(readMetadataString(artifact.metadata, 'documentExportArtifactId') ? { documentExportArtifactId: readMetadataString(artifact.metadata, 'documentExportArtifactId') } : {}),
685
+ ...(readMetadataString(artifact.metadata, 'comparisonArtifactId') ? { comparisonArtifactId: readMetadataString(artifact.metadata, 'comparisonArtifactId') } : {}),
686
+ ...(readMetadataString(artifact.metadata, 'judgmentArtifactId') ? { judgmentArtifactId: readMetadataString(artifact.metadata, 'judgmentArtifactId') } : {}),
687
+ ...(readMetadataString(artifact.metadata, 'revealedJudgmentArtifactId') ? { revealedJudgmentArtifactId: readMetadataString(artifact.metadata, 'revealedJudgmentArtifactId') } : {}),
688
+ ...(readMetadataString(artifact.metadata, 'routeDecisionArtifactId') ? { routeDecisionArtifactId: readMetadataString(artifact.metadata, 'routeDecisionArtifactId') } : {}),
689
+ ...(readMetadataString(artifact.metadata, 'routeDecision') ? { routeDecision: readMetadataString(artifact.metadata, 'routeDecision') } : {}),
690
+ ...(readMetadataString(artifact.metadata, 'handoffArtifactId') ? { handoffArtifactId: readMetadataString(artifact.metadata, 'handoffArtifactId') } : {}),
691
+ ...(readMetadataString(artifact.metadata, 'handoffArchiveArtifactId') ? { handoffArchiveArtifactId: readMetadataString(artifact.metadata, 'handoffArchiveArtifactId') } : {}),
692
+ relatedArtifactIds: readMetadataStringList(artifact.metadata, 'relatedArtifactIds'),
693
+ summary: readMetadataString(artifact.metadata, 'summary') || 'review packet preset',
694
+ };
695
+ }
696
+
697
+ export function parsePresetBody(buffer: Buffer): ReviewPacketPresetRecord | null {
698
+ try {
699
+ const parsed = JSON.parse(buffer.toString('utf-8')) as Partial<ReviewPacketPresetRecord>;
700
+ if (parsed.schema !== REVIEW_PACKET_PRESET_SCHEMA || parsed.schemaVersion !== 1 || !parsed.packet) return null;
701
+ const packet = parsed.packet as Partial<ReviewPacketPresetPacket>;
702
+ const refresh = parsed.refresh && typeof parsed.refresh === 'object'
703
+ ? parsed.refresh as Record<string, unknown>
704
+ : null;
705
+ return {
706
+ schema: REVIEW_PACKET_PRESET_SCHEMA,
707
+ schemaVersion: 1,
708
+ presetId: readString(parsed.presetId),
709
+ name: readString(parsed.name) || 'Reviewer packet preset',
710
+ createdAt: readString(parsed.createdAt),
711
+ explicitUserRequest: readString(parsed.explicitUserRequest),
712
+ ...(refresh
713
+ ? {
714
+ refresh: {
715
+ sourceArtifactId: readString(refresh.sourceArtifactId),
716
+ ...(readString(refresh.sourcePresetId)
717
+ ? { sourcePresetId: readString(refresh.sourcePresetId) }
718
+ : {}),
719
+ missingCount: readNumber(refresh.missingCount, 0),
720
+ supersededCount: readNumber(refresh.supersededCount, 0),
721
+ unresolvedCount: readNumber(refresh.unresolvedCount, 0),
722
+ },
723
+ }
724
+ : {}),
725
+ packet: {
726
+ ...(readString(packet.documentId) ? { documentId: readString(packet.documentId) } : {}),
727
+ ...(readString(packet.documentTitle) ? { documentTitle: readString(packet.documentTitle) } : {}),
728
+ ...(readString(packet.documentExportArtifactId) ? { documentExportArtifactId: readString(packet.documentExportArtifactId) } : {}),
729
+ ...(readString(packet.comparisonArtifactId) ? { comparisonArtifactId: readString(packet.comparisonArtifactId) } : {}),
730
+ ...(readString(packet.judgmentArtifactId) ? { judgmentArtifactId: readString(packet.judgmentArtifactId) } : {}),
731
+ ...(readString(packet.revealedJudgmentArtifactId) ? { revealedJudgmentArtifactId: readString(packet.revealedJudgmentArtifactId) } : {}),
732
+ ...(readString(packet.routeDecisionArtifactId) ? { routeDecisionArtifactId: readString(packet.routeDecisionArtifactId) } : {}),
733
+ ...(readString(packet.routeDecision) ? { routeDecision: readString(packet.routeDecision) } : {}),
734
+ ...(readString(packet.handoffArtifactId) ? { handoffArtifactId: readString(packet.handoffArtifactId) } : {}),
735
+ ...(readString(packet.handoffArchiveArtifactId) ? { handoffArchiveArtifactId: readString(packet.handoffArchiveArtifactId) } : {}),
736
+ relatedArtifactIds: readStringList(packet.relatedArtifactIds),
737
+ summary: readString(packet.summary) || 'review packet preset',
738
+ },
739
+ policy: {
740
+ effect: 'save-local-preset-artifact-only',
741
+ documentsChanged: false,
742
+ modelRouteChanged: false,
743
+ handoffArchiveCreated: false,
744
+ },
745
+ };
746
+ } catch {
747
+ return null;
748
+ }
749
+ }
750
+
751
+ export function sourceArtifactId(packet: ReviewPacketPresetPacket): string {
752
+ return packet.revealedJudgmentArtifactId
753
+ || packet.judgmentArtifactId
754
+ || packet.comparisonArtifactId
755
+ || packet.documentExportArtifactId
756
+ || '';
757
+ }