@smithers-orchestrator/graph 0.16.9 → 0.17.0
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/package.json +5 -3
- package/src/dom/extract.js +48 -15
- package/src/extract.js +33 -8
- package/src/index.d.ts +14 -2
- package/src/types.ts +11 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smithers-orchestrator/graph",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "Framework-neutral Smithers workflow graph model and extraction helpers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -20,8 +20,10 @@
|
|
|
20
20
|
"src/"
|
|
21
21
|
],
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"
|
|
24
|
-
"
|
|
23
|
+
"drizzle-orm": "^0.45.2",
|
|
24
|
+
"zod": "^4.3.6",
|
|
25
|
+
"@smithers-orchestrator/agents": "0.17.0",
|
|
26
|
+
"@smithers-orchestrator/errors": "0.17.0"
|
|
25
27
|
},
|
|
26
28
|
"devDependencies": {
|
|
27
29
|
"@types/bun": "latest",
|
package/src/dom/extract.js
CHANGED
|
@@ -24,11 +24,28 @@ import { SmithersError } from "@smithers-orchestrator/errors/SmithersError";
|
|
|
24
24
|
// differ, so replacing this implementation would not produce identical output
|
|
25
25
|
// for all inputs.
|
|
26
26
|
const loadRuntimeModule = new Function("specifier", "return import(specifier)");
|
|
27
|
-
// CLI agents (Claude Code, Codex,
|
|
28
|
-
// thinking without producing stdout.
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
// CLI agents (Claude Code, Codex, Gemini, Kimi) can spend many minutes reading
|
|
28
|
+
// files and thinking without producing stdout. 5 min was still too aggressive:
|
|
29
|
+
// reviewer agents on substantive diffs were getting killed mid-review and
|
|
30
|
+
// breaking ValidationLoop. 10 min matches the explicit per-task overrides used
|
|
31
|
+
// by implement/validate tasks.
|
|
32
|
+
//
|
|
33
|
+
// The default can be overridden at runtime via the SMITHERS_TASK_HEARTBEAT_MS
|
|
34
|
+
// environment variable.
|
|
35
|
+
const HEARTBEAT_DEFAULT_MS = 600_000;
|
|
36
|
+
function envHeartbeatTimeoutMs() {
|
|
37
|
+
const raw = typeof process !== "undefined" && process?.env
|
|
38
|
+
? process.env.SMITHERS_TASK_HEARTBEAT_MS
|
|
39
|
+
: undefined;
|
|
40
|
+
if (typeof raw !== "string" || raw.length === 0)
|
|
41
|
+
return HEARTBEAT_DEFAULT_MS;
|
|
42
|
+
const parsed = Number(raw);
|
|
43
|
+
if (!Number.isFinite(parsed) || parsed <= 0)
|
|
44
|
+
return HEARTBEAT_DEFAULT_MS;
|
|
45
|
+
return Math.floor(parsed);
|
|
46
|
+
}
|
|
47
|
+
const DEFAULT_LOCAL_TASK_HEARTBEAT_TIMEOUT_MS = envHeartbeatTimeoutMs();
|
|
48
|
+
const DEFAULT_SANDBOX_TASK_HEARTBEAT_TIMEOUT_MS = envHeartbeatTimeoutMs();
|
|
32
49
|
/**
|
|
33
50
|
* @param {unknown} value
|
|
34
51
|
* @returns {boolean}
|
|
@@ -101,17 +118,24 @@ function getRalphIteration(opts, id) {
|
|
|
101
118
|
/**
|
|
102
119
|
* @param {Record<string, unknown>} raw
|
|
103
120
|
*/
|
|
104
|
-
function resolveRetryConfig(raw) {
|
|
121
|
+
function resolveRetryConfig(raw, isAgent = false) {
|
|
105
122
|
const noRetry = Boolean(raw.noRetry);
|
|
106
123
|
const continueOnFail = Boolean(raw.continueOnFail);
|
|
107
124
|
const hasExplicitRetries = typeof raw.retries === "number" && !Number.isNaN(raw.retries);
|
|
108
125
|
const hasExplicitRetryPolicy = Boolean(raw.retryPolicy && typeof raw.retryPolicy === "object");
|
|
109
126
|
const defaultNoRetryForContinueOnFail = continueOnFail && !hasExplicitRetries && !hasExplicitRetryPolicy;
|
|
110
|
-
|
|
127
|
+
// Agent tasks (CLI agents like Codex/Claude) can hit transient upstream
|
|
128
|
+
// failures (e.g. "thread <uuid> not found"). Give them at least one free
|
|
129
|
+
// retry by default — even when the user opted into continueOnFail —
|
|
130
|
+
// unless they explicitly requested noRetry.
|
|
131
|
+
const baseRetries = noRetry
|
|
111
132
|
? 0
|
|
112
|
-
:
|
|
113
|
-
?
|
|
114
|
-
:
|
|
133
|
+
: defaultNoRetryForContinueOnFail
|
|
134
|
+
? (isAgent ? 1 : 0)
|
|
135
|
+
: hasExplicitRetries
|
|
136
|
+
? /** @type {number} */ (raw.retries)
|
|
137
|
+
: Infinity;
|
|
138
|
+
const retries = baseRetries;
|
|
115
139
|
const retryPolicy = hasExplicitRetryPolicy
|
|
116
140
|
? /** @type {import("../RetryPolicy.ts").RetryPolicy} */ (raw.retryPolicy)
|
|
117
141
|
: retries > 0
|
|
@@ -404,7 +428,13 @@ export function extractFromHost(root, opts) {
|
|
|
404
428
|
prompt: undefined,
|
|
405
429
|
staticPayload: undefined,
|
|
406
430
|
computeFn: async () => {
|
|
407
|
-
const
|
|
431
|
+
const [
|
|
432
|
+
{ executeSandbox },
|
|
433
|
+
{ executeChildWorkflow },
|
|
434
|
+
] = await Promise.all([
|
|
435
|
+
loadRuntimeModule("@smithers-orchestrator/sandbox/execute"),
|
|
436
|
+
loadRuntimeModule("@smithers-orchestrator/engine/child-workflow"),
|
|
437
|
+
]);
|
|
408
438
|
if (!workflowDef) {
|
|
409
439
|
throw new SmithersError("INVALID_INPUT", `Sandbox ${nodeId} is missing workflow definition.`, { nodeId });
|
|
410
440
|
}
|
|
@@ -417,6 +447,7 @@ export function extractFromHost(root, opts) {
|
|
|
417
447
|
? runtime
|
|
418
448
|
: "bubblewrap",
|
|
419
449
|
workflow: workflowDef,
|
|
450
|
+
executeChildWorkflow,
|
|
420
451
|
input: raw.__smithersSandboxInput ?? raw.input,
|
|
421
452
|
rootDir: topWorktree?.path ?? process.cwd(),
|
|
422
453
|
allowNetwork: Boolean(raw.allowNetwork),
|
|
@@ -703,14 +734,14 @@ export function extractFromHost(root, opts) {
|
|
|
703
734
|
}
|
|
704
735
|
: undefined;
|
|
705
736
|
const skipIf = Boolean(raw.skipIf);
|
|
706
|
-
const
|
|
737
|
+
const agent = raw.agent;
|
|
738
|
+
const kind = raw.__smithersKind;
|
|
739
|
+
const isAgent = kind === "agent" || Boolean(agent);
|
|
740
|
+
const { retries, retryPolicy } = resolveRetryConfig(raw, isAgent);
|
|
707
741
|
const timeoutMs = typeof raw.timeoutMs === "number" ? raw.timeoutMs : null;
|
|
708
742
|
const parsedHeartbeatTimeoutMs = parseHeartbeatTimeoutMs(raw);
|
|
709
743
|
const continueOnFail = Boolean(raw.continueOnFail);
|
|
710
744
|
const cachePolicy = raw.cache && typeof raw.cache === "object" ? raw.cache : undefined;
|
|
711
|
-
const agent = raw.agent;
|
|
712
|
-
const kind = raw.__smithersKind;
|
|
713
|
-
const isAgent = kind === "agent" || Boolean(agent);
|
|
714
745
|
const heartbeatTimeoutMs = parsedHeartbeatTimeoutMs ??
|
|
715
746
|
(isAgent ? DEFAULT_LOCAL_TASK_HEARTBEAT_TIMEOUT_MS : null);
|
|
716
747
|
const prompt = isAgent ? String(raw.children ?? "") : undefined;
|
|
@@ -761,6 +792,8 @@ export function extractFromHost(root, opts) {
|
|
|
761
792
|
heartbeatTimeoutMs,
|
|
762
793
|
continueOnFail,
|
|
763
794
|
cachePolicy,
|
|
795
|
+
hijack: Boolean(raw.hijack),
|
|
796
|
+
onHijackExit: raw.onHijackExit === "reopen" ? "reopen" : "complete",
|
|
764
797
|
agent,
|
|
765
798
|
prompt,
|
|
766
799
|
staticPayload,
|
package/src/extract.js
CHANGED
|
@@ -8,8 +8,25 @@ import { SmithersError } from "@smithers-orchestrator/errors/SmithersError";
|
|
|
8
8
|
|
|
9
9
|
const DEFAULT_MERGE_QUEUE_CONCURRENCY = 1;
|
|
10
10
|
const WORKTREE_EMPTY_PATH_ERROR = "<Worktree> requires a non-empty path prop";
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
// Default per-task heartbeat timeout. 10 min is the floor for agent-backed
|
|
12
|
+
// tasks: LLM CLIs (claude, codex, gemini, kimi) can sit silent for several
|
|
13
|
+
// minutes during long deliberation or large-context reads. The previous
|
|
14
|
+
// 5-minute default killed reviewer agents mid-review and broke ValidationLoop.
|
|
15
|
+
// Overridable at runtime via SMITHERS_TASK_HEARTBEAT_MS.
|
|
16
|
+
const HEARTBEAT_DEFAULT_MS = 600_000;
|
|
17
|
+
function envHeartbeatTimeoutMs() {
|
|
18
|
+
const raw = typeof process !== "undefined" && process?.env
|
|
19
|
+
? process.env.SMITHERS_TASK_HEARTBEAT_MS
|
|
20
|
+
: undefined;
|
|
21
|
+
if (typeof raw !== "string" || raw.length === 0)
|
|
22
|
+
return HEARTBEAT_DEFAULT_MS;
|
|
23
|
+
const parsed = Number(raw);
|
|
24
|
+
if (!Number.isFinite(parsed) || parsed <= 0)
|
|
25
|
+
return HEARTBEAT_DEFAULT_MS;
|
|
26
|
+
return Math.floor(parsed);
|
|
27
|
+
}
|
|
28
|
+
const DEFAULT_LOCAL_TASK_HEARTBEAT_TIMEOUT_MS = envHeartbeatTimeoutMs();
|
|
29
|
+
const DEFAULT_SANDBOX_TASK_HEARTBEAT_TIMEOUT_MS = envHeartbeatTimeoutMs();
|
|
13
30
|
/**
|
|
14
31
|
* @param {string} prefix
|
|
15
32
|
* @param {readonly number[]} path
|
|
@@ -102,17 +119,23 @@ function parseHeartbeatTimeoutMs(raw) {
|
|
|
102
119
|
/**
|
|
103
120
|
* @param {Record<string, unknown>} raw
|
|
104
121
|
*/
|
|
105
|
-
function resolveRetryConfig(raw) {
|
|
122
|
+
function resolveRetryConfig(raw, isAgent = false) {
|
|
106
123
|
const noRetry = Boolean(raw.noRetry);
|
|
107
124
|
const continueOnFail = Boolean(raw.continueOnFail);
|
|
108
125
|
const hasExplicitRetries = typeof raw.retries === "number" && !Number.isNaN(raw.retries);
|
|
109
126
|
const hasExplicitRetryPolicy = Boolean(raw.retryPolicy && typeof raw.retryPolicy === "object");
|
|
110
127
|
const defaultNoRetryForContinueOnFail = continueOnFail && !hasExplicitRetries && !hasExplicitRetryPolicy;
|
|
111
|
-
|
|
128
|
+
// Agent tasks (CLI agents like Codex/Claude) can hit transient upstream
|
|
129
|
+
// failures (e.g. "thread <uuid> not found"). Give them at least one free
|
|
130
|
+
// retry by default — even when the user opted into continueOnFail —
|
|
131
|
+
// unless they explicitly requested noRetry.
|
|
132
|
+
const retries = noRetry
|
|
112
133
|
? 0
|
|
113
|
-
:
|
|
114
|
-
?
|
|
115
|
-
:
|
|
134
|
+
: defaultNoRetryForContinueOnFail
|
|
135
|
+
? (isAgent ? 1 : 0)
|
|
136
|
+
: hasExplicitRetries
|
|
137
|
+
? raw.retries
|
|
138
|
+
: Infinity;
|
|
116
139
|
const retryPolicy = hasExplicitRetryPolicy
|
|
117
140
|
? raw.retryPolicy
|
|
118
141
|
: retries > 0
|
|
@@ -538,9 +561,9 @@ export function extractGraph(root, opts) {
|
|
|
538
561
|
raw.approvalOnDeny === "fail"
|
|
539
562
|
? raw.approvalOnDeny
|
|
540
563
|
: undefined;
|
|
541
|
-
const { retries, retryPolicy } = resolveRetryConfig(raw);
|
|
542
564
|
const kind = raw.__smithersKind;
|
|
543
565
|
const isAgent = kind === "agent" || Boolean(raw.agent);
|
|
566
|
+
const { retries, retryPolicy } = resolveRetryConfig(raw, isAgent);
|
|
544
567
|
const isCompute = kind === "compute" && typeof raw.__smithersComputeFn === "function";
|
|
545
568
|
const parsedHeartbeatTimeoutMs = parseHeartbeatTimeoutMs(raw);
|
|
546
569
|
const heartbeatTimeoutMs = parsedHeartbeatTimeoutMs ??
|
|
@@ -569,6 +592,8 @@ export function extractGraph(root, opts) {
|
|
|
569
592
|
cachePolicy: raw.cache && typeof raw.cache === "object"
|
|
570
593
|
? raw.cache
|
|
571
594
|
: undefined,
|
|
595
|
+
hijack: Boolean(raw.hijack),
|
|
596
|
+
onHijackExit: raw.onHijackExit === "reopen" ? "reopen" : "complete",
|
|
572
597
|
agent: raw.agent,
|
|
573
598
|
prompt,
|
|
574
599
|
staticPayload: isAgent || isCompute
|
package/src/index.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { AgentCapabilityRegistry } from '@smithers-orchestrator/agents/capability-registry';
|
|
3
2
|
|
|
4
3
|
type XmlNode$1 = XmlElement$1 | XmlText$1;
|
|
5
4
|
type XmlElement$1 = {
|
|
@@ -42,7 +41,18 @@ type CachePolicy$1<Ctx = unknown> = {
|
|
|
42
41
|
type AgentLike$1 = {
|
|
43
42
|
id?: string;
|
|
44
43
|
tools?: Record<string, unknown>;
|
|
45
|
-
capabilities?:
|
|
44
|
+
capabilities?: {
|
|
45
|
+
version: 1;
|
|
46
|
+
engine: string;
|
|
47
|
+
runtimeTools: Record<string, {
|
|
48
|
+
description?: string;
|
|
49
|
+
source?: string;
|
|
50
|
+
}>;
|
|
51
|
+
mcp: Record<string, unknown>;
|
|
52
|
+
skills: Record<string, unknown>;
|
|
53
|
+
humanInteraction: Record<string, unknown>;
|
|
54
|
+
builtIns: string[];
|
|
55
|
+
};
|
|
46
56
|
generate: (args: unknown) => Promise<unknown>;
|
|
47
57
|
};
|
|
48
58
|
type ScoreResult$1 = {
|
|
@@ -138,6 +148,8 @@ type TaskDescriptor$1 = {
|
|
|
138
148
|
heartbeatTimeoutMs: number | null;
|
|
139
149
|
continueOnFail: boolean;
|
|
140
150
|
cachePolicy?: CachePolicy$1;
|
|
151
|
+
hijack?: boolean;
|
|
152
|
+
onHijackExit?: "complete" | "reopen";
|
|
141
153
|
agent?: AgentLike$1 | AgentLike$1[];
|
|
142
154
|
prompt?: string;
|
|
143
155
|
staticPayload?: unknown;
|
package/src/types.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { z } from "zod";
|
|
2
|
-
import type { AgentCapabilityRegistry } from "@smithers-orchestrator/agents/capability-registry";
|
|
3
2
|
|
|
4
3
|
export type XmlNode = XmlElement | XmlText;
|
|
5
4
|
|
|
@@ -51,7 +50,15 @@ export type CachePolicy<Ctx = unknown> = {
|
|
|
51
50
|
export type AgentLike = {
|
|
52
51
|
id?: string;
|
|
53
52
|
tools?: Record<string, unknown>;
|
|
54
|
-
capabilities?:
|
|
53
|
+
capabilities?: {
|
|
54
|
+
version: 1;
|
|
55
|
+
engine: string;
|
|
56
|
+
runtimeTools: Record<string, { description?: string; source?: string }>;
|
|
57
|
+
mcp: Record<string, unknown>;
|
|
58
|
+
skills: Record<string, unknown>;
|
|
59
|
+
humanInteraction: Record<string, unknown>;
|
|
60
|
+
builtIns: string[];
|
|
61
|
+
};
|
|
55
62
|
generate: (args: unknown) => Promise<unknown>;
|
|
56
63
|
};
|
|
57
64
|
|
|
@@ -155,6 +162,8 @@ export type TaskDescriptor = {
|
|
|
155
162
|
heartbeatTimeoutMs: number | null;
|
|
156
163
|
continueOnFail: boolean;
|
|
157
164
|
cachePolicy?: CachePolicy;
|
|
165
|
+
hijack?: boolean;
|
|
166
|
+
onHijackExit?: "complete" | "reopen";
|
|
158
167
|
agent?: AgentLike | AgentLike[];
|
|
159
168
|
prompt?: string;
|
|
160
169
|
staticPayload?: unknown;
|