pi-harness-runtime 1.1.183 → 1.1.185
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/CHANGELOG.md +7 -0
- package/harness/glm-quota-countdown.ts +9 -1
- package/harness/honcho-init.ts +745 -0
- package/harness/honcho-memory.ts +290 -97
- package/harness/service-commands.ts +28 -3
- package/index.ts +191 -63
- package/package.json +1 -1
- package/packages/a2a-adapter/package.json +1 -1
- package/packages/agent-policy/dist/extension.d.ts.map +1 -1
- package/packages/agent-policy/dist/extension.js +16 -9
- package/packages/agent-policy/dist/extension.js.map +1 -1
- package/packages/agent-policy/dist/policy-loader.d.ts.map +1 -1
- package/packages/agent-policy/dist/policy-loader.js +5 -16
- package/packages/agent-policy/dist/policy-loader.js.map +1 -1
- package/packages/agent-policy/package.json +1 -1
- package/packages/agent-policy/src/extension.ts +16 -9
- package/packages/agent-policy/src/policy-loader.ts +5 -22
- package/packages/agent-policy/test/interlock.test.ts +101 -0
- package/packages/architecture-generator/package.json +1 -1
- package/packages/auth/package.json +1 -1
- package/packages/autonomous-refactor/package.json +1 -1
- package/packages/autonomous-runtime/package.json +1 -1
- package/packages/cache-strategy/package.json +1 -1
- package/packages/capability-registry/package.json +1 -1
- package/packages/checkpoint/package.json +1 -1
- package/packages/cli-plugin-sdk/package.json +1 -1
- package/packages/clipboard/package.json +1 -1
- package/packages/clipboard-plugin/package.json +1 -1
- package/packages/code-generation/package.json +1 -1
- package/packages/code-review/package.json +1 -1
- package/packages/codex-adapter/package.json +1 -1
- package/packages/codex-watcher/package.json +1 -1
- package/packages/config-capture/package.json +1 -1
- package/packages/context-compiler/package.json +1 -1
- package/packages/context-discovery/package.json +1 -1
- package/packages/context-manager/package.json +1 -1
- package/packages/cookie-sanitizer/package.json +1 -1
- package/packages/cost-optimizer/package.json +1 -1
- package/packages/dependency-analyzer/package.json +1 -1
- package/packages/django-plugin/package.json +1 -1
- package/packages/doc-generator/package.json +1 -1
- package/packages/e2e-tester/package.json +1 -1
- package/packages/evaluation-engine/package.json +1 -1
- package/packages/evaluation-runner/package.json +1 -1
- package/packages/event-bus/package.json +1 -1
- package/packages/event-store/package.json +1 -1
- package/packages/experience-replay/package.json +1 -1
- package/packages/feedback-collector/package.json +1 -1
- package/packages/file-copy-helper/package.json +1 -1
- package/packages/framework-detector/package.json +1 -1
- package/packages/framework-plugin-sdk/package.json +1 -1
- package/packages/frappe-plugin/package.json +1 -1
- package/packages/generic-web-plugin/package.json +1 -1
- package/packages/health-monitor/package.json +1 -1
- package/packages/intent-analyzer/package.json +1 -1
- package/packages/jev-judge/package.json +1 -1
- package/packages/knowledge-graph/package.json +1 -1
- package/packages/knowledge-retrieval/package.json +1 -1
- package/packages/laravel-plugin/package.json +1 -1
- package/packages/learning-engine/package.json +1 -1
- package/packages/mcp-adapter/package.json +1 -1
- package/packages/memory-engine/package.json +1 -1
- package/packages/milestone-manager/package.json +1 -1
- package/packages/model-registry/package.json +1 -1
- package/packages/nextjs-plugin/package.json +1 -1
- package/packages/notification/dist/adapters/telegram-adapter.d.ts +1 -1
- package/packages/notification/dist/adapters/telegram-adapter.d.ts.map +1 -1
- package/packages/notification/dist/adapters/telegram-adapter.js +34 -21
- package/packages/notification/dist/adapters/telegram-adapter.js.map +1 -1
- package/packages/notification/package.json +1 -1
- package/packages/notification/src/adapters/telegram-adapter.ts +37 -21
- package/packages/observability/package.json +1 -1
- package/packages/okf-indexer/package.json +1 -1
- package/packages/paperclip-integration/package.json +1 -1
- package/packages/performance-optimizer/package.json +1 -1
- package/packages/privilege-broker/package.json +1 -1
- package/packages/project-analyzer/package.json +1 -1
- package/packages/project-bootstrap/package.json +1 -1
- package/packages/projection-engine/package.json +1 -1
- package/packages/prompt-compiler/package.json +1 -1
- package/packages/prompt-versioning/package.json +1 -1
- package/packages/provider-adapter-sdk/package.json +1 -1
- package/packages/provider-router/package.json +1 -1
- package/packages/provider-selector/package.json +1 -1
- package/packages/providers/package.json +1 -1
- package/packages/qdrant-skills/package.json +1 -1
- package/packages/quota-manager/package.json +1 -1
- package/packages/rate-limiter/package.json +1 -1
- package/packages/react-vite-plugin/package.json +1 -1
- package/packages/release-manager/package.json +1 -1
- package/packages/requirement-compiler/package.json +1 -1
- package/packages/runtime/package.json +1 -1
- package/packages/scheduler/package.json +1 -1
- package/packages/scheduler-adapter/package.json +1 -1
- package/packages/session/package.json +1 -1
- package/packages/session-api/package.json +1 -1
- package/packages/session-export/package.json +1 -1
- package/packages/shared-context/package.json +1 -1
- package/packages/skill-mcp-client/package.json +1 -1
- package/packages/skill-registry/package.json +1 -1
- package/packages/skills/package.json +1 -1
- package/packages/sprint-planner/package.json +1 -1
- package/packages/subscription-engine/package.json +1 -1
- package/packages/task-compiler/package.json +1 -1
- package/packages/telegram/package.json +1 -1
- package/packages/tencentdb-memory/package.json +1 -1
- package/packages/tencentdb-sync/package.json +1 -1
- package/packages/test-data-generator/package.json +1 -1
- package/packages/test-generator/package.json +1 -1
- package/packages/todo-bd-sync/package.json +1 -1
- package/packages/token-estimation/package.json +1 -1
- package/packages/token-optimizer/package.json +1 -1
- package/packages/trajectory/package.json +1 -1
- package/packages/tui/package.json +1 -1
- package/packages/types/package.json +1 -1
- package/packages/webmcp-tester/package.json +1 -1
- package/packages/workflow-events/package.json +1 -1
- package/packages/workspace-scanner/package.json +1 -1
- package/packages/worktree/package.json +1 -1
- package/packages/write-review/package.json +1 -1
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
renderFullRules,
|
|
25
25
|
} from "./policy-renderer.js";
|
|
26
26
|
import { classifyToolCall } from "./types.js";
|
|
27
|
+
import { randomUUID } from "node:crypto";
|
|
27
28
|
|
|
28
29
|
// ---------------------------------------------------------------------------
|
|
29
30
|
// Extension Registration
|
|
@@ -43,6 +44,7 @@ export function registerAgentPolicy(
|
|
|
43
44
|
|
|
44
45
|
const store = getPolicyStore();
|
|
45
46
|
const toolClassifier = config.toolClassifier ?? classifyToolCall;
|
|
47
|
+
const fallbackSessionId = `policy-${randomUUID()}`;
|
|
46
48
|
|
|
47
49
|
// Track if pi-lens is available
|
|
48
50
|
let piLensAvailable = false;
|
|
@@ -65,7 +67,7 @@ export function registerAgentPolicy(
|
|
|
65
67
|
const manifest = await loader.load(cwd);
|
|
66
68
|
|
|
67
69
|
// Record delivery in store
|
|
68
|
-
const sessionId = getSessionIdFromCtx(ctx);
|
|
70
|
+
const sessionId = getSessionIdFromCtx(ctx, fallbackSessionId);
|
|
69
71
|
const receipt = store.noteDelivered(
|
|
70
72
|
sessionId,
|
|
71
73
|
manifest,
|
|
@@ -113,7 +115,7 @@ Use harness_rules to check policy status.
|
|
|
113
115
|
}
|
|
114
116
|
|
|
115
117
|
const cwd = getCwdFromContext(ctx);
|
|
116
|
-
const sessionId = getSessionIdFromCtx(ctx);
|
|
118
|
+
const sessionId = getSessionIdFromCtx(ctx, fallbackSessionId);
|
|
117
119
|
const targetPath = capability.affectedPaths?.[0];
|
|
118
120
|
|
|
119
121
|
try {
|
|
@@ -121,7 +123,7 @@ Use harness_rules to check policy status.
|
|
|
121
123
|
const receipt = store.getReceipt(sessionId, manifest.projectRoot);
|
|
122
124
|
|
|
123
125
|
// Check if current revision is covered
|
|
124
|
-
if (!receipt || receipt.revision !== manifest.revision) {
|
|
126
|
+
if (config.requireReceiptBeforeMutation && (!receipt || receipt.revision !== manifest.revision)) {
|
|
125
127
|
const blockReason = receipt
|
|
126
128
|
? `Policy revision changed (expected ${manifest.revision}, received ${receipt.revision})`
|
|
127
129
|
: `Policy revision ${manifest.revision} not yet delivered`;
|
|
@@ -200,8 +202,8 @@ Use harness_rules to check policy status.
|
|
|
200
202
|
} as const,
|
|
201
203
|
|
|
202
204
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
203
|
-
const cwd = ctx
|
|
204
|
-
const sessionId = getSessionIdFromCtx(ctx);
|
|
205
|
+
const cwd = getCwdFromContext(ctx);
|
|
206
|
+
const sessionId = getSessionIdFromCtx(ctx, fallbackSessionId);
|
|
205
207
|
|
|
206
208
|
try {
|
|
207
209
|
const input = params as HarnessRulesInput;
|
|
@@ -324,7 +326,7 @@ Use harness_rules to check policy status.
|
|
|
324
326
|
// ---------------------------------------------------------------------------
|
|
325
327
|
|
|
326
328
|
pi.on("session_start", (_event: SessionStartEvent, ctx: unknown) => {
|
|
327
|
-
const sessionId = getSessionIdFromCtx(ctx);
|
|
329
|
+
const sessionId = getSessionIdFromCtx(ctx, fallbackSessionId);
|
|
328
330
|
const cwd = getCwdFromContext(ctx);
|
|
329
331
|
|
|
330
332
|
console.log(`[agent-policy] Session started: ${sessionId} in ${cwd}`);
|
|
@@ -348,8 +350,13 @@ Use harness_rules to check policy status.
|
|
|
348
350
|
/**
|
|
349
351
|
* Extract session ID from context.
|
|
350
352
|
*/
|
|
351
|
-
function getSessionIdFromCtx(ctx: unknown): string {
|
|
353
|
+
function getSessionIdFromCtx(ctx: unknown, fallbackSessionId: string): string {
|
|
352
354
|
const ctxObj = ctx as Record<string, unknown>;
|
|
355
|
+
const manager = ctxObj?.sessionManager as { getSessionId?: () => string } | undefined;
|
|
356
|
+
const managedId = manager?.getSessionId?.();
|
|
357
|
+
if (typeof managedId === "string" && managedId) {
|
|
358
|
+
return managedId;
|
|
359
|
+
}
|
|
353
360
|
|
|
354
361
|
if (typeof ctxObj?.sessionId === "string") {
|
|
355
362
|
return ctxObj.sessionId;
|
|
@@ -359,8 +366,8 @@ function getSessionIdFromCtx(ctx: unknown): string {
|
|
|
359
366
|
return ctxObj.id;
|
|
360
367
|
}
|
|
361
368
|
|
|
362
|
-
//
|
|
363
|
-
return
|
|
369
|
+
// Legacy hosts without session identity must reuse the registration's ID.
|
|
370
|
+
return fallbackSessionId;
|
|
364
371
|
}
|
|
365
372
|
|
|
366
373
|
/**
|
|
@@ -87,28 +87,11 @@ export class PolicyLoader {
|
|
|
87
87
|
* Load policy for a specific target file (considers scoped rules).
|
|
88
88
|
*/
|
|
89
89
|
async loadForTarget(projectRoot: string, targetPath?: string): Promise<PolicyManifest> {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const relativePath = relative(projectRoot, targetPath);
|
|
96
|
-
const scope = this.inferScope(relativePath);
|
|
97
|
-
|
|
98
|
-
const manifest = await this.load(projectRoot);
|
|
99
|
-
|
|
100
|
-
// Filter sources to only those applicable to this scope
|
|
101
|
-
if (scope) {
|
|
102
|
-
manifest.sources = manifest.sources.filter(s =>
|
|
103
|
-
s.scope === "" || scope.startsWith(s.scope) || s.scope.startsWith(scope)
|
|
104
|
-
);
|
|
105
|
-
// Recompute revision if sources changed
|
|
106
|
-
if (manifest.sources.length > 0) {
|
|
107
|
-
manifest.revision = this.computeRevision(manifest.sources);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return manifest;
|
|
90
|
+
// The current loader collects only project-root and ancestor rules, all of
|
|
91
|
+
// which apply to every target. Use the same manifest as harness_rules.
|
|
92
|
+
// Filtering here mutated the cached manifest, dropped ancestor rules, and
|
|
93
|
+
// hashed advisory sources that load() deliberately excludes from revision.
|
|
94
|
+
return this.load(projectRoot);
|
|
112
95
|
}
|
|
113
96
|
|
|
114
97
|
/**
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { afterEach, beforeEach, expect, test } from "bun:test";
|
|
2
|
+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
6
|
+
import { registerAgentPolicy } from "../src/extension.js";
|
|
7
|
+
import { PolicyLoader } from "../src/policy-loader.js";
|
|
8
|
+
import { resetPolicyStore } from "../src/policy-store.js";
|
|
9
|
+
|
|
10
|
+
let root: string;
|
|
11
|
+
let cwd: string;
|
|
12
|
+
let harnessRulesPath: URL;
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
resetPolicyStore();
|
|
15
|
+
root = mkdtempSync(join(tmpdir(), "policy-interlock-"));
|
|
16
|
+
cwd = join(root, "project");
|
|
17
|
+
mkdirSync(join(cwd, "src"), { recursive: true });
|
|
18
|
+
mkdirSync(join(cwd, ".git"));
|
|
19
|
+
writeFileSync(join(root, "harness.md"), "Harness mandatory rules");
|
|
20
|
+
writeFileSync(join(root, "AGENTS.md"), "Ancestor mandatory rules");
|
|
21
|
+
writeFileSync(join(cwd, "AGENTS.md"), "Project mandatory rules");
|
|
22
|
+
writeFileSync(join(cwd, "RULES.md"), "Advisory rules");
|
|
23
|
+
harnessRulesPath = pathToFileURL(join(root, "harness.md"));
|
|
24
|
+
});
|
|
25
|
+
afterEach(() => rmSync(root, { recursive: true, force: true }));
|
|
26
|
+
|
|
27
|
+
function runtime(requireReceiptBeforeMutation = true) {
|
|
28
|
+
const hooks = new Map<string, Function[]>();
|
|
29
|
+
let tool: any;
|
|
30
|
+
registerAgentPolicy({
|
|
31
|
+
on(name: string, handler: Function) {
|
|
32
|
+
hooks.set(name, [...(hooks.get(name) ?? []), handler]);
|
|
33
|
+
},
|
|
34
|
+
registerTool(value: unknown) { tool = value; },
|
|
35
|
+
} as any, { harnessRulesPath, requireReceiptBeforeMutation, lspMode: "pi-lens" });
|
|
36
|
+
return {
|
|
37
|
+
call: (params: unknown, ctx: unknown) => tool.execute("test", params, undefined, undefined, ctx),
|
|
38
|
+
gate: (ctx: unknown, path = "src/file.ts") => hooks.get("tool_call"),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
test("target checks preserve mandatory revision, ancestor rules, and cached manifest", async () => {
|
|
44
|
+
const loader = new PolicyLoader({ harnessRulesPath });
|
|
45
|
+
const manifest = await loader.load(cwd);
|
|
46
|
+
const snapshot = JSON.stringify(manifest);
|
|
47
|
+
for (const path of ["src/file.ts", join(cwd, "src/file.ts"), "packages/a/file.ts"]) {
|
|
48
|
+
const target = await loader.loadForTarget(cwd, path);
|
|
49
|
+
expect(target.revision).toBe(manifest.revision);
|
|
50
|
+
expect(target.sources.some(s => s.path === join(root, "AGENTS.md"))).toBe(true);
|
|
51
|
+
expect(JSON.stringify(manifest)).toBe(snapshot);
|
|
52
|
+
}
|
|
53
|
+
loader.invalidateCache();
|
|
54
|
+
expect((await loader.load(cwd)).revision).toBe(manifest.revision);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("acknowledgment unlocks the next target call using Pi sessionManager identity", async () => {
|
|
58
|
+
const r = runtime();
|
|
59
|
+
const context = (id = "session-a") => ({ cwd, sessionManager: { getSessionId: () => id } });
|
|
60
|
+
expect((await r.gate(context())).block).toBe(true);
|
|
61
|
+
const revision = (await r.call({ action: "manifest" }, context())).details.manifest.revision;
|
|
62
|
+
expect((await r.call({ action: "acknowledge", revision }, context())).isError).not.toBe(true);
|
|
63
|
+
expect((await r.gate(context())).block).not.toBe(true);
|
|
64
|
+
expect((await r.gate(context(), join(cwd, "src/file.ts"))).block).not.toBe(true);
|
|
65
|
+
expect((await r.gate(context("session-b"))).block).toBe(true);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("legacy contexts reuse a stable fallback identity", async () => {
|
|
69
|
+
const r = runtime();
|
|
70
|
+
const revision = (await r.call({ action: "manifest" }, { cwd })).details.manifest.revision;
|
|
71
|
+
await r.call({ action: "acknowledge", revision }, { cwd });
|
|
72
|
+
expect((await r.gate({ cwd })).block).not.toBe(true);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("explicitly disabled receipt enforcement allows tools without an acknowledgment", async () => {
|
|
76
|
+
const r = runtime(false);
|
|
77
|
+
expect((await r.gate({ cwd })).block).not.toBe(true);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("wrong revisions remain rejected when receipt enforcement is enabled", async () => {
|
|
81
|
+
const r = runtime();
|
|
82
|
+
const ctx = { cwd, sessionId: "session-a" };
|
|
83
|
+
const result = await r.call({ action: "acknowledge", revision: "stale" }, ctx);
|
|
84
|
+
expect(result.isError).toBe(true);
|
|
85
|
+
expect((await r.gate(ctx)).block).toBe(true);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("rule changes still require a new receipt after policy cache refresh", async () => {
|
|
89
|
+
const r = runtime();
|
|
90
|
+
const ctx = { cwd, sessionId: "session-a" };
|
|
91
|
+
const revision = (await r.call({ action: "manifest" }, ctx)).details.manifest.revision;
|
|
92
|
+
await r.call({ action: "acknowledge", revision }, ctx);
|
|
93
|
+
writeFileSync(join(cwd, "AGENTS.md"), "Updated mandatory rules");
|
|
94
|
+
// A fresh loader/registration models cache refresh without a five-second wait.
|
|
95
|
+
const refreshed = runtime();
|
|
96
|
+
expect((await refreshed.gate(ctx)).block).toBe(true);
|
|
97
|
+
const current = (await refreshed.call({ action: "manifest" }, ctx)).details.manifest.revision;
|
|
98
|
+
expect(current).not.toBe(revision);
|
|
99
|
+
await refreshed.call({ action: "acknowledge", revision: current }, ctx);
|
|
100
|
+
expect((await refreshed.gate(ctx)).block).not.toBe(true);
|
|
101
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-harness/capability-registry",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.185",
|
|
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",
|