@caupulican/pi-adaptative 0.81.21 → 0.81.23

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 (63) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/core/agent-session.d.ts +3 -1
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +28 -21
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/failure-corpus.d.ts +10 -1
  7. package/dist/core/failure-corpus.d.ts.map +1 -1
  8. package/dist/core/failure-corpus.js +32 -26
  9. package/dist/core/failure-corpus.js.map +1 -1
  10. package/dist/core/model-router/intent-classifier.d.ts.map +1 -1
  11. package/dist/core/model-router/intent-classifier.js +1 -1
  12. package/dist/core/model-router/intent-classifier.js.map +1 -1
  13. package/dist/core/model-router/route-judge.d.ts.map +1 -1
  14. package/dist/core/model-router/route-judge.js +10 -6
  15. package/dist/core/model-router/route-judge.js.map +1 -1
  16. package/dist/core/model-router/tool-escalation.d.ts.map +1 -1
  17. package/dist/core/model-router/tool-escalation.js +2 -1
  18. package/dist/core/model-router/tool-escalation.js.map +1 -1
  19. package/dist/core/model-router-controller.d.ts.map +1 -1
  20. package/dist/core/model-router-controller.js +27 -2
  21. package/dist/core/model-router-controller.js.map +1 -1
  22. package/dist/core/session-analytics.d.ts +7 -1
  23. package/dist/core/session-analytics.d.ts.map +1 -1
  24. package/dist/core/session-analytics.js +42 -11
  25. package/dist/core/session-analytics.js.map +1 -1
  26. package/dist/core/settings-manager.d.ts +1 -1
  27. package/dist/core/settings-manager.d.ts.map +1 -1
  28. package/dist/core/settings-manager.js.map +1 -1
  29. package/dist/core/tool-recovery-log-records.d.ts +24 -0
  30. package/dist/core/tool-recovery-log-records.d.ts.map +1 -0
  31. package/dist/core/tool-recovery-log-records.js +63 -0
  32. package/dist/core/tool-recovery-log-records.js.map +1 -0
  33. package/dist/core/tool-recovery-log-worker.d.ts +2 -0
  34. package/dist/core/tool-recovery-log-worker.d.ts.map +1 -0
  35. package/dist/core/tool-recovery-log-worker.js +44 -0
  36. package/dist/core/tool-recovery-log-worker.js.map +1 -0
  37. package/dist/core/tool-recovery-logger.d.ts +59 -0
  38. package/dist/core/tool-recovery-logger.d.ts.map +1 -0
  39. package/dist/core/tool-recovery-logger.js +197 -0
  40. package/dist/core/tool-recovery-logger.js.map +1 -0
  41. package/dist/core/tool-repair-health.d.ts +2 -1
  42. package/dist/core/tool-repair-health.d.ts.map +1 -1
  43. package/dist/core/tool-repair-health.js +13 -4
  44. package/dist/core/tool-repair-health.js.map +1 -1
  45. package/dist/core/tool-repair-settings.d.ts +1 -0
  46. package/dist/core/tool-repair-settings.d.ts.map +1 -1
  47. package/dist/core/tool-repair-settings.js +2 -1
  48. package/dist/core/tool-repair-settings.js.map +1 -1
  49. package/dist/modes/interactive/interactive-mode.d.ts +3 -0
  50. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  51. package/dist/modes/interactive/interactive-mode.js +25 -1
  52. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  53. package/docs/settings.md +6 -4
  54. package/docs/tool-repair.md +8 -7
  55. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  56. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  57. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  58. package/examples/extensions/sandbox/package-lock.json +2 -2
  59. package/examples/extensions/sandbox/package.json +1 -1
  60. package/examples/extensions/with-deps/package-lock.json +2 -2
  61. package/examples/extensions/with-deps/package.json +1 -1
  62. package/npm-shrinkwrap.json +12 -12
  63. package/package.json +4 -4
