pi-harness-runtime 1.1.321 → 1.1.323

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 (122) hide show
  1. package/RULES.md +19 -0
  2. package/package.json +1 -1
  3. package/packages/a2a-adapter/package.json +1 -1
  4. package/packages/agent-policy/dist/extension.d.ts.map +1 -1
  5. package/packages/agent-policy/dist/extension.js +47 -12
  6. package/packages/agent-policy/dist/extension.js.map +1 -1
  7. package/packages/agent-policy/dist/index.d.ts +1 -1
  8. package/packages/agent-policy/dist/index.d.ts.map +1 -1
  9. package/packages/agent-policy/dist/index.js +1 -1
  10. package/packages/agent-policy/dist/index.js.map +1 -1
  11. package/packages/agent-policy/dist/policy-renderer.d.ts +15 -0
  12. package/packages/agent-policy/dist/policy-renderer.d.ts.map +1 -1
  13. package/packages/agent-policy/dist/policy-renderer.js +59 -0
  14. package/packages/agent-policy/dist/policy-renderer.js.map +1 -1
  15. package/packages/agent-policy/dist/ssh-guard.d.ts +51 -0
  16. package/packages/agent-policy/dist/ssh-guard.d.ts.map +1 -0
  17. package/packages/agent-policy/dist/ssh-guard.js +172 -0
  18. package/packages/agent-policy/dist/ssh-guard.js.map +1 -0
  19. package/packages/agent-policy/package.json +1 -1
  20. package/packages/agent-policy/src/extension.ts +53 -15
  21. package/packages/agent-policy/src/index.ts +2 -0
  22. package/packages/agent-policy/src/policy-renderer.ts +73 -0
  23. package/packages/agent-policy/src/ssh-guard.ts +204 -0
  24. package/packages/agent-policy/test/policy-retention.test.ts +40 -1
  25. package/packages/architecture-generator/package.json +1 -1
  26. package/packages/auth/package.json +1 -1
  27. package/packages/autonomous-refactor/package.json +1 -1
  28. package/packages/autonomous-runtime/package.json +1 -1
  29. package/packages/cache-strategy/package.json +1 -1
  30. package/packages/capability-registry/package.json +1 -1
  31. package/packages/checkpoint/package.json +1 -1
  32. package/packages/cli-plugin-sdk/package.json +1 -1
  33. package/packages/clipboard/package.json +1 -1
  34. package/packages/clipboard-plugin/package.json +1 -1
  35. package/packages/code-generation/package.json +1 -1
  36. package/packages/code-review/package.json +1 -1
  37. package/packages/codex-adapter/package.json +1 -1
  38. package/packages/codex-watcher/package.json +1 -1
  39. package/packages/config-capture/package.json +1 -1
  40. package/packages/context-compiler/package.json +1 -1
  41. package/packages/context-discovery/package.json +1 -1
  42. package/packages/context-manager/package.json +1 -1
  43. package/packages/cookie-sanitizer/package.json +1 -1
  44. package/packages/cost-optimizer/package.json +1 -1
  45. package/packages/dependency-analyzer/package.json +1 -1
  46. package/packages/django-plugin/package.json +1 -1
  47. package/packages/doc-generator/package.json +1 -1
  48. package/packages/durable-review/package.json +1 -1
  49. package/packages/e2e-tester/package.json +1 -1
  50. package/packages/evaluation-engine/package.json +1 -1
  51. package/packages/evaluation-runner/package.json +1 -1
  52. package/packages/event-bus/package.json +1 -1
  53. package/packages/event-store/package.json +1 -1
  54. package/packages/experience-replay/package.json +1 -1
  55. package/packages/feedback-collector/package.json +1 -1
  56. package/packages/file-copy-helper/package.json +1 -1
  57. package/packages/framework-detector/package.json +1 -1
  58. package/packages/framework-plugin-sdk/package.json +1 -1
  59. package/packages/frappe-plugin/package.json +1 -1
  60. package/packages/generic-web-plugin/package.json +1 -1
  61. package/packages/health-monitor/package.json +1 -1
  62. package/packages/intent-analyzer/package.json +1 -1
  63. package/packages/jev-judge/package.json +1 -1
  64. package/packages/knowledge-graph/package.json +1 -1
  65. package/packages/knowledge-retrieval/package.json +1 -1
  66. package/packages/laravel-plugin/package.json +1 -1
  67. package/packages/learning-engine/package.json +1 -1
  68. package/packages/mcp-adapter/package.json +1 -1
  69. package/packages/memory-engine/package.json +1 -1
  70. package/packages/milestone-manager/package.json +1 -1
  71. package/packages/model-registry/package.json +1 -1
  72. package/packages/nextjs-plugin/package.json +1 -1
  73. package/packages/notification/package.json +1 -1
  74. package/packages/observability/package.json +1 -1
  75. package/packages/okf-indexer/package.json +1 -1
  76. package/packages/paperclip-integration/package.json +1 -1
  77. package/packages/performance-optimizer/package.json +1 -1
  78. package/packages/privilege-broker/package.json +1 -1
  79. package/packages/project-analyzer/package.json +1 -1
  80. package/packages/project-bootstrap/package.json +1 -1
  81. package/packages/projection-engine/package.json +1 -1
  82. package/packages/prompt-compiler/package.json +1 -1
  83. package/packages/prompt-versioning/package.json +1 -1
  84. package/packages/provider-adapter-sdk/package.json +1 -1
  85. package/packages/provider-router/package.json +1 -1
  86. package/packages/provider-selector/package.json +1 -1
  87. package/packages/providers/package.json +1 -1
  88. package/packages/qdrant-skills/package.json +1 -1
  89. package/packages/quota-manager/package.json +1 -1
  90. package/packages/rate-limiter/package.json +1 -1
  91. package/packages/react-vite-plugin/package.json +1 -1
  92. package/packages/release-manager/package.json +1 -1
  93. package/packages/requirement-compiler/package.json +1 -1
  94. package/packages/runtime/package.json +1 -1
  95. package/packages/scheduler/package.json +1 -1
  96. package/packages/scheduler-adapter/package.json +1 -1
  97. package/packages/session/package.json +1 -1
  98. package/packages/session-api/package.json +1 -1
  99. package/packages/session-export/package.json +1 -1
  100. package/packages/shared-context/package.json +1 -1
  101. package/packages/skill-mcp-client/package.json +1 -1
  102. package/packages/skill-registry/package.json +1 -1
  103. package/packages/skills/package.json +1 -1
  104. package/packages/sprint-planner/package.json +1 -1
  105. package/packages/subscription-engine/package.json +1 -1
  106. package/packages/task-compiler/package.json +1 -1
  107. package/packages/telegram/package.json +1 -1
  108. package/packages/tencentdb-memory/package.json +1 -1
  109. package/packages/tencentdb-sync/package.json +1 -1
  110. package/packages/test-data-generator/package.json +1 -1
  111. package/packages/test-generator/package.json +1 -1
  112. package/packages/todo-bd-sync/package.json +1 -1
  113. package/packages/token-estimation/package.json +1 -1
  114. package/packages/token-optimizer/package.json +1 -1
  115. package/packages/trajectory/package.json +1 -1
  116. package/packages/tui/package.json +1 -1
  117. package/packages/types/package.json +1 -1
  118. package/packages/webmcp-tester/package.json +1 -1
  119. package/packages/workflow-events/package.json +1 -1
  120. package/packages/workspace-scanner/package.json +1 -1
  121. package/packages/worktree/package.json +1 -1
  122. package/packages/write-review/package.json +1 -1
