@gilbertgt/dsh-plan-orchestrator 1.0.0 → 1.0.2
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/lib/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { A as validatePlanArtifact, C as git, D as normalizeOwnedPath, E as extractPlanArtifact, O as parseValidationCommand, S as fullHead, _ as readJson, b as changedPaths, c as trustedIssueTexts, d as hashBytes, f as assertOwnedPaths, g as confined, h as atomicJson, i as ghRepository, k as schedulerPathIdentity, l as assertTrustedReceipts, m as RunStore, n as ghPreflight, o as prepareIssueBranch, p as disjointOwnership, s as remotePr, t as fetchIssue, u as receiptIndex, v as stateRoot, w as repoRoot, x as decodeUtf8Strict, y as assertRepoPathsConfined } from "./github-Cy4Pm35G.js";
|
|
2
|
-
import { n as validateFixedRoute, r as eligibleTransportFailure, t as installPlannerRoute } from "./planner-route-
|
|
2
|
+
import { n as validateFixedRoute, r as eligibleTransportFailure, t as installPlannerRoute } from "./planner-route-Z9MMuA1e.js";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import { execFile } from "node:child_process";
|
|
5
5
|
import { promisify } from "node:util";
|
|
@@ -862,6 +862,23 @@ const defaultRoute = () => ({
|
|
|
862
862
|
mode: "current",
|
|
863
863
|
fallbacks: []
|
|
864
864
|
});
|
|
865
|
+
/**
|
|
866
|
+
* Rebuild one route choice with every optional field present only when it holds
|
|
867
|
+
* a real value. Every DSH boundary this plugin crosses — `Session.append`,
|
|
868
|
+
* subagent descriptor snapshots, `llm.resolveCallConfig` and SDK child options —
|
|
869
|
+
* validates lossless JSON, where an explicitly `undefined` property is rejected
|
|
870
|
+
* while an absent one is fine. An unresolved (inherited) route legitimately has
|
|
871
|
+
* no provider/model yet, so those are omitted too rather than written as
|
|
872
|
+
* `undefined`; `routeChoices` filters such a candidate out before use.
|
|
873
|
+
*/
|
|
874
|
+
function losslessRouteChoice(choice) {
|
|
875
|
+
const route = {};
|
|
876
|
+
if (choice.provider !== void 0) route.provider = choice.provider;
|
|
877
|
+
if (choice.model !== void 0) route.model = choice.model;
|
|
878
|
+
if (choice.reasoningEffort !== void 0) route.reasoningEffort = choice.reasoningEffort;
|
|
879
|
+
if (choice.maxTokens !== void 0) route.maxTokens = choice.maxTokens;
|
|
880
|
+
return route;
|
|
881
|
+
}
|
|
865
882
|
const DEFAULT_SETTINGS = Object.freeze({
|
|
866
883
|
enabled: true,
|
|
867
884
|
roles: {
|
|
@@ -1311,6 +1328,39 @@ var PlannerReadOnlyGuard = class {
|
|
|
1311
1328
|
function isPlanxEvent(event) {
|
|
1312
1329
|
return Boolean(event && typeof event.type === "string" && event.type.startsWith("planx/") && event.data && typeof event.data.runId === "string");
|
|
1313
1330
|
}
|
|
1331
|
+
/**
|
|
1332
|
+
* Reject the payload shapes this plugin is known to produce accidentally.
|
|
1333
|
+
*
|
|
1334
|
+
* This is deliberately NOT a reimplementation of the DSH lossless-JSON
|
|
1335
|
+
* validator: it catches an explicitly `undefined` optional property (the shape
|
|
1336
|
+
* that caused the PREFLIGHT wedge), plus non-finite numbers, non-JSON
|
|
1337
|
+
* primitives, circular references, sparse arrays, symbol keys and non-plain
|
|
1338
|
+
* objects. It does not claim to match every DSH rule. `Session.append` remains
|
|
1339
|
+
* the authoritative boundary; this guard exists only so a bad payload fails
|
|
1340
|
+
* earlier with a path that names the offending field.
|
|
1341
|
+
*/
|
|
1342
|
+
function assertNoUndefinedEventData(value, label) {
|
|
1343
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1344
|
+
const walk = (node, path) => {
|
|
1345
|
+
if (node === void 0) throw new Error(`${label} carries an undefined value at ${path}; omit the optional property instead`);
|
|
1346
|
+
if (typeof node === "number" && !Number.isFinite(node)) throw new Error(`${label} carries a non-finite number at ${path}`);
|
|
1347
|
+
if (typeof node === "bigint" || typeof node === "function" || typeof node === "symbol") throw new Error(`${label} carries a non-JSON ${typeof node} at ${path}`);
|
|
1348
|
+
if (typeof node !== "object" || node === null) return;
|
|
1349
|
+
if (seen.has(node)) throw new Error(`${label} carries a circular reference at ${path}`);
|
|
1350
|
+
seen.add(node);
|
|
1351
|
+
if (Array.isArray(node)) {
|
|
1352
|
+
if (node.length !== Object.keys(node).length) throw new Error(`${label} carries a sparse array at ${path}`);
|
|
1353
|
+
node.forEach((item, index) => walk(item, `${path}[${index}]`));
|
|
1354
|
+
} else {
|
|
1355
|
+
const prototype = Object.getPrototypeOf(node);
|
|
1356
|
+
if (prototype !== Object.prototype && prototype !== null) throw new Error(`${label} carries a non-plain object at ${path}`);
|
|
1357
|
+
if (Object.getOwnPropertySymbols(node).length) throw new Error(`${label} carries a symbol key at ${path}`);
|
|
1358
|
+
for (const [key, item] of Object.entries(node)) walk(item, `${path}.${key}`);
|
|
1359
|
+
}
|
|
1360
|
+
seen.delete(node);
|
|
1361
|
+
};
|
|
1362
|
+
walk(value, label);
|
|
1363
|
+
}
|
|
1314
1364
|
function reducePlanxEvents(sessionId, events) {
|
|
1315
1365
|
const rows = /* @__PURE__ */ new Map();
|
|
1316
1366
|
for (const event of events) {
|
|
@@ -1523,6 +1573,28 @@ async function cleanupRunWorktrees(repo, runId, root = stateRoot()) {
|
|
|
1523
1573
|
}
|
|
1524
1574
|
//#endregion
|
|
1525
1575
|
//#region src/orchestration/service.ts
|
|
1576
|
+
const TERMINAL_PHASES = /* @__PURE__ */ new Set([
|
|
1577
|
+
"COMPLETE",
|
|
1578
|
+
"BLOCKED",
|
|
1579
|
+
"FAILED",
|
|
1580
|
+
"INTERRUPTED",
|
|
1581
|
+
"CANCELLED"
|
|
1582
|
+
]);
|
|
1583
|
+
/** Runner errors matching this shape are safety boundaries, not plain faults. */
|
|
1584
|
+
const BLOCKING_FAILURE = /(drift|ownership|blocked|inconclusive|unsafe|conflict|escape|checkpoint|tampered|stale)/i;
|
|
1585
|
+
function failureMessage(error) {
|
|
1586
|
+
if (error instanceof Error && error.message) return error.message;
|
|
1587
|
+
if (typeof error === "string" && error) return error;
|
|
1588
|
+
try {
|
|
1589
|
+
const encoded = JSON.stringify(error);
|
|
1590
|
+
if (encoded && encoded !== "{}" && encoded !== "null") return encoded;
|
|
1591
|
+
} catch {}
|
|
1592
|
+
return "unknown orchestration failure";
|
|
1593
|
+
}
|
|
1594
|
+
function terminalPhaseFor(aborted, message) {
|
|
1595
|
+
if (aborted) return "CANCELLED";
|
|
1596
|
+
return BLOCKING_FAILURE.test(message) ? "BLOCKED" : "FAILED";
|
|
1597
|
+
}
|
|
1526
1598
|
var OrchestrationService = class {
|
|
1527
1599
|
store;
|
|
1528
1600
|
runner;
|
|
@@ -1618,7 +1690,7 @@ var OrchestrationService = class {
|
|
|
1618
1690
|
this.append(session, "planx/run-phase", {
|
|
1619
1691
|
runId,
|
|
1620
1692
|
phase: view.phase,
|
|
1621
|
-
message: data.message,
|
|
1693
|
+
...data.message !== void 0 ? { message: String(data.message) } : {},
|
|
1622
1694
|
reviewRound: view.reviewRound,
|
|
1623
1695
|
at: now
|
|
1624
1696
|
});
|
|
@@ -1704,7 +1776,15 @@ var OrchestrationService = class {
|
|
|
1704
1776
|
return false;
|
|
1705
1777
|
}
|
|
1706
1778
|
if (pending.cancelled || this.#pending.get(sessionId) !== pending) return false;
|
|
1707
|
-
|
|
1779
|
+
let started;
|
|
1780
|
+
try {
|
|
1781
|
+
started = await this.start(pending.launch, pending.runId);
|
|
1782
|
+
} catch (error) {
|
|
1783
|
+
const message = `maintenance launch failed: ${failureMessage(error)}`;
|
|
1784
|
+
if (this.#pending.get(sessionId) === pending) this.#pending.delete(sessionId);
|
|
1785
|
+
await this.finalize(pending.launch, pending.runId, terminalPhaseFor(false, message), message).catch(() => {});
|
|
1786
|
+
return false;
|
|
1787
|
+
}
|
|
1708
1788
|
if (pending.cancelled) {
|
|
1709
1789
|
if (this.#pending.get(sessionId) === pending) this.#pending.delete(sessionId);
|
|
1710
1790
|
return false;
|
|
@@ -1793,7 +1873,15 @@ var OrchestrationService = class {
|
|
|
1793
1873
|
message: `Safe resume from ${diagnosis.resumeFrom}`,
|
|
1794
1874
|
at: now
|
|
1795
1875
|
});
|
|
1796
|
-
|
|
1876
|
+
try {
|
|
1877
|
+
await this.start(launch, runId);
|
|
1878
|
+
} catch (error) {
|
|
1879
|
+
await this.finalize(launch, runId, terminalPhaseFor(false, failureMessage(error)), failureMessage(error));
|
|
1880
|
+
return {
|
|
1881
|
+
ok: false,
|
|
1882
|
+
reason: failureMessage(error)
|
|
1883
|
+
};
|
|
1884
|
+
}
|
|
1797
1885
|
return {
|
|
1798
1886
|
ok: true,
|
|
1799
1887
|
resumeFrom: diagnosis.resumeFrom
|
|
@@ -1837,40 +1925,103 @@ var OrchestrationService = class {
|
|
|
1837
1925
|
return true;
|
|
1838
1926
|
}
|
|
1839
1927
|
async run(launch, runId, signal) {
|
|
1840
|
-
const initial = await this.store.readManifest(launch.sessionId, runId);
|
|
1841
|
-
initial.phase = launch.resumeFrom ?? "PREFLIGHT";
|
|
1842
|
-
initial.terminal = false;
|
|
1843
|
-
await this.store.writeManifest(initial);
|
|
1844
|
-
this.recordEvent("phase", {
|
|
1845
|
-
runId,
|
|
1846
|
-
phase: initial.phase
|
|
1847
|
-
});
|
|
1848
1928
|
let terminalPhase = "COMPLETE";
|
|
1849
1929
|
let terminalMessage;
|
|
1850
1930
|
let thrown;
|
|
1931
|
+
let failed = false;
|
|
1932
|
+
try {
|
|
1933
|
+
try {
|
|
1934
|
+
const initial = await this.store.readManifest(launch.sessionId, runId);
|
|
1935
|
+
initial.phase = launch.resumeFrom ?? "PREFLIGHT";
|
|
1936
|
+
initial.terminal = false;
|
|
1937
|
+
await this.store.writeManifest(initial);
|
|
1938
|
+
this.recordEvent("phase", {
|
|
1939
|
+
runId,
|
|
1940
|
+
phase: initial.phase
|
|
1941
|
+
});
|
|
1942
|
+
} catch (error) {
|
|
1943
|
+
thrown = error;
|
|
1944
|
+
failed = true;
|
|
1945
|
+
terminalMessage = failureMessage(error);
|
|
1946
|
+
terminalPhase = terminalPhaseFor(signal.aborted, terminalMessage);
|
|
1947
|
+
}
|
|
1948
|
+
if (!failed) try {
|
|
1949
|
+
await this.runner(launch, runId, signal);
|
|
1950
|
+
if (signal.aborted && terminalPhase === "COMPLETE") {
|
|
1951
|
+
terminalMessage = "run aborted before completion";
|
|
1952
|
+
terminalPhase = "CANCELLED";
|
|
1953
|
+
}
|
|
1954
|
+
} catch (error) {
|
|
1955
|
+
thrown = error;
|
|
1956
|
+
failed = true;
|
|
1957
|
+
terminalMessage = failureMessage(error);
|
|
1958
|
+
terminalPhase = terminalPhaseFor(signal.aborted, terminalMessage);
|
|
1959
|
+
}
|
|
1960
|
+
} finally {
|
|
1961
|
+
await this.finalize(launch, runId, terminalPhase, terminalMessage);
|
|
1962
|
+
}
|
|
1963
|
+
if (failed) throw thrown;
|
|
1964
|
+
}
|
|
1965
|
+
/**
|
|
1966
|
+
* Best-effort terminal manifest write. Returns the failure instead of
|
|
1967
|
+
* throwing so the caller can decide how to converge.
|
|
1968
|
+
*/
|
|
1969
|
+
async writeTerminal(launch, runId, phase) {
|
|
1851
1970
|
try {
|
|
1852
|
-
await this.
|
|
1971
|
+
const latest = await this.store.readManifest(launch.sessionId, runId);
|
|
1972
|
+
latest.phase = phase;
|
|
1973
|
+
latest.terminal = true;
|
|
1974
|
+
await this.store.writeManifest(latest);
|
|
1975
|
+
return { ok: true };
|
|
1853
1976
|
} catch (error) {
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1977
|
+
return {
|
|
1978
|
+
ok: false,
|
|
1979
|
+
error
|
|
1980
|
+
};
|
|
1857
1981
|
}
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1982
|
+
}
|
|
1983
|
+
/**
|
|
1984
|
+
* Persist the terminal state. Never throws: a session whose append is
|
|
1985
|
+
* unavailable must still not be left with a non-terminal manifest.
|
|
1986
|
+
*/
|
|
1987
|
+
async finalize(launch, runId, phase, message) {
|
|
1988
|
+
if (!TERMINAL_PHASES.has(phase)) throw new Error(`plan-orchestrator refusing non-terminal finalize phase: ${phase}`);
|
|
1989
|
+
const at = (/* @__PURE__ */ new Date()).toISOString();
|
|
1990
|
+
let effectivePhase = phase;
|
|
1991
|
+
let effectiveMessage = message;
|
|
1992
|
+
const written = await this.writeTerminal(launch, runId, effectivePhase);
|
|
1993
|
+
if (!written.ok) {
|
|
1994
|
+
effectivePhase = "FAILED";
|
|
1995
|
+
effectiveMessage = `terminal manifest persistence failed: ${failureMessage(written.error)}`;
|
|
1996
|
+
const retry = await this.writeTerminal(launch, runId, effectivePhase);
|
|
1997
|
+
if (!retry.ok) effectiveMessage = `${effectiveMessage}; retry failed: ${failureMessage(retry.error)}`;
|
|
1998
|
+
try {
|
|
1999
|
+
launch.agent?.session?.append?.("planx/finalize-error", {
|
|
2000
|
+
runId,
|
|
2001
|
+
phase: effectivePhase,
|
|
2002
|
+
message: effectiveMessage,
|
|
2003
|
+
at
|
|
2004
|
+
});
|
|
2005
|
+
} catch {}
|
|
2006
|
+
try {
|
|
2007
|
+
this.failView(runId, "FAILED", effectiveMessage, at);
|
|
2008
|
+
} catch {}
|
|
2009
|
+
}
|
|
2010
|
+
try {
|
|
2011
|
+
this.recordEvent("phase", {
|
|
2012
|
+
runId,
|
|
2013
|
+
phase: effectivePhase,
|
|
2014
|
+
...effectiveMessage !== void 0 ? { message: effectiveMessage } : {}
|
|
2015
|
+
});
|
|
2016
|
+
} catch {}
|
|
2017
|
+
try {
|
|
2018
|
+
this.append(launch.agent.session, "planx/run-terminal", {
|
|
2019
|
+
runId,
|
|
2020
|
+
phase: effectivePhase,
|
|
2021
|
+
...effectiveMessage ? { message: effectiveMessage } : {},
|
|
2022
|
+
at
|
|
2023
|
+
});
|
|
2024
|
+
} catch {}
|
|
1874
2025
|
}
|
|
1875
2026
|
async persistApproval(launch, runId, now) {
|
|
1876
2027
|
const dir = this.store.runDir(launch.sessionId, runId);
|
|
@@ -1918,21 +2069,24 @@ var OrchestrationService = class {
|
|
|
1918
2069
|
}
|
|
1919
2070
|
append(session, type, data) {
|
|
1920
2071
|
if (!session || typeof session.append !== "function") throw new Error(`plan-orchestrator cannot persist ${type}: session append unavailable`);
|
|
2072
|
+
assertNoUndefinedEventData(data, type);
|
|
1921
2073
|
session.append(type, data);
|
|
1922
2074
|
}
|
|
1923
|
-
failView(runId, phase, message) {
|
|
2075
|
+
failView(runId, phase, message, at = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
1924
2076
|
const view = this.#views.get(runId);
|
|
1925
2077
|
if (!view) return;
|
|
1926
2078
|
view.phase = phase;
|
|
1927
2079
|
view.status = phase;
|
|
1928
2080
|
view.message = message;
|
|
1929
|
-
view.updatedAt =
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
2081
|
+
view.updatedAt = at;
|
|
2082
|
+
try {
|
|
2083
|
+
this.append(this.#agents.get(view.sessionId)?.session, "planx/run-terminal", {
|
|
2084
|
+
runId,
|
|
2085
|
+
phase,
|
|
2086
|
+
message,
|
|
2087
|
+
at
|
|
2088
|
+
});
|
|
2089
|
+
} catch {}
|
|
1936
2090
|
}
|
|
1937
2091
|
async latestReview(dir, round) {
|
|
1938
2092
|
for (let index = round; index >= 0; index--) {
|
|
@@ -2122,14 +2276,18 @@ function buildTaskPacket(plan, task, dependencyHandoff = []) {
|
|
|
2122
2276
|
}
|
|
2123
2277
|
//#endregion
|
|
2124
2278
|
//#region src/contract/role-result.ts
|
|
2279
|
+
/**
|
|
2280
|
+
* Model-facing structured-output schema. DeepSeek Harness intentionally accepts
|
|
2281
|
+
* only a constrained raw JSON Schema vocabulary for subagent outputSchema.
|
|
2282
|
+
* Size/count limits therefore stay in validateRoleResult(), where they are
|
|
2283
|
+
* enforced after structured output is returned instead of being expressed with
|
|
2284
|
+
* unsupported maxLength/maxItems keywords here.
|
|
2285
|
+
*/
|
|
2125
2286
|
const ROLE_RESULT_SCHEMA = {
|
|
2126
2287
|
type: "object",
|
|
2127
2288
|
additionalProperties: false,
|
|
2128
2289
|
properties: {
|
|
2129
|
-
taskId: {
|
|
2130
|
-
type: "string",
|
|
2131
|
-
maxLength: 200
|
|
2132
|
-
},
|
|
2290
|
+
taskId: { type: "string" },
|
|
2133
2291
|
status: {
|
|
2134
2292
|
type: "string",
|
|
2135
2293
|
enum: [
|
|
@@ -2140,11 +2298,7 @@ const ROLE_RESULT_SCHEMA = {
|
|
|
2140
2298
|
},
|
|
2141
2299
|
changed: {
|
|
2142
2300
|
type: "array",
|
|
2143
|
-
items: {
|
|
2144
|
-
type: "string",
|
|
2145
|
-
maxLength: 512
|
|
2146
|
-
},
|
|
2147
|
-
maxItems: 200
|
|
2301
|
+
items: { type: "string" }
|
|
2148
2302
|
},
|
|
2149
2303
|
validation: {
|
|
2150
2304
|
type: "array",
|
|
@@ -2152,10 +2306,7 @@ const ROLE_RESULT_SCHEMA = {
|
|
|
2152
2306
|
type: "object",
|
|
2153
2307
|
additionalProperties: false,
|
|
2154
2308
|
properties: {
|
|
2155
|
-
id: {
|
|
2156
|
-
type: "string",
|
|
2157
|
-
maxLength: 200
|
|
2158
|
-
},
|
|
2309
|
+
id: { type: "string" },
|
|
2159
2310
|
status: {
|
|
2160
2311
|
type: "string",
|
|
2161
2312
|
enum: [
|
|
@@ -2164,30 +2315,18 @@ const ROLE_RESULT_SCHEMA = {
|
|
|
2164
2315
|
"INCONCLUSIVE"
|
|
2165
2316
|
]
|
|
2166
2317
|
},
|
|
2167
|
-
detail: {
|
|
2168
|
-
type: "string",
|
|
2169
|
-
maxLength: 2e3
|
|
2170
|
-
}
|
|
2318
|
+
detail: { type: "string" }
|
|
2171
2319
|
},
|
|
2172
2320
|
required: ["id", "status"]
|
|
2173
|
-
}
|
|
2174
|
-
maxItems: 30
|
|
2321
|
+
}
|
|
2175
2322
|
},
|
|
2176
2323
|
remaining: {
|
|
2177
2324
|
type: "array",
|
|
2178
|
-
items: {
|
|
2179
|
-
type: "string",
|
|
2180
|
-
maxLength: 1e3
|
|
2181
|
-
},
|
|
2182
|
-
maxItems: 30
|
|
2325
|
+
items: { type: "string" }
|
|
2183
2326
|
},
|
|
2184
2327
|
contextExpansion: {
|
|
2185
2328
|
type: "array",
|
|
2186
|
-
items: {
|
|
2187
|
-
type: "string",
|
|
2188
|
-
maxLength: 1e3
|
|
2189
|
-
},
|
|
2190
|
-
maxItems: 30
|
|
2329
|
+
items: { type: "string" }
|
|
2191
2330
|
}
|
|
2192
2331
|
},
|
|
2193
2332
|
required: [
|
|
@@ -2607,9 +2746,9 @@ function routeChoices(route, current) {
|
|
|
2607
2746
|
return [route.mode === "current" ? current : {
|
|
2608
2747
|
provider: route.provider,
|
|
2609
2748
|
model: route.model,
|
|
2610
|
-
reasoningEffort: route.reasoningEffort,
|
|
2611
|
-
maxTokens: route.maxTokens
|
|
2612
|
-
}, ...route.fallbacks].filter((v, i, a) => v.provider && v.model && a.findIndex((x) => x.provider === v.provider && x.model === v.model && x.reasoningEffort === v.reasoningEffort) === i);
|
|
2749
|
+
...route.reasoningEffort !== void 0 ? { reasoningEffort: route.reasoningEffort } : {},
|
|
2750
|
+
...route.maxTokens !== void 0 ? { maxTokens: route.maxTokens } : {}
|
|
2751
|
+
}, ...route.fallbacks].map(losslessRouteChoice).filter((v, i, a) => v.provider && v.model && a.findIndex((x) => x.provider === v.provider && x.model === v.model && x.reasoningEffort === v.reasoningEffort) === i);
|
|
2613
2752
|
}
|
|
2614
2753
|
async function preflightRoute(llm, choice) {
|
|
2615
2754
|
if (typeof llm?.resolveCallConfig !== "function") throw new Error("llm.resolveCallConfig unavailable");
|
|
@@ -2747,12 +2886,25 @@ function aggregateUsage(samples) {
|
|
|
2747
2886
|
//#endregion
|
|
2748
2887
|
//#region src/orchestration/engine.ts
|
|
2749
2888
|
const unionOwnership = (plan) => [...new Set(plan.tasks.flatMap((task) => task.modify))].sort();
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2889
|
+
/**
|
|
2890
|
+
* Absent route fields must not exist at all. An explicit `reasoningEffort:
|
|
2891
|
+
* undefined` is rejected by every DSH lossless-JSON boundary (subagent
|
|
2892
|
+
* descriptors, the session log, SDK child options), so each property is
|
|
2893
|
+
* constructed only when the live agent actually carries a value — including
|
|
2894
|
+
* provider/model, which an unresolved inherited agent legitimately lacks.
|
|
2895
|
+
*/
|
|
2896
|
+
const currentRoute = (agent) => {
|
|
2897
|
+
const provider = agent?.options?.provider;
|
|
2898
|
+
const model = agent?.options?.model;
|
|
2899
|
+
const reasoningEffort = agent?.options?.reasoningEffort;
|
|
2900
|
+
const maxTokens = agent?.options?.maxTokens;
|
|
2901
|
+
return {
|
|
2902
|
+
...provider !== void 0 ? { provider: String(provider) } : {},
|
|
2903
|
+
...model !== void 0 ? { model: String(model) } : {},
|
|
2904
|
+
...reasoningEffort !== void 0 ? { reasoningEffort: String(reasoningEffort) } : {},
|
|
2905
|
+
...maxTokens !== void 0 ? { maxTokens: Number(maxTokens) } : {}
|
|
2906
|
+
};
|
|
2907
|
+
};
|
|
2756
2908
|
async function resolvedChoices(ctx, settings, role, agent) {
|
|
2757
2909
|
const candidates = routeChoices(settings.roles[role], currentRoute(agent));
|
|
2758
2910
|
const good = [];
|
|
@@ -3715,7 +3867,7 @@ function apply(ctx) {
|
|
|
3715
3867
|
c.inject(["connection"], (scope) => scope.effect(() => {
|
|
3716
3868
|
let disposed = false;
|
|
3717
3869
|
let disposeTransport;
|
|
3718
|
-
import("./rpc-server-
|
|
3870
|
+
import("./rpc-server-Bdtz9NqI.js").then(({ registerRpc }) => {
|
|
3719
3871
|
if (disposed) return;
|
|
3720
3872
|
disposeTransport = registerRpc(scope.connection, {
|
|
3721
3873
|
ctx: scope,
|
|
@@ -11,10 +11,10 @@ async function validateFixedRoute(llm, route) {
|
|
|
11
11
|
if (!route.provider || !route.model) throw new Error("fixed route needs provider and model");
|
|
12
12
|
const requested = {
|
|
13
13
|
provider: route.provider,
|
|
14
|
-
model: route.model
|
|
15
|
-
...route.reasoningEffort ? { reasoningEffort: route.reasoningEffort } : {},
|
|
16
|
-
...route.maxTokens ? { maxTokens: route.maxTokens } : {}
|
|
14
|
+
model: route.model
|
|
17
15
|
};
|
|
16
|
+
if (route.reasoningEffort !== void 0) requested.reasoningEffort = route.reasoningEffort;
|
|
17
|
+
if (route.maxTokens !== void 0) requested.maxTokens = route.maxTokens;
|
|
18
18
|
if (typeof llm?.resolveCallConfig !== "function") throw new Error("DSH llm.resolveCallConfig is unavailable");
|
|
19
19
|
await llm.resolveCallConfig(requested);
|
|
20
20
|
for (const fallback of route.fallbacks) await llm.resolveCallConfig(fallback);
|
|
@@ -27,15 +27,15 @@ function routeAt(route, current, index) {
|
|
|
27
27
|
return {
|
|
28
28
|
provider: current.provider,
|
|
29
29
|
model: current.model,
|
|
30
|
-
reasoningEffort: current.reasoningEffort,
|
|
31
|
-
maxTokens: current.maxTokens
|
|
30
|
+
...current.reasoningEffort !== void 0 ? { reasoningEffort: current.reasoningEffort } : {},
|
|
31
|
+
...current.maxTokens !== void 0 ? { maxTokens: current.maxTokens } : {}
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
34
|
return {
|
|
35
35
|
provider: route.provider,
|
|
36
36
|
model: route.model,
|
|
37
|
-
reasoningEffort: route.reasoningEffort,
|
|
38
|
-
maxTokens: route.maxTokens
|
|
37
|
+
...route.reasoningEffort !== void 0 ? { reasoningEffort: route.reasoningEffort } : {},
|
|
38
|
+
...route.maxTokens !== void 0 ? { maxTokens: route.maxTokens } : {}
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
41
|
return route.fallbacks[index - 1];
|
|
@@ -51,12 +51,13 @@ function installPlannerRoute(ctx, getRoute, isPlanActive, isEnabled = () => true
|
|
|
51
51
|
const selected = routeAt(getRoute(agent), current, fallbackIndex.get(agent) ?? 0);
|
|
52
52
|
if (!selected) return current;
|
|
53
53
|
await ctx.llm.resolveCallConfig(selected);
|
|
54
|
+
const { reasoningEffort: _inheritedEffort, maxTokens: inheritedMaxTokens, ...rest } = current;
|
|
54
55
|
return {
|
|
55
|
-
...
|
|
56
|
+
...rest,
|
|
56
57
|
provider: selected.provider,
|
|
57
58
|
model: selected.model,
|
|
58
|
-
reasoningEffort: selected.reasoningEffort,
|
|
59
|
-
maxTokens: selected.maxTokens
|
|
59
|
+
...selected.reasoningEffort !== void 0 ? { reasoningEffort: selected.reasoningEffort } : {},
|
|
60
|
+
...selected.maxTokens !== void 0 ? { maxTokens: selected.maxTokens } : inheritedMaxTokens !== void 0 ? { maxTokens: inheritedMaxTokens } : {}
|
|
60
61
|
};
|
|
61
62
|
});
|
|
62
63
|
const assembly = ctx.on("system-prompt/assemble", async (assembled, context, next) => {
|