@@ -0,0 +1,24 @@
1
+ import type { ToolArgumentValidationTelemetryEvent } from "@caupulican/pi-ai";
2
+ export declare const TOOL_RECOVERY_EVENT_LOG_FILE = "tool-recovery-events.jsonl";
3
+ export declare const TOOL_ARGUMENT_VALIDATION_LOG_KIND = "tool_argument_validation";
4
+ export interface ToolArgumentValidationLogRecord extends ToolArgumentValidationTelemetryEvent {
5
+ kind: typeof TOOL_ARGUMENT_VALIDATION_LOG_KIND;
6
+ version: 1;
7
+ recordId: string;
8
+ ts: string;
9
+ sessionId: string;
10
+ }
11
+ export interface ToolRecoveryLogWorkerRecord {
12
+ eventLogPath: string;
13
+ failureCorpusPath: string;
14
+ record: ToolArgumentValidationLogRecord;
15
+ }
16
+ export declare function createToolArgumentValidationLogRecord(args: {
17
+ event: ToolArgumentValidationTelemetryEvent;
18
+ recordId: string;
19
+ sessionId: string;
20
+ ts: string;
21
+ }): ToolArgumentValidationLogRecord;
22
+ export declare function isToolArgumentValidationLogRecord(value: unknown): value is ToolArgumentValidationLogRecord;
23
+ export declare function writeToolRecoveryLogRecord(entry: ToolRecoveryLogWorkerRecord): void;
24
+ //# sourceMappingURL=tool-recovery-log-records.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-recovery-log-records.d.ts","sourceRoot":"","sources":["../../src/core/tool-recovery-log-records.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oCAAoC,EAAE,MAAM,mBAAmB,CAAC;AAO9E,eAAO,MAAM,4BAA4B,+BAA+B,CAAC;AACzE,eAAO,MAAM,iCAAiC,6BAA6B,CAAC;AAE5E,MAAM,WAAW,+BAAgC,SAAQ,oCAAoC;IAC5F,IAAI,EAAE,OAAO,iCAAiC,CAAC;IAC/C,OAAO,EAAE,CAAC,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,2BAA2B;IAC3C,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,MAAM,EAAE,+BAA+B,CAAC;CACxC;AAaD,wBAAgB,qCAAqC,CAAC,IAAI,EAAE;IAC3D,KAAK,EAAE,oCAAoC,CAAC;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;CACX,GAAG,+BAA+B,CAmBlC;AAED,wBAAgB,iCAAiC,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,+BAA+B,CAc1G;AAED,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,2BAA2B,GAAG,IAAI,CAgBnF","sourcesContent":["import { appendFileSync, mkdirSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport type { ToolArgumentValidationTelemetryEvent } from \"@caupulican/pi-ai\";\nimport {\n\tcreateToolValidationFailureCorpusRecord,\n\ttype ToolValidationFailureShapeEntry,\n\twriteFailureCorpusRecord,\n} from \"./failure-corpus.ts\";\n\nexport const TOOL_RECOVERY_EVENT_LOG_FILE = \"tool-recovery-events.jsonl\";\nexport const TOOL_ARGUMENT_VALIDATION_LOG_KIND = \"tool_argument_validation\";\n\nexport interface ToolArgumentValidationLogRecord extends ToolArgumentValidationTelemetryEvent {\n\tkind: typeof TOOL_ARGUMENT_VALIDATION_LOG_KIND;\n\tversion: 1;\n\trecordId: string;\n\tts: string;\n\tsessionId: string;\n}\n\nexport interface ToolRecoveryLogWorkerRecord {\n\teventLogPath: string;\n\tfailureCorpusPath: string;\n\trecord: ToolArgumentValidationLogRecord;\n}\n\nfunction copyFailureShape(\n\tshape: readonly ToolValidationFailureShapeEntry[] | undefined,\n): ToolValidationFailureShapeEntry[] {\n\treturn (shape ?? []).map((entry) => ({\n\t\tpath: entry.path,\n\t\texpectedType: entry.expectedType,\n\t\treceivedType: entry.receivedType,\n\t\t...(entry.keyword ? { keyword: entry.keyword } : {}),\n\t}));\n}\n\nexport function createToolArgumentValidationLogRecord(args: {\n\tevent: ToolArgumentValidationTelemetryEvent;\n\trecordId: string;\n\tsessionId: string;\n\tts: string;\n}): ToolArgumentValidationLogRecord {\n\treturn {\n\t\tkind: TOOL_ARGUMENT_VALIDATION_LOG_KIND,\n\t\tversion: 1,\n\t\trecordId: args.recordId,\n\t\tts: args.ts,\n\t\tsessionId: args.sessionId,\n\t\toutcome: args.event.outcome,\n\t\tprovider: args.event.provider,\n\t\tmodel: args.event.model,\n\t\ttool: args.event.tool,\n\t\tsource: args.event.source,\n\t\tfailureModes: [...args.event.failureModes],\n\t\trepairsApplied: [...args.event.repairsApplied],\n\t\tfailureShape: copyFailureShape(args.event.failureShape),\n\t\terrorKeywords: args.event.errorKeywords ? [...args.event.errorKeywords] : undefined,\n\t\ttaught: args.event.taught,\n\t\texecutionOutcome: args.event.executionOutcome,\n\t};\n}\n\nexport function isToolArgumentValidationLogRecord(value: unknown): value is ToolArgumentValidationLogRecord {\n\tif (!value || typeof value !== \"object\") return false;\n\tconst record = value as Partial<ToolArgumentValidationLogRecord>;\n\treturn (\n\t\trecord.kind === TOOL_ARGUMENT_VALIDATION_LOG_KIND &&\n\t\trecord.version === 1 &&\n\t\ttypeof record.recordId === \"string\" &&\n\t\ttypeof record.ts === \"string\" &&\n\t\ttypeof record.sessionId === \"string\" &&\n\t\t(record.outcome === \"repaired\" || record.outcome === \"bounced\") &&\n\t\ttypeof record.tool === \"string\" &&\n\t\tArray.isArray(record.failureModes) &&\n\t\tArray.isArray(record.repairsApplied)\n\t);\n}\n\nexport function writeToolRecoveryLogRecord(entry: ToolRecoveryLogWorkerRecord): void {\n\tmkdirSync(dirname(entry.eventLogPath), { recursive: true });\n\tappendFileSync(entry.eventLogPath, `${JSON.stringify(entry.record)}\\n`, \"utf-8\");\n\tif (entry.record.outcome !== \"bounced\") return;\n\twriteFailureCorpusRecord(\n\t\tentry.failureCorpusPath,\n\t\tcreateToolValidationFailureCorpusRecord({\n\t\t\tts: entry.record.ts,\n\t\t\tprovider: entry.record.provider,\n\t\t\tmodelId: entry.record.model,\n\t\t\ttool: entry.record.tool,\n\t\t\tfailureModes: entry.record.failureModes,\n\t\t\tshape: copyFailureShape(entry.record.failureShape),\n\t\t\terrorKeywords: entry.record.errorKeywords,\n\t\t}),\n\t);\n}\n"]}
@@ -0,0 +1,63 @@
1
+ import { appendFileSync, mkdirSync } from "node:fs";
2
+ import { dirname } from "node:path";
3
+ import { createToolValidationFailureCorpusRecord, writeFailureCorpusRecord, } from "./failure-corpus.js";
4
+ export const TOOL_RECOVERY_EVENT_LOG_FILE = "tool-recovery-events.jsonl";
5
+ export const TOOL_ARGUMENT_VALIDATION_LOG_KIND = "tool_argument_validation";
6
+ function copyFailureShape(shape) {
7
+ return (shape ?? []).map((entry) => ({
8
+ path: entry.path,
9
+ expectedType: entry.expectedType,
10
+ receivedType: entry.receivedType,
11
+ ...(entry.keyword ? { keyword: entry.keyword } : {}),
12
+ }));
13
+ }
14
+ export function createToolArgumentValidationLogRecord(args) {
15
+ return {
16
+ kind: TOOL_ARGUMENT_VALIDATION_LOG_KIND,
17
+ version: 1,
18
+ recordId: args.recordId,
19
+ ts: args.ts,
20
+ sessionId: args.sessionId,
21
+ outcome: args.event.outcome,
22
+ provider: args.event.provider,
23
+ model: args.event.model,
24
+ tool: args.event.tool,
25
+ source: args.event.source,
26
+ failureModes: [...args.event.failureModes],
27
+ repairsApplied: [...args.event.repairsApplied],
28
+ failureShape: copyFailureShape(args.event.failureShape),
29
+ errorKeywords: args.event.errorKeywords ? [...args.event.errorKeywords] : undefined,
30
+ taught: args.event.taught,
31
+ executionOutcome: args.event.executionOutcome,
32
+ };
33
+ }
34
+ export function isToolArgumentValidationLogRecord(value) {
35
+ if (!value || typeof value !== "object")
36
+ return false;
37
+ const record = value;
38
+ return (record.kind === TOOL_ARGUMENT_VALIDATION_LOG_KIND &&
39
+ record.version === 1 &&
40
+ typeof record.recordId === "string" &&
41
+ typeof record.ts === "string" &&
42
+ typeof record.sessionId === "string" &&
43
+ (record.outcome === "repaired" || record.outcome === "bounced") &&
44
+ typeof record.tool === "string" &&
45
+ Array.isArray(record.failureModes) &&
46
+ Array.isArray(record.repairsApplied));
47
+ }
48
+ export function writeToolRecoveryLogRecord(entry) {
49
+ mkdirSync(dirname(entry.eventLogPath), { recursive: true });
50
+ appendFileSync(entry.eventLogPath, `${JSON.stringify(entry.record)}\n`, "utf-8");
51
+ if (entry.record.outcome !== "bounced")
52
+ return;
53
+ writeFailureCorpusRecord(entry.failureCorpusPath, createToolValidationFailureCorpusRecord({
54
+ ts: entry.record.ts,
55
+ provider: entry.record.provider,
56
+ modelId: entry.record.model,
57
+ tool: entry.record.tool,
58
+ failureModes: entry.record.failureModes,
59
+ shape: copyFailureShape(entry.record.failureShape),
60
+ errorKeywords: entry.record.errorKeywords,
61
+ }));
62
+ }
63
+ //# sourceMappingURL=tool-recovery-log-records.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-recovery-log-records.js","sourceRoot":"","sources":["../../src/core/tool-recovery-log-records.ts"],"names":[],"mappings":"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","sourcesContent":["import { appendFileSync, mkdirSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport type { ToolArgumentValidationTelemetryEvent } from \"@caupulican/pi-ai\";\nimport {\n\tcreateToolValidationFailureCorpusRecord,\n\ttype ToolValidationFailureShapeEntry,\n\twriteFailureCorpusRecord,\n} from \"./failure-corpus.ts\";\n\nexport const TOOL_RECOVERY_EVENT_LOG_FILE = \"tool-recovery-events.jsonl\";\nexport const TOOL_ARGUMENT_VALIDATION_LOG_KIND = \"tool_argument_validation\";\n\nexport interface ToolArgumentValidationLogRecord extends ToolArgumentValidationTelemetryEvent {\n\tkind: typeof TOOL_ARGUMENT_VALIDATION_LOG_KIND;\n\tversion: 1;\n\trecordId: string;\n\tts: string;\n\tsessionId: string;\n}\n\nexport interface ToolRecoveryLogWorkerRecord {\n\teventLogPath: string;\n\tfailureCorpusPath: string;\n\trecord: ToolArgumentValidationLogRecord;\n}\n\nfunction copyFailureShape(\n\tshape: readonly ToolValidationFailureShapeEntry[] | undefined,\n): ToolValidationFailureShapeEntry[] {\n\treturn (shape ?? []).map((entry) => ({\n\t\tpath: entry.path,\n\t\texpectedType: entry.expectedType,\n\t\treceivedType: entry.receivedType,\n\t\t...(entry.keyword ? { keyword: entry.keyword } : {}),\n\t}));\n}\n\nexport function createToolArgumentValidationLogRecord(args: {\n\tevent: ToolArgumentValidationTelemetryEvent;\n\trecordId: string;\n\tsessionId: string;\n\tts: string;\n}): ToolArgumentValidationLogRecord {\n\treturn {\n\t\tkind: TOOL_ARGUMENT_VALIDATION_LOG_KIND,\n\t\tversion: 1,\n\t\trecordId: args.recordId,\n\t\tts: args.ts,\n\t\tsessionId: args.sessionId,\n\t\toutcome: args.event.outcome,\n\t\tprovider: args.event.provider,\n\t\tmodel: args.event.model,\n\t\ttool: args.event.tool,\n\t\tsource: args.event.source,\n\t\tfailureModes: [...args.event.failureModes],\n\t\trepairsApplied: [...args.event.repairsApplied],\n\t\tfailureShape: copyFailureShape(args.event.failureShape),\n\t\terrorKeywords: args.event.errorKeywords ? [...args.event.errorKeywords] : undefined,\n\t\ttaught: args.event.taught,\n\t\texecutionOutcome: args.event.executionOutcome,\n\t};\n}\n\nexport function isToolArgumentValidationLogRecord(value: unknown): value is ToolArgumentValidationLogRecord {\n\tif (!value || typeof value !== \"object\") return false;\n\tconst record = value as Partial<ToolArgumentValidationLogRecord>;\n\treturn (\n\t\trecord.kind === TOOL_ARGUMENT_VALIDATION_LOG_KIND &&\n\t\trecord.version === 1 &&\n\t\ttypeof record.recordId === \"string\" &&\n\t\ttypeof record.ts === \"string\" &&\n\t\ttypeof record.sessionId === \"string\" &&\n\t\t(record.outcome === \"repaired\" || record.outcome === \"bounced\") &&\n\t\ttypeof record.tool === \"string\" &&\n\t\tArray.isArray(record.failureModes) &&\n\t\tArray.isArray(record.repairsApplied)\n\t);\n}\n\nexport function writeToolRecoveryLogRecord(entry: ToolRecoveryLogWorkerRecord): void {\n\tmkdirSync(dirname(entry.eventLogPath), { recursive: true });\n\tappendFileSync(entry.eventLogPath, `${JSON.stringify(entry.record)}\\n`, \"utf-8\");\n\tif (entry.record.outcome !== \"bounced\") return;\n\twriteFailureCorpusRecord(\n\t\tentry.failureCorpusPath,\n\t\tcreateToolValidationFailureCorpusRecord({\n\t\t\tts: entry.record.ts,\n\t\t\tprovider: entry.record.provider,\n\t\t\tmodelId: entry.record.model,\n\t\t\ttool: entry.record.tool,\n\t\t\tfailureModes: entry.record.failureModes,\n\t\t\tshape: copyFailureShape(entry.record.failureShape),\n\t\t\terrorKeywords: entry.record.errorKeywords,\n\t\t}),\n\t);\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=tool-recovery-log-worker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-recovery-log-worker.d.ts","sourceRoot":"","sources":["../../src/core/tool-recovery-log-worker.ts"],"names":[],"mappings":"","sourcesContent":["import { parentPort } from \"node:worker_threads\";\nimport { type ToolRecoveryLogWorkerRecord, writeToolRecoveryLogRecord } from \"./tool-recovery-log-records.ts\";\n\ninterface ToolRecoveryLogBatchMessage {\n\ttype: \"records\";\n\tbatchId: number;\n\trecords: ToolRecoveryLogWorkerRecord[];\n}\n\ninterface ToolRecoveryLogShutdownMessage {\n\ttype: \"shutdown\";\n}\n\ntype ToolRecoveryLogWorkerMessage = ToolRecoveryLogBatchMessage | ToolRecoveryLogShutdownMessage;\n\ninterface ToolRecoveryLogAckMessage {\n\ttype: \"ack\";\n\tbatchId: number;\n\twritten: number;\n\tfailed: number;\n}\n\nfunction isWorkerRecord(value: unknown): value is ToolRecoveryLogWorkerRecord {\n\tif (!value || typeof value !== \"object\") return false;\n\tconst record = value as Partial<ToolRecoveryLogWorkerRecord>;\n\treturn typeof record.eventLogPath === \"string\" && typeof record.failureCorpusPath === \"string\" && !!record.record;\n}\n\nfunction isWorkerMessage(value: unknown): value is ToolRecoveryLogWorkerMessage {\n\tif (!value || typeof value !== \"object\") return false;\n\tconst message = value as Partial<ToolRecoveryLogWorkerMessage>;\n\tif (message.type === \"shutdown\") return true;\n\tif (message.type !== \"records\") return false;\n\treturn (\n\t\ttypeof message.batchId === \"number\" && Array.isArray(message.records) && message.records.every(isWorkerRecord)\n\t);\n}\n\nconst port = parentPort;\nif (!port) {\n\tthrow new Error(\"tool recovery log worker requires parentPort\");\n}\n\nport.on(\"message\", (message: unknown) => {\n\tif (!isWorkerMessage(message)) return;\n\tif (message.type === \"shutdown\") {\n\t\tport.close();\n\t\treturn;\n\t}\n\n\tlet written = 0;\n\tlet failed = 0;\n\tfor (const record of message.records) {\n\t\ttry {\n\t\t\twriteToolRecoveryLogRecord(record);\n\t\t\twritten++;\n\t\t} catch {\n\t\t\tfailed++;\n\t\t}\n\t}\n\tconst response: ToolRecoveryLogAckMessage = { type: \"ack\", batchId: message.batchId, written, failed };\n\tport.postMessage(response);\n});\n"]}
@@ -0,0 +1,44 @@
1
+ import { parentPort } from "node:worker_threads";
2
+ import { writeToolRecoveryLogRecord } from "./tool-recovery-log-records.js";
3
+ function isWorkerRecord(value) {
4
+ if (!value || typeof value !== "object")
5
+ return false;
6
+ const record = value;
7
+ return typeof record.eventLogPath === "string" && typeof record.failureCorpusPath === "string" && !!record.record;
8
+ }
9
+ function isWorkerMessage(value) {
10
+ if (!value || typeof value !== "object")
11
+ return false;
12
+ const message = value;
13
+ if (message.type === "shutdown")
14
+ return true;
15
+ if (message.type !== "records")
16
+ return false;
17
+ return (typeof message.batchId === "number" && Array.isArray(message.records) && message.records.every(isWorkerRecord));
18
+ }
19
+ const port = parentPort;
20
+ if (!port) {
21
+ throw new Error("tool recovery log worker requires parentPort");
22
+ }
23
+ port.on("message", (message) => {
24
+ if (!isWorkerMessage(message))
25
+ return;
26
+ if (message.type === "shutdown") {
27
+ port.close();
28
+ return;
29
+ }
30
+ let written = 0;
31
+ let failed = 0;
32
+ for (const record of message.records) {
33
+ try {
34
+ writeToolRecoveryLogRecord(record);
35
+ written++;
36
+ }
37
+ catch {
38
+ failed++;
39
+ }
40
+ }
41
+ const response = { type: "ack", batchId: message.batchId, written, failed };
42
+ port.postMessage(response);
43
+ });
44
+ //# sourceMappingURL=tool-recovery-log-worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-recovery-log-worker.js","sourceRoot":"","sources":["../../src/core/tool-recovery-log-worker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAoC,0BAA0B,EAAE,MAAM,gCAAgC,CAAC;AAqB9G,SAAS,cAAc,CAAC,KAAc,EAAwC;IAC7E,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACtD,MAAM,MAAM,GAAG,KAA6C,CAAC;IAC7D,OAAO,OAAO,MAAM,CAAC,YAAY,KAAK,QAAQ,IAAI,OAAO,MAAM,CAAC,iBAAiB,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC;AAAA,CAClH;AAED,SAAS,eAAe,CAAC,KAAc,EAAyC;IAC/E,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACtD,MAAM,OAAO,GAAG,KAA8C,CAAC;IAC/D,IAAI,OAAO,CAAC,IAAI,KAAK,UAAU;QAAE,OAAO,IAAI,CAAC;IAC7C,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAC7C,OAAO,CACN,OAAO,OAAO,CAAC,OAAO,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,CAC9G,CAAC;AAAA,CACF;AAED,MAAM,IAAI,GAAG,UAAU,CAAC;AACxB,IAAI,CAAC,IAAI,EAAE,CAAC;IACX,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;AACjE,CAAC;AAED,IAAI,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,OAAgB,EAAE,EAAE,CAAC;IACxC,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC;QAAE,OAAO;IACtC,IAAI,OAAO,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QACjC,IAAI,CAAC,KAAK,EAAE,CAAC;QACb,OAAO;IACR,CAAC;IAED,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACtC,IAAI,CAAC;YACJ,0BAA0B,CAAC,MAAM,CAAC,CAAC;YACnC,OAAO,EAAE,CAAC;QACX,CAAC;QAAC,MAAM,CAAC;YACR,MAAM,EAAE,CAAC;QACV,CAAC;IACF,CAAC;IACD,MAAM,QAAQ,GAA8B,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IACvG,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;AAAA,CAC3B,CAAC,CAAC","sourcesContent":["import { parentPort } from \"node:worker_threads\";\nimport { type ToolRecoveryLogWorkerRecord, writeToolRecoveryLogRecord } from \"./tool-recovery-log-records.ts\";\n\ninterface ToolRecoveryLogBatchMessage {\n\ttype: \"records\";\n\tbatchId: number;\n\trecords: ToolRecoveryLogWorkerRecord[];\n}\n\ninterface ToolRecoveryLogShutdownMessage {\n\ttype: \"shutdown\";\n}\n\ntype ToolRecoveryLogWorkerMessage = ToolRecoveryLogBatchMessage | ToolRecoveryLogShutdownMessage;\n\ninterface ToolRecoveryLogAckMessage {\n\ttype: \"ack\";\n\tbatchId: number;\n\twritten: number;\n\tfailed: number;\n}\n\nfunction isWorkerRecord(value: unknown): value is ToolRecoveryLogWorkerRecord {\n\tif (!value || typeof value !== \"object\") return false;\n\tconst record = value as Partial<ToolRecoveryLogWorkerRecord>;\n\treturn typeof record.eventLogPath === \"string\" && typeof record.failureCorpusPath === \"string\" && !!record.record;\n}\n\nfunction isWorkerMessage(value: unknown): value is ToolRecoveryLogWorkerMessage {\n\tif (!value || typeof value !== \"object\") return false;\n\tconst message = value as Partial<ToolRecoveryLogWorkerMessage>;\n\tif (message.type === \"shutdown\") return true;\n\tif (message.type !== \"records\") return false;\n\treturn (\n\t\ttypeof message.batchId === \"number\" && Array.isArray(message.records) && message.records.every(isWorkerRecord)\n\t);\n}\n\nconst port = parentPort;\nif (!port) {\n\tthrow new Error(\"tool recovery log worker requires parentPort\");\n}\n\nport.on(\"message\", (message: unknown) => {\n\tif (!isWorkerMessage(message)) return;\n\tif (message.type === \"shutdown\") {\n\t\tport.close();\n\t\treturn;\n\t}\n\n\tlet written = 0;\n\tlet failed = 0;\n\tfor (const record of message.records) {\n\t\ttry {\n\t\t\twriteToolRecoveryLogRecord(record);\n\t\t\twritten++;\n\t\t} catch {\n\t\t\tfailed++;\n\t\t}\n\t}\n\tconst response: ToolRecoveryLogAckMessage = { type: \"ack\", batchId: message.batchId, written, failed };\n\tport.postMessage(response);\n});\n"]}
@@ -0,0 +1,59 @@
1
+ import type { ToolArgumentValidationTelemetryEvent } from "@caupulican/pi-ai";
2
+ import { type ToolArgumentValidationLogRecord } from "./tool-recovery-log-records.ts";
3
+ export interface ToolRecoveryLoggerStats {
4
+ enabled: boolean;
5
+ queued: number;
6
+ inFlight: number;
7
+ dropped: number;
8
+ failures: number;
9
+ workerStarts: number;
10
+ workerCrashes: number;
11
+ respawns: number;
12
+ }
13
+ export interface ToolRecoveryLoggerOptions {
14
+ enabled: boolean;
15
+ sessionId: string;
16
+ eventLogPath: string;
17
+ failureCorpusPath: string;
18
+ maxQueue?: number;
19
+ batchSize?: number;
20
+ now?: () => Date;
21
+ debug?: (message: string) => void;
22
+ workerSpecifier?: string | URL;
23
+ }
24
+ export declare class ToolRecoveryLogger {
25
+ private readonly enabled;
26
+ private readonly sessionId;
27
+ private readonly eventLogPath;
28
+ private readonly failureCorpusPath;
29
+ private readonly maxQueue;
30
+ private readonly batchSize;
31
+ private readonly now;
32
+ private readonly debug;
33
+ private readonly workerSpecifier;
34
+ private readonly queue;
35
+ private readonly flushWaiters;
36
+ private worker;
37
+ private inFlight;
38
+ private batchId;
39
+ private recordSequence;
40
+ private dropped;
41
+ private failures;
42
+ private workerStarts;
43
+ private workerCrashes;
44
+ private respawns;
45
+ private respawnAttempted;
46
+ private shuttingDown;
47
+ constructor(options: ToolRecoveryLoggerOptions);
48
+ recordToolArgumentValidation(event: ToolArgumentValidationTelemetryEvent): ToolArgumentValidationLogRecord | undefined;
49
+ getStats(): ToolRecoveryLoggerStats;
50
+ flush(timeoutMs?: number): Promise<void>;
51
+ shutdown(timeoutMs?: number): Promise<void>;
52
+ private enqueue;
53
+ private pump;
54
+ private ensureWorker;
55
+ private handleWorkerMessage;
56
+ private handleWorkerFailure;
57
+ private resolveFlushWaitersIfIdle;
58
+ }
59
+ //# sourceMappingURL=tool-recovery-logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-recovery-logger.d.ts","sourceRoot":"","sources":["../../src/core/tool-recovery-logger.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oCAAoC,EAAE,MAAM,mBAAmB,CAAC;AAC9E,OAAO,EAEN,KAAK,+BAA+B,EAEpC,MAAM,gCAAgC,CAAC;AASxC,MAAM,WAAW,uBAAuB;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,yBAAyB;IACzC,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,eAAe,CAAC,EAAE,MAAM,GAAG,GAAG,CAAC;CAC/B;AAyBD,qBAAa,kBAAkB;IAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAa;IACjC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA4B;IAClD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAe;IAC/C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqC;IAC3D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAyB;IACtD,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,QAAQ,CAAqC;IACrD,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,YAAY,CAAS;IAE7B,YAAY,OAAO,EAAE,yBAAyB,EAU7C;IAED,4BAA4B,CAC3B,KAAK,EAAE,oCAAoC,GACzC,+BAA+B,GAAG,SAAS,CAU7C;IAED,QAAQ,IAAI,uBAAuB,CAWlC;IAED,KAAK,CAAC,SAAS,GAAE,MAAiC,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBjE;IAEK,QAAQ,CAAC,SAAS,GAAE,MAAiC,GAAG,OAAO,CAAC,IAAI,CAAC,CAU1E;IAED,OAAO,CAAC,OAAO;IAUf,OAAO,CAAC,IAAI;IAkBZ,OAAO,CAAC,YAAY;IAuBpB,OAAO,CAAC,mBAAmB;IAU3B,OAAO,CAAC,mBAAmB;IAe3B,OAAO,CAAC,yBAAyB;CAKjC","sourcesContent":["import { Worker } from \"node:worker_threads\";\nimport type { ToolArgumentValidationTelemetryEvent } from \"@caupulican/pi-ai\";\nimport {\n\tcreateToolArgumentValidationLogRecord,\n\ttype ToolArgumentValidationLogRecord,\n\ttype ToolRecoveryLogWorkerRecord,\n} from \"./tool-recovery-log-records.ts\";\n\ninterface ToolRecoveryLogAckMessage {\n\ttype: \"ack\";\n\tbatchId: number;\n\twritten: number;\n\tfailed: number;\n}\n\nexport interface ToolRecoveryLoggerStats {\n\tenabled: boolean;\n\tqueued: number;\n\tinFlight: number;\n\tdropped: number;\n\tfailures: number;\n\tworkerStarts: number;\n\tworkerCrashes: number;\n\trespawns: number;\n}\n\nexport interface ToolRecoveryLoggerOptions {\n\tenabled: boolean;\n\tsessionId: string;\n\teventLogPath: string;\n\tfailureCorpusPath: string;\n\tmaxQueue?: number;\n\tbatchSize?: number;\n\tnow?: () => Date;\n\tdebug?: (message: string) => void;\n\tworkerSpecifier?: string | URL;\n}\n\nconst DEFAULT_MAX_QUEUE = 1000;\nconst DEFAULT_BATCH_SIZE = 50;\nconst DEFAULT_FLUSH_TIMEOUT_MS = 200;\n\nfunction isToolRecoveryLogAckMessage(value: unknown): value is ToolRecoveryLogAckMessage {\n\tif (!value || typeof value !== \"object\") return false;\n\tconst message = value as Partial<ToolRecoveryLogAckMessage>;\n\treturn (\n\t\tmessage.type === \"ack\" &&\n\t\ttypeof message.batchId === \"number\" &&\n\t\ttypeof message.written === \"number\" &&\n\t\ttypeof message.failed === \"number\"\n\t);\n}\n\nfunction createDefaultWorkerSpecifier(): URL {\n\tconst isTypeScriptRuntime = import.meta.url.endsWith(\".ts\");\n\treturn new URL(\n\t\tisTypeScriptRuntime ? \"./tool-recovery-log-worker.ts\" : \"./tool-recovery-log-worker.js\",\n\t\timport.meta.url,\n\t);\n}\n\nexport class ToolRecoveryLogger {\n\tprivate readonly enabled: boolean;\n\tprivate readonly sessionId: string;\n\tprivate readonly eventLogPath: string;\n\tprivate readonly failureCorpusPath: string;\n\tprivate readonly maxQueue: number;\n\tprivate readonly batchSize: number;\n\tprivate readonly now: () => Date;\n\tprivate readonly debug: (message: string) => void;\n\tprivate readonly workerSpecifier: string | URL;\n\tprivate readonly queue: ToolRecoveryLogWorkerRecord[] = [];\n\tprivate readonly flushWaiters: Array<() => void> = [];\n\tprivate worker: Worker | undefined;\n\tprivate inFlight: ToolRecoveryLogWorkerRecord[] = [];\n\tprivate batchId = 0;\n\tprivate recordSequence = 0;\n\tprivate dropped = 0;\n\tprivate failures = 0;\n\tprivate workerStarts = 0;\n\tprivate workerCrashes = 0;\n\tprivate respawns = 0;\n\tprivate respawnAttempted = false;\n\tprivate shuttingDown = false;\n\n\tconstructor(options: ToolRecoveryLoggerOptions) {\n\t\tthis.enabled = options.enabled;\n\t\tthis.sessionId = options.sessionId;\n\t\tthis.eventLogPath = options.eventLogPath;\n\t\tthis.failureCorpusPath = options.failureCorpusPath;\n\t\tthis.maxQueue = options.maxQueue ?? DEFAULT_MAX_QUEUE;\n\t\tthis.batchSize = options.batchSize ?? DEFAULT_BATCH_SIZE;\n\t\tthis.now = options.now ?? (() => new Date());\n\t\tthis.debug = options.debug ?? (() => {});\n\t\tthis.workerSpecifier = options.workerSpecifier ?? createDefaultWorkerSpecifier();\n\t}\n\n\trecordToolArgumentValidation(\n\t\tevent: ToolArgumentValidationTelemetryEvent,\n\t): ToolArgumentValidationLogRecord | undefined {\n\t\tif (!this.enabled || event.outcome === \"clean\") return undefined;\n\t\tconst record = createToolArgumentValidationLogRecord({\n\t\t\tevent,\n\t\t\trecordId: `${this.sessionId}:${this.recordSequence++}`,\n\t\t\tsessionId: this.sessionId,\n\t\t\tts: this.now().toISOString(),\n\t\t});\n\t\tthis.enqueue({ eventLogPath: this.eventLogPath, failureCorpusPath: this.failureCorpusPath, record });\n\t\treturn record;\n\t}\n\n\tgetStats(): ToolRecoveryLoggerStats {\n\t\treturn {\n\t\t\tenabled: this.enabled,\n\t\t\tqueued: this.queue.length,\n\t\t\tinFlight: this.inFlight.length,\n\t\t\tdropped: this.dropped,\n\t\t\tfailures: this.failures,\n\t\t\tworkerStarts: this.workerStarts,\n\t\t\tworkerCrashes: this.workerCrashes,\n\t\t\trespawns: this.respawns,\n\t\t};\n\t}\n\n\tflush(timeoutMs: number = DEFAULT_FLUSH_TIMEOUT_MS): Promise<void> {\n\t\tif (!this.enabled || (!this.worker && this.queue.length === 0 && this.inFlight.length === 0)) {\n\t\t\treturn Promise.resolve();\n\t\t}\n\t\tthis.pump();\n\t\treturn new Promise((resolve) => {\n\t\t\tlet settled = false;\n\t\t\tconst finish = (): void => {\n\t\t\t\tif (settled) return;\n\t\t\t\tsettled = true;\n\t\t\t\tresolve();\n\t\t\t};\n\t\t\tconst timeout = setTimeout(finish, timeoutMs);\n\t\t\tconst waiter = (): void => {\n\t\t\t\tclearTimeout(timeout);\n\t\t\t\tfinish();\n\t\t\t};\n\t\t\tthis.flushWaiters.push(waiter);\n\t\t\tthis.resolveFlushWaitersIfIdle();\n\t\t});\n\t}\n\n\tasync shutdown(timeoutMs: number = DEFAULT_FLUSH_TIMEOUT_MS): Promise<void> {\n\t\tawait this.flush(timeoutMs);\n\t\tthis.shuttingDown = true;\n\t\tconst worker = this.worker;\n\t\tthis.worker = undefined;\n\t\tif (!worker) return;\n\t\ttry {\n\t\t\tworker.postMessage({ type: \"shutdown\" });\n\t\t} catch {}\n\t\tvoid worker.terminate().catch(() => undefined);\n\t}\n\n\tprivate enqueue(record: ToolRecoveryLogWorkerRecord): void {\n\t\tif (!this.enabled) return;\n\t\tthis.queue.push(record);\n\t\twhile (this.queue.length > this.maxQueue) {\n\t\t\tthis.queue.shift();\n\t\t\tthis.dropped++;\n\t\t}\n\t\tthis.pump();\n\t}\n\n\tprivate pump(): void {\n\t\tif (!this.enabled || this.shuttingDown || this.inFlight.length > 0 || this.queue.length === 0) return;\n\t\tconst worker = this.ensureWorker();\n\t\tif (!worker) return;\n\t\tconst records = this.queue.splice(0, this.batchSize);\n\t\tthis.inFlight = records;\n\t\tconst batchId = this.batchId++;\n\t\ttry {\n\t\t\tworker.postMessage({ type: \"records\", batchId, records });\n\t\t} catch (error) {\n\t\t\tthis.failures++;\n\t\t\tthis.dropped += records.length;\n\t\t\tthis.inFlight = [];\n\t\t\tthis.debug(`tool recovery logger post failed: ${error instanceof Error ? error.message : String(error)}`);\n\t\t\tthis.resolveFlushWaitersIfIdle();\n\t\t}\n\t}\n\n\tprivate ensureWorker(): Worker | undefined {\n\t\tif (this.worker) return this.worker;\n\t\ttry {\n\t\t\tconst worker = new Worker(this.workerSpecifier);\n\t\t\tworker.unref();\n\t\t\tthis.worker = worker;\n\t\t\tthis.workerStarts++;\n\t\t\tworker.on(\"message\", (message: unknown) => this.handleWorkerMessage(message));\n\t\t\tworker.on(\"error\", (error) => this.handleWorkerFailure(error));\n\t\t\tworker.on(\"exit\", (code) => {\n\t\t\t\tif (this.shuttingDown || code === 0) return;\n\t\t\t\tthis.handleWorkerFailure(new Error(`tool recovery logger worker exited with code ${code}`));\n\t\t\t});\n\t\t\treturn worker;\n\t\t} catch (error) {\n\t\t\tthis.failures++;\n\t\t\tthis.debug(\n\t\t\t\t`tool recovery logger worker start failed: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tprivate handleWorkerMessage(message: unknown): void {\n\t\tif (!isToolRecoveryLogAckMessage(message)) return;\n\t\tif (message.failed > 0) {\n\t\t\tthis.failures += message.failed;\n\t\t}\n\t\tthis.inFlight = [];\n\t\tthis.pump();\n\t\tthis.resolveFlushWaitersIfIdle();\n\t}\n\n\tprivate handleWorkerFailure(error: Error): void {\n\t\tthis.failures++;\n\t\tthis.workerCrashes++;\n\t\tthis.dropped += this.inFlight.length;\n\t\tthis.inFlight = [];\n\t\tthis.worker = undefined;\n\t\tthis.debug(`tool recovery logger worker failed: ${error.message}`);\n\t\tif (!this.respawnAttempted && !this.shuttingDown) {\n\t\t\tthis.respawnAttempted = true;\n\t\t\tthis.respawns++;\n\t\t\tthis.pump();\n\t\t}\n\t\tthis.resolveFlushWaitersIfIdle();\n\t}\n\n\tprivate resolveFlushWaitersIfIdle(): void {\n\t\tif (this.queue.length > 0 || this.inFlight.length > 0) return;\n\t\tconst waiters = this.flushWaiters.splice(0);\n\t\tfor (const waiter of waiters) waiter();\n\t}\n}\n"]}
@@ -0,0 +1,197 @@
1
+ import { Worker } from "node:worker_threads";
2
+ import { createToolArgumentValidationLogRecord, } from "./tool-recovery-log-records.js";
3
+ const DEFAULT_MAX_QUEUE = 1000;
4
+ const DEFAULT_BATCH_SIZE = 50;
5
+ const DEFAULT_FLUSH_TIMEOUT_MS = 200;
6
+ function isToolRecoveryLogAckMessage(value) {
7
+ if (!value || typeof value !== "object")
8
+ return false;
9
+ const message = value;
10
+ return (message.type === "ack" &&
11
+ typeof message.batchId === "number" &&
12
+ typeof message.written === "number" &&
13
+ typeof message.failed === "number");
14
+ }
15
+ function createDefaultWorkerSpecifier() {
16
+ const isTypeScriptRuntime = import.meta.url.endsWith(".ts");
17
+ return new URL(isTypeScriptRuntime ? "./tool-recovery-log-worker.ts" : "./tool-recovery-log-worker.js", import.meta.url);
18
+ }
19
+ export class ToolRecoveryLogger {
20
+ enabled;
21
+ sessionId;
22
+ eventLogPath;
23
+ failureCorpusPath;
24
+ maxQueue;
25
+ batchSize;
26
+ now;
27
+ debug;
28
+ workerSpecifier;
29
+ queue = [];
30
+ flushWaiters = [];
31
+ worker;
32
+ inFlight = [];
33
+ batchId = 0;
34
+ recordSequence = 0;
35
+ dropped = 0;
36
+ failures = 0;
37
+ workerStarts = 0;
38
+ workerCrashes = 0;
39
+ respawns = 0;
40
+ respawnAttempted = false;
41
+ shuttingDown = false;
42
+ constructor(options) {
43
+ this.enabled = options.enabled;
44
+ this.sessionId = options.sessionId;
45
+ this.eventLogPath = options.eventLogPath;
46
+ this.failureCorpusPath = options.failureCorpusPath;
47
+ this.maxQueue = options.maxQueue ?? DEFAULT_MAX_QUEUE;
48
+ this.batchSize = options.batchSize ?? DEFAULT_BATCH_SIZE;
49
+ this.now = options.now ?? (() => new Date());
50
+ this.debug = options.debug ?? (() => { });
51
+ this.workerSpecifier = options.workerSpecifier ?? createDefaultWorkerSpecifier();
52
+ }
53
+ recordToolArgumentValidation(event) {
54
+ if (!this.enabled || event.outcome === "clean")
55
+ return undefined;
56
+ const record = createToolArgumentValidationLogRecord({
57
+ event,
58
+ recordId: `${this.sessionId}:${this.recordSequence++}`,
59
+ sessionId: this.sessionId,
60
+ ts: this.now().toISOString(),
61
+ });
62
+ this.enqueue({ eventLogPath: this.eventLogPath, failureCorpusPath: this.failureCorpusPath, record });
63
+ return record;
64
+ }
65
+ getStats() {
66
+ return {
67
+ enabled: this.enabled,
68
+ queued: this.queue.length,
69
+ inFlight: this.inFlight.length,
70
+ dropped: this.dropped,
71
+ failures: this.failures,
72
+ workerStarts: this.workerStarts,
73
+ workerCrashes: this.workerCrashes,
74
+ respawns: this.respawns,
75
+ };
76
+ }
77
+ flush(timeoutMs = DEFAULT_FLUSH_TIMEOUT_MS) {
78
+ if (!this.enabled || (!this.worker && this.queue.length === 0 && this.inFlight.length === 0)) {
79
+ return Promise.resolve();
80
+ }
81
+ this.pump();
82
+ return new Promise((resolve) => {
83
+ let settled = false;
84
+ const finish = () => {
85
+ if (settled)
86
+ return;
87
+ settled = true;
88
+ resolve();
89
+ };
90
+ const timeout = setTimeout(finish, timeoutMs);
91
+ const waiter = () => {
92
+ clearTimeout(timeout);
93
+ finish();
94
+ };
95
+ this.flushWaiters.push(waiter);
96
+ this.resolveFlushWaitersIfIdle();
97
+ });
98
+ }
99
+ async shutdown(timeoutMs = DEFAULT_FLUSH_TIMEOUT_MS) {
100
+ await this.flush(timeoutMs);
101
+ this.shuttingDown = true;
102
+ const worker = this.worker;
103
+ this.worker = undefined;
104
+ if (!worker)
105
+ return;
106
+ try {
107
+ worker.postMessage({ type: "shutdown" });
108
+ }
109
+ catch { }
110
+ void worker.terminate().catch(() => undefined);
111
+ }
112
+ enqueue(record) {
113
+ if (!this.enabled)
114
+ return;
115
+ this.queue.push(record);
116
+ while (this.queue.length > this.maxQueue) {
117
+ this.queue.shift();
118
+ this.dropped++;
119
+ }
120
+ this.pump();
121
+ }
122
+ pump() {
123
+ if (!this.enabled || this.shuttingDown || this.inFlight.length > 0 || this.queue.length === 0)
124
+ return;
125
+ const worker = this.ensureWorker();
126
+ if (!worker)
127
+ return;
128
+ const records = this.queue.splice(0, this.batchSize);
129
+ this.inFlight = records;
130
+ const batchId = this.batchId++;
131
+ try {
132
+ worker.postMessage({ type: "records", batchId, records });
133
+ }
134
+ catch (error) {
135
+ this.failures++;
136
+ this.dropped += records.length;
137
+ this.inFlight = [];
138
+ this.debug(`tool recovery logger post failed: ${error instanceof Error ? error.message : String(error)}`);
139
+ this.resolveFlushWaitersIfIdle();
140
+ }
141
+ }
142
+ ensureWorker() {
143
+ if (this.worker)
144
+ return this.worker;
145
+ try {
146
+ const worker = new Worker(this.workerSpecifier);
147
+ worker.unref();
148
+ this.worker = worker;
149
+ this.workerStarts++;
150
+ worker.on("message", (message) => this.handleWorkerMessage(message));
151
+ worker.on("error", (error) => this.handleWorkerFailure(error));
152
+ worker.on("exit", (code) => {
153
+ if (this.shuttingDown || code === 0)
154
+ return;
155
+ this.handleWorkerFailure(new Error(`tool recovery logger worker exited with code ${code}`));
156
+ });
157
+ return worker;
158
+ }
159
+ catch (error) {
160
+ this.failures++;
161
+ this.debug(`tool recovery logger worker start failed: ${error instanceof Error ? error.message : String(error)}`);
162
+ return undefined;
163
+ }
164
+ }
165
+ handleWorkerMessage(message) {
166
+ if (!isToolRecoveryLogAckMessage(message))
167
+ return;
168
+ if (message.failed > 0) {
169
+ this.failures += message.failed;
170
+ }
171
+ this.inFlight = [];
172
+ this.pump();
173
+ this.resolveFlushWaitersIfIdle();
174
+ }
175
+ handleWorkerFailure(error) {
176
+ this.failures++;
177
+ this.workerCrashes++;
178
+ this.dropped += this.inFlight.length;
179
+ this.inFlight = [];
180
+ this.worker = undefined;
181
+ this.debug(`tool recovery logger worker failed: ${error.message}`);
182
+ if (!this.respawnAttempted && !this.shuttingDown) {
183
+ this.respawnAttempted = true;
184
+ this.respawns++;
185
+ this.pump();
186
+ }
187
+ this.resolveFlushWaitersIfIdle();
188
+ }
189
+ resolveFlushWaitersIfIdle() {
190
+ if (this.queue.length > 0 || this.inFlight.length > 0)
191
+ return;
192
+ const waiters = this.flushWaiters.splice(0);
193
+ for (const waiter of waiters)
194
+ waiter();
195
+ }
196
+ }
197
+ //# sourceMappingURL=tool-recovery-logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-recovery-logger.js","sourceRoot":"","sources":["../../src/core/tool-recovery-logger.ts"],"names":[],"mappings":"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","sourcesContent":["import { Worker } from \"node:worker_threads\";\nimport type { ToolArgumentValidationTelemetryEvent } from \"@caupulican/pi-ai\";\nimport {\n\tcreateToolArgumentValidationLogRecord,\n\ttype ToolArgumentValidationLogRecord,\n\ttype ToolRecoveryLogWorkerRecord,\n} from \"./tool-recovery-log-records.ts\";\n\ninterface ToolRecoveryLogAckMessage {\n\ttype: \"ack\";\n\tbatchId: number;\n\twritten: number;\n\tfailed: number;\n}\n\nexport interface ToolRecoveryLoggerStats {\n\tenabled: boolean;\n\tqueued: number;\n\tinFlight: number;\n\tdropped: number;\n\tfailures: number;\n\tworkerStarts: number;\n\tworkerCrashes: number;\n\trespawns: number;\n}\n\nexport interface ToolRecoveryLoggerOptions {\n\tenabled: boolean;\n\tsessionId: string;\n\teventLogPath: string;\n\tfailureCorpusPath: string;\n\tmaxQueue?: number;\n\tbatchSize?: number;\n\tnow?: () => Date;\n\tdebug?: (message: string) => void;\n\tworkerSpecifier?: string | URL;\n}\n\nconst DEFAULT_MAX_QUEUE = 1000;\nconst DEFAULT_BATCH_SIZE = 50;\nconst DEFAULT_FLUSH_TIMEOUT_MS = 200;\n\nfunction isToolRecoveryLogAckMessage(value: unknown): value is ToolRecoveryLogAckMessage {\n\tif (!value || typeof value !== \"object\") return false;\n\tconst message = value as Partial<ToolRecoveryLogAckMessage>;\n\treturn (\n\t\tmessage.type === \"ack\" &&\n\t\ttypeof message.batchId === \"number\" &&\n\t\ttypeof message.written === \"number\" &&\n\t\ttypeof message.failed === \"number\"\n\t);\n}\n\nfunction createDefaultWorkerSpecifier(): URL {\n\tconst isTypeScriptRuntime = import.meta.url.endsWith(\".ts\");\n\treturn new URL(\n\t\tisTypeScriptRuntime ? \"./tool-recovery-log-worker.ts\" : \"./tool-recovery-log-worker.js\",\n\t\timport.meta.url,\n\t);\n}\n\nexport class ToolRecoveryLogger {\n\tprivate readonly enabled: boolean;\n\tprivate readonly sessionId: string;\n\tprivate readonly eventLogPath: string;\n\tprivate readonly failureCorpusPath: string;\n\tprivate readonly maxQueue: number;\n\tprivate readonly batchSize: number;\n\tprivate readonly now: () => Date;\n\tprivate readonly debug: (message: string) => void;\n\tprivate readonly workerSpecifier: string | URL;\n\tprivate readonly queue: ToolRecoveryLogWorkerRecord[] = [];\n\tprivate readonly flushWaiters: Array<() => void> = [];\n\tprivate worker: Worker | undefined;\n\tprivate inFlight: ToolRecoveryLogWorkerRecord[] = [];\n\tprivate batchId = 0;\n\tprivate recordSequence = 0;\n\tprivate dropped = 0;\n\tprivate failures = 0;\n\tprivate workerStarts = 0;\n\tprivate workerCrashes = 0;\n\tprivate respawns = 0;\n\tprivate respawnAttempted = false;\n\tprivate shuttingDown = false;\n\n\tconstructor(options: ToolRecoveryLoggerOptions) {\n\t\tthis.enabled = options.enabled;\n\t\tthis.sessionId = options.sessionId;\n\t\tthis.eventLogPath = options.eventLogPath;\n\t\tthis.failureCorpusPath = options.failureCorpusPath;\n\t\tthis.maxQueue = options.maxQueue ?? DEFAULT_MAX_QUEUE;\n\t\tthis.batchSize = options.batchSize ?? DEFAULT_BATCH_SIZE;\n\t\tthis.now = options.now ?? (() => new Date());\n\t\tthis.debug = options.debug ?? (() => {});\n\t\tthis.workerSpecifier = options.workerSpecifier ?? createDefaultWorkerSpecifier();\n\t}\n\n\trecordToolArgumentValidation(\n\t\tevent: ToolArgumentValidationTelemetryEvent,\n\t): ToolArgumentValidationLogRecord | undefined {\n\t\tif (!this.enabled || event.outcome === \"clean\") return undefined;\n\t\tconst record = createToolArgumentValidationLogRecord({\n\t\t\tevent,\n\t\t\trecordId: `${this.sessionId}:${this.recordSequence++}`,\n\t\t\tsessionId: this.sessionId,\n\t\t\tts: this.now().toISOString(),\n\t\t});\n\t\tthis.enqueue({ eventLogPath: this.eventLogPath, failureCorpusPath: this.failureCorpusPath, record });\n\t\treturn record;\n\t}\n\n\tgetStats(): ToolRecoveryLoggerStats {\n\t\treturn {\n\t\t\tenabled: this.enabled,\n\t\t\tqueued: this.queue.length,\n\t\t\tinFlight: this.inFlight.length,\n\t\t\tdropped: this.dropped,\n\t\t\tfailures: this.failures,\n\t\t\tworkerStarts: this.workerStarts,\n\t\t\tworkerCrashes: this.workerCrashes,\n\t\t\trespawns: this.respawns,\n\t\t};\n\t}\n\n\tflush(timeoutMs: number = DEFAULT_FLUSH_TIMEOUT_MS): Promise<void> {\n\t\tif (!this.enabled || (!this.worker && this.queue.length === 0 && this.inFlight.length === 0)) {\n\t\t\treturn Promise.resolve();\n\t\t}\n\t\tthis.pump();\n\t\treturn new Promise((resolve) => {\n\t\t\tlet settled = false;\n\t\t\tconst finish = (): void => {\n\t\t\t\tif (settled) return;\n\t\t\t\tsettled = true;\n\t\t\t\tresolve();\n\t\t\t};\n\t\t\tconst timeout = setTimeout(finish, timeoutMs);\n\t\t\tconst waiter = (): void => {\n\t\t\t\tclearTimeout(timeout);\n\t\t\t\tfinish();\n\t\t\t};\n\t\t\tthis.flushWaiters.push(waiter);\n\t\t\tthis.resolveFlushWaitersIfIdle();\n\t\t});\n\t}\n\n\tasync shutdown(timeoutMs: number = DEFAULT_FLUSH_TIMEOUT_MS): Promise<void> {\n\t\tawait this.flush(timeoutMs);\n\t\tthis.shuttingDown = true;\n\t\tconst worker = this.worker;\n\t\tthis.worker = undefined;\n\t\tif (!worker) return;\n\t\ttry {\n\t\t\tworker.postMessage({ type: \"shutdown\" });\n\t\t} catch {}\n\t\tvoid worker.terminate().catch(() => undefined);\n\t}\n\n\tprivate enqueue(record: ToolRecoveryLogWorkerRecord): void {\n\t\tif (!this.enabled) return;\n\t\tthis.queue.push(record);\n\t\twhile (this.queue.length > this.maxQueue) {\n\t\t\tthis.queue.shift();\n\t\t\tthis.dropped++;\n\t\t}\n\t\tthis.pump();\n\t}\n\n\tprivate pump(): void {\n\t\tif (!this.enabled || this.shuttingDown || this.inFlight.length > 0 || this.queue.length === 0) return;\n\t\tconst worker = this.ensureWorker();\n\t\tif (!worker) return;\n\t\tconst records = this.queue.splice(0, this.batchSize);\n\t\tthis.inFlight = records;\n\t\tconst batchId = this.batchId++;\n\t\ttry {\n\t\t\tworker.postMessage({ type: \"records\", batchId, records });\n\t\t} catch (error) {\n\t\t\tthis.failures++;\n\t\t\tthis.dropped += records.length;\n\t\t\tthis.inFlight = [];\n\t\t\tthis.debug(`tool recovery logger post failed: ${error instanceof Error ? error.message : String(error)}`);\n\t\t\tthis.resolveFlushWaitersIfIdle();\n\t\t}\n\t}\n\n\tprivate ensureWorker(): Worker | undefined {\n\t\tif (this.worker) return this.worker;\n\t\ttry {\n\t\t\tconst worker = new Worker(this.workerSpecifier);\n\t\t\tworker.unref();\n\t\t\tthis.worker = worker;\n\t\t\tthis.workerStarts++;\n\t\t\tworker.on(\"message\", (message: unknown) => this.handleWorkerMessage(message));\n\t\t\tworker.on(\"error\", (error) => this.handleWorkerFailure(error));\n\t\t\tworker.on(\"exit\", (code) => {\n\t\t\t\tif (this.shuttingDown || code === 0) return;\n\t\t\t\tthis.handleWorkerFailure(new Error(`tool recovery logger worker exited with code ${code}`));\n\t\t\t});\n\t\t\treturn worker;\n\t\t} catch (error) {\n\t\t\tthis.failures++;\n\t\t\tthis.debug(\n\t\t\t\t`tool recovery logger worker start failed: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tprivate handleWorkerMessage(message: unknown): void {\n\t\tif (!isToolRecoveryLogAckMessage(message)) return;\n\t\tif (message.failed > 0) {\n\t\t\tthis.failures += message.failed;\n\t\t}\n\t\tthis.inFlight = [];\n\t\tthis.pump();\n\t\tthis.resolveFlushWaitersIfIdle();\n\t}\n\n\tprivate handleWorkerFailure(error: Error): void {\n\t\tthis.failures++;\n\t\tthis.workerCrashes++;\n\t\tthis.dropped += this.inFlight.length;\n\t\tthis.inFlight = [];\n\t\tthis.worker = undefined;\n\t\tthis.debug(`tool recovery logger worker failed: ${error.message}`);\n\t\tif (!this.respawnAttempted && !this.shuttingDown) {\n\t\t\tthis.respawnAttempted = true;\n\t\t\tthis.respawns++;\n\t\t\tthis.pump();\n\t\t}\n\t\tthis.resolveFlushWaitersIfIdle();\n\t}\n\n\tprivate resolveFlushWaitersIfIdle(): void {\n\t\tif (this.queue.length > 0 || this.inFlight.length > 0) return;\n\t\tconst waiters = this.flushWaiters.splice(0);\n\t\tfor (const waiter of waiters) waiter();\n\t}\n}\n"]}
@@ -1,3 +1,4 @@
1
1
  import type { ModelAdaptationStore } from "./models/adaptation-store.ts";