@@ -26,8 +26,11 @@ import { getPolicyStore } from "./policy-store.js";
26
26
  import {
27
27
  renderSystemContract,
28
28
  renderFullRules,
29
+ renderFullPolicyText,
30
+ checkPolicyTextSize,
29
31
  } from "./policy-renderer.js";
30
32
  import { classifyToolCall } from "./types.js";
33
+ import { checkSshNecessity } from "./ssh-guard.js";
31
34
  import { randomUUID } from "node:crypto";
32
35
 
33
36
  // ---------------------------------------------------------------------------
@@ -78,11 +81,26 @@ export function registerAgentPolicy(
78
81
  "system_prompt"
79
82
  );
80
83
 
81
- // Render and inject contract
82
- const contract = renderSystemContract(manifest);
84
+ // Decide what to deliver based on compaction state
85
+ let policyText: string;
86
+ if (needsFullDelivery) {
87
+ // After compaction, deliver FULL policy text so model has actual rules
88
+ const sizeCheck = checkPolicyTextSize(manifest);
89
+ if (sizeCheck.fits) {
90
+ policyText = renderFullPolicyText(manifest);
91
+ } else {
92
+ // Too large, deliver contract with warning
93
+ policyText = renderSystemContract(manifest) +
94
+ `\n[WARNING: ${sizeCheck.reason}]`;
95
+ }
96
+ needsFullDelivery = false;
97
+ } else {
98
+ // Normal delivery: compact contract
99
+ policyText = renderSystemContract(manifest);
100
+ }
83
101
 
