@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-B9z5GziX.js";
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
- const started = await this.start(pending.launch, pending.runId);
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
- await this.start(launch, runId);
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.runner(launch, runId, signal);
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
- thrown = error;
1855
- terminalMessage = error.message;
1856
- terminalPhase = signal.aborted ? "CANCELLED" : /(drift|ownership|blocked|inconclusive|unsafe|conflict|escape|checkpoint|tampered|stale)/i.test(terminalMessage) ? "BLOCKED" : "FAILED";
1977
+ return {
1978
+ ok: false,
1979
+ error
1980
+ };
1857
1981
  }
1858
- const latest = await this.store.readManifest(launch.sessionId, runId);
1859
- latest.phase = terminalPhase;
1860
- latest.terminal = true;
1861
- await this.store.writeManifest(latest);
1862
- this.recordEvent("phase", {
1863
- runId,
1864
- phase: terminalPhase,
1865
- message: terminalMessage
1866
- });
1867
- this.append(launch.agent.session, "planx/run-terminal", {
1868
- runId,
1869
- phase: terminalPhase,
1870
- ...terminalMessage ? { message: terminalMessage } : {},
1871
- at: (/* @__PURE__ */ new Date()).toISOString()
1872
- });
1873
- if (thrown !== void 0) throw thrown;
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 = (/* @__PURE__ */ new Date()).toISOString();
1930
- this.append(this.#agents.get(view.sessionId)?.session, "planx/run-terminal", {
1931
- runId,
1932
- phase,
1933
- message,
1934
- at: view.updatedAt
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
- const currentRoute = (agent) => ({
2751
- provider: agent.options?.provider,
2752
- model: agent.options?.model,
2753
- reasoningEffort: agent.options?.reasoningEffort,
2754
- maxTokens: agent.options?.maxTokens
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-CGs2ndXP.js").then(({ registerRpc }) => {
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
- ...current,
56
+ ...rest,
56
57
  provider: selected.provider,
57
58
  model: selected.model,
58
- reasoningEffort: selected.reasoningEffort,
59
- maxTokens: selected.maxTokens ?? current.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) => {
@@ -1,4 +1,4 @@
1
- import { n as validateFixedRoute } from "./planner-route-B9z5GziX.js";
1
+ import { n as validateFixedRoute } from "./planner-route-Z9MMuA1e.js";
2
2
  //#region src/contract/rpc.ts
3
3
  const RPC_METHODS = [
4
4
  "model-catalog",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gilbertgt/dsh-plan-orchestrator",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Product-grade orchestration layer for DeepSeek Harness native Plan Mode",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",