deepline 0.3.65 → 0.3.67
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundling-sources/sdk/src/client.ts +2 -0
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/sdk/src/types.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +89 -2
- package/dist/bundling-sources/shared_libs/play-runtime/log-provenance.ts +80 -3
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +3 -0
- package/dist/cli/index.js +278 -155
- package/dist/cli/index.mjs +190 -67
- package/dist/index.d.mts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
|
@@ -748,6 +748,8 @@ export type MonitorListEntry = {
|
|
|
748
748
|
monitor_key?: string;
|
|
749
749
|
status?: string;
|
|
750
750
|
tool?: string;
|
|
751
|
+
/** Generic provider-authored event category for this deployed monitor. */
|
|
752
|
+
type?: string;
|
|
751
753
|
name?: string;
|
|
752
754
|
configured?: boolean;
|
|
753
755
|
active?: boolean;
|
|
@@ -199,7 +199,7 @@ export const SDK_RELEASE = {
|
|
|
199
199
|
// available at toolResponse.rawV2 while toolResponse.raw and all declared
|
|
200
200
|
// getters keep their established compatibility behavior.
|
|
201
201
|
// 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
|
|
202
|
-
version: '0.3.
|
|
202
|
+
version: '0.3.67',
|
|
203
203
|
updateSummary:
|
|
204
204
|
'Automatic CLI updates are now enabled by default. To opt out, run `deepline settings autoupdate off`; use `deepline settings autoupdate on` to re-enable updates or `deepline settings autoupdate pin <version>` to hold an exact release. This release also adds raw-v2 tool responses at toolResponse.rawV2 while preserving existing toolResponse.raw and declared getters.',
|
|
205
205
|
packageCapabilities: {
|
|
@@ -915,6 +915,8 @@ export interface PlayRunListItem {
|
|
|
915
915
|
startTime?: string | null;
|
|
916
916
|
/** Unix epoch milliseconds when the run started, returned by normalized V2 run summaries. */
|
|
917
917
|
startedAt?: number | string | null;
|
|
918
|
+
/** Unix epoch milliseconds when the run was created. */
|
|
919
|
+
createdAt?: number | string | null;
|
|
918
920
|
/** ISO 8601 timestamp when the run finished. */
|
|
919
921
|
closeTime?: string | null;
|
|
920
922
|
/** Unix epoch milliseconds when the run finished, returned by normalized V2 run summaries. */
|
|
@@ -194,6 +194,10 @@ import {
|
|
|
194
194
|
type PlayAuthoringRunScope,
|
|
195
195
|
type PlayAuthoringRuntimeContext,
|
|
196
196
|
} from '../plays/authoring-contract';
|
|
197
|
+
import {
|
|
198
|
+
formatCtxFetchHttpFailureDiagnostic,
|
|
199
|
+
tagLogProvenance,
|
|
200
|
+
} from './log-provenance';
|
|
197
201
|
import {
|
|
198
202
|
DURABLE_RECEIPT_WAIT_DELAY_MS,
|
|
199
203
|
DURABLE_RECEIPT_WAIT_MAX_ATTEMPTS,
|
|
@@ -451,6 +455,10 @@ const DEFAULT_TOOL_RUNTIME_TIMEOUT_MS = 15 * 60 * 1000 + 30_000;
|
|
|
451
455
|
const FETCH_TRANSPORT_MAX_ATTEMPTS =
|
|
452
456
|
RUNTIME_RELIABILITY_POLICY.egress.fetchMaxAttempts;
|
|
453
457
|
const FETCH_TRANSPORT_RETRY_DELAY_MS = 100;
|
|
458
|
+
// Diagnostic logs are retained in the context until terminalization. Keep a
|
|
459
|
+
// representative, deduplicated set so row-heavy continued failures cannot turn
|
|
460
|
+
// customer-safe observability into unbounded runner memory or log traffic.
|
|
461
|
+
const MAX_CTX_FETCH_HTTP_FAILURE_DIAGNOSTICS = 16;
|
|
454
462
|
const CTX_FETCH_HEADERS_TIMEOUT_MS =
|
|
455
463
|
RUNTIME_RELIABILITY_POLICY.egress.fetchHeadersTimeoutMs;
|
|
456
464
|
const CTX_FETCH_BODY_TIMEOUT_MS =
|
|
@@ -2087,6 +2095,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
2087
2095
|
#options: ContextOptions;
|
|
2088
2096
|
private readonly executionScope: RunExecutionScope;
|
|
2089
2097
|
private logBuffer: string[] = [];
|
|
2098
|
+
private readonly ctxFetchHttpFailureDiagnosticIdentities = new Set<string>();
|
|
2090
2099
|
private checkpoint: PlayCheckpoint;
|
|
2091
2100
|
private readonly durableCallCacheEpochMs: number;
|
|
2092
2101
|
/**
|
|
@@ -10160,6 +10169,58 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
10160
10169
|
if (this.#options.verbose) console.log(line);
|
|
10161
10170
|
}
|
|
10162
10171
|
|
|
10172
|
+
/**
|
|
10173
|
+
* Emit a runtime-authored diagnostic through the same durable log path as
|
|
10174
|
+
* ctx.log without changing the customer-authored log wire format.
|
|
10175
|
+
*/
|
|
10176
|
+
private runtimeDiagnosticLog(message: string): void {
|
|
10177
|
+
assertNoSecretTaint(message, 'runtime diagnostic log');
|
|
10178
|
+
const line = tagLogProvenance(
|
|
10179
|
+
'diagnostic',
|
|
10180
|
+
`[${new Date().toISOString()}] ${this.secretRedactor.redactRegisteredSecrets(message)}`,
|
|
10181
|
+
);
|
|
10182
|
+
this.logBuffer.push(line);
|
|
10183
|
+
this.#options.onLog?.(line);
|
|
10184
|
+
if (this.#options.verbose) console.log(line);
|
|
10185
|
+
}
|
|
10186
|
+
|
|
10187
|
+
/** A URL safe to persist in a customer-visible run log (origin only). */
|
|
10188
|
+
private ctxFetchDiagnosticUrl(url: string): string {
|
|
10189
|
+
try {
|
|
10190
|
+
const parsed = new URL(url);
|
|
10191
|
+
return parsed.origin;
|
|
10192
|
+
} catch {
|
|
10193
|
+
// ctx.fetch already parses its URL before this helper is reachable.
|
|
10194
|
+
return '[invalid-url]';
|
|
10195
|
+
}
|
|
10196
|
+
}
|
|
10197
|
+
|
|
10198
|
+
private logCtxFetchHttpFailure(input: {
|
|
10199
|
+
key: string;
|
|
10200
|
+
method: string;
|
|
10201
|
+
url: string;
|
|
10202
|
+
httpStatus: number;
|
|
10203
|
+
}): void {
|
|
10204
|
+
const url = this.ctxFetchDiagnosticUrl(input.url);
|
|
10205
|
+
const identity = `${input.key}\u0000${input.method}\u0000${url}\u0000${input.httpStatus}`;
|
|
10206
|
+
if (
|
|
10207
|
+
this.ctxFetchHttpFailureDiagnosticIdentities.has(identity) ||
|
|
10208
|
+
this.ctxFetchHttpFailureDiagnosticIdentities.size >=
|
|
10209
|
+
MAX_CTX_FETCH_HTTP_FAILURE_DIAGNOSTICS
|
|
10210
|
+
) {
|
|
10211
|
+
return;
|
|
10212
|
+
}
|
|
10213
|
+
this.ctxFetchHttpFailureDiagnosticIdentities.add(identity);
|
|
10214
|
+
this.runtimeDiagnosticLog(
|
|
10215
|
+
formatCtxFetchHttpFailureDiagnostic({
|
|
10216
|
+
key: input.key,
|
|
10217
|
+
method: input.method,
|
|
10218
|
+
url,
|
|
10219
|
+
http_status: input.httpStatus,
|
|
10220
|
+
}),
|
|
10221
|
+
);
|
|
10222
|
+
}
|
|
10223
|
+
|
|
10163
10224
|
async sleep(ms: number): Promise<void> {
|
|
10164
10225
|
this.assertInlineChildContract('suspending_child');
|
|
10165
10226
|
const delayMs =
|
|
@@ -10212,6 +10273,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
10212
10273
|
|
|
10213
10274
|
const url = input.toString();
|
|
10214
10275
|
const parsedUrl = new URL(url);
|
|
10276
|
+
const method = (init.method ?? 'GET').toUpperCase();
|
|
10215
10277
|
const urlContainsResolvedSecret =
|
|
10216
10278
|
this.secretRedactor.containsRegisteredSecret(url, {
|
|
10217
10279
|
includeEncoded: true,
|
|
@@ -10303,6 +10365,14 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
10303
10365
|
staleAfterSeconds: options?.staleAfterSeconds,
|
|
10304
10366
|
transient: options?.transient === true,
|
|
10305
10367
|
onRecovered: (output) => {
|
|
10368
|
+
if (!output.ok) {
|
|
10369
|
+
this.logCtxFetchHttpFailure({
|
|
10370
|
+
key: normalizedKey,
|
|
10371
|
+
method,
|
|
10372
|
+
url: output.url || url,
|
|
10373
|
+
httpStatus: output.status,
|
|
10374
|
+
});
|
|
10375
|
+
}
|
|
10306
10376
|
if (!output.ok && this.currentAuthoringContractEdition >= 5) {
|
|
10307
10377
|
throw new CtxFetchHttpError(output);
|
|
10308
10378
|
}
|
|
@@ -10314,7 +10384,6 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
10314
10384
|
this.currentAuthoringContractEdition >= 5
|
|
10315
10385
|
),
|
|
10316
10386
|
execute: async ({ retainExternalCallSlot }) => {
|
|
10317
|
-
const method = (init.method ?? 'GET').toUpperCase();
|
|
10318
10387
|
const secretHeaders = await this.resolveSecretAuth(secretAuth);
|
|
10319
10388
|
const headers: Record<string, string> = {
|
|
10320
10389
|
...normalizeFetchHeaders(init.headers),
|
|
@@ -10349,6 +10418,15 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
10349
10418
|
'output' in existing
|
|
10350
10419
|
) {
|
|
10351
10420
|
this.log(`ctx.fetch(${url}): recovered response from checkpoint`);
|
|
10421
|
+
const checkpointOutput = existing.output as PlayFetchResponse;
|
|
10422
|
+
if (!checkpointOutput.ok) {
|
|
10423
|
+
this.logCtxFetchHttpFailure({
|
|
10424
|
+
key: normalizedKey,
|
|
10425
|
+
method,
|
|
10426
|
+
url: checkpointOutput.url || url,
|
|
10427
|
+
httpStatus: checkpointOutput.status,
|
|
10428
|
+
});
|
|
10429
|
+
}
|
|
10352
10430
|
if (this.durableDirectToolResultsBackedByReceipts) {
|
|
10353
10431
|
// The outer durable receipt is the replay authority in hosted
|
|
10354
10432
|
// runtimes. A legacy checkpoint fetch may seed that receipt
|
|
@@ -10356,7 +10434,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
10356
10434
|
// cache for the lifetime of a large map.
|
|
10357
10435
|
delete this.checkpoint.resolvedBoundaries?.[boundaryId];
|
|
10358
10436
|
}
|
|
10359
|
-
return
|
|
10437
|
+
return checkpointOutput;
|
|
10360
10438
|
}
|
|
10361
10439
|
|
|
10362
10440
|
if (!['GET', 'HEAD', 'OPTIONS'].includes(method)) {
|
|
@@ -10496,6 +10574,15 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
10496
10574
|
json: this.secretRedactor.redactKnownSecrets(rawJson),
|
|
10497
10575
|
};
|
|
10498
10576
|
|
|
10577
|
+
if (!output.ok) {
|
|
10578
|
+
this.logCtxFetchHttpFailure({
|
|
10579
|
+
key: normalizedKey,
|
|
10580
|
+
method,
|
|
10581
|
+
url: output.url || url,
|
|
10582
|
+
httpStatus: output.status,
|
|
10583
|
+
});
|
|
10584
|
+
}
|
|
10585
|
+
|
|
10499
10586
|
// Edition 5 adopts normal fetch semantics: a non-2xx response is
|
|
10500
10587
|
// a failed durable operation. Throw before checkpoint/receipt
|
|
10501
10588
|
// completion so the failure is never cached. Editions 1–4 retain
|
|
@@ -125,6 +125,85 @@ export function tagLogProvenance(
|
|
|
125
125
|
return `${PROVENANCE_PREFIX}${provenance}${PROVENANCE_SENTINEL}${line}`;
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
+
/**
|
|
129
|
+
* A customer-safe record of a `ctx.fetch` response that reached the server but
|
|
130
|
+
* was not successful. Its URL is destination-origin-only; it deliberately
|
|
131
|
+
* omits paths, query strings, request/response bodies, headers, receipt ids,
|
|
132
|
+
* and row identity: all of those can contain customer data or credentials.
|
|
133
|
+
* This one stable shape is shared by the
|
|
134
|
+
* runtime (emission), finalization (terminal warning), and CLI/tests
|
|
135
|
+
* (inspection), rather than each layer trying to infer an HTTP failure from
|
|
136
|
+
* arbitrary user logs.
|
|
137
|
+
*/
|
|
138
|
+
export type CtxFetchHttpFailureDiagnostic = {
|
|
139
|
+
key: string;
|
|
140
|
+
method: string;
|
|
141
|
+
url: string;
|
|
142
|
+
http_status: number;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
/** Guard terminal-transport diagnostics before they reach customer surfaces. */
|
|
146
|
+
export function isCtxFetchHttpFailureDiagnostic(
|
|
147
|
+
value: unknown,
|
|
148
|
+
): value is CtxFetchHttpFailureDiagnostic {
|
|
149
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
const record = value as Record<string, unknown>;
|
|
153
|
+
return (
|
|
154
|
+
typeof record.key === 'string' &&
|
|
155
|
+
typeof record.method === 'string' &&
|
|
156
|
+
typeof record.url === 'string' &&
|
|
157
|
+
typeof record.http_status === 'number' &&
|
|
158
|
+
Number.isInteger(record.http_status)
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export const CTX_FETCH_HTTP_FAILURE_LOG_PREFIX =
|
|
163
|
+
'[runtime.ctx_fetch_http_failure]';
|
|
164
|
+
|
|
165
|
+
/** Format the canonical durable log line for an observed non-2xx ctx.fetch. */
|
|
166
|
+
export function formatCtxFetchHttpFailureDiagnostic(
|
|
167
|
+
diagnostic: CtxFetchHttpFailureDiagnostic,
|
|
168
|
+
): string {
|
|
169
|
+
return `${CTX_FETCH_HTTP_FAILURE_LOG_PREFIX} ${JSON.stringify(diagnostic)}`;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Parse only runtime-authored, canonical ctx.fetch diagnostics. Untagged
|
|
174
|
+
* legacy/user lines are never interpreted as evidence, even if they happen to
|
|
175
|
+
* contain the same words.
|
|
176
|
+
*/
|
|
177
|
+
export function parseCtxFetchHttpFailureDiagnostic(
|
|
178
|
+
rawLine: string,
|
|
179
|
+
): CtxFetchHttpFailureDiagnostic | null {
|
|
180
|
+
const tagged = readProvenanceTag(rawLine);
|
|
181
|
+
if (tagged.provenance !== 'diagnostic') return null;
|
|
182
|
+
const index = tagged.line.indexOf(CTX_FETCH_HTTP_FAILURE_LOG_PREFIX);
|
|
183
|
+
if (index === -1) return null;
|
|
184
|
+
const json = tagged.line
|
|
185
|
+
.slice(index + CTX_FETCH_HTTP_FAILURE_LOG_PREFIX.length)
|
|
186
|
+
.trim();
|
|
187
|
+
try {
|
|
188
|
+
const parsed: unknown = JSON.parse(json);
|
|
189
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
const record = parsed as Record<string, unknown>;
|
|
193
|
+
if (!isCtxFetchHttpFailureDiagnostic(record)) {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
return {
|
|
197
|
+
key: record.key,
|
|
198
|
+
method: record.method,
|
|
199
|
+
url: record.url,
|
|
200
|
+
http_status: record.http_status,
|
|
201
|
+
};
|
|
202
|
+
} catch {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
128
207
|
/**
|
|
129
208
|
* Read a structural provenance tag off a line, if present, and return the tag
|
|
130
209
|
* plus the original untagged line. Returns `null` provenance when untagged.
|
|
@@ -141,9 +220,7 @@ export function readProvenanceTag(line: string): {
|
|
|
141
220
|
return { provenance: null, line };
|
|
142
221
|
}
|
|
143
222
|
const candidate = line.slice(PROVENANCE_PREFIX.length, end);
|
|
144
|
-
const provenance = LOG_PROVENANCE_CLASSES.includes(
|
|
145
|
-
candidate as LogProvenance,
|
|
146
|
-
)
|
|
223
|
+
const provenance = LOG_PROVENANCE_CLASSES.includes(candidate as LogProvenance)
|
|
147
224
|
? (candidate as LogProvenance)
|
|
148
225
|
: null;
|
|
149
226
|
return { provenance, line: line.slice(end + 1) };
|
|
@@ -17,6 +17,7 @@ import type { PlayRunFailureDetails } from './run-failure';
|
|
|
17
17
|
import type { ToolExecutionErrorSchemaVersion } from '../plays/tool-execution-error';
|
|
18
18
|
import type { ToolResponseContract } from '../plays/tool-response-contract';
|
|
19
19
|
import type { FixtureBehavior } from './fixture-behavior';
|
|
20
|
+
import type { CtxFetchHttpFailureDiagnostic } from './log-provenance';
|
|
20
21
|
|
|
21
22
|
export type PlayRunnerRateStateBackendConfig =
|
|
22
23
|
| {
|
|
@@ -307,6 +308,8 @@ export type PlayRunnerResult =
|
|
|
307
308
|
status: 'completed';
|
|
308
309
|
output: unknown;
|
|
309
310
|
outputWarnings?: PlayRunOutputWarning[];
|
|
311
|
+
/** Customer-safe runtime observations that must survive bounded log tails. */
|
|
312
|
+
runtimeDiagnostics?: CtxFetchHttpFailureDiagnostic[];
|
|
310
313
|
outputRowCount?: number;
|
|
311
314
|
logs: string[];
|
|
312
315
|
stats: Record<string, unknown>;
|