2
- export declare function formatToolRepairHealthReport(store: ModelAdaptationStore, now?: Date): string;
2
+ import type { ToolRecoveryLoggerStats } from "./tool-recovery-logger.ts";
3
+ export declare function formatToolRepairHealthReport(store: ModelAdaptationStore, now?: Date, loggerStats?: ToolRecoveryLoggerStats): string;
3
4
  //# sourceMappingURL=tool-repair-health.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tool-repair-health.d.ts","sourceRoot":"","sources":["../../src/core/tool-repair-health.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAyB,MAAM,8BAA8B,CAAC;AAahG,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,oBAAoB,EAAE,GAAG,GAAE,IAAiB,GAAG,MAAM,CAqDxG","sourcesContent":["import chalk from \"chalk\";\nimport type { ModelAdaptationStore, StoredModelAdaptation } from \"./models/adaptation-store.ts\";\n\nfunction formatAgeDays(iso: string, now: Date): string {\n\tconst ms = Date.parse(iso);\n\tif (!Number.isFinite(ms)) return \"age unknown\";\n\tconst days = Math.max(0, Math.floor((now.getTime() - ms) / (24 * 60 * 60 * 1000)));\n\treturn `${days}d ago`;\n}\n\nfunction sortProfiles(profiles: StoredModelAdaptation[]): StoredModelAdaptation[] {\n\treturn [...profiles].sort((left, right) => left.model.localeCompare(right.model));\n}\n\nexport function formatToolRepairHealthReport(store: ModelAdaptationStore, now: Date = new Date()): string {\n\tconst profiles = sortProfiles(store.getForHost());\n\tif (profiles.length === 0) return \"Tool repair health: no model adaptation records for this host.\";\n\n\tconst lines = [chalk.bold(\"Tool repair health\")];\n\tfor (const entry of profiles) {\n\t\tlines.push(`${entry.model}`);\n\t\tconst toolProbe = entry.profile.toolProbe;\n\t\tif (!toolProbe) {\n\t\t\tlines.push(\" tool probe: none\");\n\t\t} else {\n\t\t\tconst variant = toolProbe.variant ? ` (${toolProbe.variant})` : \"\";\n\t\t\tconst nativeGrade = toolProbe.nativeGrade ? ` native=${toolProbe.nativeGrade}` : \"\";\n\t\t\tlines.push(\n\t\t\t\t` tool probe: v${toolProbe.version} ${toolProbe.status}${variant}${nativeGrade} ${formatAgeDays(toolProbe.probedAt, now)}`,\n\t\t\t);\n\t\t\tif (toolProbe.diagnostic) lines.push(` probe diagnostic: ${toolProbe.diagnostic}`);\n\t\t}\n\t\tconst protocol = entry.profile.protocol;\n\t\tif (!protocol) {\n\t\t\tlines.push(\" protocol: none\");\n\t\t} else if (protocol.status === \"failed\") {\n\t\t\tlines.push(` protocol: v${protocol.version} failed ${formatAgeDays(protocol.attemptedAt, now)}`);\n\t\t\tlines.push(` variants tried: ${protocol.variantsTried.join(\", \")}`);\n\t\t\tlines.push(` reset: /toolprotocol-reset ${entry.model}`);\n\t\t} else {\n\t\t\tlines.push(\n\t\t\t\t` protocol: v${protocol.version} ${protocol.variant} calibrated ${formatAgeDays(protocol.calibratedAt, now)}`,\n\t\t\t);\n\t\t}\n\t\tif (entry.profile.rules.length === 0) {\n\t\t\tlines.push(\" rules: none\");\n\t\t} else {\n\t\t\tlines.push(\" rules:\");\n\t\t\tfor (const rule of [...entry.profile.rules].sort((left, right) => left.mode.localeCompare(right.mode))) {\n\t\t\t\tlines.push(` - ${rule.mode} (${formatAgeDays(rule.lastFiredAt, now)}): ${rule.text}`);\n\t\t\t}\n\t\t}\n\t\tconst teachEntries = Object.entries(entry.profile.teachStats).sort(([left], [right]) =>\n\t\t\tleft.localeCompare(right),\n\t\t);\n\t\tif (teachEntries.length === 0) {\n\t\t\tlines.push(\" teach stats: none\");\n\t\t} else {\n\t\t\tlines.push(\" teach stats:\");\n\t\t\tfor (const [mode, stats] of teachEntries) {\n\t\t\t\tlines.push(\n\t\t\t\t\t` - ${mode}: taught=${stats.taught} before=${stats.recurrenceBefore} after=${stats.recurrenceAfter}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\treturn lines.join(\"\\n\");\n}\n"]}
