@caupulican/pi-agent-core 0.81.3 → 0.81.5
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/dist/compaction/compaction.d.ts +10 -0
- package/dist/compaction/compaction.d.ts.map +1 -1
- package/dist/compaction/compaction.js +38 -13
- package/dist/compaction/compaction.js.map +1 -1
- package/dist/compaction/extraction.d.ts.map +1 -1
- package/dist/compaction/extraction.js +11 -1
- package/dist/compaction/extraction.js.map +1 -1
- package/dist/compaction/loop.d.ts.map +1 -1
- package/dist/compaction/loop.js +4 -1
- package/dist/compaction/loop.js.map +1 -1
- package/dist/compaction/verification.d.ts +1 -1
- package/dist/compaction/verification.d.ts.map +1 -1
- package/dist/compaction/verification.js +14 -6
- package/dist/compaction/verification.js.map +1 -1
- package/dist/reliability/provider-signatures.d.ts +3 -1
- package/dist/reliability/provider-signatures.d.ts.map +1 -1
- package/dist/reliability/provider-signatures.js +23 -0
- package/dist/reliability/provider-signatures.js.map +1 -1
- package/dist/reliability/retry-controller.d.ts.map +1 -1
- package/dist/reliability/retry-controller.js +1 -1
- package/dist/reliability/retry-controller.js.map +1 -1
- package/package.json +2 -2
package/dist/compaction/loop.js
CHANGED
|
@@ -103,7 +103,7 @@ function selectCycleParams(cycle, cause, lastParams, baseKeepRecent) {
|
|
|
103
103
|
...lastParams,
|
|
104
104
|
deterministicOnly: false,
|
|
105
105
|
};
|
|
106
|
-
if (cause === "gate-failed" || cause === "auth-failed") {
|
|
106
|
+
if (cause === "gate-failed" || cause === "auth-failed" || cause === "length-stop") {
|
|
107
107
|
params.modelTier = "session";
|
|
108
108
|
}
|
|
109
109
|
else if (cause === "input-overflow") {
|
|
@@ -145,6 +145,9 @@ function mapFailureCause(error) {
|
|
|
145
145
|
return "gate-failed";
|
|
146
146
|
if (message.includes("input-overflow"))
|
|
147
147
|
return "input-overflow";
|
|
148
|
+
// A length-stopped summary lost gated sections; escalating the tier buys a larger output cap.
|
|
149
|
+
if (message.includes("summary-length-stop"))
|
|
150
|
+
return "length-stop";
|
|
148
151
|
if (message.includes("auto-compaction-cancelled") || message.includes("aborted"))
|
|
149
152
|
return "aborted";
|
|
150
153
|
if (message.includes("auth") || message.includes("api key") || message.includes("not compacted"))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loop.js","sourceRoot":"","sources":["../../src/compaction/loop.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Model } from \"@caupulican/pi-ai\";\nimport type { SessionEntry } from \"../session/session-manager.ts\";\nimport type { CompactionResult } from \"./compaction.ts\";\n\nexport interface CompactionCycleParams {\n\tmodelTier: \"cheap\" | \"session\";\n\tkeepRecentTokens: number;\n\tchunked: boolean;\n\tdeterministicOnly: boolean;\n}\n\nexport interface ModelAndAuth {\n\tmodel: Model<any>;\n\tapiKey?: string;\n\theaders?: Record<string, string>;\n\tfailure?: string;\n}\n\nexport interface CompactionLoopDeps {\n\tmeasureLiveTokens(): number;\n\tgetTriggerThreshold(): number;\n\tgetMargin(): number;\n\tgetBranch(): SessionEntry[];\n\tresolveModelAndAuth(modelTier: CompactionCycleParams[\"modelTier\"]): Promise<ModelAndAuth>;\n\tsummarizeAndVerify(\n\t\tparams: CompactionCycleParams,\n\t\tmodel: Model<any>,\n\t\tapiKey: string | undefined,\n\t\theaders: Record<string, string> | undefined,\n\t\tbranch: SessionEntry[],\n\t): Promise<{ result: CompactionResult }>;\n\tbuildDeterministicCheckpoint(): Promise<{ result: CompactionResult }> | { result: CompactionResult };\n\tapply(result: CompactionResult): Promise<void> | void;\n\tonTransition(info: { cycle: number; from: string; cause: string }): void;\n\tgetBaseKeepRecentTokens?(): number;\n\tsignal?: AbortSignal;\n}\n\nexport type CompactionLoopOutcome =\n\t| { kind: \"success\"; result: CompactionResult; cycles: number }\n\t| { kind: \"skip\"; reason: string }\n\t| { kind: \"failed\"; reason: string; cycles: number };\n\nconst MAX_CYCLES = 4;\nconst MAX_LLM_CYCLES = 3;\nconst DEFAULT_KEEP_RECENT = 20_000;\n\nexport async function runCompactionLoop(deps: CompactionLoopDeps): Promise<CompactionLoopOutcome> {\n\tlet lastCause = \"start\";\n\tlet lastParams: CompactionCycleParams | undefined;\n\tlet lastObservedTokens: number | undefined;\n\tlet appliedResult: CompactionResult | undefined;\n\tlet baseKeepRecent = deps.getBaseKeepRecentTokens ? deps.getBaseKeepRecentTokens() : DEFAULT_KEEP_RECENT;\n\tif (!Number.isFinite(baseKeepRecent) || baseKeepRecent <= 0) {\n\t\tbaseKeepRecent = DEFAULT_KEEP_RECENT;\n\t}\n\n\tfor (let cycle = 1; cycle <= MAX_CYCLES; cycle++) {\n\t\tif (deps.signal?.aborted) {\n\t\t\treturn { kind: \"failed\", reason: \"aborted\", cycles: cycle - 1 };\n\t\t}\n\n\t\tconst branch = deps.getBranch();\n\t\tif (branch.length > 0 && branch[branch.length - 1]?.type === \"compaction\") {\n\t\t\tif (appliedResult) {\n\t\t\t\treturn { kind: \"success\", result: appliedResult, cycles: cycle - 1 };\n\t\t\t}\n\t\t\treturn { kind: \"skip\", reason: \"already compacted\" };\n\t\t}\n\n\t\tconst observedTokens = deps.measureLiveTokens();\n\t\tif (observedTokens <= deps.getTriggerThreshold()) {\n\t\t\treturn {\n\t\t\t\tkind: \"skip\",\n\t\t\t\treason:\n\t\t\t\t\tbranch.length > 0 && branch[branch.length - 1]?.type === \"compaction\"\n\t\t\t\t\t\t? \"already compacted\"\n\t\t\t\t\t\t: \"within threshold\",\n\t\t\t};\n\t\t}\n\n\t\tconst selectedParams = selectCycleParams(cycle, lastCause, lastParams, baseKeepRecent);\n\t\tconst params = enforceMonotonicProgress(selectedParams, lastParams, observedTokens, lastObservedTokens);\n\t\tlastObservedTokens = observedTokens;\n\t\tlastParams = params;\n\n\t\tif (params.deterministicOnly || cycle > MAX_LLM_CYCLES) {\n\t\t\tif (deps.signal?.aborted) {\n\t\t\t\treturn { kind: \"failed\", reason: \"aborted\", cycles: cycle - 1 };\n\t\t\t}\n\t\t\tconst { result } = await Promise.resolve(deps.buildDeterministicCheckpoint());\n\t\t\tawait deps.apply(result);\n\t\t\treturn { kind: \"success\", result, cycles: cycle };\n\t\t}\n\n\t\tlet modelInfo: ModelAndAuth;\n\t\ttry {\n\t\t\tmodelInfo = await deps.resolveModelAndAuth(params.modelTier);\n\t\t} catch {\n\t\t\tlastCause = \"auth-failed\";\n\t\t\tdeps.onTransition({ cycle: cycle + 1, from: \"step0\", cause: lastCause });\n\t\t\tcontinue;\n\t\t}\n\t\tif (modelInfo.failure) {\n\t\t\tlastCause = \"auth-failed\";\n\t\t\tdeps.onTransition({ cycle: cycle + 1, from: \"step0\", cause: lastCause });\n\t\t\tcontinue;\n\t\t}\n\n\t\tlet result: CompactionResult;\n\t\ttry {\n\t\t\t({ result } = await deps.summarizeAndVerify(\n\t\t\t\tparams,\n\t\t\t\tmodelInfo.model,\n\t\t\t\tmodelInfo.apiKey,\n\t\t\t\tmodelInfo.headers,\n\t\t\t\tbranch,\n\t\t\t));\n\t\t} catch (error) {\n\t\t\tlastCause = mapFailureCause(error);\n\t\t\tif (lastCause === \"aborted\") {\n\t\t\t\treturn { kind: \"failed\", reason: \"aborted\", cycles: cycle };\n\t\t\t}\n\t\t\tdeps.onTransition({ cycle: cycle + 1, from: \"step3\", cause: lastCause });\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (deps.signal?.aborted) {\n\t\t\treturn { kind: \"failed\", reason: \"aborted\", cycles: cycle };\n\t\t}\n\t\tawait deps.apply(result);\n\t\tappliedResult = result;\n\n\t\tconst measuredAfter = deps.measureLiveTokens();\n\t\tif (measuredAfter <= deps.getTriggerThreshold() - deps.getMargin()) {\n\t\t\treturn { kind: \"success\", result, cycles: cycle };\n\t\t}\n\n\t\tlastCause = \"effect-not-restored\";\n\t\tdeps.onTransition({ cycle: cycle + 1, from: \"step5\", cause: lastCause });\n\t}\n\n\treturn { kind: \"failed\", reason: \"exhausted-compaction-cycles\", cycles: MAX_CYCLES };\n}\n\nfunction selectCycleParams(\n\tcycle: number,\n\tcause: string,\n\tlastParams: CompactionCycleParams | undefined,\n\tbaseKeepRecent: number,\n): CompactionCycleParams {\n\tif (cycle >= MAX_CYCLES) {\n\t\treturn {\n\t\t\tmodelTier: lastParams?.modelTier ?? \"session\",\n\t\t\tkeepRecentTokens: Math.max(1, Math.floor((lastParams?.keepRecentTokens ?? baseKeepRecent) / 2)),\n\t\t\tchunked: true,\n\t\t\tdeterministicOnly: true,\n\t\t};\n\t}\n\n\tif (!lastParams) {\n\t\treturn {\n\t\t\tmodelTier: \"cheap\",\n\t\t\tkeepRecentTokens: Math.max(1, baseKeepRecent),\n\t\t\tchunked: false,\n\t\t\tdeterministicOnly: false,\n\t\t};\n\t}\n\n\tconst params: CompactionCycleParams = {\n\t\t...lastParams,\n\t\tdeterministicOnly: false,\n\t};\n\n\tif (cause === \"gate-failed\" || cause === \"auth-failed\") {\n\t\tparams.modelTier = \"session\";\n\t} else if (cause === \"input-overflow\") {\n\t\tparams.chunked = true;\n\t} else if (cause === \"effect-not-restored\") {\n\t\tparams.chunked = true;\n\t\tparams.keepRecentTokens = Math.max(1, Math.floor(lastParams.keepRecentTokens / 2));\n\t}\n\n\treturn params;\n}\n\nfunction enforceMonotonicProgress(\n\tparams: CompactionCycleParams,\n\tlastParams: CompactionCycleParams | undefined,\n\tobservedTokens: number,\n\tlastObservedTokens: number | undefined,\n): CompactionCycleParams {\n\tif (\n\t\t!lastParams ||\n\t\tparams.deterministicOnly ||\n\t\t(lastObservedTokens !== undefined && observedTokens < lastObservedTokens)\n\t) {\n\t\treturn params;\n\t}\n\tif (!sameParams(params, lastParams)) {\n\t\treturn params;\n\t}\n\n\tconst keepRecentTokens = Math.max(1, Math.floor(params.keepRecentTokens / 2));\n\tif (keepRecentTokens !== params.keepRecentTokens) {\n\t\treturn { ...params, chunked: true, keepRecentTokens };\n\t}\n\tif (!params.chunked) {\n\t\treturn { ...params, chunked: true };\n\t}\n\treturn { ...params, modelTier: params.modelTier === \"cheap\" ? \"session\" : \"cheap\" };\n}\n\nfunction sameParams(a: CompactionCycleParams, b: CompactionCycleParams): boolean {\n\treturn (\n\t\ta.modelTier === b.modelTier &&\n\t\ta.keepRecentTokens === b.keepRecentTokens &&\n\t\ta.chunked === b.chunked &&\n\t\ta.deterministicOnly === b.deterministicOnly\n\t);\n}\n\nfunction mapFailureCause(error: unknown): string {\n\tconst message = error instanceof Error ? error.message : typeof error === \"string\" ? error : \"\";\n\tif (message.includes(\"gate-failed\")) return \"gate-failed\";\n\tif (message.includes(\"input-overflow\")) return \"input-overflow\";\n\tif (message.includes(\"auto-compaction-cancelled\") || message.includes(\"aborted\")) return \"aborted\";\n\tif (message.includes(\"auth\") || message.includes(\"api key\") || message.includes(\"not compacted\"))\n\t\treturn \"auth-failed\";\n\treturn \"unknown-failure\";\n}\n"]}
|
|
1
|
+
{"version":3,"file":"loop.js","sourceRoot":"","sources":["../../src/compaction/loop.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Model } from \"@caupulican/pi-ai\";\nimport type { SessionEntry } from \"../session/session-manager.ts\";\nimport type { CompactionResult } from \"./compaction.ts\";\n\nexport interface CompactionCycleParams {\n\tmodelTier: \"cheap\" | \"session\";\n\tkeepRecentTokens: number;\n\tchunked: boolean;\n\tdeterministicOnly: boolean;\n}\n\nexport interface ModelAndAuth {\n\tmodel: Model<any>;\n\tapiKey?: string;\n\theaders?: Record<string, string>;\n\tfailure?: string;\n}\n\nexport interface CompactionLoopDeps {\n\tmeasureLiveTokens(): number;\n\tgetTriggerThreshold(): number;\n\tgetMargin(): number;\n\tgetBranch(): SessionEntry[];\n\tresolveModelAndAuth(modelTier: CompactionCycleParams[\"modelTier\"]): Promise<ModelAndAuth>;\n\tsummarizeAndVerify(\n\t\tparams: CompactionCycleParams,\n\t\tmodel: Model<any>,\n\t\tapiKey: string | undefined,\n\t\theaders: Record<string, string> | undefined,\n\t\tbranch: SessionEntry[],\n\t): Promise<{ result: CompactionResult }>;\n\tbuildDeterministicCheckpoint(): Promise<{ result: CompactionResult }> | { result: CompactionResult };\n\tapply(result: CompactionResult): Promise<void> | void;\n\tonTransition(info: { cycle: number; from: string; cause: string }): void;\n\tgetBaseKeepRecentTokens?(): number;\n\tsignal?: AbortSignal;\n}\n\nexport type CompactionLoopOutcome =\n\t| { kind: \"success\"; result: CompactionResult; cycles: number }\n\t| { kind: \"skip\"; reason: string }\n\t| { kind: \"failed\"; reason: string; cycles: number };\n\nconst MAX_CYCLES = 4;\nconst MAX_LLM_CYCLES = 3;\nconst DEFAULT_KEEP_RECENT = 20_000;\n\nexport async function runCompactionLoop(deps: CompactionLoopDeps): Promise<CompactionLoopOutcome> {\n\tlet lastCause = \"start\";\n\tlet lastParams: CompactionCycleParams | undefined;\n\tlet lastObservedTokens: number | undefined;\n\tlet appliedResult: CompactionResult | undefined;\n\tlet baseKeepRecent = deps.getBaseKeepRecentTokens ? deps.getBaseKeepRecentTokens() : DEFAULT_KEEP_RECENT;\n\tif (!Number.isFinite(baseKeepRecent) || baseKeepRecent <= 0) {\n\t\tbaseKeepRecent = DEFAULT_KEEP_RECENT;\n\t}\n\n\tfor (let cycle = 1; cycle <= MAX_CYCLES; cycle++) {\n\t\tif (deps.signal?.aborted) {\n\t\t\treturn { kind: \"failed\", reason: \"aborted\", cycles: cycle - 1 };\n\t\t}\n\n\t\tconst branch = deps.getBranch();\n\t\tif (branch.length > 0 && branch[branch.length - 1]?.type === \"compaction\") {\n\t\t\tif (appliedResult) {\n\t\t\t\treturn { kind: \"success\", result: appliedResult, cycles: cycle - 1 };\n\t\t\t}\n\t\t\treturn { kind: \"skip\", reason: \"already compacted\" };\n\t\t}\n\n\t\tconst observedTokens = deps.measureLiveTokens();\n\t\tif (observedTokens <= deps.getTriggerThreshold()) {\n\t\t\treturn {\n\t\t\t\tkind: \"skip\",\n\t\t\t\treason:\n\t\t\t\t\tbranch.length > 0 && branch[branch.length - 1]?.type === \"compaction\"\n\t\t\t\t\t\t? \"already compacted\"\n\t\t\t\t\t\t: \"within threshold\",\n\t\t\t};\n\t\t}\n\n\t\tconst selectedParams = selectCycleParams(cycle, lastCause, lastParams, baseKeepRecent);\n\t\tconst params = enforceMonotonicProgress(selectedParams, lastParams, observedTokens, lastObservedTokens);\n\t\tlastObservedTokens = observedTokens;\n\t\tlastParams = params;\n\n\t\tif (params.deterministicOnly || cycle > MAX_LLM_CYCLES) {\n\t\t\tif (deps.signal?.aborted) {\n\t\t\t\treturn { kind: \"failed\", reason: \"aborted\", cycles: cycle - 1 };\n\t\t\t}\n\t\t\tconst { result } = await Promise.resolve(deps.buildDeterministicCheckpoint());\n\t\t\tawait deps.apply(result);\n\t\t\treturn { kind: \"success\", result, cycles: cycle };\n\t\t}\n\n\t\tlet modelInfo: ModelAndAuth;\n\t\ttry {\n\t\t\tmodelInfo = await deps.resolveModelAndAuth(params.modelTier);\n\t\t} catch {\n\t\t\tlastCause = \"auth-failed\";\n\t\t\tdeps.onTransition({ cycle: cycle + 1, from: \"step0\", cause: lastCause });\n\t\t\tcontinue;\n\t\t}\n\t\tif (modelInfo.failure) {\n\t\t\tlastCause = \"auth-failed\";\n\t\t\tdeps.onTransition({ cycle: cycle + 1, from: \"step0\", cause: lastCause });\n\t\t\tcontinue;\n\t\t}\n\n\t\tlet result: CompactionResult;\n\t\ttry {\n\t\t\t({ result } = await deps.summarizeAndVerify(\n\t\t\t\tparams,\n\t\t\t\tmodelInfo.model,\n\t\t\t\tmodelInfo.apiKey,\n\t\t\t\tmodelInfo.headers,\n\t\t\t\tbranch,\n\t\t\t));\n\t\t} catch (error) {\n\t\t\tlastCause = mapFailureCause(error);\n\t\t\tif (lastCause === \"aborted\") {\n\t\t\t\treturn { kind: \"failed\", reason: \"aborted\", cycles: cycle };\n\t\t\t}\n\t\t\tdeps.onTransition({ cycle: cycle + 1, from: \"step3\", cause: lastCause });\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (deps.signal?.aborted) {\n\t\t\treturn { kind: \"failed\", reason: \"aborted\", cycles: cycle };\n\t\t}\n\t\tawait deps.apply(result);\n\t\tappliedResult = result;\n\n\t\tconst measuredAfter = deps.measureLiveTokens();\n\t\tif (measuredAfter <= deps.getTriggerThreshold() - deps.getMargin()) {\n\t\t\treturn { kind: \"success\", result, cycles: cycle };\n\t\t}\n\n\t\tlastCause = \"effect-not-restored\";\n\t\tdeps.onTransition({ cycle: cycle + 1, from: \"step5\", cause: lastCause });\n\t}\n\n\treturn { kind: \"failed\", reason: \"exhausted-compaction-cycles\", cycles: MAX_CYCLES };\n}\n\nfunction selectCycleParams(\n\tcycle: number,\n\tcause: string,\n\tlastParams: CompactionCycleParams | undefined,\n\tbaseKeepRecent: number,\n): CompactionCycleParams {\n\tif (cycle >= MAX_CYCLES) {\n\t\treturn {\n\t\t\tmodelTier: lastParams?.modelTier ?? \"session\",\n\t\t\tkeepRecentTokens: Math.max(1, Math.floor((lastParams?.keepRecentTokens ?? baseKeepRecent) / 2)),\n\t\t\tchunked: true,\n\t\t\tdeterministicOnly: true,\n\t\t};\n\t}\n\n\tif (!lastParams) {\n\t\treturn {\n\t\t\tmodelTier: \"cheap\",\n\t\t\tkeepRecentTokens: Math.max(1, baseKeepRecent),\n\t\t\tchunked: false,\n\t\t\tdeterministicOnly: false,\n\t\t};\n\t}\n\n\tconst params: CompactionCycleParams = {\n\t\t...lastParams,\n\t\tdeterministicOnly: false,\n\t};\n\n\tif (cause === \"gate-failed\" || cause === \"auth-failed\" || cause === \"length-stop\") {\n\t\tparams.modelTier = \"session\";\n\t} else if (cause === \"input-overflow\") {\n\t\tparams.chunked = true;\n\t} else if (cause === \"effect-not-restored\") {\n\t\tparams.chunked = true;\n\t\tparams.keepRecentTokens = Math.max(1, Math.floor(lastParams.keepRecentTokens / 2));\n\t}\n\n\treturn params;\n}\n\nfunction enforceMonotonicProgress(\n\tparams: CompactionCycleParams,\n\tlastParams: CompactionCycleParams | undefined,\n\tobservedTokens: number,\n\tlastObservedTokens: number | undefined,\n): CompactionCycleParams {\n\tif (\n\t\t!lastParams ||\n\t\tparams.deterministicOnly ||\n\t\t(lastObservedTokens !== undefined && observedTokens < lastObservedTokens)\n\t) {\n\t\treturn params;\n\t}\n\tif (!sameParams(params, lastParams)) {\n\t\treturn params;\n\t}\n\n\tconst keepRecentTokens = Math.max(1, Math.floor(params.keepRecentTokens / 2));\n\tif (keepRecentTokens !== params.keepRecentTokens) {\n\t\treturn { ...params, chunked: true, keepRecentTokens };\n\t}\n\tif (!params.chunked) {\n\t\treturn { ...params, chunked: true };\n\t}\n\treturn { ...params, modelTier: params.modelTier === \"cheap\" ? \"session\" : \"cheap\" };\n}\n\nfunction sameParams(a: CompactionCycleParams, b: CompactionCycleParams): boolean {\n\treturn (\n\t\ta.modelTier === b.modelTier &&\n\t\ta.keepRecentTokens === b.keepRecentTokens &&\n\t\ta.chunked === b.chunked &&\n\t\ta.deterministicOnly === b.deterministicOnly\n\t);\n}\n\nfunction mapFailureCause(error: unknown): string {\n\tconst message = error instanceof Error ? error.message : typeof error === \"string\" ? error : \"\";\n\tif (message.includes(\"gate-failed\")) return \"gate-failed\";\n\tif (message.includes(\"input-overflow\")) return \"input-overflow\";\n\t// A length-stopped summary lost gated sections; escalating the tier buys a larger output cap.\n\tif (message.includes(\"summary-length-stop\")) return \"length-stop\";\n\tif (message.includes(\"auto-compaction-cancelled\") || message.includes(\"aborted\")) return \"aborted\";\n\tif (message.includes(\"auth\") || message.includes(\"api key\") || message.includes(\"not compacted\"))\n\t\treturn \"auth-failed\";\n\treturn \"unknown-failure\";\n}\n"]}
|
|
@@ -11,7 +11,7 @@ export declare const FILES_READ_RECALL_THRESHOLD = 0.8;
|
|
|
11
11
|
export declare const ACTIVE_TASK_CONTAINMENT_THRESHOLD = 0.9;
|
|
12
12
|
export declare const MANDATORY_RULES_RECALL_THRESHOLD = 0.7;
|
|
13
13
|
export declare const CANCELLED_WORK_DROPPED_THRESHOLD = 0.1;
|
|
14
|
-
export declare const
|
|
14
|
+
export declare const ACTIONS_RECALL_THRESHOLD = 0.6;
|
|
15
15
|
export declare function verifySummary(summary: string, facts: CompactionFacts): VerificationReport;
|
|
16
16
|
export declare function buildRetryPrompt(report: VerificationReport, previousAttempt?: string): string;
|
|
17
17
|
export declare function tokenSet(text: string): Set<string>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verification.d.ts","sourceRoot":"","sources":["../../src/compaction/verification.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEvD,MAAM,WAAW,mBAAmB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IAClC,EAAE,EAAE,OAAO,CAAC;IACZ,QAAQ,EAAE,mBAAmB,EAAE,CAAC;CAChC;AAED,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAC/C,eAAO,MAAM,iCAAiC,MAAM,CAAC;AACrD,eAAO,MAAM,gCAAgC,MAAM,CAAC;AACpD,eAAO,MAAM,gCAAgC,MAAM,CAAC;AACpD,eAAO,MAAM,
|
|
1
|
+
{"version":3,"file":"verification.d.ts","sourceRoot":"","sources":["../../src/compaction/verification.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEvD,MAAM,WAAW,mBAAmB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IAClC,EAAE,EAAE,OAAO,CAAC;IACZ,QAAQ,EAAE,mBAAmB,EAAE,CAAC;CAChC;AAED,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAC/C,eAAO,MAAM,iCAAiC,MAAM,CAAC;AACrD,eAAO,MAAM,gCAAgC,MAAM,CAAC;AACpD,eAAO,MAAM,gCAAgC,MAAM,CAAC;AACpD,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAO5C,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,kBAAkB,CAkFzF;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,kBAAkB,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,CAI7F;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAQlD;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,CAWzE;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,CAY9D","sourcesContent":["import type { CompactionFacts } from \"./extraction.ts\";\n\nexport interface VerificationFailure {\n\tcheck: string;\n\tdetail: string;\n}\n\nexport interface VerificationReport {\n\tok: boolean;\n\tfailures: VerificationFailure[];\n}\n\nexport const FILES_READ_RECALL_THRESHOLD = 0.8;\nexport const ACTIVE_TASK_CONTAINMENT_THRESHOLD = 0.9;\nexport const MANDATORY_RULES_RECALL_THRESHOLD = 0.7;\nexport const CANCELLED_WORK_DROPPED_THRESHOLD = 0.1;\nexport const ACTIONS_RECALL_THRESHOLD = 0.6;\n\nconst SECTION_FILES = \"files\";\nconst SECTION_DONE = \"done\";\nconst SECTION_ACTIVE_TASK = \"active task\";\nconst SECTION_MANDATORY_RULES = \"mandatory rules\";\n\nexport function verifySummary(summary: string, facts: CompactionFacts): VerificationReport {\n\tif (factsAreEmpty(facts)) {\n\t\treturn { ok: true, failures: [] };\n\t}\n\n\tconst sections = extractSections(summary);\n\tconst failures: VerificationFailure[] = [];\n\tconst filesSection = sections[SECTION_FILES] ?? \"\";\n\tconst doneSection = sections[SECTION_DONE] ?? \"\";\n\tconst activeTaskSection = sections[SECTION_ACTIVE_TASK] ?? \"\";\n\tconst mandatoryRulesSection = sections[SECTION_MANDATORY_RULES] ?? \"\";\n\n\tconst modifiedFiles = facts.files.filter((file) => file.kind !== \"read\");\n\tconst missingModifiedFiles = modifiedFiles.map((file) => file.path).filter((path) => !filesSection.includes(path));\n\tif (missingModifiedFiles.length > 0) {\n\t\tfailures.push({\n\t\t\tcheck: \"files-modified-recall\",\n\t\t\tdetail: `Missing modified/created files in ## Files: ${missingModifiedFiles.join(\", \")}`,\n\t\t});\n\t}\n\n\tconst readPaths = facts.files.filter((file) => file.kind === \"read\").map((file) => file.path);\n\tif (readPaths.length > 0) {\n\t\tconst score = containment(tokenSet(readPaths.join(\"\\n\")), tokenSet(filesSection));\n\t\tif (score < FILES_READ_RECALL_THRESHOLD) {\n\t\t\tfailures.push({\n\t\t\t\tcheck: \"files-read-recall\",\n\t\t\t\tdetail: `Read file recall ${formatScore(score)} below ${FILES_READ_RECALL_THRESHOLD}`,\n\t\t\t});\n\t\t}\n\t}\n\n\tif (facts.activeTaskSource) {\n\t\tconst score = containment(tokenSet(facts.activeTaskSource), tokenSet(activeTaskSection));\n\t\tif (score < ACTIVE_TASK_CONTAINMENT_THRESHOLD) {\n\t\t\tfailures.push({\n\t\t\t\tcheck: \"active-task-containment\",\n\t\t\t\tdetail: `Active task containment ${formatScore(score)} below ${ACTIVE_TASK_CONTAINMENT_THRESHOLD}`,\n\t\t\t});\n\t\t}\n\t}\n\n\tfor (const prohibition of facts.prohibitions) {\n\t\tconst score = containment(tokenSet(prohibition), tokenSet(mandatoryRulesSection));\n\t\tif (score < MANDATORY_RULES_RECALL_THRESHOLD) {\n\t\t\tfailures.push({\n\t\t\t\tcheck: \"mandatory-rules-recall\",\n\t\t\t\tdetail: `Missing mandatory rule: ${prohibition}`,\n\t\t\t});\n\t\t}\n\t}\n\n\tif (facts.cancelledText) {\n\t\tconst summaryOutsideMandatoryRules = removeSection(summary, SECTION_MANDATORY_RULES);\n\t\t// File paths from the facts are REQUIRED elsewhere (files-modified/read-recall demand them\n\t\t// in ## Files), so counting them as cancelled-work leakage would make the two gates\n\t\t// unsatisfiable together whenever a reversal message references a touched file.\n\t\tconst factPathTokens = tokenSet(facts.files.map((file) => file.path).join(\"\\n\"));\n\t\tconst cancelledTokens = new Set([...tokenSet(facts.cancelledText)].filter((token) => !factPathTokens.has(token)));\n\t\tconst score = containment(cancelledTokens, tokenSet(summaryOutsideMandatoryRules));\n\t\tif (score > CANCELLED_WORK_DROPPED_THRESHOLD) {\n\t\t\tfailures.push({\n\t\t\t\tcheck: \"cancelled-work-dropped\",\n\t\t\t\tdetail: `Cancelled work leakage ${formatScore(score)} above ${CANCELLED_WORK_DROPPED_THRESHOLD}`,\n\t\t\t});\n\t\t}\n\t}\n\n\tif (facts.actions.length > 0) {\n\t\t// Asymmetric on purpose: the update path carries prior ## Done items forward (bounded), so a\n\t\t// symmetric overlap metric would punish faithful carry-over — the gate demands only that the\n\t\t// NEW span's actions are recalled in ## Done, however much history rides alongside them.\n\t\tconst score = containment(tokenSet(facts.actions.join(\"\\n\")), tokenSet(doneSection));\n\t\tif (score < ACTIONS_RECALL_THRESHOLD) {\n\t\t\tfailures.push({\n\t\t\t\tcheck: \"actions-recall\",\n\t\t\t\tdetail: `New-action recall in ## Done ${formatScore(score)} below ${ACTIONS_RECALL_THRESHOLD}`,\n\t\t\t});\n\t\t}\n\t}\n\n\treturn { ok: failures.length === 0, failures };\n}\n\nexport function buildRetryPrompt(report: VerificationReport, previousAttempt?: string): string {\n\tconst failures = report.failures.map((failure) => `${failure.check}: ${failure.detail}`).join(\"; \");\n\tconst previous = previousAttempt ? `\\n\\n<previous-attempt>\\n${previousAttempt}\\n</previous-attempt>` : \"\";\n\treturn `Your previous checkpoint failed verification: ${failures}. Fix ONLY these omissions.${previous}`;\n}\n\nexport function tokenSet(text: string): Set<string> {\n\treturn new Set(\n\t\ttext\n\t\t\t.toLowerCase()\n\t\t\t.split(/[^a-z0-9_./-]+/)\n\t\t\t.map((token) => token.trim())\n\t\t\t.filter((token) => token.length >= 3),\n\t);\n}\n\nexport function containment(needle: Set<string>, hay: Set<string>): number {\n\tif (needle.size === 0) {\n\t\treturn 1;\n\t}\n\tlet hits = 0;\n\tfor (const token of needle) {\n\t\tif (hay.has(token)) {\n\t\t\thits += 1;\n\t\t}\n\t}\n\treturn hits / needle.size;\n}\n\nexport function jaccard(a: Set<string>, b: Set<string>): number {\n\tif (a.size === 0 && b.size === 0) {\n\t\treturn 1;\n\t}\n\tlet intersection = 0;\n\tfor (const token of a) {\n\t\tif (b.has(token)) {\n\t\t\tintersection += 1;\n\t\t}\n\t}\n\tconst union = new Set([...a, ...b]).size;\n\treturn union === 0 ? 1 : intersection / union;\n}\n\nfunction factsAreEmpty(facts: CompactionFacts): boolean {\n\treturn (\n\t\tfacts.files.length === 0 &&\n\t\tfacts.actions.length === 0 &&\n\t\tfacts.prohibitions.length === 0 &&\n\t\tfacts.cancelledText === \"\" &&\n\t\tfacts.activeTaskSource === \"\"\n\t);\n}\n\nfunction extractSections(summary: string): Record<string, string> {\n\tconst sections: Record<string, string> = {};\n\tlet current: string | undefined;\n\tlet bucket: string[] = [];\n\n\tconst flush = (): void => {\n\t\tif (current) {\n\t\t\tsections[current] = bucket.join(\"\\n\").trim();\n\t\t}\n\t\tbucket = [];\n\t};\n\n\tfor (const line of summary.split(/\\r?\\n/)) {\n\t\tconst match = /^(?:##|###)\\s+(.+?)\\s*$/.exec(line);\n\t\tif (match) {\n\t\t\tflush();\n\t\t\tcurrent = normalizeHeading(match[1]);\n\t\t\tcontinue;\n\t\t}\n\t\tif (current) {\n\t\t\tbucket.push(line);\n\t\t}\n\t}\n\tflush();\n\treturn sections;\n}\n\nfunction removeSection(summary: string, heading: string): string {\n\tconst normalizedHeading = normalizeHeading(heading);\n\tconst kept: string[] = [];\n\tlet skipping = false;\n\tfor (const line of summary.split(/\\r?\\n/)) {\n\t\tconst match = /^(?:##|###)\\s+(.+?)\\s*$/.exec(line);\n\t\tif (match) {\n\t\t\tskipping = normalizeHeading(match[1]) === normalizedHeading;\n\t\t\tif (skipping) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tif (!skipping) {\n\t\t\tkept.push(line);\n\t\t}\n\t}\n\treturn kept.join(\"\\n\");\n}\n\nfunction normalizeHeading(heading: string): string {\n\treturn heading.trim().toLowerCase();\n}\n\nfunction formatScore(score: number): string {\n\treturn score.toFixed(2);\n}\n"]}
|
|
@@ -2,7 +2,7 @@ export const FILES_READ_RECALL_THRESHOLD = 0.8;
|
|
|
2
2
|
export const ACTIVE_TASK_CONTAINMENT_THRESHOLD = 0.9;
|
|
3
3
|
export const MANDATORY_RULES_RECALL_THRESHOLD = 0.7;
|
|
4
4
|
export const CANCELLED_WORK_DROPPED_THRESHOLD = 0.1;
|
|
5
|
-
export const
|
|
5
|
+
export const ACTIONS_RECALL_THRESHOLD = 0.6;
|
|
6
6
|
const SECTION_FILES = "files";
|
|
7
7
|
const SECTION_DONE = "done";
|
|
8
8
|
const SECTION_ACTIVE_TASK = "active task";
|
|
@@ -55,7 +55,12 @@ export function verifySummary(summary, facts) {
|
|
|
55
55
|
}
|
|
56
56
|
if (facts.cancelledText) {
|
|
57
57
|
const summaryOutsideMandatoryRules = removeSection(summary, SECTION_MANDATORY_RULES);
|
|
58
|
-
|
|
58
|
+
// File paths from the facts are REQUIRED elsewhere (files-modified/read-recall demand them
|
|
59
|
+
// in ## Files), so counting them as cancelled-work leakage would make the two gates
|
|
60
|
+
// unsatisfiable together whenever a reversal message references a touched file.
|
|
61
|
+
const factPathTokens = tokenSet(facts.files.map((file) => file.path).join("\n"));
|
|
62
|
+
const cancelledTokens = new Set([...tokenSet(facts.cancelledText)].filter((token) => !factPathTokens.has(token)));
|
|
63
|
+
const score = containment(cancelledTokens, tokenSet(summaryOutsideMandatoryRules));
|
|
59
64
|
if (score > CANCELLED_WORK_DROPPED_THRESHOLD) {
|
|
60
65
|
failures.push({
|
|
61
66
|
check: "cancelled-work-dropped",
|
|
@@ -64,11 +69,14 @@ export function verifySummary(summary, facts) {
|
|
|
64
69
|
}
|
|
65
70
|
}
|
|
66
71
|
if (facts.actions.length > 0) {
|
|
67
|
-
|
|
68
|
-
|
|
72
|
+
// Asymmetric on purpose: the update path carries prior ## Done items forward (bounded), so a
|
|
73
|
+
// symmetric overlap metric would punish faithful carry-over — the gate demands only that the
|
|
74
|
+
// NEW span's actions are recalled in ## Done, however much history rides alongside them.
|
|
75
|
+
const score = containment(tokenSet(facts.actions.join("\n")), tokenSet(doneSection));
|
|
76
|
+
if (score < ACTIONS_RECALL_THRESHOLD) {
|
|
69
77
|
failures.push({
|
|
70
|
-
check: "actions-
|
|
71
|
-
detail: `Done
|
|
78
|
+
check: "actions-recall",
|
|
79
|
+
detail: `New-action recall in ## Done ${formatScore(score)} below ${ACTIONS_RECALL_THRESHOLD}`,
|
|
72
80
|
});
|
|
73
81
|
}
|
|
74
82
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verification.js","sourceRoot":"","sources":["../../src/compaction/verification.ts"],"names":[],"mappings":"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","sourcesContent":["import type { CompactionFacts } from \"./extraction.ts\";\n\nexport interface VerificationFailure {\n\tcheck: string;\n\tdetail: string;\n}\n\nexport interface VerificationReport {\n\tok: boolean;\n\tfailures: VerificationFailure[];\n}\n\nexport const FILES_READ_RECALL_THRESHOLD = 0.8;\nexport const ACTIVE_TASK_CONTAINMENT_THRESHOLD = 0.9;\nexport const MANDATORY_RULES_RECALL_THRESHOLD = 0.7;\nexport const CANCELLED_WORK_DROPPED_THRESHOLD = 0.1;\nexport const ACTIONS_OVERLAP_THRESHOLD = 0.5;\n\nconst SECTION_FILES = \"files\";\nconst SECTION_DONE = \"done\";\nconst SECTION_ACTIVE_TASK = \"active task\";\nconst SECTION_MANDATORY_RULES = \"mandatory rules\";\n\nexport function verifySummary(summary: string, facts: CompactionFacts): VerificationReport {\n\tif (factsAreEmpty(facts)) {\n\t\treturn { ok: true, failures: [] };\n\t}\n\n\tconst sections = extractSections(summary);\n\tconst failures: VerificationFailure[] = [];\n\tconst filesSection = sections[SECTION_FILES] ?? \"\";\n\tconst doneSection = sections[SECTION_DONE] ?? \"\";\n\tconst activeTaskSection = sections[SECTION_ACTIVE_TASK] ?? \"\";\n\tconst mandatoryRulesSection = sections[SECTION_MANDATORY_RULES] ?? \"\";\n\n\tconst modifiedFiles = facts.files.filter((file) => file.kind !== \"read\");\n\tconst missingModifiedFiles = modifiedFiles.map((file) => file.path).filter((path) => !filesSection.includes(path));\n\tif (missingModifiedFiles.length > 0) {\n\t\tfailures.push({\n\t\t\tcheck: \"files-modified-recall\",\n\t\t\tdetail: `Missing modified/created files in ## Files: ${missingModifiedFiles.join(\", \")}`,\n\t\t});\n\t}\n\n\tconst readPaths = facts.files.filter((file) => file.kind === \"read\").map((file) => file.path);\n\tif (readPaths.length > 0) {\n\t\tconst score = containment(tokenSet(readPaths.join(\"\\n\")), tokenSet(filesSection));\n\t\tif (score < FILES_READ_RECALL_THRESHOLD) {\n\t\t\tfailures.push({\n\t\t\t\tcheck: \"files-read-recall\",\n\t\t\t\tdetail: `Read file recall ${formatScore(score)} below ${FILES_READ_RECALL_THRESHOLD}`,\n\t\t\t});\n\t\t}\n\t}\n\n\tif (facts.activeTaskSource) {\n\t\tconst score = containment(tokenSet(facts.activeTaskSource), tokenSet(activeTaskSection));\n\t\tif (score < ACTIVE_TASK_CONTAINMENT_THRESHOLD) {\n\t\t\tfailures.push({\n\t\t\t\tcheck: \"active-task-containment\",\n\t\t\t\tdetail: `Active task containment ${formatScore(score)} below ${ACTIVE_TASK_CONTAINMENT_THRESHOLD}`,\n\t\t\t});\n\t\t}\n\t}\n\n\tfor (const prohibition of facts.prohibitions) {\n\t\tconst score = containment(tokenSet(prohibition), tokenSet(mandatoryRulesSection));\n\t\tif (score < MANDATORY_RULES_RECALL_THRESHOLD) {\n\t\t\tfailures.push({\n\t\t\t\tcheck: \"mandatory-rules-recall\",\n\t\t\t\tdetail: `Missing mandatory rule: ${prohibition}`,\n\t\t\t});\n\t\t}\n\t}\n\n\tif (facts.cancelledText) {\n\t\tconst summaryOutsideMandatoryRules = removeSection(summary, SECTION_MANDATORY_RULES);\n\t\tconst score = containment(tokenSet(facts.cancelledText), tokenSet(summaryOutsideMandatoryRules));\n\t\tif (score > CANCELLED_WORK_DROPPED_THRESHOLD) {\n\t\t\tfailures.push({\n\t\t\t\tcheck: \"cancelled-work-dropped\",\n\t\t\t\tdetail: `Cancelled work leakage ${formatScore(score)} above ${CANCELLED_WORK_DROPPED_THRESHOLD}`,\n\t\t\t});\n\t\t}\n\t}\n\n\tif (facts.actions.length > 0) {\n\t\tconst score = jaccard(tokenSet(facts.actions.join(\"\\n\")), tokenSet(doneSection));\n\t\tif (score < ACTIONS_OVERLAP_THRESHOLD) {\n\t\t\tfailures.push({\n\t\t\t\tcheck: \"actions-overlap\",\n\t\t\t\tdetail: `Done/actions Jaccard ${formatScore(score)} below ${ACTIONS_OVERLAP_THRESHOLD}`,\n\t\t\t});\n\t\t}\n\t}\n\n\treturn { ok: failures.length === 0, failures };\n}\n\nexport function buildRetryPrompt(report: VerificationReport, previousAttempt?: string): string {\n\tconst failures = report.failures.map((failure) => `${failure.check}: ${failure.detail}`).join(\"; \");\n\tconst previous = previousAttempt ? `\\n\\n<previous-attempt>\\n${previousAttempt}\\n</previous-attempt>` : \"\";\n\treturn `Your previous checkpoint failed verification: ${failures}. Fix ONLY these omissions.${previous}`;\n}\n\nexport function tokenSet(text: string): Set<string> {\n\treturn new Set(\n\t\ttext\n\t\t\t.toLowerCase()\n\t\t\t.split(/[^a-z0-9_./-]+/)\n\t\t\t.map((token) => token.trim())\n\t\t\t.filter((token) => token.length >= 3),\n\t);\n}\n\nexport function containment(needle: Set<string>, hay: Set<string>): number {\n\tif (needle.size === 0) {\n\t\treturn 1;\n\t}\n\tlet hits = 0;\n\tfor (const token of needle) {\n\t\tif (hay.has(token)) {\n\t\t\thits += 1;\n\t\t}\n\t}\n\treturn hits / needle.size;\n}\n\nexport function jaccard(a: Set<string>, b: Set<string>): number {\n\tif (a.size === 0 && b.size === 0) {\n\t\treturn 1;\n\t}\n\tlet intersection = 0;\n\tfor (const token of a) {\n\t\tif (b.has(token)) {\n\t\t\tintersection += 1;\n\t\t}\n\t}\n\tconst union = new Set([...a, ...b]).size;\n\treturn union === 0 ? 1 : intersection / union;\n}\n\nfunction factsAreEmpty(facts: CompactionFacts): boolean {\n\treturn (\n\t\tfacts.files.length === 0 &&\n\t\tfacts.actions.length === 0 &&\n\t\tfacts.prohibitions.length === 0 &&\n\t\tfacts.cancelledText === \"\" &&\n\t\tfacts.activeTaskSource === \"\"\n\t);\n}\n\nfunction extractSections(summary: string): Record<string, string> {\n\tconst sections: Record<string, string> = {};\n\tlet current: string | undefined;\n\tlet bucket: string[] = [];\n\n\tconst flush = (): void => {\n\t\tif (current) {\n\t\t\tsections[current] = bucket.join(\"\\n\").trim();\n\t\t}\n\t\tbucket = [];\n\t};\n\n\tfor (const line of summary.split(/\\r?\\n/)) {\n\t\tconst match = /^(?:##|###)\\s+(.+?)\\s*$/.exec(line);\n\t\tif (match) {\n\t\t\tflush();\n\t\t\tcurrent = normalizeHeading(match[1]);\n\t\t\tcontinue;\n\t\t}\n\t\tif (current) {\n\t\t\tbucket.push(line);\n\t\t}\n\t}\n\tflush();\n\treturn sections;\n}\n\nfunction removeSection(summary: string, heading: string): string {\n\tconst normalizedHeading = normalizeHeading(heading);\n\tconst kept: string[] = [];\n\tlet skipping = false;\n\tfor (const line of summary.split(/\\r?\\n/)) {\n\t\tconst match = /^(?:##|###)\\s+(.+?)\\s*$/.exec(line);\n\t\tif (match) {\n\t\t\tskipping = normalizeHeading(match[1]) === normalizedHeading;\n\t\t\tif (skipping) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tif (!skipping) {\n\t\t\tkept.push(line);\n\t\t}\n\t}\n\treturn kept.join(\"\\n\");\n}\n\nfunction normalizeHeading(heading: string): string {\n\treturn heading.trim().toLowerCase();\n}\n\nfunction formatScore(score: number): string {\n\treturn score.toFixed(2);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"verification.js","sourceRoot":"","sources":["../../src/compaction/verification.ts"],"names":[],"mappings":"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","sourcesContent":["import type { CompactionFacts } from \"./extraction.ts\";\n\nexport interface VerificationFailure {\n\tcheck: string;\n\tdetail: string;\n}\n\nexport interface VerificationReport {\n\tok: boolean;\n\tfailures: VerificationFailure[];\n}\n\nexport const FILES_READ_RECALL_THRESHOLD = 0.8;\nexport const ACTIVE_TASK_CONTAINMENT_THRESHOLD = 0.9;\nexport const MANDATORY_RULES_RECALL_THRESHOLD = 0.7;\nexport const CANCELLED_WORK_DROPPED_THRESHOLD = 0.1;\nexport const ACTIONS_RECALL_THRESHOLD = 0.6;\n\nconst SECTION_FILES = \"files\";\nconst SECTION_DONE = \"done\";\nconst SECTION_ACTIVE_TASK = \"active task\";\nconst SECTION_MANDATORY_RULES = \"mandatory rules\";\n\nexport function verifySummary(summary: string, facts: CompactionFacts): VerificationReport {\n\tif (factsAreEmpty(facts)) {\n\t\treturn { ok: true, failures: [] };\n\t}\n\n\tconst sections = extractSections(summary);\n\tconst failures: VerificationFailure[] = [];\n\tconst filesSection = sections[SECTION_FILES] ?? \"\";\n\tconst doneSection = sections[SECTION_DONE] ?? \"\";\n\tconst activeTaskSection = sections[SECTION_ACTIVE_TASK] ?? \"\";\n\tconst mandatoryRulesSection = sections[SECTION_MANDATORY_RULES] ?? \"\";\n\n\tconst modifiedFiles = facts.files.filter((file) => file.kind !== \"read\");\n\tconst missingModifiedFiles = modifiedFiles.map((file) => file.path).filter((path) => !filesSection.includes(path));\n\tif (missingModifiedFiles.length > 0) {\n\t\tfailures.push({\n\t\t\tcheck: \"files-modified-recall\",\n\t\t\tdetail: `Missing modified/created files in ## Files: ${missingModifiedFiles.join(\", \")}`,\n\t\t});\n\t}\n\n\tconst readPaths = facts.files.filter((file) => file.kind === \"read\").map((file) => file.path);\n\tif (readPaths.length > 0) {\n\t\tconst score = containment(tokenSet(readPaths.join(\"\\n\")), tokenSet(filesSection));\n\t\tif (score < FILES_READ_RECALL_THRESHOLD) {\n\t\t\tfailures.push({\n\t\t\t\tcheck: \"files-read-recall\",\n\t\t\t\tdetail: `Read file recall ${formatScore(score)} below ${FILES_READ_RECALL_THRESHOLD}`,\n\t\t\t});\n\t\t}\n\t}\n\n\tif (facts.activeTaskSource) {\n\t\tconst score = containment(tokenSet(facts.activeTaskSource), tokenSet(activeTaskSection));\n\t\tif (score < ACTIVE_TASK_CONTAINMENT_THRESHOLD) {\n\t\t\tfailures.push({\n\t\t\t\tcheck: \"active-task-containment\",\n\t\t\t\tdetail: `Active task containment ${formatScore(score)} below ${ACTIVE_TASK_CONTAINMENT_THRESHOLD}`,\n\t\t\t});\n\t\t}\n\t}\n\n\tfor (const prohibition of facts.prohibitions) {\n\t\tconst score = containment(tokenSet(prohibition), tokenSet(mandatoryRulesSection));\n\t\tif (score < MANDATORY_RULES_RECALL_THRESHOLD) {\n\t\t\tfailures.push({\n\t\t\t\tcheck: \"mandatory-rules-recall\",\n\t\t\t\tdetail: `Missing mandatory rule: ${prohibition}`,\n\t\t\t});\n\t\t}\n\t}\n\n\tif (facts.cancelledText) {\n\t\tconst summaryOutsideMandatoryRules = removeSection(summary, SECTION_MANDATORY_RULES);\n\t\t// File paths from the facts are REQUIRED elsewhere (files-modified/read-recall demand them\n\t\t// in ## Files), so counting them as cancelled-work leakage would make the two gates\n\t\t// unsatisfiable together whenever a reversal message references a touched file.\n\t\tconst factPathTokens = tokenSet(facts.files.map((file) => file.path).join(\"\\n\"));\n\t\tconst cancelledTokens = new Set([...tokenSet(facts.cancelledText)].filter((token) => !factPathTokens.has(token)));\n\t\tconst score = containment(cancelledTokens, tokenSet(summaryOutsideMandatoryRules));\n\t\tif (score > CANCELLED_WORK_DROPPED_THRESHOLD) {\n\t\t\tfailures.push({\n\t\t\t\tcheck: \"cancelled-work-dropped\",\n\t\t\t\tdetail: `Cancelled work leakage ${formatScore(score)} above ${CANCELLED_WORK_DROPPED_THRESHOLD}`,\n\t\t\t});\n\t\t}\n\t}\n\n\tif (facts.actions.length > 0) {\n\t\t// Asymmetric on purpose: the update path carries prior ## Done items forward (bounded), so a\n\t\t// symmetric overlap metric would punish faithful carry-over — the gate demands only that the\n\t\t// NEW span's actions are recalled in ## Done, however much history rides alongside them.\n\t\tconst score = containment(tokenSet(facts.actions.join(\"\\n\")), tokenSet(doneSection));\n\t\tif (score < ACTIONS_RECALL_THRESHOLD) {\n\t\t\tfailures.push({\n\t\t\t\tcheck: \"actions-recall\",\n\t\t\t\tdetail: `New-action recall in ## Done ${formatScore(score)} below ${ACTIONS_RECALL_THRESHOLD}`,\n\t\t\t});\n\t\t}\n\t}\n\n\treturn { ok: failures.length === 0, failures };\n}\n\nexport function buildRetryPrompt(report: VerificationReport, previousAttempt?: string): string {\n\tconst failures = report.failures.map((failure) => `${failure.check}: ${failure.detail}`).join(\"; \");\n\tconst previous = previousAttempt ? `\\n\\n<previous-attempt>\\n${previousAttempt}\\n</previous-attempt>` : \"\";\n\treturn `Your previous checkpoint failed verification: ${failures}. Fix ONLY these omissions.${previous}`;\n}\n\nexport function tokenSet(text: string): Set<string> {\n\treturn new Set(\n\t\ttext\n\t\t\t.toLowerCase()\n\t\t\t.split(/[^a-z0-9_./-]+/)\n\t\t\t.map((token) => token.trim())\n\t\t\t.filter((token) => token.length >= 3),\n\t);\n}\n\nexport function containment(needle: Set<string>, hay: Set<string>): number {\n\tif (needle.size === 0) {\n\t\treturn 1;\n\t}\n\tlet hits = 0;\n\tfor (const token of needle) {\n\t\tif (hay.has(token)) {\n\t\t\thits += 1;\n\t\t}\n\t}\n\treturn hits / needle.size;\n}\n\nexport function jaccard(a: Set<string>, b: Set<string>): number {\n\tif (a.size === 0 && b.size === 0) {\n\t\treturn 1;\n\t}\n\tlet intersection = 0;\n\tfor (const token of a) {\n\t\tif (b.has(token)) {\n\t\t\tintersection += 1;\n\t\t}\n\t}\n\tconst union = new Set([...a, ...b]).size;\n\treturn union === 0 ? 1 : intersection / union;\n}\n\nfunction factsAreEmpty(facts: CompactionFacts): boolean {\n\treturn (\n\t\tfacts.files.length === 0 &&\n\t\tfacts.actions.length === 0 &&\n\t\tfacts.prohibitions.length === 0 &&\n\t\tfacts.cancelledText === \"\" &&\n\t\tfacts.activeTaskSource === \"\"\n\t);\n}\n\nfunction extractSections(summary: string): Record<string, string> {\n\tconst sections: Record<string, string> = {};\n\tlet current: string | undefined;\n\tlet bucket: string[] = [];\n\n\tconst flush = (): void => {\n\t\tif (current) {\n\t\t\tsections[current] = bucket.join(\"\\n\").trim();\n\t\t}\n\t\tbucket = [];\n\t};\n\n\tfor (const line of summary.split(/\\r?\\n/)) {\n\t\tconst match = /^(?:##|###)\\s+(.+?)\\s*$/.exec(line);\n\t\tif (match) {\n\t\t\tflush();\n\t\t\tcurrent = normalizeHeading(match[1]);\n\t\t\tcontinue;\n\t\t}\n\t\tif (current) {\n\t\t\tbucket.push(line);\n\t\t}\n\t}\n\tflush();\n\treturn sections;\n}\n\nfunction removeSection(summary: string, heading: string): string {\n\tconst normalizedHeading = normalizeHeading(heading);\n\tconst kept: string[] = [];\n\tlet skipping = false;\n\tfor (const line of summary.split(/\\r?\\n/)) {\n\t\tconst match = /^(?:##|###)\\s+(.+?)\\s*$/.exec(line);\n\t\tif (match) {\n\t\t\tskipping = normalizeHeading(match[1]) === normalizedHeading;\n\t\t\tif (skipping) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tif (!skipping) {\n\t\t\tkept.push(line);\n\t\t}\n\t}\n\treturn kept.join(\"\\n\");\n}\n\nfunction normalizeHeading(heading: string): string {\n\treturn heading.trim().toLowerCase();\n}\n\nfunction formatScore(score: number): string {\n\treturn score.toFixed(2);\n}\n"]}
|
|
@@ -2,8 +2,10 @@ import type { FailureReason } from "./classifier.ts";
|
|
|
2
2
|
export interface ProviderSignature {
|
|
3
3
|
reason: FailureReason;
|
|
4
4
|
pattern: RegExp;
|
|
5
|
-
/**
|
|
5
|
+
/** Evidence citation: sdk package+version+file, corpus capture, or adapter file:line. */
|
|
6
6
|
source: string;
|
|
7
|
+
/** True when the SDK-rendered fixture uses a vendor body shape that still awaits corpus confirmation. */
|
|
8
|
+
provisional?: boolean;
|
|
7
9
|
}
|
|
8
10
|
/** Provider-thrown-message signatures checked before the generic ladder. */
|
|
9
11
|
export declare const PROVIDER_FAILURE_SIGNATURES: Record<string, readonly ProviderSignature[]>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider-signatures.d.ts","sourceRoot":"","sources":["../../src/reliability/provider-signatures.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErD,MAAM,WAAW,iBAAiB;IACjC,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,
|
|
1
|
+
{"version":3,"file":"provider-signatures.d.ts","sourceRoot":"","sources":["../../src/reliability/provider-signatures.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErD,MAAM,WAAW,iBAAiB;IACjC,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,yFAAyF;IACzF,MAAM,EAAE,MAAM,CAAC;IACf,yGAAyG;IACzG,WAAW,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,4EAA4E;AAC5E,eAAO,MAAM,2BAA2B,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,iBAAiB,EAAE,CA+BpF,CAAC","sourcesContent":["import type { FailureReason } from \"./classifier.ts\";\n\nexport interface ProviderSignature {\n\treason: FailureReason;\n\tpattern: RegExp;\n\t/** Evidence citation: sdk package+version+file, corpus capture, or adapter file:line. */\n\tsource: string;\n\t/** True when the SDK-rendered fixture uses a vendor body shape that still awaits corpus confirmation. */\n\tprovisional?: boolean;\n}\n\n/** Provider-thrown-message signatures checked before the generic ladder. */\nexport const PROVIDER_FAILURE_SIGNATURES: Record<string, readonly ProviderSignature[]> = {\n\tanthropic: [\n\t\t{\n\t\t\treason: \"billing_or_quota\",\n\t\t\tpattern: /credit balance is too low/i,\n\t\t\tsource: \"sdk:@anthropic-ai/sdk@0.91.1 node_modules/@anthropic-ai/sdk/core/error.js\",\n\t\t},\n\t],\n\tmistral: [\n\t\t{\n\t\t\treason: \"billing_or_quota\",\n\t\t\tpattern: /insufficient credits/i,\n\t\t\tsource: \"sdk:@mistralai/mistralai@2.2.1 node_modules/@mistralai/mistralai/esm/models/errors/sdkerror.js\",\n\t\t\tprovisional: true,\n\t\t},\n\t],\n\topenrouter: [\n\t\t{\n\t\t\treason: \"billing_or_quota\",\n\t\t\tpattern: /insufficient credits/i,\n\t\t\tsource: \"sdk:openai@6.26.0 node_modules/openai/core/error.js\",\n\t\t\tprovisional: true,\n\t\t},\n\t],\n\t\"openai-codex\": [\n\t\t{\n\t\t\treason: \"billing_or_quota\",\n\t\t\tpattern: /You have hit your ChatGPT usage limit/i,\n\t\t\tsource: \"packages/ai/src/providers/openai-codex-responses.ts:1402\",\n\t\t},\n\t],\n};\n"]}
|
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
/** Provider-thrown-message signatures checked before the generic ladder. */
|
|
2
2
|
export const PROVIDER_FAILURE_SIGNATURES = {
|
|
3
|
+
anthropic: [
|
|
4
|
+
{
|
|
5
|
+
reason: "billing_or_quota",
|
|
6
|
+
pattern: /credit balance is too low/i,
|
|
7
|
+
source: "sdk:@anthropic-ai/sdk@0.91.1 node_modules/@anthropic-ai/sdk/core/error.js",
|
|
8
|
+
},
|
|
9
|
+
],
|
|
10
|
+
mistral: [
|
|
11
|
+
{
|
|
12
|
+
reason: "billing_or_quota",
|
|
13
|
+
pattern: /insufficient credits/i,
|
|
14
|
+
source: "sdk:@mistralai/mistralai@2.2.1 node_modules/@mistralai/mistralai/esm/models/errors/sdkerror.js",
|
|
15
|
+
provisional: true,
|
|
16
|
+
},
|
|
17
|
+
],
|
|
18
|
+
openrouter: [
|
|
19
|
+
{
|
|
20
|
+
reason: "billing_or_quota",
|
|
21
|
+
pattern: /insufficient credits/i,
|
|
22
|
+
source: "sdk:openai@6.26.0 node_modules/openai/core/error.js",
|
|
23
|
+
provisional: true,
|
|
24
|
+
},
|
|
25
|
+
],
|
|
3
26
|
"openai-codex": [
|
|
4
27
|
{
|
|
5
28
|
reason: "billing_or_quota",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider-signatures.js","sourceRoot":"","sources":["../../src/reliability/provider-signatures.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"provider-signatures.js","sourceRoot":"","sources":["../../src/reliability/provider-signatures.ts"],"names":[],"mappings":"AAWA,4EAA4E;AAC5E,MAAM,CAAC,MAAM,2BAA2B,GAAiD;IACxF,SAAS,EAAE;QACV;YACC,MAAM,EAAE,kBAAkB;YAC1B,OAAO,EAAE,4BAA4B;YACrC,MAAM,EAAE,2EAA2E;SACnF;KACD;IACD,OAAO,EAAE;QACR;YACC,MAAM,EAAE,kBAAkB;YAC1B,OAAO,EAAE,uBAAuB;YAChC,MAAM,EAAE,gGAAgG;YACxG,WAAW,EAAE,IAAI;SACjB;KACD;IACD,UAAU,EAAE;QACX;YACC,MAAM,EAAE,kBAAkB;YAC1B,OAAO,EAAE,uBAAuB;YAChC,MAAM,EAAE,qDAAqD;YAC7D,WAAW,EAAE,IAAI;SACjB;KACD;IACD,cAAc,EAAE;QACf;YACC,MAAM,EAAE,kBAAkB;YAC1B,OAAO,EAAE,wCAAwC;YACjD,MAAM,EAAE,0DAA0D;SAClE;KACD;CACD,CAAC","sourcesContent":["import type { FailureReason } from \"./classifier.ts\";\n\nexport interface ProviderSignature {\n\treason: FailureReason;\n\tpattern: RegExp;\n\t/** Evidence citation: sdk package+version+file, corpus capture, or adapter file:line. */\n\tsource: string;\n\t/** True when the SDK-rendered fixture uses a vendor body shape that still awaits corpus confirmation. */\n\tprovisional?: boolean;\n}\n\n/** Provider-thrown-message signatures checked before the generic ladder. */\nexport const PROVIDER_FAILURE_SIGNATURES: Record<string, readonly ProviderSignature[]> = {\n\tanthropic: [\n\t\t{\n\t\t\treason: \"billing_or_quota\",\n\t\t\tpattern: /credit balance is too low/i,\n\t\t\tsource: \"sdk:@anthropic-ai/sdk@0.91.1 node_modules/@anthropic-ai/sdk/core/error.js\",\n\t\t},\n\t],\n\tmistral: [\n\t\t{\n\t\t\treason: \"billing_or_quota\",\n\t\t\tpattern: /insufficient credits/i,\n\t\t\tsource: \"sdk:@mistralai/mistralai@2.2.1 node_modules/@mistralai/mistralai/esm/models/errors/sdkerror.js\",\n\t\t\tprovisional: true,\n\t\t},\n\t],\n\topenrouter: [\n\t\t{\n\t\t\treason: \"billing_or_quota\",\n\t\t\tpattern: /insufficient credits/i,\n\t\t\tsource: \"sdk:openai@6.26.0 node_modules/openai/core/error.js\",\n\t\t\tprovisional: true,\n\t\t},\n\t],\n\t\"openai-codex\": [\n\t\t{\n\t\t\treason: \"billing_or_quota\",\n\t\t\tpattern: /You have hit your ChatGPT usage limit/i,\n\t\t\tsource: \"packages/ai/src/providers/openai-codex-responses.ts:1402\",\n\t\t},\n\t],\n};\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retry-controller.d.ts","sourceRoot":"","sources":["../../src/reliability/retry-controller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,KAAK,gBAAgB,EAAqB,MAAM,mBAAmB,CAAC;AAC7E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,EAAuB,KAAK,WAAW,EAAkB,MAAM,YAAY,CAAC;AAEnF,0FAA0F;AAC1F,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,YAAY,EAAE,CAAA;KAAE,CAAC;CAC7C;AAED,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC3B,YAAY,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI,CAAC;IACzC,UAAU,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAAC;CACrC;AAED,iGAAiG;AACjG,MAAM,MAAM,qBAAqB,GAAG,WAAW,GAAG;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AAEvE,qBAAa,eAAe;IAC3B,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,gBAAgB,CAA8B;IACtD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA8B;IACxD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAe;IAEhD,YACC,KAAK,EAAE,UAAU,EACjB,SAAS,EAAE,MAAM,qBAAqB,EACtC,MAAM,EAAE,WAAW,EACnB,gBAAgB,EAAE,MAAM,MAAM,EAM9B;IAED,0EAA0E;IAC1E,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,+FAA+F;IAC/F,IAAI,UAAU,IAAI,OAAO,CAExB;IAED,oGAAkG;IAClG,KAAK,IAAI,IAAI,CAEZ;IAED,8EAA8E;IAC9E,KAAK,IAAI,IAAI,CAEZ;IAED;;;;OAIG;IACG,YAAY,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC,CAyD9D;CACD","sourcesContent":["/**\n * Host-agnostic auto-retry driver.\n *\n * Owns the retry attempt counter and the abortable backoff for one agent run. It reads the\n * failure verdict from {@link classifyFailure} (fed a host-computed context-overflow flag), so\n * billing/auth are terminal and overflow routes to compaction — never to a pointless retry.\n * The controller only ever touches `agent.state.messages` (it drops the trailing assistant error\n * before retrying); durable history stays the host session's responsibility.\n *\n * Ported from AgentSession._prepareRetry / _isRetryableError so the exact event ordering and\n * exhaustion semantics carry over: the retry window is marked active (isRetrying) before the\n * start event fires, so prompts arriving inside start handlers queue as steering instead of\n * racing the retry continuation.\n */\n\nimport { type AssistantMessage, isContextOverflow } from \"@caupulican/pi-ai\";\nimport type { AgentMessage } from \"../types.ts\";\nimport { classifyFailure } from \"./classifier.ts\";\nimport { computeRetryDelayMs, type RetryPolicy, sleepAbortable } from \"./retry.ts\";\n\n/** The slice of an Agent the retry driver reads and mutates: just the live transcript. */\nexport interface RetryAgent {\n\treadonly state: { messages: AgentMessage[] };\n}\n\nexport interface RetryStartInfo {\n\tattempt: number;\n\tmaxAttempts: number;\n\tdelayMs: number;\n\terrorMessage: string;\n}\n\nexport interface RetryEndInfo {\n\tsuccess: boolean;\n\tattempt: number;\n\tfinalError?: string;\n}\n\nexport interface RetryEvents {\n\tonRetryStart(info: RetryStartInfo): void;\n\tonRetryEnd(info: RetryEndInfo): void;\n}\n\n/** Runtime retry policy: the backoff shape plus the on/off switch the host resolves per call. */\nexport type RetryControllerPolicy = RetryPolicy & { enabled: boolean };\n\nexport class RetryController {\n\tprivate _attempt = 0;\n\tprivate _abortController: AbortController | undefined;\n\tprivate readonly agent: RetryAgent;\n\tprivate readonly getPolicy: () => RetryControllerPolicy;\n\tprivate readonly events: RetryEvents;\n\tprivate readonly getContextWindow: () => number;\n\n\tconstructor(\n\t\tagent: RetryAgent,\n\t\tgetPolicy: () => RetryControllerPolicy,\n\t\tevents: RetryEvents,\n\t\tgetContextWindow: () => number,\n\t) {\n\t\tthis.agent = agent;\n\t\tthis.getPolicy = getPolicy;\n\t\tthis.events = events;\n\t\tthis.getContextWindow = getContextWindow;\n\t}\n\n\t/** Completed retry attempts for the current run (0 when not retrying). */\n\tget attempt(): number {\n\t\treturn this._attempt;\n\t}\n\n\t/** True from the instant onRetryStart fires until the backoff sleep resolves or is aborted. */\n\tget isRetrying(): boolean {\n\t\treturn this._abortController !== undefined;\n\t}\n\n\t/** Clear the attempt counter — the host calls this after a successful turn or a final failure. */\n\treset(): void {\n\t\tthis._attempt = 0;\n\t}\n\n\t/** Cancel an in-progress backoff; the pending prepareRetry resolves false. */\n\tabort(): void {\n\t\tthis._abortController?.abort();\n\t}\n\n\t/**\n\t * Classify `message`; if it is retryable and attempts remain, drop the trailing assistant\n\t * error from agent state, emit the start event, and wait out the backoff (abortable).\n\t * @returns true if the caller should continue the agent, false otherwise.\n\t */\n\tasync prepareRetry(message: AssistantMessage): Promise<boolean> {\n\t\tconst policy = this.getPolicy();\n\t\tif (!policy.enabled) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// The classifier is the single source of the retry verdict: context overflow (host-computed\n\t\t// from the live window) routes to compaction, billing/auth are terminal, transient failures retry.\n\t\tconst classified = classifyFailure({\n\t\t\tmessage: message.errorMessage ?? \"\",\n\t\t\tcontextOverflow: isContextOverflow(message, this.getContextWindow()),\n\t\t\tprovider: message.provider,\n\t\t});\n\t\tif (!classified.retryable) {\n\t\t\treturn false;\n\t\t}\n\n\t\tthis._attempt++;\n\t\tif (this._attempt > policy.maxAttempts) {\n\t\t\t// Preserve the completed attempt count so the host can emit the final failure.\n\t\t\tthis._attempt--;\n\t\t\treturn false;\n\t\t}\n\n\t\tconst delayMs = computeRetryDelayMs(policy, this._attempt);\n\n\t\t// The retry window counts as active work from the instant listeners hear about it:\n\t\t// isRetrying must already be true inside onRetryStart handlers so prompts arriving there\n\t\t// queue as steering instead of racing the retry continuation.\n\t\tthis._abortController = new AbortController();\n\n\t\tthis.events.onRetryStart({\n\t\t\tattempt: this._attempt,\n\t\t\tmaxAttempts: policy.maxAttempts,\n\t\t\tdelayMs,\n\t\t\terrorMessage: message.errorMessage || \"Unknown error\",\n\t\t});\n\n\t\t// Remove the trailing assistant error from live agent state (the host session keeps it in history).\n\t\tconst messages = this.agent.state.messages;\n\t\tif (messages.length > 0 && messages[messages.length - 1].role === \"assistant\") {\n\t\t\tthis.agent.state.messages = messages.slice(0, -1);\n\t\t}\n\n\t\ttry {\n\t\t\tawait sleepAbortable(delayMs, this._abortController.signal);\n\t\t} catch {\n\t\t\t// Aborted mid-backoff: report the cancellation and reset so the next turn starts clean.\n\t\t\tconst attempt = this._attempt;\n\t\t\tthis._attempt = 0;\n\t\t\tthis.events.onRetryEnd({ success: false, attempt, finalError: \"Retry cancelled\" });\n\t\t\treturn false;\n\t\t} finally {\n\t\t\tthis._abortController = undefined;\n\t\t}\n\n\t\treturn true;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"retry-controller.d.ts","sourceRoot":"","sources":["../../src/reliability/retry-controller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,KAAK,gBAAgB,EAAqB,MAAM,mBAAmB,CAAC;AAC7E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,EAAuB,KAAK,WAAW,EAAkB,MAAM,YAAY,CAAC;AAEnF,0FAA0F;AAC1F,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,YAAY,EAAE,CAAA;KAAE,CAAC;CAC7C;AAED,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC3B,YAAY,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI,CAAC;IACzC,UAAU,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAAC;CACrC;AAED,iGAAiG;AACjG,MAAM,MAAM,qBAAqB,GAAG,WAAW,GAAG;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AAEvE,qBAAa,eAAe;IAC3B,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,gBAAgB,CAA8B;IACtD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA8B;IACxD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAe;IAEhD,YACC,KAAK,EAAE,UAAU,EACjB,SAAS,EAAE,MAAM,qBAAqB,EACtC,MAAM,EAAE,WAAW,EACnB,gBAAgB,EAAE,MAAM,MAAM,EAM9B;IAED,0EAA0E;IAC1E,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,+FAA+F;IAC/F,IAAI,UAAU,IAAI,OAAO,CAExB;IAED,oGAAkG;IAClG,KAAK,IAAI,IAAI,CAEZ;IAED,8EAA8E;IAC9E,KAAK,IAAI,IAAI,CAEZ;IAED;;;;OAIG;IACG,YAAY,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC,CAyD9D;CACD","sourcesContent":["/**\n * Host-agnostic auto-retry driver.\n *\n * Owns the retry attempt counter and the abortable backoff for one agent run. It reads the\n * failure verdict from {@link classifyFailure} (fed a host-computed context-overflow flag), so\n * billing/auth are terminal and overflow routes to compaction — never to a pointless retry.\n * The controller only ever touches `agent.state.messages` (it drops the trailing assistant error\n * before retrying); durable history stays the host session's responsibility.\n *\n * Ported from AgentSession._prepareRetry / _isRetryableError so the exact event ordering and\n * exhaustion semantics carry over: the retry window is marked active (isRetrying) before the\n * start event fires, so prompts arriving inside start handlers queue as steering instead of\n * racing the retry continuation.\n */\n\nimport { type AssistantMessage, isContextOverflow } from \"@caupulican/pi-ai\";\nimport type { AgentMessage } from \"../types.ts\";\nimport { classifyFailure } from \"./classifier.ts\";\nimport { computeRetryDelayMs, type RetryPolicy, sleepAbortable } from \"./retry.ts\";\n\n/** The slice of an Agent the retry driver reads and mutates: just the live transcript. */\nexport interface RetryAgent {\n\treadonly state: { messages: AgentMessage[] };\n}\n\nexport interface RetryStartInfo {\n\tattempt: number;\n\tmaxAttempts: number;\n\tdelayMs: number;\n\terrorMessage: string;\n}\n\nexport interface RetryEndInfo {\n\tsuccess: boolean;\n\tattempt: number;\n\tfinalError?: string;\n}\n\nexport interface RetryEvents {\n\tonRetryStart(info: RetryStartInfo): void;\n\tonRetryEnd(info: RetryEndInfo): void;\n}\n\n/** Runtime retry policy: the backoff shape plus the on/off switch the host resolves per call. */\nexport type RetryControllerPolicy = RetryPolicy & { enabled: boolean };\n\nexport class RetryController {\n\tprivate _attempt = 0;\n\tprivate _abortController: AbortController | undefined;\n\tprivate readonly agent: RetryAgent;\n\tprivate readonly getPolicy: () => RetryControllerPolicy;\n\tprivate readonly events: RetryEvents;\n\tprivate readonly getContextWindow: () => number;\n\n\tconstructor(\n\t\tagent: RetryAgent,\n\t\tgetPolicy: () => RetryControllerPolicy,\n\t\tevents: RetryEvents,\n\t\tgetContextWindow: () => number,\n\t) {\n\t\tthis.agent = agent;\n\t\tthis.getPolicy = getPolicy;\n\t\tthis.events = events;\n\t\tthis.getContextWindow = getContextWindow;\n\t}\n\n\t/** Completed retry attempts for the current run (0 when not retrying). */\n\tget attempt(): number {\n\t\treturn this._attempt;\n\t}\n\n\t/** True from the instant onRetryStart fires until the backoff sleep resolves or is aborted. */\n\tget isRetrying(): boolean {\n\t\treturn this._abortController !== undefined;\n\t}\n\n\t/** Clear the attempt counter — the host calls this after a successful turn or a final failure. */\n\treset(): void {\n\t\tthis._attempt = 0;\n\t}\n\n\t/** Cancel an in-progress backoff; the pending prepareRetry resolves false. */\n\tabort(): void {\n\t\tthis._abortController?.abort();\n\t}\n\n\t/**\n\t * Classify `message`; if it is retryable and attempts remain, drop the trailing assistant\n\t * error from agent state, emit the start event, and wait out the backoff (abortable).\n\t * @returns true if the caller should continue the agent, false otherwise.\n\t */\n\tasync prepareRetry(message: AssistantMessage): Promise<boolean> {\n\t\tconst policy = this.getPolicy();\n\t\tif (!policy.enabled) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// The classifier is the single source of the retry verdict: context overflow (host-computed\n\t\t// from the live window) routes to compaction, billing/auth are terminal, transient failures retry.\n\t\tconst classified = classifyFailure({\n\t\t\tmessage: message.errorMessage ?? \"\",\n\t\t\tcontextOverflow: isContextOverflow(message, this.getContextWindow()),\n\t\t\tprovider: message.provider,\n\t\t});\n\t\tif (!classified.retryable) {\n\t\t\treturn false;\n\t\t}\n\n\t\tthis._attempt++;\n\t\tif (this._attempt > policy.maxAttempts) {\n\t\t\t// Preserve the completed attempt count so the host can emit the final failure.\n\t\t\tthis._attempt--;\n\t\t\treturn false;\n\t\t}\n\n\t\tconst delayMs = computeRetryDelayMs(policy, this._attempt, { retryAfterMs: classified.retryAfterMs });\n\n\t\t// The retry window counts as active work from the instant listeners hear about it:\n\t\t// isRetrying must already be true inside onRetryStart handlers so prompts arriving there\n\t\t// queue as steering instead of racing the retry continuation.\n\t\tthis._abortController = new AbortController();\n\n\t\tthis.events.onRetryStart({\n\t\t\tattempt: this._attempt,\n\t\t\tmaxAttempts: policy.maxAttempts,\n\t\t\tdelayMs,\n\t\t\terrorMessage: message.errorMessage || \"Unknown error\",\n\t\t});\n\n\t\t// Remove the trailing assistant error from live agent state (the host session keeps it in history).\n\t\tconst messages = this.agent.state.messages;\n\t\tif (messages.length > 0 && messages[messages.length - 1].role === \"assistant\") {\n\t\t\tthis.agent.state.messages = messages.slice(0, -1);\n\t\t}\n\n\t\ttry {\n\t\t\tawait sleepAbortable(delayMs, this._abortController.signal);\n\t\t} catch {\n\t\t\t// Aborted mid-backoff: report the cancellation and reset so the next turn starts clean.\n\t\t\tconst attempt = this._attempt;\n\t\t\tthis._attempt = 0;\n\t\t\tthis.events.onRetryEnd({ success: false, attempt, finalError: \"Retry cancelled\" });\n\t\t\treturn false;\n\t\t} finally {\n\t\t\tthis._abortController = undefined;\n\t\t}\n\n\t\treturn true;\n\t}\n}\n"]}
|
|
@@ -70,7 +70,7 @@ export class RetryController {
|
|
|
70
70
|
this._attempt--;
|
|
71
71
|
return false;
|
|
72
72
|
}
|
|
73
|
-
const delayMs = computeRetryDelayMs(policy, this._attempt);
|
|
73
|
+
const delayMs = computeRetryDelayMs(policy, this._attempt, { retryAfterMs: classified.retryAfterMs });
|
|
74
74
|
// The retry window counts as active work from the instant listeners hear about it:
|
|
75
75
|
// isRetrying must already be true inside onRetryStart handlers so prompts arriving there
|
|
76
76
|
// queue as steering instead of racing the retry continuation.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retry-controller.js","sourceRoot":"","sources":["../../src/reliability/retry-controller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAyB,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE7E,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAoB,cAAc,EAAE,MAAM,YAAY,CAAC;AA4BnF,MAAM,OAAO,eAAe;IACnB,QAAQ,GAAG,CAAC,CAAC;IACb,gBAAgB,CAA8B;IACrC,KAAK,CAAa;IAClB,SAAS,CAA8B;IACvC,MAAM,CAAc;IACpB,gBAAgB,CAAe;IAEhD,YACC,KAAiB,EACjB,SAAsC,EACtC,MAAmB,EACnB,gBAA8B,EAC7B;QACD,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,gBAAgB,GAAG,gBAAgB,CAAC;IAAA,CACzC;IAED,0EAA0E;IAC1E,IAAI,OAAO,GAAW;QACrB,OAAO,IAAI,CAAC,QAAQ,CAAC;IAAA,CACrB;IAED,+FAA+F;IAC/F,IAAI,UAAU,GAAY;QACzB,OAAO,IAAI,CAAC,gBAAgB,KAAK,SAAS,CAAC;IAAA,CAC3C;IAED,oGAAkG;IAClG,KAAK,GAAS;QACb,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;IAAA,CAClB;IAED,8EAA8E;IAC9E,KAAK,GAAS;QACb,IAAI,CAAC,gBAAgB,EAAE,KAAK,EAAE,CAAC;IAAA,CAC/B;IAED;;;;OAIG;IACH,KAAK,CAAC,YAAY,CAAC,OAAyB,EAAoB;QAC/D,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAChC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACrB,OAAO,KAAK,CAAC;QACd,CAAC;QAED,4FAA4F;QAC5F,mGAAmG;QACnG,MAAM,UAAU,GAAG,eAAe,CAAC;YAClC,OAAO,EAAE,OAAO,CAAC,YAAY,IAAI,EAAE;YACnC,eAAe,EAAE,iBAAiB,CAAC,OAAO,EAAE,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACpE,QAAQ,EAAE,OAAO,CAAC,QAAQ;SAC1B,CAAC,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC;YAC3B,OAAO,KAAK,CAAC;QACd,CAAC;QAED,IAAI,CAAC,QAAQ,EAAE,CAAC;QAChB,IAAI,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;YACxC,+EAA+E;YAC/E,IAAI,CAAC,QAAQ,EAAE,CAAC;YAChB,OAAO,KAAK,CAAC;QACd,CAAC;QAED,MAAM,OAAO,GAAG,mBAAmB,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"retry-controller.js","sourceRoot":"","sources":["../../src/reliability/retry-controller.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Host-agnostic auto-retry driver.\n *\n * Owns the retry attempt counter and the abortable backoff for one agent run. It reads the\n * failure verdict from {@link classifyFailure} (fed a host-computed context-overflow flag), so\n * billing/auth are terminal and overflow routes to compaction — never to a pointless retry.\n * The controller only ever touches `agent.state.messages` (it drops the trailing assistant error\n * before retrying); durable history stays the host session's responsibility.\n *\n * Ported from AgentSession._prepareRetry / _isRetryableError so the exact event ordering and\n * exhaustion semantics carry over: the retry window is marked active (isRetrying) before the\n * start event fires, so prompts arriving inside start handlers queue as steering instead of\n * racing the retry continuation.\n */\n\nimport { type AssistantMessage, isContextOverflow } from \"@caupulican/pi-ai\";\nimport type { AgentMessage } from \"../types.ts\";\nimport { classifyFailure } from \"./classifier.ts\";\nimport { computeRetryDelayMs, type RetryPolicy, sleepAbortable } from \"./retry.ts\";\n\n/** The slice of an Agent the retry driver reads and mutates: just the live transcript. */\nexport interface RetryAgent {\n\treadonly state: { messages: AgentMessage[] };\n}\n\nexport interface RetryStartInfo {\n\tattempt: number;\n\tmaxAttempts: number;\n\tdelayMs: number;\n\terrorMessage: string;\n}\n\nexport interface RetryEndInfo {\n\tsuccess: boolean;\n\tattempt: number;\n\tfinalError?: string;\n}\n\nexport interface RetryEvents {\n\tonRetryStart(info: RetryStartInfo): void;\n\tonRetryEnd(info: RetryEndInfo): void;\n}\n\n/** Runtime retry policy: the backoff shape plus the on/off switch the host resolves per call. */\nexport type RetryControllerPolicy = RetryPolicy & { enabled: boolean };\n\nexport class RetryController {\n\tprivate _attempt = 0;\n\tprivate _abortController: AbortController | undefined;\n\tprivate readonly agent: RetryAgent;\n\tprivate readonly getPolicy: () => RetryControllerPolicy;\n\tprivate readonly events: RetryEvents;\n\tprivate readonly getContextWindow: () => number;\n\n\tconstructor(\n\t\tagent: RetryAgent,\n\t\tgetPolicy: () => RetryControllerPolicy,\n\t\tevents: RetryEvents,\n\t\tgetContextWindow: () => number,\n\t) {\n\t\tthis.agent = agent;\n\t\tthis.getPolicy = getPolicy;\n\t\tthis.events = events;\n\t\tthis.getContextWindow = getContextWindow;\n\t}\n\n\t/** Completed retry attempts for the current run (0 when not retrying). */\n\tget attempt(): number {\n\t\treturn this._attempt;\n\t}\n\n\t/** True from the instant onRetryStart fires until the backoff sleep resolves or is aborted. */\n\tget isRetrying(): boolean {\n\t\treturn this._abortController !== undefined;\n\t}\n\n\t/** Clear the attempt counter — the host calls this after a successful turn or a final failure. */\n\treset(): void {\n\t\tthis._attempt = 0;\n\t}\n\n\t/** Cancel an in-progress backoff; the pending prepareRetry resolves false. */\n\tabort(): void {\n\t\tthis._abortController?.abort();\n\t}\n\n\t/**\n\t * Classify `message`; if it is retryable and attempts remain, drop the trailing assistant\n\t * error from agent state, emit the start event, and wait out the backoff (abortable).\n\t * @returns true if the caller should continue the agent, false otherwise.\n\t */\n\tasync prepareRetry(message: AssistantMessage): Promise<boolean> {\n\t\tconst policy = this.getPolicy();\n\t\tif (!policy.enabled) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// The classifier is the single source of the retry verdict: context overflow (host-computed\n\t\t// from the live window) routes to compaction, billing/auth are terminal, transient failures retry.\n\t\tconst classified = classifyFailure({\n\t\t\tmessage: message.errorMessage ?? \"\",\n\t\t\tcontextOverflow: isContextOverflow(message, this.getContextWindow()),\n\t\t\tprovider: message.provider,\n\t\t});\n\t\tif (!classified.retryable) {\n\t\t\treturn false;\n\t\t}\n\n\t\tthis._attempt++;\n\t\tif (this._attempt > policy.maxAttempts) {\n\t\t\t// Preserve the completed attempt count so the host can emit the final failure.\n\t\t\tthis._attempt--;\n\t\t\treturn false;\n\t\t}\n\n\t\tconst delayMs = computeRetryDelayMs(policy, this._attempt, { retryAfterMs: classified.retryAfterMs });\n\n\t\t// The retry window counts as active work from the instant listeners hear about it:\n\t\t// isRetrying must already be true inside onRetryStart handlers so prompts arriving there\n\t\t// queue as steering instead of racing the retry continuation.\n\t\tthis._abortController = new AbortController();\n\n\t\tthis.events.onRetryStart({\n\t\t\tattempt: this._attempt,\n\t\t\tmaxAttempts: policy.maxAttempts,\n\t\t\tdelayMs,\n\t\t\terrorMessage: message.errorMessage || \"Unknown error\",\n\t\t});\n\n\t\t// Remove the trailing assistant error from live agent state (the host session keeps it in history).\n\t\tconst messages = this.agent.state.messages;\n\t\tif (messages.length > 0 && messages[messages.length - 1].role === \"assistant\") {\n\t\t\tthis.agent.state.messages = messages.slice(0, -1);\n\t\t}\n\n\t\ttry {\n\t\t\tawait sleepAbortable(delayMs, this._abortController.signal);\n\t\t} catch {\n\t\t\t// Aborted mid-backoff: report the cancellation and reset so the next turn starts clean.\n\t\t\tconst attempt = this._attempt;\n\t\t\tthis._attempt = 0;\n\t\t\tthis.events.onRetryEnd({ success: false, attempt, finalError: \"Retry cancelled\" });\n\t\t\treturn false;\n\t\t} finally {\n\t\t\tthis._abortController = undefined;\n\t\t}\n\n\t\treturn true;\n\t}\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@caupulican/pi-agent-core",
|
|
3
|
-
"version": "0.81.
|
|
3
|
+
"version": "0.81.5",
|
|
4
4
|
"description": "General-purpose agent with transport abstraction, state management, and attachment support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"prepublishOnly": "npm run clean && npm run build"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@caupulican/pi-ai": "^0.81.
|
|
36
|
+
"@caupulican/pi-ai": "^0.81.5",
|
|
37
37
|
"ignore": "7.0.5",
|
|
38
38
|
"typebox": "1.1.38",
|
|
39
39
|
"yaml": "2.9.0"
|