84
102
  // Build augmented system prompt
85
- const augmentedPrompt = event.systemPrompt + "\n\n" + contract;
103
+ const augmentedPrompt = event.systemPrompt + "\n\n" + policyText;
86
104
 
87
105
  // Log for debugging
88
106
  logInfo("[agent-policy]", `Delivered policy ${manifest.revision} to session ${receipt.sessionId}`);
@@ -155,12 +173,30 @@ Use harness_rules to check policy status.
155
173
  }
156
174
  }
157
175
 
176
+ // Check SSH guard for shell/bash commands
177
+ if ((toolName === "bash" || toolName === "shell" || toolName === "ctx_execute") && input && typeof input === "object") {
178
+ const inputObj = input as Record<string, unknown>;
179
+ const command = String(inputObj.command ?? "");
180
+
181
+ // Check if this is an SSH command
182
+ if (/\bssh\b/.test(command)) {
183
+ const sshCheck = await checkSshNecessity(command);
184
+ if (!sshCheck.allowed) {
185
+ logInfo("[agent-policy]", `Blocked SSH to local destination: ${sshCheck.message}`);
186
+ return {
187
+ block: true,
188
+ reason: `[SSH GUARD] ${sshCheck.message}`,
189
+ };
190
+ }
191
+ }
192
+ }
193
+
158
194
  // Policy satisfied, allow the call
159
195
  return {};
160
196
  } catch (err) {
161
197
  const errorMessage = err instanceof Error ? err.message : String(err);
162
198
  logError("[agent-policy]", `Policy check failed: ${errorMessage}`);
163
-
199
+
164
200
  // On error, fail open for the command but log the issue
165
201
  // This matches pi-lens's total handler guard behavior
166
202
  return {};
@@ -336,24 +372,26 @@ Use harness_rules to check policy status.
336
372
  logInfo("[agent-policy]", `Session started: ${sessionId} in ${cwd}`);
337
373
  });
338
374
 
375
+ // Track if we need full policy delivery (after compaction)
376
+ let needsFullDelivery = false;
377
+
339
378
  // ---------------------------------------------------------------------------
340
- // context: Request-time policy delivery
341
- // ---------------------------------------------------------------------------
342
- // session_compact: Invalidate delivery coverage for next request
379
+ // session_compact: Flag for full policy delivery
343
380
  // ---------------------------------------------------------------------------
344
381
  //
345
- // After compaction, clear receipts so the next before_agent_start delivers fresh policy.
346
- // This ensures the model sees current rules after context recovery.
382
+ // After compaction, we must deliver FULL policy text on next before_agent_start.
383
+ // The model needs actual rule content, not a reminder pointer.
347
384
  //
348
-
349
385
  pi.on("session_compact", (_event: SessionCompactEvent, ctx: unknown) => {
350
386
  const sessionId = getSessionIdFromCtx(ctx, fallbackSessionId);
351
-
352
- // Clear receipts for this session so next request gets fresh policy delivery
353
- // This ensures rules are re-delivered after compaction without stale receipt blocking
387
+
388
+ // Clear receipts so before_agent_start will re-deliver
354
389
  store.clearSession(sessionId);
355
-
356
- logInfo("[agent-policy]", `Session compact: cleared receipts for session ${sessionId}`);
390
+
391
+ // Flag that next before_agent_start should deliver full text
392
+ needsFullDelivery = true;
393
+
394
+ logInfo("[agent-policy]", `Session compact: flagged for full policy delivery to session ${sessionId}`);
357
395
  });
358
396
 
