@caupulican/pi-adaptative 0.93.16 → 0.93.17

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 CHANGED
@@ -1,3 +1,11 @@
1
+ ## [0.93.17] - 2026-08-21
2
+
3
+ ### Fixed
4
+
5
+ - Learned a bounded next-attempt first-progress timeout from censored stream stalls, scoped it to comparable prompt sizes, and cleared it after a successful stream instead of repeating identical Grok timeout windows.
6
+ - Allowed credential-safe source brace globs and explicit source filenames even when one path is stale, while retaining directory and dotenv protections.
7
+ - Bounded retained failed jscpd evidence by age, count, and total bytes while protecting concurrent scans and explicit report directories.
8
+
1
9
  ## [0.93.16] - 2026-08-20
2
10
 
3
11
  ### Fixed
@@ -1,4 +1,4 @@
1
- import type { StreamIdleOptions } from "@caupulican/pi-agent-core/reliability";
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,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAC/E,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,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,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,CAa5E;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAMzE;AAED,wBAAgB,sBAAsB,CACrC,OAAO,EAAE,gBAAgB,GAAG,SAAS,EACrC,MAAM,EAAE,eAAe,EACvB,EAAE,GAAE,MAA8C,GAChD,gBAAgB,GAAG,SAAS,CAyB9B;AAED,wBAAgB,gCAAgC,CAAC,KAAK,EAAE,uBAAuB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAgC3G;AAED,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAEpE;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,8BAA8B,GAAG,QAAQ,CA4D3G"}
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;AAyHD,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"}
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 === "-" || isExistingRegularFile(target, cwd));
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 suffix = filePattern.match(/\.([A-Za-z0-9_-]+)$/)?.[1]?.toLowerCase();
166
- return suffix !== undefined && suffix !== "env";
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":"AAAA,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AACzE,OAAO,EAAkB,uBAAuB,EAAwB,MAAM,2BAA2B,CAAC;AAE1G,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,+BAA+B,EAA+B,MAAM,kCAAkC,CAAC;AAChH,OAAO,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AAExE,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;AACnE,MAAM,yBAAyB,GAAG,IAAI,GAAG,CAAC;IACzC,KAAK;IACL,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,KAAK;IACL,KAAK;IACL,MAAM;IACN,IAAI;IACJ,SAAS;IACT,MAAM;IACN,aAAa;IACb,eAAe;IACf,QAAQ;CACR,CAAC,CAAC;AACH,MAAM,oBAAoB,GACzB,0IAA0I,CAAC;AAC5I,MAAM,qBAAqB,GAAG,uFAAuF,CAAC;AACtH,MAAM,oBAAoB,GAAG,mCAAmC,CAAC;AACjE,MAAM,cAAc,GAAG,qCAAqC,CAAC;AAC7D,MAAM,yBAAyB,GAAG,CAAC,CAAC;AACpC,MAAM,yBAAyB,GAAG,MAAM,CAAC;AACzC,MAAM,2BAA2B,GAAG,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC;AAChE,MAAM,4BAA4B,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,CAAC,CAAC,CAAC;AAQjG,SAAS,QAAQ,CAAC,KAAc;IAC/B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC7E,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY,EAAE,MAAc;IAC7C,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACxC,OAAO,QAAQ,KAAK,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,KAAK,GAAG,EAAE,CAAC,IAAI,QAAQ,KAAK,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC;AAC5G,CAAC;AAED,MAAM,UAAU,yBAAyB,CACxC,OAAe,EACf,GAAW,EACX,QAAqC;IAErC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACvC,MAAM,UAAU,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC9B,IAAI,CAAC;QACJ,MAAM,SAAS,GAAG,YAAY,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAChD,IAAI,SAAS,KAAK,QAAQ;YAAE,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACxD,CAAC;IAAC,MAAM,CAAC;QACR,sEAAsE;IACvE,CAAC;IACD,MAAM,cAAc,GAAG,CAAC,QAAQ,EAAE,cAAc,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACrF,MAAM,oBAAoB,GAAG,CAAC,QAAQ,EAAE,oBAAoB,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACjG,KAAK,MAAM,IAAI,IAAI,CAAC,GAAG,cAAc,CAAC,EAAE,CAAC;QACxC,IAAI,CAAC;YACJ,cAAc,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;QAChD,CAAC;QAAC,MAAM,CAAC;YACR,mEAAmE;QACpE,CAAC;IACF,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,CAAC,GAAG,oBAAoB,CAAC,EAAE,CAAC;QAC9C,IAAI,CAAC;YACJ,oBAAoB,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;QACtD,CAAC;QAAC,MAAM,CAAC;YACR,wEAAwE;QACzE,CAAC;IACF,CAAC;IACD,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE;QACpC,IAAI,cAAc,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,oBAAoB,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC;YAC1G,OAAO,IAAI,CAAC;QACb,CAAC;QACD,MAAM,QAAQ,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;QACrC,OAAO,QAAQ,KAAK,MAAM,IAAI,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACzF,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,mBAAmB,CAC3B,OAAe,EACf,GAAW,EACX,QAAqC;IAErC,MAAM,WAAW,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAClD,IAAI,CAAC,WAAW;QAAE,OAAO,mBAAmB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC;IAExF,MAAM,gBAAgB,GAAG,CACxB,UAAoB,EACpB,SAAkB,EACuD,EAAE;QAC3E,KAAK,IAAI,YAAY,GAAG,CAAC,EAAE,YAAY,GAAG,UAAU,CAAC,MAAM,EAAE,YAAY,EAAE,EAAE,CAAC;YAC7E,MAAM,QAAQ,GAAG,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;YAC9G,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;gBACvB,MAAM,MAAM,GAAG,gBAAgB,CAAC,UAAU,CAAC,KAAK,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC,CAAC;gBACpE,IAAI,MAAM,IAAI,+BAA+B,CAAC,MAAM,CAAC;oBAAE,OAAO,qBAAqB,CAAC;YACrF,CAAC;YACD,IAAI,CAAC,yBAAyB,CAAC,GAAG,CAAC,QAAQ,CAAC;gBAAE,SAAS;YACvD,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC;YAChD,MAAM,UAAU,GACf,QAAQ,KAAK,IAAI,IAAI,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;YAC/F,IAAI,UAAU,EAAE,CAAC;gBAChB,MAAM,KAAK,GAAG,+BAA+B,CAAC,UAAU,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;gBAC3E,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,KAAK,GAAG,IAAI,yBAAyB,CAAC,MAAM,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC;oBACxG,OAAO,iBAAiB,CAAC;gBAC1B,CAAC;gBACD,MAAM,WAAW,GAChB,KAAK,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC;gBACnG,MAAM,oBAAoB,GACzB,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;oBACxB,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,KAAK,GAAG,IAAI,4BAA4B,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;gBAC9F,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,CAAC,KAAK,CAAC,UAAU,IAAI,CAAC,WAAW,IAAI,CAAC,oBAAoB;oBAAE,OAAO,cAAc,CAAC;gBACzG,SAAS;YACV,CAAC;YACD,KAAK,MAAM,KAAK,IAAI,IAAI,EAAE,CAAC;gBAC1B,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,KAAK,GAAG;oBAAE,SAAS;gBAC/D,IAAI,yBAAyB,CAAC,KAAK,EAAE,GAAG,EAAE,QAAQ,CAAC;oBAAE,OAAO,iBAAiB,CAAC;YAC/E,CAAC;QACF,CAAC;QACD,OAAO,SAAS,CAAC;IAClB,CAAC,CAAC;IAEF,IAAI,UAAU,GAAa,EAAE,CAAC;IAC9B,IAAI,kBAAkB,GAAG,KAAK,CAAC;IAC/B,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,KAAK,MAAM,KAAK,IAAI,WAAW,EAAE,CAAC;QACjC,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;YAC1B,IAAI,kBAAkB;gBAAE,kBAAkB,GAAG,KAAK,CAAC;;gBAC9C,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAClC,SAAS;QACV,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YAC/B,uEAAuE;YACvE,kEAAkE;YAClE,kBAAkB,GAAG,CAAC,mBAAmB,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAC5D,SAAS;QACV,CAAC;QACD,MAAM,IAAI,GAAG,gBAAgB,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;QACrD,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC;QACtB,UAAU,GAAG,EAAE,CAAC;QAChB,kBAAkB,GAAG,KAAK,CAAC;QAC3B,SAAS,GAAG,KAAK,CAAC,IAAI,KAAK,MAAM,CAAC;IACnC,CAAC;IACD,OAAO,gBAAgB,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;AAChD,CAAC;AAED,SAAS,gBAAgB,CAAC,IAAc;IACvC,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QAClD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1B,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QAC3C,IAAI,4BAA4B,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAC7C,KAAK,IAAI,CAAC,CAAC;YACX,SAAS;QACV,CAAC;QACD,IAAI,2BAA2B,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5C,KAAK,EAAE,CAAC;YACR,SAAS;QACV,CAAC;QACD,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QACpC,OAAO,KAAK,CAAC;IACd,CAAC;IACD,OAAO,SAAS,CAAC;AAClB,CAAC;AAED,SAAS,+BAA+B,CAAC,MAAc;IACtD,OAAO,mDAAmD,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AACzE,CAAC;AAED,SAAS,oBAAoB,CAAC,IAAY;IACzC,IAAI,+BAA+B,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC7D,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACrE,MAAM,aAAa,GAAG,WAAW,CAAC,KAAK,CAAC,6CAA6C,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC5F,MAAM,QAAQ,GAAG,aAAa;QAC7B,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;QAChE,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,qBAAqB,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,MAAM,CACrE,CAAC,MAAM,EAAoB,EAAE,CAAC,MAAM,KAAK,SAAS,CAClD,CAAC;IACJ,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC;AAC5E,CAAC;AAED,SAAS,qBAAqB,CAAC,OAAe,EAAE,GAAW;IAC1D,IAAI,CAAC;QACJ,OAAO,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IACjD,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAC;IACd,CAAC;AACF,CAAC;AAED,SAAS,4BAA4B,CAAC,OAAe,EAAE,GAAW;IACjE,IAAI,CAAC;QACJ,OAAO,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IACjD,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,oBAAoB,CAAC,OAAO,CAAC,CAAC;IACtC,CAAC;AACF,CAAC;AAED,SAAS,4BAA4B,CAAC,IAAY,EAAE,GAAW,EAAE,QAAqC;IACrG,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACnD,cAAc,CAAC,SAAS,GAAG,CAAC,CAAC;IAC7B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;QACnD,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;QACzD,IAAI,SAAS,IAAI,yBAAyB,CAAC,SAAS,EAAE,GAAG,EAAE,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAC;IACnF,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED,SAAS,kBAAkB,CAAC,IAAa;IACxC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IACtC,OAAO,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;AAC9D,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,yBAAyB,CACxC,QAAgB,EAChB,IAAa,EACb,GAAW,EACX,QAAqC;IAErC,IAAI,QAAQ,KAAK,cAAc,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IACrE,IAAI,iBAAiB,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;QACrC,MAAM,IAAI,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;QACtC,IAAI,IAAI,IAAI,yBAAyB,CAAC,IAAI,EAAE,GAAG,EAAE,QAAQ,CAAC,EAAE,CAAC;YAC5D,OAAO,sJAAsJ,CAAC;QAC/J,CAAC;IACF,CAAC;IACD,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;QACnE,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;QACnE,IACC,CAAC,IAAI,IAAI,yBAAyB,CAAC,IAAI,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;YACxD,CAAC,IAAI,IAAI,+BAA+B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EACnD,CAAC;YACF,OAAO,yGAAyG,CAAC;QAClH,CAAC;QACD,IAAI,CAAC,CAAC,IAAI,IAAI,qBAAqB,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,oBAAoB,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YAC1F,OAAO,uLAAuL,CAAC;QAChM,CAAC;IACF,CAAC;IACD,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;QACnE,MAAM,OAAO,GAAG,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;QAC5E,IACC,CAAC,IAAI,IAAI,yBAAyB,CAAC,IAAI,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;YACxD,CAAC,OAAO,IAAI,+BAA+B,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EACzD,CAAC;YACF,OAAO,yGAAyG,CAAC;QAClH,CAAC;IACF,CAAC;IACD,IAAI,QAAQ,KAAK,MAAM,IAAI,QAAQ,KAAK,YAAY,IAAI,QAAQ,KAAK,aAAa,EAAE,CAAC;QACpF,MAAM,OAAO,GAAG,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QACrE,MAAM,SAAS,GAAG,mBAAmB,CAAC,OAAO,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;QAC9D,IAAI,SAAS,KAAK,cAAc,EAAE,CAAC;YAClC,OAAO,uKAAuK,CAAC;QAChL,CAAC;QACD,IAAI,SAAS,KAAK,qBAAqB,EAAE,CAAC;YACzC,OAAO,sKAAsK,CAAC;QAC/K,CAAC;QACD,IAAI,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,SAAS,KAAK,iBAAiB,EAAE,CAAC;YAC3E,OAAO,0JAA0J,CAAC;QACnK,CAAC;IACF,CAAC;IACD,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5D,MAAM,UAAU,GAAG,OAAO,IAAI,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;QACrF,IACC,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC;YAChC,4BAA4B,CAAC,IAAI,EAAE,GAAG,EAAE,QAAQ,CAAC;YACjD,CAAC,UAAU,KAAK,SAAS,IAAI,yBAAyB,CAAC,UAAU,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC,EACjF,CAAC;YACF,OAAO,2JAA2J,CAAC;QACpK,CAAC;IACF,CAAC;IACD,OAAO,SAAS,CAAC;AAClB,CAAC;AAED,SAAS,YAAY,CAAI,MAA0B,EAAE,QAAqC;IACzF,MAAM,MAAM,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC;IAC5G,MAAM,MAAM,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;IAC5B,OAAO;QACN,GAAG,MAAM;QACT,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAChH,OAAO,EAAE,uBAAuB,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAM;KACrE,CAAC;AACH,CAAC;AAED,SAAS,uBAAuB,CAC/B,KAAc,EACd,MAAgC,EAChC,MAAyB,EACzB,KAAK,GAAG,CAAC;IAET,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IACpD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,CAAC,KAAK,EAAE,CAAC;IACf,IAAI,KAAK,IAAI,yBAAyB,IAAI,MAAM,CAAC,KAAK,GAAG,yBAAyB,EAAE,CAAC;QACpF,OAAO,yCAAyC,CAAC;IAClD,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,uBAAuB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;IACxF,CAAC;IACD,IAAI,SAAwB,CAAC;IAC7B,IAAI,CAAC;QACJ,SAAS,GAAG,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,oDAAoD,CAAC;IAC7D,CAAC;IACD,IAAI,SAAS,KAAK,MAAM,CAAC,SAAS,IAAI,SAAS,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IACvE,OAAO,MAAM,CAAC,WAAW,CACxB,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,uBAAuB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAC7G,CAAC;AACH,CAAC;AAED,wGAAwG;AACxG,MAAM,UAAU,mCAAmC,CAClD,IAAsC,EACtC,GAAW,EACX,QAAqC;IAErC,OAAO;QACN,GAAG,IAAI;QACP,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ;YACjD,MAAM,WAAW,GAAG,yBAAyB,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;YAChF,IAAI,WAAW,EAAE,CAAC;gBACjB,MAAM,IAAI,uBAAuB,CAChC,WAAW,EACX,2BAA2B,EAC3B,2BAA2B,EAC3B,cAAc,CACd,CAAC;YACH,CAAC;YACD,MAAM,UAAU,GAAG,QAAQ;gBAC1B,CAAC,CAAC,CAAC,OAAkC,EAAE,EAAE;oBACvC,QAAQ,CAAC,YAAY,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;gBAC3C,CAAC;gBACF,CAAC,CAAC,SAAS,CAAC;YACb,IAAI,CAAC;gBACJ,OAAO,YAAY,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,QAAQ,CAAC,CAAC;YAC3F,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;oBAC5B,MAAM,OAAO,GAAG,QAAQ;wBACvB,CAAC,CAAC,QAAQ,CAAC,mBAAmB,CAAC,KAAK,CAAC,OAAO,CAAC;wBAC7C,CAAC,CAAC,kBAAkB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;oBACrC,IAAI,KAAK,YAAY,uBAAuB,EAAE,CAAC;wBAC9C,MAAM,IAAI,uBAAuB,CAAC,OAAO,EAAE,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,eAAe,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;oBACvG,CAAC;oBACD,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;gBAC1B,CAAC;gBACD,MAAM,IAAI,KAAK,CAAC,2EAA2E,CAAC,CAAC;YAC9F,CAAC;QACF,CAAC;KACD,CAAC;AACH,CAAC","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"]}
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider",
3
- "version": "0.93.16",
3
+ "version": "0.93.17",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-custom-provider",
9
- "version": "0.93.16",
9
+ "version": "0.93.17",
10
10
  "dependencies": {
11
11
  "@anthropic-ai/sdk": "^0.52.0"
12
12
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider-anthropic",
3
3
  "private": true,
4
- "version": "0.93.16",
4
+ "version": "0.93.17",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider-gitlab-duo",
3
3
  "private": true,
4
- "version": "0.93.16",
4
+ "version": "0.93.17",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-sandbox",
3
- "version": "0.93.16",
3
+ "version": "0.93.17",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-sandbox",
9
- "version": "0.93.16",
9
+ "version": "0.93.17",
10
10
  "dependencies": {
11
11
  "@anthropic-ai/sandbox-runtime": "^0.0.26"
12
12
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-sandbox",
3
3
  "private": true,
4
- "version": "0.93.16",
4
+ "version": "0.93.17",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-with-deps",
3
- "version": "0.93.16",
3
+ "version": "0.93.17",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-with-deps",
9
- "version": "0.93.16",
9
+ "version": "0.93.17",
10
10
  "dependencies": {
11
11
  "ms": "^2.1.3"
12
12
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-with-deps",
3
3
  "private": true,
4
- "version": "0.93.16",
4
+ "version": "0.93.17",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@caupulican/pi-adaptative",
3
- "version": "0.93.16",
3
+ "version": "0.93.17",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@caupulican/pi-adaptative",
9
- "version": "0.93.16",
9
+ "version": "0.93.17",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
- "@caupulican/pi-agent-core": "^0.93.16",
13
- "@caupulican/pi-ai": "^0.93.16",
14
- "@caupulican/pi-tui": "^0.93.16",
12
+ "@caupulican/pi-agent-core": "^0.93.17",
13
+ "@caupulican/pi-ai": "^0.93.17",
14
+ "@caupulican/pi-tui": "^0.93.17",
15
15
  "@silvia-odwyer/photon-node": "0.3.4",
16
16
  "chalk": "5.6.2",
17
17
  "cross-spawn": "7.0.6",
@@ -506,11 +506,11 @@
506
506
  }
507
507
  },
508
508
  "node_modules/@caupulican/pi-agent-core": {
509
- "version": "0.93.16",
510
- "resolved": "https://registry.npmjs.org/@caupulican/pi-agent-core/-/pi-agent-core-0.93.16.tgz",
509
+ "version": "0.93.17",
510
+ "resolved": "https://registry.npmjs.org/@caupulican/pi-agent-core/-/pi-agent-core-0.93.17.tgz",
511
511
  "license": "MIT",
512
512
  "dependencies": {
513
- "@caupulican/pi-ai": "^0.93.16",
513
+ "@caupulican/pi-ai": "^0.93.17",
514
514
  "ignore": "7.0.5",
515
515
  "typebox": "1.1.38",
516
516
  "yaml": "2.9.0"
@@ -520,8 +520,8 @@
520
520
  }
521
521
  },
522
522
  "node_modules/@caupulican/pi-ai": {
523
- "version": "0.93.16",
524
- "resolved": "https://registry.npmjs.org/@caupulican/pi-ai/-/pi-ai-0.93.16.tgz",
523
+ "version": "0.93.17",
524
+ "resolved": "https://registry.npmjs.org/@caupulican/pi-ai/-/pi-ai-0.93.17.tgz",
525
525
  "license": "MIT",
526
526
  "dependencies": {
527
527
  "@anthropic-ai/sdk": "0.117.1",
@@ -546,8 +546,8 @@
546
546
  }
547
547
  },
548
548
  "node_modules/@caupulican/pi-tui": {
549
- "version": "0.93.16",
550
- "resolved": "https://registry.npmjs.org/@caupulican/pi-tui/-/pi-tui-0.93.16.tgz",
549
+ "version": "0.93.17",
550
+ "resolved": "https://registry.npmjs.org/@caupulican/pi-tui/-/pi-tui-0.93.17.tgz",
551
551
  "license": "MIT",
552
552
  "dependencies": {
553
553
  "get-east-asian-width": "1.6.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caupulican/pi-adaptative",
3
- "version": "0.93.16",
3
+ "version": "0.93.17",
4
4
  "description": "Adaptive fork of Pi coding agent for self-evolving agent harness experiments",
5
5
  "type": "module",
6
6
  "piConfig": {
@@ -89,9 +89,9 @@
89
89
  "postinstall": "node scripts/postinstall-python-runtime.mjs"
90
90
  },
91
91
  "dependencies": {
92
- "@caupulican/pi-agent-core": "^0.93.16",
93
- "@caupulican/pi-ai": "^0.93.16",
94
- "@caupulican/pi-tui": "^0.93.16",
92
+ "@caupulican/pi-agent-core": "^0.93.17",
93
+ "@caupulican/pi-ai": "^0.93.17",
94
+ "@caupulican/pi-tui": "^0.93.17",
95
95
  "@silvia-odwyer/photon-node": "0.3.4",
96
96
  "chalk": "5.6.2",
97
97
  "cross-spawn": "7.0.6",