@ordewell/core 0.4.6 → 0.4.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ ALLOWED_PLUGIN_HOSTS: () => ALLOWED_PLUGIN_HOSTS,
33
34
  ALL_PROVIDERS: () => ALL_PROVIDERS,
34
35
  AUTO_COMMANDS: () => AUTO_COMMANDS,
35
36
  AbstractRunner: () => AbstractRunner,
@@ -48,6 +49,8 @@ __export(index_exports, {
48
49
  CommandLineTooLongError: () => CommandLineTooLongError,
49
50
  ConsoleLogger: () => ConsoleLogger,
50
51
  ContextCollector: () => ContextCollector,
52
+ DAEMON_SUBPROTOCOL: () => DAEMON_SUBPROTOCOL,
53
+ DAEMON_TOKEN_SUBPROTOCOL_PREFIX: () => DAEMON_TOKEN_SUBPROTOCOL_PREFIX,
51
54
  DENY_ALL: () => DENY_ALL,
52
55
  EmbeddedNewlineError: () => EmbeddedNewlineError,
53
56
  EnvConfig: () => EnvConfig,
@@ -64,9 +67,12 @@ __export(index_exports, {
64
67
  ModelResolver: () => ModelResolver,
65
68
  OPENCODE_MANIFEST: () => OPENCODE_MANIFEST,
66
69
  ORCHESTRATOR_SHORTCUTS: () => ORCHESTRATOR_SHORTCUTS,
70
+ ORDEWELL_SETTABLE_ENV: () => ORDEWELL_SETTABLE_ENV,
67
71
  OpenAiService: () => OpenAiService,
68
72
  OpenCodeAdapter: () => OpenCodeAdapter,
69
73
  PLAN_ENVELOPE_KEY: () => PLAN_ENVELOPE_KEY,
74
+ PLUGIN_NAME_PATTERN: () => PLUGIN_NAME_PATTERN,
75
+ PROVIDER_CREDENTIAL_ENV: () => PROVIDER_CREDENTIAL_ENV,
70
76
  PROVIDER_DETECT_PRIORITY: () => PROVIDER_DETECT_PRIORITY,
71
77
  PROVIDER_LABEL: () => PROVIDER_LABEL,
72
78
  PROVIDER_PRIORITY: () => PROVIDER_PRIORITY,
@@ -81,6 +87,8 @@ __export(index_exports, {
81
87
  RunnerInstallation: () => RunnerInstallation,
82
88
  RunnerRegistry: () => RunnerRegistry,
83
89
  SEARCH_EXCLUSIONS: () => SEARCH_EXCLUSIONS,
90
+ SETTINGS_ENV_ALLOWLIST: () => SETTINGS_ENV_ALLOWLIST,
91
+ SETTINGS_ENV_REFUSED: () => SETTINGS_ENV_REFUSED,
84
92
  STATE_DIR: () => STATE_DIR,
85
93
  STOPPED_TOOL_RESULT: () => STOPPED_TOOL_RESULT,
86
94
  SYMBOL_LANGUAGES: () => SYMBOL_LANGUAGES,
@@ -93,13 +101,19 @@ __export(index_exports, {
93
101
  TmuxRunner: () => TmuxRunner,
94
102
  VerdictEngine: () => VerdictEngine,
95
103
  WINDOWS_MAX_COMMAND_LINE: () => WINDOWS_MAX_COMMAND_LINE,
104
+ WorkspaceNotAProjectError: () => WorkspaceNotAProjectError,
96
105
  WorkspaceNotFoundError: () => WorkspaceNotFoundError,
97
106
  addTaskToPlan: () => addTaskToPlan,
107
+ admitSettingsEnv: () => admitSettingsEnv,
98
108
  applyHeadLimit: () => applyHeadLimit,
99
109
  applyTaskOps: () => applyTaskOps,
110
+ assertInstallablePluginUrl: () => assertInstallablePluginUrl,
111
+ assertPlainPluginName: () => assertPlainPluginName,
100
112
  assertWorkspaceExists: () => assertWorkspaceExists,
113
+ assertWorkspaceIsProject: () => assertWorkspaceIsProject,
101
114
  augmentPromptWithPriorOutputs: () => augmentPromptWithPriorOutputs,
102
115
  augmentedPath: () => augmentedPath,
116
+ bearerHeaderValue: () => bearerHeaderValue,
103
117
  buildConversationSystemPrompt: () => buildConversationSystemPrompt,
104
118
  buildFallbackGrepArgs: () => buildFallbackGrepArgs,
105
119
  buildGlobArgs: () => buildGlobArgs,
@@ -127,7 +141,9 @@ __export(index_exports, {
127
141
  classifyCommand: () => classifyCommand,
128
142
  classifyOutcome: () => classifyOutcome,
129
143
  classifyPlannerReply: () => classifyPlannerReply,
144
+ classifyPluginSource: () => classifyPluginSource,
130
145
  clearAugmentedPathCache: () => clearAugmentedPathCache,
146
+ clearDaemonToken: () => clearDaemonToken,
131
147
  clearResearchShellCache: () => clearResearchShellCache,
132
148
  clearState: () => clearState,
133
149
  clipboardCopyCommand: () => clipboardCopyCommand,
@@ -139,6 +155,7 @@ __export(index_exports, {
139
155
  createAiService: () => createAiService,
140
156
  createEmptyPlan: () => createEmptyPlan,
141
157
  createTask: () => createTask,
158
+ daemonTokenPath: () => daemonTokenPath,
142
159
  defaultLogger: () => defaultLogger,
143
160
  definitionPattern: () => definitionPattern,
144
161
  deleteSession: () => deleteSession,
@@ -156,6 +173,7 @@ __export(index_exports, {
156
173
  extractObjectWithBalance: () => extractObjectWithBalance,
157
174
  extractObjectsWithKey: () => extractObjectsWithKey,
158
175
  extractPrdBlock: () => extractPrdBlock,
176
+ extractPresentedToken: () => extractPresentedToken,
159
177
  fetchAllProviderModels: () => fetchAllProviderModels,
160
178
  filterFallbackByAnchoredInclude: () => filterFallbackByAnchoredInclude,
161
179
  filterModelsForPrompt: () => filterModelsForPrompt,
@@ -173,6 +191,9 @@ __export(index_exports, {
173
191
  isCliProvider: () => isCliProvider,
174
192
  isExecutableResolved: () => isExecutableResolved,
175
193
  isOpenAiProvider: () => isOpenAiProvider,
194
+ isPlainPluginName: () => isPlainPluginName,
195
+ isReservedRunnerName: () => isReservedRunnerName,
196
+ isValidManifest: () => isValidManifest,
176
197
  killTree: () => killTree,
177
198
  knownModelId: () => knownModelId,
178
199
  languageForId: () => languageForId,
@@ -185,6 +206,8 @@ __export(index_exports, {
185
206
  migrateLegacyPlan: () => migrateLegacyPlan,
186
207
  migratePlanState: () => migratePlanState,
187
208
  migrateTask: () => migrateTask,
209
+ mintDaemonToken: () => mintDaemonToken,
210
+ mintSessionId: () => mintSessionId,
188
211
  modelContextBlock: () => modelContextBlock,
189
212
  modifyValidationFeedback: () => modifyValidationFeedback,
190
213
  normalizeAgentArgs: () => normalizeAgentArgs,
@@ -200,6 +223,7 @@ __export(index_exports, {
200
223
  providerForRunner: () => providerForRunner,
201
224
  reEmitPlanPrompt: () => reEmitPlanPrompt,
202
225
  reEmitTaskOpsPrompt: () => reEmitTaskOpsPrompt,
226
+ readDaemonToken: () => readDaemonToken,
203
227
  referencePattern: () => referencePattern,
204
228
  removeTaskFromPlan: () => removeTaskFromPlan,
205
229
  renderPlanMap: () => renderPlanMap,
@@ -211,6 +235,7 @@ __export(index_exports, {
211
235
  resolveArgs: () => resolveArgs,
212
236
  resolveDefaultMode: () => resolveDefaultMode,
213
237
  resolveModelShortcut: () => resolveModelShortcut,
238
+ resolvePluginInstallDir: () => resolvePluginInstallDir,
214
239
  resolveProvider: () => resolveProvider,
215
240
  resolveProviderFromPrefix: () => resolveProviderFromPrefix,
216
241
  resolveResearchShell: () => resolveResearchShell,
@@ -241,6 +266,8 @@ __export(index_exports, {
241
266
  tmuxSocketName: () => tmuxSocketName,
242
267
  tmuxWindowName: () => tmuxWindowName,
243
268
  toOrchestratorOptions: () => toOrchestratorOptions,
269
+ tokenSubprotocols: () => tokenSubprotocols,
270
+ tokensMatch: () => tokensMatch,
244
271
  truncatedPlanReEmitPrompt: () => truncatedPlanReEmitPrompt,
245
272
  updateTaskInPlan: () => updateTaskInPlan,
246
273
  validateModifiedPlan: () => validateModifiedPlan,
@@ -605,7 +632,6 @@ var AUTO_COMMANDS = [
605
632
  "pwd",
606
633
  "which",
607
634
  "type",
608
- "env",
609
635
  "uname",
610
636
  "whoami",
611
637
  "cut",
@@ -806,6 +832,52 @@ var MULTIPLEXERS = [
806
832
  "flutter",
807
833
  "dart"
808
834
  ];
835
+ var HELP_FLAGS = ["--help", "--version"];
836
+ var WRAPPER_FAMILY = {
837
+ env: {
838
+ valueFlags: ["-u", "--unset", "-C", "--chdir"],
839
+ booleanFlags: [
840
+ "-i",
841
+ "--ignore-environment",
842
+ "-0",
843
+ "--null",
844
+ "-v",
845
+ "--debug",
846
+ // Optional-value flags: see WrapperSpec.booleanFlags.
847
+ "--block-signal",
848
+ "--default-signal",
849
+ "--ignore-signal",
850
+ "--list-signal-handling",
851
+ ...HELP_FLAGS
852
+ ],
853
+ stringFlags: ["-S", "--split-string"],
854
+ acceptsAssignments: true
855
+ },
856
+ nice: {
857
+ valueFlags: ["-n", "--adjustment"],
858
+ booleanFlags: HELP_FLAGS,
859
+ booleanPattern: /^-\d+$/
860
+ },
861
+ timeout: {
862
+ valueFlags: ["-s", "--signal", "-k", "--kill-after"],
863
+ booleanFlags: ["--preserve-status", "--foreground", "-v", "--verbose", ...HELP_FLAGS],
864
+ positionals: 1
865
+ },
866
+ nohup: { booleanFlags: HELP_FLAGS },
867
+ setsid: { booleanFlags: ["-c", "--ctty", "-f", "--fork", "-w", "--wait", "-h", "-V", ...HELP_FLAGS] },
868
+ stdbuf: {
869
+ valueFlags: ["-i", "--input", "-o", "--output", "-e", "--error"],
870
+ booleanFlags: HELP_FLAGS
871
+ },
872
+ ionice: {
873
+ valueFlags: ["-c", "--class", "-n", "--classdata", "-p", "--pid", "-P", "--pgid", "-u", "--uid"],
874
+ booleanFlags: ["-t", "--ignore", "-h", "-V", ...HELP_FLAGS]
875
+ },
876
+ // The multi-call binary ships its own `rm`, `mv` and `sh`, so the refused
877
+ // name arrives as its first argument.
878
+ busybox: { booleanFlags: ["--list", "--install", ...HELP_FLAGS] },
879
+ command: { booleanFlags: ["-p"], noExecFlags: ["-v", "-V"] }
880
+ };
809
881
  var POSIX_DIALECT = {
810
882
  escape: "\\",
811
883
  escapeInQuotes: true,
@@ -1020,10 +1092,92 @@ function binaryName(token, dialect) {
1020
1092
  if (dot <= 0) return base;
1021
1093
  return dialect.strippedExtensions.includes(base.slice(dot).toLowerCase()) ? base.slice(0, dot) : base;
1022
1094
  }
1095
+ var ASSIGNMENT = /^[A-Za-z_][A-Za-z0-9_]*=/;
1023
1096
  function toSegment(tokens, piped, dialect) {
1024
1097
  const rest = [...tokens];
1025
- while (rest.length > 0 && /^[A-Za-z_][A-Za-z0-9_]*=/.test(rest[0])) rest.shift();
1026
- return { binary: rest.length > 0 ? binaryName(rest[0], dialect) : "", args: rest.slice(1), piped, expandable: false };
1098
+ const assignments = [];
1099
+ while (rest.length > 0 && ASSIGNMENT.test(rest[0])) assignments.push(rest.shift());
1100
+ return {
1101
+ binary: rest.length > 0 ? binaryName(rest[0], dialect) : "",
1102
+ args: rest.slice(1),
1103
+ assignments,
1104
+ piped,
1105
+ expandable: false
1106
+ };
1107
+ }
1108
+ function joinedShortFlag(list, token) {
1109
+ if (token.length <= 2) return void 0;
1110
+ return list.find((f) => f.length === 2 && token.startsWith(f));
1111
+ }
1112
+ function scanWrapperArgs(binary, spec, args) {
1113
+ const valueFlags = spec.valueFlags ?? [];
1114
+ const stringFlags = spec.stringFlags ?? [];
1115
+ const assignments = [];
1116
+ let positionals = spec.positionals ?? 0;
1117
+ let i = 0;
1118
+ while (i < args.length) {
1119
+ const token = args[i];
1120
+ if (token === "--") {
1121
+ i++;
1122
+ break;
1123
+ }
1124
+ if (/^-./.test(token)) {
1125
+ const eq = token.startsWith("--") ? token.indexOf("=") : -1;
1126
+ const name = eq > 0 ? token.slice(0, eq) : token;
1127
+ const selfContained = eq > 0;
1128
+ if ((spec.noExecFlags ?? []).includes(name)) return { kind: "no-exec" };
1129
+ if (stringFlags.includes(name) || joinedShortFlag(stringFlags, token)) {
1130
+ return {
1131
+ kind: "refuse",
1132
+ reason: `"${binary} ${name}" splits a string into a command, which this classifier cannot inspect. Use the read-only research tools, or describe it as a task.`
1133
+ };
1134
+ }
1135
+ if ((spec.booleanFlags ?? []).includes(name) || spec.booleanPattern?.test(token)) {
1136
+ i++;
1137
+ continue;
1138
+ }
1139
+ if (valueFlags.includes(name)) {
1140
+ i += selfContained ? 1 : 2;
1141
+ continue;
1142
+ }
1143
+ if (joinedShortFlag(valueFlags, token)) {
1144
+ i++;
1145
+ continue;
1146
+ }
1147
+ return {
1148
+ kind: "refuse",
1149
+ reason: `"${token}" is not a flag this classifier knows on the wrapper "${binary}", so it cannot tell which command "${binary}" would actually run. Re-run without it.`
1150
+ };
1151
+ }
1152
+ if (spec.acceptsAssignments && ASSIGNMENT.test(token)) {
1153
+ assignments.push(token);
1154
+ i++;
1155
+ continue;
1156
+ }
1157
+ if (positionals > 0) {
1158
+ positionals--;
1159
+ i++;
1160
+ continue;
1161
+ }
1162
+ break;
1163
+ }
1164
+ return { kind: "command", tokens: args.slice(i), assignments };
1165
+ }
1166
+ function unwrap(seg, dialect) {
1167
+ const wrappers = [];
1168
+ const assignments = [...seg.assignments];
1169
+ let current = seg;
1170
+ while (WRAPPER_FAMILY[current.binary]) {
1171
+ const scan = scanWrapperArgs(current.binary, WRAPPER_FAMILY[current.binary], current.args);
1172
+ if (scan.kind === "refuse") return { seg: { ...current, assignments }, wrappers, reason: scan.reason };
1173
+ if (scan.kind === "no-exec" || scan.tokens.length === 0) break;
1174
+ assignments.push(...scan.assignments);
1175
+ wrappers.push(current.binary);
1176
+ const inner = toSegment(scan.tokens, current.piped, dialect);
1177
+ assignments.push(...inner.assignments);
1178
+ current = { ...inner, assignments: [], expandable: current.expandable };
1179
+ }
1180
+ return { seg: { ...current, assignments }, wrappers };
1027
1181
  }
1028
1182
  function lexAll(command, dialect) {
1029
1183
  const queue = [];
@@ -1086,6 +1240,16 @@ function refusalFor(seg) {
1086
1240
  return `"git ${sub} ${flag}" mutates refs. You are a read-only planner \u2014 describe the change as a task instead.`;
1087
1241
  }
1088
1242
  }
1243
+ const execPathFlag = seg.args.find((a) => a === "--exec-path" || a.startsWith("--exec-path="));
1244
+ if (execPathFlag) {
1245
+ return `"git ${execPathFlag}" redirects where git looks for its helper programs, and can make it run an arbitrary one. Use the read-only research tools, or describe the change as a task.`;
1246
+ }
1247
+ if (sub === "grep") {
1248
+ const pagerFlag = seg.args.find((a) => a === "-O" || a === "--open-files-in-pager" || a.startsWith("--open-files-in-pager="));
1249
+ if (pagerFlag) {
1250
+ return `"git grep ${pagerFlag}" opens the results in an arbitrary named program. Use the read-only research tools, or describe the change as a task.`;
1251
+ }
1252
+ }
1089
1253
  }
1090
1254
  if (seg.binary === "find") {
1091
1255
  const flag = seg.args.find((a) => ["-exec", "-execdir", "-ok", "-okdir", "-delete"].includes(a));
@@ -1108,8 +1272,17 @@ function refusalFor(seg) {
1108
1272
  return `Inline code via "${seg.binary} ${seg.args.find(isInlineCodeFlag)}" is not available to the planner. Use the read-only research tools, or describe it as a task.`;
1109
1273
  }
1110
1274
  }
1275
+ if (seg.assignments.length > 0) {
1276
+ return `The environment assignment "${seg.assignments[0]}" decides what "${seg.binary}" actually does, and this classifier cannot judge the value. Re-run the command without the assignment.`;
1277
+ }
1111
1278
  return void 0;
1112
1279
  }
1280
+ function withWrapperNote(reason, wrappers) {
1281
+ if (wrappers.length === 0) return reason;
1282
+ const quoted = wrappers.map((w) => `"${w}"`);
1283
+ const names = quoted.length > 1 ? `${quoted.slice(0, -1).join(", ")} and ${quoted[quoted.length - 1]}` : quoted[0];
1284
+ return `${reason} Wrapping it in ${names} does not change what runs.`;
1285
+ }
1113
1286
  function classifyCommand(command, opts = {}) {
1114
1287
  const trimmed = command.trim();
1115
1288
  if (!trimmed) return { tier: "refuse", scope: "", reason: "Empty command." };
@@ -1138,11 +1311,13 @@ function classifyCommand(command, opts = {}) {
1138
1311
  };
1139
1312
  }
1140
1313
  if (segments.length === 0) return { tier: "refuse", scope: "", reason: "No command found." };
1141
- for (const seg of segments) {
1314
+ const unwrapped = segments.map((seg) => unwrap(seg, dialect));
1315
+ for (const { seg, wrappers, reason: wrapperReason } of unwrapped) {
1316
+ if (wrapperReason) return { tier: "refuse", scope: "", reason: wrapperReason };
1142
1317
  const reason = refusalFor(seg);
1143
- if (reason) return { tier: "refuse", scope: "", reason };
1318
+ if (reason) return { tier: "refuse", scope: "", reason: withWrapperNote(reason, wrappers) };
1144
1319
  }
1145
- const nonAuto = segments.filter((s) => !isAuto(s));
1320
+ const nonAuto = unwrapped.map((u) => u.seg).filter((s) => !isAuto(s));
1146
1321
  if (nonAuto.length === 0) return { tier: "auto", scope: "" };
1147
1322
  const scope = [...new Set(nonAuto.map(scopeFor))].sort().join(" + ");
1148
1323
  return { tier: "ask", scope };
@@ -1641,14 +1816,14 @@ var PendingApprovals = class {
1641
1816
  ask(request) {
1642
1817
  const id = `ap-${Date.now()}-${counter++}`;
1643
1818
  const pending = { id, request, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
1644
- return new Promise((resolve3) => {
1819
+ return new Promise((resolve4) => {
1645
1820
  const settle = (granted) => {
1646
1821
  const entry = this.entries.get(id);
1647
1822
  if (!entry) return;
1648
1823
  clearTimeout(entry.timer);
1649
1824
  this.entries.delete(id);
1650
1825
  this.opts.onSettled?.(id, granted);
1651
- resolve3(granted);
1826
+ resolve4(granted);
1652
1827
  };
1653
1828
  const timer = setTimeout(() => settle(false), this.opts.timeoutMs ?? DEFAULT_TIMEOUT_MS);
1654
1829
  timer.unref?.();
@@ -4558,9 +4733,9 @@ function summarizeToolCall(tool, argsJson, toolLabel) {
4558
4733
  const scope = args.include ? ` in ${args.include}` : "";
4559
4734
  return `grep ${ellipsize(String(args.pattern), 40)}${scope}${mode}`;
4560
4735
  }
4561
- const path15 = args.filePath || args.file_path || args.path;
4562
- if (path15) {
4563
- const basename = String(path15).split("/").pop() || String(path15);
4736
+ const path17 = args.filePath || args.file_path || args.path;
4737
+ if (path17) {
4738
+ const basename = String(path17).split("/").pop() || String(path17);
4564
4739
  return `${display} ${basename}`;
4565
4740
  }
4566
4741
  if (args.pattern) {
@@ -7221,28 +7396,28 @@ function posixBinDirs() {
7221
7396
  ];
7222
7397
  }
7223
7398
  function windowsBinDirs(env, home) {
7224
- const join14 = (...parts) => path5.win32.join(...parts);
7225
- const under = (base, ...parts) => base ? [join14(base, ...parts)] : [];
7399
+ const join16 = (...parts) => path5.win32.join(...parts);
7400
+ const under = (base, ...parts) => base ? [join16(base, ...parts)] : [];
7226
7401
  const appData = env.APPDATA;
7227
7402
  const localAppData = env.LOCALAPPDATA;
7228
7403
  const programData = env.ProgramData;
7229
7404
  return [
7230
7405
  // Native per-user installers, PowerShell-driven and otherwise.
7231
- join14(home, ".local", "bin"),
7232
- join14(home, ".opencode", "bin"),
7233
- join14(home, ".claude", "bin"),
7234
- join14(home, ".codex", "bin"),
7406
+ join16(home, ".local", "bin"),
7407
+ join16(home, ".opencode", "bin"),
7408
+ join16(home, ".claude", "bin"),
7409
+ join16(home, ".codex", "bin"),
7235
7410
  ...under(localAppData, "Programs", "claude"),
7236
7411
  // Node package managers. npm's global prefix is `%APPDATA%\npm`; the
7237
7412
  // literal spelling is kept as a fallback for a host that does not export
7238
7413
  // APPDATA (a service account, a stripped CI container).
7239
7414
  ...under(appData, "npm"),
7240
- join14(home, "AppData", "Roaming", "npm"),
7415
+ join16(home, "AppData", "Roaming", "npm"),
7241
7416
  ...under(localAppData, "pnpm"),
7242
7417
  ...under(localAppData, "Yarn", "bin"),
7243
- join14(home, ".bun", "bin"),
7418
+ join16(home, ".bun", "bin"),
7244
7419
  // Windows package managers.
7245
- join14(home, "scoop", "shims"),
7420
+ join16(home, "scoop", "shims"),
7246
7421
  ...under(env.SCOOP, "shims"),
7247
7422
  ...under(env.SCOOP_GLOBAL ?? programData, "scoop", "shims"),
7248
7423
  ...under(programData, "chocolatey", "bin"),
@@ -7252,7 +7427,7 @@ function windowsBinDirs(env, home) {
7252
7427
  ...under(localAppData, "Microsoft", "WindowsApps"),
7253
7428
  // Version managers. Volta's Windows home is not the POSIX `~/.volta`.
7254
7429
  ...under(localAppData, "Volta", "bin"),
7255
- join14(home, ".volta", "bin")
7430
+ join16(home, ".volta", "bin")
7256
7431
  ];
7257
7432
  }
7258
7433
  function wellKnownBinDirs(deps = {}) {
@@ -7553,7 +7728,7 @@ var defaultAppServerClient = async (command, args, options) => {
7553
7728
  const PATH = await augmentedPath();
7554
7729
  const launch = await planDirectLaunch(command, args).catch(() => null);
7555
7730
  if (!launch) return null;
7556
- return new Promise((resolve3) => {
7731
+ return new Promise((resolve4) => {
7557
7732
  let child;
7558
7733
  try {
7559
7734
  child = (0, import_child_process3.spawn)(launch.file, launch.args, {
@@ -7562,7 +7737,7 @@ var defaultAppServerClient = async (command, args, options) => {
7562
7737
  windowsVerbatimArguments: launch.verbatim
7563
7738
  });
7564
7739
  } catch {
7565
- resolve3(null);
7740
+ resolve4(null);
7566
7741
  return;
7567
7742
  }
7568
7743
  let settled = false;
@@ -7571,7 +7746,7 @@ var defaultAppServerClient = async (command, args, options) => {
7571
7746
  settled = true;
7572
7747
  clearTimeout(timer);
7573
7748
  killTree(child);
7574
- resolve3(value);
7749
+ resolve4(value);
7575
7750
  };
7576
7751
  const timer = setTimeout(() => finish(null), options?.timeout ?? 2e4);
7577
7752
  child.on("error", () => finish(null));
@@ -8611,11 +8786,71 @@ var RunnerInstallation = class {
8611
8786
  }
8612
8787
  };
8613
8788
 
8789
+ // src/services/settingsEnvAllowlist.ts
8790
+ var ORDEWELL_SETTABLE_ENV = [
8791
+ "AI_PROVIDER",
8792
+ "ORCHESTRATOR_MODEL",
8793
+ "ORDEWELL_SUBAGENT_MODEL",
8794
+ "ORDEWELL_PLANNER_EFFORT",
8795
+ "ORDEWELL_RESEARCH_ENABLED",
8796
+ "ORDEWELL_MAX_PARALLEL"
8797
+ ];
8798
+ var SETTINGS_ENV_REFUSED = [
8799
+ // Approval posture and pre-approved scope.
8800
+ "ORDEWELL_AUTONOMOUS_MODE",
8801
+ "ORDEWELL_APPROVAL_MODE",
8802
+ "ORDEWELL_APPROVAL_ALLOW",
8803
+ // Where settings are persisted.
8804
+ "ORDEWELL_SETTINGS_PATH",
8805
+ // Runtime options and dynamic loaders.
8806
+ "NODE_OPTIONS",
8807
+ "NODE_REPL_EXTERNAL_MODULE",
8808
+ "NODE_PATH",
8809
+ "ELECTRON_RUN_AS_NODE",
8810
+ "LD_PRELOAD",
8811
+ "LD_LIBRARY_PATH",
8812
+ "LD_AUDIT",
8813
+ "DYLD_INSERT_LIBRARIES",
8814
+ "DYLD_LIBRARY_PATH",
8815
+ "DYLD_FRAMEWORK_PATH",
8816
+ // Executable resolution.
8817
+ "PATH",
8818
+ "PATHEXT",
8819
+ "NODE"
8820
+ ];
8821
+ var REFUSED = new Set(SETTINGS_ENV_REFUSED);
8822
+ function providerEnvKeys() {
8823
+ const keys = [];
8824
+ for (const meta of Object.values(ALL_PROVIDERS)) {
8825
+ if (meta.apiKeyEnvVar) keys.push(meta.apiKeyEnvVar);
8826
+ if (meta.baseUrlEnvVar) keys.push(meta.baseUrlEnvVar);
8827
+ for (const detect of meta.detectEnvVars) keys.push(detect);
8828
+ }
8829
+ return keys;
8830
+ }
8831
+ var PROVIDER_CREDENTIAL_ENV = new Set(providerEnvKeys());
8832
+ var SETTINGS_ENV_ALLOWLIST = new Set(
8833
+ [...providerEnvKeys(), ...ORDEWELL_SETTABLE_ENV].filter((key) => !REFUSED.has(key))
8834
+ );
8835
+ function admitSettingsEnv(env) {
8836
+ const accepted = {};
8837
+ const rejected = [];
8838
+ for (const [key, value] of Object.entries(env)) {
8839
+ if (REFUSED.has(key) || !SETTINGS_ENV_ALLOWLIST.has(key) || typeof value !== "string") {
8840
+ rejected.push(key);
8841
+ continue;
8842
+ }
8843
+ accepted[key] = value;
8844
+ }
8845
+ return { accepted, rejected };
8846
+ }
8847
+
8614
8848
  // src/services/harness/CliAgentAiService.ts
8615
8849
  var import_child_process5 = require("child_process");
8616
8850
 
8617
8851
  // src/utils/workspace.ts
8618
8852
  var fs3 = __toESM(require("fs"));
8853
+ var path9 = __toESM(require("path"));
8619
8854
  var WorkspaceNotFoundError = class extends Error {
8620
8855
  constructor(workspace) {
8621
8856
  super(`Workspace "${workspace}" does not exist.`);
@@ -8635,6 +8870,46 @@ function assertWorkspaceExists(workspace, deps = {}) {
8635
8870
  const isDirectory = deps.isDirectory ?? defaultIsDirectory;
8636
8871
  if (!isDirectory(workspace)) throw new WorkspaceNotFoundError(workspace);
8637
8872
  }
8873
+ var WorkspaceNotAProjectError = class extends Error {
8874
+ constructor(workspace) {
8875
+ super(`Workspace "${workspace}" is not a project directory (no .ordewell state, version control, or recognized manifest found).`);
8876
+ this.workspace = workspace;
8877
+ this.name = "WorkspaceNotAProjectError";
8878
+ }
8879
+ workspace;
8880
+ };
8881
+ var PROJECT_MARKER_DIRS = [".ordewell", ".git", ".hg", ".svn", ".bzr"];
8882
+ var PROJECT_MARKER_FILES = [
8883
+ "package.json",
8884
+ "Cargo.toml",
8885
+ "go.mod",
8886
+ "pyproject.toml",
8887
+ "setup.py",
8888
+ "requirements.txt",
8889
+ "Pipfile",
8890
+ "pom.xml",
8891
+ "build.gradle",
8892
+ "build.gradle.kts",
8893
+ "Gemfile",
8894
+ "composer.json",
8895
+ "pubspec.yaml",
8896
+ "mix.exs",
8897
+ "CMakeLists.txt"
8898
+ ];
8899
+ function assertWorkspaceIsProject(workspace, deps = {}) {
8900
+ const realpath = deps.realpath ?? ((candidate) => fs3.realpathSync(candidate));
8901
+ const exists = deps.exists ?? ((candidate) => fs3.existsSync(candidate));
8902
+ let resolved;
8903
+ try {
8904
+ resolved = realpath(workspace);
8905
+ } catch {
8906
+ throw new WorkspaceNotAProjectError(workspace);
8907
+ }
8908
+ const hasMarker = [...PROJECT_MARKER_DIRS, ...PROJECT_MARKER_FILES].some(
8909
+ (marker) => exists(path9.join(resolved, marker))
8910
+ );
8911
+ if (!hasMarker) throw new WorkspaceNotAProjectError(workspace);
8912
+ }
8638
8913
 
8639
8914
  // src/services/harness/AgentAdapter.ts
8640
8915
  var LineBuffer = class {
@@ -8690,8 +8965,8 @@ var StdioAgentAdapter = class {
8690
8965
  }
8691
8966
  async start(opts) {
8692
8967
  assertWorkspaceExists(opts.cwd, { isDirectory: this.deps.isDirectory });
8693
- this.processEnded = new Promise((resolve3) => {
8694
- this.markEnded = resolve3;
8968
+ this.processEnded = new Promise((resolve4) => {
8969
+ this.markEnded = resolve4;
8695
8970
  });
8696
8971
  const spec = this.spawnSpec(opts);
8697
8972
  const resolvePath = this.deps.resolvePath ?? augmentedPath;
@@ -8739,7 +9014,7 @@ ${err.message}`).slice(-STDERR_TAIL_CHARS);
8739
9014
  onEvent({ type: "error", message: this.exitMessage() });
8740
9015
  return;
8741
9016
  }
8742
- await new Promise((resolve3) => {
9017
+ await new Promise((resolve4) => {
8743
9018
  let settled = false;
8744
9019
  const finish = () => {
8745
9020
  if (settled) return;
@@ -8748,7 +9023,7 @@ ${err.message}`).slice(-STDERR_TAIL_CHARS);
8748
9023
  signal?.removeEventListener("abort", onAbort);
8749
9024
  this.process?.removeListener("exit", onExit);
8750
9025
  this.process?.removeListener("error", onExit);
8751
- resolve3();
9026
+ resolve4();
8752
9027
  };
8753
9028
  const emit = (event) => {
8754
9029
  onEvent(event);
@@ -8949,14 +9224,14 @@ function codexSandboxUnavailableMessage() {
8949
9224
  ].join("\n");
8950
9225
  }
8951
9226
  function runProbe(deps, cwd, env, flags) {
8952
- return new Promise((resolve3) => {
9227
+ return new Promise((resolve4) => {
8953
9228
  let output = "";
8954
9229
  let settled = false;
8955
9230
  const finish = (code) => {
8956
9231
  if (settled) return;
8957
9232
  settled = true;
8958
9233
  clearTimeout(timer);
8959
- resolve3({ code, output });
9234
+ resolve4({ code, output });
8960
9235
  };
8961
9236
  const timer = setTimeout(() => finish(null), PROBE_TIMEOUT_MS);
8962
9237
  timer.unref?.();
@@ -9039,8 +9314,8 @@ var CodexAdapter = class extends StdioAgentAdapter {
9039
9314
  async handshake(opts) {
9040
9315
  this.sandbox = await probeCodexSandbox(this.deps, opts.cwd, this.spawnEnv);
9041
9316
  if (this.sandbox === "unavailable") throw new Error(codexSandboxUnavailableMessage());
9042
- const ready = new Promise((resolve3) => {
9043
- this.settleHandshake = resolve3;
9317
+ const ready = new Promise((resolve4) => {
9318
+ this.settleHandshake = resolve4;
9044
9319
  });
9045
9320
  this.writeLine({
9046
9321
  jsonrpc: "2.0",
@@ -9053,8 +9328,8 @@ var CodexAdapter = class extends StdioAgentAdapter {
9053
9328
  // A binary that rejects `app-server` outright dies immediately; waiting
9054
9329
  // out the timeout would turn a one-line diagnostic into a 30s stall.
9055
9330
  this.processEnded.then(() => false),
9056
- new Promise((resolve3) => {
9057
- const t = setTimeout(() => resolve3(false), HANDSHAKE_TIMEOUT_MS);
9331
+ new Promise((resolve4) => {
9332
+ const t = setTimeout(() => resolve4(false), HANDSHAKE_TIMEOUT_MS);
9058
9333
  t.unref?.();
9059
9334
  })
9060
9335
  ]);
@@ -9342,12 +9617,12 @@ var OpenCodeAdapter = class {
9342
9617
  cwd: opts.cwd,
9343
9618
  windowsVerbatimArguments: launch.verbatim
9344
9619
  });
9345
- const banner = new Promise((resolve3) => {
9620
+ const banner = new Promise((resolve4) => {
9346
9621
  let seen = "";
9347
9622
  const scan = (chunk) => {
9348
9623
  seen += chunk.toString();
9349
9624
  const match = seen.match(/https?:\/\/[^\s]+/);
9350
- if (match) resolve3(match[0].replace(/[.,)]$/, ""));
9625
+ if (match) resolve4(match[0].replace(/[.,)]$/, ""));
9351
9626
  };
9352
9627
  this.process.stdout?.on("data", scan);
9353
9628
  this.process.stderr?.on("data", (chunk) => {
@@ -9356,15 +9631,15 @@ var OpenCodeAdapter = class {
9356
9631
  });
9357
9632
  this.process.on("exit", () => {
9358
9633
  this.exited = true;
9359
- resolve3(null);
9634
+ resolve4(null);
9360
9635
  });
9361
9636
  this.process.on("error", (err) => {
9362
9637
  this.exited = true;
9363
9638
  this.stderrTail = (this.stderrTail + `
9364
9639
  ${err.message}`).slice(-STDERR_TAIL_CHARS2);
9365
- resolve3(null);
9640
+ resolve4(null);
9366
9641
  });
9367
- const timer = setTimeout(() => resolve3(null), SERVER_READY_TIMEOUT_MS);
9642
+ const timer = setTimeout(() => resolve4(null), SERVER_READY_TIMEOUT_MS);
9368
9643
  timer.unref?.();
9369
9644
  });
9370
9645
  this.baseUrl = await banner;
@@ -9394,8 +9669,8 @@ ${this.stderrTail.trim()}` : ""}`);
9394
9669
  const streamAbort = new AbortController();
9395
9670
  let connected = () => {
9396
9671
  };
9397
- const streamReady = new Promise((resolve3) => {
9398
- connected = resolve3;
9672
+ const streamReady = new Promise((resolve4) => {
9673
+ connected = resolve4;
9399
9674
  });
9400
9675
  const live = this.streamEvents(
9401
9676
  streamAbort.signal,
@@ -9547,14 +9822,14 @@ ${this.stderrTail.trim()}` : ""}`);
9547
9822
  }
9548
9823
  }
9549
9824
  }
9550
- async json(method, path15, body, signal) {
9551
- const response = await this.deps.fetch(`${this.baseUrl}${path15}`, {
9825
+ async json(method, path17, body, signal) {
9826
+ const response = await this.deps.fetch(`${this.baseUrl}${path17}`, {
9552
9827
  method,
9553
9828
  headers: { "content-type": "application/json" },
9554
9829
  ...body === void 0 ? {} : { body: JSON.stringify(body) },
9555
9830
  ...signal ? { signal } : {}
9556
9831
  });
9557
- if (!response.ok) throw new Error(`${method} ${path15} failed: ${response.status} ${response.statusText}`);
9832
+ if (!response.ok) throw new Error(`${method} ${path17} failed: ${response.status} ${response.statusText}`);
9558
9833
  return await response.json().catch(() => null);
9559
9834
  }
9560
9835
  nativeSessionId() {
@@ -10368,11 +10643,11 @@ function executionSummary(tasks) {
10368
10643
 
10369
10644
  // src/utils/sessionStore.ts
10370
10645
  var fs5 = __toESM(require("fs"));
10371
- var path10 = __toESM(require("path"));
10646
+ var path11 = __toESM(require("path"));
10372
10647
 
10373
10648
  // src/utils/fsHelpers.ts
10374
10649
  var fs4 = __toESM(require("fs"));
10375
- var path9 = __toESM(require("path"));
10650
+ var path10 = __toESM(require("path"));
10376
10651
  var STATE_DIR = ".ordewell";
10377
10652
  function ensureDir(dir) {
10378
10653
  if (!fs4.existsSync(dir)) {
@@ -10380,13 +10655,19 @@ function ensureDir(dir) {
10380
10655
  }
10381
10656
  }
10382
10657
  function getStateDir(baseDir) {
10383
- return path9.join(baseDir ?? process.cwd(), STATE_DIR);
10658
+ return path10.join(baseDir ?? process.cwd(), STATE_DIR);
10659
+ }
10660
+
10661
+ // src/utils/sessionId.ts
10662
+ var import_crypto = require("crypto");
10663
+ function mintSessionId() {
10664
+ return `session-${(0, import_crypto.randomBytes)(16).toString("hex")}`;
10384
10665
  }
10385
10666
 
10386
10667
  // src/utils/sessionStore.ts
10387
10668
  var SESSIONS_SUBDIR = "sessions";
10388
10669
  function getSessionsDir(baseDir) {
10389
- return path10.join(getStateDir(baseDir), SESSIONS_SUBDIR);
10670
+ return path11.join(getStateDir(baseDir), SESSIONS_SUBDIR);
10390
10671
  }
10391
10672
  var REJECTION_MESSAGE = "This session was created with an older version of Ordewell. Please re-plan.";
10392
10673
  function hasScalarRunner(parsed) {
@@ -10407,7 +10688,7 @@ function saveSession(plan, goal, baseDir, id) {
10407
10688
  ensureDir(sessionsDir);
10408
10689
  const now = (/* @__PURE__ */ new Date()).toISOString();
10409
10690
  const meta = {
10410
- id: id || `session-${Date.now()}`,
10691
+ id: id || mintSessionId(),
10411
10692
  goal: goal || "(untitled)",
10412
10693
  runners: plan.runners,
10413
10694
  taskCount: plan.tasks.flatMap((t) => [t, ...t.subtasks || []]).length,
@@ -10420,7 +10701,7 @@ function saveSession(plan, goal, baseDir, id) {
10420
10701
  plan: JSON.parse(JSON.stringify(plan))
10421
10702
  };
10422
10703
  const filename = sessionFilename(meta);
10423
- const filePath = path10.join(sessionsDir, filename);
10704
+ const filePath = path11.join(sessionsDir, filename);
10424
10705
  fs5.writeFileSync(filePath, JSON.stringify(session, null, 2));
10425
10706
  return meta;
10426
10707
  }
@@ -10430,7 +10711,7 @@ function listSessions(baseDir, logger = defaultLogger) {
10430
10711
  const files = fs5.readdirSync(sessionsDir).filter((f) => f.endsWith(".json"));
10431
10712
  return files.map((f) => {
10432
10713
  try {
10433
- const raw = fs5.readFileSync(path10.join(sessionsDir, f), "utf-8");
10714
+ const raw = fs5.readFileSync(path11.join(sessionsDir, f), "utf-8");
10434
10715
  const parsed = JSON.parse(raw);
10435
10716
  if (hasScalarRunner(parsed)) {
10436
10717
  logger.warn("sessionStore", `session file ${f}: ${REJECTION_MESSAGE}`);
@@ -10448,7 +10729,7 @@ function findSessionFile(sessionsDir, sessionId, logger) {
10448
10729
  const files = fs5.readdirSync(sessionsDir).filter((f) => f.endsWith(".json"));
10449
10730
  for (const f of files) {
10450
10731
  try {
10451
- const raw = fs5.readFileSync(path10.join(sessionsDir, f), "utf-8");
10732
+ const raw = fs5.readFileSync(path11.join(sessionsDir, f), "utf-8");
10452
10733
  const session = JSON.parse(raw);
10453
10734
  if (session.meta.id === sessionId) return f;
10454
10735
  } catch (err) {
@@ -10463,7 +10744,7 @@ function loadSession(sessionId, baseDir, logger = defaultLogger) {
10463
10744
  try {
10464
10745
  const file = findSessionFile(sessionsDir, sessionId, logger);
10465
10746
  if (file) {
10466
- const raw = fs5.readFileSync(path10.join(sessionsDir, file), "utf-8");
10747
+ const raw = fs5.readFileSync(path11.join(sessionsDir, file), "utf-8");
10467
10748
  const parsed = JSON.parse(raw);
10468
10749
  if (hasScalarRunner(parsed)) {
10469
10750
  logger.warn("sessionStore", `session file ${file}: ${REJECTION_MESSAGE}`);
@@ -10497,7 +10778,7 @@ function deleteSession(sessionId, baseDir, logger = defaultLogger) {
10497
10778
  try {
10498
10779
  const file = findSessionFile(sessionsDir, sessionId, logger);
10499
10780
  if (file) {
10500
- fs5.unlinkSync(path10.join(sessionsDir, file));
10781
+ fs5.unlinkSync(path11.join(sessionsDir, file));
10501
10782
  return true;
10502
10783
  }
10503
10784
  } catch (err) {
@@ -10516,11 +10797,6 @@ var PlanEditError = class extends Error {
10516
10797
  function sessionRuntimeSettings(settings) {
10517
10798
  return { ...plannerRuntimeToggles(settings), modelAllowlist: settings.modelAllowlist };
10518
10799
  }
10519
- var lastMintedAt = 0;
10520
- function mintSessionId() {
10521
- lastMintedAt = Math.max(Date.now(), lastMintedAt + 1);
10522
- return `session-${lastMintedAt}`;
10523
- }
10524
10800
  var Session = class {
10525
10801
  /** Injected by a test; when present it is the service, forever. */
10526
10802
  pinnedAiService;
@@ -12318,26 +12594,128 @@ var TmuxRunner = class extends AbstractRunner {
12318
12594
  }
12319
12595
  };
12320
12596
 
12597
+ // src/utils/daemonToken.ts
12598
+ var import_crypto2 = require("crypto");
12599
+ var import_fs2 = require("fs");
12600
+ var import_os2 = require("os");
12601
+ var import_path2 = require("path");
12602
+ var BEARER_PREFIX = "bearer ";
12603
+ var DAEMON_TOKEN_SUBPROTOCOL_PREFIX = "ordewell.token.";
12604
+ var DAEMON_SUBPROTOCOL = "ordewell.v1";
12605
+ function configDir() {
12606
+ return (0, import_path2.join)((0, import_os2.homedir)(), ".config", "ordewell");
12607
+ }
12608
+ function daemonTokenPath(port) {
12609
+ return (0, import_path2.join)(configDir(), `server-${port}.token`);
12610
+ }
12611
+ function mintDaemonToken(port) {
12612
+ const token = (0, import_crypto2.randomBytes)(32).toString("base64url");
12613
+ const file = daemonTokenPath(port);
12614
+ (0, import_fs2.mkdirSync)(configDir(), { recursive: true });
12615
+ try {
12616
+ (0, import_fs2.unlinkSync)(file);
12617
+ } catch {
12618
+ }
12619
+ const fd = (0, import_fs2.openSync)(file, "wx", 384);
12620
+ try {
12621
+ (0, import_fs2.writeSync)(fd, token);
12622
+ } finally {
12623
+ (0, import_fs2.closeSync)(fd);
12624
+ }
12625
+ return { token, file };
12626
+ }
12627
+ function readDaemonToken(port) {
12628
+ try {
12629
+ const token = (0, import_fs2.readFileSync)(daemonTokenPath(port), "utf8").trim();
12630
+ return token === "" ? void 0 : token;
12631
+ } catch {
12632
+ return void 0;
12633
+ }
12634
+ }
12635
+ function clearDaemonToken(port) {
12636
+ try {
12637
+ (0, import_fs2.unlinkSync)(daemonTokenPath(port));
12638
+ } catch {
12639
+ }
12640
+ }
12641
+ function bearerHeaderValue(token) {
12642
+ return `Bearer ${token}`;
12643
+ }
12644
+ function tokenSubprotocols(token) {
12645
+ return [DAEMON_SUBPROTOCOL, `${DAEMON_TOKEN_SUBPROTOCOL_PREFIX}${token}`];
12646
+ }
12647
+ function extractPresentedToken(carriers) {
12648
+ const authorization = carriers.authorization;
12649
+ if (authorization && authorization.toLowerCase().startsWith(BEARER_PREFIX)) {
12650
+ const value = authorization.slice(BEARER_PREFIX.length).trim();
12651
+ if (value !== "") return value;
12652
+ }
12653
+ for (const offered of (carriers.secWebSocketProtocol ?? "").split(",")) {
12654
+ const value = offered.trim();
12655
+ if (!value.startsWith(DAEMON_TOKEN_SUBPROTOCOL_PREFIX)) continue;
12656
+ const token = value.slice(DAEMON_TOKEN_SUBPROTOCOL_PREFIX.length);
12657
+ if (token !== "") return token;
12658
+ }
12659
+ return void 0;
12660
+ }
12661
+ function tokensMatch(presented, expected) {
12662
+ if (presented === void 0) return false;
12663
+ const a = Buffer.from(presented, "utf8");
12664
+ const b = Buffer.from(expected, "utf8");
12665
+ if (a.length !== b.length) return false;
12666
+ return (0, import_crypto2.timingSafeEqual)(a, b);
12667
+ }
12668
+
12321
12669
  // src/plugins/RunnerRegistry.ts
12322
12670
  var fs7 = __toESM(require("fs"));
12323
- var path12 = __toESM(require("path"));
12671
+ var path14 = __toESM(require("path"));
12324
12672
  var os5 = __toESM(require("os"));
12325
12673
  var import_child_process9 = require("child_process");
12674
+ var import_crypto3 = require("crypto");
12326
12675
 
12327
12676
  // src/plugins/FsPluginStore.ts
12328
12677
  var fs6 = __toESM(require("fs"));
12329
- var path11 = __toESM(require("path"));
12678
+ var path13 = __toESM(require("path"));
12330
12679
  var os4 = __toESM(require("os"));
12331
- function userPluginsDir() {
12332
- return path11.join(os4.homedir(), ".config", "ordewell", "plugins");
12680
+
12681
+ // src/plugins/pluginNames.ts
12682
+ var path12 = __toESM(require("path"));
12683
+ var PLUGIN_NAME_PATTERN = /^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/;
12684
+ var MAX_PLUGIN_NAME_LENGTH = 64;
12685
+ function isPlainPluginName(name) {
12686
+ return typeof name === "string" && name.length <= MAX_PLUGIN_NAME_LENGTH && PLUGIN_NAME_PATTERN.test(name);
12687
+ }
12688
+ function assertPlainPluginName(name) {
12689
+ if (!isPlainPluginName(name)) {
12690
+ throw new Error(
12691
+ `Invalid plugin name: ${JSON.stringify(name)}. Names must be a single plain segment of letters, digits, '.', '-' or '_' (max ${MAX_PLUGIN_NAME_LENGTH} characters).`
12692
+ );
12693
+ }
12333
12694
  }
12695
+ function resolvePluginInstallDir(pluginsDir, name) {
12696
+ assertPlainPluginName(name);
12697
+ const root = path12.resolve(pluginsDir);
12698
+ const dest = path12.resolve(root, name);
12699
+ const relative3 = path12.relative(root, dest);
12700
+ if (relative3 !== name || relative3 === "" || path12.isAbsolute(relative3)) {
12701
+ throw new Error(`Plugin install path escapes the plugins directory: ${JSON.stringify(name)}`);
12702
+ }
12703
+ return dest;
12704
+ }
12705
+
12706
+ // src/plugins/manifestValidation.ts
12334
12707
  function isValidManifest(obj) {
12335
12708
  if (!obj || typeof obj !== "object") return false;
12336
12709
  const m = obj;
12337
- return typeof m.name === "string" && typeof m.displayName === "string" && typeof m.description === "string" && typeof m.version === "string" && typeof m.runner === "object" && m.runner !== null && typeof m.runner.command === "string" && Array.isArray(m.runner.argsTemplate) && typeof m.features === "object" && m.features !== null && typeof m.modelDiscovery === "object" && m.modelDiscovery !== null;
12710
+ return isPlainPluginName(m.name) && typeof m.displayName === "string" && typeof m.description === "string" && typeof m.version === "string" && typeof m.runner === "object" && m.runner !== null && typeof m.runner.command === "string" && Array.isArray(m.runner.argsTemplate) && typeof m.features === "object" && m.features !== null && typeof m.modelDiscovery === "object" && m.modelDiscovery !== null;
12711
+ }
12712
+
12713
+ // src/plugins/FsPluginStore.ts
12714
+ function userPluginsDir() {
12715
+ return path13.join(os4.homedir(), ".config", "ordewell", "plugins");
12338
12716
  }
12339
12717
  function loadManifestFromDir(dir) {
12340
- const manifestPath = path11.join(dir, "manifest.json");
12718
+ const manifestPath = path13.join(dir, "manifest.json");
12341
12719
  if (!fs6.existsSync(manifestPath)) return null;
12342
12720
  try {
12343
12721
  const raw = fs6.readFileSync(manifestPath, "utf8");
@@ -12352,8 +12730,8 @@ function copyDirSync(src, dest) {
12352
12730
  ensureDir(dest);
12353
12731
  const entries = fs6.readdirSync(src, { withFileTypes: true });
12354
12732
  for (const entry of entries) {
12355
- const srcPath = path11.join(src, entry.name);
12356
- const destPath = path11.join(dest, entry.name);
12733
+ const srcPath = path13.join(src, entry.name);
12734
+ const destPath = path13.join(dest, entry.name);
12357
12735
  if (entry.isDirectory()) {
12358
12736
  copyDirSync(srcPath, destPath);
12359
12737
  } else {
@@ -12365,7 +12743,7 @@ function rmDirSync(dir) {
12365
12743
  if (!fs6.existsSync(dir)) return;
12366
12744
  const entries = fs6.readdirSync(dir, { withFileTypes: true });
12367
12745
  for (const entry of entries) {
12368
- const p = path11.join(dir, entry.name);
12746
+ const p = path13.join(dir, entry.name);
12369
12747
  if (entry.isDirectory()) {
12370
12748
  rmDirSync(p);
12371
12749
  } else {
@@ -12389,6 +12767,13 @@ var FsPluginStore = class {
12389
12767
  }
12390
12768
  return entries.filter((e) => e.isDirectory()).map((e) => e.name);
12391
12769
  }
12770
+ listDir(dir) {
12771
+ try {
12772
+ return fs6.readdirSync(dir);
12773
+ } catch {
12774
+ return [];
12775
+ }
12776
+ }
12392
12777
  loadManifest(pluginDir) {
12393
12778
  return loadManifestFromDir(pluginDir);
12394
12779
  }
@@ -12411,11 +12796,11 @@ var FsPluginStore = class {
12411
12796
  return null;
12412
12797
  }
12413
12798
  }
12414
- dirExists(path15) {
12415
- return fs6.existsSync(path15) && fs6.statSync(path15).isDirectory();
12799
+ dirExists(path17) {
12800
+ return fs6.existsSync(path17) && fs6.statSync(path17).isDirectory();
12416
12801
  }
12417
- exists(path15) {
12418
- return fs6.existsSync(path15);
12802
+ exists(path17) {
12803
+ return fs6.existsSync(path17);
12419
12804
  }
12420
12805
  };
12421
12806
 
@@ -12703,17 +13088,98 @@ var OPENCODE_MANIFEST = {
12703
13088
  ]
12704
13089
  };
12705
13090
 
13091
+ // src/plugins/pluginSource.ts
13092
+ var ALLOWED_PLUGIN_HOSTS = [
13093
+ "github.com",
13094
+ "gitlab.com",
13095
+ "bitbucket.org",
13096
+ "codeberg.org"
13097
+ ];
13098
+ var SAFE_REPO_PATH = /^\/[A-Za-z0-9._~-]+(?:\/[A-Za-z0-9._~-]+)*$/;
13099
+ var HAS_SCHEME = /^[A-Za-z][A-Za-z0-9+.-]+:/;
13100
+ var SCP_LIKE_REMOTE = /^[^/\\]+@[^/\\]+:/;
13101
+ function hasControlCharacter(value) {
13102
+ for (let i = 0; i < value.length; i++) {
13103
+ const code = value.charCodeAt(i);
13104
+ if (code < 32 || code === 127) return true;
13105
+ }
13106
+ return false;
13107
+ }
13108
+ function assertInstallablePluginUrl(url) {
13109
+ if (typeof url !== "string" || url.trim() === "") {
13110
+ throw new Error("A plugin repository URL is required.");
13111
+ }
13112
+ if (hasControlCharacter(url)) {
13113
+ throw new Error("Plugin URL contains control characters.");
13114
+ }
13115
+ let parsed;
13116
+ try {
13117
+ parsed = new URL(url.trim());
13118
+ } catch {
13119
+ throw new Error(`Not a valid plugin repository URL: ${url}`);
13120
+ }
13121
+ if (parsed.protocol !== "https:") {
13122
+ throw new Error(
13123
+ `Plugin repositories must be cloned over https (got "${parsed.protocol}//"). git transport helpers and non-https schemes are not accepted.`
13124
+ );
13125
+ }
13126
+ if (parsed.username || parsed.password) {
13127
+ throw new Error("Plugin URL must not embed credentials.");
13128
+ }
13129
+ if (parsed.port) {
13130
+ throw new Error("Plugin URL must not specify a port.");
13131
+ }
13132
+ if (parsed.search || parsed.hash) {
13133
+ throw new Error("Plugin URL must not carry a query string or fragment.");
13134
+ }
13135
+ const host = parsed.hostname.toLowerCase();
13136
+ if (!ALLOWED_PLUGIN_HOSTS.includes(host)) {
13137
+ throw new Error(
13138
+ `Plugin host not allowed: ${host}. Allowed hosts: ${ALLOWED_PLUGIN_HOSTS.join(", ")}.`
13139
+ );
13140
+ }
13141
+ if (!SAFE_REPO_PATH.test(parsed.pathname)) {
13142
+ throw new Error(`Not a plain repository path: ${parsed.pathname}`);
13143
+ }
13144
+ return parsed;
13145
+ }
13146
+ function classifyPluginSource(source) {
13147
+ const trimmed = source.trim();
13148
+ if (trimmed.startsWith("github:")) {
13149
+ const repo = trimmed.slice("github:".length).replace(/\.git$/, "");
13150
+ return { kind: "git", url: `https://github.com/${repo}.git` };
13151
+ }
13152
+ if (HAS_SCHEME.test(trimmed) || SCP_LIKE_REMOTE.test(trimmed)) {
13153
+ return { kind: "git", url: trimmed };
13154
+ }
13155
+ return { kind: "path", path: source };
13156
+ }
13157
+
12706
13158
  // src/plugins/RunnerRegistry.ts
12707
13159
  var BUILTIN_MANIFESTS = [
12708
13160
  CLAUDE_CODE_MANIFEST,
12709
13161
  CODEX_MANIFEST,
12710
13162
  OPENCODE_MANIFEST
12711
13163
  ];
13164
+ var RESERVED_RUNNER_NAMES = new Set(BUILTIN_MANIFESTS.map((m) => m.name.toLowerCase()));
13165
+ function isReservedRunnerName(name) {
13166
+ return RESERVED_RUNNER_NAMES.has(name.toLowerCase());
13167
+ }
13168
+ function gitClone(url, destDir) {
13169
+ (0, import_child_process9.execFileSync)("git", ["clone", "--depth", "1", "--", url, destDir], {
13170
+ stdio: "pipe",
13171
+ timeout: 3e4,
13172
+ // A repository that demands credentials should fail, not hang on a prompt.
13173
+ env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
13174
+ });
13175
+ }
12712
13176
  var RunnerRegistry = class {
12713
13177
  plugins = /* @__PURE__ */ new Map();
12714
13178
  store;
12715
- constructor(store) {
13179
+ clone;
13180
+ constructor(store, clone) {
12716
13181
  this.store = store ?? new FsPluginStore();
13182
+ this.clone = clone ?? gitClone;
12717
13183
  this.loadBuiltins();
12718
13184
  }
12719
13185
  loadBuiltins() {
@@ -12724,9 +13190,15 @@ var RunnerRegistry = class {
12724
13190
  loadUserPlugins() {
12725
13191
  const names = this.store.listUserPluginDirs();
12726
13192
  for (const name of names) {
12727
- const pluginDir = path12.join(this.store.getUserPluginsDir(), name);
13193
+ let pluginDir;
13194
+ try {
13195
+ pluginDir = resolvePluginInstallDir(this.store.getUserPluginsDir(), name);
13196
+ } catch {
13197
+ continue;
13198
+ }
12728
13199
  const manifest = this.store.loadManifest(pluginDir);
12729
- if (!manifest) continue;
13200
+ if (!manifest || !isPlainPluginName(manifest.name)) continue;
13201
+ if (isReservedRunnerName(manifest.name)) continue;
12730
13202
  this.plugins.set(manifest.name, {
12731
13203
  manifest,
12732
13204
  source: "user",
@@ -12755,7 +13227,7 @@ var RunnerRegistry = class {
12755
13227
  return entry?.source === "builtin";
12756
13228
  }
12757
13229
  installFromPath(sourcePath) {
12758
- const absPath = path12.resolve(sourcePath);
13230
+ const absPath = path14.resolve(sourcePath);
12759
13231
  if (!this.store.dirExists(absPath)) {
12760
13232
  throw new Error(`Plugin source must be a directory: ${absPath}`);
12761
13233
  }
@@ -12763,48 +13235,50 @@ var RunnerRegistry = class {
12763
13235
  if (!manifest) {
12764
13236
  throw new Error(`No valid manifest.json found in: ${absPath}`);
12765
13237
  }
12766
- const destDir = path12.join(this.store.getUserPluginsDir(), manifest.name);
12767
- this.store.ensureDir(destDir);
12768
- this.store.copyDir(absPath, destDir);
12769
- this.plugins.set(manifest.name, {
12770
- manifest,
12771
- source: "user",
12772
- installPath: destDir
12773
- });
12774
- return manifest;
13238
+ return this.registerInstalled(manifest, absPath);
12775
13239
  }
12776
13240
  installFromGit(url) {
12777
- const repoName = url.split("/").pop()?.replace(/\.git$/, "") || "plugin";
12778
- const tmpDir = path12.join(os5.tmpdir(), `ordewell-plugin-${Date.now()}`);
13241
+ const validated = assertInstallablePluginUrl(url);
13242
+ const tmpDir = path14.join(os5.tmpdir(), `ordewell-plugin-${(0, import_crypto3.randomBytes)(12).toString("hex")}`);
12779
13243
  this.store.ensureDir(tmpDir);
12780
13244
  try {
12781
- (0, import_child_process9.execSync)(`git clone --depth 1 "${url}" "${tmpDir}"`, { stdio: "pipe", timeout: 3e4 });
12782
- } catch (err) {
12783
- const message = err instanceof Error ? err.message : String(err);
12784
- throw new Error(`Failed to clone repository: ${message}`);
12785
- }
12786
- const contents = fsReaddirSync(tmpDir);
12787
- const hasManifest = contents.some((f) => f === "manifest.json");
12788
- if (!hasManifest && contents.length === 1 && this.store.dirExists(path12.join(tmpDir, contents[0]))) {
12789
- const subDir = path12.join(tmpDir, contents[0]);
12790
- return this.finishInstall(subDir, repoName, tmpDir);
12791
- }
12792
- if (!hasManifest) {
13245
+ try {
13246
+ this.clone(validated.toString(), tmpDir);
13247
+ } catch (err) {
13248
+ const message = err instanceof Error ? err.message : String(err);
13249
+ throw new Error(`Failed to clone repository: ${message}`);
13250
+ }
13251
+ const contents = this.store.listDir(tmpDir);
13252
+ const hasManifest = contents.some((f) => f === "manifest.json");
13253
+ let sourceDir = tmpDir;
13254
+ if (!hasManifest) {
13255
+ const onlyEntry = contents.length === 1 ? path14.join(tmpDir, contents[0]) : void 0;
13256
+ if (!onlyEntry || !this.store.dirExists(onlyEntry)) {
13257
+ throw new Error(`No manifest.json found in repository: ${validated.toString()}`);
13258
+ }
13259
+ sourceDir = onlyEntry;
13260
+ }
13261
+ const manifest = this.store.loadManifest(sourceDir);
13262
+ if (!manifest) {
13263
+ throw new Error("No valid manifest.json found in cloned repository");
13264
+ }
13265
+ return this.registerInstalled(manifest, sourceDir);
13266
+ } finally {
12793
13267
  this.store.removeDir(tmpDir);
12794
- throw new Error(`No manifest.json found in repository: ${url}`);
12795
13268
  }
12796
- return this.finishInstall(tmpDir, repoName, tmpDir);
12797
13269
  }
12798
- finishInstall(sourceDir, repoName, tmpDir) {
12799
- const manifest = this.store.loadManifest(sourceDir);
12800
- if (!manifest) {
12801
- this.store.removeDir(tmpDir);
12802
- throw new Error(`No valid manifest.json found in cloned repository`);
13270
+ /**
13271
+ * The single destination-building step both install routes share: the name is
13272
+ * constrained to a plain segment and the resolved destination is asserted to
13273
+ * be inside the plugins directory before anything is copied.
13274
+ */
13275
+ registerInstalled(manifest, sourceDir) {
13276
+ if (isReservedRunnerName(manifest.name)) {
13277
+ throw new Error(`Cannot install a plugin named after a built-in runner: ${manifest.name}`);
12803
13278
  }
12804
- const destDir = path12.join(this.store.getUserPluginsDir(), manifest.name);
13279
+ const destDir = resolvePluginInstallDir(this.store.getUserPluginsDir(), manifest.name);
12805
13280
  this.store.ensureDir(destDir);
12806
13281
  this.store.copyDir(sourceDir, destDir);
12807
- this.store.removeDir(tmpDir);
12808
13282
  this.plugins.set(manifest.name, {
12809
13283
  manifest,
12810
13284
  source: "user",
@@ -12825,7 +13299,8 @@ var RunnerRegistry = class {
12825
13299
  this.plugins.delete(name);
12826
13300
  }
12827
13301
  createSkeleton(name, outputDir) {
12828
- const dir = path12.resolve(outputDir, name);
13302
+ assertPlainPluginName(name);
13303
+ const dir = path14.resolve(outputDir, name);
12829
13304
  this.store.ensureDir(dir);
12830
13305
  const manifest = {
12831
13306
  name,
@@ -12857,8 +13332,8 @@ var RunnerRegistry = class {
12857
13332
  ]
12858
13333
  }
12859
13334
  };
12860
- this.store.writeFile(path12.join(dir, "manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
12861
- const runSh = path12.join(dir, "run.sh");
13335
+ this.store.writeFile(path14.join(dir, "manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
13336
+ const runSh = path14.join(dir, "run.sh");
12862
13337
  const scriptContent = `#!/usr/bin/env bash
12863
13338
  # Custom runner script for ${name}
12864
13339
  # Receives:
@@ -12882,13 +13357,6 @@ echo "Running ${name} with prompt: $prompt"
12882
13357
  return dir;
12883
13358
  }
12884
13359
  };
12885
- function fsReaddirSync(dir) {
12886
- try {
12887
- return fs7.readdirSync(dir);
12888
- } catch {
12889
- return [];
12890
- }
12891
- }
12892
13360
  function fsChmodSync(fpath, mode) {
12893
13361
  try {
12894
13362
  fs7.chmodSync(fpath, mode);
@@ -12898,14 +13366,14 @@ function fsChmodSync(fpath, mode) {
12898
13366
 
12899
13367
  // src/utils/stateStore.ts
12900
13368
  var fs8 = __toESM(require("fs"));
12901
- var path13 = __toESM(require("path"));
13369
+ var path15 = __toESM(require("path"));
12902
13370
  var STATE_FILE = "state.json";
12903
13371
  function getStatePath(baseDir) {
12904
- return path13.join(getStateDir(baseDir), STATE_FILE);
13372
+ return path15.join(getStateDir(baseDir), STATE_FILE);
12905
13373
  }
12906
13374
  function saveState(plan, baseDir) {
12907
13375
  const statePath = getStatePath(baseDir ?? process.cwd());
12908
- ensureDir(path13.dirname(statePath));
13376
+ ensureDir(path15.dirname(statePath));
12909
13377
  plan.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
12910
13378
  fs8.writeFileSync(statePath, JSON.stringify(plan, null, 2));
12911
13379
  }
@@ -12938,7 +13406,7 @@ function stateExists(baseDir) {
12938
13406
 
12939
13407
  // src/services/SettingsService.ts
12940
13408
  var fs9 = __toESM(require("fs"));
12941
- var path14 = __toESM(require("path"));
13409
+ var path16 = __toESM(require("path"));
12942
13410
  var os6 = __toESM(require("os"));
12943
13411
  var DEFAULTS = {
12944
13412
  grillMe: { enabled: false },
@@ -12951,7 +13419,7 @@ var DEFAULTS = {
12951
13419
  function getSettingsPath() {
12952
13420
  const override = process.env.ORDEWELL_SETTINGS_PATH;
12953
13421
  if (override && override.trim()) return override.trim();
12954
- return path14.join(os6.homedir(), ".config", "ordewell", "settings.json");
13422
+ return path16.join(os6.homedir(), ".config", "ordewell", "settings.json");
12955
13423
  }
12956
13424
  function readPlannerModels(raw) {
12957
13425
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
@@ -13116,7 +13584,7 @@ var SettingsService = class {
13116
13584
  return { ...DEFAULTS, grillMe: { ...DEFAULTS.grillMe }, tdd: { ...DEFAULTS.tdd }, prd: { ...DEFAULTS.prd }, review: { ...DEFAULTS.review }, verification: { ...DEFAULTS.verification }, researchSubagents: { ...DEFAULTS.researchSubagents } };
13117
13585
  }
13118
13586
  persist() {
13119
- const dir = path14.dirname(this.filePath);
13587
+ const dir = path16.dirname(this.filePath);
13120
13588
  if (!fs9.existsSync(dir)) {
13121
13589
  fs9.mkdirSync(dir, { recursive: true });
13122
13590
  }
@@ -13161,6 +13629,7 @@ var PlannerModelMemory = class {
13161
13629
  };
13162
13630
  // Annotate the CommonJS export names for ESM import in node:
13163
13631
  0 && (module.exports = {
13632
+ ALLOWED_PLUGIN_HOSTS,
13164
13633
  ALL_PROVIDERS,
13165
13634
  AUTO_COMMANDS,
13166
13635
  AbstractRunner,
@@ -13179,6 +13648,8 @@ var PlannerModelMemory = class {
13179
13648
  CommandLineTooLongError,
13180
13649
  ConsoleLogger,
13181
13650
  ContextCollector,
13651
+ DAEMON_SUBPROTOCOL,
13652
+ DAEMON_TOKEN_SUBPROTOCOL_PREFIX,
13182
13653
  DENY_ALL,
13183
13654
  EmbeddedNewlineError,
13184
13655
  EnvConfig,
@@ -13195,9 +13666,12 @@ var PlannerModelMemory = class {
13195
13666
  ModelResolver,
13196
13667
  OPENCODE_MANIFEST,
13197
13668
  ORCHESTRATOR_SHORTCUTS,
13669
+ ORDEWELL_SETTABLE_ENV,
13198
13670
  OpenAiService,
13199
13671
  OpenCodeAdapter,
13200
13672
  PLAN_ENVELOPE_KEY,
13673
+ PLUGIN_NAME_PATTERN,
13674
+ PROVIDER_CREDENTIAL_ENV,
13201
13675
  PROVIDER_DETECT_PRIORITY,
13202
13676
  PROVIDER_LABEL,
13203
13677
  PROVIDER_PRIORITY,
@@ -13212,6 +13686,8 @@ var PlannerModelMemory = class {
13212
13686
  RunnerInstallation,
13213
13687
  RunnerRegistry,
13214
13688
  SEARCH_EXCLUSIONS,
13689
+ SETTINGS_ENV_ALLOWLIST,
13690
+ SETTINGS_ENV_REFUSED,
13215
13691
  STATE_DIR,
13216
13692
  STOPPED_TOOL_RESULT,
13217
13693
  SYMBOL_LANGUAGES,
@@ -13224,13 +13700,19 @@ var PlannerModelMemory = class {
13224
13700
  TmuxRunner,
13225
13701
  VerdictEngine,
13226
13702
  WINDOWS_MAX_COMMAND_LINE,
13703
+ WorkspaceNotAProjectError,
13227
13704
  WorkspaceNotFoundError,
13228
13705
  addTaskToPlan,
13706
+ admitSettingsEnv,
13229
13707
  applyHeadLimit,
13230
13708
  applyTaskOps,
13709
+ assertInstallablePluginUrl,
13710
+ assertPlainPluginName,
13231
13711
  assertWorkspaceExists,
13712
+ assertWorkspaceIsProject,
13232
13713
  augmentPromptWithPriorOutputs,
13233
13714
  augmentedPath,
13715
+ bearerHeaderValue,
13234
13716
  buildConversationSystemPrompt,
13235
13717
  buildFallbackGrepArgs,
13236
13718
  buildGlobArgs,
@@ -13258,7 +13740,9 @@ var PlannerModelMemory = class {
13258
13740
  classifyCommand,
13259
13741
  classifyOutcome,
13260
13742
  classifyPlannerReply,
13743
+ classifyPluginSource,
13261
13744
  clearAugmentedPathCache,
13745
+ clearDaemonToken,
13262
13746
  clearResearchShellCache,
13263
13747
  clearState,
13264
13748
  clipboardCopyCommand,
@@ -13270,6 +13754,7 @@ var PlannerModelMemory = class {
13270
13754
  createAiService,
13271
13755
  createEmptyPlan,
13272
13756
  createTask,
13757
+ daemonTokenPath,
13273
13758
  defaultLogger,
13274
13759
  definitionPattern,
13275
13760
  deleteSession,
@@ -13287,6 +13772,7 @@ var PlannerModelMemory = class {
13287
13772
  extractObjectWithBalance,
13288
13773
  extractObjectsWithKey,
13289
13774
  extractPrdBlock,
13775
+ extractPresentedToken,
13290
13776
  fetchAllProviderModels,
13291
13777
  filterFallbackByAnchoredInclude,
13292
13778
  filterModelsForPrompt,
@@ -13304,6 +13790,9 @@ var PlannerModelMemory = class {
13304
13790
  isCliProvider,
13305
13791
  isExecutableResolved,
13306
13792
  isOpenAiProvider,
13793
+ isPlainPluginName,
13794
+ isReservedRunnerName,
13795
+ isValidManifest,
13307
13796
  killTree,
13308
13797
  knownModelId,
13309
13798
  languageForId,
@@ -13316,6 +13805,8 @@ var PlannerModelMemory = class {
13316
13805
  migrateLegacyPlan,
13317
13806
  migratePlanState,
13318
13807
  migrateTask,
13808
+ mintDaemonToken,
13809
+ mintSessionId,
13319
13810
  modelContextBlock,
13320
13811
  modifyValidationFeedback,
13321
13812
  normalizeAgentArgs,
@@ -13331,6 +13822,7 @@ var PlannerModelMemory = class {
13331
13822
  providerForRunner,
13332
13823
  reEmitPlanPrompt,
13333
13824
  reEmitTaskOpsPrompt,
13825
+ readDaemonToken,
13334
13826
  referencePattern,
13335
13827
  removeTaskFromPlan,
13336
13828
  renderPlanMap,
@@ -13342,6 +13834,7 @@ var PlannerModelMemory = class {
13342
13834
  resolveArgs,
13343
13835
  resolveDefaultMode,
13344
13836
  resolveModelShortcut,
13837
+ resolvePluginInstallDir,
13345
13838
  resolveProvider,
13346
13839
  resolveProviderFromPrefix,
13347
13840
  resolveResearchShell,
@@ -13372,6 +13865,8 @@ var PlannerModelMemory = class {
13372
13865
  tmuxSocketName,
13373
13866
  tmuxWindowName,
13374
13867
  toOrchestratorOptions,
13868
+ tokenSubprotocols,
13869
+ tokensMatch,
13375
13870
  truncatedPlanReEmitPrompt,
13376
13871
  updateTaskInPlan,
13377
13872
  validateModifiedPlan,