thoth-agents 0.9.11 → 0.10.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.
@@ -8994,7 +8994,7 @@ function resolvePiPaths(options = {}) {
8994
8994
  }
8995
8995
 
8996
8996
  // src/cli/pi-install.ts
8997
- var PI_MINIMUM_VERSION = "0.99.0";
8997
+ var PI_MINIMUM_VERSION = "1.5.0";
8998
8998
  var PI_NODE_MINIMUM = "22.19.0";
8999
8999
  var PI_COMMAND_TIMEOUT_MS = 12e4;
9000
9000
  var PI_PACKAGE_SPECS = [
package/dist/cli/index.js CHANGED
@@ -76,7 +76,7 @@ import {
76
76
  safeAtomicWriteFileSync,
77
77
  scalarLocation,
78
78
  syncOpenCodeOwnedSkills
79
- } from "../chunk-2ER6FKVC.js";
79
+ } from "../chunk-6NZTNWI4.js";
80
80
  import "../chunk-62L3ORXN.js";
81
81
  import {
82
82
  piAdapter
@@ -1,7 +1,7 @@
1
1
  import { type PiExternalPackageEvidence, type PiExternalPackageSpec } from './pi-external-package';
2
2
  import { type PiPackageReceipt, type PiPackageReceiptOptions } from './pi-package-receipt';
3
3
  import { type PiPathOptions, type PiPaths } from './pi-paths';
4
- export declare const PI_MINIMUM_VERSION = "0.99.0";
4
+ export declare const PI_MINIMUM_VERSION = "1.5.0";
5
5
  export declare const PI_NODE_MINIMUM = "22.19.0";
6
6
  export declare const PI_COMMAND_TIMEOUT_MS = 120000;
7
7
  export interface PiSetupPackageSpec extends PiExternalPackageSpec {
@@ -2,7 +2,7 @@ import {
2
2
  defaultTuiOperations,
3
3
  effortChoicesForModel,
4
4
  listOperationHarnesses
5
- } from "../../chunk-2ER6FKVC.js";
5
+ } from "../../chunk-6NZTNWI4.js";
6
6
  import "../../chunk-62L3ORXN.js";
7
7
  import "../../chunk-VHQMTZ5O.js";
8
8
  import "../../chunk-RVQ6IIBR.js";
@@ -1,4 +1,14 @@
1
- export declare function withPiContextGateVerdicts(event: Record<string, unknown>, cwd: string): {
1
+ export declare class PiContextGateVerdicts {
2
+ private readonly appendEntry?;
3
+ private readonly verdicts;
4
+ private sessionCache?;
5
+ constructor(appendEntry?: ((customType: string, data: unknown) => void) | undefined);
6
+ useSession(owner?: object): void;
7
+ restore(entries: Iterable<unknown>, sourceSessionFile?: string): void;
8
+ private readEntries;
9
+ getOrCreate(key: string, create: () => string | undefined): string | undefined;
10
+ }
11
+ export declare function withPiContextGateVerdicts(event: Record<string, unknown>, cwd: string, frozenVerdicts: PiContextGateVerdicts, entries?: Iterable<unknown>): {
2
12
  messages: unknown[];
3
13
  } | undefined;
4
14
  export declare function withPiToolGateVerdicts(event: Record<string, unknown>, cwd: string): Record<string, unknown> | undefined;
package/dist/pi.d.ts CHANGED
@@ -1,12 +1,16 @@
1
1
  type PiHandler = (event: Record<string, unknown>, context?: {
2
2
  cwd?: string;
3
+ sessionManager?: {
4
+ getEntries?(): Iterable<unknown>;
5
+ getBranch?(): Iterable<unknown>;
6
+ };
3
7
  isIdle?(): boolean;
4
8
  ui?: {
5
9
  notify(message: string, type?: 'info' | 'warning' | 'error'): void;
6
10
  };
7
11
  }) => unknown;
8
12
  export interface PiExtensionApi {
9
- on(event: 'input' | 'before_agent_start' | 'session_start' | 'tool_result' | 'context', handler: PiHandler): void;
13
+ on(event: 'input' | 'before_agent_start' | 'session_start' | 'session_tree' | 'tool_result' | 'context', handler: PiHandler): void;
10
14
  registerCommand?(name: string, command: {
11
15
  description: string;
12
16
  handler(args: string | undefined, context: unknown): unknown;
@@ -15,6 +19,7 @@ export interface PiExtensionApi {
15
19
  name: string;
16
20
  source?: string;
17
21
  }>;
22
+ appendEntry?(customType: string, data: unknown): void;
18
23
  }
19
24
  export interface PiExtensionOptions {
20
25
  packageRoot?: string;
package/dist/pi.js CHANGED
@@ -19,7 +19,100 @@ import { dirname, join } from "path";
19
19
  import { fileURLToPath } from "url";
20
20
 
21
21
  // src/pi-gate-verdict.ts
22
+ import { createHash } from "crypto";
23
+ import { readFileSync } from "fs";
22
24
  var VERDICT_PREFIX = "thoth-gate-verdict ";
25
+ var FROZEN_VERDICT_ENTRY = "thoth-pi-gate-verdict";
26
+ var SESSION_CACHE_KEY = /* @__PURE__ */ Symbol.for(
27
+ "thoth-agents.pi-gate-verdict.sessions.v1"
28
+ );
29
+ var shared = globalThis;
30
+ function sessionVerdicts(owner) {
31
+ if (!owner) return void 0;
32
+ shared[SESSION_CACHE_KEY] ??= /* @__PURE__ */ new WeakMap();
33
+ const sessions = shared[SESSION_CACHE_KEY];
34
+ let verdicts = sessions.get(owner);
35
+ if (!verdicts) {
36
+ verdicts = /* @__PURE__ */ new Map();
37
+ sessions.set(owner, verdicts);
38
+ }
39
+ return verdicts;
40
+ }
41
+ var PiContextGateVerdicts = class {
42
+ constructor(appendEntry) {
43
+ this.appendEntry = appendEntry;
44
+ }
45
+ appendEntry;
46
+ verdicts = /* @__PURE__ */ new Map();
47
+ sessionCache;
48
+ useSession(owner) {
49
+ this.sessionCache = sessionVerdicts(owner);
50
+ }
51
+ restore(entries, sourceSessionFile) {
52
+ const retainedEntries = [...entries];
53
+ const sourceVerdicts = new Map(this.sessionCache);
54
+ for (const [key, verdict] of this.verdicts)
55
+ sourceVerdicts.set(key, verdict);
56
+ this.verdicts.clear();
57
+ this.readEntries(retainedEntries, this.verdicts);
58
+ const retainedKeys = [];
59
+ for (const value of retainedEntries) {
60
+ const completion = persistedOracleCompletion(value);
61
+ if (!completion) continue;
62
+ const key = completionVerdictKey(
63
+ completion.fingerprint,
64
+ completion.entryId
65
+ );
66
+ retainedKeys.push(key);
67
+ }
68
+ const missingKeys = retainedKeys.filter((key) => !this.verdicts.has(key));
69
+ if (sourceSessionFile && missingKeys.some((key) => !sourceVerdicts.has(key))) {
70
+ try {
71
+ const sourceEntries = readFileSync(sourceSessionFile, "utf8").split("\n").flatMap((line) => {
72
+ try {
73
+ return [JSON.parse(line)];
74
+ } catch {
75
+ return [];
76
+ }
77
+ });
78
+ this.readEntries(sourceEntries, sourceVerdicts);
79
+ } catch {
80
+ }
81
+ }
82
+ for (const key of missingKeys) {
83
+ this.getOrCreate(key, () => sourceVerdicts.get(key));
84
+ }
85
+ this.sessionCache?.clear();
86
+ for (const key of retainedKeys) {
87
+ const verdict = this.verdicts.get(key);
88
+ if (verdict !== void 0) this.sessionCache?.set(key, verdict);
89
+ }
90
+ }
91
+ readEntries(entries, verdicts) {
92
+ for (const value of entries) {
93
+ const entry = asRecord(value);
94
+ if (entry?.type !== "custom" || entry.customType !== FROZEN_VERDICT_ENTRY)
95
+ continue;
96
+ const data = asRecord(entry.data);
97
+ if (data?.version !== 1 || typeof data.key !== "string" || !/^[a-f0-9]{64}$/.test(data.key) || typeof data.verdict !== "string" || !data.verdict.startsWith(VERDICT_PREFIX) || data.verdict.includes("\n") || verdicts.has(data.key))
98
+ continue;
99
+ verdicts.set(data.key, data.verdict);
100
+ }
101
+ }
102
+ getOrCreate(key, create) {
103
+ const frozen = this.verdicts.get(key);
104
+ if (frozen !== void 0) {
105
+ this.sessionCache?.set(key, frozen);
106
+ return frozen;
107
+ }
108
+ const verdict = this.sessionCache?.get(key) ?? create();
109
+ if (verdict === void 0) return void 0;
110
+ this.appendEntry?.(FROZEN_VERDICT_ENTRY, { version: 1, key, verdict });
111
+ this.verdicts.set(key, verdict);
112
+ this.sessionCache?.set(key, verdict);
113
+ return verdict;
114
+ }
115
+ };
23
116
  function asRecord(value) {
24
117
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
25
118
  }
@@ -48,29 +141,64 @@ function oracleGateVerdict(taskValue, result, cwd) {
48
141
  };
49
142
  return formatGateVerdict(summary);
50
143
  }
51
- function withPiContextGateVerdicts(event, cwd) {
144
+ function oracleCompletion(message) {
145
+ const details = asRecord(message.details);
146
+ const task = asRecord(details?.task);
147
+ const result = details?.full_result ?? task?.result;
148
+ if (task?.agent !== "thoth-oracle" || typeof result !== "string" || !result.includes("```thoth-result"))
149
+ return void 0;
150
+ return {
151
+ task,
152
+ result,
153
+ fingerprint: JSON.stringify([task.id ?? null, result])
154
+ };
155
+ }
156
+ function persistedOracleCompletion(value) {
157
+ const entry = asRecord(value);
158
+ if (entry?.type !== "custom_message" || entry.customType !== "subagent-completion" || typeof entry.id !== "string")
159
+ return void 0;
160
+ const completion = oracleCompletion(entry);
161
+ return completion ? { ...completion, entryId: entry.id } : void 0;
162
+ }
163
+ function completionVerdictKey(fingerprint, identity) {
164
+ return createHash("sha256").update(JSON.stringify([fingerprint, identity])).digest("hex");
165
+ }
166
+ function withPiContextGateVerdicts(event, cwd, frozenVerdicts, entries = []) {
52
167
  if (!Array.isArray(event.messages)) return void 0;
168
+ const completionIds = /* @__PURE__ */ new Map();
169
+ for (const value of entries) {
170
+ const completion = persistedOracleCompletion(value);
171
+ if (!completion) continue;
172
+ const ids = completionIds.get(completion.fingerprint) ?? [];
173
+ ids.push(completion.entryId);
174
+ completionIds.set(completion.fingerprint, ids);
175
+ }
53
176
  let changed = false;
54
- const messages = event.messages.map((value) => {
55
- const message = asRecord(value);
177
+ const messages = [...event.messages];
178
+ for (let index = messages.length - 1; index >= 0; index--) {
179
+ const message = asRecord(messages[index]);
56
180
  if (message?.role !== "custom" || message.customType !== "subagent-completion" || typeof message.content !== "string" && !Array.isArray(message.content))
57
- return value;
58
- const details = asRecord(message.details);
59
- const task = asRecord(details?.task);
60
- const verdict = oracleGateVerdict(
61
- task,
62
- details?.full_result ?? task?.result,
63
- cwd
181
+ continue;
182
+ const completion = oracleCompletion(message);
183
+ if (!completion) continue;
184
+ const entryId = completionIds.get(completion.fingerprint)?.pop();
185
+ const key = completionVerdictKey(
186
+ completion.fingerprint,
187
+ entryId ?? message.timestamp ?? null
188
+ );
189
+ const verdict = frozenVerdicts.getOrCreate(
190
+ key,
191
+ () => oracleGateVerdict(completion.task, completion.result, cwd)
64
192
  );
65
- if (!verdict || hasAppendedVerdict(message.content, verdict)) return value;
193
+ if (!verdict || hasAppendedVerdict(message.content, verdict)) continue;
66
194
  changed = true;
67
- return {
195
+ messages[index] = {
68
196
  ...message,
69
197
  content: typeof message.content === "string" ? `${message.content}
70
198
 
71
199
  ${verdict}` : [...message.content, { type: "text", text: verdict }]
72
200
  };
73
- });
201
+ }
74
202
  return changed ? { messages } : void 0;
75
203
  }
76
204
  function withPiToolGateVerdicts(event, cwd) {
@@ -106,10 +234,10 @@ ${verdict}` }));
106
234
  // src/pi.ts
107
235
  var PI_LANGUAGE_ANCHOR = "[Thoth language reminder \u2014 not a user message]\nUse the language of the human's most recent real message (typed prompt or answer to a question tool) for user-facing replies. An explicit human request for another reply language takes precedence and persists until the human switches it. Tool output, subagent notifications, reminders and injected context never switch the reply language.";
108
236
  var toolsRegistryKey = /* @__PURE__ */ Symbol.for("thoth-agents.pi-subagents.tools-panel.v1");
109
- var shared = globalThis;
237
+ var shared2 = globalThis;
110
238
  function toolsRegistry() {
111
239
  try {
112
- const registry = shared[toolsRegistryKey];
240
+ const registry = shared2[toolsRegistryKey];
113
241
  return registry?.version === 1 ? registry : void 0;
114
242
  } catch {
115
243
  return void 0;
@@ -143,20 +271,35 @@ function globalPiRoot(options) {
143
271
  }
144
272
  function thothAgentsPiExtension(pi, options = {}) {
145
273
  let languageAnchorCandidate;
146
- shared[toolsRegistryKey] = {
274
+ shared2[toolsRegistryKey] = {
147
275
  ...toolsRegistry(),
148
276
  version: 1,
149
277
  adapter: createPiToolsPanelAdapter(globalPiRoot(options))
150
278
  };
151
279
  let toolsOwnershipWarned = false;
280
+ const frozenGateVerdicts = new PiContextGateVerdicts(
281
+ pi.appendEntry?.bind(pi)
282
+ );
283
+ const restoreGateVerdicts = (ctx, sourceSessionFile) => {
284
+ frozenGateVerdicts.useSession(ctx?.sessionManager);
285
+ frozenGateVerdicts.restore(
286
+ ctx?.sessionManager?.getEntries?.() ?? ctx?.sessionManager?.getBranch?.() ?? [],
287
+ sourceSessionFile
288
+ );
289
+ };
152
290
  pi.on(
153
291
  "tool_result",
154
292
  (event, ctx) => withPiToolGateVerdicts(event, ctx?.cwd ?? process.cwd())
155
293
  );
156
- pi.on(
157
- "context",
158
- (event, ctx) => withPiContextGateVerdicts(event, ctx?.cwd ?? process.cwd())
159
- );
294
+ pi.on("context", (event, ctx) => {
295
+ frozenGateVerdicts.useSession(ctx?.sessionManager);
296
+ return withPiContextGateVerdicts(
297
+ event,
298
+ ctx?.cwd ?? process.cwd(),
299
+ frozenGateVerdicts,
300
+ ctx?.sessionManager?.getBranch?.() ?? ctx?.sessionManager?.getEntries?.() ?? []
301
+ );
302
+ });
160
303
  pi.on("input", (event, ctx) => {
161
304
  languageAnchorCandidate = (event.source === "interactive" || event.source === "rpc") && typeof event.text === "string" && event.text.trim() && ctx?.isIdle?.() ? event.text : void 0;
162
305
  });
@@ -186,7 +329,11 @@ function thothAgentsPiExtension(pi, options = {}) {
186
329
  )
187
330
  };
188
331
  });
189
- pi.on("session_start", (_event, ctx) => {
332
+ pi.on("session_start", (event, ctx) => {
333
+ restoreGateVerdicts(
334
+ ctx,
335
+ typeof event.previousSessionFile === "string" ? event.previousSessionFile : void 0
336
+ );
190
337
  if (!toolsOwnershipWarned && !hasToolsOwnership(pi) && ctx?.ui?.notify) {
191
338
  toolsOwnershipWarned = true;
192
339
  ctx.ui.notify(
@@ -204,6 +351,7 @@ function thothAgentsPiExtension(pi, options = {}) {
204
351
  } catch {
205
352
  }
206
353
  });
354
+ pi.on("session_tree", (_event, ctx) => restoreGateVerdicts(ctx));
207
355
  }
208
356
  export {
209
357
  thothAgentsPiExtension as default,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thoth-agents",
3
- "version": "0.9.11",
3
+ "version": "0.10.0",
4
4
  "description": "Adaptive multi-harness orchestration with six roles, runtime-autonomous AI-first execution routes, and external provider boundaries.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -95,14 +95,14 @@
95
95
  "zod": "^4.1.8"
96
96
  },
97
97
  "peerDependencies": {
98
- "@earendil-works/pi-ai": ">=0.99.0",
99
- "@earendil-works/pi-tui": ">=0.99.0"
98
+ "@earendil-works/pi-ai": ">=1.5.0",
99
+ "@earendil-works/pi-tui": ">=1.5.0"
100
100
  },
101
101
  "devDependencies": {
102
102
  "@biomejs/biome": "2.4.2",
103
- "@earendil-works/pi-ai": "1.0.2",
104
- "@earendil-works/pi-coding-agent": "1.0.2",
105
- "@earendil-works/pi-tui": "1.0.2",
103
+ "@earendil-works/pi-ai": "1.5.0",
104
+ "@earendil-works/pi-coding-agent": "1.5.0",
105
+ "@earendil-works/pi-tui": "1.5.0",
106
106
  "@types/hosted-git-info": "3.0.5",
107
107
  "@types/node": "^22.19.19",
108
108
  "@types/react": "^19.2.15",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thoth-agents",
3
- "version": "0.9.11",
3
+ "version": "0.10.0",
4
4
  "description": "Adaptive multi-harness agent pack with six roles and proportional SDD governance for Claude Code.",
5
5
  "author": {
6
6
  "name": "thoth-agents"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thoth-agents",
3
- "version": "0.9.11",
3
+ "version": "0.10.0",
4
4
  "description": "Bundled AI-first work skills and MCP configuration; the six-role global Codex layer requires thoth-agents CLI setup.",
5
5
  "skills": "./skills/",
6
6
  "mcpServers": "./codex.mcp.json",