@caupulican/pi-adaptative 0.93.16 → 0.93.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/dist/core/compaction-controller.d.ts +1 -0
- package/dist/core/compaction-controller.d.ts.map +1 -1
- package/dist/core/compaction-controller.js +43 -5
- package/dist/core/compaction-controller.js.map +1 -1
- package/dist/core/compaction-support.d.ts.map +1 -1
- package/dist/core/compaction-support.js +21 -1
- package/dist/core/compaction-support.js.map +1 -1
- package/dist/core/models/perf-profile.d.ts +9 -1
- package/dist/core/models/perf-profile.d.ts.map +1 -1
- package/dist/core/models/perf-profile.js +80 -1
- package/dist/core/models/perf-profile.js.map +1 -1
- package/dist/core/secrets/credential-exposure-guard.d.ts.map +1 -1
- package/dist/core/secrets/credential-exposure-guard.js +14 -3
- package/dist/core/secrets/credential-exposure-guard.js.map +1 -1
- package/dist/core/settings-manager.d.ts +1 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +3 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +12 -12
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compaction-support.js","sourceRoot":"","sources":["../../src/core/compaction-support.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Compaction support: summarizer model selection (#30 cost guard), request-auth resolution\n * with session-model fallback, and window-adapted compaction settings.\n *\n * This is the policy half of compaction. CompactionController owns detection, execution, retry,\n * persistence, notification, and cancellation; AgentSession keeps compatibility delegations at\n * established regression seams.\n */\nimport { type CompactionSettings, summarizerCanIngest } from \"@caupulican/pi-agent-core/compaction/compaction\";\nimport type { ThinkingLevel } from \"@caupulican/pi-agent-core/types\";\nimport type { Api, Model } from \"@caupulican/pi-ai\";\nimport type { ModelRegistry } from \"./model-registry.ts\";\nimport { resolveCliModel } from \"./model-resolver.ts\";\nimport { evaluateSurfaceFitness } from \"./model-router/fitness-gate.ts\";\nimport type { RequestAuth } from \"./request-auth.ts\";\nimport type { ModelFitnessReport } from \"./research/model-fitness.ts\";\nimport type { SettingsManager } from \"./settings-manager.ts\";\n\nexport interface CompactionSupportDeps {\n\tgetModel(): Model<Api> | undefined;\n\tgetSettingsManager(): SettingsManager;\n\tgetModelRegistry(): ModelRegistry;\n\t/** True when the agent's streamFn is (or wraps) the raw streamSimple — auth must be explicit then. */\n\tisRawStream(): boolean;\n\t/** Host auth resolution that THROWS with a user-actionable message when no key exists. */\n\tgetRequiredRequestAuth(model: Model<Api>): Promise<RequestAuth>;\n\tisModelExhausted(ref: string): boolean;\n\tgetStoredFitnessReport(ref: string): ModelFitnessReport | undefined;\n\t/** Estimated tokens of the summarization input (live context; over-estimates, which is safe). */\n\testimateSummarizationInputTokens(): number;\n\temitWarning(message: string): void;\n\t/**\n\t * The same readiness/residency gate every other isolated consumer uses\n\t * (LocalRuntimeController.ensureIsolatedModelReady) before this model is used for an\n\t * out-of-band call. No-ops for a non-managed-local model (checked internally by the gate);\n\t * throws with a user-actionable reason when a managed-local model cannot be made ready.\n\t */\n\tensureModelReady(model: Model<Api>): Promise<void>;\n}\n\nexport class CompactionSupport {\n\tprivate readonly deps: CompactionSupportDeps;\n\tprivate lastSelectionReason: string | undefined;\n\n\tconstructor(deps: CompactionSupportDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\tgetLastSelectionReason(): string | undefined {\n\t\treturn this.lastSelectionReason;\n\t}\n\n\tgetAdaptedSettings(): CompactionSettings {\n\t\tconst settings = this.deps.getSettingsManager().getCompactionSettings();\n\t\tconst model = this.deps.getModel();\n\t\tif (!model) return settings;\n\t\tconst contextWindow = model.contextWindow ?? 0;\n\t\tif (contextWindow <= 0) return settings;\n\n\t\t// Adapt reserveTokens: at most 25% of context window\n\t\tconst maxReserve = Math.floor(contextWindow * 0.25);\n\t\tconst reserveTokens = Math.min(settings.reserveTokens, maxReserve);\n\n\t\t// Adapt keepRecentTokens: at most 50% of context window\n\t\tconst maxKeepRecent = Math.floor(contextWindow * 0.5);\n\t\tconst keepRecentTokens = Math.min(settings.keepRecentTokens, maxKeepRecent);\n\n\t\treturn {\n\t\t\t...settings,\n\t\t\treserveTokens,\n\t\t\tkeepRecentTokens,\n\t\t};\n\t}\n\n\tasync getRequestAuth(model: Model<Api>): Promise<RequestAuth> {\n\t\tif (this.deps.isRawStream()) {\n\t\t\treturn this.deps.getRequiredRequestAuth(model);\n\t\t}\n\n\t\tconst result = await this.deps.getModelRegistry().getApiKeyAndHeaders(model);\n\t\treturn result.ok ? { apiKey: result.apiKey, headers: result.headers } : {};\n\t}\n\n\t/**\n\t * Readiness/residency gate a candidate summarizer model right before it is handed back for\n\t * use. A no-op for a non-managed-local model (checked internally by\n\t * {@link CompactionSupportDeps.ensureModelReady}); a managed-local model that cannot be made\n\t * ready (server down, model missing, residency refused) returns its failure reason instead of\n\t * throwing, so callers can still try the next candidate in the existing auth-fallback ladder\n\t * rather than aborting resolution outright.\n\t */\n\tprivate async readinessFailure(model: Model<Api>): Promise<string | undefined> {\n\t\ttry {\n\t\t\tawait this.deps.ensureModelReady(model);\n\t\t\treturn undefined;\n\t\t} catch (error) {\n\t\t\treturn error instanceof Error ? error.message : String(error);\n\t\t}\n\t}\n\n\t/**\n\t * Resolve the summarizer model AND its request auth for auto-compaction. The cheap auxiliary\n\t * model (#30) can be nominally available yet fail key resolution at request time (expired\n\t * OAuth, revoked key) — or, for a managed-local model, fail the readiness/residency gate (\n\t * server down, model not installed, residency refused). Falling back to the session model keeps\n\t * auto-compaction working in exactly the situations where manual /compact works; only when\n\t * neither resolves do we fail — and then with a concrete message instead of a silent no-op or a\n\t * bypass of the readiness gate.\n\t */\n\tasync resolveModelAndAuth(\n\t\tcompactionModel: Model<Api>,\n\t\tsessionModel: Model<Api>,\n\t): Promise<{ model: Model<Api>; apiKey?: string; headers?: Record<string, string>; failure?: string }> {\n\t\tif (this.deps.isRawStream()) {\n\t\t\tconst registry = this.deps.getModelRegistry();\n\t\t\tlet auth = await registry.getApiKeyAndHeaders(compactionModel);\n\t\t\tlet readiness: string | undefined;\n\t\t\tif (registry.canUseResolvedRequestAuth(compactionModel, auth)) {\n\t\t\t\treadiness = await this.readinessFailure(compactionModel);\n\t\t\t\tif (!readiness) return { model: compactionModel, apiKey: auth.apiKey, headers: auth.headers };\n\t\t\t}\n\t\t\tconst isSameModel =\n\t\t\t\tcompactionModel.provider === sessionModel.provider && compactionModel.id === sessionModel.id;\n\t\t\tif (!isSameModel) {\n\t\t\t\tauth = await registry.getApiKeyAndHeaders(sessionModel);\n\t\t\t\tif (registry.canUseResolvedRequestAuth(sessionModel, auth)) {\n\t\t\t\t\treadiness = await this.readinessFailure(sessionModel);\n\t\t\t\t\tif (!readiness) return { model: sessionModel, apiKey: auth.apiKey, headers: auth.headers };\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tmodel: sessionModel,\n\t\t\t\tfailure: readiness\n\t\t\t\t\t? `summarizer ${isSameModel ? compactionModel.id : sessionModel.id} not ready: ${readiness}`\n\t\t\t\t\t: `no usable request authentication for the summarizer (tried ${compactionModel.id}${isSameModel ? \"\" : ` and ${sessionModel.id}`})`,\n\t\t\t};\n\t\t}\n\n\t\t// Custom streamFn owns auth injection (CLI path) — resolve best-effort, never fail on auth\n\t\t// here; a managed-local summarizer must still pass the readiness gate before compact() runs.\n\t\tconst { apiKey, headers } = await this.getRequestAuth(compactionModel);\n\t\tconst readiness = await this.readinessFailure(compactionModel);\n\t\tif (readiness) {\n\t\t\treturn {\n\t\t\t\tmodel: compactionModel,\n\t\t\t\tapiKey,\n\t\t\t\theaders,\n\t\t\t\tfailure: `summarizer ${compactionModel.id} not ready: ${readiness}`,\n\t\t\t};\n\t\t}\n\t\treturn { model: compactionModel, apiKey, headers };\n\t}\n\n\tprivate getExplicitCompactionModelSetting(): string | undefined {\n\t\tconst setting = this.deps.getSettingsManager().getCompactionModel().trim();\n\t\treturn setting && setting !== \"auto\" ? setting : undefined;\n\t}\n\n\tprivate modelRef(model: Model<Api>): string {\n\t\treturn `${model.provider}/${model.id}`;\n\t}\n\n\tprivate resolveConfiguredModel(pattern: string): { model?: Model<Api>; cause?: \"unresolved\" | \"unauthed\" } {\n\t\tconst registry = this.deps.getModelRegistry();\n\t\tconst resolved = resolveCliModel({ cliModel: pattern, modelRegistry: registry });\n\t\tif (!resolved.model) return { cause: \"unresolved\" };\n\t\tif (!registry.hasConfiguredAuth(resolved.model)) return { cause: \"unauthed\" };\n\t\treturn { model: resolved.model };\n\t}\n\n\tprivate selectConfiguredModel(pattern: string): { model?: Model<Api>; cause?: string } {\n\t\tconst resolved = this.resolveConfiguredModel(pattern);\n\t\tif (!resolved.model) return { cause: resolved.cause };\n\t\tif (this.deps.isModelExhausted(this.modelRef(resolved.model))) return { cause: \"exhausted\" };\n\t\treturn { model: resolved.model };\n\t}\n\n\tprivate effectiveContextWindow(model: Model<Api>): number {\n\t\tconst registered = model.contextWindow > 0 ? model.contextWindow : Number.POSITIVE_INFINITY;\n\t\tconst served = this.deps.getStoredFitnessReport(this.modelRef(model))?.capacity?.servedContextWindow;\n\t\treturn served && served > 0 ? Math.min(registered, served) : registered;\n\t}\n\n\tprivate modelWithEffectiveWindow(model: Model<Api>): Model<Api> {\n\t\tconst contextWindow = this.effectiveContextWindow(model);\n\t\treturn contextWindow === model.contextWindow ? model : { ...model, contextWindow };\n\t}\n\n\tprivate resolveDefaultModel(sessionModel: Model<Api>): Model<Api> {\n\t\tconst router = this.deps.getSettingsManager().getModelRouterSettings();\n\t\tif (!router.enabled || !router.cheapModel) {\n\t\t\tthis.lastSelectionReason = \"session_default\";\n\t\t\treturn sessionModel;\n\t\t}\n\t\tconst selected = this.selectConfiguredModel(router.cheapModel);\n\t\tif (!selected.model) {\n\t\t\tthis.lastSelectionReason = `fallback:${selected.cause}`;\n\t\t\treturn sessionModel;\n\t\t}\n\t\t// Capacity is a hard constraint, independent of the fitness doctrine: a summarizer whose\n\t\t// window cannot hold the actual span produces recall-empty checkpoints (local servers\n\t\t// silently truncate over-window prompts instead of erroring), and the verification gate\n\t\t// then fails deterministically.\n\t\tconst estimatedInputTokens = this.deps.estimateSummarizationInputTokens();\n\t\tconst effectiveWindow = this.effectiveContextWindow(selected.model);\n\t\tif (!summarizerCanIngest(this.modelWithEffectiveWindow(selected.model), estimatedInputTokens)) {\n\t\t\tthis.lastSelectionReason = `fallback:window_too_small(~${Math.ceil(estimatedInputTokens / 1000)}k input vs ${effectiveWindow} window)`;\n\t\t\treturn sessionModel;\n\t\t}\n\t\tconst fitness = this.deps.getStoredFitnessReport(this.modelRef(selected.model));\n\t\tconst verdict = evaluateSurfaceFitness(\"compaction\", fitness);\n\t\tif (!verdict.fit) {\n\t\t\tthis.lastSelectionReason =\n\t\t\t\tverdict.reason === \"lane_failed\"\n\t\t\t\t\t? `fallback:digest_unfit(${verdict.succeeded}/${verdict.total})`\n\t\t\t\t\t: \"fallback:unprobed\";\n\t\t\treturn sessionModel;\n\t\t}\n\t\tthis.lastSelectionReason = \"router_cheap\";\n\t\treturn selected.model;\n\t}\n\n\tprivate modelsAreEqual(left: Model<Api>, right: Model<Api>): boolean {\n\t\treturn left.provider === right.provider && left.id === right.id;\n\t}\n\n\t/**\n\t * Resolve the model used to SUMMARIZE during compaction. Selection:\n\t * - an explicit `compaction.model` setting wins, but only if its provider is authed (else fall back);\n\t * - `\"auto\"`/unset follows the model router's configured cheap model when the router is enabled;\n\t * - otherwise the session model is used (safe default).\n\t */\n\tresolveModel(sessionModel: Model<Api>): Model<Api> {\n\t\tconst explicitSetting = this.getExplicitCompactionModelSetting();\n\t\tif (explicitSetting) {\n\t\t\tconst selected = this.selectConfiguredModel(explicitSetting);\n\t\t\tthis.lastSelectionReason = selected.model ? \"explicit\" : `fallback:${selected.cause}`;\n\t\t\tif (!selected.model) this.deps.emitWarning(`Compaction summarizer ${this.lastSelectionReason}`);\n\t\t\t// An explicit user choice is honored (Class C doctrine), but silently sending an\n\t\t\t// over-window prompt yields a recall-empty summary — warn with the numbers.\n\t\t\tif (selected.model) {\n\t\t\t\tconst estimatedInputTokens = this.deps.estimateSummarizationInputTokens();\n\t\t\t\tconst effectiveWindow = this.effectiveContextWindow(selected.model);\n\t\t\t\tif (!summarizerCanIngest(this.modelWithEffectiveWindow(selected.model), estimatedInputTokens)) {\n\t\t\t\t\tthis.deps.emitWarning(\n\t\t\t\t\t\t`Compaction summarizer (explicit setting) likely cannot ingest the current context: ~${Math.ceil(estimatedInputTokens / 1000)}k input tokens vs a ${effectiveWindow}-token window`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn selected.model ?? sessionModel;\n\t\t}\n\t\tconst model = this.resolveDefaultModel(sessionModel);\n\t\tif (this.lastSelectionReason?.startsWith(\"fallback:\")) {\n\t\t\tthis.deps.emitWarning(`Compaction summarizer ${this.lastSelectionReason}`);\n\t\t}\n\t\treturn model;\n\t}\n\n\t/** Default compaction should never inherit expensive session thinking. */\n\tresolveThinkingLevel(\n\t\tsessionThinkingLevel: ThinkingLevel | undefined,\n\t\tcompactionModel: Model<Api>,\n\t\tsessionModel: Model<Api>,\n\t): ThinkingLevel | undefined {\n\t\tif (this.getExplicitCompactionModelSetting()) return sessionThinkingLevel;\n\n\t\tconst router = this.deps.getSettingsManager().getModelRouterSettings();\n\t\tif (router.enabled && router.cheapModel) {\n\t\t\tconst routerModel = this.resolveConfiguredModel(router.cheapModel).model;\n\t\t\tif (routerModel && this.modelsAreEqual(routerModel, compactionModel)) {\n\t\t\t\treturn router.cheapThinking ?? \"low\";\n\t\t\t}\n\t\t}\n\n\t\treturn this.modelsAreEqual(compactionModel, sessionModel) ? \"low\" : sessionThinkingLevel;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"compaction-support.js","sourceRoot":"","sources":["../../src/core/compaction-support.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAA2B,mBAAmB,EAAE,MAAM,iDAAiD,CAAC;AAI/G,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAKxE,MAAM,2CAA2C,GAAG,GAAG,CAAC;AAExD,SAAS,qCAAqC,CAAC,KAAiB;IAC/D,IAAI,KAAK,CAAC,QAAQ,KAAK,KAAK,IAAI,KAAK,CAAC,GAAG,KAAK,kBAAkB;QAAE,OAAO,KAAK,CAAC;IAC/E,OAAO,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,eAAe,IAAI,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,aAAa,KAAK,SAAS,CAAC;AAClH,CAAC;AAwBD,MAAM,OAAO,iBAAiB;IAI7B,YAAY,IAA2B;QACtC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IAClB,CAAC;IAED,sBAAsB;QACrB,OAAO,IAAI,CAAC,mBAAmB,CAAC;IACjC,CAAC;IAED,kBAAkB;QACjB,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC;QACvD,MAAM,QAAQ,GAAG,eAAe,CAAC,qBAAqB,EAAE,CAAC;QACzD,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACnC,IAAI,CAAC,KAAK;YAAE,OAAO,QAAQ,CAAC;QAC5B,MAAM,aAAa,GAAG,KAAK,CAAC,aAAa,IAAI,CAAC,CAAC;QAC/C,IAAI,aAAa,IAAI,CAAC;YAAE,OAAO,QAAQ,CAAC;QAExC,qDAAqD;QACrD,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;QACpD,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC;QAEnE,wDAAwD;QACxD,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,GAAG,GAAG,CAAC,CAAC;QACtD,MAAM,gBAAgB,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,gBAAgB,EAAE,aAAa,CAAC,CAAC;QAC5E,MAAM,kBAAkB,GAAG,qCAAqC,CAAC,KAAK,CAAC,CAAC;QAExE,OAAO;YACN,GAAG,QAAQ;YACX,aAAa;YACb,gBAAgB;YAChB,GAAG,CAAC,kBAAkB;gBACrB,CAAC,CAAC;oBACA,QAAQ,EAAE,qBAA8B;oBACxC,cAAc,EAAE,eAAe,CAAC,mCAAmC,EAAE;wBACpE,CAAC,CAAC,QAAQ,CAAC,cAAc;wBACzB,CAAC,CAAC,2CAA2C;iBAC9C;gBACF,CAAC,CAAC,EAAE,CAAC;SACN,CAAC;IACH,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,KAAiB;QACrC,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;YAC7B,OAAO,IAAI,CAAC,IAAI,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC;QAChD,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC;QAC7E,OAAO,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5E,CAAC;IAED;;;;;;;OAOG;IACK,KAAK,CAAC,gBAAgB,CAAC,KAAiB;QAC/C,IAAI,CAAC;YACJ,MAAM,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;YACxC,OAAO,SAAS,CAAC;QAClB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC/D,CAAC;IACF,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,mBAAmB,CACxB,eAA2B,EAC3B,YAAwB;QAExB,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;YAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC9C,IAAI,IAAI,GAAG,MAAM,QAAQ,CAAC,mBAAmB,CAAC,eAAe,CAAC,CAAC;YAC/D,IAAI,SAA6B,CAAC;YAClC,IAAI,QAAQ,CAAC,yBAAyB,CAAC,eAAe,EAAE,IAAI,CAAC,EAAE,CAAC;gBAC/D,SAAS,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,eAAe,CAAC,CAAC;gBACzD,IAAI,CAAC,SAAS;oBAAE,OAAO,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC;YAC/F,CAAC;YACD,MAAM,WAAW,GAChB,eAAe,CAAC,QAAQ,KAAK,YAAY,CAAC,QAAQ,IAAI,eAAe,CAAC,EAAE,KAAK,YAAY,CAAC,EAAE,CAAC;YAC9F,IAAI,CAAC,WAAW,EAAE,CAAC;gBAClB,IAAI,GAAG,MAAM,QAAQ,CAAC,mBAAmB,CAAC,YAAY,CAAC,CAAC;gBACxD,IAAI,QAAQ,CAAC,yBAAyB,CAAC,YAAY,EAAE,IAAI,CAAC,EAAE,CAAC;oBAC5D,SAAS,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC;oBACtD,IAAI,CAAC,SAAS;wBAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC;gBAC5F,CAAC;YACF,CAAC;YACD,OAAO;gBACN,KAAK,EAAE,YAAY;gBACnB,OAAO,EAAE,SAAS;oBACjB,CAAC,CAAC,cAAc,WAAW,CAAC,CAAC,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,EAAE,eAAe,SAAS,EAAE;oBAC5F,CAAC,CAAC,8DAA8D,eAAe,CAAC,EAAE,GAAG,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,YAAY,CAAC,EAAE,EAAE,GAAG;aACrI,CAAC;QACH,CAAC;QAED,2FAA2F;QAC3F,6FAA6F;QAC7F,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,eAAe,CAAC,CAAC;QACvE,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,eAAe,CAAC,CAAC;QAC/D,IAAI,SAAS,EAAE,CAAC;YACf,OAAO;gBACN,KAAK,EAAE,eAAe;gBACtB,MAAM;gBACN,OAAO;gBACP,OAAO,EAAE,cAAc,eAAe,CAAC,EAAE,eAAe,SAAS,EAAE;aACnE,CAAC;QACH,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;IACpD,CAAC;IAEO,iCAAiC;QACxC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,kBAAkB,EAAE,CAAC,IAAI,EAAE,CAAC;QAC3E,OAAO,OAAO,IAAI,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5D,CAAC;IAEO,QAAQ,CAAC,KAAiB;QACjC,OAAO,GAAG,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,EAAE,EAAE,CAAC;IACxC,CAAC;IAEO,sBAAsB,CAAC,OAAe;QAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC9C,MAAM,QAAQ,GAAG,eAAe,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC,CAAC;QACjF,IAAI,CAAC,QAAQ,CAAC,KAAK;YAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC;QACpD,IAAI,CAAC,QAAQ,CAAC,iBAAiB,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;QAC9E,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC;IAClC,CAAC;IAEO,qBAAqB,CAAC,OAAe;QAC5C,MAAM,QAAQ,GAAG,IAAI,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC;QACtD,IAAI,CAAC,QAAQ,CAAC,KAAK;YAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC;QACtD,IAAI,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;YAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;QAC7F,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC;IAClC,CAAC;IAEO,sBAAsB,CAAC,KAAiB;QAC/C,MAAM,UAAU,GAAG,KAAK,CAAC,aAAa,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,iBAAiB,CAAC;QAC5F,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,EAAE,mBAAmB,CAAC;QACrG,OAAO,MAAM,IAAI,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;IACzE,CAAC;IAEO,wBAAwB,CAAC,KAAiB;QACjD,MAAM,aAAa,GAAG,IAAI,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC;QACzD,OAAO,aAAa,KAAK,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,aAAa,EAAE,CAAC;IACpF,CAAC;IAEO,mBAAmB,CAAC,YAAwB;QACnD,IAAI,qCAAqC,CAAC,YAAY,CAAC,EAAE,CAAC;YACzD,IAAI,CAAC,mBAAmB,GAAG,qBAAqB,CAAC;YACjD,OAAO,YAAY,CAAC;QACrB,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,sBAAsB,EAAE,CAAC;QACvE,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;YAC3C,IAAI,CAAC,mBAAmB,GAAG,iBAAiB,CAAC;YAC7C,OAAO,YAAY,CAAC;QACrB,CAAC;QACD,MAAM,QAAQ,GAAG,IAAI,CAAC,qBAAqB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAC/D,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;YACrB,IAAI,CAAC,mBAAmB,GAAG,YAAY,QAAQ,CAAC,KAAK,EAAE,CAAC;YACxD,OAAO,YAAY,CAAC;QACrB,CAAC;QACD,yFAAyF;QACzF,sFAAsF;QACtF,wFAAwF;QACxF,gCAAgC;QAChC,MAAM,oBAAoB,GAAG,IAAI,CAAC,IAAI,CAAC,gCAAgC,EAAE,CAAC;QAC1E,MAAM,eAAe,GAAG,IAAI,CAAC,sBAAsB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACpE,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,wBAAwB,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,oBAAoB,CAAC,EAAE,CAAC;YAC/F,IAAI,CAAC,mBAAmB,GAAG,8BAA8B,IAAI,CAAC,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC,cAAc,eAAe,UAAU,CAAC;YACvI,OAAO,YAAY,CAAC;QACrB,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;QAChF,MAAM,OAAO,GAAG,sBAAsB,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QAC9D,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;YAClB,IAAI,CAAC,mBAAmB;gBACvB,OAAO,CAAC,MAAM,KAAK,aAAa;oBAC/B,CAAC,CAAC,yBAAyB,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,KAAK,GAAG;oBAChE,CAAC,CAAC,mBAAmB,CAAC;YACxB,OAAO,YAAY,CAAC;QACrB,CAAC;QACD,IAAI,CAAC,mBAAmB,GAAG,cAAc,CAAC;QAC1C,OAAO,QAAQ,CAAC,KAAK,CAAC;IACvB,CAAC;IAEO,cAAc,CAAC,IAAgB,EAAE,KAAiB;QACzD,OAAO,IAAI,CAAC,QAAQ,KAAK,KAAK,CAAC,QAAQ,IAAI,IAAI,CAAC,EAAE,KAAK,KAAK,CAAC,EAAE,CAAC;IACjE,CAAC;IAED;;;;;OAKG;IACH,YAAY,CAAC,YAAwB;QACpC,MAAM,eAAe,GAAG,IAAI,CAAC,iCAAiC,EAAE,CAAC;QACjE,IAAI,eAAe,EAAE,CAAC;YACrB,MAAM,QAAQ,GAAG,IAAI,CAAC,qBAAqB,CAAC,eAAe,CAAC,CAAC;YAC7D,IAAI,CAAC,mBAAmB,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,YAAY,QAAQ,CAAC,KAAK,EAAE,CAAC;YACtF,IAAI,CAAC,QAAQ,CAAC,KAAK;gBAAE,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,yBAAyB,IAAI,CAAC,mBAAmB,EAAE,CAAC,CAAC;YAChG,iFAAiF;YACjF,4EAA4E;YAC5E,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;gBACpB,MAAM,oBAAoB,GAAG,IAAI,CAAC,IAAI,CAAC,gCAAgC,EAAE,CAAC;gBAC1E,MAAM,eAAe,GAAG,IAAI,CAAC,sBAAsB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;gBACpE,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,wBAAwB,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,oBAAoB,CAAC,EAAE,CAAC;oBAC/F,IAAI,CAAC,IAAI,CAAC,WAAW,CACpB,uFAAuF,IAAI,CAAC,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC,uBAAuB,eAAe,eAAe,CAClL,CAAC;gBACH,CAAC;YACF,CAAC;YACD,OAAO,QAAQ,CAAC,KAAK,IAAI,YAAY,CAAC;QACvC,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,mBAAmB,CAAC,YAAY,CAAC,CAAC;QACrD,IAAI,IAAI,CAAC,mBAAmB,EAAE,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;YACvD,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,yBAAyB,IAAI,CAAC,mBAAmB,EAAE,CAAC,CAAC;QAC5E,CAAC;QACD,OAAO,KAAK,CAAC;IACd,CAAC;IAED,0EAA0E;IAC1E,oBAAoB,CACnB,oBAA+C,EAC/C,eAA2B,EAC3B,YAAwB;QAExB,IAAI,IAAI,CAAC,iCAAiC,EAAE;YAAE,OAAO,oBAAoB,CAAC;QAE1E,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,sBAAsB,EAAE,CAAC;QACvE,IAAI,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;YACzC,MAAM,WAAW,GAAG,IAAI,CAAC,sBAAsB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACzE,IAAI,WAAW,IAAI,IAAI,CAAC,cAAc,CAAC,WAAW,EAAE,eAAe,CAAC,EAAE,CAAC;gBACtE,OAAO,MAAM,CAAC,aAAa,IAAI,KAAK,CAAC;YACtC,CAAC;QACF,CAAC;QAED,OAAO,IAAI,CAAC,cAAc,CAAC,eAAe,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,oBAAoB,CAAC;IAC1F,CAAC;CACD","sourcesContent":["/**\n * Compaction support: summarizer model selection (#30 cost guard), request-auth resolution\n * with session-model fallback, and window-adapted compaction settings.\n *\n * This is the policy half of compaction. CompactionController owns detection, execution, retry,\n * persistence, notification, and cancellation; AgentSession keeps compatibility delegations at\n * established regression seams.\n */\nimport { type CompactionSettings, summarizerCanIngest } from \"@caupulican/pi-agent-core/compaction/compaction\";\nimport type { ThinkingLevel } from \"@caupulican/pi-agent-core/types\";\nimport type { Api, Model } from \"@caupulican/pi-ai\";\nimport type { ModelRegistry } from \"./model-registry.ts\";\nimport { resolveCliModel } from \"./model-resolver.ts\";\nimport { evaluateSurfaceFitness } from \"./model-router/fitness-gate.ts\";\nimport type { RequestAuth } from \"./request-auth.ts\";\nimport type { ModelFitnessReport } from \"./research/model-fitness.ts\";\nimport type { SettingsManager } from \"./settings-manager.ts\";\n\nconst XAI_SUBSCRIPTION_COMPACTION_TRIGGER_PERCENT = 0.8;\n\nfunction usesXaiSubscriptionSessionReplacement(model: Model<Api>): boolean {\n\tif (model.provider !== \"xai\" || model.api !== \"openai-responses\") return false;\n\treturn model.compat !== undefined && \"requestFormat\" in model.compat && model.compat.requestFormat === \"xai-cli\";\n}\n\nexport interface CompactionSupportDeps {\n\tgetModel(): Model<Api> | undefined;\n\tgetSettingsManager(): SettingsManager;\n\tgetModelRegistry(): ModelRegistry;\n\t/** True when the agent's streamFn is (or wraps) the raw streamSimple — auth must be explicit then. */\n\tisRawStream(): boolean;\n\t/** Host auth resolution that THROWS with a user-actionable message when no key exists. */\n\tgetRequiredRequestAuth(model: Model<Api>): Promise<RequestAuth>;\n\tisModelExhausted(ref: string): boolean;\n\tgetStoredFitnessReport(ref: string): ModelFitnessReport | undefined;\n\t/** Estimated tokens of the summarization input (live context; over-estimates, which is safe). */\n\testimateSummarizationInputTokens(): number;\n\temitWarning(message: string): void;\n\t/**\n\t * The same readiness/residency gate every other isolated consumer uses\n\t * (LocalRuntimeController.ensureIsolatedModelReady) before this model is used for an\n\t * out-of-band call. No-ops for a non-managed-local model (checked internally by the gate);\n\t * throws with a user-actionable reason when a managed-local model cannot be made ready.\n\t */\n\tensureModelReady(model: Model<Api>): Promise<void>;\n}\n\nexport class CompactionSupport {\n\tprivate readonly deps: CompactionSupportDeps;\n\tprivate lastSelectionReason: string | undefined;\n\n\tconstructor(deps: CompactionSupportDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\tgetLastSelectionReason(): string | undefined {\n\t\treturn this.lastSelectionReason;\n\t}\n\n\tgetAdaptedSettings(): CompactionSettings {\n\t\tconst settingsManager = this.deps.getSettingsManager();\n\t\tconst settings = settingsManager.getCompactionSettings();\n\t\tconst model = this.deps.getModel();\n\t\tif (!model) return settings;\n\t\tconst contextWindow = model.contextWindow ?? 0;\n\t\tif (contextWindow <= 0) return settings;\n\n\t\t// Adapt reserveTokens: at most 25% of context window\n\t\tconst maxReserve = Math.floor(contextWindow * 0.25);\n\t\tconst reserveTokens = Math.min(settings.reserveTokens, maxReserve);\n\n\t\t// Adapt keepRecentTokens: at most 50% of context window\n\t\tconst maxKeepRecent = Math.floor(contextWindow * 0.5);\n\t\tconst keepRecentTokens = Math.min(settings.keepRecentTokens, maxKeepRecent);\n\t\tconst sessionReplacement = usesXaiSubscriptionSessionReplacement(model);\n\n\t\treturn {\n\t\t\t...settings,\n\t\t\treserveTokens,\n\t\t\tkeepRecentTokens,\n\t\t\t...(sessionReplacement\n\t\t\t\t? {\n\t\t\t\t\t\tstrategy: \"session-replacement\" as const,\n\t\t\t\t\t\ttriggerPercent: settingsManager.hasExplicitCompactionTriggerPercent()\n\t\t\t\t\t\t\t? settings.triggerPercent\n\t\t\t\t\t\t\t: XAI_SUBSCRIPTION_COMPACTION_TRIGGER_PERCENT,\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t};\n\t}\n\n\tasync getRequestAuth(model: Model<Api>): Promise<RequestAuth> {\n\t\tif (this.deps.isRawStream()) {\n\t\t\treturn this.deps.getRequiredRequestAuth(model);\n\t\t}\n\n\t\tconst result = await this.deps.getModelRegistry().getApiKeyAndHeaders(model);\n\t\treturn result.ok ? { apiKey: result.apiKey, headers: result.headers } : {};\n\t}\n\n\t/**\n\t * Readiness/residency gate a candidate summarizer model right before it is handed back for\n\t * use. A no-op for a non-managed-local model (checked internally by\n\t * {@link CompactionSupportDeps.ensureModelReady}); a managed-local model that cannot be made\n\t * ready (server down, model missing, residency refused) returns its failure reason instead of\n\t * throwing, so callers can still try the next candidate in the existing auth-fallback ladder\n\t * rather than aborting resolution outright.\n\t */\n\tprivate async readinessFailure(model: Model<Api>): Promise<string | undefined> {\n\t\ttry {\n\t\t\tawait this.deps.ensureModelReady(model);\n\t\t\treturn undefined;\n\t\t} catch (error) {\n\t\t\treturn error instanceof Error ? error.message : String(error);\n\t\t}\n\t}\n\n\t/**\n\t * Resolve the summarizer model AND its request auth for auto-compaction. The cheap auxiliary\n\t * model (#30) can be nominally available yet fail key resolution at request time (expired\n\t * OAuth, revoked key) — or, for a managed-local model, fail the readiness/residency gate (\n\t * server down, model not installed, residency refused). Falling back to the session model keeps\n\t * auto-compaction working in exactly the situations where manual /compact works; only when\n\t * neither resolves do we fail — and then with a concrete message instead of a silent no-op or a\n\t * bypass of the readiness gate.\n\t */\n\tasync resolveModelAndAuth(\n\t\tcompactionModel: Model<Api>,\n\t\tsessionModel: Model<Api>,\n\t): Promise<{ model: Model<Api>; apiKey?: string; headers?: Record<string, string>; failure?: string }> {\n\t\tif (this.deps.isRawStream()) {\n\t\t\tconst registry = this.deps.getModelRegistry();\n\t\t\tlet auth = await registry.getApiKeyAndHeaders(compactionModel);\n\t\t\tlet readiness: string | undefined;\n\t\t\tif (registry.canUseResolvedRequestAuth(compactionModel, auth)) {\n\t\t\t\treadiness = await this.readinessFailure(compactionModel);\n\t\t\t\tif (!readiness) return { model: compactionModel, apiKey: auth.apiKey, headers: auth.headers };\n\t\t\t}\n\t\t\tconst isSameModel =\n\t\t\t\tcompactionModel.provider === sessionModel.provider && compactionModel.id === sessionModel.id;\n\t\t\tif (!isSameModel) {\n\t\t\t\tauth = await registry.getApiKeyAndHeaders(sessionModel);\n\t\t\t\tif (registry.canUseResolvedRequestAuth(sessionModel, auth)) {\n\t\t\t\t\treadiness = await this.readinessFailure(sessionModel);\n\t\t\t\t\tif (!readiness) return { model: sessionModel, apiKey: auth.apiKey, headers: auth.headers };\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tmodel: sessionModel,\n\t\t\t\tfailure: readiness\n\t\t\t\t\t? `summarizer ${isSameModel ? compactionModel.id : sessionModel.id} not ready: ${readiness}`\n\t\t\t\t\t: `no usable request authentication for the summarizer (tried ${compactionModel.id}${isSameModel ? \"\" : ` and ${sessionModel.id}`})`,\n\t\t\t};\n\t\t}\n\n\t\t// Custom streamFn owns auth injection (CLI path) — resolve best-effort, never fail on auth\n\t\t// here; a managed-local summarizer must still pass the readiness gate before compact() runs.\n\t\tconst { apiKey, headers } = await this.getRequestAuth(compactionModel);\n\t\tconst readiness = await this.readinessFailure(compactionModel);\n\t\tif (readiness) {\n\t\t\treturn {\n\t\t\t\tmodel: compactionModel,\n\t\t\t\tapiKey,\n\t\t\t\theaders,\n\t\t\t\tfailure: `summarizer ${compactionModel.id} not ready: ${readiness}`,\n\t\t\t};\n\t\t}\n\t\treturn { model: compactionModel, apiKey, headers };\n\t}\n\n\tprivate getExplicitCompactionModelSetting(): string | undefined {\n\t\tconst setting = this.deps.getSettingsManager().getCompactionModel().trim();\n\t\treturn setting && setting !== \"auto\" ? setting : undefined;\n\t}\n\n\tprivate modelRef(model: Model<Api>): string {\n\t\treturn `${model.provider}/${model.id}`;\n\t}\n\n\tprivate resolveConfiguredModel(pattern: string): { model?: Model<Api>; cause?: \"unresolved\" | \"unauthed\" } {\n\t\tconst registry = this.deps.getModelRegistry();\n\t\tconst resolved = resolveCliModel({ cliModel: pattern, modelRegistry: registry });\n\t\tif (!resolved.model) return { cause: \"unresolved\" };\n\t\tif (!registry.hasConfiguredAuth(resolved.model)) return { cause: \"unauthed\" };\n\t\treturn { model: resolved.model };\n\t}\n\n\tprivate selectConfiguredModel(pattern: string): { model?: Model<Api>; cause?: string } {\n\t\tconst resolved = this.resolveConfiguredModel(pattern);\n\t\tif (!resolved.model) return { cause: resolved.cause };\n\t\tif (this.deps.isModelExhausted(this.modelRef(resolved.model))) return { cause: \"exhausted\" };\n\t\treturn { model: resolved.model };\n\t}\n\n\tprivate effectiveContextWindow(model: Model<Api>): number {\n\t\tconst registered = model.contextWindow > 0 ? model.contextWindow : Number.POSITIVE_INFINITY;\n\t\tconst served = this.deps.getStoredFitnessReport(this.modelRef(model))?.capacity?.servedContextWindow;\n\t\treturn served && served > 0 ? Math.min(registered, served) : registered;\n\t}\n\n\tprivate modelWithEffectiveWindow(model: Model<Api>): Model<Api> {\n\t\tconst contextWindow = this.effectiveContextWindow(model);\n\t\treturn contextWindow === model.contextWindow ? model : { ...model, contextWindow };\n\t}\n\n\tprivate resolveDefaultModel(sessionModel: Model<Api>): Model<Api> {\n\t\tif (usesXaiSubscriptionSessionReplacement(sessionModel)) {\n\t\t\tthis.lastSelectionReason = \"session_replacement\";\n\t\t\treturn sessionModel;\n\t\t}\n\t\tconst router = this.deps.getSettingsManager().getModelRouterSettings();\n\t\tif (!router.enabled || !router.cheapModel) {\n\t\t\tthis.lastSelectionReason = \"session_default\";\n\t\t\treturn sessionModel;\n\t\t}\n\t\tconst selected = this.selectConfiguredModel(router.cheapModel);\n\t\tif (!selected.model) {\n\t\t\tthis.lastSelectionReason = `fallback:${selected.cause}`;\n\t\t\treturn sessionModel;\n\t\t}\n\t\t// Capacity is a hard constraint, independent of the fitness doctrine: a summarizer whose\n\t\t// window cannot hold the actual span produces recall-empty checkpoints (local servers\n\t\t// silently truncate over-window prompts instead of erroring), and the verification gate\n\t\t// then fails deterministically.\n\t\tconst estimatedInputTokens = this.deps.estimateSummarizationInputTokens();\n\t\tconst effectiveWindow = this.effectiveContextWindow(selected.model);\n\t\tif (!summarizerCanIngest(this.modelWithEffectiveWindow(selected.model), estimatedInputTokens)) {\n\t\t\tthis.lastSelectionReason = `fallback:window_too_small(~${Math.ceil(estimatedInputTokens / 1000)}k input vs ${effectiveWindow} window)`;\n\t\t\treturn sessionModel;\n\t\t}\n\t\tconst fitness = this.deps.getStoredFitnessReport(this.modelRef(selected.model));\n\t\tconst verdict = evaluateSurfaceFitness(\"compaction\", fitness);\n\t\tif (!verdict.fit) {\n\t\t\tthis.lastSelectionReason =\n\t\t\t\tverdict.reason === \"lane_failed\"\n\t\t\t\t\t? `fallback:digest_unfit(${verdict.succeeded}/${verdict.total})`\n\t\t\t\t\t: \"fallback:unprobed\";\n\t\t\treturn sessionModel;\n\t\t}\n\t\tthis.lastSelectionReason = \"router_cheap\";\n\t\treturn selected.model;\n\t}\n\n\tprivate modelsAreEqual(left: Model<Api>, right: Model<Api>): boolean {\n\t\treturn left.provider === right.provider && left.id === right.id;\n\t}\n\n\t/**\n\t * Resolve the model used to SUMMARIZE during compaction. Selection:\n\t * - an explicit `compaction.model` setting wins, but only if its provider is authed (else fall back);\n\t * - `\"auto\"`/unset follows the model router's configured cheap model when the router is enabled;\n\t * - otherwise the session model is used (safe default).\n\t */\n\tresolveModel(sessionModel: Model<Api>): Model<Api> {\n\t\tconst explicitSetting = this.getExplicitCompactionModelSetting();\n\t\tif (explicitSetting) {\n\t\t\tconst selected = this.selectConfiguredModel(explicitSetting);\n\t\t\tthis.lastSelectionReason = selected.model ? \"explicit\" : `fallback:${selected.cause}`;\n\t\t\tif (!selected.model) this.deps.emitWarning(`Compaction summarizer ${this.lastSelectionReason}`);\n\t\t\t// An explicit user choice is honored (Class C doctrine), but silently sending an\n\t\t\t// over-window prompt yields a recall-empty summary — warn with the numbers.\n\t\t\tif (selected.model) {\n\t\t\t\tconst estimatedInputTokens = this.deps.estimateSummarizationInputTokens();\n\t\t\t\tconst effectiveWindow = this.effectiveContextWindow(selected.model);\n\t\t\t\tif (!summarizerCanIngest(this.modelWithEffectiveWindow(selected.model), estimatedInputTokens)) {\n\t\t\t\t\tthis.deps.emitWarning(\n\t\t\t\t\t\t`Compaction summarizer (explicit setting) likely cannot ingest the current context: ~${Math.ceil(estimatedInputTokens / 1000)}k input tokens vs a ${effectiveWindow}-token window`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn selected.model ?? sessionModel;\n\t\t}\n\t\tconst model = this.resolveDefaultModel(sessionModel);\n\t\tif (this.lastSelectionReason?.startsWith(\"fallback:\")) {\n\t\t\tthis.deps.emitWarning(`Compaction summarizer ${this.lastSelectionReason}`);\n\t\t}\n\t\treturn model;\n\t}\n\n\t/** Default compaction should never inherit expensive session thinking. */\n\tresolveThinkingLevel(\n\t\tsessionThinkingLevel: ThinkingLevel | undefined,\n\t\tcompactionModel: Model<Api>,\n\t\tsessionModel: Model<Api>,\n\t): ThinkingLevel | undefined {\n\t\tif (this.getExplicitCompactionModelSetting()) return sessionThinkingLevel;\n\n\t\tconst router = this.deps.getSettingsManager().getModelRouterSettings();\n\t\tif (router.enabled && router.cheapModel) {\n\t\t\tconst routerModel = this.resolveConfiguredModel(router.cheapModel).model;\n\t\t\tif (routerModel && this.modelsAreEqual(routerModel, compactionModel)) {\n\t\t\t\treturn router.cheapThinking ?? \"low\";\n\t\t\t}\n\t\t}\n\n\t\treturn this.modelsAreEqual(compactionModel, sessionModel) ? \"low\" : sessionThinkingLevel;\n\t}\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type StreamIdleOptions } from "@caupulican/pi-agent-core/reliability";
|
|
2
2
|
import type { StreamFn } from "@caupulican/pi-agent-core/types";
|
|
3
3
|
import type { Api, Context, Model } from "@caupulican/pi-ai/types";
|
|
4
4
|
export declare const DEFAULT_ADAPTIVE_STREAM_IDLE_CEILING_MS: number;
|
|
@@ -6,6 +6,12 @@ export interface ModelPerfProfile {
|
|
|
6
6
|
prefillTokensPerSecond?: number;
|
|
7
7
|
decodeTokensPerSecond?: number;
|
|
8
8
|
loadMs?: number;
|
|
9
|
+
firstProgressStall?: {
|
|
10
|
+
elapsedMs: number;
|
|
11
|
+
promptTokens: number;
|
|
12
|
+
consecutive: number;
|
|
13
|
+
observedAt: string;
|
|
14
|
+
};
|
|
9
15
|
samples: number;
|
|
10
16
|
updatedAt: string;
|
|
11
17
|
}
|
|
@@ -16,6 +22,8 @@ export interface ModelPerfSample {
|
|
|
16
22
|
requestToFirstTokenMs?: number;
|
|
17
23
|
firstTokenToDoneMs?: number;
|
|
18
24
|
loadMs?: number;
|
|
25
|
+
/** Censored observation: transport connected but emitted no model token before this bound. */
|
|
26
|
+
firstProgressStallMs?: number;
|
|
19
27
|
at?: string;
|
|
20
28
|
}
|
|
21
29
|
export interface AdaptiveStreamIdleInput {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"perf-profile.d.ts","sourceRoot":"","sources":["../../../src/core/models/perf-profile.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"perf-profile.d.ts","sourceRoot":"","sources":["../../../src/core/models/perf-profile.ts"],"names":[],"mappings":"AACA,OAAO,EAAsB,KAAK,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AACnG,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAEhE,OAAO,KAAK,EACX,GAAG,EAGH,OAAO,EACP,KAAK,EAEL,MAAM,yBAAyB,CAAC;AAMjC,eAAO,MAAM,uCAAuC,QAAiB,CAAC;AAEtE,MAAM,WAAW,gBAAgB;IAChC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kBAAkB,CAAC,EAAE;QACpB,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,EAAE,MAAM,CAAC;QACrB,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8FAA8F;IAC9F,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,EAAE,CAAC,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,uBAAuB;IACvC,IAAI,EAAE,iBAAiB,CAAC;IACxB,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,8BAA8B;IAC9C,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,KAAK,MAAM,GAAG,SAAS,CAAC;IACpD,YAAY,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;IAClE,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,MAAM,CAAC;CACtB;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,gBAAgB,CAc5E;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAOzE;AAED,wBAAgB,sBAAsB,CACrC,OAAO,EAAE,gBAAgB,GAAG,SAAS,EACrC,MAAM,EAAE,eAAe,EACvB,EAAE,GAAE,MAA8C,GAChD,gBAAgB,GAAG,SAAS,CAqD9B;AAED,wBAAgB,gCAAgC,CAAC,KAAK,EAAE,uBAAuB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAsC3G;AAED,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAEpE;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,8BAA8B,GAAG,QAAQ,CA0F3G"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { estimateProviderRequestTokens } from "@caupulican/pi-agent-core/provider-request-estimator";
|
|
2
|
+
import { isStreamStallError } from "@caupulican/pi-agent-core/reliability";
|
|
2
3
|
import { createAssistantMessageEventStream } from "@caupulican/pi-ai/event-stream";
|
|
3
4
|
import { createEmptyUsage } from "@caupulican/pi-ai/usage";
|
|
4
5
|
const PERF_EWMA_ALPHA = 0.3;
|
|
@@ -13,6 +14,7 @@ export function isModelPerfProfile(value) {
|
|
|
13
14
|
return ((record.prefillTokensPerSecond === undefined || isPositiveFiniteNumber(record.prefillTokensPerSecond)) &&
|
|
14
15
|
(record.decodeTokensPerSecond === undefined || isPositiveFiniteNumber(record.decodeTokensPerSecond)) &&
|
|
15
16
|
(record.loadMs === undefined || isPositiveFiniteNumber(record.loadMs)) &&
|
|
17
|
+
(record.firstProgressStall === undefined || isFirstProgressStall(record.firstProgressStall)) &&
|
|
16
18
|
typeof samples === "number" &&
|
|
17
19
|
Number.isInteger(samples) &&
|
|
18
20
|
samples >= 0 &&
|
|
@@ -21,12 +23,38 @@ export function isModelPerfProfile(value) {
|
|
|
21
23
|
export function hasUsableModelPerfSample(sample) {
|
|
22
24
|
return (prefillRateFromSample(sample) !== undefined ||
|
|
23
25
|
decodeRateFromSample(sample) !== undefined ||
|
|
24
|
-
isPositiveFiniteNumber(sample.loadMs)
|
|
26
|
+
isPositiveFiniteNumber(sample.loadMs) ||
|
|
27
|
+
(isPositiveFiniteNumber(sample.firstProgressStallMs) && isPositiveFiniteNumber(sample.promptTokens)));
|
|
25
28
|
}
|
|
26
29
|
export function updateModelPerfProfile(current, sample, at = sample.at ?? new Date().toISOString()) {
|
|
27
30
|
const prefillRate = prefillRateFromSample(sample);
|
|
28
31
|
const decodeRate = decodeRateFromSample(sample);
|
|
29
32
|
const loadMs = isPositiveFiniteNumber(sample.loadMs) ? sample.loadMs : undefined;
|
|
33
|
+
const firstProgressStallMs = isPositiveFiniteNumber(sample.firstProgressStallMs)
|
|
34
|
+
? sample.firstProgressStallMs
|
|
35
|
+
: undefined;
|
|
36
|
+
const stallPromptTokens = isPositiveFiniteNumber(sample.promptTokens) ? sample.promptTokens : undefined;
|
|
37
|
+
if (prefillRate === undefined &&
|
|
38
|
+
decodeRate === undefined &&
|
|
39
|
+
loadMs === undefined &&
|
|
40
|
+
firstProgressStallMs !== undefined &&
|
|
41
|
+
stallPromptTokens !== undefined) {
|
|
42
|
+
const previous = current?.firstProgressStall;
|
|
43
|
+
const comparablePrompt = previous !== undefined &&
|
|
44
|
+
stallPromptTokens >= previous.promptTokens / 2 &&
|
|
45
|
+
stallPromptTokens <= previous.promptTokens * 2;
|
|
46
|
+
return {
|
|
47
|
+
...current,
|
|
48
|
+
firstProgressStall: {
|
|
49
|
+
elapsedMs: firstProgressStallMs,
|
|
50
|
+
promptTokens: stallPromptTokens,
|
|
51
|
+
consecutive: comparablePrompt ? previous.consecutive + 1 : 1,
|
|
52
|
+
observedAt: at,
|
|
53
|
+
},
|
|
54
|
+
samples: current?.samples ?? 0,
|
|
55
|
+
updatedAt: at,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
30
58
|
if (prefillRate === undefined && decodeRate === undefined && loadMs === undefined)
|
|
31
59
|
return current;
|
|
32
60
|
const previousSamples = current?.samples ?? 0;
|
|
@@ -67,6 +95,13 @@ export function resolveAdaptiveStreamIdleOptions(input) {
|
|
|
67
95
|
if (quietIdleMs > input.base.quietIdleMs)
|
|
68
96
|
result.quietIdleMs = quietIdleMs;
|
|
69
97
|
}
|
|
98
|
+
const firstProgressStall = profile?.firstProgressStall;
|
|
99
|
+
if (firstProgressStall && input.promptTokens >= firstProgressStall.promptTokens / 2) {
|
|
100
|
+
const nextAttemptBoundMs = Math.min(Math.ceil(firstProgressStall.elapsedMs * 2), firstProgressCeilingMs);
|
|
101
|
+
const currentBoundMs = result.firstProgressMs ?? input.base.firstProgressMs;
|
|
102
|
+
if (nextAttemptBoundMs > currentBoundMs)
|
|
103
|
+
result.firstProgressMs = nextAttemptBoundMs;
|
|
104
|
+
}
|
|
70
105
|
if (input.localClass) {
|
|
71
106
|
const expectedConnectWorkMs = expectedPrefillMs !== undefined ? expectedPrefillMs + (profile?.loadMs ?? 0) : profile?.loadMs;
|
|
72
107
|
const connectMs = expectedConnectWorkMs !== undefined
|
|
@@ -87,7 +122,36 @@ export function withModelPerfProfile(streamFn, recorder) {
|
|
|
87
122
|
const requestStartedAtMs = nowMs();
|
|
88
123
|
let responseHeadersAtMs;
|
|
89
124
|
let firstTokenAtMs;
|
|
125
|
+
let firstProgressStallRecorded = false;
|
|
90
126
|
const originalOnResponse = streamOptions?.onResponse;
|
|
127
|
+
const signal = streamOptions?.signal;
|
|
128
|
+
const recordFirstProgressStall = () => {
|
|
129
|
+
const reason = signal?.reason;
|
|
130
|
+
if (firstProgressStallRecorded ||
|
|
131
|
+
firstTokenAtMs !== undefined ||
|
|
132
|
+
!isStreamStallError(reason) ||
|
|
133
|
+
reason.phase !== "first-progress") {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
firstProgressStallRecorded = true;
|
|
137
|
+
const modelKey = recorder.modelKey(model);
|
|
138
|
+
if (!modelKey)
|
|
139
|
+
return;
|
|
140
|
+
try {
|
|
141
|
+
recorder.recordSample(modelKey, {
|
|
142
|
+
promptTokens: estimateContextPromptTokens(context),
|
|
143
|
+
firstProgressStallMs: reason.elapsedMs,
|
|
144
|
+
at: nowIso(),
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
// Perf profiling must never fail the user turn.
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
if (signal?.aborted)
|
|
152
|
+
recordFirstProgressStall();
|
|
153
|
+
else
|
|
154
|
+
signal?.addEventListener("abort", recordFirstProgressStall, { once: true });
|
|
91
155
|
const outer = createAssistantMessageEventStream();
|
|
92
156
|
let latest = emptyAssistantMessage(model);
|
|
93
157
|
let inner;
|
|
@@ -101,6 +165,7 @@ export function withModelPerfProfile(streamFn, recorder) {
|
|
|
101
165
|
});
|
|
102
166
|
}
|
|
103
167
|
catch (error) {
|
|
168
|
+
signal?.removeEventListener("abort", recordFirstProgressStall);
|
|
104
169
|
outer.push(perfStreamFailure(latest, streamOptions?.signal?.aborted === true, error));
|
|
105
170
|
return outer;
|
|
106
171
|
}
|
|
@@ -138,6 +203,9 @@ export function withModelPerfProfile(streamFn, recorder) {
|
|
|
138
203
|
catch (error) {
|
|
139
204
|
outer.push(perfStreamFailure(latest, streamOptions?.signal?.aborted === true, error));
|
|
140
205
|
}
|
|
206
|
+
finally {
|
|
207
|
+
signal?.removeEventListener("abort", recordFirstProgressStall);
|
|
208
|
+
}
|
|
141
209
|
})();
|
|
142
210
|
return outer;
|
|
143
211
|
};
|
|
@@ -242,4 +310,15 @@ function updateEwma(previous, next, previousSamples) {
|
|
|
242
310
|
function isPositiveFiniteNumber(value) {
|
|
243
311
|
return typeof value === "number" && Number.isFinite(value) && value > 0;
|
|
244
312
|
}
|
|
313
|
+
function isFirstProgressStall(value) {
|
|
314
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
315
|
+
return false;
|
|
316
|
+
const record = value;
|
|
317
|
+
return (isPositiveFiniteNumber(record.elapsedMs) &&
|
|
318
|
+
isPositiveFiniteNumber(record.promptTokens) &&
|
|
319
|
+
typeof record.consecutive === "number" &&
|
|
320
|
+
Number.isSafeInteger(record.consecutive) &&
|
|
321
|
+
record.consecutive > 0 &&
|
|
322
|
+
typeof record.observedAt === "string");
|
|
323
|
+
}
|
|
245
324
|
//# sourceMappingURL=perf-profile.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"perf-profile.js","sourceRoot":"","sources":["../../../src/core/models/perf-profile.ts"],"names":[],"mappings":"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","sourcesContent":["import { estimateProviderRequestTokens } from \"@caupulican/pi-agent-core/provider-request-estimator\";\nimport type { StreamIdleOptions } from \"@caupulican/pi-agent-core/reliability\";\nimport type { StreamFn } from \"@caupulican/pi-agent-core/types\";\nimport { createAssistantMessageEventStream } from \"@caupulican/pi-ai/event-stream\";\nimport type {\n\tApi,\n\tAssistantMessage,\n\tAssistantMessageEvent,\n\tContext,\n\tModel,\n\tProviderResponse,\n} from \"@caupulican/pi-ai/types\";\nimport { createEmptyUsage } from \"@caupulican/pi-ai/usage\";\n\nconst PERF_EWMA_ALPHA = 0.3;\nconst DEFERRED_HEADERS_MAX_GAP_MS = 100;\nconst DEFERRED_HEADERS_MIN_REQUEST_MS = 1_000;\nexport const DEFAULT_ADAPTIVE_STREAM_IDLE_CEILING_MS = 30 * 60 * 1000;\n\nexport interface ModelPerfProfile {\n\tprefillTokensPerSecond?: number;\n\tdecodeTokensPerSecond?: number;\n\tloadMs?: number;\n\tsamples: number;\n\tupdatedAt: string;\n}\n\nexport interface ModelPerfSample {\n\tpromptTokens?: number;\n\tcompletionTokens?: number;\n\theadersToFirstTokenMs?: number;\n\trequestToFirstTokenMs?: number;\n\tfirstTokenToDoneMs?: number;\n\tloadMs?: number;\n\tat?: string;\n}\n\nexport interface AdaptiveStreamIdleInput {\n\tbase: StreamIdleOptions;\n\tprofile?: ModelPerfProfile;\n\tpromptTokens: number;\n\tceilingMs?: number;\n\tlocalClass?: boolean;\n}\n\nexport interface ModelPerfProfileStreamRecorder {\n\tmodelKey: (model: Model<Api>) => string | undefined;\n\trecordSample: (modelKey: string, sample: ModelPerfSample) => void;\n\tnowMs?: () => number;\n\tnowIso?: () => string;\n}\n\nexport function isModelPerfProfile(value: unknown): value is ModelPerfProfile {\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) return false;\n\tconst record = value as Record<string, unknown>;\n\tconst samples = record.samples;\n\treturn (\n\t\t(record.prefillTokensPerSecond === undefined || isPositiveFiniteNumber(record.prefillTokensPerSecond)) &&\n\t\t(record.decodeTokensPerSecond === undefined || isPositiveFiniteNumber(record.decodeTokensPerSecond)) &&\n\t\t(record.loadMs === undefined || isPositiveFiniteNumber(record.loadMs)) &&\n\t\ttypeof samples === \"number\" &&\n\t\tNumber.isInteger(samples) &&\n\t\tsamples >= 0 &&\n\t\ttypeof record.updatedAt === \"string\"\n\t);\n}\n\nexport function hasUsableModelPerfSample(sample: ModelPerfSample): boolean {\n\treturn (\n\t\tprefillRateFromSample(sample) !== undefined ||\n\t\tdecodeRateFromSample(sample) !== undefined ||\n\t\tisPositiveFiniteNumber(sample.loadMs)\n\t);\n}\n\nexport function updateModelPerfProfile(\n\tcurrent: ModelPerfProfile | undefined,\n\tsample: ModelPerfSample,\n\tat: string = sample.at ?? new Date().toISOString(),\n): ModelPerfProfile | undefined {\n\tconst prefillRate = prefillRateFromSample(sample);\n\tconst decodeRate = decodeRateFromSample(sample);\n\tconst loadMs = isPositiveFiniteNumber(sample.loadMs) ? sample.loadMs : undefined;\n\tif (prefillRate === undefined && decodeRate === undefined && loadMs === undefined) return current;\n\n\tconst previousSamples = current?.samples ?? 0;\n\treturn {\n\t\t...(prefillRate !== undefined && {\n\t\t\tprefillTokensPerSecond: updateEwma(current?.prefillTokensPerSecond, prefillRate, previousSamples),\n\t\t}),\n\t\t...(prefillRate === undefined && current?.prefillTokensPerSecond !== undefined\n\t\t\t? { prefillTokensPerSecond: current.prefillTokensPerSecond }\n\t\t\t: {}),\n\t\t...(decodeRate !== undefined && {\n\t\t\tdecodeTokensPerSecond: updateEwma(current?.decodeTokensPerSecond, decodeRate, previousSamples),\n\t\t}),\n\t\t...(decodeRate === undefined && current?.decodeTokensPerSecond !== undefined\n\t\t\t? { decodeTokensPerSecond: current.decodeTokensPerSecond }\n\t\t\t: {}),\n\t\t...(loadMs !== undefined && { loadMs: updateEwma(current?.loadMs, loadMs, previousSamples) }),\n\t\t...(loadMs === undefined && current?.loadMs !== undefined ? { loadMs: current.loadMs } : {}),\n\t\tsamples: previousSamples + 1,\n\t\tupdatedAt: at,\n\t};\n}\n\nexport function resolveAdaptiveStreamIdleOptions(input: AdaptiveStreamIdleInput): Partial<StreamIdleOptions> {\n\tconst profile = input.profile;\n\tconst result: Partial<StreamIdleOptions> = {};\n\tconst localConnectDefaultMs = input.localClass\n\t\t? Math.max(input.base.connectMs, input.base.quietIdleMs)\n\t\t: input.base.connectMs;\n\tconst firstProgressCeilingMs = Math.max(\n\t\tinput.base.firstProgressMs,\n\t\tinput.ceilingMs ?? DEFAULT_ADAPTIVE_STREAM_IDLE_CEILING_MS,\n\t);\n\tconst quietCeilingMs = Math.max(input.base.quietIdleMs, input.ceilingMs ?? DEFAULT_ADAPTIVE_STREAM_IDLE_CEILING_MS);\n\tconst connectCeilingMs = Math.max(localConnectDefaultMs, input.ceilingMs ?? DEFAULT_ADAPTIVE_STREAM_IDLE_CEILING_MS);\n\n\tconst expectedPrefillMs = expectedPrefillFromProfile(profile, input.promptTokens);\n\tif (expectedPrefillMs !== undefined) {\n\t\tconst firstProgressMs = adaptiveBound(input.base.firstProgressMs, firstProgressCeilingMs, expectedPrefillMs);\n\t\tif (firstProgressMs > input.base.firstProgressMs) result.firstProgressMs = firstProgressMs;\n\t\tconst quietIdleMs = adaptiveBound(input.base.quietIdleMs, quietCeilingMs, expectedPrefillMs);\n\t\tif (quietIdleMs > input.base.quietIdleMs) result.quietIdleMs = quietIdleMs;\n\t}\n\n\tif (input.localClass) {\n\t\tconst expectedConnectWorkMs =\n\t\t\texpectedPrefillMs !== undefined ? expectedPrefillMs + (profile?.loadMs ?? 0) : profile?.loadMs;\n\t\tconst connectMs =\n\t\t\texpectedConnectWorkMs !== undefined\n\t\t\t\t? adaptiveBound(localConnectDefaultMs, connectCeilingMs, expectedConnectWorkMs)\n\t\t\t\t: localConnectDefaultMs;\n\t\tif (connectMs > input.base.connectMs) result.connectMs = connectMs;\n\t}\n\n\treturn result;\n}\n\nexport function estimateContextPromptTokens(context: Context): number {\n\treturn estimateProviderRequestTokens(context);\n}\n\nexport function withModelPerfProfile(streamFn: StreamFn, recorder: ModelPerfProfileStreamRecorder): StreamFn {\n\treturn async (model, context, streamOptions) => {\n\t\tconst nowMs = recorder.nowMs ?? Date.now;\n\t\tconst nowIso = recorder.nowIso ?? (() => new Date().toISOString());\n\t\tconst requestStartedAtMs = nowMs();\n\t\tlet responseHeadersAtMs: number | undefined;\n\t\tlet firstTokenAtMs: number | undefined;\n\t\tconst originalOnResponse = streamOptions?.onResponse;\n\t\tconst outer = createAssistantMessageEventStream();\n\t\tlet latest = emptyAssistantMessage(model);\n\t\tlet inner: Awaited<ReturnType<StreamFn>>;\n\t\ttry {\n\t\t\tinner = await streamFn(model, context, {\n\t\t\t\t...streamOptions,\n\t\t\t\tonResponse: async (response: ProviderResponse, responseModel: Model<Api>) => {\n\t\t\t\t\tresponseHeadersAtMs ??= nowMs();\n\t\t\t\t\tawait originalOnResponse?.(response, responseModel);\n\t\t\t\t},\n\t\t\t});\n\t\t} catch (error) {\n\t\t\touter.push(perfStreamFailure(latest, streamOptions?.signal?.aborted === true, error));\n\t\t\treturn outer;\n\t\t}\n\n\t\tvoid (async () => {\n\t\t\tlet terminalPushed = false;\n\t\t\ttry {\n\t\t\t\tfor await (const event of inner) {\n\t\t\t\t\tlatest = assistantMessageFromEvent(event);\n\t\t\t\t\tif (firstTokenAtMs === undefined && isFirstTokenEvent(event)) {\n\t\t\t\t\t\tfirstTokenAtMs = nowMs();\n\t\t\t\t\t}\n\t\t\t\t\tif (event.type === \"done\") {\n\t\t\t\t\t\tterminalPushed = true;\n\t\t\t\t\t\trecordSuccessfulStreamSample({\n\t\t\t\t\t\t\tcompletionTokens: event.message.usage.output,\n\t\t\t\t\t\t\tdoneAtMs: nowMs(),\n\t\t\t\t\t\t\tfirstTokenAtMs,\n\t\t\t\t\t\t\tmodel,\n\t\t\t\t\t\t\tnowIso,\n\t\t\t\t\t\t\tpromptTokens:\n\t\t\t\t\t\t\t\tevent.message.usage.input + event.message.usage.cacheRead + event.message.usage.cacheWrite,\n\t\t\t\t\t\t\trecorder,\n\t\t\t\t\t\t\trequestStartedAtMs,\n\t\t\t\t\t\t\tresponseHeadersAtMs,\n\t\t\t\t\t\t});\n\t\t\t\t\t} else if (event.type === \"error\") {\n\t\t\t\t\t\tterminalPushed = true;\n\t\t\t\t\t}\n\t\t\t\t\touter.push(event);\n\t\t\t\t\tif (terminalPushed) return;\n\t\t\t\t}\n\t\t\t\touter.push(perfStreamFailure(latest, streamOptions?.signal?.aborted === true));\n\t\t\t} catch (error) {\n\t\t\t\touter.push(perfStreamFailure(latest, streamOptions?.signal?.aborted === true, error));\n\t\t\t}\n\t\t})();\n\n\t\treturn outer;\n\t};\n}\n\nfunction assistantMessageFromEvent(event: AssistantMessageEvent): AssistantMessage {\n\tif (event.type === \"done\") return event.message;\n\tif (event.type === \"error\") return event.error;\n\treturn event.partial;\n}\n\nfunction emptyAssistantMessage(model: Model<Api>): AssistantMessage {\n\treturn {\n\t\trole: \"assistant\",\n\t\tcontent: [],\n\t\tapi: model.api,\n\t\tprovider: model.provider,\n\t\tmodel: model.id,\n\t\tusage: createEmptyUsage(),\n\t\tstopReason: \"stop\",\n\t\ttimestamp: Date.now(),\n\t};\n}\n\nfunction perfStreamFailure(latest: AssistantMessage, aborted: boolean, error?: unknown): AssistantMessageEvent {\n\tconst stopReason = aborted ? \"aborted\" : \"error\";\n\tconst detail = error instanceof Error ? `: ${error.message}` : error === undefined ? \"\" : `: ${String(error)}`;\n\treturn {\n\t\ttype: \"error\",\n\t\treason: stopReason,\n\t\terror: {\n\t\t\t...latest,\n\t\t\tstopReason,\n\t\t\terrorMessage: aborted\n\t\t\t\t? `stream aborted before terminal event${detail}`\n\t\t\t\t: `stream ended without terminal event${detail}`,\n\t\t},\n\t};\n}\n\nfunction recordSuccessfulStreamSample(input: {\n\tmodel: Model<Api>;\n\trecorder: ModelPerfProfileStreamRecorder;\n\trequestStartedAtMs: number;\n\tresponseHeadersAtMs: number | undefined;\n\tfirstTokenAtMs: number | undefined;\n\tdoneAtMs: number;\n\tpromptTokens: number;\n\tcompletionTokens: number;\n\tnowIso: () => string;\n}): void {\n\tif (input.firstTokenAtMs === undefined) return;\n\tconst modelKey = input.recorder.modelKey(input.model);\n\tif (!modelKey) return;\n\tconst requestToFirstTokenMs = input.firstTokenAtMs - input.requestStartedAtMs;\n\tconst sample: ModelPerfSample = {\n\t\tpromptTokens: input.promptTokens,\n\t\tcompletionTokens: input.completionTokens,\n\t\trequestToFirstTokenMs,\n\t\tfirstTokenToDoneMs: input.doneAtMs - input.firstTokenAtMs,\n\t\tat: input.nowIso(),\n\t};\n\tif (input.responseHeadersAtMs !== undefined) {\n\t\tconst headersToFirstTokenMs = input.firstTokenAtMs - input.responseHeadersAtMs;\n\t\tconst headersWereEffectivelyDeferred =\n\t\t\trequestToFirstTokenMs >= DEFERRED_HEADERS_MIN_REQUEST_MS &&\n\t\t\theadersToFirstTokenMs <= DEFERRED_HEADERS_MAX_GAP_MS;\n\t\tif (!headersWereEffectivelyDeferred) sample.headersToFirstTokenMs = headersToFirstTokenMs;\n\t}\n\tif (!hasUsableModelPerfSample(sample)) return;\n\ttry {\n\t\tinput.recorder.recordSample(modelKey, sample);\n\t} catch {\n\t\t// Perf profiling must never fail the user turn.\n\t}\n}\n\nfunction isFirstTokenEvent(event: AssistantMessageEvent): boolean {\n\treturn (\n\t\t(event.type === \"text_delta\" || event.type === \"thinking_delta\" || event.type === \"toolcall_delta\") &&\n\t\tevent.delta.length > 0\n\t);\n}\n\nfunction expectedPrefillFromProfile(profile: ModelPerfProfile | undefined, promptTokens: number): number | undefined {\n\tconst prefillRate = profile?.prefillTokensPerSecond;\n\tif (!profile || profile.samples < 1 || !prefillRate || promptTokens <= 0) return undefined;\n\tconst expectedPrefillMs = (promptTokens / prefillRate) * 1000;\n\treturn Number.isFinite(expectedPrefillMs) && expectedPrefillMs > 0 ? expectedPrefillMs : undefined;\n}\n\nfunction adaptiveBound(defaultMs: number, ceilingMs: number, expectedMs: number): number {\n\treturn Math.min(Math.ceil(Math.max(defaultMs, expectedMs * 3)), ceilingMs);\n}\n\nfunction prefillRateFromSample(sample: ModelPerfSample): number | undefined {\n\tconst durationMs = isPositiveFiniteNumber(sample.headersToFirstTokenMs)\n\t\t? sample.headersToFirstTokenMs\n\t\t: sample.requestToFirstTokenMs;\n\treturn tokensPerSecond(sample.promptTokens, durationMs);\n}\n\nfunction decodeRateFromSample(sample: ModelPerfSample): number | undefined {\n\treturn tokensPerSecond(sample.completionTokens, sample.firstTokenToDoneMs);\n}\n\nfunction tokensPerSecond(tokens: number | undefined, durationMs: number | undefined): number | undefined {\n\tif (!isPositiveFiniteNumber(tokens) || !isPositiveFiniteNumber(durationMs)) return undefined;\n\treturn tokens / (durationMs / 1000);\n}\n\nfunction updateEwma(previous: number | undefined, next: number, previousSamples: number): number {\n\tif (previous === undefined || previousSamples === 0) return next;\n\treturn previous * (1 - PERF_EWMA_ALPHA) + next * PERF_EWMA_ALPHA;\n}\n\nfunction isPositiveFiniteNumber(value: unknown): value is number {\n\treturn typeof value === \"number\" && Number.isFinite(value) && value > 0;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"perf-profile.js","sourceRoot":"","sources":["../../../src/core/models/perf-profile.ts"],"names":[],"mappings":"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","sourcesContent":["import { estimateProviderRequestTokens } from \"@caupulican/pi-agent-core/provider-request-estimator\";\nimport { isStreamStallError, type StreamIdleOptions } from \"@caupulican/pi-agent-core/reliability\";\nimport type { StreamFn } from \"@caupulican/pi-agent-core/types\";\nimport { createAssistantMessageEventStream } from \"@caupulican/pi-ai/event-stream\";\nimport type {\n\tApi,\n\tAssistantMessage,\n\tAssistantMessageEvent,\n\tContext,\n\tModel,\n\tProviderResponse,\n} from \"@caupulican/pi-ai/types\";\nimport { createEmptyUsage } from \"@caupulican/pi-ai/usage\";\n\nconst PERF_EWMA_ALPHA = 0.3;\nconst DEFERRED_HEADERS_MAX_GAP_MS = 100;\nconst DEFERRED_HEADERS_MIN_REQUEST_MS = 1_000;\nexport const DEFAULT_ADAPTIVE_STREAM_IDLE_CEILING_MS = 30 * 60 * 1000;\n\nexport interface ModelPerfProfile {\n\tprefillTokensPerSecond?: number;\n\tdecodeTokensPerSecond?: number;\n\tloadMs?: number;\n\tfirstProgressStall?: {\n\t\telapsedMs: number;\n\t\tpromptTokens: number;\n\t\tconsecutive: number;\n\t\tobservedAt: string;\n\t};\n\tsamples: number;\n\tupdatedAt: string;\n}\n\nexport interface ModelPerfSample {\n\tpromptTokens?: number;\n\tcompletionTokens?: number;\n\theadersToFirstTokenMs?: number;\n\trequestToFirstTokenMs?: number;\n\tfirstTokenToDoneMs?: number;\n\tloadMs?: number;\n\t/** Censored observation: transport connected but emitted no model token before this bound. */\n\tfirstProgressStallMs?: number;\n\tat?: string;\n}\n\nexport interface AdaptiveStreamIdleInput {\n\tbase: StreamIdleOptions;\n\tprofile?: ModelPerfProfile;\n\tpromptTokens: number;\n\tceilingMs?: number;\n\tlocalClass?: boolean;\n}\n\nexport interface ModelPerfProfileStreamRecorder {\n\tmodelKey: (model: Model<Api>) => string | undefined;\n\trecordSample: (modelKey: string, sample: ModelPerfSample) => void;\n\tnowMs?: () => number;\n\tnowIso?: () => string;\n}\n\nexport function isModelPerfProfile(value: unknown): value is ModelPerfProfile {\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) return false;\n\tconst record = value as Record<string, unknown>;\n\tconst samples = record.samples;\n\treturn (\n\t\t(record.prefillTokensPerSecond === undefined || isPositiveFiniteNumber(record.prefillTokensPerSecond)) &&\n\t\t(record.decodeTokensPerSecond === undefined || isPositiveFiniteNumber(record.decodeTokensPerSecond)) &&\n\t\t(record.loadMs === undefined || isPositiveFiniteNumber(record.loadMs)) &&\n\t\t(record.firstProgressStall === undefined || isFirstProgressStall(record.firstProgressStall)) &&\n\t\ttypeof samples === \"number\" &&\n\t\tNumber.isInteger(samples) &&\n\t\tsamples >= 0 &&\n\t\ttypeof record.updatedAt === \"string\"\n\t);\n}\n\nexport function hasUsableModelPerfSample(sample: ModelPerfSample): boolean {\n\treturn (\n\t\tprefillRateFromSample(sample) !== undefined ||\n\t\tdecodeRateFromSample(sample) !== undefined ||\n\t\tisPositiveFiniteNumber(sample.loadMs) ||\n\t\t(isPositiveFiniteNumber(sample.firstProgressStallMs) && isPositiveFiniteNumber(sample.promptTokens))\n\t);\n}\n\nexport function updateModelPerfProfile(\n\tcurrent: ModelPerfProfile | undefined,\n\tsample: ModelPerfSample,\n\tat: string = sample.at ?? new Date().toISOString(),\n): ModelPerfProfile | undefined {\n\tconst prefillRate = prefillRateFromSample(sample);\n\tconst decodeRate = decodeRateFromSample(sample);\n\tconst loadMs = isPositiveFiniteNumber(sample.loadMs) ? sample.loadMs : undefined;\n\tconst firstProgressStallMs = isPositiveFiniteNumber(sample.firstProgressStallMs)\n\t\t? sample.firstProgressStallMs\n\t\t: undefined;\n\tconst stallPromptTokens = isPositiveFiniteNumber(sample.promptTokens) ? sample.promptTokens : undefined;\n\tif (\n\t\tprefillRate === undefined &&\n\t\tdecodeRate === undefined &&\n\t\tloadMs === undefined &&\n\t\tfirstProgressStallMs !== undefined &&\n\t\tstallPromptTokens !== undefined\n\t) {\n\t\tconst previous = current?.firstProgressStall;\n\t\tconst comparablePrompt =\n\t\t\tprevious !== undefined &&\n\t\t\tstallPromptTokens >= previous.promptTokens / 2 &&\n\t\t\tstallPromptTokens <= previous.promptTokens * 2;\n\t\treturn {\n\t\t\t...current,\n\t\t\tfirstProgressStall: {\n\t\t\t\telapsedMs: firstProgressStallMs,\n\t\t\t\tpromptTokens: stallPromptTokens,\n\t\t\t\tconsecutive: comparablePrompt ? previous.consecutive + 1 : 1,\n\t\t\t\tobservedAt: at,\n\t\t\t},\n\t\t\tsamples: current?.samples ?? 0,\n\t\t\tupdatedAt: at,\n\t\t};\n\t}\n\tif (prefillRate === undefined && decodeRate === undefined && loadMs === undefined) return current;\n\n\tconst previousSamples = current?.samples ?? 0;\n\treturn {\n\t\t...(prefillRate !== undefined && {\n\t\t\tprefillTokensPerSecond: updateEwma(current?.prefillTokensPerSecond, prefillRate, previousSamples),\n\t\t}),\n\t\t...(prefillRate === undefined && current?.prefillTokensPerSecond !== undefined\n\t\t\t? { prefillTokensPerSecond: current.prefillTokensPerSecond }\n\t\t\t: {}),\n\t\t...(decodeRate !== undefined && {\n\t\t\tdecodeTokensPerSecond: updateEwma(current?.decodeTokensPerSecond, decodeRate, previousSamples),\n\t\t}),\n\t\t...(decodeRate === undefined && current?.decodeTokensPerSecond !== undefined\n\t\t\t? { decodeTokensPerSecond: current.decodeTokensPerSecond }\n\t\t\t: {}),\n\t\t...(loadMs !== undefined && { loadMs: updateEwma(current?.loadMs, loadMs, previousSamples) }),\n\t\t...(loadMs === undefined && current?.loadMs !== undefined ? { loadMs: current.loadMs } : {}),\n\t\tsamples: previousSamples + 1,\n\t\tupdatedAt: at,\n\t};\n}\n\nexport function resolveAdaptiveStreamIdleOptions(input: AdaptiveStreamIdleInput): Partial<StreamIdleOptions> {\n\tconst profile = input.profile;\n\tconst result: Partial<StreamIdleOptions> = {};\n\tconst localConnectDefaultMs = input.localClass\n\t\t? Math.max(input.base.connectMs, input.base.quietIdleMs)\n\t\t: input.base.connectMs;\n\tconst firstProgressCeilingMs = Math.max(\n\t\tinput.base.firstProgressMs,\n\t\tinput.ceilingMs ?? DEFAULT_ADAPTIVE_STREAM_IDLE_CEILING_MS,\n\t);\n\tconst quietCeilingMs = Math.max(input.base.quietIdleMs, input.ceilingMs ?? DEFAULT_ADAPTIVE_STREAM_IDLE_CEILING_MS);\n\tconst connectCeilingMs = Math.max(localConnectDefaultMs, input.ceilingMs ?? DEFAULT_ADAPTIVE_STREAM_IDLE_CEILING_MS);\n\n\tconst expectedPrefillMs = expectedPrefillFromProfile(profile, input.promptTokens);\n\tif (expectedPrefillMs !== undefined) {\n\t\tconst firstProgressMs = adaptiveBound(input.base.firstProgressMs, firstProgressCeilingMs, expectedPrefillMs);\n\t\tif (firstProgressMs > input.base.firstProgressMs) result.firstProgressMs = firstProgressMs;\n\t\tconst quietIdleMs = adaptiveBound(input.base.quietIdleMs, quietCeilingMs, expectedPrefillMs);\n\t\tif (quietIdleMs > input.base.quietIdleMs) result.quietIdleMs = quietIdleMs;\n\t}\n\tconst firstProgressStall = profile?.firstProgressStall;\n\tif (firstProgressStall && input.promptTokens >= firstProgressStall.promptTokens / 2) {\n\t\tconst nextAttemptBoundMs = Math.min(Math.ceil(firstProgressStall.elapsedMs * 2), firstProgressCeilingMs);\n\t\tconst currentBoundMs = result.firstProgressMs ?? input.base.firstProgressMs;\n\t\tif (nextAttemptBoundMs > currentBoundMs) result.firstProgressMs = nextAttemptBoundMs;\n\t}\n\n\tif (input.localClass) {\n\t\tconst expectedConnectWorkMs =\n\t\t\texpectedPrefillMs !== undefined ? expectedPrefillMs + (profile?.loadMs ?? 0) : profile?.loadMs;\n\t\tconst connectMs =\n\t\t\texpectedConnectWorkMs !== undefined\n\t\t\t\t? adaptiveBound(localConnectDefaultMs, connectCeilingMs, expectedConnectWorkMs)\n\t\t\t\t: localConnectDefaultMs;\n\t\tif (connectMs > input.base.connectMs) result.connectMs = connectMs;\n\t}\n\n\treturn result;\n}\n\nexport function estimateContextPromptTokens(context: Context): number {\n\treturn estimateProviderRequestTokens(context);\n}\n\nexport function withModelPerfProfile(streamFn: StreamFn, recorder: ModelPerfProfileStreamRecorder): StreamFn {\n\treturn async (model, context, streamOptions) => {\n\t\tconst nowMs = recorder.nowMs ?? Date.now;\n\t\tconst nowIso = recorder.nowIso ?? (() => new Date().toISOString());\n\t\tconst requestStartedAtMs = nowMs();\n\t\tlet responseHeadersAtMs: number | undefined;\n\t\tlet firstTokenAtMs: number | undefined;\n\t\tlet firstProgressStallRecorded = false;\n\t\tconst originalOnResponse = streamOptions?.onResponse;\n\t\tconst signal = streamOptions?.signal;\n\t\tconst recordFirstProgressStall = (): void => {\n\t\t\tconst reason = signal?.reason;\n\t\t\tif (\n\t\t\t\tfirstProgressStallRecorded ||\n\t\t\t\tfirstTokenAtMs !== undefined ||\n\t\t\t\t!isStreamStallError(reason) ||\n\t\t\t\treason.phase !== \"first-progress\"\n\t\t\t) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tfirstProgressStallRecorded = true;\n\t\t\tconst modelKey = recorder.modelKey(model);\n\t\t\tif (!modelKey) return;\n\t\t\ttry {\n\t\t\t\trecorder.recordSample(modelKey, {\n\t\t\t\t\tpromptTokens: estimateContextPromptTokens(context),\n\t\t\t\t\tfirstProgressStallMs: reason.elapsedMs,\n\t\t\t\t\tat: nowIso(),\n\t\t\t\t});\n\t\t\t} catch {\n\t\t\t\t// Perf profiling must never fail the user turn.\n\t\t\t}\n\t\t};\n\t\tif (signal?.aborted) recordFirstProgressStall();\n\t\telse signal?.addEventListener(\"abort\", recordFirstProgressStall, { once: true });\n\t\tconst outer = createAssistantMessageEventStream();\n\t\tlet latest = emptyAssistantMessage(model);\n\t\tlet inner: Awaited<ReturnType<StreamFn>>;\n\t\ttry {\n\t\t\tinner = await streamFn(model, context, {\n\t\t\t\t...streamOptions,\n\t\t\t\tonResponse: async (response: ProviderResponse, responseModel: Model<Api>) => {\n\t\t\t\t\tresponseHeadersAtMs ??= nowMs();\n\t\t\t\t\tawait originalOnResponse?.(response, responseModel);\n\t\t\t\t},\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tsignal?.removeEventListener(\"abort\", recordFirstProgressStall);\n\t\t\touter.push(perfStreamFailure(latest, streamOptions?.signal?.aborted === true, error));\n\t\t\treturn outer;\n\t\t}\n\n\t\tvoid (async () => {\n\t\t\tlet terminalPushed = false;\n\t\t\ttry {\n\t\t\t\tfor await (const event of inner) {\n\t\t\t\t\tlatest = assistantMessageFromEvent(event);\n\t\t\t\t\tif (firstTokenAtMs === undefined && isFirstTokenEvent(event)) {\n\t\t\t\t\t\tfirstTokenAtMs = nowMs();\n\t\t\t\t\t}\n\t\t\t\t\tif (event.type === \"done\") {\n\t\t\t\t\t\tterminalPushed = true;\n\t\t\t\t\t\trecordSuccessfulStreamSample({\n\t\t\t\t\t\t\tcompletionTokens: event.message.usage.output,\n\t\t\t\t\t\t\tdoneAtMs: nowMs(),\n\t\t\t\t\t\t\tfirstTokenAtMs,\n\t\t\t\t\t\t\tmodel,\n\t\t\t\t\t\t\tnowIso,\n\t\t\t\t\t\t\tpromptTokens:\n\t\t\t\t\t\t\t\tevent.message.usage.input + event.message.usage.cacheRead + event.message.usage.cacheWrite,\n\t\t\t\t\t\t\trecorder,\n\t\t\t\t\t\t\trequestStartedAtMs,\n\t\t\t\t\t\t\tresponseHeadersAtMs,\n\t\t\t\t\t\t});\n\t\t\t\t\t} else if (event.type === \"error\") {\n\t\t\t\t\t\tterminalPushed = true;\n\t\t\t\t\t}\n\t\t\t\t\touter.push(event);\n\t\t\t\t\tif (terminalPushed) return;\n\t\t\t\t}\n\t\t\t\touter.push(perfStreamFailure(latest, streamOptions?.signal?.aborted === true));\n\t\t\t} catch (error) {\n\t\t\t\touter.push(perfStreamFailure(latest, streamOptions?.signal?.aborted === true, error));\n\t\t\t} finally {\n\t\t\t\tsignal?.removeEventListener(\"abort\", recordFirstProgressStall);\n\t\t\t}\n\t\t})();\n\n\t\treturn outer;\n\t};\n}\n\nfunction assistantMessageFromEvent(event: AssistantMessageEvent): AssistantMessage {\n\tif (event.type === \"done\") return event.message;\n\tif (event.type === \"error\") return event.error;\n\treturn event.partial;\n}\n\nfunction emptyAssistantMessage(model: Model<Api>): AssistantMessage {\n\treturn {\n\t\trole: \"assistant\",\n\t\tcontent: [],\n\t\tapi: model.api,\n\t\tprovider: model.provider,\n\t\tmodel: model.id,\n\t\tusage: createEmptyUsage(),\n\t\tstopReason: \"stop\",\n\t\ttimestamp: Date.now(),\n\t};\n}\n\nfunction perfStreamFailure(latest: AssistantMessage, aborted: boolean, error?: unknown): AssistantMessageEvent {\n\tconst stopReason = aborted ? \"aborted\" : \"error\";\n\tconst detail = error instanceof Error ? `: ${error.message}` : error === undefined ? \"\" : `: ${String(error)}`;\n\treturn {\n\t\ttype: \"error\",\n\t\treason: stopReason,\n\t\terror: {\n\t\t\t...latest,\n\t\t\tstopReason,\n\t\t\terrorMessage: aborted\n\t\t\t\t? `stream aborted before terminal event${detail}`\n\t\t\t\t: `stream ended without terminal event${detail}`,\n\t\t},\n\t};\n}\n\nfunction recordSuccessfulStreamSample(input: {\n\tmodel: Model<Api>;\n\trecorder: ModelPerfProfileStreamRecorder;\n\trequestStartedAtMs: number;\n\tresponseHeadersAtMs: number | undefined;\n\tfirstTokenAtMs: number | undefined;\n\tdoneAtMs: number;\n\tpromptTokens: number;\n\tcompletionTokens: number;\n\tnowIso: () => string;\n}): void {\n\tif (input.firstTokenAtMs === undefined) return;\n\tconst modelKey = input.recorder.modelKey(input.model);\n\tif (!modelKey) return;\n\tconst requestToFirstTokenMs = input.firstTokenAtMs - input.requestStartedAtMs;\n\tconst sample: ModelPerfSample = {\n\t\tpromptTokens: input.promptTokens,\n\t\tcompletionTokens: input.completionTokens,\n\t\trequestToFirstTokenMs,\n\t\tfirstTokenToDoneMs: input.doneAtMs - input.firstTokenAtMs,\n\t\tat: input.nowIso(),\n\t};\n\tif (input.responseHeadersAtMs !== undefined) {\n\t\tconst headersToFirstTokenMs = input.firstTokenAtMs - input.responseHeadersAtMs;\n\t\tconst headersWereEffectivelyDeferred =\n\t\t\trequestToFirstTokenMs >= DEFERRED_HEADERS_MIN_REQUEST_MS &&\n\t\t\theadersToFirstTokenMs <= DEFERRED_HEADERS_MAX_GAP_MS;\n\t\tif (!headersWereEffectivelyDeferred) sample.headersToFirstTokenMs = headersToFirstTokenMs;\n\t}\n\tif (!hasUsableModelPerfSample(sample)) return;\n\ttry {\n\t\tinput.recorder.recordSample(modelKey, sample);\n\t} catch {\n\t\t// Perf profiling must never fail the user turn.\n\t}\n}\n\nfunction isFirstTokenEvent(event: AssistantMessageEvent): boolean {\n\treturn (\n\t\t(event.type === \"text_delta\" || event.type === \"thinking_delta\" || event.type === \"toolcall_delta\") &&\n\t\tevent.delta.length > 0\n\t);\n}\n\nfunction expectedPrefillFromProfile(profile: ModelPerfProfile | undefined, promptTokens: number): number | undefined {\n\tconst prefillRate = profile?.prefillTokensPerSecond;\n\tif (!profile || profile.samples < 1 || !prefillRate || promptTokens <= 0) return undefined;\n\tconst expectedPrefillMs = (promptTokens / prefillRate) * 1000;\n\treturn Number.isFinite(expectedPrefillMs) && expectedPrefillMs > 0 ? expectedPrefillMs : undefined;\n}\n\nfunction adaptiveBound(defaultMs: number, ceilingMs: number, expectedMs: number): number {\n\treturn Math.min(Math.ceil(Math.max(defaultMs, expectedMs * 3)), ceilingMs);\n}\n\nfunction prefillRateFromSample(sample: ModelPerfSample): number | undefined {\n\tconst durationMs = isPositiveFiniteNumber(sample.headersToFirstTokenMs)\n\t\t? sample.headersToFirstTokenMs\n\t\t: sample.requestToFirstTokenMs;\n\treturn tokensPerSecond(sample.promptTokens, durationMs);\n}\n\nfunction decodeRateFromSample(sample: ModelPerfSample): number | undefined {\n\treturn tokensPerSecond(sample.completionTokens, sample.firstTokenToDoneMs);\n}\n\nfunction tokensPerSecond(tokens: number | undefined, durationMs: number | undefined): number | undefined {\n\tif (!isPositiveFiniteNumber(tokens) || !isPositiveFiniteNumber(durationMs)) return undefined;\n\treturn tokens / (durationMs / 1000);\n}\n\nfunction updateEwma(previous: number | undefined, next: number, previousSamples: number): number {\n\tif (previous === undefined || previousSamples === 0) return next;\n\treturn previous * (1 - PERF_EWMA_ALPHA) + next * PERF_EWMA_ALPHA;\n}\n\nfunction isPositiveFiniteNumber(value: unknown): value is number {\n\treturn typeof value === \"number\" && Number.isFinite(value) && value > 0;\n}\n\nfunction isFirstProgressStall(value: unknown): value is NonNullable<ModelPerfProfile[\"firstProgressStall\"]> {\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) return false;\n\tconst record = value as Record<string, unknown>;\n\treturn (\n\t\tisPositiveFiniteNumber(record.elapsedMs) &&\n\t\tisPositiveFiniteNumber(record.promptTokens) &&\n\t\ttypeof record.consecutive === \"number\" &&\n\t\tNumber.isSafeInteger(record.consecutive) &&\n\t\trecord.consecutive > 0 &&\n\t\ttypeof record.observedAt === \"string\"\n\t);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credential-exposure-guard.d.ts","sourceRoot":"","sources":["../../../src/core/secrets/credential-exposure-guard.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAiD,MAAM,2BAA2B,CAAC;AAC1G,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAgCvC,MAAM,WAAW,0BAA0B;IAC1C,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1C,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,oBAAoB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC;AAWD,wBAAgB,yBAAyB,CACxC,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,0BAA0B,GACnC,OAAO,CAgCT;
|
|
1
|
+
{"version":3,"file":"credential-exposure-guard.d.ts","sourceRoot":"","sources":["../../../src/core/secrets/credential-exposure-guard.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAiD,MAAM,2BAA2B,CAAC;AAC1G,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAgCvC,MAAM,WAAW,0BAA0B;IAC1C,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1C,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,oBAAoB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC;AAWD,wBAAgB,yBAAyB,CACxC,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,0BAA0B,GACnC,OAAO,CAgCT;AAsID,yFAAyF;AACzF,wBAAgB,yBAAyB,CACxC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,OAAO,EACb,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,0BAA0B,GACnC,MAAM,GAAG,SAAS,CAwDpB;AAuCD,wGAAwG;AACxG,wBAAgB,mCAAmC,CAAC,WAAW,SAAS,OAAO,EAAE,QAAQ,EACxF,IAAI,EAAE,SAAS,CAAC,WAAW,EAAE,QAAQ,CAAC,EACtC,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,0BAA0B,GACnC,SAAS,CAAC,WAAW,EAAE,QAAQ,CAAC,CAkClC"}
|
|
@@ -96,7 +96,7 @@ function shellCredentialRisk(command, cwd, boundary) {
|
|
|
96
96
|
}
|
|
97
97
|
const hasSafeGlob = scope.positiveGlobs.length > 0 && scope.positiveGlobs.every((glob) => isCredentialSafeGlob(glob));
|
|
98
98
|
const hasOnlyExplicitFiles = scope.targets.length > 0 &&
|
|
99
|
-
scope.targets.every((target) => target === "-" ||
|
|
99
|
+
scope.targets.every((target) => target === "-" || isCredentialSafeExplicitFile(target, cwd));
|
|
100
100
|
if (!scope.metaOnly && !scope.readsStdin && !hasSafeGlob && !hasOnlyExplicitFiles)
|
|
101
101
|
return "broad_search";
|
|
102
102
|
continue;
|
|
@@ -162,8 +162,11 @@ function isCredentialSafeGlob(glob) {
|
|
|
162
162
|
if (/(?:^|[\\/])?\.env(?:\.|\*|$)/i.test(glob))
|
|
163
163
|
return false;
|
|
164
164
|
const filePattern = glob.replace(/\\/g, "/").split("/").at(-1) ?? "";
|
|
165
|
-
const
|
|
166
|
-
|
|
165
|
+
const braceSuffixes = filePattern.match(/\.\{([A-Za-z0-9_-]+(?:,[A-Za-z0-9_-]+)+)\}$/)?.[1];
|
|
166
|
+
const suffixes = braceSuffixes
|
|
167
|
+
? braceSuffixes.split(",").map((suffix) => suffix.toLowerCase())
|
|
168
|
+
: [filePattern.match(/\.([A-Za-z0-9_-]+)$/)?.[1]?.toLowerCase()].filter((suffix) => suffix !== undefined);
|
|
169
|
+
return suffixes.length > 0 && suffixes.every((suffix) => suffix !== "env");
|
|
167
170
|
}
|
|
168
171
|
function isExistingRegularFile(rawPath, cwd) {
|
|
169
172
|
try {
|
|
@@ -173,6 +176,14 @@ function isExistingRegularFile(rawPath, cwd) {
|
|
|
173
176
|
return false;
|
|
174
177
|
}
|
|
175
178
|
}
|
|
179
|
+
function isCredentialSafeExplicitFile(rawPath, cwd) {
|
|
180
|
+
try {
|
|
181
|
+
return statSync(resolve(cwd, rawPath)).isFile();
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
return isCredentialSafeGlob(rawPath);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
176
187
|
function pythonInspectsCredentialPath(code, cwd, boundary) {
|
|
177
188
|
if (!PYTHON_INSPECTION_RE.test(code))
|
|
178
189
|
return false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credential-exposure-guard.js","sourceRoot":"","sources":["../../../src/core/secrets/credential-exposure-guard.ts"],"names":[],"mappings":"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","sourcesContent":["import { realpathSync, statSync } from \"node:fs\";\nimport { basename, isAbsolute, relative, resolve, sep } from \"node:path\";\nimport { type AgentTool, AgentToolExecutionError, type AgentToolResult } from \"@caupulican/pi-agent-core\";\nimport type { TSchema } from \"typebox\";\nimport { redactKnownSecrets } from \"../security/secret-text.ts\";\nimport { parseShellSearchInvocationScope, type ShellContentSearchTool } from \"../tools/search-command-guard.ts\";\nimport { tokenizeShellCommand } from \"../tools/shell-command-parser.ts\";\n\nconst DIRECT_PATH_TOOLS = new Set([\"read\", \"edit\", \"write\", \"ls\"]);\nconst SHELL_INSPECTION_COMMANDS = new Set([\n\t\"cat\",\n\t\"head\",\n\t\"tail\",\n\t\"less\",\n\t\"more\",\n\t\"sed\",\n\t\"awk\",\n\t\"grep\",\n\t\"rg\",\n\t\"ripgrep\",\n\t\"type\",\n\t\"get-content\",\n\t\"select-string\",\n\t\"source\",\n]);\nconst SHELL_SECRET_READ_RE =\n\t/\\b(?:cat|head|tail|less|more|sed|awk|grep|rg|type|get-content|select-string|source)\\b[^\\n;&|]*(?:^|[\\\\/])?\\.env(?:\\.[A-Za-z0-9._-]+)?\\b/i;\nconst PYTHON_SECRET_READ_RE = /\\b(?:open|read_text|read_bytes)\\s*\\([^\\n)]*(?:^|[\\\\/])?\\.env(?:\\.[A-Za-z0-9._-]+)?\\b/i;\nconst PYTHON_INSPECTION_RE = /\\b(?:open|read_text|read_bytes)\\b/;\nconst QUOTED_TEXT_RE = /([\"'])([^\"'\\\\]*(?:\\\\.[^\"'\\\\]*)*)\\1/g;\nconst MAX_REDACTED_DETAIL_DEPTH = 8;\nconst MAX_REDACTED_DETAIL_NODES = 10_000;\nconst JQ_OPTIONS_WITH_ONE_OPERAND = new Set([\"-L\", \"--indent\"]);\nconst JQ_OPTIONS_WITH_TWO_OPERANDS = new Set([\"--arg\", \"--argjson\", \"--rawfile\", \"--slurpfile\"]);\n\nexport interface CredentialExposureBoundary {\n\tredactSensitiveText(text: string): string;\n\tprotectedFiles?: readonly string[];\n\tprotectedDirectories?: readonly string[];\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isInside(root: string, target: string): boolean {\n\tconst fromRoot = relative(root, target);\n\treturn fromRoot === \"\" || (!fromRoot.startsWith(`..${sep}`) && fromRoot !== \"..\" && !isAbsolute(fromRoot));\n}\n\nexport function isProtectedCredentialPath(\n\trawPath: string,\n\tcwd: string,\n\tboundary?: CredentialExposureBoundary,\n): boolean {\n\tconst resolved = resolve(cwd, rawPath);\n\tconst candidates = [resolved];\n\ttry {\n\t\tconst canonical = realpathSync.native(resolved);\n\t\tif (canonical !== resolved) candidates.push(canonical);\n\t} catch {\n\t\t// Nonexistent write targets still receive the lexical filename check.\n\t}\n\tconst protectedFiles = (boundary?.protectedFiles ?? []).map((path) => resolve(path));\n\tconst protectedDirectories = (boundary?.protectedDirectories ?? []).map((path) => resolve(path));\n\tfor (const path of [...protectedFiles]) {\n\t\ttry {\n\t\t\tprotectedFiles.push(realpathSync.native(path));\n\t\t} catch {\n\t\t\t// A not-yet-created protected file still retains its lexical path.\n\t\t}\n\t}\n\tfor (const path of [...protectedDirectories]) {\n\t\ttry {\n\t\t\tprotectedDirectories.push(realpathSync.native(path));\n\t\t} catch {\n\t\t\t// A not-yet-created protected directory still retains its lexical root.\n\t\t}\n\t}\n\treturn candidates.some((candidate) => {\n\t\tif (protectedFiles.includes(candidate) || protectedDirectories.some((root) => isInside(root, candidate))) {\n\t\t\treturn true;\n\t\t}\n\t\tconst fileName = basename(candidate);\n\t\treturn fileName === \".env\" || fileName.startsWith(\".env.\") || fileName.endsWith(\".env\");\n\t});\n}\n\nfunction shellCredentialRisk(\n\tcommand: string,\n\tcwd: string,\n\tboundary?: CredentialExposureBoundary,\n): \"broad_search\" | \"credential_path\" | \"process_environment\" | undefined {\n\tconst shellTokens = tokenizeShellCommand(command);\n\tif (!shellTokens) return /\\b(?:grep|rg)\\b/iu.test(command) ? \"broad_search\" : undefined;\n\n\tconst assessInvocation = (\n\t\tinvocation: string[],\n\t\treadsPipe: boolean,\n\t): \"broad_search\" | \"credential_path\" | \"process_environment\" | undefined => {\n\t\tfor (let commandIndex = 0; commandIndex < invocation.length; commandIndex++) {\n\t\t\tconst toolName = (invocation[commandIndex].split(/[\\\\/]/u).at(-1) ?? \"\").toLowerCase().replace(/\\.exe$/u, \"\");\n\t\t\tif (toolName === \"jq\") {\n\t\t\t\tconst filter = jqFilterFromArgs(invocation.slice(commandIndex + 1));\n\t\t\t\tif (filter && jqFilterReadsProcessEnvironment(filter)) return \"process_environment\";\n\t\t\t}\n\t\t\tif (!SHELL_INSPECTION_COMMANDS.has(toolName)) continue;\n\t\t\tconst args = invocation.slice(commandIndex + 1);\n\t\t\tconst searchTool: ShellContentSearchTool | undefined =\n\t\t\t\ttoolName === \"rg\" || toolName === \"ripgrep\" ? \"rg\" : toolName === \"grep\" ? \"grep\" : undefined;\n\t\t\tif (searchTool) {\n\t\t\t\tconst scope = parseShellSearchInvocationScope(searchTool, args, readsPipe);\n\t\t\t\tif (scope.targets.some((target) => target !== \"-\" && isProtectedCredentialPath(target, cwd, boundary))) {\n\t\t\t\t\treturn \"credential_path\";\n\t\t\t\t}\n\t\t\t\tconst hasSafeGlob =\n\t\t\t\t\tscope.positiveGlobs.length > 0 && scope.positiveGlobs.every((glob) => isCredentialSafeGlob(glob));\n\t\t\t\tconst hasOnlyExplicitFiles =\n\t\t\t\t\tscope.targets.length > 0 &&\n\t\t\t\t\tscope.targets.every((target) => target === \"-\" || isExistingRegularFile(target, cwd));\n\t\t\t\tif (!scope.metaOnly && !scope.readsStdin && !hasSafeGlob && !hasOnlyExplicitFiles) return \"broad_search\";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfor (const token of args) {\n\t\t\t\tif (!token || token.startsWith(\"-\") || token === \".\") continue;\n\t\t\t\tif (isProtectedCredentialPath(token, cwd, boundary)) return \"credential_path\";\n\t\t\t}\n\t\t}\n\t\treturn undefined;\n\t};\n\n\tlet invocation: string[] = [];\n\tlet skipRedirectTarget = false;\n\tlet readsPipe = false;\n\tfor (const token of shellTokens) {\n\t\tif (token.kind === \"arg\") {\n\t\t\tif (skipRedirectTarget) skipRedirectTarget = false;\n\t\t\telse invocation.push(token.value);\n\t\t\tcontinue;\n\t\t}\n\t\tif (token.kind === \"redirect\") {\n\t\t\t// Descriptor duplication (`2>&1`) has no following path operand. Other\n\t\t\t// redirects do, and that target must never count as search scope.\n\t\t\tskipRedirectTarget = !/^\\d*[<>]&[\\d-]+$/u.test(token.value);\n\t\t\tcontinue;\n\t\t}\n\t\tconst risk = assessInvocation(invocation, readsPipe);\n\t\tif (risk) return risk;\n\t\tinvocation = [];\n\t\tskipRedirectTarget = false;\n\t\treadsPipe = token.kind === \"pipe\";\n\t}\n\treturn assessInvocation(invocation, readsPipe);\n}\n\nfunction jqFilterFromArgs(args: string[]): string | undefined {\n\tfor (let index = 0; index < args.length; index++) {\n\t\tconst token = args[index];\n\t\tif (token === \"--\") return args[index + 1];\n\t\tif (JQ_OPTIONS_WITH_TWO_OPERANDS.has(token)) {\n\t\t\tindex += 2;\n\t\t\tcontinue;\n\t\t}\n\t\tif (JQ_OPTIONS_WITH_ONE_OPERAND.has(token)) {\n\t\t\tindex++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (token.startsWith(\"-\")) continue;\n\t\treturn token;\n\t}\n\treturn undefined;\n}\n\nfunction jqFilterReadsProcessEnvironment(filter: string): boolean {\n\treturn /(?:^|[\\s|,(:;[])(?:env|\\$ENV)(?=$|[\\s|),.;[\\]}])/u.test(filter);\n}\n\nfunction isCredentialSafeGlob(glob: string): boolean {\n\tif (/(?:^|[\\\\/])?\\.env(?:\\.|\\*|$)/i.test(glob)) return false;\n\tconst filePattern = glob.replace(/\\\\/g, \"/\").split(\"/\").at(-1) ?? \"\";\n\tconst suffix = filePattern.match(/\\.([A-Za-z0-9_-]+)$/)?.[1]?.toLowerCase();\n\treturn suffix !== undefined && suffix !== \"env\";\n}\n\nfunction isExistingRegularFile(rawPath: string, cwd: string): boolean {\n\ttry {\n\t\treturn statSync(resolve(cwd, rawPath)).isFile();\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction pythonInspectsCredentialPath(code: string, cwd: string, boundary?: CredentialExposureBoundary): boolean {\n\tif (!PYTHON_INSPECTION_RE.test(code)) return false;\n\tQUOTED_TEXT_RE.lastIndex = 0;\n\tfor (const match of code.matchAll(QUOTED_TEXT_RE)) {\n\t\tconst candidate = match[2]?.replace(/\\\\([\\\\\"'])/g, \"$1\");\n\t\tif (candidate && isProtectedCredentialPath(candidate, cwd, boundary)) return true;\n\t}\n\treturn false;\n}\n\nfunction directPathFromArgs(args: unknown): string | undefined {\n\tif (!isRecord(args)) return undefined;\n\treturn typeof args.path === \"string\" ? args.path : undefined;\n}\n\n/** Stable model-facing refusal for direct inspection/mutation of credential material. */\nexport function credentialToolBlockReason(\n\ttoolName: string,\n\targs: unknown,\n\tcwd: string,\n\tboundary?: CredentialExposureBoundary,\n): string | undefined {\n\tif (toolName === \"secret_store\" || !isRecord(args)) return undefined;\n\tif (DIRECT_PATH_TOOLS.has(toolName)) {\n\t\tconst path = directPathFromArgs(args);\n\t\tif (path && isProtectedCredentialPath(path, cwd, boundary)) {\n\t\t\treturn \"Credential file access is model-blind. Use secret_store migrate with this path, or activate an existing profile, without inspecting credential data.\";\n\t\t}\n\t}\n\tif (toolName === \"grep\") {\n\t\tconst path = typeof args.path === \"string\" ? args.path : undefined;\n\t\tconst glob = typeof args.glob === \"string\" ? args.glob : undefined;\n\t\tif (\n\t\t\t(path && isProtectedCredentialPath(path, cwd, boundary)) ||\n\t\t\t(glob && /(?:^|[\\\\/])?\\.env(?:\\.|\\*|$)/i.test(glob))\n\t\t) {\n\t\t\treturn \"Credential dotenv files are model-blind. Use secret_store discover instead of searching their contents.\";\n\t\t}\n\t\tif (!(path && isExistingRegularFile(path, cwd)) && !(glob && isCredentialSafeGlob(glob))) {\n\t\t\treturn \"Credential-safe grep requires one explicit regular file or a narrow non-dotenv file glob (for example *.ts). Refine the search instead of scanning a directory without a file filter.\";\n\t\t}\n\t}\n\tif (toolName === \"find\") {\n\t\tconst path = typeof args.path === \"string\" ? args.path : undefined;\n\t\tconst pattern = typeof args.pattern === \"string\" ? args.pattern : undefined;\n\t\tif (\n\t\t\t(path && isProtectedCredentialPath(path, cwd, boundary)) ||\n\t\t\t(pattern && /(?:^|[\\\\/])?\\.env(?:\\.|\\*|$)/i.test(pattern))\n\t\t) {\n\t\t\treturn \"Credential dotenv files are model-blind. Use secret_store discover instead of searching their contents.\";\n\t\t}\n\t}\n\tif (toolName === \"bash\" || toolName === \"powershell\" || toolName === \"run_process\") {\n\t\tconst command = typeof args.command === \"string\" ? args.command : \"\";\n\t\tconst shellRisk = shellCredentialRisk(command, cwd, boundary);\n\t\tif (shellRisk === \"broad_search\") {\n\t\t\treturn \"Credential-safe shell search requires a narrow non-dotenv file glob (for example -g '*.ts') or one explicit regular file. Refine the rg/grep command before retrying.\";\n\t\t}\n\t\tif (shellRisk === \"process_environment\") {\n\t\t\treturn \"Direct process-environment projection is blocked because it can expose credentials. Use secret_store discover to list eligible source names without exposing values.\";\n\t\t}\n\t\tif (SHELL_SECRET_READ_RE.test(command) || shellRisk === \"credential_path\") {\n\t\t\treturn \"Direct shell inspection of credential files is blocked. Use secret_store migrate with the file path, then run the credential-consuming command normally.\";\n\t\t}\n\t}\n\tif (toolName === \"python\") {\n\t\tconst code = typeof args.code === \"string\" ? args.code : \"\";\n\t\tconst scriptPath = typeof args.scriptPath === \"string\" ? args.scriptPath : undefined;\n\t\tif (\n\t\t\tPYTHON_SECRET_READ_RE.test(code) ||\n\t\t\tpythonInspectsCredentialPath(code, cwd, boundary) ||\n\t\t\t(scriptPath !== undefined && isProtectedCredentialPath(scriptPath, cwd, boundary))\n\t\t) {\n\t\t\treturn \"Direct Python inspection of credential files is blocked. Use secret_store migrate with the file path, then run the credential-consuming program normally.\";\n\t\t}\n\t}\n\treturn undefined;\n}\n\nfunction redactResult<T>(result: AgentToolResult<T>, boundary?: CredentialExposureBoundary): AgentToolResult<T> {\n\tconst redact = (text: string) => (boundary ? boundary.redactSensitiveText(text) : redactKnownSecrets(text));\n\tconst budget = { nodes: 0 };\n\treturn {\n\t\t...result,\n\t\tcontent: result.content.map((block) => (block.type === \"text\" ? { ...block, text: redact(block.text) } : block)),\n\t\tdetails: redactStructuredDetails(result.details, redact, budget) as T,\n\t};\n}\n\nfunction redactStructuredDetails(\n\tvalue: unknown,\n\tredact: (text: string) => string,\n\tbudget: { nodes: number },\n\tdepth = 0,\n): unknown {\n\tif (typeof value === \"string\") return redact(value);\n\tif (value === null || typeof value !== \"object\") return value;\n\tbudget.nodes++;\n\tif (depth >= MAX_REDACTED_DETAIL_DEPTH || budget.nodes > MAX_REDACTED_DETAIL_NODES) {\n\t\treturn \"[DETAIL OMITTED AT CREDENTIAL BOUNDARY]\";\n\t}\n\tif (Array.isArray(value)) {\n\t\treturn value.map((entry) => redactStructuredDetails(entry, redact, budget, depth + 1));\n\t}\n\tlet prototype: object | null;\n\ttry {\n\t\tprototype = Object.getPrototypeOf(value);\n\t} catch {\n\t\treturn \"[UNREADABLE DETAIL OMITTED AT CREDENTIAL BOUNDARY]\";\n\t}\n\tif (prototype !== Object.prototype && prototype !== null) return value;\n\treturn Object.fromEntries(\n\t\tObject.entries(value).map(([key, entry]) => [key, redactStructuredDetails(entry, redact, budget, depth + 1)]),\n\t);\n}\n\n/** Apply the same path refusal and output redaction to foreground, extension, scout, and lane tools. */\nexport function wrapToolWithCredentialExposureGuard<TParameters extends TSchema, TDetails>(\n\ttool: AgentTool<TParameters, TDetails>,\n\tcwd: string,\n\tboundary?: CredentialExposureBoundary,\n): AgentTool<TParameters, TDetails> {\n\treturn {\n\t\t...tool,\n\t\tasync execute(toolCallId, params, signal, onUpdate) {\n\t\t\tconst blockReason = credentialToolBlockReason(tool.name, params, cwd, boundary);\n\t\t\tif (blockReason) {\n\t\t\t\tthrow new AgentToolExecutionError(\n\t\t\t\t\tblockReason,\n\t\t\t\t\t\"credential_access_blocked\",\n\t\t\t\t\t\"credential-access-blocked\",\n\t\t\t\t\t\"tool_failure\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst safeUpdate = onUpdate\n\t\t\t\t? (partial: AgentToolResult<TDetails>) => {\n\t\t\t\t\t\tonUpdate(redactResult(partial, boundary));\n\t\t\t\t\t}\n\t\t\t\t: undefined;\n\t\t\ttry {\n\t\t\t\treturn redactResult(await tool.execute(toolCallId, params, signal, safeUpdate), boundary);\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof Error) {\n\t\t\t\t\tconst message = boundary\n\t\t\t\t\t\t? boundary.redactSensitiveText(error.message)\n\t\t\t\t\t\t: redactKnownSecrets(error.message);\n\t\t\t\t\tif (error instanceof AgentToolExecutionError) {\n\t\t\t\t\t\tthrow new AgentToolExecutionError(message, error.failureCode, error.outputSignature, error.errorKind);\n\t\t\t\t\t}\n\t\t\t\t\tthrow new Error(message);\n\t\t\t\t}\n\t\t\t\tthrow new Error(\"Credential-safe tool execution failed without retaining raw error output.\");\n\t\t\t}\n\t\t},\n\t};\n}\n"]}
|
|
1
|
+
{"version":3,"file":"credential-exposure-guard.js","sourceRoot":"","sources":["../../../src/core/secrets/credential-exposure-guard.ts"],"names":[],"mappings":"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","sourcesContent":["import { realpathSync, statSync } from \"node:fs\";\nimport { basename, isAbsolute, relative, resolve, sep } from \"node:path\";\nimport { type AgentTool, AgentToolExecutionError, type AgentToolResult } from \"@caupulican/pi-agent-core\";\nimport type { TSchema } from \"typebox\";\nimport { redactKnownSecrets } from \"../security/secret-text.ts\";\nimport { parseShellSearchInvocationScope, type ShellContentSearchTool } from \"../tools/search-command-guard.ts\";\nimport { tokenizeShellCommand } from \"../tools/shell-command-parser.ts\";\n\nconst DIRECT_PATH_TOOLS = new Set([\"read\", \"edit\", \"write\", \"ls\"]);\nconst SHELL_INSPECTION_COMMANDS = new Set([\n\t\"cat\",\n\t\"head\",\n\t\"tail\",\n\t\"less\",\n\t\"more\",\n\t\"sed\",\n\t\"awk\",\n\t\"grep\",\n\t\"rg\",\n\t\"ripgrep\",\n\t\"type\",\n\t\"get-content\",\n\t\"select-string\",\n\t\"source\",\n]);\nconst SHELL_SECRET_READ_RE =\n\t/\\b(?:cat|head|tail|less|more|sed|awk|grep|rg|type|get-content|select-string|source)\\b[^\\n;&|]*(?:^|[\\\\/])?\\.env(?:\\.[A-Za-z0-9._-]+)?\\b/i;\nconst PYTHON_SECRET_READ_RE = /\\b(?:open|read_text|read_bytes)\\s*\\([^\\n)]*(?:^|[\\\\/])?\\.env(?:\\.[A-Za-z0-9._-]+)?\\b/i;\nconst PYTHON_INSPECTION_RE = /\\b(?:open|read_text|read_bytes)\\b/;\nconst QUOTED_TEXT_RE = /([\"'])([^\"'\\\\]*(?:\\\\.[^\"'\\\\]*)*)\\1/g;\nconst MAX_REDACTED_DETAIL_DEPTH = 8;\nconst MAX_REDACTED_DETAIL_NODES = 10_000;\nconst JQ_OPTIONS_WITH_ONE_OPERAND = new Set([\"-L\", \"--indent\"]);\nconst JQ_OPTIONS_WITH_TWO_OPERANDS = new Set([\"--arg\", \"--argjson\", \"--rawfile\", \"--slurpfile\"]);\n\nexport interface CredentialExposureBoundary {\n\tredactSensitiveText(text: string): string;\n\tprotectedFiles?: readonly string[];\n\tprotectedDirectories?: readonly string[];\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isInside(root: string, target: string): boolean {\n\tconst fromRoot = relative(root, target);\n\treturn fromRoot === \"\" || (!fromRoot.startsWith(`..${sep}`) && fromRoot !== \"..\" && !isAbsolute(fromRoot));\n}\n\nexport function isProtectedCredentialPath(\n\trawPath: string,\n\tcwd: string,\n\tboundary?: CredentialExposureBoundary,\n): boolean {\n\tconst resolved = resolve(cwd, rawPath);\n\tconst candidates = [resolved];\n\ttry {\n\t\tconst canonical = realpathSync.native(resolved);\n\t\tif (canonical !== resolved) candidates.push(canonical);\n\t} catch {\n\t\t// Nonexistent write targets still receive the lexical filename check.\n\t}\n\tconst protectedFiles = (boundary?.protectedFiles ?? []).map((path) => resolve(path));\n\tconst protectedDirectories = (boundary?.protectedDirectories ?? []).map((path) => resolve(path));\n\tfor (const path of [...protectedFiles]) {\n\t\ttry {\n\t\t\tprotectedFiles.push(realpathSync.native(path));\n\t\t} catch {\n\t\t\t// A not-yet-created protected file still retains its lexical path.\n\t\t}\n\t}\n\tfor (const path of [...protectedDirectories]) {\n\t\ttry {\n\t\t\tprotectedDirectories.push(realpathSync.native(path));\n\t\t} catch {\n\t\t\t// A not-yet-created protected directory still retains its lexical root.\n\t\t}\n\t}\n\treturn candidates.some((candidate) => {\n\t\tif (protectedFiles.includes(candidate) || protectedDirectories.some((root) => isInside(root, candidate))) {\n\t\t\treturn true;\n\t\t}\n\t\tconst fileName = basename(candidate);\n\t\treturn fileName === \".env\" || fileName.startsWith(\".env.\") || fileName.endsWith(\".env\");\n\t});\n}\n\nfunction shellCredentialRisk(\n\tcommand: string,\n\tcwd: string,\n\tboundary?: CredentialExposureBoundary,\n): \"broad_search\" | \"credential_path\" | \"process_environment\" | undefined {\n\tconst shellTokens = tokenizeShellCommand(command);\n\tif (!shellTokens) return /\\b(?:grep|rg)\\b/iu.test(command) ? \"broad_search\" : undefined;\n\n\tconst assessInvocation = (\n\t\tinvocation: string[],\n\t\treadsPipe: boolean,\n\t): \"broad_search\" | \"credential_path\" | \"process_environment\" | undefined => {\n\t\tfor (let commandIndex = 0; commandIndex < invocation.length; commandIndex++) {\n\t\t\tconst toolName = (invocation[commandIndex].split(/[\\\\/]/u).at(-1) ?? \"\").toLowerCase().replace(/\\.exe$/u, \"\");\n\t\t\tif (toolName === \"jq\") {\n\t\t\t\tconst filter = jqFilterFromArgs(invocation.slice(commandIndex + 1));\n\t\t\t\tif (filter && jqFilterReadsProcessEnvironment(filter)) return \"process_environment\";\n\t\t\t}\n\t\t\tif (!SHELL_INSPECTION_COMMANDS.has(toolName)) continue;\n\t\t\tconst args = invocation.slice(commandIndex + 1);\n\t\t\tconst searchTool: ShellContentSearchTool | undefined =\n\t\t\t\ttoolName === \"rg\" || toolName === \"ripgrep\" ? \"rg\" : toolName === \"grep\" ? \"grep\" : undefined;\n\t\t\tif (searchTool) {\n\t\t\t\tconst scope = parseShellSearchInvocationScope(searchTool, args, readsPipe);\n\t\t\t\tif (scope.targets.some((target) => target !== \"-\" && isProtectedCredentialPath(target, cwd, boundary))) {\n\t\t\t\t\treturn \"credential_path\";\n\t\t\t\t}\n\t\t\t\tconst hasSafeGlob =\n\t\t\t\t\tscope.positiveGlobs.length > 0 && scope.positiveGlobs.every((glob) => isCredentialSafeGlob(glob));\n\t\t\t\tconst hasOnlyExplicitFiles =\n\t\t\t\t\tscope.targets.length > 0 &&\n\t\t\t\t\tscope.targets.every((target) => target === \"-\" || isCredentialSafeExplicitFile(target, cwd));\n\t\t\t\tif (!scope.metaOnly && !scope.readsStdin && !hasSafeGlob && !hasOnlyExplicitFiles) return \"broad_search\";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfor (const token of args) {\n\t\t\t\tif (!token || token.startsWith(\"-\") || token === \".\") continue;\n\t\t\t\tif (isProtectedCredentialPath(token, cwd, boundary)) return \"credential_path\";\n\t\t\t}\n\t\t}\n\t\treturn undefined;\n\t};\n\n\tlet invocation: string[] = [];\n\tlet skipRedirectTarget = false;\n\tlet readsPipe = false;\n\tfor (const token of shellTokens) {\n\t\tif (token.kind === \"arg\") {\n\t\t\tif (skipRedirectTarget) skipRedirectTarget = false;\n\t\t\telse invocation.push(token.value);\n\t\t\tcontinue;\n\t\t}\n\t\tif (token.kind === \"redirect\") {\n\t\t\t// Descriptor duplication (`2>&1`) has no following path operand. Other\n\t\t\t// redirects do, and that target must never count as search scope.\n\t\t\tskipRedirectTarget = !/^\\d*[<>]&[\\d-]+$/u.test(token.value);\n\t\t\tcontinue;\n\t\t}\n\t\tconst risk = assessInvocation(invocation, readsPipe);\n\t\tif (risk) return risk;\n\t\tinvocation = [];\n\t\tskipRedirectTarget = false;\n\t\treadsPipe = token.kind === \"pipe\";\n\t}\n\treturn assessInvocation(invocation, readsPipe);\n}\n\nfunction jqFilterFromArgs(args: string[]): string | undefined {\n\tfor (let index = 0; index < args.length; index++) {\n\t\tconst token = args[index];\n\t\tif (token === \"--\") return args[index + 1];\n\t\tif (JQ_OPTIONS_WITH_TWO_OPERANDS.has(token)) {\n\t\t\tindex += 2;\n\t\t\tcontinue;\n\t\t}\n\t\tif (JQ_OPTIONS_WITH_ONE_OPERAND.has(token)) {\n\t\t\tindex++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (token.startsWith(\"-\")) continue;\n\t\treturn token;\n\t}\n\treturn undefined;\n}\n\nfunction jqFilterReadsProcessEnvironment(filter: string): boolean {\n\treturn /(?:^|[\\s|,(:;[])(?:env|\\$ENV)(?=$|[\\s|),.;[\\]}])/u.test(filter);\n}\n\nfunction isCredentialSafeGlob(glob: string): boolean {\n\tif (/(?:^|[\\\\/])?\\.env(?:\\.|\\*|$)/i.test(glob)) return false;\n\tconst filePattern = glob.replace(/\\\\/g, \"/\").split(\"/\").at(-1) ?? \"\";\n\tconst braceSuffixes = filePattern.match(/\\.\\{([A-Za-z0-9_-]+(?:,[A-Za-z0-9_-]+)+)\\}$/)?.[1];\n\tconst suffixes = braceSuffixes\n\t\t? braceSuffixes.split(\",\").map((suffix) => suffix.toLowerCase())\n\t\t: [filePattern.match(/\\.([A-Za-z0-9_-]+)$/)?.[1]?.toLowerCase()].filter(\n\t\t\t\t(suffix): suffix is string => suffix !== undefined,\n\t\t\t);\n\treturn suffixes.length > 0 && suffixes.every((suffix) => suffix !== \"env\");\n}\n\nfunction isExistingRegularFile(rawPath: string, cwd: string): boolean {\n\ttry {\n\t\treturn statSync(resolve(cwd, rawPath)).isFile();\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction isCredentialSafeExplicitFile(rawPath: string, cwd: string): boolean {\n\ttry {\n\t\treturn statSync(resolve(cwd, rawPath)).isFile();\n\t} catch {\n\t\treturn isCredentialSafeGlob(rawPath);\n\t}\n}\n\nfunction pythonInspectsCredentialPath(code: string, cwd: string, boundary?: CredentialExposureBoundary): boolean {\n\tif (!PYTHON_INSPECTION_RE.test(code)) return false;\n\tQUOTED_TEXT_RE.lastIndex = 0;\n\tfor (const match of code.matchAll(QUOTED_TEXT_RE)) {\n\t\tconst candidate = match[2]?.replace(/\\\\([\\\\\"'])/g, \"$1\");\n\t\tif (candidate && isProtectedCredentialPath(candidate, cwd, boundary)) return true;\n\t}\n\treturn false;\n}\n\nfunction directPathFromArgs(args: unknown): string | undefined {\n\tif (!isRecord(args)) return undefined;\n\treturn typeof args.path === \"string\" ? args.path : undefined;\n}\n\n/** Stable model-facing refusal for direct inspection/mutation of credential material. */\nexport function credentialToolBlockReason(\n\ttoolName: string,\n\targs: unknown,\n\tcwd: string,\n\tboundary?: CredentialExposureBoundary,\n): string | undefined {\n\tif (toolName === \"secret_store\" || !isRecord(args)) return undefined;\n\tif (DIRECT_PATH_TOOLS.has(toolName)) {\n\t\tconst path = directPathFromArgs(args);\n\t\tif (path && isProtectedCredentialPath(path, cwd, boundary)) {\n\t\t\treturn \"Credential file access is model-blind. Use secret_store migrate with this path, or activate an existing profile, without inspecting credential data.\";\n\t\t}\n\t}\n\tif (toolName === \"grep\") {\n\t\tconst path = typeof args.path === \"string\" ? args.path : undefined;\n\t\tconst glob = typeof args.glob === \"string\" ? args.glob : undefined;\n\t\tif (\n\t\t\t(path && isProtectedCredentialPath(path, cwd, boundary)) ||\n\t\t\t(glob && /(?:^|[\\\\/])?\\.env(?:\\.|\\*|$)/i.test(glob))\n\t\t) {\n\t\t\treturn \"Credential dotenv files are model-blind. Use secret_store discover instead of searching their contents.\";\n\t\t}\n\t\tif (!(path && isExistingRegularFile(path, cwd)) && !(glob && isCredentialSafeGlob(glob))) {\n\t\t\treturn \"Credential-safe grep requires one explicit regular file or a narrow non-dotenv file glob (for example *.ts). Refine the search instead of scanning a directory without a file filter.\";\n\t\t}\n\t}\n\tif (toolName === \"find\") {\n\t\tconst path = typeof args.path === \"string\" ? args.path : undefined;\n\t\tconst pattern = typeof args.pattern === \"string\" ? args.pattern : undefined;\n\t\tif (\n\t\t\t(path && isProtectedCredentialPath(path, cwd, boundary)) ||\n\t\t\t(pattern && /(?:^|[\\\\/])?\\.env(?:\\.|\\*|$)/i.test(pattern))\n\t\t) {\n\t\t\treturn \"Credential dotenv files are model-blind. Use secret_store discover instead of searching their contents.\";\n\t\t}\n\t}\n\tif (toolName === \"bash\" || toolName === \"powershell\" || toolName === \"run_process\") {\n\t\tconst command = typeof args.command === \"string\" ? args.command : \"\";\n\t\tconst shellRisk = shellCredentialRisk(command, cwd, boundary);\n\t\tif (shellRisk === \"broad_search\") {\n\t\t\treturn \"Credential-safe shell search requires a narrow non-dotenv file glob (for example -g '*.ts') or one explicit regular file. Refine the rg/grep command before retrying.\";\n\t\t}\n\t\tif (shellRisk === \"process_environment\") {\n\t\t\treturn \"Direct process-environment projection is blocked because it can expose credentials. Use secret_store discover to list eligible source names without exposing values.\";\n\t\t}\n\t\tif (SHELL_SECRET_READ_RE.test(command) || shellRisk === \"credential_path\") {\n\t\t\treturn \"Direct shell inspection of credential files is blocked. Use secret_store migrate with the file path, then run the credential-consuming command normally.\";\n\t\t}\n\t}\n\tif (toolName === \"python\") {\n\t\tconst code = typeof args.code === \"string\" ? args.code : \"\";\n\t\tconst scriptPath = typeof args.scriptPath === \"string\" ? args.scriptPath : undefined;\n\t\tif (\n\t\t\tPYTHON_SECRET_READ_RE.test(code) ||\n\t\t\tpythonInspectsCredentialPath(code, cwd, boundary) ||\n\t\t\t(scriptPath !== undefined && isProtectedCredentialPath(scriptPath, cwd, boundary))\n\t\t) {\n\t\t\treturn \"Direct Python inspection of credential files is blocked. Use secret_store migrate with the file path, then run the credential-consuming program normally.\";\n\t\t}\n\t}\n\treturn undefined;\n}\n\nfunction redactResult<T>(result: AgentToolResult<T>, boundary?: CredentialExposureBoundary): AgentToolResult<T> {\n\tconst redact = (text: string) => (boundary ? boundary.redactSensitiveText(text) : redactKnownSecrets(text));\n\tconst budget = { nodes: 0 };\n\treturn {\n\t\t...result,\n\t\tcontent: result.content.map((block) => (block.type === \"text\" ? { ...block, text: redact(block.text) } : block)),\n\t\tdetails: redactStructuredDetails(result.details, redact, budget) as T,\n\t};\n}\n\nfunction redactStructuredDetails(\n\tvalue: unknown,\n\tredact: (text: string) => string,\n\tbudget: { nodes: number },\n\tdepth = 0,\n): unknown {\n\tif (typeof value === \"string\") return redact(value);\n\tif (value === null || typeof value !== \"object\") return value;\n\tbudget.nodes++;\n\tif (depth >= MAX_REDACTED_DETAIL_DEPTH || budget.nodes > MAX_REDACTED_DETAIL_NODES) {\n\t\treturn \"[DETAIL OMITTED AT CREDENTIAL BOUNDARY]\";\n\t}\n\tif (Array.isArray(value)) {\n\t\treturn value.map((entry) => redactStructuredDetails(entry, redact, budget, depth + 1));\n\t}\n\tlet prototype: object | null;\n\ttry {\n\t\tprototype = Object.getPrototypeOf(value);\n\t} catch {\n\t\treturn \"[UNREADABLE DETAIL OMITTED AT CREDENTIAL BOUNDARY]\";\n\t}\n\tif (prototype !== Object.prototype && prototype !== null) return value;\n\treturn Object.fromEntries(\n\t\tObject.entries(value).map(([key, entry]) => [key, redactStructuredDetails(entry, redact, budget, depth + 1)]),\n\t);\n}\n\n/** Apply the same path refusal and output redaction to foreground, extension, scout, and lane tools. */\nexport function wrapToolWithCredentialExposureGuard<TParameters extends TSchema, TDetails>(\n\ttool: AgentTool<TParameters, TDetails>,\n\tcwd: string,\n\tboundary?: CredentialExposureBoundary,\n): AgentTool<TParameters, TDetails> {\n\treturn {\n\t\t...tool,\n\t\tasync execute(toolCallId, params, signal, onUpdate) {\n\t\t\tconst blockReason = credentialToolBlockReason(tool.name, params, cwd, boundary);\n\t\t\tif (blockReason) {\n\t\t\t\tthrow new AgentToolExecutionError(\n\t\t\t\t\tblockReason,\n\t\t\t\t\t\"credential_access_blocked\",\n\t\t\t\t\t\"credential-access-blocked\",\n\t\t\t\t\t\"tool_failure\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst safeUpdate = onUpdate\n\t\t\t\t? (partial: AgentToolResult<TDetails>) => {\n\t\t\t\t\t\tonUpdate(redactResult(partial, boundary));\n\t\t\t\t\t}\n\t\t\t\t: undefined;\n\t\t\ttry {\n\t\t\t\treturn redactResult(await tool.execute(toolCallId, params, signal, safeUpdate), boundary);\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof Error) {\n\t\t\t\t\tconst message = boundary\n\t\t\t\t\t\t? boundary.redactSensitiveText(error.message)\n\t\t\t\t\t\t: redactKnownSecrets(error.message);\n\t\t\t\t\tif (error instanceof AgentToolExecutionError) {\n\t\t\t\t\t\tthrow new AgentToolExecutionError(message, error.failureCode, error.outputSignature, error.errorKind);\n\t\t\t\t\t}\n\t\t\t\t\tthrow new Error(message);\n\t\t\t\t}\n\t\t\t\tthrow new Error(\"Credential-safe tool execution failed without retaining raw error output.\");\n\t\t\t}\n\t\t},\n\t};\n}\n"]}
|
|
@@ -656,6 +656,7 @@ export declare class SettingsManager {
|
|
|
656
656
|
getCompactionReserveTokens(): number;
|
|
657
657
|
getCompactionKeepRecentTokens(): number;
|
|
658
658
|
getCompactionTriggerPercent(): number;
|
|
659
|
+
hasExplicitCompactionTriggerPercent(): boolean;
|
|
659
660
|
/**
|
|
660
661
|
* Skill curator (#32). Auto-archive of stale reflection-promoted skills is ON by default (restorable,
|
|
661
662
|
* announced, promoted-only). Set `autoArchive: false` to make it propose-only (`/curate`).
|