@jameslovespancakes/pi-plus 1.0.10 → 1.0.12
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/README.md +44 -20
- package/config/skills/workflow-code-review-actions/SKILL.md +44 -0
- package/package.json +6 -3
- package/src/core/accounts/oauth-pool.ts +102 -0
- package/src/core/accounts/registry.ts +8 -25
- package/src/core/accounts/routing.ts +122 -0
- package/src/core/anthropic/client-identity.ts +9 -64
- package/src/core/anthropic/quota.ts +8 -53
- package/src/core/anthropic/routing.ts +40 -115
- package/src/core/anthropic/store.ts +7 -9
- package/src/core/catalog/quality.ts +33 -15
- package/src/core/codex/quota.ts +8 -2
- package/src/core/codex/store.ts +12 -9
- package/src/core/config.ts +4 -17
- package/src/core/store.ts +13 -12
- package/src/domains/models/provider-picker.ts +1 -1
- package/src/domains/setup/index.ts +23 -23
- package/src/domains/subscriptions/accounts-picker.ts +18 -37
- package/src/domains/subscriptions/accounts.ts +23 -29
- package/src/domains/subscriptions/footer.ts +48 -29
- package/src/domains/subscriptions/index.ts +11 -27
- package/src/domains/subscriptions/provider.ts +38 -103
- package/src/domains/subscriptions/providers/anthropic.ts +5 -17
- package/src/domains/subscriptions/providers/codex.ts +171 -4
- package/src/domains/subscriptions/providers/hosted.ts +18 -0
- package/src/domains/subscriptions/providers/oauth-pool.ts +273 -0
- package/src/domains/subscriptions/routing.ts +19 -11
- package/src/domains/workflows/LICENSE.md +21 -0
- package/src/domains/workflows/index.ts +836 -0
- package/src/domains/workflows/runtime/advisory-challenge.ts +75 -0
- package/src/domains/workflows/runtime/advisory-evidence.ts +90 -0
- package/src/domains/workflows/runtime/advisory-schema.ts +83 -0
- package/src/domains/workflows/runtime/agent-attempt.ts +147 -0
- package/src/domains/workflows/runtime/agent-limits.ts +43 -0
- package/src/domains/workflows/runtime/agent-replay.ts +399 -0
- package/src/domains/workflows/runtime/agent-retry.ts +116 -0
- package/src/domains/workflows/runtime/agent-runner-types.ts +96 -0
- package/src/domains/workflows/runtime/agent-runner.ts +203 -0
- package/src/domains/workflows/runtime/agent-session-identity.ts +256 -0
- package/src/domains/workflows/runtime/agent-session-providers.ts +50 -0
- package/src/domains/workflows/runtime/agent-session.ts +382 -0
- package/src/domains/workflows/runtime/agent-skills.ts +270 -0
- package/src/domains/workflows/runtime/agent-workspace.ts +103 -0
- package/src/domains/workflows/runtime/background-workflow-tool.ts +75 -0
- package/src/domains/workflows/runtime/background-workflows.ts +492 -0
- package/src/domains/workflows/runtime/budget.ts +53 -0
- package/src/domains/workflows/runtime/cancellation.ts +87 -0
- package/src/domains/workflows/runtime/command-completions.ts +36 -0
- package/src/domains/workflows/runtime/concurrency.ts +403 -0
- package/src/domains/workflows/runtime/debug.ts +3 -0
- package/src/domains/workflows/runtime/diff-capture.ts +81 -0
- package/src/domains/workflows/runtime/discovery.ts +137 -0
- package/src/domains/workflows/runtime/dynamax-shortcuts.ts +122 -0
- package/src/domains/workflows/runtime/dynamax.ts +330 -0
- package/src/domains/workflows/runtime/engine.ts +543 -0
- package/src/domains/workflows/runtime/filesystem-error.ts +4 -0
- package/src/domains/workflows/runtime/finalizers.ts +66 -0
- package/src/domains/workflows/runtime/identity-canonicalization.ts +138 -0
- package/src/domains/workflows/runtime/identity-fingerprint.ts +15 -0
- package/src/domains/workflows/runtime/inline-workflow.ts +403 -0
- package/src/domains/workflows/runtime/journal.ts +313 -0
- package/src/domains/workflows/runtime/model-profiles.ts +310 -0
- package/src/domains/workflows/runtime/options.ts +157 -0
- package/src/domains/workflows/runtime/perf.ts +146 -0
- package/src/domains/workflows/runtime/pi-compat.ts +32 -0
- package/src/domains/workflows/runtime/process-runner.ts +260 -0
- package/src/domains/workflows/runtime/progress-types.ts +59 -0
- package/src/domains/workflows/runtime/progress.ts +400 -0
- package/src/domains/workflows/runtime/provider-usage-limit.ts +191 -0
- package/src/domains/workflows/runtime/replay-path-identity.ts +48 -0
- package/src/domains/workflows/runtime/research-contract.ts +89 -0
- package/src/domains/workflows/runtime/research-evidence.ts +289 -0
- package/src/domains/workflows/runtime/resume-context.ts +751 -0
- package/src/domains/workflows/runtime/review/code-review-orchestration.ts +24 -0
- package/src/domains/workflows/runtime/review/github-pr-comments.ts +249 -0
- package/src/domains/workflows/runtime/review/patch-validation.ts +120 -0
- package/src/domains/workflows/runtime/review/review-actions.ts +180 -0
- package/src/domains/workflows/runtime/review/review-budget.ts +45 -0
- package/src/domains/workflows/runtime/review/review-fix-workflow.ts +153 -0
- package/src/domains/workflows/runtime/review/review-format.ts +133 -0
- package/src/domains/workflows/runtime/review/review-handoff.ts +38 -0
- package/src/domains/workflows/runtime/review/review-issues.ts +87 -0
- package/src/domains/workflows/runtime/review/review-report.ts +42 -0
- package/src/domains/workflows/runtime/review/review-results-flow.ts +52 -0
- package/src/domains/workflows/runtime/review/review-results-viewer.ts +312 -0
- package/src/domains/workflows/runtime/review/review-session-coordinator.ts +153 -0
- package/src/domains/workflows/runtime/review/review-snapshot.ts +294 -0
- package/src/domains/workflows/runtime/review-diff-target.ts +130 -0
- package/src/domains/workflows/runtime/session-identity.ts +6 -0
- package/src/domains/workflows/runtime/structured-output.ts +32 -0
- package/src/domains/workflows/runtime/tool-capabilities.ts +44 -0
- package/src/domains/workflows/runtime/tool-source-identity.ts +150 -0
- package/src/domains/workflows/runtime/tree-fingerprint.ts +404 -0
- package/src/domains/workflows/runtime/types.ts +255 -0
- package/src/domains/workflows/runtime/ui/display-text.ts +13 -0
- package/src/domains/workflows/runtime/ui/dynamax-editor-decoration.ts +220 -0
- package/src/domains/workflows/runtime/ui/workflow-format.ts +165 -0
- package/src/domains/workflows/runtime/ui/workflow-inspector.ts +398 -0
- package/src/domains/workflows/runtime/ui/workflow-result-renderer.ts +171 -0
- package/src/domains/workflows/runtime/ui/workflow-viewer-layout.ts +51 -0
- package/src/domains/workflows/runtime/ui/workflow-widget.ts +78 -0
- package/src/domains/workflows/runtime/unknown-error.ts +8 -0
- package/src/domains/workflows/runtime/usage.ts +341 -0
- package/src/domains/workflows/runtime/workflow-advisory-utils.ts +245 -0
- package/src/domains/workflows/runtime/workflow-execution.ts +121 -0
- package/src/domains/workflows/runtime/workflow-module.ts +75 -0
- package/src/domains/workflows/runtime/workflow-run-background.ts +72 -0
- package/src/domains/workflows/runtime/workflow-run-controller.ts +342 -0
- package/src/domains/workflows/runtime/workflow-run-history.ts +162 -0
- package/src/domains/workflows/runtime/workflow-run-record.ts +635 -0
- package/src/domains/workflows/runtime/workflow-run-store.ts +176 -0
- package/src/domains/workflows/runtime/workflow-usage-limit-scheduler.ts +80 -0
- package/src/domains/workflows/runtime/workflows.ts +40 -0
- package/src/domains/workflows/runtime/worktree.ts +581 -0
- package/src/domains/workflows/workflows/code-review.ts +232 -0
- package/src/domains/workflows/workflows/diagnose.ts +154 -0
- package/src/domains/workflows/workflows/perf-review.ts +149 -0
- package/src/domains/workflows/workflows/refactor-scout.ts +143 -0
- package/src/domains/workflows/workflows/research.ts +169 -0
- package/src/services/usage-service.ts +6 -30
|
@@ -0,0 +1,635 @@
|
|
|
1
|
+
import { validateWorkflowRunId } from "./journal.ts";
|
|
2
|
+
import {
|
|
3
|
+
type ResolvedWorkflowRunOptions,
|
|
4
|
+
WORKFLOW_USAGE_LIMIT_ATTEMPTS_MAX,
|
|
5
|
+
WORKFLOW_USAGE_LIMIT_ATTEMPTS_MIN,
|
|
6
|
+
WORKFLOW_USAGE_LIMIT_DELAY_MAX_MS,
|
|
7
|
+
WORKFLOW_USAGE_LIMIT_DELAY_MIN_MS,
|
|
8
|
+
} from "./options.ts";
|
|
9
|
+
import type { WorkflowProgressSnapshot } from "./progress-types.ts";
|
|
10
|
+
import type { LoadedWorkflow, WorkflowSourceIdentity } from "./types.ts";
|
|
11
|
+
import { isWorkflowUsageSnapshot, type WorkflowUsageSnapshot } from "./usage.ts";
|
|
12
|
+
import { unknownErrorMessage } from "./unknown-error.ts";
|
|
13
|
+
import {
|
|
14
|
+
createPersistedWorkflowBackground,
|
|
15
|
+
isPersistedWorkflowBackground,
|
|
16
|
+
type PersistedWorkflowBackground,
|
|
17
|
+
} from "./workflow-run-background.ts";
|
|
18
|
+
|
|
19
|
+
export const WORKFLOW_RUN_RECORD_VERSION = 1;
|
|
20
|
+
|
|
21
|
+
const MAX_RESULT_BYTES = 1 << 20;
|
|
22
|
+
const MAX_RESULT_DEPTH = 64;
|
|
23
|
+
const MAX_RESULT_NODES = 16_384;
|
|
24
|
+
const MAX_PERSISTED_AGENTS = 1_000;
|
|
25
|
+
const MAX_PERSISTED_COUNTERS = 200;
|
|
26
|
+
const MAX_PERSISTED_LANES = 50;
|
|
27
|
+
const MAX_PERSISTED_LANE_ITEMS = 200;
|
|
28
|
+
const MAX_PERSISTED_PHASES = 200;
|
|
29
|
+
const MAX_PERSISTED_SUMMARY_ENTRIES = 200;
|
|
30
|
+
const MAX_PERSISTED_TEXT = 2_048;
|
|
31
|
+
const MAX_PERSISTED_USAGE_AGENTS = 1_000;
|
|
32
|
+
const REDACTED = "[redacted]";
|
|
33
|
+
|
|
34
|
+
export type WorkflowRunState = "queued" | "running" | "completed" | "failed" | "stopped" | "paused";
|
|
35
|
+
|
|
36
|
+
export type WorkflowRunStoredValue =
|
|
37
|
+
| null
|
|
38
|
+
| boolean
|
|
39
|
+
| number
|
|
40
|
+
| string
|
|
41
|
+
| readonly WorkflowRunStoredValue[]
|
|
42
|
+
| { readonly [key: string]: WorkflowRunStoredValue };
|
|
43
|
+
|
|
44
|
+
export type WorkflowRunStoredResult =
|
|
45
|
+
| { readonly kind: "value"; readonly value: WorkflowRunStoredValue }
|
|
46
|
+
| { readonly kind: "unavailable"; readonly reason: string };
|
|
47
|
+
|
|
48
|
+
export interface PersistedWorkflowRunOptions {
|
|
49
|
+
readonly inspect: boolean;
|
|
50
|
+
readonly perf: boolean;
|
|
51
|
+
readonly concurrency: number | null;
|
|
52
|
+
readonly parallelSubmissionLimit: number | null;
|
|
53
|
+
readonly maxAgents: number | null;
|
|
54
|
+
readonly agentTimeoutMs: number | null;
|
|
55
|
+
readonly agentRetries: number;
|
|
56
|
+
/** Explicit scheduler opt-in. Omitted on records created before provider-limit scheduling. */
|
|
57
|
+
readonly autoResumeOnUsageLimit?: boolean;
|
|
58
|
+
readonly usageLimitMaxAttempts?: number;
|
|
59
|
+
readonly usageLimitMaxDelayMs?: number;
|
|
60
|
+
readonly usageLimitAttempt?: number;
|
|
61
|
+
/** Explicit edited-source replay opt-in. Omitted on older records. */
|
|
62
|
+
readonly resumeEditedWorkflow?: boolean;
|
|
63
|
+
readonly budget: number | null;
|
|
64
|
+
/** Whether replay would require redacted invocation arguments. Omitted on legacy records. */
|
|
65
|
+
readonly argumentsPresent?: boolean;
|
|
66
|
+
readonly resultViewer?: "open" | "skip";
|
|
67
|
+
readonly resumeFromRunId?: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface PersistedWorkflowIdentity {
|
|
71
|
+
readonly name: string;
|
|
72
|
+
readonly sourceKind: WorkflowSourceIdentity["kind"];
|
|
73
|
+
readonly sourceFingerprint?: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface WorkflowProviderUsageLimitPause {
|
|
77
|
+
readonly kind: "provider_usage_limit";
|
|
78
|
+
readonly reason: "provider_usage_limit";
|
|
79
|
+
readonly providerMessage: string;
|
|
80
|
+
readonly provider?: string;
|
|
81
|
+
readonly model?: string;
|
|
82
|
+
readonly api?: string;
|
|
83
|
+
readonly resetHint?: string;
|
|
84
|
+
/** Total run attempt number, including the initial run. */
|
|
85
|
+
readonly attempt: number;
|
|
86
|
+
readonly nextEligibleAt: number;
|
|
87
|
+
readonly autoResume: boolean;
|
|
88
|
+
readonly maxAttempts: number;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export type WorkflowRunPause = WorkflowProviderUsageLimitPause;
|
|
92
|
+
|
|
93
|
+
interface WorkflowRunRecordBase {
|
|
94
|
+
readonly version: typeof WORKFLOW_RUN_RECORD_VERSION;
|
|
95
|
+
readonly runId: string;
|
|
96
|
+
readonly workflow: PersistedWorkflowIdentity;
|
|
97
|
+
readonly journalFile: string;
|
|
98
|
+
readonly options: PersistedWorkflowRunOptions;
|
|
99
|
+
readonly createdAt: number;
|
|
100
|
+
readonly updatedAt: number;
|
|
101
|
+
readonly progress: WorkflowProgressSnapshot;
|
|
102
|
+
readonly usage?: WorkflowUsageSnapshot;
|
|
103
|
+
readonly background?: PersistedWorkflowBackground;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export type WorkflowRunRecord = WorkflowRunRecordBase & (
|
|
107
|
+
| { readonly state: "queued"; readonly startedAt?: number; readonly endedAt?: never; readonly result?: never; readonly message?: never }
|
|
108
|
+
| { readonly state: "running"; readonly startedAt: number; readonly endedAt?: never; readonly result?: never; readonly message?: never }
|
|
109
|
+
| { readonly state: "paused"; readonly startedAt: number; readonly endedAt?: never; readonly result?: never; readonly message: string; readonly pause?: WorkflowRunPause }
|
|
110
|
+
| { readonly state: "completed"; readonly startedAt: number; readonly endedAt: number; readonly usage: WorkflowUsageSnapshot; readonly result: WorkflowRunStoredResult; readonly message?: never }
|
|
111
|
+
| { readonly state: "failed" | "stopped"; readonly startedAt: number; readonly endedAt: number; readonly usage: WorkflowUsageSnapshot; readonly result?: never; readonly message: string }
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
export type WorkflowRunTransition =
|
|
115
|
+
| { readonly state: "queued"; readonly progress: WorkflowProgressSnapshot; readonly at?: number }
|
|
116
|
+
| { readonly state: "running"; readonly progress: WorkflowProgressSnapshot; readonly at?: number }
|
|
117
|
+
| { readonly state: "paused"; readonly progress: WorkflowProgressSnapshot; readonly message: string; readonly pause?: WorkflowRunPause; readonly at?: number }
|
|
118
|
+
| { readonly state: "completed"; readonly progress: WorkflowProgressSnapshot; readonly usage: WorkflowUsageSnapshot; readonly result: unknown; readonly at?: number }
|
|
119
|
+
| { readonly state: "failed" | "stopped"; readonly progress: WorkflowProgressSnapshot; readonly usage: WorkflowUsageSnapshot; readonly error: unknown; readonly at?: number };
|
|
120
|
+
|
|
121
|
+
export function createWorkflowRunRecord(input: {
|
|
122
|
+
readonly runId: string;
|
|
123
|
+
readonly workflow: LoadedWorkflow;
|
|
124
|
+
readonly options: ResolvedWorkflowRunOptions;
|
|
125
|
+
readonly progress: WorkflowProgressSnapshot;
|
|
126
|
+
readonly argumentsPresent?: boolean;
|
|
127
|
+
}): WorkflowRunRecord {
|
|
128
|
+
const runId = validateWorkflowRunId(input.runId);
|
|
129
|
+
assertMatchingRun(runId, input.progress.runId);
|
|
130
|
+
return {
|
|
131
|
+
version: WORKFLOW_RUN_RECORD_VERSION,
|
|
132
|
+
runId,
|
|
133
|
+
state: "queued",
|
|
134
|
+
workflow: persistedWorkflowIdentity(input.workflow),
|
|
135
|
+
journalFile: `${runId}.jsonl`,
|
|
136
|
+
options: persistedWorkflowRunOptions(input.options, input.argumentsPresent ?? false),
|
|
137
|
+
createdAt: input.progress.startedAt,
|
|
138
|
+
updatedAt: input.progress.startedAt,
|
|
139
|
+
progress: compactWorkflowProgress(input.progress),
|
|
140
|
+
background: createPersistedWorkflowBackground(input.options.background),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function updateWorkflowRunProgress(
|
|
145
|
+
record: WorkflowRunRecord,
|
|
146
|
+
progress: WorkflowProgressSnapshot,
|
|
147
|
+
at = Date.now(),
|
|
148
|
+
): WorkflowRunRecord {
|
|
149
|
+
assertMatchingRun(record.runId, progress.runId);
|
|
150
|
+
const compact = compactWorkflowProgress(progress);
|
|
151
|
+
const updated = {
|
|
152
|
+
...record,
|
|
153
|
+
updatedAt: at,
|
|
154
|
+
progress: compact,
|
|
155
|
+
};
|
|
156
|
+
return compact.usage === undefined ? updated : { ...updated, usage: compact.usage };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function transitionWorkflowRun(record: WorkflowRunRecord, transition: WorkflowRunTransition): WorkflowRunRecord {
|
|
160
|
+
if (!isAllowedTransition(record.state, transition.state)) {
|
|
161
|
+
throw new Error(`Invalid workflow run transition: ${record.state} -> ${transition.state}.`);
|
|
162
|
+
}
|
|
163
|
+
assertMatchingRun(record.runId, transition.progress.runId);
|
|
164
|
+
const at = transition.at ?? Date.now();
|
|
165
|
+
const progress = compactWorkflowProgress(transition.progress);
|
|
166
|
+
const base: WorkflowRunRecordBase = {
|
|
167
|
+
version: record.version,
|
|
168
|
+
runId: record.runId,
|
|
169
|
+
workflow: record.workflow,
|
|
170
|
+
journalFile: record.journalFile,
|
|
171
|
+
options: record.options,
|
|
172
|
+
createdAt: record.createdAt,
|
|
173
|
+
updatedAt: at,
|
|
174
|
+
progress,
|
|
175
|
+
usage: progress.usage ?? record.usage,
|
|
176
|
+
background: record.background,
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
switch (transition.state) {
|
|
180
|
+
case "queued":
|
|
181
|
+
return { ...base, state: "queued", startedAt: record.startedAt };
|
|
182
|
+
case "running":
|
|
183
|
+
return { ...base, state: "running", startedAt: record.startedAt ?? at };
|
|
184
|
+
case "paused":
|
|
185
|
+
return {
|
|
186
|
+
...base,
|
|
187
|
+
state: "paused",
|
|
188
|
+
startedAt: record.startedAt ?? record.createdAt,
|
|
189
|
+
message: persistedErrorMessage(transition.message),
|
|
190
|
+
pause: transition.pause === undefined ? undefined : compactWorkflowRunPause(transition.pause),
|
|
191
|
+
};
|
|
192
|
+
case "completed":
|
|
193
|
+
return {
|
|
194
|
+
...base,
|
|
195
|
+
state: "completed",
|
|
196
|
+
startedAt: record.startedAt ?? record.createdAt,
|
|
197
|
+
endedAt: at,
|
|
198
|
+
usage: compactWorkflowUsage(transition.usage),
|
|
199
|
+
result: captureWorkflowRunResult(transition.result),
|
|
200
|
+
};
|
|
201
|
+
case "failed":
|
|
202
|
+
case "stopped":
|
|
203
|
+
return {
|
|
204
|
+
...base,
|
|
205
|
+
state: transition.state,
|
|
206
|
+
startedAt: record.startedAt ?? record.createdAt,
|
|
207
|
+
endedAt: at,
|
|
208
|
+
usage: compactWorkflowUsage(transition.usage),
|
|
209
|
+
message: persistedErrorMessage(transition.error),
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function captureWorkflowRunResult(value: unknown): WorkflowRunStoredResult {
|
|
215
|
+
try {
|
|
216
|
+
const state = { nodes: 0, bytes: 0, active: new WeakSet<object>() };
|
|
217
|
+
const captured = captureStoredValue(value, state, 0, false);
|
|
218
|
+
const normalized = captured === undefined ? null : captured;
|
|
219
|
+
if (Buffer.byteLength(JSON.stringify(normalized)) > MAX_RESULT_BYTES) {
|
|
220
|
+
throw new WorkflowRunValueError(`result exceeded ${MAX_RESULT_BYTES} bytes`);
|
|
221
|
+
}
|
|
222
|
+
return { kind: "value", value: normalized };
|
|
223
|
+
} catch (error) {
|
|
224
|
+
return {
|
|
225
|
+
kind: "unavailable",
|
|
226
|
+
reason: error instanceof WorkflowRunValueError ? error.message : "result inspection failed",
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function persistedWorkflowIdentity(workflow: LoadedWorkflow): PersistedWorkflowIdentity {
|
|
232
|
+
const sourceFingerprint = workflow.source.kind === "unverifiable" ? undefined : workflow.source.fingerprint;
|
|
233
|
+
return {
|
|
234
|
+
name: boundedText(workflow.meta.name),
|
|
235
|
+
sourceKind: workflow.source.kind,
|
|
236
|
+
sourceFingerprint,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function persistedWorkflowRunOptions(
|
|
241
|
+
options: ResolvedWorkflowRunOptions,
|
|
242
|
+
argumentsPresent: boolean,
|
|
243
|
+
): PersistedWorkflowRunOptions {
|
|
244
|
+
return {
|
|
245
|
+
inspect: options.inspect ?? false,
|
|
246
|
+
perf: options.perf,
|
|
247
|
+
concurrency: options.concurrency,
|
|
248
|
+
parallelSubmissionLimit: options.parallelSubmissionLimit,
|
|
249
|
+
maxAgents: options.maxAgents,
|
|
250
|
+
agentTimeoutMs: options.agentTimeoutMs,
|
|
251
|
+
agentRetries: options.agentRetries,
|
|
252
|
+
autoResumeOnUsageLimit: options.autoResumeOnUsageLimit,
|
|
253
|
+
usageLimitMaxAttempts: options.usageLimitMaxAttempts,
|
|
254
|
+
usageLimitMaxDelayMs: options.usageLimitMaxDelayMs,
|
|
255
|
+
usageLimitAttempt: options.usageLimitAttempt,
|
|
256
|
+
resumeEditedWorkflow: options.resumeEditedWorkflow,
|
|
257
|
+
budget: options.budget,
|
|
258
|
+
argumentsPresent,
|
|
259
|
+
resultViewer: options.resultViewer,
|
|
260
|
+
resumeFromRunId: options.resumeFromRunId,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function compactWorkflowProgress(snapshot: WorkflowProgressSnapshot): WorkflowProgressSnapshot {
|
|
265
|
+
let remainingAgents = MAX_PERSISTED_AGENTS;
|
|
266
|
+
return {
|
|
267
|
+
runId: snapshot.runId,
|
|
268
|
+
title: boundedText(snapshot.title),
|
|
269
|
+
startedAt: snapshot.startedAt,
|
|
270
|
+
doneAt: snapshot.doneAt,
|
|
271
|
+
currentPhase: boundedText(snapshot.currentPhase),
|
|
272
|
+
phases: snapshot.phases.slice(-MAX_PERSISTED_PHASES).map((phase) => {
|
|
273
|
+
const agents = phase.agents.slice(0, remainingAgents).map((agent) => ({
|
|
274
|
+
id: agent.id,
|
|
275
|
+
label: boundedText(agent.label),
|
|
276
|
+
model: agent.model === undefined ? undefined : boundedText(agent.model),
|
|
277
|
+
status: agent.status,
|
|
278
|
+
startedAt: agent.startedAt,
|
|
279
|
+
doneAt: agent.doneAt,
|
|
280
|
+
toolUses: agent.toolUses,
|
|
281
|
+
lastTool: agent.lastTool === undefined ? undefined : boundedText(agent.lastTool),
|
|
282
|
+
}));
|
|
283
|
+
remainingAgents -= agents.length;
|
|
284
|
+
return { title: boundedText(phase.title), agents };
|
|
285
|
+
}),
|
|
286
|
+
counters: snapshot.counters
|
|
287
|
+
.slice(-MAX_PERSISTED_COUNTERS)
|
|
288
|
+
.map((counter) => ({ ...counter, key: boundedText(counter.key), label: boundedText(counter.label) })),
|
|
289
|
+
summary: snapshot.summary
|
|
290
|
+
.slice(-MAX_PERSISTED_SUMMARY_ENTRIES)
|
|
291
|
+
.map(([key, value]) => [boundedText(key), typeof value === "string" ? boundedText(value) : value]),
|
|
292
|
+
lanes: snapshot.lanes.slice(0, MAX_PERSISTED_LANES).map(([lane, items]) => [
|
|
293
|
+
boundedText(lane),
|
|
294
|
+
items.slice(-MAX_PERSISTED_LANE_ITEMS).map((item) => ({
|
|
295
|
+
lane: boundedText(item.lane),
|
|
296
|
+
title: boundedText(item.title),
|
|
297
|
+
subtitle: item.subtitle === undefined ? undefined : boundedText(item.subtitle),
|
|
298
|
+
status: item.status,
|
|
299
|
+
createdAt: item.createdAt,
|
|
300
|
+
})),
|
|
301
|
+
]),
|
|
302
|
+
laneOverflow: snapshot.laneOverflow.slice(0, MAX_PERSISTED_LANES).map(([lane, count]) => [boundedText(lane), count]),
|
|
303
|
+
logs: [],
|
|
304
|
+
usage: snapshot.usage === undefined ? undefined : compactWorkflowUsage(snapshot.usage),
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function compactWorkflowUsage(snapshot: WorkflowUsageSnapshot): WorkflowUsageSnapshot {
|
|
309
|
+
return {
|
|
310
|
+
agents: snapshot.agents.slice(-MAX_PERSISTED_USAGE_AGENTS).map((agent) => ({
|
|
311
|
+
label: boundedText(agent.label),
|
|
312
|
+
phase: agent.phase === undefined ? undefined : boundedText(agent.phase),
|
|
313
|
+
provider: agent.provider === undefined ? undefined : boundedText(agent.provider),
|
|
314
|
+
model: agent.model === undefined ? undefined : boundedText(agent.model),
|
|
315
|
+
assistantMessages: agent.assistantMessages,
|
|
316
|
+
usage: {
|
|
317
|
+
...agent.usage,
|
|
318
|
+
coverage: { ...agent.usage.coverage },
|
|
319
|
+
cost: { ...agent.usage.cost },
|
|
320
|
+
},
|
|
321
|
+
})),
|
|
322
|
+
totals: {
|
|
323
|
+
...snapshot.totals,
|
|
324
|
+
coverage: { ...snapshot.totals.coverage },
|
|
325
|
+
cost: { ...snapshot.totals.cost },
|
|
326
|
+
},
|
|
327
|
+
assistantMessages: snapshot.assistantMessages,
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function isAllowedTransition(from: WorkflowRunState, to: WorkflowRunState): boolean {
|
|
332
|
+
switch (from) {
|
|
333
|
+
case "queued":
|
|
334
|
+
return to === "running" || to === "failed" || to === "stopped" || to === "paused";
|
|
335
|
+
case "running":
|
|
336
|
+
return to === "completed" || to === "failed" || to === "stopped" || to === "paused";
|
|
337
|
+
case "paused":
|
|
338
|
+
return to === "queued" || to === "running" || to === "failed" || to === "stopped";
|
|
339
|
+
case "completed":
|
|
340
|
+
case "failed":
|
|
341
|
+
case "stopped":
|
|
342
|
+
return false;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function assertMatchingRun(expected: string, actual: string): void {
|
|
347
|
+
if (expected !== actual) throw new Error(`Workflow progress run id ${actual} does not match ${expected}.`);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
interface CaptureState {
|
|
351
|
+
nodes: number;
|
|
352
|
+
bytes: number;
|
|
353
|
+
active: WeakSet<object>;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function captureStoredValue(
|
|
357
|
+
value: unknown,
|
|
358
|
+
state: CaptureState,
|
|
359
|
+
depth: number,
|
|
360
|
+
arrayItem: boolean,
|
|
361
|
+
): WorkflowRunStoredValue | undefined {
|
|
362
|
+
state.nodes += 1;
|
|
363
|
+
if (state.nodes > MAX_RESULT_NODES) throw new WorkflowRunValueError(`result exceeded ${MAX_RESULT_NODES} values`);
|
|
364
|
+
if (depth > MAX_RESULT_DEPTH) throw new WorkflowRunValueError(`result exceeded ${MAX_RESULT_DEPTH} levels`);
|
|
365
|
+
|
|
366
|
+
if (value === null) return null;
|
|
367
|
+
switch (typeof value) {
|
|
368
|
+
case "undefined":
|
|
369
|
+
return arrayItem ? null : undefined;
|
|
370
|
+
case "boolean":
|
|
371
|
+
return value;
|
|
372
|
+
case "number":
|
|
373
|
+
if (!Number.isFinite(value)) throw new WorkflowRunValueError("result contains a non-finite number");
|
|
374
|
+
return value;
|
|
375
|
+
case "string":
|
|
376
|
+
countResultBytes(state, value);
|
|
377
|
+
return value;
|
|
378
|
+
case "bigint":
|
|
379
|
+
case "function":
|
|
380
|
+
case "symbol":
|
|
381
|
+
throw new WorkflowRunValueError(`result contains unsupported ${typeof value} data`);
|
|
382
|
+
case "object":
|
|
383
|
+
break;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (state.active.has(value)) throw new WorkflowRunValueError("result contains a cycle");
|
|
387
|
+
state.active.add(value);
|
|
388
|
+
try {
|
|
389
|
+
const prototype = Object.getPrototypeOf(value);
|
|
390
|
+
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
391
|
+
if (Array.isArray(value)) {
|
|
392
|
+
if (prototype !== Array.prototype) throw new WorkflowRunValueError("result contains an array with a custom prototype");
|
|
393
|
+
if (value.length > MAX_RESULT_NODES - state.nodes) {
|
|
394
|
+
throw new WorkflowRunValueError(`result exceeded ${MAX_RESULT_NODES} values`);
|
|
395
|
+
}
|
|
396
|
+
const output: WorkflowRunStoredValue[] = [];
|
|
397
|
+
for (let index = 0; index < value.length; index++) {
|
|
398
|
+
const descriptor = descriptors[String(index)];
|
|
399
|
+
if (!descriptor) {
|
|
400
|
+
output.push(null);
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
if (!("value" in descriptor)) {
|
|
404
|
+
output.push(REDACTED);
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
output.push(captureStoredValue(descriptor.value, state, depth + 1, true) ?? null);
|
|
408
|
+
}
|
|
409
|
+
return output;
|
|
410
|
+
}
|
|
411
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
412
|
+
throw new WorkflowRunValueError("result contains an object with a custom prototype");
|
|
413
|
+
}
|
|
414
|
+
const output: Record<string, WorkflowRunStoredValue> = {};
|
|
415
|
+
for (const key of Object.keys(descriptors)) {
|
|
416
|
+
const descriptor = descriptors[key]!;
|
|
417
|
+
if (!descriptor.enumerable) continue;
|
|
418
|
+
countResultBytes(state, key);
|
|
419
|
+
if (isSensitivePersistenceKey(key) || !("value" in descriptor)) {
|
|
420
|
+
output[key] = REDACTED;
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
const captured = captureStoredValue(descriptor.value, state, depth + 1, false);
|
|
424
|
+
if (captured !== undefined) output[key] = captured;
|
|
425
|
+
}
|
|
426
|
+
return output;
|
|
427
|
+
} finally {
|
|
428
|
+
state.active.delete(value);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function countResultBytes(state: CaptureState, value: string): void {
|
|
433
|
+
state.bytes += Buffer.byteLength(value);
|
|
434
|
+
if (state.bytes > MAX_RESULT_BYTES) throw new WorkflowRunValueError(`result exceeded ${MAX_RESULT_BYTES} bytes`);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function isSensitivePersistenceKey(key: string): boolean {
|
|
438
|
+
return /^(?:api[-_]?key|authorization|cookie|credentials?|env|environment|messages?|password|private[-_]?key|prompts?|secret|tool[-_]?outputs?|tool[-_]?results?|transcripts?)$/i.test(key);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
function persistedErrorMessage(error: unknown): string {
|
|
442
|
+
return boundedText(
|
|
443
|
+
unknownErrorMessage(error)
|
|
444
|
+
.replace(/\bBearer\s+[^\s,;]+/gi, "Bearer [redacted]")
|
|
445
|
+
.replace(/\b(?:api[-_ ]?key|authorization|cookie|credentials?|password|private[-_ ]?key|secret|token)\s*[:=]\s*[^\s,;]+/gi, "credential=[redacted]")
|
|
446
|
+
.replace(/\bsk-(?:proj-)?[A-Za-z0-9_-]{8,}\b/g, REDACTED),
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
function compactWorkflowRunPause(pause: WorkflowRunPause): WorkflowRunPause {
|
|
451
|
+
return {
|
|
452
|
+
...pause,
|
|
453
|
+
providerMessage: persistedErrorMessage(pause.providerMessage),
|
|
454
|
+
provider: pause.provider === undefined ? undefined : boundedText(pause.provider),
|
|
455
|
+
model: pause.model === undefined ? undefined : boundedText(pause.model),
|
|
456
|
+
api: pause.api === undefined ? undefined : boundedText(pause.api),
|
|
457
|
+
resetHint: pause.resetHint === undefined ? undefined : persistedErrorMessage(pause.resetHint),
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
function boundedText(value: string): string {
|
|
462
|
+
return value.length <= MAX_PERSISTED_TEXT ? value : `${value.slice(0, MAX_PERSISTED_TEXT - 1)}…`;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
export function isWorkflowRunRecord(value: unknown): value is WorkflowRunRecord {
|
|
466
|
+
if (!isRecord(value) || value.version !== WORKFLOW_RUN_RECORD_VERSION) return false;
|
|
467
|
+
if (!isValidRunId(value.runId) || !isWorkflowRunState(value.state)) return false;
|
|
468
|
+
if (!isPersistedWorkflowIdentity(value.workflow) || !isPersistedWorkflowRunOptions(value.options)) return false;
|
|
469
|
+
if (value.journalFile !== `${value.runId}.jsonl`) return false;
|
|
470
|
+
if (!isFiniteNumber(value.createdAt) || !isFiniteNumber(value.updatedAt)) return false;
|
|
471
|
+
if (value.startedAt !== undefined && !isFiniteNumber(value.startedAt)) return false;
|
|
472
|
+
if (value.endedAt !== undefined && !isFiniteNumber(value.endedAt)) return false;
|
|
473
|
+
if (!isWorkflowProgressSnapshot(value.progress) || value.progress.runId !== value.runId) return false;
|
|
474
|
+
if (value.usage !== undefined && !isWorkflowUsageSnapshot(value.usage)) return false;
|
|
475
|
+
if (value.result !== undefined && !isWorkflowRunStoredResult(value.result)) return false;
|
|
476
|
+
if (value.message !== undefined && typeof value.message !== "string") return false;
|
|
477
|
+
if (value.pause !== undefined && !isWorkflowRunPause(value.pause)) return false;
|
|
478
|
+
if (value.background !== undefined && !isPersistedWorkflowBackground(value.background)) return false;
|
|
479
|
+
switch (value.state) {
|
|
480
|
+
case "queued":
|
|
481
|
+
return value.endedAt === undefined && value.result === undefined && value.message === undefined && value.pause === undefined;
|
|
482
|
+
case "running":
|
|
483
|
+
return value.startedAt !== undefined && value.endedAt === undefined && value.result === undefined && value.message === undefined && value.pause === undefined;
|
|
484
|
+
case "paused":
|
|
485
|
+
return value.startedAt !== undefined && value.endedAt === undefined && value.result === undefined && value.message !== undefined;
|
|
486
|
+
case "completed":
|
|
487
|
+
return value.startedAt !== undefined && value.endedAt !== undefined && value.usage !== undefined && value.result !== undefined && value.message === undefined && value.pause === undefined;
|
|
488
|
+
case "failed":
|
|
489
|
+
case "stopped":
|
|
490
|
+
return value.startedAt !== undefined && value.endedAt !== undefined && value.usage !== undefined && value.result === undefined && value.message !== undefined && value.pause === undefined;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
function isPersistedWorkflowIdentity(value: unknown): value is PersistedWorkflowIdentity {
|
|
495
|
+
if (!isRecord(value) || typeof value.name !== "string") return false;
|
|
496
|
+
if (value.sourceKind !== "file" && value.sourceKind !== "fingerprint" && value.sourceKind !== "unverifiable") return false;
|
|
497
|
+
return value.sourceKind === "unverifiable"
|
|
498
|
+
? value.sourceFingerprint === undefined
|
|
499
|
+
: typeof value.sourceFingerprint === "string";
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
function isPersistedWorkflowRunOptions(value: unknown): value is PersistedWorkflowRunOptions {
|
|
503
|
+
if (!isRecord(value)) return false;
|
|
504
|
+
if (typeof value.inspect !== "boolean" || typeof value.perf !== "boolean") return false;
|
|
505
|
+
if (!isFiniteNumber(value.agentRetries)) return false;
|
|
506
|
+
if (value.concurrency !== null && !isFiniteNumber(value.concurrency)) return false;
|
|
507
|
+
if (value.maxAgents !== null && !isFiniteNumber(value.maxAgents)) return false;
|
|
508
|
+
if (value.agentTimeoutMs !== null && !isFiniteNumber(value.agentTimeoutMs)) return false;
|
|
509
|
+
if (value.autoResumeOnUsageLimit !== undefined && typeof value.autoResumeOnUsageLimit !== "boolean") return false;
|
|
510
|
+
if (
|
|
511
|
+
value.usageLimitMaxAttempts !== undefined
|
|
512
|
+
&& !isIntegerInRange(value.usageLimitMaxAttempts, WORKFLOW_USAGE_LIMIT_ATTEMPTS_MIN, WORKFLOW_USAGE_LIMIT_ATTEMPTS_MAX)
|
|
513
|
+
) return false;
|
|
514
|
+
if (
|
|
515
|
+
value.usageLimitMaxDelayMs !== undefined
|
|
516
|
+
&& !isIntegerInRange(value.usageLimitMaxDelayMs, WORKFLOW_USAGE_LIMIT_DELAY_MIN_MS, WORKFLOW_USAGE_LIMIT_DELAY_MAX_MS)
|
|
517
|
+
) return false;
|
|
518
|
+
if (
|
|
519
|
+
value.usageLimitAttempt !== undefined
|
|
520
|
+
&& !isIntegerInRange(value.usageLimitAttempt, 0, WORKFLOW_USAGE_LIMIT_ATTEMPTS_MAX)
|
|
521
|
+
) return false;
|
|
522
|
+
if (value.resumeEditedWorkflow !== undefined && typeof value.resumeEditedWorkflow !== "boolean") return false;
|
|
523
|
+
if (value.parallelSubmissionLimit !== null && !isFiniteNumber(value.parallelSubmissionLimit)) return false;
|
|
524
|
+
if (value.budget !== null && !isFiniteNumber(value.budget)) return false;
|
|
525
|
+
if (value.argumentsPresent !== undefined && typeof value.argumentsPresent !== "boolean") return false;
|
|
526
|
+
if (value.resultViewer !== undefined && value.resultViewer !== "open" && value.resultViewer !== "skip") return false;
|
|
527
|
+
return value.resumeFromRunId === undefined || typeof value.resumeFromRunId === "string";
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
function isWorkflowRunPause(value: unknown): value is WorkflowRunPause {
|
|
531
|
+
if (!isRecord(value) || value.kind !== "provider_usage_limit" || value.reason !== "provider_usage_limit") return false;
|
|
532
|
+
if (typeof value.providerMessage !== "string") return false;
|
|
533
|
+
if (value.provider !== undefined && typeof value.provider !== "string") return false;
|
|
534
|
+
if (value.model !== undefined && typeof value.model !== "string") return false;
|
|
535
|
+
if (value.api !== undefined && typeof value.api !== "string") return false;
|
|
536
|
+
if (value.resetHint !== undefined && typeof value.resetHint !== "string") return false;
|
|
537
|
+
return (
|
|
538
|
+
isIntegerInRange(value.attempt, 1, Number.MAX_SAFE_INTEGER) &&
|
|
539
|
+
isFiniteNumber(value.nextEligibleAt) && value.nextEligibleAt >= 0 &&
|
|
540
|
+
typeof value.autoResume === "boolean" &&
|
|
541
|
+
isIntegerInRange(value.maxAttempts, WORKFLOW_USAGE_LIMIT_ATTEMPTS_MIN, WORKFLOW_USAGE_LIMIT_ATTEMPTS_MAX) &&
|
|
542
|
+
(!value.autoResume || value.attempt < value.maxAttempts)
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
function isWorkflowProgressSnapshot(value: unknown): value is WorkflowProgressSnapshot {
|
|
547
|
+
if (!isRecord(value) || typeof value.runId !== "string" || typeof value.title !== "string") return false;
|
|
548
|
+
if (!isFiniteNumber(value.startedAt) || typeof value.currentPhase !== "string") return false;
|
|
549
|
+
if (value.doneAt !== undefined && !isFiniteNumber(value.doneAt)) return false;
|
|
550
|
+
if (!Array.isArray(value.phases) || !value.phases.every(isPhaseSnapshot)) return false;
|
|
551
|
+
if (!Array.isArray(value.counters) || !value.counters.every(isCounterSnapshot)) return false;
|
|
552
|
+
if (!Array.isArray(value.summary) || !value.summary.every(isSummaryEntry)) return false;
|
|
553
|
+
if (!Array.isArray(value.lanes) || !value.lanes.every(isLaneSnapshot)) return false;
|
|
554
|
+
if (!Array.isArray(value.laneOverflow) || !value.laneOverflow.every(isLaneOverflowEntry)) return false;
|
|
555
|
+
if (!Array.isArray(value.logs) || !value.logs.every((log) => typeof log === "string")) return false;
|
|
556
|
+
return value.usage === undefined || isWorkflowUsageSnapshot(value.usage);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
function isPhaseSnapshot(value: unknown): boolean {
|
|
560
|
+
return isRecord(value) && typeof value.title === "string" && Array.isArray(value.agents) && value.agents.every(isAgentSnapshot);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
function isAgentSnapshot(value: unknown): boolean {
|
|
564
|
+
if (!isRecord(value) || !isFiniteNumber(value.id) || typeof value.label !== "string") return false;
|
|
565
|
+
if (value.status !== "queued" && value.status !== "running" && value.status !== "done" && value.status !== "failed") return false;
|
|
566
|
+
if (!isFiniteNumber(value.toolUses)) return false;
|
|
567
|
+
if (value.startedAt !== undefined && !isFiniteNumber(value.startedAt)) return false;
|
|
568
|
+
if (value.doneAt !== undefined && !isFiniteNumber(value.doneAt)) return false;
|
|
569
|
+
if (value.lastTool !== undefined && typeof value.lastTool !== "string") return false;
|
|
570
|
+
return value.error === undefined || typeof value.error === "string";
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
function isCounterSnapshot(value: unknown): boolean {
|
|
574
|
+
return isRecord(value) && typeof value.key === "string" && typeof value.label === "string" && isFiniteNumber(value.value);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
function isSummaryEntry(value: unknown): boolean {
|
|
578
|
+
return Array.isArray(value) && value.length === 2 && typeof value[0] === "string" && (typeof value[1] === "string" || isFiniteNumber(value[1]));
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
function isLaneSnapshot(value: unknown): boolean {
|
|
582
|
+
return Array.isArray(value) && value.length === 2 && typeof value[0] === "string" && Array.isArray(value[1]) && value[1].every(isLaneItemSnapshot);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
function isLaneItemSnapshot(value: unknown): boolean {
|
|
586
|
+
if (!isRecord(value) || typeof value.lane !== "string" || typeof value.title !== "string") return false;
|
|
587
|
+
if (value.subtitle !== undefined && typeof value.subtitle !== "string") return false;
|
|
588
|
+
if (value.status !== "pending" && value.status !== "running" && value.status !== "success" && value.status !== "warning" && value.status !== "error") return false;
|
|
589
|
+
if (!isFiniteNumber(value.createdAt)) return false;
|
|
590
|
+
return value.details === undefined || typeof value.details === "string";
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
function isLaneOverflowEntry(value: unknown): boolean {
|
|
594
|
+
return Array.isArray(value) && value.length === 2 && typeof value[0] === "string" && isFiniteNumber(value[1]);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
function isWorkflowRunStoredResult(value: unknown): value is WorkflowRunStoredResult {
|
|
598
|
+
if (!isRecord(value)) return false;
|
|
599
|
+
if (value.kind === "unavailable") return typeof value.reason === "string";
|
|
600
|
+
return value.kind === "value" && isWorkflowRunStoredValue(value.value);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
function isWorkflowRunStoredValue(value: unknown): value is WorkflowRunStoredValue {
|
|
604
|
+
if (value === null || typeof value === "boolean" || typeof value === "string") return true;
|
|
605
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
606
|
+
if (Array.isArray(value)) return value.every(isWorkflowRunStoredValue);
|
|
607
|
+
return isRecord(value) && Object.values(value).every(isWorkflowRunStoredValue);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
function isWorkflowRunState(value: unknown): value is WorkflowRunState {
|
|
611
|
+
return value === "queued" || value === "running" || value === "completed" || value === "failed" || value === "stopped" || value === "paused";
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
function isValidRunId(value: unknown): value is string {
|
|
615
|
+
if (typeof value !== "string") return false;
|
|
616
|
+
try {
|
|
617
|
+
return validateWorkflowRunId(value) === value;
|
|
618
|
+
} catch {
|
|
619
|
+
return false;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
function isFiniteNumber(value: unknown): value is number {
|
|
624
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
function isIntegerInRange(value: unknown, min: number, max: number): value is number {
|
|
628
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= min && value <= max;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
632
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
class WorkflowRunValueError extends Error {}
|