359
397
  // ---------------------------------------------------------------------------
@@ -40,6 +40,8 @@ export {
40
40
  renderFullRules,
41
41
  renderManifestSummary,
42
42
  renderFindingEnvelope,
43
+ renderFullPolicyText,
44
+ checkPolicyTextSize,
43
45
  parseSections,
44
46
  createCleanEnvelope,
45
47
  createDegradedEnvelope,
@@ -139,6 +139,79 @@ export function parseSections(content: string, source: string, scope: string, pr
139
139
  return sections;
140
140
  }
141
141
 
142
+ // ---------------------------------------------------------------------------
143
+ // Full Policy Text Rendering
144
+ // ---------------------------------------------------------------------------
145
+
146
+ /**
147
+ * Render full policy text for injection after compaction.
148
+ * This includes the ACTUAL rule file contents, not just a contract pointer.
149
+ * Used after session_compact to deliver fresh policy text to the model.
150
+ */
151
+ export function renderFullPolicyText(manifest: PolicyManifest): string {
152
+ const lines: string[] = [];
153
+
154
+ lines.push(`PI-HARNESS POLICY (full text) revision=${manifest.revision} coverage=${manifest.coverage}`);
155
+ lines.push("");
156
+
157
+ // Include all mandatory sources with their full content
158
+ for (const source of manifest.sources) {
159
+ if (source.priority === "mandatory" || source.harnessOwned) {
160
+ const content = readSourceContent(source.path);
161
+ if (content) {
162
+ lines.push(`--- ${source.path} [${source.priority}] ---`);
163
+ lines.push("");
164
+ lines.push(content);
165
+ lines.push("");
166
+ }
167
+ }
168
+ }
169
+
170
+ // Include advisory sources if they exist
171
+ for (const source of manifest.sources) {
172
+ if (source.priority === "advisory" && !source.harnessOwned) {
173
+ const content = readSourceContent(source.path);
174
+ if (content) {
175
+ lines.push(`--- ${source.path} [advisory] ---`);
176
+ lines.push("");
177
+ lines.push(content);
178
+ lines.push("");
179
+ }
180
+ }
181
+ }
182
+
183
+ return lines.join("\n");
184
+ }
185
+
186
+ /**
187
+ * Check if full policy text would fit in context.
188
+ * Returns { fits: true } or { fits: false, reason: string, bytes: number }.
189
+ */
190
+ export function checkPolicyTextSize(manifest: PolicyManifest, maxBytes = 100 * 1024): {
191
+ fits: boolean;
192
+ bytes?: number;
193
+ reason?: string;
194
+ } {
195
+ let totalBytes = 0;
196
+
197
+ for (const source of manifest.sources) {
198
+ const content = readSourceContent(source.path);
199
+ if (content) {
200
+ totalBytes += Buffer.byteLength(content, "utf8");
201
+ }
202
+ }
203
+
204
+ if (totalBytes <= maxBytes) {
205
+ return { fits: true, bytes: totalBytes };
206
+ }
207
+
208
+ return {
209
+ fits: false,
210
+ bytes: totalBytes,
211
+ reason: `Full policy text (${totalBytes} bytes) exceeds limit (${maxBytes} bytes). Use harness_rules read for selective retrieval.`,
212
+ };
213
+ }
214
+
142
215
  // ---------------------------------------------------------------------------
143
216
  // Full Rules Rendering
144
217
  // ---------------------------------------------------------------------------
@@ -0,0 +1,204 @@
1
+ /**
2
+ * SSH Guard Tool
3
+ *
4
+ * Intercepts SSH commands and checks if the destination matches the local environment.
5
+ * Combines automatic checking with a fresh reminder when the agent proposes SSH.
6
+ */
7
+
8
+ import { execSync } from "node:child_process";
9
+ import { createHash } from "node:crypto";
10
+ import dns from "node:dns";
11
+ import { promisify } from "node:util";
12
+
13
+ const lookupAsync = promisify(dns.lookup);
14
+
15
+ /**
16
+ * Result of SSH guard check
17
+ */
18
+ export interface SshGuardResult {
19
+ /** Whether SSH is allowed to proceed */
20
+ allowed: boolean;
21
+ /** Message explaining the decision */
22
+ message: string;
23
+ /** Whether the destination matches local */
24
+ isLocal: boolean;
25
+ /** Resolved destination address */
26
+ destinationAddress?: string;
27
+ }
28
+
29
+ /**
30
+ * Parse SSH destination from command line
31
+ */
32
+ function parseSshDestination(command: string): string | null {
33
+ // Match patterns like:
34
+ // ssh user@host
35
+ // ssh host
36
+ // ssh -p port user@host
37
+ // ssh host.domain.com
38
+ const match = command.match(/ssh\s+(?:-[a-z]+\s+)*((?:[a-zA-Z0-9_-]+\@)?[a-zA-Z0-9._-]+)/);
39
+ return match ? match[1] : null;
40
+ }
41
+
42
+ /**
43
+ * Get local IP addresses (cross-platform)
44
+ */
45
+ function getLocalAddresses(): string[] {
46
+ try {
47
+ const isWindows = process.platform === "win32";
48
+ let output: string;
49
+
50
+ if (isWindows) {
51
+ // Windows: use ipconfig
52
+ output = execSync("ipconfig", { encoding: "utf8", timeout: 5000, windowsHide: true });
53
+ } else {
54
+ // Unix-like: use ip address
55
+ output = execSync("ip -j address 2>/dev/null || ip address 2>/dev/null", {
56
+ encoding: "utf8",
57
+ timeout: 5000,
58
+ });
59
+ }
60
+
61
+ const addresses: string[] = ["127.0.0.1", "::1", "localhost"];
62
+
63
+ // Parse JSON output if available (Linux)
64
+ try {
65
+ const json = JSON.parse(output);
66
+ for (const iface of json) {
67
+ for (const addr of iface.addr_info || []) {
68
+ if (addr.local !== addr.peer) {
69
+ addresses.push(addr.local);
70
+ }
71
+ }
72
+ }
73
+ } catch {
74
+ // Fallback to text parsing
75
+ if (isWindows) {
76
+ // Windows ipconfig: parse "IPv4 Address. . . . . . . . . . : X.X.X.X"
77
+ const windowsMatch = output.matchAll(/IPv4[^:]*:\s*([0-9.]+)/gi);
78
+ for (const match of windowsMatch) {
79
+ const ip = match[1];
80
+ if (ip !== "127.0.0.1") {
81
+ addresses.push(ip);
82
+ }
83
+ }
84
+ // Also check for IPv6
85
+ const ipv6Match = output.matchAll(/IPv6[^:]*:\s*([0-9a-f:]+)/gi);
86
+ for (const match of ipv6Match) {
87
+ addresses.push(match[1]);
88
+ }
89
+ } else {
90
+ // Unix-like: parse "inet X.X.X.X"
91
+ const ipMatch = output.match(/inet\s+([0-9.]+)/g);
92
+ if (ipMatch) {
93
+ for (const match of ipMatch) {
94
+ const ip = match.replace("inet ", "");
95
+ if (ip !== "127.0.0.1") {
96
+ addresses.push(ip);
97
+ }
98
+ }
99
+ }
100
+ }
101
+ }
102
+
103
+ return [...new Set(addresses)];
104
+ } catch {
105
+ return ["127.0.0.1", "::1", "localhost"];
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Resolve hostname to IP address
111
+ */
112
+ async function resolveHost(hostname: string): Promise<string | null> {
113
+ // Skip if already an IP
114
+ if (/^[0-9.]+$/.test(hostname)) return hostname;
115
+ if (/^[0-9a-f:]+$/i.test(hostname)) return hostname;
116
+
117
+ try {
118
+ const result = await lookupAsync(hostname);
119
+ return result.address;
120
+ } catch {
121
+ return null;
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Check if destination is local
127
+ */
128
+ export async function checkSshNecessity(command: string): Promise<SshGuardResult> {
129
+ const destination = parseSshDestination(command);
130
+
131
+ if (!destination) {
132
+ return {
133
+ allowed: true,
134
+ message: "Could not parse SSH destination",
135
+ isLocal: false,
136
+ };
137
+ }
138
+
139
+ const localAddresses = getLocalAddresses();
140
+ const resolved = await resolveHost(destination);
141
+
142
+ if (!resolved) {
143
+ return {
144
+ allowed: true,
145
+ message: `Could not resolve SSH destination: ${destination}`,
146
+ isLocal: false,
147
+ destinationAddress: undefined,
148
+ };
149
+ }
150
+
151
+ const isLocal = localAddresses.some(
152
+ (addr) => addr === resolved || resolved === addr
153
+ );
154
+
155
+ if (isLocal) {
156
+ return {
157
+ allowed: false,
158
+ message: `SSH destination ${destination} (${resolved}) matches this environment (local addresses: ${localAddresses.join(", ")}). Run the command locally, or explain why a separate SSH session is required.`,
159
+ isLocal: true,
160
+ destinationAddress: resolved,
161
+ };
162
+ }
163
+
164
+ return {
165
+ allowed: true,
166
+ message: `SSH to ${destination} (${resolved}) appears necessary - destination differs from local addresses`,
167
+ isLocal: false,
168
+ destinationAddress: resolved,
169
+ };
170
+ }
171
+
172
+ /**
173
+ * Tool definition for SSH guard
174
+ */
175
+ export const SshGuardTool = {
176
+ name: "ssh_guard_check",
177
+ description: "Check if SSH destination matches local environment before allowing SSH",
178
+ parameters: {
179
+ type: "object",
180
+ properties: {
181
+ command: {
182
+ type: "string",
183
+ description: "The SSH command to check",
184
+ },
185
+ },
186
+ required: ["command"],
187
+ },
188
+ } as const;
189
+
190
+ /**
191
+ * Execute SSH guard check
192
+ */
193
+ export async function executeSshGuard(
194
+ args: { command: string },
195
+ _api: unknown,
196
+ _context: unknown
197
+ ): Promise<{ allowed: boolean; message: string; isLocal: boolean }> {
198
+ const result = await checkSshNecessity(args.command);
199
+ return {
200
+ allowed: result.allowed,
201
+ message: result.message,
202
+ isLocal: result.isLocal,
203
+ };
204
+ }
@@ -292,8 +292,47 @@ describe("Policy Retention Tests", () => {
292
292
 
293
293
  // Note partial delivery (only section 1)
294
294
  const receipt = store.noteDelivered(sessionId, manifest, "tool", ["section-1"]);
295
-
296
295
  assert.deepStrictEqual(receipt.sectionsDelivered, ["section-1"]);
297
296
  });
298
297
  });
298
+
299
+ // --- Full Policy Text Delivery Tests ---
300
+ describe("Full policy text delivery", () => {
301
+ it("should compute full policy text after revision change", async () => {
302
+ writeFileSync(AGENTS_MD, "# Full Agents\n\n## Rule 1\n\nContent 1.\n\n## Rule 2\n\nContent 2.", "utf8");
303
+
304
+ const loader = new PolicyLoader({
305
+ harnessRulesPath: new URL(`file://${AGENTS_MD}`),
306
+ });
307
+
308
+ const manifest = await loader.load(TEST_DIR);
309
+
310
+ // Full text should include actual content, not just contract pointers
311
+ const { renderFullPolicyText } = await import("../dist/policy-renderer.js");
312
+ const fullText = renderFullPolicyText(manifest);
313
+
314
+ assert.ok(fullText.includes("Full Agents"), "Should include source title");
315
+ assert.ok(fullText.includes("Rule 1"), "Should include section heading");
316
+ assert.ok(fullText.includes("Content 1"), "Should include actual content");
317
+ assert.ok(fullText.includes("revision="), "Should include revision info");
318
+ });
319
+
320
+ it("should detect when full policy exceeds size limit", async () => {
321
+ const loader = new PolicyLoader({
322
+ harnessRulesPath: new URL(`file://${AGENTS_MD}`),
323
+ });
324
+
325
+ const manifest = await loader.load(TEST_DIR);
326
+ const { checkPolicyTextSize } = await import("../dist/policy-renderer.js");
327
+
328
+ // With normal-sized content, should fit in 100KB limit
329
+ const result = checkPolicyTextSize(manifest, 100 * 1024);
330
+ assert.strictEqual(result.fits, true, "Normal policy should fit in 100KB");
331
+
332
+ // With very small limit, should report overflow
333
+ const smallResult = checkPolicyTextSize(manifest, 10);
334
+ assert.strictEqual(smallResult.fits, false, "Should report when limit is too small");
335
+ assert.ok(smallResult.reason?.includes("exceeds limit"), "Should explain overflow");
336
+ });
337
+ });
299
338
  });
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/architecture-generator",
3
- "version": "1.1.321",
3
+ "version": "1.1.323",
4
4
  "description": "Generate ADR and Mermaid diagrams from project analysis",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/auth",
3
- "version": "1.1.321",
3
+ "version": "1.1.323",
4
4
  "description": "pi-harness-runtime — auth",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/autonomous-refactor",
3
- "version": "1.1.321",
3
+ "version": "1.1.323",
4
4
  "type": "module",
5
5
  "description": "Autonomous refactoring engine",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/autonomous-runtime",
3
- "version": "1.1.321",
3
+ "version": "1.1.323",
4
4
  "description": "Autonomous Operations Runtime — task inbox, lease management, and worker lifecycle (RFC-0101)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/cache-strategy",
3
- "version": "1.1.321",
3
+ "version": "1.1.323",
4
4
  "type": "module",
5
5
  "description": "Smart caching with TTL and invalidation strategies (RFC-0022)",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/capability-registry",
3
- "version": "1.1.321",
3
+ "version": "1.1.323",
4
4
  "description": "Capability Registry for pi-harness-runtime — tracks model capabilities and enables capability-based routing",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/checkpoint",
3
- "version": "1.1.321",
3
+ "version": "1.1.323",
4
4
  "description": "Enhanced checkpoint system with incremental snapshots, diff-based storage, and recovery strategies",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/cli-plugin-sdk",
3
- "version": "1.1.321",
3
+ "version": "1.1.323",
4
4
  "type": "module",
5
5
  "description": "CLI Plugin SDK for pi-harness — install, list, remove, update, invoke plugins",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/clipboard",
3
- "version": "1.1.321",
3
+ "version": "1.1.323",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/clipboard-plugin",
3
- "version": "1.1.321",
3
+ "version": "1.1.323",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/code-generation",
3
- "version": "1.1.321",
3
+ "version": "1.1.323",
4
4
  "description": "Code generation pipeline for pi-harness-runtime",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/code-review",
3
- "version": "1.1.321",
3
+ "version": "1.1.323",
4
4
  "description": "Automated code review engine for pi-harness-runtime",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/codex-adapter",
3
- "version": "1.1.321",
3
+ "version": "1.1.323",
4
4
  "type": "module",
5
5
  "description": "OpenAI Codex provider adapter for pi-harness",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/codex-watcher",
3
- "version": "1.1.321",
3
+ "version": "1.1.323",
4
4
  "description": "Watches Codex CLI sessions and emits visible messages for PING-PONG monitoring",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moocoding/config-capture",
3
- "version": "1.1.321",
3
+ "version": "1.1.323",
4
4
  "description": "Auto-capture API-fetched configuration values to AGENTS.md",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/context-compiler",
3
- "version": "1.1.321",
3
+ "version": "1.1.323",
4
4
  "description": "Bounded context selection engine for pi-harness-runtime",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/context-discovery",
3
- "version": "1.1.321",
3
+ "version": "1.1.323",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/context-manager",
3
- "version": "1.1.321",
3
+ "version": "1.1.323",
4
4
  "description": "Context Window Manager — prevents model context overflow via compaction and resume prompts (RFC-0010)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/cookie-sanitizer",
3
- "version": "1.1.321",
3
+ "version": "1.1.323",
4
4
  "description": "pi-harness-runtime — cookie sanitizer (provider-quota cookie file normaliser)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/cost-optimizer",
3
- "version": "1.1.321",
3
+ "version": "1.1.323",
4
4
  "description": "Cost Optimizer for pi-harness-runtime — budget tracking, forecasting, and model selection optimization",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/dependency-analyzer",
3
- "version": "1.1.321",
3
+ "version": "1.1.323",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/django-plugin",
3
- "version": "1.1.321",
3
+ "version": "1.1.323",
4
4
  "description": "Django framework analysis — models, management commands, settings",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/doc-generator",
3
- "version": "1.1.321",
3
+ "version": "1.1.323",
4
4
  "description": "Auto-Doc Generator — generate documentation from source code (RFC-0014)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",