@caupulican/pi-adaptative 0.81.23 → 0.81.24

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 (44) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/dist/core/agent-session.d.ts +2 -0
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +4 -3
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/autonomy/status.d.ts +2 -3
  7. package/dist/core/autonomy/status.d.ts.map +1 -1
  8. package/dist/core/autonomy/status.js +3 -12
  9. package/dist/core/autonomy/status.js.map +1 -1
  10. package/dist/core/autonomy-telemetry.d.ts +3 -8
  11. package/dist/core/autonomy-telemetry.d.ts.map +1 -1
  12. package/dist/core/autonomy-telemetry.js +15 -20
  13. package/dist/core/autonomy-telemetry.js.map +1 -1
  14. package/dist/core/cost/cost-summary.d.ts +26 -0
  15. package/dist/core/cost/cost-summary.d.ts.map +1 -0
  16. package/dist/core/cost/cost-summary.js +85 -0
  17. package/dist/core/cost/cost-summary.js.map +1 -0
  18. package/dist/core/cost/session-usage.d.ts.map +1 -1
  19. package/dist/core/cost/session-usage.js +8 -2
  20. package/dist/core/cost/session-usage.js.map +1 -1
  21. package/dist/core/session-analytics.d.ts +3 -1
  22. package/dist/core/session-analytics.d.ts.map +1 -1
  23. package/dist/core/session-analytics.js +33 -18
  24. package/dist/core/session-analytics.js.map +1 -1
  25. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  26. package/dist/modes/interactive/components/footer.js +4 -20
  27. package/dist/modes/interactive/components/footer.js.map +1 -1
  28. package/dist/modes/interactive/report-commands.d.ts.map +1 -1
  29. package/dist/modes/interactive/report-commands.js +7 -8
  30. package/dist/modes/interactive/report-commands.js.map +1 -1
  31. package/dist/modes/interactive/session-flow-commands.d.ts +1 -2
  32. package/dist/modes/interactive/session-flow-commands.d.ts.map +1 -1
  33. package/dist/modes/interactive/session-flow-commands.js +8 -4
  34. package/dist/modes/interactive/session-flow-commands.js.map +1 -1
  35. package/docs/usage.md +6 -0
  36. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  37. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  38. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  39. package/examples/extensions/sandbox/package-lock.json +2 -2
  40. package/examples/extensions/sandbox/package.json +1 -1
  41. package/examples/extensions/with-deps/package-lock.json +2 -2
  42. package/examples/extensions/with-deps/package.json +1 -1
  43. package/npm-shrinkwrap.json +12 -12
  44. package/package.json +4 -4
@@ -1,3 +1,4 @@
1
+ import { type SessionCostSummary } from "../cost/cost-summary.ts";
1
2
  import type { GateOutcomeKind } from "./contracts.ts";
2
3
  export interface AutonomyStatusSnapshot {
3
4
  latestRoute?: {
@@ -10,9 +11,7 @@ export interface AutonomyStatusSnapshot {
10
11
  gate: string;
11
12
  reasonCode: string;
12
13
  };
13
- currentCostUsd?: number;
14
- dailyCostUsd?: number;
15
- spawnedCostUsd?: number;
14
+ costSummary?: SessionCostSummary;
16
15
  activeGoal?: {
17
16
  goalId: string;
18
17
  status: string;
@@ -1 +1 @@
1
- {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/core/autonomy/status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEtD,MAAM,WAAW,sBAAsB;IACtC,WAAW,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAClE,UAAU,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IACnE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAChG,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,MAAM,WAAW,uBAAuB;IACvC,OAAO,EAAE,eAAe,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,EAAE,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,eAAe;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,0BAA0B;IAC1C,MAAM,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IACpC,KAAK,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IACnC,KAAK,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IACnC,QAAQ,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IACtC,UAAU,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IACxC,QAAQ,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IACtC,KAAK,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IACnC,aAAa,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;CAC3C;AA+CD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,sBAAsB,GAAG,MAAM,CAuCzE;AAsBD,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,0BAA0B,GAAG,MAAM,CAclF","sourcesContent":["import type { GateOutcomeKind } from \"./contracts.ts\";\n\nexport interface AutonomyStatusSnapshot {\n\tlatestRoute?: { tier: string; reasonCode: string; risk?: string };\n\tlatestGate?: { outcome: string; gate: string; reasonCode: string };\n\tcurrentCostUsd?: number;\n\tdailyCostUsd?: number;\n\tspawnedCostUsd?: number;\n\tactiveGoal?: { goalId: string; status: string; openRequirements?: number; stallTurns?: number };\n\tactiveLaneCount?: number;\n}\n\n/**\n * One bounded entry in AgentSession's gate-outcome history (G8). Codes/ids only — never the gate's\n * human-facing `message`. The most recent entry is the tail; `at` is an ISO timestamp.\n */\nexport interface GateOutcomeHistoryEntry {\n\toutcome: GateOutcomeKind;\n\tgate: string;\n\treasonCode: string;\n\tat: string;\n}\n\nexport interface DiagnosticEntry {\n\ttitle: string;\n\tsummary?: string;\n\treasonCode?: string;\n\tmetadata?: Record<string, unknown>;\n}\n\nexport interface AutonomyDiagnosticSnapshot {\n\troutes?: readonly DiagnosticEntry[];\n\tgates?: readonly DiagnosticEntry[];\n\tcosts?: readonly DiagnosticEntry[];\n\tresearch?: readonly DiagnosticEntry[];\n\tdelegation?: readonly DiagnosticEntry[];\n\tlearning?: readonly DiagnosticEntry[];\n\tgoals?: readonly DiagnosticEntry[];\n\tprocessMemory?: readonly DiagnosticEntry[];\n}\n\nconst REDACTED = \"[REDACTED]\";\nconst CIRCULAR = \"[Circular]\";\nconst MAX_STRING_LENGTH = 200;\nconst SENSITIVE_KEYS = [\"token\", \"secret\", \"key\", \"credential\", \"password\", \"authorization\"];\nconst SENSITIVE_VALUE_REGEX = /bearer\\s+[\\w\\-._]+|api[-_]?key[-_]?[\\w\\-._]+|sk-[\\w\\-._]+/i;\n\nfunction formatCost(value: number): string {\n\treturn Number.isFinite(value) ? `$${value.toFixed(4)}` : \"$0.0000\";\n}\n\nfunction redactAndTruncateString(value: string): string {\n\tif (SENSITIVE_VALUE_REGEX.test(value)) return REDACTED;\n\tif (value.length <= MAX_STRING_LENGTH) return value;\n\treturn `${value.slice(0, MAX_STRING_LENGTH - 1)}…`;\n}\n\nfunction isSensitiveKey(key: string): boolean {\n\tconst lowerKey = key.toLowerCase();\n\treturn SENSITIVE_KEYS.some((sensitiveKey) => lowerKey.includes(sensitiveKey));\n}\n\nfunction sanitizeMetadataValue(value: unknown, seen: WeakSet<object>): unknown {\n\tif (typeof value === \"string\") return redactAndTruncateString(value);\n\tif (typeof value !== \"object\" || value === null) return value;\n\tif (seen.has(value)) return CIRCULAR;\n\tif (Array.isArray(value)) {\n\t\tseen.add(value);\n\t\treturn value.map((item) => sanitizeMetadataValue(item, seen));\n\t}\n\treturn sanitizeMetadata(value as Record<string, unknown>, seen);\n}\n\nfunction sanitizeMetadata(\n\tobj: Record<string, unknown>,\n\tseen: WeakSet<object> = new WeakSet(),\n): Record<string, unknown> {\n\tif (seen.has(obj)) return { value: CIRCULAR };\n\tseen.add(obj);\n\tconst result: Record<string, unknown> = {};\n\tfor (const [key, value] of Object.entries(obj)) {\n\t\tresult[key] = isSensitiveKey(key) ? REDACTED : sanitizeMetadataValue(value, seen);\n\t}\n\treturn result;\n}\n\nexport function formatAutonomyStatus(args: AutonomyStatusSnapshot): string {\n\tconst parts: string[] = [];\n\n\tif (args.latestRoute) {\n\t\tconst risk = args.latestRoute.risk ? ` (${redactAndTruncateString(args.latestRoute.risk)})` : \"\";\n\t\tparts.push(\n\t\t\t`Route: ${redactAndTruncateString(args.latestRoute.tier)}${risk} - ${redactAndTruncateString(args.latestRoute.reasonCode)}`,\n\t\t);\n\t}\n\n\tif (args.latestGate) {\n\t\tparts.push(\n\t\t\t`Gate: ${redactAndTruncateString(args.latestGate.gate)} = ${redactAndTruncateString(args.latestGate.outcome)} (${redactAndTruncateString(args.latestGate.reasonCode)})`,\n\t\t);\n\t}\n\n\tconst costs: string[] = [];\n\tif (args.currentCostUsd !== undefined) costs.push(`current: ${formatCost(args.currentCostUsd)}`);\n\tif (args.dailyCostUsd !== undefined) costs.push(`daily: ${formatCost(args.dailyCostUsd)}`);\n\tif (args.spawnedCostUsd !== undefined) costs.push(`spawned: ${formatCost(args.spawnedCostUsd)}`);\n\tif (costs.length > 0) {\n\t\tparts.push(`Costs: ${costs.join(\", \")}`);\n\t}\n\n\tif (args.activeGoal) {\n\t\tconst goal = args.activeGoal;\n\t\tconst requirements = goal.openRequirements !== undefined ? `, open reqs: ${goal.openRequirements}` : \"\";\n\t\tconst stalls = goal.stallTurns !== undefined ? `, stalls: ${goal.stallTurns}` : \"\";\n\t\tparts.push(\n\t\t\t`Goal [${redactAndTruncateString(goal.goalId)}]: ${redactAndTruncateString(goal.status)}${requirements}${stalls}`,\n\t\t);\n\t}\n\n\tif (args.activeLaneCount !== undefined) {\n\t\tparts.push(`Lanes: ${args.activeLaneCount}`);\n\t}\n\n\tif (parts.length === 0) return \"Autonomy: idle\";\n\treturn parts.join(\" | \");\n}\n\nfunction formatDiagnosticSection(name: string, entries?: readonly DiagnosticEntry[]): string | undefined {\n\tif (!entries || entries.length === 0) return undefined;\n\n\tconst lines: string[] = [`--- ${name} ---`];\n\tfor (const entry of entries) {\n\t\tlet header = `- ${redactAndTruncateString(entry.title)}`;\n\t\tif (entry.reasonCode) header += ` [${redactAndTruncateString(entry.reasonCode)}]`;\n\t\tlines.push(header);\n\n\t\tif (entry.summary) {\n\t\t\tlines.push(` Summary: ${redactAndTruncateString(entry.summary)}`);\n\t\t}\n\n\t\tif (entry.metadata) {\n\t\t\tlines.push(` Metadata: ${JSON.stringify(sanitizeMetadata(entry.metadata))}`);\n\t\t}\n\t}\n\treturn lines.join(\"\\n\");\n}\n\nexport function formatAutonomyDiagnostics(args: AutonomyDiagnosticSnapshot): string {\n\tconst sections = [\n\t\tformatDiagnosticSection(\"Routes\", args.routes),\n\t\tformatDiagnosticSection(\"Gates\", args.gates),\n\t\tformatDiagnosticSection(\"Costs\", args.costs),\n\t\tformatDiagnosticSection(\"Research\", args.research),\n\t\tformatDiagnosticSection(\"Delegation\", args.delegation),\n\t\tformatDiagnosticSection(\"Learning\", args.learning),\n\t\tformatDiagnosticSection(\"Goals\", args.goals),\n\t\tformatDiagnosticSection(\"Process Memory\", args.processMemory),\n\t].filter((section): section is string => Boolean(section));\n\n\tif (sections.length === 0) return \"No diagnostics available.\";\n\treturn sections.join(\"\\n\\n\");\n}\n"]}
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/core/autonomy/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2B,KAAK,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC3F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEtD,MAAM,WAAW,sBAAsB;IACtC,WAAW,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAClE,UAAU,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IACnE,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,UAAU,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAChG,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,MAAM,WAAW,uBAAuB;IACvC,OAAO,EAAE,eAAe,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,EAAE,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,eAAe;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,0BAA0B;IAC1C,MAAM,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IACpC,KAAK,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IACnC,KAAK,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IACnC,QAAQ,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IACtC,UAAU,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IACxC,QAAQ,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IACtC,KAAK,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IACnC,aAAa,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;CAC3C;AA2CD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,sBAAsB,GAAG,MAAM,CAmCzE;AAsBD,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,0BAA0B,GAAG,MAAM,CAclF","sourcesContent":["import { formatStatusCostSummary, type SessionCostSummary } from \"../cost/cost-summary.ts\";\nimport type { GateOutcomeKind } from \"./contracts.ts\";\n\nexport interface AutonomyStatusSnapshot {\n\tlatestRoute?: { tier: string; reasonCode: string; risk?: string };\n\tlatestGate?: { outcome: string; gate: string; reasonCode: string };\n\tcostSummary?: SessionCostSummary;\n\tactiveGoal?: { goalId: string; status: string; openRequirements?: number; stallTurns?: number };\n\tactiveLaneCount?: number;\n}\n\n/**\n * One bounded entry in AgentSession's gate-outcome history (G8). Codes/ids only — never the gate's\n * human-facing `message`. The most recent entry is the tail; `at` is an ISO timestamp.\n */\nexport interface GateOutcomeHistoryEntry {\n\toutcome: GateOutcomeKind;\n\tgate: string;\n\treasonCode: string;\n\tat: string;\n}\n\nexport interface DiagnosticEntry {\n\ttitle: string;\n\tsummary?: string;\n\treasonCode?: string;\n\tmetadata?: Record<string, unknown>;\n}\n\nexport interface AutonomyDiagnosticSnapshot {\n\troutes?: readonly DiagnosticEntry[];\n\tgates?: readonly DiagnosticEntry[];\n\tcosts?: readonly DiagnosticEntry[];\n\tresearch?: readonly DiagnosticEntry[];\n\tdelegation?: readonly DiagnosticEntry[];\n\tlearning?: readonly DiagnosticEntry[];\n\tgoals?: readonly DiagnosticEntry[];\n\tprocessMemory?: readonly DiagnosticEntry[];\n}\n\nconst REDACTED = \"[REDACTED]\";\nconst CIRCULAR = \"[Circular]\";\nconst MAX_STRING_LENGTH = 200;\nconst SENSITIVE_KEYS = [\"token\", \"secret\", \"key\", \"credential\", \"password\", \"authorization\"];\nconst SENSITIVE_VALUE_REGEX = /bearer\\s+[\\w\\-._]+|api[-_]?key[-_]?[\\w\\-._]+|sk-[\\w\\-._]+/i;\n\nfunction redactAndTruncateString(value: string): string {\n\tif (SENSITIVE_VALUE_REGEX.test(value)) return REDACTED;\n\tif (value.length <= MAX_STRING_LENGTH) return value;\n\treturn `${value.slice(0, MAX_STRING_LENGTH - 1)}…`;\n}\n\nfunction isSensitiveKey(key: string): boolean {\n\tconst lowerKey = key.toLowerCase();\n\treturn SENSITIVE_KEYS.some((sensitiveKey) => lowerKey.includes(sensitiveKey));\n}\n\nfunction sanitizeMetadataValue(value: unknown, seen: WeakSet<object>): unknown {\n\tif (typeof value === \"string\") return redactAndTruncateString(value);\n\tif (typeof value !== \"object\" || value === null) return value;\n\tif (seen.has(value)) return CIRCULAR;\n\tif (Array.isArray(value)) {\n\t\tseen.add(value);\n\t\treturn value.map((item) => sanitizeMetadataValue(item, seen));\n\t}\n\treturn sanitizeMetadata(value as Record<string, unknown>, seen);\n}\n\nfunction sanitizeMetadata(\n\tobj: Record<string, unknown>,\n\tseen: WeakSet<object> = new WeakSet(),\n): Record<string, unknown> {\n\tif (seen.has(obj)) return { value: CIRCULAR };\n\tseen.add(obj);\n\tconst result: Record<string, unknown> = {};\n\tfor (const [key, value] of Object.entries(obj)) {\n\t\tresult[key] = isSensitiveKey(key) ? REDACTED : sanitizeMetadataValue(value, seen);\n\t}\n\treturn result;\n}\n\nexport function formatAutonomyStatus(args: AutonomyStatusSnapshot): string {\n\tconst parts: string[] = [];\n\n\tif (args.latestRoute) {\n\t\tconst risk = args.latestRoute.risk ? ` (${redactAndTruncateString(args.latestRoute.risk)})` : \"\";\n\t\tparts.push(\n\t\t\t`Route: ${redactAndTruncateString(args.latestRoute.tier)}${risk} - ${redactAndTruncateString(args.latestRoute.reasonCode)}`,\n\t\t);\n\t}\n\n\tif (args.latestGate) {\n\t\tparts.push(\n\t\t\t`Gate: ${redactAndTruncateString(args.latestGate.gate)} = ${redactAndTruncateString(args.latestGate.outcome)} (${redactAndTruncateString(args.latestGate.reasonCode)})`,\n\t\t);\n\t}\n\n\tif (args.costSummary) {\n\t\tparts.push(`Costs: ${formatStatusCostSummary(args.costSummary)}`);\n\t}\n\n\tif (args.activeGoal) {\n\t\tconst goal = args.activeGoal;\n\t\tconst requirements = goal.openRequirements !== undefined ? `, open reqs: ${goal.openRequirements}` : \"\";\n\t\tconst stalls = goal.stallTurns !== undefined ? `, stalls: ${goal.stallTurns}` : \"\";\n\t\tparts.push(\n\t\t\t`Goal [${redactAndTruncateString(goal.goalId)}]: ${redactAndTruncateString(goal.status)}${requirements}${stalls}`,\n\t\t);\n\t}\n\n\tif (args.activeLaneCount !== undefined) {\n\t\tparts.push(`Lanes: ${args.activeLaneCount}`);\n\t}\n\n\tif (parts.length === 0) return \"Autonomy: idle\";\n\treturn parts.join(\" | \");\n}\n\nfunction formatDiagnosticSection(name: string, entries?: readonly DiagnosticEntry[]): string | undefined {\n\tif (!entries || entries.length === 0) return undefined;\n\n\tconst lines: string[] = [`--- ${name} ---`];\n\tfor (const entry of entries) {\n\t\tlet header = `- ${redactAndTruncateString(entry.title)}`;\n\t\tif (entry.reasonCode) header += ` [${redactAndTruncateString(entry.reasonCode)}]`;\n\t\tlines.push(header);\n\n\t\tif (entry.summary) {\n\t\t\tlines.push(` Summary: ${redactAndTruncateString(entry.summary)}`);\n\t\t}\n\n\t\tif (entry.metadata) {\n\t\t\tlines.push(` Metadata: ${JSON.stringify(sanitizeMetadata(entry.metadata))}`);\n\t\t}\n\t}\n\treturn lines.join(\"\\n\");\n}\n\nexport function formatAutonomyDiagnostics(args: AutonomyDiagnosticSnapshot): string {\n\tconst sections = [\n\t\tformatDiagnosticSection(\"Routes\", args.routes),\n\t\tformatDiagnosticSection(\"Gates\", args.gates),\n\t\tformatDiagnosticSection(\"Costs\", args.costs),\n\t\tformatDiagnosticSection(\"Research\", args.research),\n\t\tformatDiagnosticSection(\"Delegation\", args.delegation),\n\t\tformatDiagnosticSection(\"Learning\", args.learning),\n\t\tformatDiagnosticSection(\"Goals\", args.goals),\n\t\tformatDiagnosticSection(\"Process Memory\", args.processMemory),\n\t].filter((section): section is string => Boolean(section));\n\n\tif (sections.length === 0) return \"No diagnostics available.\";\n\treturn sections.join(\"\\n\\n\");\n}\n"]}
@@ -1,11 +1,9 @@
1
+ import { formatStatusCostSummary } from "../cost/cost-summary.js";
1
2
  const REDACTED = "[REDACTED]";