1
+ {"version":3,"file":"tool-repair-health.d.ts","sourceRoot":"","sources":["../../src/core/tool-repair-health.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAyB,MAAM,8BAA8B,CAAC;AAChG,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAoBzE,wBAAgB,4BAA4B,CAC3C,KAAK,EAAE,oBAAoB,EAC3B,GAAG,GAAE,IAAiB,EACtB,WAAW,CAAC,EAAE,uBAAuB,GACnC,MAAM,CAwDR","sourcesContent":["import chalk from \"chalk\";\nimport type { ModelAdaptationStore, StoredModelAdaptation } from \"./models/adaptation-store.ts\";\nimport type { ToolRecoveryLoggerStats } from \"./tool-recovery-logger.ts\";\n\nfunction formatAgeDays(iso: string, now: Date): string {\n\tconst ms = Date.parse(iso);\n\tif (!Number.isFinite(ms)) return \"age unknown\";\n\tconst days = Math.max(0, Math.floor((now.getTime() - ms) / (24 * 60 * 60 * 1000)));\n\treturn `${days}d ago`;\n}\n\nfunction sortProfiles(profiles: StoredModelAdaptation[]): StoredModelAdaptation[] {\n\treturn [...profiles].sort((left, right) => left.model.localeCompare(right.model));\n}\n\nfunction formatLoggerStats(stats: ToolRecoveryLoggerStats | undefined): string[] {\n\tif (!stats) return [];\n\treturn [\n\t\t`recovery logging: ${stats.enabled ? \"enabled\" : \"disabled\"} queued=${stats.queued} inFlight=${stats.inFlight} dropped=${stats.dropped} failures=${stats.failures} workerStarts=${stats.workerStarts} crashes=${stats.workerCrashes} respawns=${stats.respawns}`,\n\t];\n}\n\nexport function formatToolRepairHealthReport(\n\tstore: ModelAdaptationStore,\n\tnow: Date = new Date(),\n\tloggerStats?: ToolRecoveryLoggerStats,\n): string {\n\tconst profiles = sortProfiles(store.getForHost());\n\tif (profiles.length === 0) {\n\t\tconst loggerLines = formatLoggerStats(loggerStats);\n\t\treturn [\"Tool repair health: no model adaptation records for this host.\", ...loggerLines].join(\"\\n\");\n\t}\n\n\tconst lines = [chalk.bold(\"Tool repair health\"), ...formatLoggerStats(loggerStats)];\n\tfor (const entry of profiles) {\n\t\tlines.push(`${entry.model}`);\n\t\tconst toolProbe = entry.profile.toolProbe;\n\t\tif (!toolProbe) {\n\t\t\tlines.push(\" tool probe: none\");\n\t\t} else {\n\t\t\tconst variant = toolProbe.variant ? ` (${toolProbe.variant})` : \"\";\n\t\t\tconst nativeGrade = toolProbe.nativeGrade ? ` native=${toolProbe.nativeGrade}` : \"\";\n\t\t\tlines.push(\n\t\t\t\t` tool probe: v${toolProbe.version} ${toolProbe.status}${variant}${nativeGrade} ${formatAgeDays(toolProbe.probedAt, now)}`,\n\t\t\t);\n\t\t\tif (toolProbe.diagnostic) lines.push(` probe diagnostic: ${toolProbe.diagnostic}`);\n\t\t}\n\t\tconst protocol = entry.profile.protocol;\n\t\tif (!protocol) {\n\t\t\tlines.push(\" protocol: none\");\n\t\t} else if (protocol.status === \"failed\") {\n\t\t\tlines.push(` protocol: v${protocol.version} failed ${formatAgeDays(protocol.attemptedAt, now)}`);\n\t\t\tlines.push(` variants tried: ${protocol.variantsTried.join(\", \")}`);\n\t\t\tlines.push(` reset: /toolprotocol-reset ${entry.model}`);\n\t\t} else {\n\t\t\tlines.push(\n\t\t\t\t` protocol: v${protocol.version} ${protocol.variant} calibrated ${formatAgeDays(protocol.calibratedAt, now)}`,\n\t\t\t);\n\t\t}\n\t\tif (entry.profile.rules.length === 0) {\n\t\t\tlines.push(\" rules: none\");\n\t\t} else {\n\t\t\tlines.push(\" rules:\");\n\t\t\tfor (const rule of [...entry.profile.rules].sort((left, right) => left.mode.localeCompare(right.mode))) {\n\t\t\t\tlines.push(` - ${rule.mode} (${formatAgeDays(rule.lastFiredAt, now)}): ${rule.text}`);\n\t\t\t}\n\t\t}\n\t\tconst teachEntries = Object.entries(entry.profile.teachStats).sort(([left], [right]) =>\n\t\t\tleft.localeCompare(right),\n\t\t);\n\t\tif (teachEntries.length === 0) {\n\t\t\tlines.push(\" teach stats: none\");\n\t\t} else {\n\t\t\tlines.push(\" teach stats:\");\n\t\t\tfor (const [mode, stats] of teachEntries) {\n\t\t\t\tlines.push(\n\t\t\t\t\t` - ${mode}: taught=${stats.taught} before=${stats.recurrenceBefore} after=${stats.recurrenceAfter}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\treturn lines.join(\"\\n\");\n}\n"]}
@@ -9,11 +9,20 @@ function formatAgeDays(iso, now) {
9
9
  function sortProfiles(profiles) {
10
10
  return [...profiles].sort((left, right) => left.model.localeCompare(right.model));
11
11
  }
12
- export function formatToolRepairHealthReport(store, now = new Date()) {
12
+ function formatLoggerStats(stats) {
13
+ if (!stats)
14
+ return [];
15
+ return [
16
+ `recovery logging: ${stats.enabled ? "enabled" : "disabled"} queued=${stats.queued} inFlight=${stats.inFlight} dropped=${stats.dropped} failures=${stats.failures} workerStarts=${stats.workerStarts} crashes=${stats.workerCrashes} respawns=${stats.respawns}`,
17
+ ];
18
+ }
19
+ export function formatToolRepairHealthReport(store, now = new Date(), loggerStats) {
13
20
  const profiles = sortProfiles(store.getForHost());
14
- if (profiles.length === 0)
15
- return "Tool repair health: no model adaptation records for this host.";
16
- const lines = [chalk.bold("Tool repair health")];
21
+ if (profiles.length === 0) {
22
+ const loggerLines = formatLoggerStats(loggerStats);
23
+ return ["Tool repair health: no model adaptation records for this host.", ...loggerLines].join("\n");
24
+ }
25
+ const lines = [chalk.bold("Tool repair health"), ...formatLoggerStats(loggerStats)];
17
26
  for (const entry of profiles) {
18
27
  lines.push(`${entry.model}`);
19
28
  const toolProbe = entry.profile.toolProbe;
@@ -1 +1 @@
1
- {"version":3,"file":"tool-repair-health.js","sourceRoot":"","sources":["../../src/core/tool-repair-health.ts"],"names":[],"mappings":"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","sourcesContent":["import chalk from \"chalk\";\nimport type { ModelAdaptationStore, StoredModelAdaptation } from \"./models/adaptation-store.ts\";\n\nfunction formatAgeDays(iso: string, now: Date): string {\n\tconst ms = Date.parse(iso);\n\tif (!Number.isFinite(ms)) return \"age unknown\";\n\tconst days = Math.max(0, Math.floor((now.getTime() - ms) / (24 * 60 * 60 * 1000)));\n\treturn `${days}d ago`;\n}\n\nfunction sortProfiles(profiles: StoredModelAdaptation[]): StoredModelAdaptation[] {\n\treturn [...profiles].sort((left, right) => left.model.localeCompare(right.model));\n}\n\nexport function formatToolRepairHealthReport(store: ModelAdaptationStore, now: Date = new Date()): string {\n\tconst profiles = sortProfiles(store.getForHost());\n\tif (profiles.length === 0) return \"Tool repair health: no model adaptation records for this host.\";\n\n\tconst lines = [chalk.bold(\"Tool repair health\")];\n\tfor (const entry of profiles) {\n\t\tlines.push(`${entry.model}`);\n\t\tconst toolProbe = entry.profile.toolProbe;\n\t\tif (!toolProbe) {\n\t\t\tlines.push(\" tool probe: none\");\n\t\t} else {\n\t\t\tconst variant = toolProbe.variant ? ` (${toolProbe.variant})` : \"\";\n\t\t\tconst nativeGrade = toolProbe.nativeGrade ? ` native=${toolProbe.nativeGrade}` : \"\";\n\t\t\tlines.push(\n\t\t\t\t` tool probe: v${toolProbe.version} ${toolProbe.status}${variant}${nativeGrade} ${formatAgeDays(toolProbe.probedAt, now)}`,\n\t\t\t);\n\t\t\tif (toolProbe.diagnostic) lines.push(` probe diagnostic: ${toolProbe.diagnostic}`);\n\t\t}\n\t\tconst protocol = entry.profile.protocol;\n\t\tif (!protocol) {\n\t\t\tlines.push(\" protocol: none\");\n\t\t} else if (protocol.status === \"failed\") {\n\t\t\tlines.push(` protocol: v${protocol.version} failed ${formatAgeDays(protocol.attemptedAt, now)}`);\n\t\t\tlines.push(` variants tried: ${protocol.variantsTried.join(\", \")}`);\n\t\t\tlines.push(` reset: /toolprotocol-reset ${entry.model}`);\n\t\t} else {\n\t\t\tlines.push(\n\t\t\t\t` protocol: v${protocol.version} ${protocol.variant} calibrated ${formatAgeDays(protocol.calibratedAt, now)}`,\n\t\t\t);\n\t\t}\n\t\tif (entry.profile.rules.length === 0) {\n\t\t\tlines.push(\" rules: none\");\n\t\t} else {\n\t\t\tlines.push(\" rules:\");\n\t\t\tfor (const rule of [...entry.profile.rules].sort((left, right) => left.mode.localeCompare(right.mode))) {\n\t\t\t\tlines.push(` - ${rule.mode} (${formatAgeDays(rule.lastFiredAt, now)}): ${rule.text}`);\n\t\t\t}\n\t\t}\n\t\tconst teachEntries = Object.entries(entry.profile.teachStats).sort(([left], [right]) =>\n\t\t\tleft.localeCompare(right),\n\t\t);\n\t\tif (teachEntries.length === 0) {\n\t\t\tlines.push(\" teach stats: none\");\n\t\t} else {\n\t\t\tlines.push(\" teach stats:\");\n\t\t\tfor (const [mode, stats] of teachEntries) {\n\t\t\t\tlines.push(\n\t\t\t\t\t` - ${mode}: taught=${stats.taught} before=${stats.recurrenceBefore} after=${stats.recurrenceAfter}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\treturn lines.join(\"\\n\");\n}\n"]}
1
+ {"version":3,"file":"tool-repair-health.js","sourceRoot":"","sources":["../../src/core/tool-repair-health.ts"],"names":[],"mappings":"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","sourcesContent":["import chalk from \"chalk\";\nimport type { ModelAdaptationStore, StoredModelAdaptation } from \"./models/adaptation-store.ts\";\nimport type { ToolRecoveryLoggerStats } from \"./tool-recovery-logger.ts\";\n\nfunction formatAgeDays(iso: string, now: Date): string {\n\tconst ms = Date.parse(iso);\n\tif (!Number.isFinite(ms)) return \"age unknown\";\n\tconst days = Math.max(0, Math.floor((now.getTime() - ms) / (24 * 60 * 60 * 1000)));\n\treturn `${days}d ago`;\n}\n\nfunction sortProfiles(profiles: StoredModelAdaptation[]): StoredModelAdaptation[] {\n\treturn [...profiles].sort((left, right) => left.model.localeCompare(right.model));\n}\n\nfunction formatLoggerStats(stats: ToolRecoveryLoggerStats | undefined): string[] {\n\tif (!stats) return [];\n\treturn [\n\t\t`recovery logging: ${stats.enabled ? \"enabled\" : \"disabled\"} queued=${stats.queued} inFlight=${stats.inFlight} dropped=${stats.dropped} failures=${stats.failures} workerStarts=${stats.workerStarts} crashes=${stats.workerCrashes} respawns=${stats.respawns}`,\n\t];\n}\n\nexport function formatToolRepairHealthReport(\n\tstore: ModelAdaptationStore,\n\tnow: Date = new Date(),\n\tloggerStats?: ToolRecoveryLoggerStats,\n): string {\n\tconst profiles = sortProfiles(store.getForHost());\n\tif (profiles.length === 0) {\n\t\tconst loggerLines = formatLoggerStats(loggerStats);\n\t\treturn [\"Tool repair health: no model adaptation records for this host.\", ...loggerLines].join(\"\\n\");\n\t}\n\n\tconst lines = [chalk.bold(\"Tool repair health\"), ...formatLoggerStats(loggerStats)];\n\tfor (const entry of profiles) {\n\t\tlines.push(`${entry.model}`);\n\t\tconst toolProbe = entry.profile.toolProbe;\n\t\tif (!toolProbe) {\n\t\t\tlines.push(\" tool probe: none\");\n\t\t} else {\n\t\t\tconst variant = toolProbe.variant ? ` (${toolProbe.variant})` : \"\";\n\t\t\tconst nativeGrade = toolProbe.nativeGrade ? ` native=${toolProbe.nativeGrade}` : \"\";\n\t\t\tlines.push(\n\t\t\t\t` tool probe: v${toolProbe.version} ${toolProbe.status}${variant}${nativeGrade} ${formatAgeDays(toolProbe.probedAt, now)}`,\n\t\t\t);\n\t\t\tif (toolProbe.diagnostic) lines.push(` probe diagnostic: ${toolProbe.diagnostic}`);\n\t\t}\n\t\tconst protocol = entry.profile.protocol;\n\t\tif (!protocol) {\n\t\t\tlines.push(\" protocol: none\");\n\t\t} else if (protocol.status === \"failed\") {\n\t\t\tlines.push(` protocol: v${protocol.version} failed ${formatAgeDays(protocol.attemptedAt, now)}`);\n\t\t\tlines.push(` variants tried: ${protocol.variantsTried.join(\", \")}`);\n\t\t\tlines.push(` reset: /toolprotocol-reset ${entry.model}`);\n\t\t} else {\n\t\t\tlines.push(\n\t\t\t\t` protocol: v${protocol.version} ${protocol.variant} calibrated ${formatAgeDays(protocol.calibratedAt, now)}`,\n\t\t\t);\n\t\t}\n\t\tif (entry.profile.rules.length === 0) {\n\t\t\tlines.push(\" rules: none\");\n\t\t} else {\n\t\t\tlines.push(\" rules:\");\n\t\t\tfor (const rule of [...entry.profile.rules].sort((left, right) => left.mode.localeCompare(right.mode))) {\n\t\t\t\tlines.push(` - ${rule.mode} (${formatAgeDays(rule.lastFiredAt, now)}): ${rule.text}`);\n\t\t\t}\n\t\t}\n\t\tconst teachEntries = Object.entries(entry.profile.teachStats).sort(([left], [right]) =>\n\t\t\tleft.localeCompare(right),\n\t\t);\n\t\tif (teachEntries.length === 0) {\n\t\t\tlines.push(\" teach stats: none\");\n\t\t} else {\n\t\t\tlines.push(\" teach stats:\");\n\t\t\tfor (const [mode, stats] of teachEntries) {\n\t\t\t\tlines.push(\n\t\t\t\t\t` - ${mode}: taught=${stats.taught} before=${stats.recurrenceBefore} after=${stats.recurrenceAfter}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\treturn lines.join(\"\\n\");\n}\n"]}
@@ -3,6 +3,7 @@ export interface ResolvedToolRepairSettings {
3
3
  repair: boolean;
4
4
  teach: boolean;
5
5
  textProtocol: boolean | undefined;
6
+ logging: boolean;
6
7
  }
7
8
  type EnvLike = Record<string, string | undefined>;
8
9
  export declare function resolveToolRepairSettings(settings: Pick<Settings, "toolRepair">, env: EnvLike): ResolvedToolRepairSettings;
@@ -1 +1 @@
1
- {"version":3,"file":"tool-repair-settings.d.ts","sourceRoot":"","sources":["../../src/core/tool-repair-settings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEtD,MAAM,WAAW,0BAA0B;IAC1C,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;IACf,YAAY,EAAE,OAAO,GAAG,SAAS,CAAC;CAClC;AAED,KAAK,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAOlD,wBAAgB,yBAAyB,CACxC,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,EACtC,GAAG,EAAE,OAAO,GACV,0BAA0B,CAQ5B;AAED,wBAAgB,gCAAgC,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,GAAG,0BAA0B,CAEnH","sourcesContent":["import type { Settings } from \"./settings-manager.ts\";\n\nexport interface ResolvedToolRepairSettings {\n\trepair: boolean;\n\tteach: boolean;\n\ttextProtocol: boolean | undefined;\n}\n\ntype EnvLike = Record<string, string | undefined>;\n\nfunction isTruthyEnvFlag(value: string | undefined): boolean {\n\tif (!value) return false;\n\treturn [\"1\", \"true\", \"yes\", \"on\"].includes(value.trim().toLowerCase());\n}\n\nexport function resolveToolRepairSettings(\n\tsettings: Pick<Settings, \"toolRepair\">,\n\tenv: EnvLike,\n): ResolvedToolRepairSettings {\n\treturn {\n\t\trepair: !isTruthyEnvFlag(env.PI_TOOL_REPAIR_DISABLED) && settings.toolRepair?.repair !== false,\n\t\tteach: !isTruthyEnvFlag(env.PI_TOOL_REPAIR_TEACH_DISABLED) && settings.toolRepair?.teach !== false,\n\t\ttextProtocol: isTruthyEnvFlag(env.PI_TEXT_TOOL_CALL_PROTOCOL_DISABLED)\n\t\t\t? false\n\t\t\t: settings.toolRepair?.textProtocol,\n\t};\n}\n\nexport function resolveCurrentToolRepairSettings(settings: Pick<Settings, \"toolRepair\">): ResolvedToolRepairSettings {\n\treturn resolveToolRepairSettings(settings, process.env);\n}\n"]}
1
+ {"version":3,"file":"tool-repair-settings.d.ts","sourceRoot":"","sources":["../../src/core/tool-repair-settings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEtD,MAAM,WAAW,0BAA0B;IAC1C,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;IACf,YAAY,EAAE,OAAO,GAAG,SAAS,CAAC;IAClC,OAAO,EAAE,OAAO,CAAC;CACjB;AAED,KAAK,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAOlD,wBAAgB,yBAAyB,CACxC,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,EACtC,GAAG,EAAE,OAAO,GACV,0BAA0B,CAS5B;AAED,wBAAgB,gCAAgC,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,GAAG,0BAA0B,CAEnH","sourcesContent":["import type { Settings } from \"./settings-manager.ts\";\n\nexport interface ResolvedToolRepairSettings {\n\trepair: boolean;\n\tteach: boolean;\n\ttextProtocol: boolean | undefined;\n\tlogging: boolean;\n}\n\ntype EnvLike = Record<string, string | undefined>;\n\nfunction isTruthyEnvFlag(value: string | undefined): boolean {\n\tif (!value) return false;\n\treturn [\"1\", \"true\", \"yes\", \"on\"].includes(value.trim().toLowerCase());\n}\n\nexport function resolveToolRepairSettings(\n\tsettings: Pick<Settings, \"toolRepair\">,\n\tenv: EnvLike,\n): ResolvedToolRepairSettings {\n\treturn {\n\t\trepair: !isTruthyEnvFlag(env.PI_TOOL_REPAIR_DISABLED),\n\t\tteach: !isTruthyEnvFlag(env.PI_TOOL_REPAIR_TEACH_DISABLED) && settings.toolRepair?.teach !== false,\n\t\ttextProtocol: isTruthyEnvFlag(env.PI_TEXT_TOOL_CALL_PROTOCOL_DISABLED)\n\t\t\t? false\n\t\t\t: settings.toolRepair?.textProtocol,\n\t\tlogging: settings.toolRepair?.logging !== false,\n\t};\n}\n\nexport function resolveCurrentToolRepairSettings(settings: Pick<Settings, \"toolRepair\">): ResolvedToolRepairSettings {\n\treturn resolveToolRepairSettings(settings, process.env);\n}\n"]}
@@ -5,11 +5,12 @@ function isTruthyEnvFlag(value) {
5
5
  }
6
6
  export function resolveToolRepairSettings(settings, env) {
7
7
  return {
8
- repair: !isTruthyEnvFlag(env.PI_TOOL_REPAIR_DISABLED) && settings.toolRepair?.repair !== false,
8
+ repair: !isTruthyEnvFlag(env.PI_TOOL_REPAIR_DISABLED),
9
9
  teach: !isTruthyEnvFlag(env.PI_TOOL_REPAIR_TEACH_DISABLED) && settings.toolRepair?.teach !== false,
10
10
  textProtocol: isTruthyEnvFlag(env.PI_TEXT_TOOL_CALL_PROTOCOL_DISABLED)
11
11
  ? false
12
12
  : settings.toolRepair?.textProtocol,
13
+ logging: settings.toolRepair?.logging !== false,
13
14
  };
14
15
  }
15
16
  export function resolveCurrentToolRepairSettings(settings) {