2
3
  const CIRCULAR = "[Circular]";
3
4
  const MAX_STRING_LENGTH = 200;
4
5
  const SENSITIVE_KEYS = ["token", "secret", "key", "credential", "password", "authorization"];
5
6
  const SENSITIVE_VALUE_REGEX = /bearer\s+[\w\-._]+|api[-_]?key[-_]?[\w\-._]+|sk-[\w\-._]+/i;
6
- function formatCost(value) {
7
- return Number.isFinite(value) ? `$${value.toFixed(4)}` : "$0.0000";
8
- }
9
7
  function redactAndTruncateString(value) {
10
8
  if (SENSITIVE_VALUE_REGEX.test(value))
11
9
  return REDACTED;
@@ -49,15 +47,8 @@ export function formatAutonomyStatus(args) {
49
47
  if (args.latestGate) {
50
48
  parts.push(`Gate: ${redactAndTruncateString(args.latestGate.gate)} = ${redactAndTruncateString(args.latestGate.outcome)} (${redactAndTruncateString(args.latestGate.reasonCode)})`);
51
49
  }
52
- const costs = [];
53
- if (args.currentCostUsd !== undefined)
54
- costs.push(`current: ${formatCost(args.currentCostUsd)}`);
55
- if (args.dailyCostUsd !== undefined)
56
- costs.push(`daily: ${formatCost(args.dailyCostUsd)}`);
57
- if (args.spawnedCostUsd !== undefined)
58
- costs.push(`spawned: ${formatCost(args.spawnedCostUsd)}`);
59
- if (costs.length > 0) {
60
- parts.push(`Costs: ${costs.join(", ")}`);
50
+ if (args.costSummary) {
51
+ parts.push(`Costs: ${formatStatusCostSummary(args.costSummary)}`);
61
52
  }
62
53
  if (args.activeGoal) {
63
54
  const goal = args.activeGoal;
@@ -1 +1 @@
1
- {"version":3,"file":"status.js","sourceRoot":"","sources":["../../../src/core/autonomy/status.ts"],"names":[],"mappings":"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","sourcesContent":["import type { GateOutcomeKind } from \"./contracts.ts\";\n\nexport interface AutonomyStatusSnapshot {\n\tlatestRoute?: { tier: string; reasonCode: string; risk?: string };\n\tlatestGate?: { outcome: string; gate: string; reasonCode: string };\n\tcurrentCostUsd?: number;\n\tdailyCostUsd?: number;\n\tspawnedCostUsd?: number;\n\tactiveGoal?: { goalId: string; status: string; openRequirements?: number; stallTurns?: number };\n\tactiveLaneCount?: number;\n}\n\n/**\n * One bounded entry in AgentSession's gate-outcome history (G8). Codes/ids only — never the gate's\n * human-facing `message`. The most recent entry is the tail; `at` is an ISO timestamp.\n */\nexport interface GateOutcomeHistoryEntry {\n\toutcome: GateOutcomeKind;\n\tgate: string;\n\treasonCode: string;\n\tat: string;\n}\n\nexport interface DiagnosticEntry {\n\ttitle: string;\n\tsummary?: string;\n\treasonCode?: string;\n\tmetadata?: Record<string, unknown>;\n}\n\nexport interface AutonomyDiagnosticSnapshot {\n\troutes?: readonly DiagnosticEntry[];\n\tgates?: readonly DiagnosticEntry[];\n\tcosts?: readonly DiagnosticEntry[];\n\tresearch?: readonly DiagnosticEntry[];\n\tdelegation?: readonly DiagnosticEntry[];\n\tlearning?: readonly DiagnosticEntry[];\n\tgoals?: readonly DiagnosticEntry[];\n\tprocessMemory?: readonly DiagnosticEntry[];\n}\n\nconst REDACTED = \"[REDACTED]\";\nconst CIRCULAR = \"[Circular]\";\nconst MAX_STRING_LENGTH = 200;\nconst SENSITIVE_KEYS = [\"token\", \"secret\", \"key\", \"credential\", \"password\", \"authorization\"];\nconst SENSITIVE_VALUE_REGEX = /bearer\\s+[\\w\\-._]+|api[-_]?key[-_]?[\\w\\-._]+|sk-[\\w\\-._]+/i;\n\nfunction formatCost(value: number): string {\n\treturn Number.isFinite(value) ? `$${value.toFixed(4)}` : \"$0.0000\";\n}\n\nfunction redactAndTruncateString(value: string): string {\n\tif (SENSITIVE_VALUE_REGEX.test(value)) return REDACTED;\n\tif (value.length <= MAX_STRING_LENGTH) return value;\n\treturn `${value.slice(0, MAX_STRING_LENGTH - 1)}…`;\n}\n\nfunction isSensitiveKey(key: string): boolean {\n\tconst lowerKey = key.toLowerCase();\n\treturn SENSITIVE_KEYS.some((sensitiveKey) => lowerKey.includes(sensitiveKey));\n}\n\nfunction sanitizeMetadataValue(value: unknown, seen: WeakSet<object>): unknown {\n\tif (typeof value === \"string\") return redactAndTruncateString(value);\n\tif (typeof value !== \"object\" || value === null) return value;\n\tif (seen.has(value)) return CIRCULAR;\n\tif (Array.isArray(value)) {\n\t\tseen.add(value);\n\t\treturn value.map((item) => sanitizeMetadataValue(item, seen));\n\t}\n\treturn sanitizeMetadata(value as Record<string, unknown>, seen);\n}\n\nfunction sanitizeMetadata(\n\tobj: Record<string, unknown>,\n\tseen: WeakSet<object> = new WeakSet(),\n): Record<string, unknown> {\n\tif (seen.has(obj)) return { value: CIRCULAR };\n\tseen.add(obj);\n\tconst result: Record<string, unknown> = {};\n\tfor (const [key, value] of Object.entries(obj)) {\n\t\tresult[key] = isSensitiveKey(key) ? REDACTED : sanitizeMetadataValue(value, seen);\n\t}\n\treturn result;\n}\n\nexport function formatAutonomyStatus(args: AutonomyStatusSnapshot): string {\n\tconst parts: string[] = [];\n\n\tif (args.latestRoute) {\n\t\tconst risk = args.latestRoute.risk ? ` (${redactAndTruncateString(args.latestRoute.risk)})` : \"\";\n\t\tparts.push(\n\t\t\t`Route: ${redactAndTruncateString(args.latestRoute.tier)}${risk} - ${redactAndTruncateString(args.latestRoute.reasonCode)}`,\n\t\t);\n\t}\n\n\tif (args.latestGate) {\n\t\tparts.push(\n\t\t\t`Gate: ${redactAndTruncateString(args.latestGate.gate)} = ${redactAndTruncateString(args.latestGate.outcome)} (${redactAndTruncateString(args.latestGate.reasonCode)})`,\n\t\t);\n\t}\n\n\tconst costs: string[] = [];\n\tif (args.currentCostUsd !== undefined) costs.push(`current: ${formatCost(args.currentCostUsd)}`);\n\tif (args.dailyCostUsd !== undefined) costs.push(`daily: ${formatCost(args.dailyCostUsd)}`);\n\tif (args.spawnedCostUsd !== undefined) costs.push(`spawned: ${formatCost(args.spawnedCostUsd)}`);\n\tif (costs.length > 0) {\n\t\tparts.push(`Costs: ${costs.join(\", \")}`);\n\t}\n\n\tif (args.activeGoal) {\n\t\tconst goal = args.activeGoal;\n\t\tconst requirements = goal.openRequirements !== undefined ? `, open reqs: ${goal.openRequirements}` : \"\";\n\t\tconst stalls = goal.stallTurns !== undefined ? `, stalls: ${goal.stallTurns}` : \"\";\n\t\tparts.push(\n\t\t\t`Goal [${redactAndTruncateString(goal.goalId)}]: ${redactAndTruncateString(goal.status)}${requirements}${stalls}`,\n\t\t);\n\t}\n\n\tif (args.activeLaneCount !== undefined) {\n\t\tparts.push(`Lanes: ${args.activeLaneCount}`);\n\t}\n\n\tif (parts.length === 0) return \"Autonomy: idle\";\n\treturn parts.join(\" | \");\n}\n\nfunction formatDiagnosticSection(name: string, entries?: readonly DiagnosticEntry[]): string | undefined {\n\tif (!entries || entries.length === 0) return undefined;\n\n\tconst lines: string[] = [`--- ${name} ---`];\n\tfor (const entry of entries) {\n\t\tlet header = `- ${redactAndTruncateString(entry.title)}`;\n\t\tif (entry.reasonCode) header += ` [${redactAndTruncateString(entry.reasonCode)}]`;\n\t\tlines.push(header);\n\n\t\tif (entry.summary) {\n\t\t\tlines.push(` Summary: ${redactAndTruncateString(entry.summary)}`);\n\t\t}\n\n\t\tif (entry.metadata) {\n\t\t\tlines.push(` Metadata: ${JSON.stringify(sanitizeMetadata(entry.metadata))}`);\n\t\t}\n\t}\n\treturn lines.join(\"\\n\");\n}\n\nexport function formatAutonomyDiagnostics(args: AutonomyDiagnosticSnapshot): string {\n\tconst sections = [\n\t\tformatDiagnosticSection(\"Routes\", args.routes),\n\t\tformatDiagnosticSection(\"Gates\", args.gates),\n\t\tformatDiagnosticSection(\"Costs\", args.costs),\n\t\tformatDiagnosticSection(\"Research\", args.research),\n\t\tformatDiagnosticSection(\"Delegation\", args.delegation),\n\t\tformatDiagnosticSection(\"Learning\", args.learning),\n\t\tformatDiagnosticSection(\"Goals\", args.goals),\n\t\tformatDiagnosticSection(\"Process Memory\", args.processMemory),\n\t].filter((section): section is string => Boolean(section));\n\n\tif (sections.length === 0) return \"No diagnostics available.\";\n\treturn sections.join(\"\\n\\n\");\n}\n"]}
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../../src/core/autonomy/status.ts"],"names":[],"mappings":"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","sourcesContent":["import { formatStatusCostSummary, type SessionCostSummary } from \"../cost/cost-summary.ts\";\nimport type { GateOutcomeKind } from \"./contracts.ts\";\n\nexport interface AutonomyStatusSnapshot {\n\tlatestRoute?: { tier: string; reasonCode: string; risk?: string };\n\tlatestGate?: { outcome: string; gate: string; reasonCode: string };\n\tcostSummary?: SessionCostSummary;\n\tactiveGoal?: { goalId: string; status: string; openRequirements?: number; stallTurns?: number };\n\tactiveLaneCount?: number;\n}\n\n/**\n * One bounded entry in AgentSession's gate-outcome history (G8). Codes/ids only — never the gate's\n * human-facing `message`. The most recent entry is the tail; `at` is an ISO timestamp.\n */\nexport interface GateOutcomeHistoryEntry {\n\toutcome: GateOutcomeKind;\n\tgate: string;\n\treasonCode: string;\n\tat: string;\n}\n\nexport interface DiagnosticEntry {\n\ttitle: string;\n\tsummary?: string;\n\treasonCode?: string;\n\tmetadata?: Record<string, unknown>;\n}\n\nexport interface AutonomyDiagnosticSnapshot {\n\troutes?: readonly DiagnosticEntry[];\n\tgates?: readonly DiagnosticEntry[];\n\tcosts?: readonly DiagnosticEntry[];\n\tresearch?: readonly DiagnosticEntry[];\n\tdelegation?: readonly DiagnosticEntry[];\n\tlearning?: readonly DiagnosticEntry[];\n\tgoals?: readonly DiagnosticEntry[];\n\tprocessMemory?: readonly DiagnosticEntry[];\n}\n\nconst REDACTED = \"[REDACTED]\";\nconst CIRCULAR = \"[Circular]\";\nconst MAX_STRING_LENGTH = 200;\nconst SENSITIVE_KEYS = [\"token\", \"secret\", \"key\", \"credential\", \"password\", \"authorization\"];\nconst SENSITIVE_VALUE_REGEX = /bearer\\s+[\\w\\-._]+|api[-_]?key[-_]?[\\w\\-._]+|sk-[\\w\\-._]+/i;\n\nfunction redactAndTruncateString(value: string): string {\n\tif (SENSITIVE_VALUE_REGEX.test(value)) return REDACTED;\n\tif (value.length <= MAX_STRING_LENGTH) return value;\n\treturn `${value.slice(0, MAX_STRING_LENGTH - 1)}…`;\n}\n\nfunction isSensitiveKey(key: string): boolean {\n\tconst lowerKey = key.toLowerCase();\n\treturn SENSITIVE_KEYS.some((sensitiveKey) => lowerKey.includes(sensitiveKey));\n}\n\nfunction sanitizeMetadataValue(value: unknown, seen: WeakSet<object>): unknown {\n\tif (typeof value === \"string\") return redactAndTruncateString(value);\n\tif (typeof value !== \"object\" || value === null) return value;\n\tif (seen.has(value)) return CIRCULAR;\n\tif (Array.isArray(value)) {\n\t\tseen.add(value);\n\t\treturn value.map((item) => sanitizeMetadataValue(item, seen));\n\t}\n\treturn sanitizeMetadata(value as Record<string, unknown>, seen);\n}\n\nfunction sanitizeMetadata(\n\tobj: Record<string, unknown>,\n\tseen: WeakSet<object> = new WeakSet(),\n): Record<string, unknown> {\n\tif (seen.has(obj)) return { value: CIRCULAR };\n\tseen.add(obj);\n\tconst result: Record<string, unknown> = {};\n\tfor (const [key, value] of Object.entries(obj)) {\n\t\tresult[key] = isSensitiveKey(key) ? REDACTED : sanitizeMetadataValue(value, seen);\n\t}\n\treturn result;\n}\n\nexport function formatAutonomyStatus(args: AutonomyStatusSnapshot): string {\n\tconst parts: string[] = [];\n\n\tif (args.latestRoute) {\n\t\tconst risk = args.latestRoute.risk ? ` (${redactAndTruncateString(args.latestRoute.risk)})` : \"\";\n\t\tparts.push(\n\t\t\t`Route: ${redactAndTruncateString(args.latestRoute.tier)}${risk} - ${redactAndTruncateString(args.latestRoute.reasonCode)}`,\n\t\t);\n\t}\n\n\tif (args.latestGate) {\n\t\tparts.push(\n\t\t\t`Gate: ${redactAndTruncateString(args.latestGate.gate)} = ${redactAndTruncateString(args.latestGate.outcome)} (${redactAndTruncateString(args.latestGate.reasonCode)})`,\n\t\t);\n\t}\n\n\tif (args.costSummary) {\n\t\tparts.push(`Costs: ${formatStatusCostSummary(args.costSummary)}`);\n\t}\n\n\tif (args.activeGoal) {\n\t\tconst goal = args.activeGoal;\n\t\tconst requirements = goal.openRequirements !== undefined ? `, open reqs: ${goal.openRequirements}` : \"\";\n\t\tconst stalls = goal.stallTurns !== undefined ? `, stalls: ${goal.stallTurns}` : \"\";\n\t\tparts.push(\n\t\t\t`Goal [${redactAndTruncateString(goal.goalId)}]: ${redactAndTruncateString(goal.status)}${requirements}${stalls}`,\n\t\t);\n\t}\n\n\tif (args.activeLaneCount !== undefined) {\n\t\tparts.push(`Lanes: ${args.activeLaneCount}`);\n\t}\n\n\tif (parts.length === 0) return \"Autonomy: idle\";\n\treturn parts.join(\" | \");\n}\n\nfunction formatDiagnosticSection(name: string, entries?: readonly DiagnosticEntry[]): string | undefined {\n\tif (!entries || entries.length === 0) return undefined;\n\n\tconst lines: string[] = [`--- ${name} ---`];\n\tfor (const entry of entries) {\n\t\tlet header = `- ${redactAndTruncateString(entry.title)}`;\n\t\tif (entry.reasonCode) header += ` [${redactAndTruncateString(entry.reasonCode)}]`;\n\t\tlines.push(header);\n\n\t\tif (entry.summary) {\n\t\t\tlines.push(` Summary: ${redactAndTruncateString(entry.summary)}`);\n\t\t}\n\n\t\tif (entry.metadata) {\n\t\t\tlines.push(` Metadata: ${JSON.stringify(sanitizeMetadata(entry.metadata))}`);\n\t\t}\n\t}\n\treturn lines.join(\"\\n\");\n}\n\nexport function formatAutonomyDiagnostics(args: AutonomyDiagnosticSnapshot): string {\n\tconst sections = [\n\t\tformatDiagnosticSection(\"Routes\", args.routes),\n\t\tformatDiagnosticSection(\"Gates\", args.gates),\n\t\tformatDiagnosticSection(\"Costs\", args.costs),\n\t\tformatDiagnosticSection(\"Research\", args.research),\n\t\tformatDiagnosticSection(\"Delegation\", args.delegation),\n\t\tformatDiagnosticSection(\"Learning\", args.learning),\n\t\tformatDiagnosticSection(\"Goals\", args.goals),\n\t\tformatDiagnosticSection(\"Process Memory\", args.processMemory),\n\t].filter((section): section is string => Boolean(section));\n\n\tif (sections.length === 0) return \"No diagnostics available.\";\n\treturn sections.join(\"\\n\\n\");\n}\n"]}
@@ -8,11 +8,10 @@
8
8
  * rather than the whole AgentSession — they never mutate anything but the owned gate-outcome fields.
9
9
  */
10
10
  import type { SessionManager } from "@caupulican/pi-agent-core/node";
11
- import type { SessionStats, SpawnedUsageTotals } from "./agent-session.ts";
12
11
  import type { EvidenceBundle, GateOutcome, LearningDecision, WorkerResult } from "./autonomy/contracts.ts";
13
12
  import type { AutonomyDiagnosticSnapshot, AutonomyStatusSnapshot, GateOutcomeHistoryEntry } from "./autonomy/status.ts";
14
13
  import { type AutonomyTelemetryEvent } from "./autonomy/telemetry-events.ts";
15
- import type { DailyUsageTotals } from "./cost/daily-usage.ts";
14
+ import { type SessionCostSummary } from "./cost/cost-summary.ts";
16
15
  import type { GoalState } from "./goals/goal-state.ts";
17
16
  import type { LearningAuditRecord } from "./learning/learning-audit.ts";
18
17
  import { type ModelRouterDecisionStatus } from "./model-router/status.ts";
@@ -23,12 +22,8 @@ export interface AutonomyTelemetryDeps {
23
22
  getLastModelRouterDecision(): ModelRouterDecisionStatus | undefined;
24
23
  /** Reason the last research lane was skipped, if any — surfaced in the diagnostic's research family. */
25
24
  getLastResearchLaneSkipReason(): string | undefined;
26
- /** This session's own usage totals (cost only is read). */
27
- getSessionStats(): SessionStats;
28
- /** Spawned-child usage totals (cost only is read). */
29
- getSpawnedUsage(): SpawnedUsageTotals;
30
- /** Cross-session daily usage totals, if trackable — preserves the source's optional-call semantics. */
31
- getDailyUsageTotals(): DailyUsageTotals | undefined;
25
+ /** Single source of truth for CURRENT/TODAY/SUBAGENTS display costs. */
26
+ getCostSummary(): SessionCostSummary;
32
27
  /** Current goal state, if a goal is active. */
33
28
  getGoalStateSnapshot(): GoalState | undefined;
34
29
  /** Live count of active lanes from the lane tracker — never inferred from historical snapshots. */
@@ -1 +1 @@
1
- {"version":3,"file":"autonomy-telemetry.d.ts","sourceRoot":"","sources":["../../src/core/autonomy-telemetry.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAErE,OAAO,KAAK,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC3E,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAE3G,OAAO,KAAK,EACX,0BAA0B,EAC1B,sBAAsB,EAEtB,uBAAuB,EACvB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAEN,KAAK,sBAAsB,EAE3B,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EAAiC,KAAK,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AASzG,MAAM,WAAW,qBAAqB;IACrC,yGAAyG;IACzG,iBAAiB,IAAI,IAAI,CAAC,cAAc,EAAE,mBAAmB,GAAG,YAAY,CAAC,CAAC;IAC9E,6GAA2G;IAC3G,0BAA0B,IAAI,yBAAyB,GAAG,SAAS,CAAC;IACpE,0GAAwG;IACxG,6BAA6B,IAAI,MAAM,GAAG,SAAS,CAAC;IACpD,2DAA2D;IAC3D,eAAe,IAAI,YAAY,CAAC;IAChC,sDAAsD;IACtD,eAAe,IAAI,kBAAkB,CAAC;IACtC,yGAAuG;IACvG,mBAAmB,IAAI,gBAAgB,GAAG,SAAS,CAAC;IACpD,+CAA+C;IAC/C,oBAAoB,IAAI,SAAS,GAAG,SAAS,CAAC;IAC9C,qGAAmG;IACnG,kBAAkB,IAAI,MAAM,CAAC;IAC7B,uDAAuD;IACvD,0BAA0B,IAAI,cAAc,EAAE,CAAC;IAC/C,gEAAgE;IAChE,wBAAwB,IAAI,YAAY,EAAE,CAAC;IAC3C,yDAAyD;IACzD,4BAA4B,IAAI,gBAAgB,EAAE,CAAC;IACnD,oDAAoD;IACpD,uBAAuB,IAAI,mBAAmB,EAAE,CAAC;CACjD;AAED,qBAAa,iBAAiB;IAC7B,OAAO,CAAC,wBAAwB,CAAC,CAAc;IAC/C,qGAAqG;IACrG,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAiC;IAErE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAwB;IAE7C,YAAY,IAAI,EAAE,qBAAqB,EAEtC;IAED;;;;;;OAMG;IACH,aAAa,CAAC,KAAK,EAAE,sBAAsB,GAAG,IAAI,CAOjD;IAED;;;;;;OAMG;IACH,iBAAiB,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,CAsB5C;IAED,iFAAiF;IACjF,qBAAqB,IAAI,uBAAuB,EAAE,CAEjD;IAED,iBAAiB,IAAI,sBAAsB,CAqD1C;IAED;;;;;OAKG;IACH,qBAAqB,CAAC,OAAO,CAAC,EAAE;QAAE,mBAAmB,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,0BAA0B,CAuJ5F;CACD","sourcesContent":["/**\n * Autonomy telemetry sink + status/diagnostic snapshots.\n *\n * Extracted verbatim from agent-session.ts (god-file decomposition). Owns the latest gate outcome\n * and the bounded gate-outcome history, and is the single sink for G3 autonomy-telemetry custom\n * entries. The two snapshot builders READ broadly across live session state (router decision, cost,\n * goal, lanes, and the research/delegation/learning/goal getters) through narrow deps accessors\n * rather than the whole AgentSession — they never mutate anything but the owned gate-outcome fields.\n */\n\nimport type { SessionManager } from \"@caupulican/pi-agent-core/node\";\nimport { getProcessMemoryMb } from \"../utils/process-memory.ts\";\nimport type { SessionStats, SpawnedUsageTotals } from \"./agent-session.ts\";\nimport type { EvidenceBundle, GateOutcome, LearningDecision, WorkerResult } from \"./autonomy/contracts.ts\";\nimport { getLaneRecordSnapshots } from \"./autonomy/session-lane-record.ts\";\nimport type {\n\tAutonomyDiagnosticSnapshot,\n\tAutonomyStatusSnapshot,\n\tDiagnosticEntry,\n\tGateOutcomeHistoryEntry,\n} from \"./autonomy/status.ts\";\nimport {\n\tAUTONOMY_TELEMETRY_EVENT_TYPES,\n\ttype AutonomyTelemetryEvent,\n\tredactTelemetryValue,\n} from \"./autonomy/telemetry-events.ts\";\nimport type { DailyUsageTotals } from \"./cost/daily-usage.ts\";\nimport type { GoalState } from \"./goals/goal-state.ts\";\nimport type { LearningAuditRecord } from \"./learning/learning-audit.ts\";\nimport { getRecentModelRouterDecisions, type ModelRouterDecisionStatus } from \"./model-router/status.ts\";\n\n/** Custom-entry type for G3 autonomy telemetry. Distinct from the router/lane record types so a\n * telemetry consumer can filter on it without decoding operational snapshots. */\nconst AUTONOMY_TELEMETRY_CUSTOM_TYPE = \"autonomy-telemetry\";\n\n/** G8: bound on the in-memory gate-outcome history. Oldest entries evict once the cap is reached. */\nconst GATE_OUTCOME_HISTORY_LIMIT = 50;\n\nexport interface AutonomyTelemetryDeps {\n\t/** Session log: `appendCustomEntry` is the telemetry sink; `getEntries` feeds diagnostic aggregation. */\n\tgetSessionManager(): Pick<SessionManager, \"appendCustomEntry\" | \"getEntries\">;\n\t/** Latest model-router decision, for the snapshot's `latestRoute` — owned by the router, not this sink. */\n\tgetLastModelRouterDecision(): ModelRouterDecisionStatus | undefined;\n\t/** Reason the last research lane was skipped, if any — surfaced in the diagnostic's research family. */\n\tgetLastResearchLaneSkipReason(): string | undefined;\n\t/** This session's own usage totals (cost only is read). */\n\tgetSessionStats(): SessionStats;\n\t/** Spawned-child usage totals (cost only is read). */\n\tgetSpawnedUsage(): SpawnedUsageTotals;\n\t/** Cross-session daily usage totals, if trackable — preserves the source's optional-call semantics. */\n\tgetDailyUsageTotals(): DailyUsageTotals | undefined;\n\t/** Current goal state, if a goal is active. */\n\tgetGoalStateSnapshot(): GoalState | undefined;\n\t/** Live count of active lanes from the lane tracker — never inferred from historical snapshots. */\n\tgetActiveLaneCount(): number;\n\t/** Research evidence bundles recorded this session. */\n\tgetEvidenceBundleSnapshots(): EvidenceBundle[];\n\t/** Worker-delegation result snapshots recorded this session. */\n\tgetWorkerResultSnapshots(): WorkerResult[];\n\t/** Learning decision snapshots recorded this session. */\n\tgetLearningDecisionSnapshots(): LearningDecision[];\n\t/** Learning audit records recorded this session. */\n\tgetLearningAuditRecords(): LearningAuditRecord[];\n}\n\nexport class AutonomyTelemetry {\n\tprivate _lastAutonomyGateOutcome?: GateOutcome;\n\t/** G8: bounded (cap {@link GATE_OUTCOME_HISTORY_LIMIT}) history of gate outcomes; tail is latest. */\n\tprivate readonly _gateOutcomeHistory: GateOutcomeHistoryEntry[] = [];\n\n\tprivate readonly deps: AutonomyTelemetryDeps;\n\n\tconstructor(deps: AutonomyTelemetryDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\t/**\n\t * G3: bounded autonomy-telemetry sink. Passes the whole event through {@link redactTelemetryValue}\n\t * (the taxonomy's redaction contract) before storing it, so a secret that leaked into a payload\n\t * field never lands in the session log. Observe-only: a failure here can never surface into the\n\t * turn it is measuring, so the whole body is swallowed. Payloads MUST stay small (ids, codes,\n\t * numbers) — never prompt/summary text; callers own that discipline.\n\t */\n\temitTelemetry(event: AutonomyTelemetryEvent): void {\n\t\ttry {\n\t\t\tconst redacted = redactTelemetryValue(event) as Record<string, unknown>;\n\t\t\tthis.deps.getSessionManager().appendCustomEntry(AUTONOMY_TELEMETRY_CUSTOM_TYPE, { version: 1, ...redacted });\n\t\t} catch {\n\t\t\t// Telemetry is best-effort: swallow so a sink failure cannot break the observed turn.\n\t\t}\n\t}\n\n\t/**\n\t * G8: single sink for a gate outcome. Keeps the latest-outcome getter behavior identical (the\n\t * full {@link GateOutcome} still lands in `_lastAutonomyGateOutcome`), and additionally appends a\n\t * bounded codes-only entry to {@link _gateOutcomeHistory} (oldest evicted at\n\t * {@link GATE_OUTCOME_HISTORY_LIMIT}) and emits the `gate_outcome` telemetry event. The history\n\t * tail therefore always mirrors the latest outcome. Only called with an active envelope.\n\t */\n\trecordGateOutcome(outcome: GateOutcome): void {\n\t\tthis._lastAutonomyGateOutcome = outcome;\n\t\tconst at = new Date().toISOString();\n\t\tthis._gateOutcomeHistory.push({\n\t\t\toutcome: outcome.outcome,\n\t\t\tgate: outcome.gate,\n\t\t\treasonCode: outcome.reasonCode,\n\t\t\tat,\n\t\t});\n\t\twhile (this._gateOutcomeHistory.length > GATE_OUTCOME_HISTORY_LIMIT) {\n\t\t\tthis._gateOutcomeHistory.shift();\n\t\t}\n\t\t// G8: gate outcome event. Codes/ids only — never the gate's human-facing message.\n\t\tthis.emitTelemetry({\n\t\t\ttype: AUTONOMY_TELEMETRY_EVENT_TYPES.gateOutcome,\n\t\t\ttimestamp: at,\n\t\t\tpayload: {\n\t\t\t\toutcome: outcome.outcome,\n\t\t\t\tgate: outcome.gate,\n\t\t\t\treasonCode: outcome.reasonCode,\n\t\t\t},\n\t\t});\n\t}\n\n\t/** G8: copies of the bounded gate-outcome history, oldest first, latest last. */\n\tgetGateOutcomeHistory(): GateOutcomeHistoryEntry[] {\n\t\treturn this._gateOutcomeHistory.map((entry) => ({ ...entry }));\n\t}\n\n\tgetStatusSnapshot(): AutonomyStatusSnapshot {\n\t\tconst snapshot: AutonomyStatusSnapshot = {};\n\n\t\tconst lastRouterDecision = this.deps.getLastModelRouterDecision();\n\t\tif (lastRouterDecision?.route) {\n\t\t\tsnapshot.latestRoute = {\n\t\t\t\ttier: lastRouterDecision.route.tier,\n\t\t\t\treasonCode: lastRouterDecision.route.reasonCode,\n\t\t\t\trisk: lastRouterDecision.route.risk,\n\t\t\t};\n\t\t}\n\n\t\tif (this._lastAutonomyGateOutcome) {\n\t\t\tsnapshot.latestGate = {\n\t\t\t\toutcome: this._lastAutonomyGateOutcome.outcome,\n\t\t\t\tgate: this._lastAutonomyGateOutcome.gate,\n\t\t\t\treasonCode: this._lastAutonomyGateOutcome.reasonCode,\n\t\t\t};\n\t\t}\n\n\t\tconst currentCost = this.deps.getSessionStats().cost;\n\t\tif (currentCost > 0) {\n\t\t\tsnapshot.currentCostUsd = currentCost;\n\t\t}\n\n\t\tconst spawnedCost = this.deps.getSpawnedUsage().cost;\n\t\tif (spawnedCost > 0) {\n\t\t\tsnapshot.spawnedCostUsd = spawnedCost;\n\t\t}\n\n\t\tconst dailyCost = this.deps.getDailyUsageTotals()?.totalCost;\n\t\tif (dailyCost !== undefined && dailyCost > 0) {\n\t\t\tsnapshot.dailyCostUsd = dailyCost;\n\t\t}\n\n\t\tconst goal = this.deps.getGoalStateSnapshot();\n\t\tif (goal) {\n\t\t\tsnapshot.activeGoal = {\n\t\t\t\tgoalId: goal.goalId,\n\t\t\t\tstatus: goal.status,\n\t\t\t\topenRequirements: goal.requirements.filter((requirement) => requirement.status === \"open\").length,\n\t\t\t\tstallTurns: goal.stallTurns,\n\t\t\t};\n\t\t}\n\n\t\t// Real live count from the lane tracker — never inferred from historical snapshots. Absent\n\t\t// while zero, matching the presence-means-signal convention of the sibling fields.\n\t\tconst activeLaneCount = this.deps.getActiveLaneCount();\n\t\tif (activeLaneCount > 0) {\n\t\t\tsnapshot.activeLaneCount = activeLaneCount;\n\t\t}\n\n\t\treturn snapshot;\n\t}\n\n\t/**\n\t * Aggregate an effectiveness/autonomy dashboard: what Pi has actually been doing (recent\n\t * route choices, latest gate outcome, cost, and any research/delegation/learning/goal\n\t * activity). Read-only — combines existing session-log getters, never mutates state or\n\t * recomputes a route/gate decision.\n\t */\n\tgetDiagnosticSnapshot(options?: { maxEntriesPerFamily?: number }): AutonomyDiagnosticSnapshot {\n\t\tconst maxEntriesPerFamily = options?.maxEntriesPerFamily ?? 10;\n\t\tconst snapshot: AutonomyDiagnosticSnapshot = {};\n\t\tconst goal = this.deps.getGoalStateSnapshot();\n\n\t\tconst recentDecisions = getRecentModelRouterDecisions(\n\t\t\tthis.deps.getSessionManager().getEntries(),\n\t\t\tmaxEntriesPerFamily,\n\t\t);\n\t\tif (recentDecisions.length > 0) {\n\t\t\tsnapshot.routes = recentDecisions.map(\n\t\t\t\t(decision): DiagnosticEntry => ({\n\t\t\t\t\ttitle: decision.route.tier,\n\t\t\t\t\tsummary: decision.routedModel,\n\t\t\t\t\treasonCode: decision.route.reasonCode,\n\t\t\t\t\tmetadata: { risk: decision.route.risk, outcome: decision.outcome, intent: decision.intent },\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tif (this._lastAutonomyGateOutcome) {\n\t\t\tconst gate = this._lastAutonomyGateOutcome;\n\t\t\tsnapshot.gates = [\n\t\t\t\t{\n\t\t\t\t\ttitle: gate.gate,\n\t\t\t\t\tsummary: gate.message,\n\t\t\t\t\treasonCode: gate.reasonCode,\n\t\t\t\t\tmetadata: { outcome: gate.outcome, reversible: gate.reversible },\n\t\t\t\t},\n\t\t\t];\n\t\t}\n\n\t\tconst costs: DiagnosticEntry[] = [];\n\t\tconst currentCostForDiagnostics = this.deps.getSessionStats().cost;\n\t\tif (currentCostForDiagnostics > 0) {\n\t\t\tcosts.push({ title: \"current\", summary: `$${currentCostForDiagnostics.toFixed(4)}` });\n\t\t}\n\t\tconst spawnedCost = this.deps.getSpawnedUsage().cost;\n\t\tif (spawnedCost > 0) costs.push({ title: \"spawned\", summary: `$${spawnedCost.toFixed(4)}` });\n\t\tconst dailyCostForDiagnostics = this.deps.getDailyUsageTotals()?.totalCost;\n\t\tif (dailyCostForDiagnostics !== undefined && dailyCostForDiagnostics > 0) {\n\t\t\tcosts.push({ title: \"daily\", summary: `$${dailyCostForDiagnostics.toFixed(4)}` });\n\t\t}\n\t\tif (costs.length > 0) snapshot.costs = costs;\n\n\t\tconst researchEntries: DiagnosticEntry[] = [];\n\t\tconst researchLaneRecords = getLaneRecordSnapshots(this.deps.getSessionManager().getEntries()).filter(\n\t\t\t(record) => record.type === \"research\",\n\t\t);\n\t\tfor (const record of researchLaneRecords.slice(-maxEntriesPerFamily)) {\n\t\t\tresearchEntries.push({\n\t\t\t\ttitle: `Lane ${record.laneId} (${record.status})`,\n\t\t\t\treasonCode: record.reasonCode,\n\t\t\t\tmetadata: {\n\t\t\t\t\tcostUsd: record.costUsd,\n\t\t\t\t\tstartedAt: record.startedAt,\n\t\t\t\t\tcompletedAt: record.completedAt,\n\t\t\t\t\tgoalId: record.goalId,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t\tfor (const bundle of this.deps.getEvidenceBundleSnapshots().slice(-maxEntriesPerFamily)) {\n\t\t\tresearchEntries.push({\n\t\t\t\ttitle: `Research: ${bundle.query}`,\n\t\t\t\tmetadata: { sourceCount: bundle.sources.length, findingCount: bundle.findings.length },\n\t\t\t});\n\t\t}\n\t\tif (this.deps.getLastResearchLaneSkipReason()) {\n\t\t\tresearchEntries.push({ title: \"Last skip\", reasonCode: this.deps.getLastResearchLaneSkipReason() });\n\t\t}\n\t\tif (researchEntries.length > 0) {\n\t\t\tsnapshot.research = researchEntries;\n\t\t}\n\n\t\tconst delegationEntries: DiagnosticEntry[] = [];\n\t\tconst workerLaneRecords = getLaneRecordSnapshots(this.deps.getSessionManager().getEntries()).filter(\n\t\t\t(record) => record.type === \"worker\",\n\t\t);\n\t\tfor (const record of workerLaneRecords.slice(-maxEntriesPerFamily)) {\n\t\t\tdelegationEntries.push({\n\t\t\t\ttitle: `Lane ${record.laneId} (${record.status})`,\n\t\t\t\treasonCode: record.reasonCode,\n\t\t\t\tmetadata: { costUsd: record.costUsd, startedAt: record.startedAt, completedAt: record.completedAt },\n\t\t\t});\n\t\t}\n\t\tconst workerResults = this.deps.getWorkerResultSnapshots();\n\t\tfor (const result of workerResults.slice(-maxEntriesPerFamily)) {\n\t\t\tdelegationEntries.push({\n\t\t\t\ttitle: `Worker ${result.requestId} (${result.status})`,\n\t\t\t\tsummary: result.summary,\n\t\t\t\tmetadata: {\n\t\t\t\t\tchangedFileCount: result.changedFiles.length,\n\t\t\t\t\tblockerCount: result.blockers?.length ?? 0,\n\t\t\t\t\tusageReportId: result.usageReportId,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t\tif (delegationEntries.length > 0) {\n\t\t\tsnapshot.delegation = delegationEntries;\n\t\t}\n\n\t\tconst learningEntries: DiagnosticEntry[] = [];\n\t\tconst learningDecisions = this.deps.getLearningDecisionSnapshots();\n\t\tfor (const decision of learningDecisions.slice(-maxEntriesPerFamily)) {\n\t\t\tlearningEntries.push({\n\t\t\t\ttitle: `Learning (${decision.kind})`,\n\t\t\t\tsummary: decision.summary,\n\t\t\t\treasonCode: decision.reasonCode,\n\t\t\t\tmetadata: { confidence: decision.confidence, requiresApproval: decision.requiresApproval },\n\t\t\t});\n\t\t}\n\t\tfor (const audit of this.deps.getLearningAuditRecords().slice(-maxEntriesPerFamily)) {\n\t\t\tlearningEntries.push({\n\t\t\t\ttitle: `Audit ${audit.id} (${audit.action})`,\n\t\t\t\tsummary: audit.summary,\n\t\t\t\treasonCode: audit.reasonCode,\n\t\t\t\tmetadata: { layer: audit.layer, proposalId: audit.proposalId, rollbackOf: audit.rollbackOf },\n\t\t\t});\n\t\t}\n\t\tif (learningEntries.length > 0) {\n\t\t\tsnapshot.learning = learningEntries;\n\t\t}\n\n\t\tif (goal) {\n\t\t\tsnapshot.goals = [\n\t\t\t\t{\n\t\t\t\t\ttitle: `Goal ${goal.goalId}`,\n\t\t\t\t\tsummary: goal.userGoal,\n\t\t\t\t\treasonCode: goal.status,\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\topenRequirementCount: goal.requirements.filter((requirement) => requirement.status === \"open\").length,\n\t\t\t\t\t\tstallTurns: goal.stallTurns,\n\t\t\t\t\t\tblockedReason: goal.blockedReason,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t];\n\t\t}\n\n\t\tconst processMemory = getProcessMemoryMb();\n\t\tsnapshot.processMemory = [\n\t\t\t{\n\t\t\t\ttitle: \"process\",\n\t\t\t\tmetadata: {\n\t\t\t\t\trssMb: processMemory.rssMb,\n\t\t\t\t\theapUsedMb: processMemory.heapUsedMb,\n\t\t\t\t\texternalMb: processMemory.externalMb,\n\t\t\t\t},\n\t\t\t},\n\t\t];\n\n\t\treturn snapshot;\n\t}\n}\n"]}
1
+ {"version":3,"file":"autonomy-telemetry.d.ts","sourceRoot":"","sources":["../../src/core/autonomy-telemetry.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAErE,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAE3G,OAAO,KAAK,EACX,0BAA0B,EAC1B,sBAAsB,EAEtB,uBAAuB,EACvB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAEN,KAAK,sBAAsB,EAE3B,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAA+C,KAAK,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC9G,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EAAiC,KAAK,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AASzG,MAAM,WAAW,qBAAqB;IACrC,yGAAyG;IACzG,iBAAiB,IAAI,IAAI,CAAC,cAAc,EAAE,mBAAmB,GAAG,YAAY,CAAC,CAAC;IAC9E,6GAA2G;IAC3G,0BAA0B,IAAI,yBAAyB,GAAG,SAAS,CAAC;IACpE,0GAAwG;IACxG,6BAA6B,IAAI,MAAM,GAAG,SAAS,CAAC;IACpD,wEAAwE;IACxE,cAAc,IAAI,kBAAkB,CAAC;IACrC,+CAA+C;IAC/C,oBAAoB,IAAI,SAAS,GAAG,SAAS,CAAC;IAC9C,qGAAmG;IACnG,kBAAkB,IAAI,MAAM,CAAC;IAC7B,uDAAuD;IACvD,0BAA0B,IAAI,cAAc,EAAE,CAAC;IAC/C,gEAAgE;IAChE,wBAAwB,IAAI,YAAY,EAAE,CAAC;IAC3C,yDAAyD;IACzD,4BAA4B,IAAI,gBAAgB,EAAE,CAAC;IACnD,oDAAoD;IACpD,uBAAuB,IAAI,mBAAmB,EAAE,CAAC;CACjD;AAED,qBAAa,iBAAiB;IAC7B,OAAO,CAAC,wBAAwB,CAAC,CAAc;IAC/C,qGAAqG;IACrG,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAiC;IAErE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAwB;IAE7C,YAAY,IAAI,EAAE,qBAAqB,EAEtC;IAED;;;;;;OAMG;IACH,aAAa,CAAC,KAAK,EAAE,sBAAsB,GAAG,IAAI,CAOjD;IAED;;;;;;OAMG;IACH,iBAAiB,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,CAsB5C;IAED,iFAAiF;IACjF,qBAAqB,IAAI,uBAAuB,EAAE,CAEjD;IAED,iBAAiB,IAAI,sBAAsB,CA2C1C;IAED;;;;;OAKG;IACH,qBAAqB,CAAC,OAAO,CAAC,EAAE;QAAE,mBAAmB,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,0BAA0B,CA0J5F;CACD","sourcesContent":["/**\n * Autonomy telemetry sink + status/diagnostic snapshots.\n *\n * Extracted verbatim from agent-session.ts (god-file decomposition). Owns the latest gate outcome\n * and the bounded gate-outcome history, and is the single sink for G3 autonomy-telemetry custom\n * entries. The two snapshot builders READ broadly across live session state (router decision, cost,\n * goal, lanes, and the research/delegation/learning/goal getters) through narrow deps accessors\n * rather than the whole AgentSession — they never mutate anything but the owned gate-outcome fields.\n */\n\nimport type { SessionManager } from \"@caupulican/pi-agent-core/node\";\nimport { getProcessMemoryMb } from \"../utils/process-memory.ts\";\nimport type { EvidenceBundle, GateOutcome, LearningDecision, WorkerResult } from \"./autonomy/contracts.ts\";\nimport { getLaneRecordSnapshots } from \"./autonomy/session-lane-record.ts\";\nimport type {\n\tAutonomyDiagnosticSnapshot,\n\tAutonomyStatusSnapshot,\n\tDiagnosticEntry,\n\tGateOutcomeHistoryEntry,\n} from \"./autonomy/status.ts\";\nimport {\n\tAUTONOMY_TELEMETRY_EVENT_TYPES,\n\ttype AutonomyTelemetryEvent,\n\tredactTelemetryValue,\n} from \"./autonomy/telemetry-events.ts\";\nimport { hasCostSummarySignal, hasSubagentCostSignal, type SessionCostSummary } from \"./cost/cost-summary.ts\";\nimport type { GoalState } from \"./goals/goal-state.ts\";\nimport type { LearningAuditRecord } from \"./learning/learning-audit.ts\";\nimport { getRecentModelRouterDecisions, type ModelRouterDecisionStatus } from \"./model-router/status.ts\";\n\n/** Custom-entry type for G3 autonomy telemetry. Distinct from the router/lane record types so a\n * telemetry consumer can filter on it without decoding operational snapshots. */\nconst AUTONOMY_TELEMETRY_CUSTOM_TYPE = \"autonomy-telemetry\";\n\n/** G8: bound on the in-memory gate-outcome history. Oldest entries evict once the cap is reached. */\nconst GATE_OUTCOME_HISTORY_LIMIT = 50;\n\nexport interface AutonomyTelemetryDeps {\n\t/** Session log: `appendCustomEntry` is the telemetry sink; `getEntries` feeds diagnostic aggregation. */\n\tgetSessionManager(): Pick<SessionManager, \"appendCustomEntry\" | \"getEntries\">;\n\t/** Latest model-router decision, for the snapshot's `latestRoute` — owned by the router, not this sink. */\n\tgetLastModelRouterDecision(): ModelRouterDecisionStatus | undefined;\n\t/** Reason the last research lane was skipped, if any — surfaced in the diagnostic's research family. */\n\tgetLastResearchLaneSkipReason(): string | undefined;\n\t/** Single source of truth for CURRENT/TODAY/SUBAGENTS display costs. */\n\tgetCostSummary(): SessionCostSummary;\n\t/** Current goal state, if a goal is active. */\n\tgetGoalStateSnapshot(): GoalState | undefined;\n\t/** Live count of active lanes from the lane tracker — never inferred from historical snapshots. */\n\tgetActiveLaneCount(): number;\n\t/** Research evidence bundles recorded this session. */\n\tgetEvidenceBundleSnapshots(): EvidenceBundle[];\n\t/** Worker-delegation result snapshots recorded this session. */\n\tgetWorkerResultSnapshots(): WorkerResult[];\n\t/** Learning decision snapshots recorded this session. */\n\tgetLearningDecisionSnapshots(): LearningDecision[];\n\t/** Learning audit records recorded this session. */\n\tgetLearningAuditRecords(): LearningAuditRecord[];\n}\n\nexport class AutonomyTelemetry {\n\tprivate _lastAutonomyGateOutcome?: GateOutcome;\n\t/** G8: bounded (cap {@link GATE_OUTCOME_HISTORY_LIMIT}) history of gate outcomes; tail is latest. */\n\tprivate readonly _gateOutcomeHistory: GateOutcomeHistoryEntry[] = [];\n\n\tprivate readonly deps: AutonomyTelemetryDeps;\n\n\tconstructor(deps: AutonomyTelemetryDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\t/**\n\t * G3: bounded autonomy-telemetry sink. Passes the whole event through {@link redactTelemetryValue}\n\t * (the taxonomy's redaction contract) before storing it, so a secret that leaked into a payload\n\t * field never lands in the session log. Observe-only: a failure here can never surface into the\n\t * turn it is measuring, so the whole body is swallowed. Payloads MUST stay small (ids, codes,\n\t * numbers) — never prompt/summary text; callers own that discipline.\n\t */\n\temitTelemetry(event: AutonomyTelemetryEvent): void {\n\t\ttry {\n\t\t\tconst redacted = redactTelemetryValue(event) as Record<string, unknown>;\n\t\t\tthis.deps.getSessionManager().appendCustomEntry(AUTONOMY_TELEMETRY_CUSTOM_TYPE, { version: 1, ...redacted });\n\t\t} catch {\n\t\t\t// Telemetry is best-effort: swallow so a sink failure cannot break the observed turn.\n\t\t}\n\t}\n\n\t/**\n\t * G8: single sink for a gate outcome. Keeps the latest-outcome getter behavior identical (the\n\t * full {@link GateOutcome} still lands in `_lastAutonomyGateOutcome`), and additionally appends a\n\t * bounded codes-only entry to {@link _gateOutcomeHistory} (oldest evicted at\n\t * {@link GATE_OUTCOME_HISTORY_LIMIT}) and emits the `gate_outcome` telemetry event. The history\n\t * tail therefore always mirrors the latest outcome. Only called with an active envelope.\n\t */\n\trecordGateOutcome(outcome: GateOutcome): void {\n\t\tthis._lastAutonomyGateOutcome = outcome;\n\t\tconst at = new Date().toISOString();\n\t\tthis._gateOutcomeHistory.push({\n\t\t\toutcome: outcome.outcome,\n\t\t\tgate: outcome.gate,\n\t\t\treasonCode: outcome.reasonCode,\n\t\t\tat,\n\t\t});\n\t\twhile (this._gateOutcomeHistory.length > GATE_OUTCOME_HISTORY_LIMIT) {\n\t\t\tthis._gateOutcomeHistory.shift();\n\t\t}\n\t\t// G8: gate outcome event. Codes/ids only — never the gate's human-facing message.\n\t\tthis.emitTelemetry({\n\t\t\ttype: AUTONOMY_TELEMETRY_EVENT_TYPES.gateOutcome,\n\t\t\ttimestamp: at,\n\t\t\tpayload: {\n\t\t\t\toutcome: outcome.outcome,\n\t\t\t\tgate: outcome.gate,\n\t\t\t\treasonCode: outcome.reasonCode,\n\t\t\t},\n\t\t});\n\t}\n\n\t/** G8: copies of the bounded gate-outcome history, oldest first, latest last. */\n\tgetGateOutcomeHistory(): GateOutcomeHistoryEntry[] {\n\t\treturn this._gateOutcomeHistory.map((entry) => ({ ...entry }));\n\t}\n\n\tgetStatusSnapshot(): AutonomyStatusSnapshot {\n\t\tconst snapshot: AutonomyStatusSnapshot = {};\n\n\t\tconst lastRouterDecision = this.deps.getLastModelRouterDecision();\n\t\tif (lastRouterDecision?.route) {\n\t\t\tsnapshot.latestRoute = {\n\t\t\t\ttier: lastRouterDecision.route.tier,\n\t\t\t\treasonCode: lastRouterDecision.route.reasonCode,\n\t\t\t\trisk: lastRouterDecision.route.risk,\n\t\t\t};\n\t\t}\n\n\t\tif (this._lastAutonomyGateOutcome) {\n\t\t\tsnapshot.latestGate = {\n\t\t\t\toutcome: this._lastAutonomyGateOutcome.outcome,\n\t\t\t\tgate: this._lastAutonomyGateOutcome.gate,\n\t\t\t\treasonCode: this._lastAutonomyGateOutcome.reasonCode,\n\t\t\t};\n\t\t}\n\n\t\tconst costSummary = this.deps.getCostSummary();\n\t\tif (hasCostSummarySignal(costSummary)) {\n\t\t\tsnapshot.costSummary = costSummary;\n\t\t}\n\n\t\tconst goal = this.deps.getGoalStateSnapshot();\n\t\tif (goal) {\n\t\t\tsnapshot.activeGoal = {\n\t\t\t\tgoalId: goal.goalId,\n\t\t\t\tstatus: goal.status,\n\t\t\t\topenRequirements: goal.requirements.filter((requirement) => requirement.status === \"open\").length,\n\t\t\t\tstallTurns: goal.stallTurns,\n\t\t\t};\n\t\t}\n\n\t\t// Real live count from the lane tracker — never inferred from historical snapshots. Absent\n\t\t// while zero, matching the presence-means-signal convention of the sibling fields.\n\t\tconst activeLaneCount = this.deps.getActiveLaneCount();\n\t\tif (activeLaneCount > 0) {\n\t\t\tsnapshot.activeLaneCount = activeLaneCount;\n\t\t}\n\n\t\treturn snapshot;\n\t}\n\n\t/**\n\t * Aggregate an effectiveness/autonomy dashboard: what Pi has actually been doing (recent\n\t * route choices, latest gate outcome, cost, and any research/delegation/learning/goal\n\t * activity). Read-only — combines existing session-log getters, never mutates state or\n\t * recomputes a route/gate decision.\n\t */\n\tgetDiagnosticSnapshot(options?: { maxEntriesPerFamily?: number }): AutonomyDiagnosticSnapshot {\n\t\tconst maxEntriesPerFamily = options?.maxEntriesPerFamily ?? 10;\n\t\tconst snapshot: AutonomyDiagnosticSnapshot = {};\n\t\tconst goal = this.deps.getGoalStateSnapshot();\n\n\t\tconst recentDecisions = getRecentModelRouterDecisions(\n\t\t\tthis.deps.getSessionManager().getEntries(),\n\t\t\tmaxEntriesPerFamily,\n\t\t);\n\t\tif (recentDecisions.length > 0) {\n\t\t\tsnapshot.routes = recentDecisions.map(\n\t\t\t\t(decision): DiagnosticEntry => ({\n\t\t\t\t\ttitle: decision.route.tier,\n\t\t\t\t\tsummary: decision.routedModel,\n\t\t\t\t\treasonCode: decision.route.reasonCode,\n\t\t\t\t\tmetadata: { risk: decision.route.risk, outcome: decision.outcome, intent: decision.intent },\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tif (this._lastAutonomyGateOutcome) {\n\t\t\tconst gate = this._lastAutonomyGateOutcome;\n\t\t\tsnapshot.gates = [\n\t\t\t\t{\n\t\t\t\t\ttitle: gate.gate,\n\t\t\t\t\tsummary: gate.message,\n\t\t\t\t\treasonCode: gate.reasonCode,\n\t\t\t\t\tmetadata: { outcome: gate.outcome, reversible: gate.reversible },\n\t\t\t\t},\n\t\t\t];\n\t\t}\n\n\t\tconst costs: DiagnosticEntry[] = [];\n\t\tconst costSummary = this.deps.getCostSummary();\n\t\tif (costSummary.currentCost > 0 || hasSubagentCostSignal(costSummary)) {\n\t\t\tcosts.push({ title: \"CURRENT\", summary: `$${costSummary.currentCost.toFixed(4)}` });\n\t\t}\n\t\tif (costSummary.todayCost > 0) {\n\t\t\tcosts.push({ title: \"TODAY\", summary: `$${costSummary.todayCost.toFixed(4)}` });\n\t\t}\n\t\tif (hasSubagentCostSignal(costSummary)) {\n\t\t\tcosts.push({\n\t\t\t\ttitle: \"SUBAGENTS\",\n\t\t\t\tsummary: `$${costSummary.subagentCost.toFixed(4)} included in CURRENT`,\n\t\t\t});\n\t\t}\n\t\tif (costs.length > 0) snapshot.costs = costs;\n\n\t\tconst researchEntries: DiagnosticEntry[] = [];\n\t\tconst researchLaneRecords = getLaneRecordSnapshots(this.deps.getSessionManager().getEntries()).filter(\n\t\t\t(record) => record.type === \"research\",\n\t\t);\n\t\tfor (const record of researchLaneRecords.slice(-maxEntriesPerFamily)) {\n\t\t\tresearchEntries.push({\n\t\t\t\ttitle: `Lane ${record.laneId} (${record.status})`,\n\t\t\t\treasonCode: record.reasonCode,\n\t\t\t\tmetadata: {\n\t\t\t\t\tcostUsd: record.costUsd,\n\t\t\t\t\tstartedAt: record.startedAt,\n\t\t\t\t\tcompletedAt: record.completedAt,\n\t\t\t\t\tgoalId: record.goalId,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t\tfor (const bundle of this.deps.getEvidenceBundleSnapshots().slice(-maxEntriesPerFamily)) {\n\t\t\tresearchEntries.push({\n\t\t\t\ttitle: `Research: ${bundle.query}`,\n\t\t\t\tmetadata: { sourceCount: bundle.sources.length, findingCount: bundle.findings.length },\n\t\t\t});\n\t\t}\n\t\tif (this.deps.getLastResearchLaneSkipReason()) {\n\t\t\tresearchEntries.push({ title: \"Last skip\", reasonCode: this.deps.getLastResearchLaneSkipReason() });\n\t\t}\n\t\tif (researchEntries.length > 0) {\n\t\t\tsnapshot.research = researchEntries;\n\t\t}\n\n\t\tconst delegationEntries: DiagnosticEntry[] = [];\n\t\tconst workerLaneRecords = getLaneRecordSnapshots(this.deps.getSessionManager().getEntries()).filter(\n\t\t\t(record) => record.type === \"worker\",\n\t\t);\n\t\tfor (const record of workerLaneRecords.slice(-maxEntriesPerFamily)) {\n\t\t\tdelegationEntries.push({\n\t\t\t\ttitle: `Lane ${record.laneId} (${record.status})`,\n\t\t\t\treasonCode: record.reasonCode,\n\t\t\t\tmetadata: { costUsd: record.costUsd, startedAt: record.startedAt, completedAt: record.completedAt },\n\t\t\t});\n\t\t}\n\t\tconst workerResults = this.deps.getWorkerResultSnapshots();\n\t\tfor (const result of workerResults.slice(-maxEntriesPerFamily)) {\n\t\t\tdelegationEntries.push({\n\t\t\t\ttitle: `Worker ${result.requestId} (${result.status})`,\n\t\t\t\tsummary: result.summary,\n\t\t\t\tmetadata: {\n\t\t\t\t\tchangedFileCount: result.changedFiles.length,\n\t\t\t\t\tblockerCount: result.blockers?.length ?? 0,\n\t\t\t\t\tusageReportId: result.usageReportId,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t\tif (delegationEntries.length > 0) {\n\t\t\tsnapshot.delegation = delegationEntries;\n\t\t}\n\n\t\tconst learningEntries: DiagnosticEntry[] = [];\n\t\tconst learningDecisions = this.deps.getLearningDecisionSnapshots();\n\t\tfor (const decision of learningDecisions.slice(-maxEntriesPerFamily)) {\n\t\t\tlearningEntries.push({\n\t\t\t\ttitle: `Learning (${decision.kind})`,\n\t\t\t\tsummary: decision.summary,\n\t\t\t\treasonCode: decision.reasonCode,\n\t\t\t\tmetadata: { confidence: decision.confidence, requiresApproval: decision.requiresApproval },\n\t\t\t});\n\t\t}\n\t\tfor (const audit of this.deps.getLearningAuditRecords().slice(-maxEntriesPerFamily)) {\n\t\t\tlearningEntries.push({\n\t\t\t\ttitle: `Audit ${audit.id} (${audit.action})`,\n\t\t\t\tsummary: audit.summary,\n\t\t\t\treasonCode: audit.reasonCode,\n\t\t\t\tmetadata: { layer: audit.layer, proposalId: audit.proposalId, rollbackOf: audit.rollbackOf },\n\t\t\t});\n\t\t}\n\t\tif (learningEntries.length > 0) {\n\t\t\tsnapshot.learning = learningEntries;\n\t\t}\n\n\t\tif (goal) {\n\t\t\tsnapshot.goals = [\n\t\t\t\t{\n\t\t\t\t\ttitle: `Goal ${goal.goalId}`,\n\t\t\t\t\tsummary: goal.userGoal,\n\t\t\t\t\treasonCode: goal.status,\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\topenRequirementCount: goal.requirements.filter((requirement) => requirement.status === \"open\").length,\n\t\t\t\t\t\tstallTurns: goal.stallTurns,\n\t\t\t\t\t\tblockedReason: goal.blockedReason,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t];\n\t\t}\n\n\t\tconst processMemory = getProcessMemoryMb();\n\t\tsnapshot.processMemory = [\n\t\t\t{\n\t\t\t\ttitle: \"process\",\n\t\t\t\tmetadata: {\n\t\t\t\t\trssMb: processMemory.rssMb,\n\t\t\t\t\theapUsedMb: processMemory.heapUsedMb,\n\t\t\t\t\texternalMb: processMemory.externalMb,\n\t\t\t\t},\n\t\t\t},\n\t\t];\n\n\t\treturn snapshot;\n\t}\n}\n"]}
@@ -10,6 +10,7 @@
10
10
  import { getProcessMemoryMb } from "../utils/process-memory.js";
11
11
  import { getLaneRecordSnapshots } from "./autonomy/session-lane-record.js";
12
12
  import { AUTONOMY_TELEMETRY_EVENT_TYPES, redactTelemetryValue, } from "./autonomy/telemetry-events.js";
13
+ import { hasCostSummarySignal, hasSubagentCostSignal } from "./cost/cost-summary.js";
13
14
  import { getRecentModelRouterDecisions } from "./model-router/status.js";
14
15
  /** Custom-entry type for G3 autonomy telemetry. Distinct from the router/lane record types so a
15
16
  * telemetry consumer can filter on it without decoding operational snapshots. */
@@ -91,17 +92,9 @@ export class AutonomyTelemetry {
91
92
  reasonCode: this._lastAutonomyGateOutcome.reasonCode,
92
93
  };
93
94
  }
94
- const currentCost = this.deps.getSessionStats().cost;
95
- if (currentCost > 0) {
96
- snapshot.currentCostUsd = currentCost;
97
- }
98
- const spawnedCost = this.deps.getSpawnedUsage().cost;
99
- if (spawnedCost > 0) {
100
- snapshot.spawnedCostUsd = spawnedCost;
101
- }
102
- const dailyCost = this.deps.getDailyUsageTotals()?.totalCost;
103
- if (dailyCost !== undefined && dailyCost > 0) {
104
- snapshot.dailyCostUsd = dailyCost;
95
+ const costSummary = this.deps.getCostSummary();
96
+ if (hasCostSummarySignal(costSummary)) {
97
+ snapshot.costSummary = costSummary;
105
98
  }
106
99
  const goal = this.deps.getGoalStateSnapshot();
107
100
  if (goal) {
@@ -151,16 +144,18 @@ export class AutonomyTelemetry {
151
144
  ];
152
145
  }
153
146
  const costs = [];
154
- const currentCostForDiagnostics = this.deps.getSessionStats().cost;
155
- if (currentCostForDiagnostics > 0) {
156
- costs.push({ title: "current", summary: `$${currentCostForDiagnostics.toFixed(4)}` });
147
+ const costSummary = this.deps.getCostSummary();
148
+ if (costSummary.currentCost > 0 || hasSubagentCostSignal(costSummary)) {
149
+ costs.push({ title: "CURRENT", summary: `$${costSummary.currentCost.toFixed(4)}` });
157
150
  }
158
- const spawnedCost = this.deps.getSpawnedUsage().cost;
159
- if (spawnedCost > 0)
160
- costs.push({ title: "spawned", summary: `$${spawnedCost.toFixed(4)}` });
161
- const dailyCostForDiagnostics = this.deps.getDailyUsageTotals()?.totalCost;
162
- if (dailyCostForDiagnostics !== undefined && dailyCostForDiagnostics > 0) {
163
- costs.push({ title: "daily", summary: `$${dailyCostForDiagnostics.toFixed(4)}` });
151
+ if (costSummary.todayCost > 0) {
152
+ costs.push({ title: "TODAY", summary: `$${costSummary.todayCost.toFixed(4)}` });
153
+ }
154
+ if (hasSubagentCostSignal(costSummary)) {
155
+ costs.push({
156
+ title: "SUBAGENTS",
157
+ summary: `$${costSummary.subagentCost.toFixed(4)} included in CURRENT`,
158
+ });
164
159
  }
165
160
  if (costs.length > 0)
166
161
  snapshot.costs = costs;
@@ -1 +1 @@
1
- {"version":3,"file":"autonomy-telemetry.js","sourceRoot":"","sources":["../../src/core/autonomy-telemetry.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Autonomy telemetry sink + status/diagnostic snapshots.\n *\n * Extracted verbatim from agent-session.ts (god-file decomposition). Owns the latest gate outcome\n * and the bounded gate-outcome history, and is the single sink for G3 autonomy-telemetry custom\n * entries. The two snapshot builders READ broadly across live session state (router decision, cost,\n * goal, lanes, and the research/delegation/learning/goal getters) through narrow deps accessors\n * rather than the whole AgentSession — they never mutate anything but the owned gate-outcome fields.\n */\n\nimport type { SessionManager } from \"@caupulican/pi-agent-core/node\";\nimport { getProcessMemoryMb } from \"../utils/process-memory.ts\";\nimport type { SessionStats, SpawnedUsageTotals } from \"./agent-session.ts\";\nimport type { EvidenceBundle, GateOutcome, LearningDecision, WorkerResult } from \"./autonomy/contracts.ts\";\nimport { getLaneRecordSnapshots } from \"./autonomy/session-lane-record.ts\";\nimport type {\n\tAutonomyDiagnosticSnapshot,\n\tAutonomyStatusSnapshot,\n\tDiagnosticEntry,\n\tGateOutcomeHistoryEntry,\n} from \"./autonomy/status.ts\";\nimport {\n\tAUTONOMY_TELEMETRY_EVENT_TYPES,\n\ttype AutonomyTelemetryEvent,\n\tredactTelemetryValue,\n} from \"./autonomy/telemetry-events.ts\";\nimport type { DailyUsageTotals } from \"./cost/daily-usage.ts\";\nimport type { GoalState } from \"./goals/goal-state.ts\";\nimport type { LearningAuditRecord } from \"./learning/learning-audit.ts\";\nimport { getRecentModelRouterDecisions, type ModelRouterDecisionStatus } from \"./model-router/status.ts\";\n\n/** Custom-entry type for G3 autonomy telemetry. Distinct from the router/lane record types so a\n * telemetry consumer can filter on it without decoding operational snapshots. */\nconst AUTONOMY_TELEMETRY_CUSTOM_TYPE = \"autonomy-telemetry\";\n\n/** G8: bound on the in-memory gate-outcome history. Oldest entries evict once the cap is reached. */\nconst GATE_OUTCOME_HISTORY_LIMIT = 50;\n\nexport interface AutonomyTelemetryDeps {\n\t/** Session log: `appendCustomEntry` is the telemetry sink; `getEntries` feeds diagnostic aggregation. */\n\tgetSessionManager(): Pick<SessionManager, \"appendCustomEntry\" | \"getEntries\">;\n\t/** Latest model-router decision, for the snapshot's `latestRoute` — owned by the router, not this sink. */\n\tgetLastModelRouterDecision(): ModelRouterDecisionStatus | undefined;\n\t/** Reason the last research lane was skipped, if any — surfaced in the diagnostic's research family. */\n\tgetLastResearchLaneSkipReason(): string | undefined;\n\t/** This session's own usage totals (cost only is read). */\n\tgetSessionStats(): SessionStats;\n\t/** Spawned-child usage totals (cost only is read). */\n\tgetSpawnedUsage(): SpawnedUsageTotals;\n\t/** Cross-session daily usage totals, if trackable — preserves the source's optional-call semantics. */\n\tgetDailyUsageTotals(): DailyUsageTotals | undefined;\n\t/** Current goal state, if a goal is active. */\n\tgetGoalStateSnapshot(): GoalState | undefined;\n\t/** Live count of active lanes from the lane tracker — never inferred from historical snapshots. */\n\tgetActiveLaneCount(): number;\n\t/** Research evidence bundles recorded this session. */\n\tgetEvidenceBundleSnapshots(): EvidenceBundle[];\n\t/** Worker-delegation result snapshots recorded this session. */\n\tgetWorkerResultSnapshots(): WorkerResult[];\n\t/** Learning decision snapshots recorded this session. */\n\tgetLearningDecisionSnapshots(): LearningDecision[];\n\t/** Learning audit records recorded this session. */\n\tgetLearningAuditRecords(): LearningAuditRecord[];\n}\n\nexport class AutonomyTelemetry {\n\tprivate _lastAutonomyGateOutcome?: GateOutcome;\n\t/** G8: bounded (cap {@link GATE_OUTCOME_HISTORY_LIMIT}) history of gate outcomes; tail is latest. */\n\tprivate readonly _gateOutcomeHistory: GateOutcomeHistoryEntry[] = [];\n\n\tprivate readonly deps: AutonomyTelemetryDeps;\n\n\tconstructor(deps: AutonomyTelemetryDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\t/**\n\t * G3: bounded autonomy-telemetry sink. Passes the whole event through {@link redactTelemetryValue}\n\t * (the taxonomy's redaction contract) before storing it, so a secret that leaked into a payload\n\t * field never lands in the session log. Observe-only: a failure here can never surface into the\n\t * turn it is measuring, so the whole body is swallowed. Payloads MUST stay small (ids, codes,\n\t * numbers) — never prompt/summary text; callers own that discipline.\n\t */\n\temitTelemetry(event: AutonomyTelemetryEvent): void {\n\t\ttry {\n\t\t\tconst redacted = redactTelemetryValue(event) as Record<string, unknown>;\n\t\t\tthis.deps.getSessionManager().appendCustomEntry(AUTONOMY_TELEMETRY_CUSTOM_TYPE, { version: 1, ...redacted });\n\t\t} catch {\n\t\t\t// Telemetry is best-effort: swallow so a sink failure cannot break the observed turn.\n\t\t}\n\t}\n\n\t/**\n\t * G8: single sink for a gate outcome. Keeps the latest-outcome getter behavior identical (the\n\t * full {@link GateOutcome} still lands in `_lastAutonomyGateOutcome`), and additionally appends a\n\t * bounded codes-only entry to {@link _gateOutcomeHistory} (oldest evicted at\n\t * {@link GATE_OUTCOME_HISTORY_LIMIT}) and emits the `gate_outcome` telemetry event. The history\n\t * tail therefore always mirrors the latest outcome. Only called with an active envelope.\n\t */\n\trecordGateOutcome(outcome: GateOutcome): void {\n\t\tthis._lastAutonomyGateOutcome = outcome;\n\t\tconst at = new Date().toISOString();\n\t\tthis._gateOutcomeHistory.push({\n\t\t\toutcome: outcome.outcome,\n\t\t\tgate: outcome.gate,\n\t\t\treasonCode: outcome.reasonCode,\n\t\t\tat,\n\t\t});\n\t\twhile (this._gateOutcomeHistory.length > GATE_OUTCOME_HISTORY_LIMIT) {\n\t\t\tthis._gateOutcomeHistory.shift();\n\t\t}\n\t\t// G8: gate outcome event. Codes/ids only — never the gate's human-facing message.\n\t\tthis.emitTelemetry({\n\t\t\ttype: AUTONOMY_TELEMETRY_EVENT_TYPES.gateOutcome,\n\t\t\ttimestamp: at,\n\t\t\tpayload: {\n\t\t\t\toutcome: outcome.outcome,\n\t\t\t\tgate: outcome.gate,\n\t\t\t\treasonCode: outcome.reasonCode,\n\t\t\t},\n\t\t});\n\t}\n\n\t/** G8: copies of the bounded gate-outcome history, oldest first, latest last. */\n\tgetGateOutcomeHistory(): GateOutcomeHistoryEntry[] {\n\t\treturn this._gateOutcomeHistory.map((entry) => ({ ...entry }));\n\t}\n\n\tgetStatusSnapshot(): AutonomyStatusSnapshot {\n\t\tconst snapshot: AutonomyStatusSnapshot = {};\n\n\t\tconst lastRouterDecision = this.deps.getLastModelRouterDecision();\n\t\tif (lastRouterDecision?.route) {\n\t\t\tsnapshot.latestRoute = {\n\t\t\t\ttier: lastRouterDecision.route.tier,\n\t\t\t\treasonCode: lastRouterDecision.route.reasonCode,\n\t\t\t\trisk: lastRouterDecision.route.risk,\n\t\t\t};\n\t\t}\n\n\t\tif (this._lastAutonomyGateOutcome) {\n\t\t\tsnapshot.latestGate = {\n\t\t\t\toutcome: this._lastAutonomyGateOutcome.outcome,\n\t\t\t\tgate: this._lastAutonomyGateOutcome.gate,\n\t\t\t\treasonCode: this._lastAutonomyGateOutcome.reasonCode,\n\t\t\t};\n\t\t}\n\n\t\tconst currentCost = this.deps.getSessionStats().cost;\n\t\tif (currentCost > 0) {\n\t\t\tsnapshot.currentCostUsd = currentCost;\n\t\t}\n\n\t\tconst spawnedCost = this.deps.getSpawnedUsage().cost;\n\t\tif (spawnedCost > 0) {\n\t\t\tsnapshot.spawnedCostUsd = spawnedCost;\n\t\t}\n\n\t\tconst dailyCost = this.deps.getDailyUsageTotals()?.totalCost;\n\t\tif (dailyCost !== undefined && dailyCost > 0) {\n\t\t\tsnapshot.dailyCostUsd = dailyCost;\n\t\t}\n\n\t\tconst goal = this.deps.getGoalStateSnapshot();\n\t\tif (goal) {\n\t\t\tsnapshot.activeGoal = {\n\t\t\t\tgoalId: goal.goalId,\n\t\t\t\tstatus: goal.status,\n\t\t\t\topenRequirements: goal.requirements.filter((requirement) => requirement.status === \"open\").length,\n\t\t\t\tstallTurns: goal.stallTurns,\n\t\t\t};\n\t\t}\n\n\t\t// Real live count from the lane tracker — never inferred from historical snapshots. Absent\n\t\t// while zero, matching the presence-means-signal convention of the sibling fields.\n\t\tconst activeLaneCount = this.deps.getActiveLaneCount();\n\t\tif (activeLaneCount > 0) {\n\t\t\tsnapshot.activeLaneCount = activeLaneCount;\n\t\t}\n\n\t\treturn snapshot;\n\t}\n\n\t/**\n\t * Aggregate an effectiveness/autonomy dashboard: what Pi has actually been doing (recent\n\t * route choices, latest gate outcome, cost, and any research/delegation/learning/goal\n\t * activity). Read-only — combines existing session-log getters, never mutates state or\n\t * recomputes a route/gate decision.\n\t */\n\tgetDiagnosticSnapshot(options?: { maxEntriesPerFamily?: number }): AutonomyDiagnosticSnapshot {\n\t\tconst maxEntriesPerFamily = options?.maxEntriesPerFamily ?? 10;\n\t\tconst snapshot: AutonomyDiagnosticSnapshot = {};\n\t\tconst goal = this.deps.getGoalStateSnapshot();\n\n\t\tconst recentDecisions = getRecentModelRouterDecisions(\n\t\t\tthis.deps.getSessionManager().getEntries(),\n\t\t\tmaxEntriesPerFamily,\n\t\t);\n\t\tif (recentDecisions.length > 0) {\n\t\t\tsnapshot.routes = recentDecisions.map(\n\t\t\t\t(decision): DiagnosticEntry => ({\n\t\t\t\t\ttitle: decision.route.tier,\n\t\t\t\t\tsummary: decision.routedModel,\n\t\t\t\t\treasonCode: decision.route.reasonCode,\n\t\t\t\t\tmetadata: { risk: decision.route.risk, outcome: decision.outcome, intent: decision.intent },\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tif (this._lastAutonomyGateOutcome) {\n\t\t\tconst gate = this._lastAutonomyGateOutcome;\n\t\t\tsnapshot.gates = [\n\t\t\t\t{\n\t\t\t\t\ttitle: gate.gate,\n\t\t\t\t\tsummary: gate.message,\n\t\t\t\t\treasonCode: gate.reasonCode,\n\t\t\t\t\tmetadata: { outcome: gate.outcome, reversible: gate.reversible },\n\t\t\t\t},\n\t\t\t];\n\t\t}\n\n\t\tconst costs: DiagnosticEntry[] = [];\n\t\tconst currentCostForDiagnostics = this.deps.getSessionStats().cost;\n\t\tif (currentCostForDiagnostics > 0) {\n\t\t\tcosts.push({ title: \"current\", summary: `$${currentCostForDiagnostics.toFixed(4)}` });\n\t\t}\n\t\tconst spawnedCost = this.deps.getSpawnedUsage().cost;\n\t\tif (spawnedCost > 0) costs.push({ title: \"spawned\", summary: `$${spawnedCost.toFixed(4)}` });\n\t\tconst dailyCostForDiagnostics = this.deps.getDailyUsageTotals()?.totalCost;\n\t\tif (dailyCostForDiagnostics !== undefined && dailyCostForDiagnostics > 0) {\n\t\t\tcosts.push({ title: \"daily\", summary: `$${dailyCostForDiagnostics.toFixed(4)}` });\n\t\t}\n\t\tif (costs.length > 0) snapshot.costs = costs;\n\n\t\tconst researchEntries: DiagnosticEntry[] = [];\n\t\tconst researchLaneRecords = getLaneRecordSnapshots(this.deps.getSessionManager().getEntries()).filter(\n\t\t\t(record) => record.type === \"research\",\n\t\t);\n\t\tfor (const record of researchLaneRecords.slice(-maxEntriesPerFamily)) {\n\t\t\tresearchEntries.push({\n\t\t\t\ttitle: `Lane ${record.laneId} (${record.status})`,\n\t\t\t\treasonCode: record.reasonCode,\n\t\t\t\tmetadata: {\n\t\t\t\t\tcostUsd: record.costUsd,\n\t\t\t\t\tstartedAt: record.startedAt,\n\t\t\t\t\tcompletedAt: record.completedAt,\n\t\t\t\t\tgoalId: record.goalId,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t\tfor (const bundle of this.deps.getEvidenceBundleSnapshots().slice(-maxEntriesPerFamily)) {\n\t\t\tresearchEntries.push({\n\t\t\t\ttitle: `Research: ${bundle.query}`,\n\t\t\t\tmetadata: { sourceCount: bundle.sources.length, findingCount: bundle.findings.length },\n\t\t\t});\n\t\t}\n\t\tif (this.deps.getLastResearchLaneSkipReason()) {\n\t\t\tresearchEntries.push({ title: \"Last skip\", reasonCode: this.deps.getLastResearchLaneSkipReason() });\n\t\t}\n\t\tif (researchEntries.length > 0) {\n\t\t\tsnapshot.research = researchEntries;\n\t\t}\n\n\t\tconst delegationEntries: DiagnosticEntry[] = [];\n\t\tconst workerLaneRecords = getLaneRecordSnapshots(this.deps.getSessionManager().getEntries()).filter(\n\t\t\t(record) => record.type === \"worker\",\n\t\t);\n\t\tfor (const record of workerLaneRecords.slice(-maxEntriesPerFamily)) {\n\t\t\tdelegationEntries.push({\n\t\t\t\ttitle: `Lane ${record.laneId} (${record.status})`,\n\t\t\t\treasonCode: record.reasonCode,\n\t\t\t\tmetadata: { costUsd: record.costUsd, startedAt: record.startedAt, completedAt: record.completedAt },\n\t\t\t});\n\t\t}\n\t\tconst workerResults = this.deps.getWorkerResultSnapshots();\n\t\tfor (const result of workerResults.slice(-maxEntriesPerFamily)) {\n\t\t\tdelegationEntries.push({\n\t\t\t\ttitle: `Worker ${result.requestId} (${result.status})`,\n\t\t\t\tsummary: result.summary,\n\t\t\t\tmetadata: {\n\t\t\t\t\tchangedFileCount: result.changedFiles.length,\n\t\t\t\t\tblockerCount: result.blockers?.length ?? 0,\n\t\t\t\t\tusageReportId: result.usageReportId,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t\tif (delegationEntries.length > 0) {\n\t\t\tsnapshot.delegation = delegationEntries;\n\t\t}\n\n\t\tconst learningEntries: DiagnosticEntry[] = [];\n\t\tconst learningDecisions = this.deps.getLearningDecisionSnapshots();\n\t\tfor (const decision of learningDecisions.slice(-maxEntriesPerFamily)) {\n\t\t\tlearningEntries.push({\n\t\t\t\ttitle: `Learning (${decision.kind})`,\n\t\t\t\tsummary: decision.summary,\n\t\t\t\treasonCode: decision.reasonCode,\n\t\t\t\tmetadata: { confidence: decision.confidence, requiresApproval: decision.requiresApproval },\n\t\t\t});\n\t\t}\n\t\tfor (const audit of this.deps.getLearningAuditRecords().slice(-maxEntriesPerFamily)) {\n\t\t\tlearningEntries.push({\n\t\t\t\ttitle: `Audit ${audit.id} (${audit.action})`,\n\t\t\t\tsummary: audit.summary,\n\t\t\t\treasonCode: audit.reasonCode,\n\t\t\t\tmetadata: { layer: audit.layer, proposalId: audit.proposalId, rollbackOf: audit.rollbackOf },\n\t\t\t});\n\t\t}\n\t\tif (learningEntries.length > 0) {\n\t\t\tsnapshot.learning = learningEntries;\n\t\t}\n\n\t\tif (goal) {\n\t\t\tsnapshot.goals = [\n\t\t\t\t{\n\t\t\t\t\ttitle: `Goal ${goal.goalId}`,\n\t\t\t\t\tsummary: goal.userGoal,\n\t\t\t\t\treasonCode: goal.status,\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\topenRequirementCount: goal.requirements.filter((requirement) => requirement.status === \"open\").length,\n\t\t\t\t\t\tstallTurns: goal.stallTurns,\n\t\t\t\t\t\tblockedReason: goal.blockedReason,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t];\n\t\t}\n\n\t\tconst processMemory = getProcessMemoryMb();\n\t\tsnapshot.processMemory = [\n\t\t\t{\n\t\t\t\ttitle: \"process\",\n\t\t\t\tmetadata: {\n\t\t\t\t\trssMb: processMemory.rssMb,\n\t\t\t\t\theapUsedMb: processMemory.heapUsedMb,\n\t\t\t\t\texternalMb: processMemory.externalMb,\n\t\t\t\t},\n\t\t\t},\n\t\t];\n\n\t\treturn snapshot;\n\t}\n}\n"]}
1
+ {"version":3,"file":"autonomy-telemetry.js","sourceRoot":"","sources":["../../src/core/autonomy-telemetry.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Autonomy telemetry sink + status/diagnostic snapshots.\n *\n * Extracted verbatim from agent-session.ts (god-file decomposition). Owns the latest gate outcome\n * and the bounded gate-outcome history, and is the single sink for G3 autonomy-telemetry custom\n * entries. The two snapshot builders READ broadly across live session state (router decision, cost,\n * goal, lanes, and the research/delegation/learning/goal getters) through narrow deps accessors\n * rather than the whole AgentSession — they never mutate anything but the owned gate-outcome fields.\n */\n\nimport type { SessionManager } from \"@caupulican/pi-agent-core/node\";\nimport { getProcessMemoryMb } from \"../utils/process-memory.ts\";\nimport type { EvidenceBundle, GateOutcome, LearningDecision, WorkerResult } from \"./autonomy/contracts.ts\";\nimport { getLaneRecordSnapshots } from \"./autonomy/session-lane-record.ts\";\nimport type {\n\tAutonomyDiagnosticSnapshot,\n\tAutonomyStatusSnapshot,\n\tDiagnosticEntry,\n\tGateOutcomeHistoryEntry,\n} from \"./autonomy/status.ts\";\nimport {\n\tAUTONOMY_TELEMETRY_EVENT_TYPES,\n\ttype AutonomyTelemetryEvent,\n\tredactTelemetryValue,\n} from \"./autonomy/telemetry-events.ts\";\nimport { hasCostSummarySignal, hasSubagentCostSignal, type SessionCostSummary } from \"./cost/cost-summary.ts\";\nimport type { GoalState } from \"./goals/goal-state.ts\";\nimport type { LearningAuditRecord } from \"./learning/learning-audit.ts\";\nimport { getRecentModelRouterDecisions, type ModelRouterDecisionStatus } from \"./model-router/status.ts\";\n\n/** Custom-entry type for G3 autonomy telemetry. Distinct from the router/lane record types so a\n * telemetry consumer can filter on it without decoding operational snapshots. */\nconst AUTONOMY_TELEMETRY_CUSTOM_TYPE = \"autonomy-telemetry\";\n\n/** G8: bound on the in-memory gate-outcome history. Oldest entries evict once the cap is reached. */\nconst GATE_OUTCOME_HISTORY_LIMIT = 50;\n\nexport interface AutonomyTelemetryDeps {\n\t/** Session log: `appendCustomEntry` is the telemetry sink; `getEntries` feeds diagnostic aggregation. */\n\tgetSessionManager(): Pick<SessionManager, \"appendCustomEntry\" | \"getEntries\">;\n\t/** Latest model-router decision, for the snapshot's `latestRoute` — owned by the router, not this sink. */\n\tgetLastModelRouterDecision(): ModelRouterDecisionStatus | undefined;\n\t/** Reason the last research lane was skipped, if any — surfaced in the diagnostic's research family. */\n\tgetLastResearchLaneSkipReason(): string | undefined;\n\t/** Single source of truth for CURRENT/TODAY/SUBAGENTS display costs. */\n\tgetCostSummary(): SessionCostSummary;\n\t/** Current goal state, if a goal is active. */\n\tgetGoalStateSnapshot(): GoalState | undefined;\n\t/** Live count of active lanes from the lane tracker — never inferred from historical snapshots. */\n\tgetActiveLaneCount(): number;\n\t/** Research evidence bundles recorded this session. */\n\tgetEvidenceBundleSnapshots(): EvidenceBundle[];\n\t/** Worker-delegation result snapshots recorded this session. */\n\tgetWorkerResultSnapshots(): WorkerResult[];\n\t/** Learning decision snapshots recorded this session. */\n\tgetLearningDecisionSnapshots(): LearningDecision[];\n\t/** Learning audit records recorded this session. */\n\tgetLearningAuditRecords(): LearningAuditRecord[];\n}\n\nexport class AutonomyTelemetry {\n\tprivate _lastAutonomyGateOutcome?: GateOutcome;\n\t/** G8: bounded (cap {@link GATE_OUTCOME_HISTORY_LIMIT}) history of gate outcomes; tail is latest. */\n\tprivate readonly _gateOutcomeHistory: GateOutcomeHistoryEntry[] = [];\n\n\tprivate readonly deps: AutonomyTelemetryDeps;\n\n\tconstructor(deps: AutonomyTelemetryDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\t/**\n\t * G3: bounded autonomy-telemetry sink. Passes the whole event through {@link redactTelemetryValue}\n\t * (the taxonomy's redaction contract) before storing it, so a secret that leaked into a payload\n\t * field never lands in the session log. Observe-only: a failure here can never surface into the\n\t * turn it is measuring, so the whole body is swallowed. Payloads MUST stay small (ids, codes,\n\t * numbers) — never prompt/summary text; callers own that discipline.\n\t */\n\temitTelemetry(event: AutonomyTelemetryEvent): void {\n\t\ttry {\n\t\t\tconst redacted = redactTelemetryValue(event) as Record<string, unknown>;\n\t\t\tthis.deps.getSessionManager().appendCustomEntry(AUTONOMY_TELEMETRY_CUSTOM_TYPE, { version: 1, ...redacted });\n\t\t} catch {\n\t\t\t// Telemetry is best-effort: swallow so a sink failure cannot break the observed turn.\n\t\t}\n\t}\n\n\t/**\n\t * G8: single sink for a gate outcome. Keeps the latest-outcome getter behavior identical (the\n\t * full {@link GateOutcome} still lands in `_lastAutonomyGateOutcome`), and additionally appends a\n\t * bounded codes-only entry to {@link _gateOutcomeHistory} (oldest evicted at\n\t * {@link GATE_OUTCOME_HISTORY_LIMIT}) and emits the `gate_outcome` telemetry event. The history\n\t * tail therefore always mirrors the latest outcome. Only called with an active envelope.\n\t */\n\trecordGateOutcome(outcome: GateOutcome): void {\n\t\tthis._lastAutonomyGateOutcome = outcome;\n\t\tconst at = new Date().toISOString();\n\t\tthis._gateOutcomeHistory.push({\n\t\t\toutcome: outcome.outcome,\n\t\t\tgate: outcome.gate,\n\t\t\treasonCode: outcome.reasonCode,\n\t\t\tat,\n\t\t});\n\t\twhile (this._gateOutcomeHistory.length > GATE_OUTCOME_HISTORY_LIMIT) {\n\t\t\tthis._gateOutcomeHistory.shift();\n\t\t}\n\t\t// G8: gate outcome event. Codes/ids only — never the gate's human-facing message.\n\t\tthis.emitTelemetry({\n\t\t\ttype: AUTONOMY_TELEMETRY_EVENT_TYPES.gateOutcome,\n\t\t\ttimestamp: at,\n\t\t\tpayload: {\n\t\t\t\toutcome: outcome.outcome,\n\t\t\t\tgate: outcome.gate,\n\t\t\t\treasonCode: outcome.reasonCode,\n\t\t\t},\n\t\t});\n\t}\n\n\t/** G8: copies of the bounded gate-outcome history, oldest first, latest last. */\n\tgetGateOutcomeHistory(): GateOutcomeHistoryEntry[] {\n\t\treturn this._gateOutcomeHistory.map((entry) => ({ ...entry }));\n\t}\n\n\tgetStatusSnapshot(): AutonomyStatusSnapshot {\n\t\tconst snapshot: AutonomyStatusSnapshot = {};\n\n\t\tconst lastRouterDecision = this.deps.getLastModelRouterDecision();\n\t\tif (lastRouterDecision?.route) {\n\t\t\tsnapshot.latestRoute = {\n\t\t\t\ttier: lastRouterDecision.route.tier,\n\t\t\t\treasonCode: lastRouterDecision.route.reasonCode,\n\t\t\t\trisk: lastRouterDecision.route.risk,\n\t\t\t};\n\t\t}\n\n\t\tif (this._lastAutonomyGateOutcome) {\n\t\t\tsnapshot.latestGate = {\n\t\t\t\toutcome: this._lastAutonomyGateOutcome.outcome,\n\t\t\t\tgate: this._lastAutonomyGateOutcome.gate,\n\t\t\t\treasonCode: this._lastAutonomyGateOutcome.reasonCode,\n\t\t\t};\n\t\t}\n\n\t\tconst costSummary = this.deps.getCostSummary();\n\t\tif (hasCostSummarySignal(costSummary)) {\n\t\t\tsnapshot.costSummary = costSummary;\n\t\t}\n\n\t\tconst goal = this.deps.getGoalStateSnapshot();\n\t\tif (goal) {\n\t\t\tsnapshot.activeGoal = {\n\t\t\t\tgoalId: goal.goalId,\n\t\t\t\tstatus: goal.status,\n\t\t\t\topenRequirements: goal.requirements.filter((requirement) => requirement.status === \"open\").length,\n\t\t\t\tstallTurns: goal.stallTurns,\n\t\t\t};\n\t\t}\n\n\t\t// Real live count from the lane tracker — never inferred from historical snapshots. Absent\n\t\t// while zero, matching the presence-means-signal convention of the sibling fields.\n\t\tconst activeLaneCount = this.deps.getActiveLaneCount();\n\t\tif (activeLaneCount > 0) {\n\t\t\tsnapshot.activeLaneCount = activeLaneCount;\n\t\t}\n\n\t\treturn snapshot;\n\t}\n\n\t/**\n\t * Aggregate an effectiveness/autonomy dashboard: what Pi has actually been doing (recent\n\t * route choices, latest gate outcome, cost, and any research/delegation/learning/goal\n\t * activity). Read-only — combines existing session-log getters, never mutates state or\n\t * recomputes a route/gate decision.\n\t */\n\tgetDiagnosticSnapshot(options?: { maxEntriesPerFamily?: number }): AutonomyDiagnosticSnapshot {\n\t\tconst maxEntriesPerFamily = options?.maxEntriesPerFamily ?? 10;\n\t\tconst snapshot: AutonomyDiagnosticSnapshot = {};\n\t\tconst goal = this.deps.getGoalStateSnapshot();\n\n\t\tconst recentDecisions = getRecentModelRouterDecisions(\n\t\t\tthis.deps.getSessionManager().getEntries(),\n\t\t\tmaxEntriesPerFamily,\n\t\t);\n\t\tif (recentDecisions.length > 0) {\n\t\t\tsnapshot.routes = recentDecisions.map(\n\t\t\t\t(decision): DiagnosticEntry => ({\n\t\t\t\t\ttitle: decision.route.tier,\n\t\t\t\t\tsummary: decision.routedModel,\n\t\t\t\t\treasonCode: decision.route.reasonCode,\n\t\t\t\t\tmetadata: { risk: decision.route.risk, outcome: decision.outcome, intent: decision.intent },\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tif (this._lastAutonomyGateOutcome) {\n\t\t\tconst gate = this._lastAutonomyGateOutcome;\n\t\t\tsnapshot.gates = [\n\t\t\t\t{\n\t\t\t\t\ttitle: gate.gate,\n\t\t\t\t\tsummary: gate.message,\n\t\t\t\t\treasonCode: gate.reasonCode,\n\t\t\t\t\tmetadata: { outcome: gate.outcome, reversible: gate.reversible },\n\t\t\t\t},\n\t\t\t];\n\t\t}\n\n\t\tconst costs: DiagnosticEntry[] = [];\n\t\tconst costSummary = this.deps.getCostSummary();\n\t\tif (costSummary.currentCost > 0 || hasSubagentCostSignal(costSummary)) {\n\t\t\tcosts.push({ title: \"CURRENT\", summary: `$${costSummary.currentCost.toFixed(4)}` });\n\t\t}\n\t\tif (costSummary.todayCost > 0) {\n\t\t\tcosts.push({ title: \"TODAY\", summary: `$${costSummary.todayCost.toFixed(4)}` });\n\t\t}\n\t\tif (hasSubagentCostSignal(costSummary)) {\n\t\t\tcosts.push({\n\t\t\t\ttitle: \"SUBAGENTS\",\n\t\t\t\tsummary: `$${costSummary.subagentCost.toFixed(4)} included in CURRENT`,\n\t\t\t});\n\t\t}\n\t\tif (costs.length > 0) snapshot.costs = costs;\n\n\t\tconst researchEntries: DiagnosticEntry[] = [];\n\t\tconst researchLaneRecords = getLaneRecordSnapshots(this.deps.getSessionManager().getEntries()).filter(\n\t\t\t(record) => record.type === \"research\",\n\t\t);\n\t\tfor (const record of researchLaneRecords.slice(-maxEntriesPerFamily)) {\n\t\t\tresearchEntries.push({\n\t\t\t\ttitle: `Lane ${record.laneId} (${record.status})`,\n\t\t\t\treasonCode: record.reasonCode,\n\t\t\t\tmetadata: {\n\t\t\t\t\tcostUsd: record.costUsd,\n\t\t\t\t\tstartedAt: record.startedAt,\n\t\t\t\t\tcompletedAt: record.completedAt,\n\t\t\t\t\tgoalId: record.goalId,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t\tfor (const bundle of this.deps.getEvidenceBundleSnapshots().slice(-maxEntriesPerFamily)) {\n\t\t\tresearchEntries.push({\n\t\t\t\ttitle: `Research: ${bundle.query}`,\n\t\t\t\tmetadata: { sourceCount: bundle.sources.length, findingCount: bundle.findings.length },\n\t\t\t});\n\t\t}\n\t\tif (this.deps.getLastResearchLaneSkipReason()) {\n\t\t\tresearchEntries.push({ title: \"Last skip\", reasonCode: this.deps.getLastResearchLaneSkipReason() });\n\t\t}\n\t\tif (researchEntries.length > 0) {\n\t\t\tsnapshot.research = researchEntries;\n\t\t}\n\n\t\tconst delegationEntries: DiagnosticEntry[] = [];\n\t\tconst workerLaneRecords = getLaneRecordSnapshots(this.deps.getSessionManager().getEntries()).filter(\n\t\t\t(record) => record.type === \"worker\",\n\t\t);\n\t\tfor (const record of workerLaneRecords.slice(-maxEntriesPerFamily)) {\n\t\t\tdelegationEntries.push({\n\t\t\t\ttitle: `Lane ${record.laneId} (${record.status})`,\n\t\t\t\treasonCode: record.reasonCode,\n\t\t\t\tmetadata: { costUsd: record.costUsd, startedAt: record.startedAt, completedAt: record.completedAt },\n\t\t\t});\n\t\t}\n\t\tconst workerResults = this.deps.getWorkerResultSnapshots();\n\t\tfor (const result of workerResults.slice(-maxEntriesPerFamily)) {\n\t\t\tdelegationEntries.push({\n\t\t\t\ttitle: `Worker ${result.requestId} (${result.status})`,\n\t\t\t\tsummary: result.summary,\n\t\t\t\tmetadata: {\n\t\t\t\t\tchangedFileCount: result.changedFiles.length,\n\t\t\t\t\tblockerCount: result.blockers?.length ?? 0,\n\t\t\t\t\tusageReportId: result.usageReportId,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t\tif (delegationEntries.length > 0) {\n\t\t\tsnapshot.delegation = delegationEntries;\n\t\t}\n\n\t\tconst learningEntries: DiagnosticEntry[] = [];\n\t\tconst learningDecisions = this.deps.getLearningDecisionSnapshots();\n\t\tfor (const decision of learningDecisions.slice(-maxEntriesPerFamily)) {\n\t\t\tlearningEntries.push({\n\t\t\t\ttitle: `Learning (${decision.kind})`,\n\t\t\t\tsummary: decision.summary,\n\t\t\t\treasonCode: decision.reasonCode,\n\t\t\t\tmetadata: { confidence: decision.confidence, requiresApproval: decision.requiresApproval },\n\t\t\t});\n\t\t}\n\t\tfor (const audit of this.deps.getLearningAuditRecords().slice(-maxEntriesPerFamily)) {\n\t\t\tlearningEntries.push({\n\t\t\t\ttitle: `Audit ${audit.id} (${audit.action})`,\n\t\t\t\tsummary: audit.summary,\n\t\t\t\treasonCode: audit.reasonCode,\n\t\t\t\tmetadata: { layer: audit.layer, proposalId: audit.proposalId, rollbackOf: audit.rollbackOf },\n\t\t\t});\n\t\t}\n\t\tif (learningEntries.length > 0) {\n\t\t\tsnapshot.learning = learningEntries;\n\t\t}\n\n\t\tif (goal) {\n\t\t\tsnapshot.goals = [\n\t\t\t\t{\n\t\t\t\t\ttitle: `Goal ${goal.goalId}`,\n\t\t\t\t\tsummary: goal.userGoal,\n\t\t\t\t\treasonCode: goal.status,\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\topenRequirementCount: goal.requirements.filter((requirement) => requirement.status === \"open\").length,\n\t\t\t\t\t\tstallTurns: goal.stallTurns,\n\t\t\t\t\t\tblockedReason: goal.blockedReason,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t];\n\t\t}\n\n\t\tconst processMemory = getProcessMemoryMb();\n\t\tsnapshot.processMemory = [\n\t\t\t{\n\t\t\t\ttitle: \"process\",\n\t\t\t\tmetadata: {\n\t\t\t\t\trssMb: processMemory.rssMb,\n\t\t\t\t\theapUsedMb: processMemory.heapUsedMb,\n\t\t\t\t\texternalMb: processMemory.externalMb,\n\t\t\t\t},\n\t\t\t},\n\t\t];\n\n\t\treturn snapshot;\n\t}\n}\n"]}
@@ -0,0 +1,26 @@
1
+ import type { SessionEntry } from "@caupulican/pi-agent-core/node";
2
+ import type { DailyUsageTotals, DailyUsageWindow } from "./daily-usage.ts";
3
+ export interface CurrentSessionCostTotals {
4
+ ownCost: number;
5
+ subagentCost: number;
6
+ subagentReports: number;
7
+ currentCost: number;
8
+ }
9
+ export interface SessionCostSummary extends CurrentSessionCostTotals {
10
+ todayCost: number;
11
+ todayOwnCost: number;
12
+ todaySubagentCost: number;
13
+ todayWindow: DailyUsageWindow;
14
+ todayRollover: "local-midnight";
15
+ }
16
+ export declare function aggregateCurrentSessionCostsFromEntries(entries: readonly SessionEntry[]): CurrentSessionCostTotals;
17
+ export declare function createSessionCostSummary(args: {
18
+ entries: readonly SessionEntry[];
19
+ dailyTotals: DailyUsageTotals;
20
+ todayWindow: DailyUsageWindow;
21
+ }): SessionCostSummary;
22
+ export declare function hasCostSummarySignal(summary: SessionCostSummary): boolean;
23
+ export declare function hasSubagentCostSignal(summary: Pick<SessionCostSummary, "subagentReports" | "subagentCost">): boolean;
24
+ export declare function formatFooterCostParts(summary: SessionCostSummary, precision?: number): string[];
25
+ export declare function formatStatusCostSummary(summary: SessionCostSummary, precision?: number): string;
26
+ //# sourceMappingURL=cost-summary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cost-summary.d.ts","sourceRoot":"","sources":["../../../src/core/cost/cost-summary.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAGnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAE3E,MAAM,WAAW,wBAAwB;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,kBAAmB,SAAQ,wBAAwB;IACnE,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,gBAAgB,CAAC;IAC9B,aAAa,EAAE,gBAAgB,CAAC;CAChC;AAWD,wBAAgB,uCAAuC,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,GAAG,wBAAwB,CA+BlH;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE;IAC9C,OAAO,EAAE,SAAS,YAAY,EAAE,CAAC;IACjC,WAAW,EAAE,gBAAgB,CAAC;IAC9B,WAAW,EAAE,gBAAgB,CAAC;CAC9B,GAAG,kBAAkB,CAUrB;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAEzE;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,IAAI,CAAC,kBAAkB,EAAE,iBAAiB,GAAG,cAAc,CAAC,GAAG,OAAO,CAEpH;AAMD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,kBAAkB,EAAE,SAAS,SAAI,GAAG,MAAM,EAAE,CAY1F;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,kBAAkB,EAAE,SAAS,SAAI,GAAG,MAAM,CAS1F","sourcesContent":["import type { SessionEntry } from \"@caupulican/pi-agent-core/node\";\nimport type { AssistantMessage, Usage } from \"@caupulican/pi-ai\";\nimport { SPAWNED_USAGE_CUSTOM_TYPE, type SpawnedUsageReport } from \"../agent-session.ts\";\nimport type { DailyUsageTotals, DailyUsageWindow } from \"./daily-usage.ts\";\n\nexport interface CurrentSessionCostTotals {\n\townCost: number;\n\tsubagentCost: number;\n\tsubagentReports: number;\n\tcurrentCost: number;\n}\n\nexport interface SessionCostSummary extends CurrentSessionCostTotals {\n\ttodayCost: number;\n\ttodayOwnCost: number;\n\ttodaySubagentCost: number;\n\ttodayWindow: DailyUsageWindow;\n\ttodayRollover: \"local-midnight\";\n}\n\nfunction isFiniteCost(value: unknown): value is number {\n\treturn typeof value === \"number\" && Number.isFinite(value);\n}\n\nfunction getUsageTotalCost(usage: Usage | undefined): number | undefined {\n\tconst total = usage?.cost?.total;\n\treturn isFiniteCost(total) ? total : undefined;\n}\n\nexport function aggregateCurrentSessionCostsFromEntries(entries: readonly SessionEntry[]): CurrentSessionCostTotals {\n\tlet ownCost = 0;\n\tlet subagentCost = 0;\n\tlet subagentReports = 0;\n\tconst seenSubagentReportIds = new Set<string>();\n\n\tfor (const entry of entries) {\n\t\tif (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\tconst total = getUsageTotalCost((entry.message as AssistantMessage).usage);\n\t\t\tif (total !== undefined) ownCost += total;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (entry.type !== \"custom\" || entry.customType !== SPAWNED_USAGE_CUSTOM_TYPE) continue;\n\t\tconst report = entry.data as SpawnedUsageReport | undefined;\n\t\tconst total = getUsageTotalCost(report?.usage);\n\t\tif (total === undefined) continue;\n\t\tif (report?.reportId) {\n\t\t\tif (seenSubagentReportIds.has(report.reportId)) continue;\n\t\t\tseenSubagentReportIds.add(report.reportId);\n\t\t}\n\t\tsubagentCost += total;\n\t\tsubagentReports += 1;\n\t}\n\n\treturn {\n\t\townCost,\n\t\tsubagentCost,\n\t\tsubagentReports,\n\t\tcurrentCost: ownCost + subagentCost,\n\t};\n}\n\nexport function createSessionCostSummary(args: {\n\tentries: readonly SessionEntry[];\n\tdailyTotals: DailyUsageTotals;\n\ttodayWindow: DailyUsageWindow;\n}): SessionCostSummary {\n\tconst current = aggregateCurrentSessionCostsFromEntries(args.entries);\n\treturn {\n\t\t...current,\n\t\ttodayCost: args.dailyTotals.totalCost,\n\t\ttodayOwnCost: args.dailyTotals.ownCost,\n\t\ttodaySubagentCost: args.dailyTotals.spawnedCost,\n\t\ttodayWindow: args.todayWindow,\n\t\ttodayRollover: \"local-midnight\",\n\t};\n}\n\nexport function hasCostSummarySignal(summary: SessionCostSummary): boolean {\n\treturn summary.currentCost > 0 || summary.todayCost > 0 || summary.subagentReports > 0;\n}\n\nexport function hasSubagentCostSignal(summary: Pick<SessionCostSummary, \"subagentReports\" | \"subagentCost\">): boolean {\n\treturn summary.subagentReports > 0 || summary.subagentCost > 0;\n}\n\nfunction formatCost(value: number, precision: number): string {\n\treturn `$${value.toFixed(precision)}`;\n}\n\nexport function formatFooterCostParts(summary: SessionCostSummary, precision = 3): string[] {\n\tconst parts: string[] = [];\n\tif (summary.currentCost > 0 || hasSubagentCostSignal(summary)) {\n\t\tparts.push(`CURRENT:${formatCost(summary.currentCost, precision)}`);\n\t}\n\tif (summary.todayCost > 0) {\n\t\tparts.push(`TODAY:${formatCost(summary.todayCost, precision)}`);\n\t}\n\tif (hasSubagentCostSignal(summary)) {\n\t\tparts.push(`SUBAGENTS:${formatCost(summary.subagentCost, precision)} in CURRENT`);\n\t}\n\treturn parts;\n}\n\nexport function formatStatusCostSummary(summary: SessionCostSummary, precision = 4): string {\n\tconst parts = [\n\t\t`CURRENT ${formatCost(summary.currentCost, precision)}`,\n\t\t`TODAY ${formatCost(summary.todayCost, precision)}`,\n\t];\n\tif (hasSubagentCostSignal(summary)) {\n\t\tparts.push(`SUBAGENTS ${formatCost(summary.subagentCost, precision)} (included in CURRENT)`);\n\t}\n\treturn parts.join(\", \");\n}\n"]}
@@ -0,0 +1,85 @@
1
+ import { SPAWNED_USAGE_CUSTOM_TYPE } from "../agent-session.js";
2
+ function isFiniteCost(value) {
3
+ return typeof value === "number" && Number.isFinite(value);
4
+ }
5
+ function getUsageTotalCost(usage) {
6
+ const total = usage?.cost?.total;
7
+ return isFiniteCost(total) ? total : undefined;
8
+ }
9
+ export function aggregateCurrentSessionCostsFromEntries(entries) {
10
+ let ownCost = 0;
11
+ let subagentCost = 0;
12
+ let subagentReports = 0;
13
+ const seenSubagentReportIds = new Set();
14
+ for (const entry of entries) {
15
+ if (entry.type === "message" && entry.message.role === "assistant") {
16
+ const total = getUsageTotalCost(entry.message.usage);
17
+ if (total !== undefined)
18
+ ownCost += total;
19
+ continue;
20
+ }
21
+ if (entry.type !== "custom" || entry.customType !== SPAWNED_USAGE_CUSTOM_TYPE)
22
+ continue;
23
+ const report = entry.data;
24
+ const total = getUsageTotalCost(report?.usage);
25
+ if (total === undefined)
26
+ continue;
27
+ if (report?.reportId) {
28
+ if (seenSubagentReportIds.has(report.reportId))
29
+ continue;
30
+ seenSubagentReportIds.add(report.reportId);
31
+ }
32
+ subagentCost += total;
33
+ subagentReports += 1;
34
+ }
35
+ return {
36
+ ownCost,
37
+ subagentCost,
38
+ subagentReports,
39
+ currentCost: ownCost + subagentCost,
40
+ };
41
+ }
42
+ export function createSessionCostSummary(args) {
43
+ const current = aggregateCurrentSessionCostsFromEntries(args.entries);
44
+ return {
45
+ ...current,
46
+ todayCost: args.dailyTotals.totalCost,
47
+ todayOwnCost: args.dailyTotals.ownCost,
48
+ todaySubagentCost: args.dailyTotals.spawnedCost,
49
+ todayWindow: args.todayWindow,
50
+ todayRollover: "local-midnight",
51
+ };
52
+ }
53
+ export function hasCostSummarySignal(summary) {
54
+ return summary.currentCost > 0 || summary.todayCost > 0 || summary.subagentReports > 0;
55
+ }
56
+ export function hasSubagentCostSignal(summary) {
57
+ return summary.subagentReports > 0 || summary.subagentCost > 0;
58
+ }
59
+ function formatCost(value, precision) {
60
+ return `$${value.toFixed(precision)}`;
61
+ }
62
+ export function formatFooterCostParts(summary, precision = 3) {
63
+ const parts = [];
64
+ if (summary.currentCost > 0 || hasSubagentCostSignal(summary)) {
65
+ parts.push(`CURRENT:${formatCost(summary.currentCost, precision)}`);
66
+ }
67
+ if (summary.todayCost > 0) {
68
+ parts.push(`TODAY:${formatCost(summary.todayCost, precision)}`);
69
+ }
70
+ if (hasSubagentCostSignal(summary)) {
71
+ parts.push(`SUBAGENTS:${formatCost(summary.subagentCost, precision)} in CURRENT`);
72
+ }
73
+ return parts;
74
+ }
75
+ export function formatStatusCostSummary(summary, precision = 4) {
76
+ const parts = [
77
+ `CURRENT ${formatCost(summary.currentCost, precision)}`,
78
+ `TODAY ${formatCost(summary.todayCost, precision)}`,
79
+ ];
80
+ if (hasSubagentCostSignal(summary)) {
81
+ parts.push(`SUBAGENTS ${formatCost(summary.subagentCost, precision)} (included in CURRENT)`);
82
+ }
83
+ return parts.join(", ");
84
+ }
85
+ //# sourceMappingURL=cost-summary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cost-summary.js","sourceRoot":"","sources":["../../../src/core/cost/cost-summary.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SessionEntry } from \"@caupulican/pi-agent-core/node\";\nimport type { AssistantMessage, Usage } from \"@caupulican/pi-ai\";\nimport { SPAWNED_USAGE_CUSTOM_TYPE, type SpawnedUsageReport } from \"../agent-session.ts\";\nimport type { DailyUsageTotals, DailyUsageWindow } from \"./daily-usage.ts\";\n\nexport interface CurrentSessionCostTotals {\n\townCost: number;\n\tsubagentCost: number;\n\tsubagentReports: number;\n\tcurrentCost: number;\n}\n\nexport interface SessionCostSummary extends CurrentSessionCostTotals {\n\ttodayCost: number;\n\ttodayOwnCost: number;\n\ttodaySubagentCost: number;\n\ttodayWindow: DailyUsageWindow;\n\ttodayRollover: \"local-midnight\";\n}\n\nfunction isFiniteCost(value: unknown): value is number {\n\treturn typeof value === \"number\" && Number.isFinite(value);\n}\n\nfunction getUsageTotalCost(usage: Usage | undefined): number | undefined {\n\tconst total = usage?.cost?.total;\n\treturn isFiniteCost(total) ? total : undefined;\n}\n\nexport function aggregateCurrentSessionCostsFromEntries(entries: readonly SessionEntry[]): CurrentSessionCostTotals {\n\tlet ownCost = 0;\n\tlet subagentCost = 0;\n\tlet subagentReports = 0;\n\tconst seenSubagentReportIds = new Set<string>();\n\n\tfor (const entry of entries) {\n\t\tif (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\tconst total = getUsageTotalCost((entry.message as AssistantMessage).usage);\n\t\t\tif (total !== undefined) ownCost += total;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (entry.type !== \"custom\" || entry.customType !== SPAWNED_USAGE_CUSTOM_TYPE) continue;\n\t\tconst report = entry.data as SpawnedUsageReport | undefined;\n\t\tconst total = getUsageTotalCost(report?.usage);\n\t\tif (total === undefined) continue;\n\t\tif (report?.reportId) {\n\t\t\tif (seenSubagentReportIds.has(report.reportId)) continue;\n\t\t\tseenSubagentReportIds.add(report.reportId);\n\t\t}\n\t\tsubagentCost += total;\n\t\tsubagentReports += 1;\n\t}\n\n\treturn {\n\t\townCost,\n\t\tsubagentCost,\n\t\tsubagentReports,\n\t\tcurrentCost: ownCost + subagentCost,\n\t};\n}\n\nexport function createSessionCostSummary(args: {\n\tentries: readonly SessionEntry[];\n\tdailyTotals: DailyUsageTotals;\n\ttodayWindow: DailyUsageWindow;\n}): SessionCostSummary {\n\tconst current = aggregateCurrentSessionCostsFromEntries(args.entries);\n\treturn {\n\t\t...current,\n\t\ttodayCost: args.dailyTotals.totalCost,\n\t\ttodayOwnCost: args.dailyTotals.ownCost,\n\t\ttodaySubagentCost: args.dailyTotals.spawnedCost,\n\t\ttodayWindow: args.todayWindow,\n\t\ttodayRollover: \"local-midnight\",\n\t};\n}\n\nexport function hasCostSummarySignal(summary: SessionCostSummary): boolean {\n\treturn summary.currentCost > 0 || summary.todayCost > 0 || summary.subagentReports > 0;\n}\n\nexport function hasSubagentCostSignal(summary: Pick<SessionCostSummary, \"subagentReports\" | \"subagentCost\">): boolean {\n\treturn summary.subagentReports > 0 || summary.subagentCost > 0;\n}\n\nfunction formatCost(value: number, precision: number): string {\n\treturn `$${value.toFixed(precision)}`;\n}\n\nexport function formatFooterCostParts(summary: SessionCostSummary, precision = 3): string[] {\n\tconst parts: string[] = [];\n\tif (summary.currentCost > 0 || hasSubagentCostSignal(summary)) {\n\t\tparts.push(`CURRENT:${formatCost(summary.currentCost, precision)}`);\n\t}\n\tif (summary.todayCost > 0) {\n\t\tparts.push(`TODAY:${formatCost(summary.todayCost, precision)}`);\n\t}\n\tif (hasSubagentCostSignal(summary)) {\n\t\tparts.push(`SUBAGENTS:${formatCost(summary.subagentCost, precision)} in CURRENT`);\n\t}\n\treturn parts;\n}\n\nexport function formatStatusCostSummary(summary: SessionCostSummary, precision = 4): string {\n\tconst parts = [\n\t\t`CURRENT ${formatCost(summary.currentCost, precision)}`,\n\t\t`TODAY ${formatCost(summary.todayCost, precision)}`,\n\t];\n\tif (hasSubagentCostSignal(summary)) {\n\t\tparts.push(`SUBAGENTS ${formatCost(summary.subagentCost, precision)} (included in CURRENT)`);\n\t}\n\treturn parts.join(\", \");\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"session-usage.d.ts","sourceRoot":"","sources":["../../../src/core/cost/session-usage.ts"],"names":[],"mappings":"AAEA,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACxF,OAAO,KAAK,EAAoB,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAsBjE,wBAAgB,8BAA8B,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAqBnF;AAED,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAuC9F;AAED,wBAAgB,0CAA0C,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,KAAK,CAqDzF;AAED,wBAAgB,mCAAmC,CAAC,IAAI,EAAE;IACzD,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE;QACd,eAAe,EAAE,CAChB,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,eAAe,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAA;SAAE,KAC9D,MAAM,GAAG,SAAS,CAAC;KACxB,CAAC;IACF,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACvD,GAAG,IAAI,CA0BP","sourcesContent":["import * as fs from \"node:fs\";\nimport { basename, join } from \"node:path\";\nimport { loadEntriesFromFile, type SessionEntry } from \"@caupulican/pi-agent-core/node\";\nimport type { AssistantMessage, Usage } from \"@caupulican/pi-ai\";\nimport { SPAWNED_USAGE_CUSTOM_TYPE, type SpawnedUsageReport } from \"../agent-session.ts\";\n\nfunction isUsage(value: unknown): value is Usage {\n\tif (!value || typeof value !== \"object\") return false;\n\tconst usage = value as Partial<Usage>;\n\tconst cost = usage.cost as Partial<Usage[\"cost\"]> | undefined;\n\treturn (\n\t\ttypeof usage.input === \"number\" &&\n\t\ttypeof usage.output === \"number\" &&\n\t\ttypeof usage.cacheRead === \"number\" &&\n\t\ttypeof usage.cacheWrite === \"number\" &&\n\t\ttypeof usage.totalTokens === \"number\" &&\n\t\t!!cost &&\n\t\ttypeof cost.input === \"number\" &&\n\t\ttypeof cost.output === \"number\" &&\n\t\ttypeof cost.cacheRead === \"number\" &&\n\t\ttypeof cost.cacheWrite === \"number\" &&\n\t\ttypeof cost.total === \"number\"\n\t);\n}\n\nexport function readAutoLearnSessionIdFromFile(filePath: string): string | undefined {\n\tlet fd: number | undefined;\n\ttry {\n\t\tfd = fs.openSync(filePath, \"r\");\n\t\tconst buffer = Buffer.alloc(64 * 1024);\n\t\tconst bytesRead = fs.readSync(fd, buffer, 0, buffer.length, 0);\n\t\tconst firstLine = buffer.toString(\"utf8\", 0, bytesRead).split(\"\\n\", 1)[0]?.trim();\n\t\tif (!firstLine) return undefined;\n\t\tconst header = JSON.parse(firstLine) as Record<string, unknown>;\n\t\treturn header.type === \"session\" && typeof header.id === \"string\" ? header.id : undefined;\n\t} catch {\n\t\treturn undefined;\n\t} finally {\n\t\tif (fd !== undefined) {\n\t\t\ttry {\n\t\t\t\tfs.closeSync(fd);\n\t\t\t} catch {\n\t\t\t\t// Ignore close errors\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport function findChildSessionFile(sessionDir: string, sessionId: string): string | undefined {\n\tif (!fs.existsSync(sessionDir)) return undefined;\n\n\tconst jsonlFiles: string[] = [];\n\n\tfunction collectJsonlFiles(dirPath: string) {\n\t\tlet currentEntries: fs.Dirent[];\n\t\ttry {\n\t\t\tcurrentEntries = fs.readdirSync(dirPath, { withFileTypes: true });\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\t\tfor (const entry of currentEntries) {\n\t\t\tconst fullPath = join(dirPath, entry.name);\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\tcollectJsonlFiles(fullPath);\n\t\t\t} else if (entry.isFile() && entry.name.endsWith(\".jsonl\")) {\n\t\t\t\tjsonlFiles.push(fullPath);\n\t\t\t}\n\t\t}\n\t}\n\n\tcollectJsonlFiles(sessionDir);\n\n\tfor (const file of jsonlFiles) {\n\t\tconst base = basename(file);\n\t\tif (base === `${sessionId}.jsonl` || base.endsWith(`_${sessionId}.jsonl`)) {\n\t\t\treturn file;\n\t\t}\n\t}\n\n\tfor (const file of jsonlFiles) {\n\t\tconst headerId = readAutoLearnSessionIdFromFile(file);\n\t\tif (headerId === sessionId) {\n\t\t\treturn file;\n\t\t}\n\t}\n\n\treturn undefined;\n}\n\nexport function aggregateCumulativeUsageFromSessionEntries(entries: SessionEntry[]): Usage {\n\tlet input = 0;\n\tlet output = 0;\n\tlet cacheRead = 0;\n\tlet cacheWrite = 0;\n\tlet totalTokens = 0;\n\tlet costInput = 0;\n\tlet costOutput = 0;\n\tlet costCacheRead = 0;\n\tlet costCacheWrite = 0;\n\tlet costTotal = 0;\n\n\tconst add = (usage: Usage) => {\n\t\tinput += usage.input;\n\t\toutput += usage.output;\n\t\tcacheRead += usage.cacheRead;\n\t\tcacheWrite += usage.cacheWrite;\n\t\ttotalTokens += usage.totalTokens;\n\t\tcostInput += usage.cost.input;\n\t\tcostOutput += usage.cost.output;\n\t\tcostCacheRead += usage.cost.cacheRead;\n\t\tcostCacheWrite += usage.cost.cacheWrite;\n\t\tcostTotal += usage.cost.total;\n\t};\n\n\tfor (const entry of entries) {\n\t\tif (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\tconst usage = (entry.message as AssistantMessage).usage;\n\t\t\tif (usage && isUsage(usage)) {\n\t\t\t\tadd(usage);\n\t\t\t}\n\t\t} else if (entry.type === \"custom\" && entry.customType === SPAWNED_USAGE_CUSTOM_TYPE) {\n\t\t\tconst data = entry.data as SpawnedUsageReport | undefined;\n\t\t\tif (data?.usage && isUsage(data.usage)) {\n\t\t\t\tadd(data.usage);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {\n\t\tinput,\n\t\toutput,\n\t\tcacheRead,\n\t\tcacheWrite,\n\t\ttotalTokens,\n\t\tcost: {\n\t\t\tinput: costInput,\n\t\t\toutput: costOutput,\n\t\t\tcacheRead: costCacheRead,\n\t\t\tcacheWrite: costCacheWrite,\n\t\t\ttotal: costTotal,\n\t\t},\n\t};\n}\n\nexport function reportCompletedAutoLearnUsageHelper(args: {\n\trunId: string;\n\tsessionDir: string;\n\tsessionId: string;\n\tlogPath?: string;\n\tparentSession: {\n\t\taddSpawnedUsage: (\n\t\t\tusage: Usage,\n\t\t\topts: { label: string; sourceSessionId: string; reportId: string },\n\t\t) => string | undefined;\n\t};\n\tappendLog?: (logPath: string, message: string) => void;\n}): void {\n\tconst { runId, sessionDir, sessionId, logPath, parentSession, appendLog } = args;\n\tconst sessionFile = findChildSessionFile(sessionDir, sessionId);\n\tif (!sessionFile) {\n\t\tif (logPath && appendLog) {\n\t\t\tappendLog(logPath, `Auto Learn usage report skipped: no child session file found for ${sessionId}.`);\n\t\t}\n\t\treturn;\n\t}\n\tconst fileEntries = loadEntriesFromFile(sessionFile);\n\tconst sessionEntries = fileEntries.filter((entry): entry is SessionEntry => entry.type !== \"session\");\n\tconst usage = aggregateCumulativeUsageFromSessionEntries(sessionEntries);\n\tif (usage.cost.total === 0 && usage.totalTokens === 0) {\n\t\tif (logPath && appendLog) {\n\t\t\tappendLog(logPath, `Auto Learn usage report skipped: child session had no usage for ${sessionId}.`);\n\t\t}\n\t\treturn;\n\t}\n\tparentSession.addSpawnedUsage(usage, {\n\t\tlabel: \"auto-learn\",\n\t\tsourceSessionId: sessionId,\n\t\treportId: `auto-learn:${runId}:${sessionId}`,\n\t});\n\tif (logPath && appendLog) {\n\t\tappendLog(logPath, `Auto Learn usage reported: ${sessionId}.`);\n\t}\n}\n"]}
1
+ {"version":3,"file":"session-usage.d.ts","sourceRoot":"","sources":["../../../src/core/cost/session-usage.ts"],"names":[],"mappings":"AAEA,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACxF,OAAO,KAAK,EAAoB,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAsBjE,wBAAgB,8BAA8B,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAqBnF;AAED,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAuC9F;AAED,wBAAgB,0CAA0C,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,KAAK,CAyDzF;AAED,wBAAgB,mCAAmC,CAAC,IAAI,EAAE;IACzD,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE;QACd,eAAe,EAAE,CAChB,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,eAAe,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAA;SAAE,KAC9D,MAAM,GAAG,SAAS,CAAC;KACxB,CAAC;IACF,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACvD,GAAG,IAAI,CA0BP","sourcesContent":["import * as fs from \"node:fs\";\nimport { basename, join } from \"node:path\";\nimport { loadEntriesFromFile, type SessionEntry } from \"@caupulican/pi-agent-core/node\";\nimport type { AssistantMessage, Usage } from \"@caupulican/pi-ai\";\nimport { SPAWNED_USAGE_CUSTOM_TYPE, type SpawnedUsageReport } from \"../agent-session.ts\";\n\nfunction isUsage(value: unknown): value is Usage {\n\tif (!value || typeof value !== \"object\") return false;\n\tconst usage = value as Partial<Usage>;\n\tconst cost = usage.cost as Partial<Usage[\"cost\"]> | undefined;\n\treturn (\n\t\ttypeof usage.input === \"number\" &&\n\t\ttypeof usage.output === \"number\" &&\n\t\ttypeof usage.cacheRead === \"number\" &&\n\t\ttypeof usage.cacheWrite === \"number\" &&\n\t\ttypeof usage.totalTokens === \"number\" &&\n\t\t!!cost &&\n\t\ttypeof cost.input === \"number\" &&\n\t\ttypeof cost.output === \"number\" &&\n\t\ttypeof cost.cacheRead === \"number\" &&\n\t\ttypeof cost.cacheWrite === \"number\" &&\n\t\ttypeof cost.total === \"number\"\n\t);\n}\n\nexport function readAutoLearnSessionIdFromFile(filePath: string): string | undefined {\n\tlet fd: number | undefined;\n\ttry {\n\t\tfd = fs.openSync(filePath, \"r\");\n\t\tconst buffer = Buffer.alloc(64 * 1024);\n\t\tconst bytesRead = fs.readSync(fd, buffer, 0, buffer.length, 0);\n\t\tconst firstLine = buffer.toString(\"utf8\", 0, bytesRead).split(\"\\n\", 1)[0]?.trim();\n\t\tif (!firstLine) return undefined;\n\t\tconst header = JSON.parse(firstLine) as Record<string, unknown>;\n\t\treturn header.type === \"session\" && typeof header.id === \"string\" ? header.id : undefined;\n\t} catch {\n\t\treturn undefined;\n\t} finally {\n\t\tif (fd !== undefined) {\n\t\t\ttry {\n\t\t\t\tfs.closeSync(fd);\n\t\t\t} catch {\n\t\t\t\t// Ignore close errors\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport function findChildSessionFile(sessionDir: string, sessionId: string): string | undefined {\n\tif (!fs.existsSync(sessionDir)) return undefined;\n\n\tconst jsonlFiles: string[] = [];\n\n\tfunction collectJsonlFiles(dirPath: string) {\n\t\tlet currentEntries: fs.Dirent[];\n\t\ttry {\n\t\t\tcurrentEntries = fs.readdirSync(dirPath, { withFileTypes: true });\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\t\tfor (const entry of currentEntries) {\n\t\t\tconst fullPath = join(dirPath, entry.name);\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\tcollectJsonlFiles(fullPath);\n\t\t\t} else if (entry.isFile() && entry.name.endsWith(\".jsonl\")) {\n\t\t\t\tjsonlFiles.push(fullPath);\n\t\t\t}\n\t\t}\n\t}\n\n\tcollectJsonlFiles(sessionDir);\n\n\tfor (const file of jsonlFiles) {\n\t\tconst base = basename(file);\n\t\tif (base === `${sessionId}.jsonl` || base.endsWith(`_${sessionId}.jsonl`)) {\n\t\t\treturn file;\n\t\t}\n\t}\n\n\tfor (const file of jsonlFiles) {\n\t\tconst headerId = readAutoLearnSessionIdFromFile(file);\n\t\tif (headerId === sessionId) {\n\t\t\treturn file;\n\t\t}\n\t}\n\n\treturn undefined;\n}\n\nexport function aggregateCumulativeUsageFromSessionEntries(entries: SessionEntry[]): Usage {\n\tlet input = 0;\n\tlet output = 0;\n\tlet cacheRead = 0;\n\tlet cacheWrite = 0;\n\tlet totalTokens = 0;\n\tlet costInput = 0;\n\tlet costOutput = 0;\n\tlet costCacheRead = 0;\n\tlet costCacheWrite = 0;\n\tlet costTotal = 0;\n\n\tconst add = (usage: Usage) => {\n\t\tinput += usage.input;\n\t\toutput += usage.output;\n\t\tcacheRead += usage.cacheRead;\n\t\tcacheWrite += usage.cacheWrite;\n\t\ttotalTokens += usage.totalTokens;\n\t\tcostInput += usage.cost.input;\n\t\tcostOutput += usage.cost.output;\n\t\tcostCacheRead += usage.cost.cacheRead;\n\t\tcostCacheWrite += usage.cost.cacheWrite;\n\t\tcostTotal += usage.cost.total;\n\t};\n\n\tconst seenSpawnedReportIds = new Set<string>();\n\tfor (const entry of entries) {\n\t\tif (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\tconst usage = (entry.message as AssistantMessage).usage;\n\t\t\tif (usage && isUsage(usage)) {\n\t\t\t\tadd(usage);\n\t\t\t}\n\t\t} else if (entry.type === \"custom\" && entry.customType === SPAWNED_USAGE_CUSTOM_TYPE) {\n\t\t\tconst data = entry.data as SpawnedUsageReport | undefined;\n\t\t\tif (!data?.usage || !isUsage(data.usage)) continue;\n\t\t\tif (data.reportId) {\n\t\t\t\tif (seenSpawnedReportIds.has(data.reportId)) continue;\n\t\t\t\tseenSpawnedReportIds.add(data.reportId);\n\t\t\t}\n\t\t\tadd(data.usage);\n\t\t}\n\t}\n\n\treturn {\n\t\tinput,\n\t\toutput,\n\t\tcacheRead,\n\t\tcacheWrite,\n\t\ttotalTokens,\n\t\tcost: {\n\t\t\tinput: costInput,\n\t\t\toutput: costOutput,\n\t\t\tcacheRead: costCacheRead,\n\t\t\tcacheWrite: costCacheWrite,\n\t\t\ttotal: costTotal,\n\t\t},\n\t};\n}\n\nexport function reportCompletedAutoLearnUsageHelper(args: {\n\trunId: string;\n\tsessionDir: string;\n\tsessionId: string;\n\tlogPath?: string;\n\tparentSession: {\n\t\taddSpawnedUsage: (\n\t\t\tusage: Usage,\n\t\t\topts: { label: string; sourceSessionId: string; reportId: string },\n\t\t) => string | undefined;\n\t};\n\tappendLog?: (logPath: string, message: string) => void;\n}): void {\n\tconst { runId, sessionDir, sessionId, logPath, parentSession, appendLog } = args;\n\tconst sessionFile = findChildSessionFile(sessionDir, sessionId);\n\tif (!sessionFile) {\n\t\tif (logPath && appendLog) {\n\t\t\tappendLog(logPath, `Auto Learn usage report skipped: no child session file found for ${sessionId}.`);\n\t\t}\n\t\treturn;\n\t}\n\tconst fileEntries = loadEntriesFromFile(sessionFile);\n\tconst sessionEntries = fileEntries.filter((entry): entry is SessionEntry => entry.type !== \"session\");\n\tconst usage = aggregateCumulativeUsageFromSessionEntries(sessionEntries);\n\tif (usage.cost.total === 0 && usage.totalTokens === 0) {\n\t\tif (logPath && appendLog) {\n\t\t\tappendLog(logPath, `Auto Learn usage report skipped: child session had no usage for ${sessionId}.`);\n\t\t}\n\t\treturn;\n\t}\n\tparentSession.addSpawnedUsage(usage, {\n\t\tlabel: \"auto-learn\",\n\t\tsourceSessionId: sessionId,\n\t\treportId: `auto-learn:${runId}:${sessionId}`,\n\t});\n\tif (logPath && appendLog) {\n\t\tappendLog(logPath, `Auto Learn usage reported: ${sessionId}.`);\n\t}\n}\n"]}