@ordewell/core 0.4.3 → 0.4.4

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.
@@ -439,8 +439,22 @@ interface ResolveContext {
439
439
  /** All variant ids the assigned model offers — lets {{opencodeVariantConfig}} disable the non-chosen ones. */
440
440
  modelVariants?: string[];
441
441
  mode: string;
442
- /** When true, resolve {{if headless}} blocks and the {{feature:headless}} token. */
442
+ /**
443
+ * Autonomy axis: when true, resolve {{if headless}} blocks and the
444
+ * {{feature:headless}} token so the agent never stops to ask for permission.
445
+ * True for every orchestrated task run — nobody is watching the terminal on
446
+ * Ordewell's behalf — independently of the session *shape* below.
447
+ */
443
448
  headless?: boolean;
449
+ /**
450
+ * Session-shape axis: true when the runner is launched onto a real TTY the
451
+ * user can attach to (a tmux window, a VS Code pseudoterminal), so the
452
+ * runner's own TUI should come up rather than its non-interactive
453
+ * subcommand. Defaults to `!headless` for callers that predate the split.
454
+ */
455
+ interactive?: boolean;
456
+ /** The task's working directory — needed by runners whose autonomy flags name a path. */
457
+ cwd?: string;
444
458
  }
445
459
  interface RunnerInvocation {
446
460
  command: string;
@@ -439,8 +439,22 @@ interface ResolveContext {
439
439
  /** All variant ids the assigned model offers — lets {{opencodeVariantConfig}} disable the non-chosen ones. */
440
440
  modelVariants?: string[];
441
441
  mode: string;
442
- /** When true, resolve {{if headless}} blocks and the {{feature:headless}} token. */
442
+ /**
443
+ * Autonomy axis: when true, resolve {{if headless}} blocks and the
444
+ * {{feature:headless}} token so the agent never stops to ask for permission.
445
+ * True for every orchestrated task run — nobody is watching the terminal on
446
+ * Ordewell's behalf — independently of the session *shape* below.
447
+ */
443
448
  headless?: boolean;
449
+ /**
450
+ * Session-shape axis: true when the runner is launched onto a real TTY the
451
+ * user can attach to (a tmux window, a VS Code pseudoterminal), so the
452
+ * runner's own TUI should come up rather than its non-interactive
453
+ * subcommand. Defaults to `!headless` for callers that predate the split.
454
+ */
455
+ interactive?: boolean;
456
+ /** The task's working directory — needed by runners whose autonomy flags name a path. */
457
+ cwd?: string;
444
458
  }
445
459
  interface RunnerInvocation {
446
460
  command: string;
package/dist/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { h as RunnerId, c as PlanStatus, L as LegacyPlanState, d as ResearchProgress, a as DiscoveredModel, T as Task, R as ResearchLogEntry, C as ConversationMessage, n as TaskSnapshot, Q as QueuedMessage, A as ActiveTaskSession, g as ResearchToolType, u as Verdict, e as ResearchStep, b as PlanState } from './Task-1NwmUImI.mjs';
2
2
  export { D as DiscoveredMode, M as Message, P as PlanModificationWarnings, f as ResearchStepOutcome, S as StreamEvent, i as StreamStepEvent, j as StreamThinkingEvent, k as TaskMode, l as TaskModelAssignment, m as TaskOutputSummary, o as TaskStatus, p as TaskType, q as ThinkingBlock, U as UserPromptEntry, r as UserStep, V as ValidationCheck, s as ValidationContext, t as ValidationResult, v as VerificationCheck, w as addTaskToPlan, x as createEmptyPlan, y as createTask, z as emptyWarnings, B as flattenTasks, E as migrateLegacyPlan, F as migratePlanState, G as migrateTask, H as removeTaskFromPlan, I as renumberTasks, J as updateTaskInPlan, K as validateModifiedPlan, N as warningsText } from './Task-1NwmUImI.mjs';
3
- import { n as IFileSystem, I as IApproval, R as ReadFileOpts, T as ToolOutcome, k as GrepOptions, j as GlobOptions, i as FindSymbolOptions, f as ApprovalRequest, m as IConfig, A as AiProvider, y as ProviderModelLists, c as ApprovalMode, q as ITerminalSession, p as ITerminalRunner, J as RunnerRegistry, s as OrchestratorOption, b as ApprovalKind, g as ApprovalSource, E as RunnerInvocation, o as IPluginStore, H as RunnerPluginManifest, B as ResolveContext } from './ITerminalRunner-D8Bf-HD4.mjs';
4
- export { a as AllProviderModels, d as ApprovalPolicy, e as ApprovalPolicyOptions, C as CatalogModel, D as DENY_ALL, h as DiscoveryCommand, F as FetchAllProviderModelsOptions, G as GREP_DEFAULT_HEAD_LIMIT, l as GrepOutputMode, M as ModelCatalog, r as ModelShortcut, O as ORCHESTRATOR_SHORTCUTS, P as PluginEntry, t as PluginFeatures, u as PluginMode, v as PluginModelDiscovery, w as PluginRunnerDef, x as ProviderCredentialSource, z as ProviderModelsResult, S as SEARCH_EXCLUSIONS, K as collectProviderCredentials, L as enabledRunners, N as fetchAllProviderModels, Q as knownModelId, U as resolveModelShortcut, V as resolveProvider, W as toOrchestratorOptions } from './ITerminalRunner-D8Bf-HD4.mjs';
3
+ import { n as IFileSystem, I as IApproval, R as ReadFileOpts, T as ToolOutcome, k as GrepOptions, j as GlobOptions, i as FindSymbolOptions, f as ApprovalRequest, m as IConfig, A as AiProvider, y as ProviderModelLists, c as ApprovalMode, q as ITerminalSession, p as ITerminalRunner, J as RunnerRegistry, s as OrchestratorOption, b as ApprovalKind, g as ApprovalSource, E as RunnerInvocation, o as IPluginStore, H as RunnerPluginManifest, B as ResolveContext } from './ITerminalRunner-YMVsbfem.mjs';
4
+ export { a as AllProviderModels, d as ApprovalPolicy, e as ApprovalPolicyOptions, C as CatalogModel, D as DENY_ALL, h as DiscoveryCommand, F as FetchAllProviderModelsOptions, G as GREP_DEFAULT_HEAD_LIMIT, l as GrepOutputMode, M as ModelCatalog, r as ModelShortcut, O as ORCHESTRATOR_SHORTCUTS, P as PluginEntry, t as PluginFeatures, u as PluginMode, v as PluginModelDiscovery, w as PluginRunnerDef, x as ProviderCredentialSource, z as ProviderModelsResult, S as SEARCH_EXCLUSIONS, K as collectProviderCredentials, L as enabledRunners, N as fetchAllProviderModels, Q as knownModelId, U as resolveModelShortcut, V as resolveProvider, W as toOrchestratorOptions } from './ITerminalRunner-YMVsbfem.mjs';
5
5
  import { R as RunnerModeInfo, b as PlanParseError } from './parsing-xpozY5ac.mjs';
6
6
  export { M as ManifestLookup, P as PLAN_ENVELOPE_KEY, a as PartialPlanTask, T as TASK_OPS_ENVELOPE_KEY, c as buildModeGuide, d as checkDepsResolve, e as checkImmutableLog, f as checkInProgress, g as checkNoCycles, h as checkUniqueIds, i as escapeControlCharsInStrings, j as extractJsonObject, k as extractObjectWithBalance, l as extractObjectsWithKey, m as filteredBuildModes, n as looksLikePlanAttempt, p as parsePartialPlan, o as parsePlanJson, r as resolveDefaultMode, q as resolveTaskMode, s as runnerModesFrom, t as stripModelNoise, u as stripTrailingCommas, v as validatePlanModification } from './parsing-xpozY5ac.mjs';
7
7
  import { T as TaskOp } from './plan-utils-nfHKSbd3.mjs';
@@ -789,8 +789,14 @@ declare class HeadlessRunner extends AbstractRunner<HeadlessSession> {
789
789
  private hasScriptCmd;
790
790
  private resolvePath;
791
791
  private launchDeps;
792
- /** Session shape when the caller names none; the VS Code runner overrides it to interactive. */
793
- protected readonly defaultHeadless: boolean;
792
+ /**
793
+ * Session shape: a piped subprocess is not a terminal, so runners get their
794
+ * non-interactive subcommand. The VS Code runner owns a pseudoterminal and
795
+ * overrides this to true. Autonomy is a separate axis (see `ResolveContext`)
796
+ * and stays on either way — no surface has a human answering permission
797
+ * prompts on the orchestrator's behalf.
798
+ */
799
+ protected readonly defaultInteractive: boolean;
794
800
  constructor(deps?: HeadlessRunnerDeps);
795
801
  protected createSession(id: string, taskId: string): HeadlessSession;
796
802
  /** Everything up to, but not including, spawning — so a surface that owns its own child reaches the same decisions. */
@@ -2706,6 +2712,8 @@ declare function buildRunnerInvocation(opts: {
2706
2712
  modelVariants?: string[];
2707
2713
  mode?: RunnerMode;
2708
2714
  headless?: boolean;
2715
+ interactive?: boolean;
2716
+ cwd?: string;
2709
2717
  registry: RunnerRegistry;
2710
2718
  }): RunnerInvocation;
2711
2719
 
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { h as RunnerId, c as PlanStatus, L as LegacyPlanState, d as ResearchProgress, a as DiscoveredModel, T as Task, R as ResearchLogEntry, C as ConversationMessage, n as TaskSnapshot, Q as QueuedMessage, A as ActiveTaskSession, g as ResearchToolType, u as Verdict, e as ResearchStep, b as PlanState } from './Task-1NwmUImI.js';
2
2
  export { D as DiscoveredMode, M as Message, P as PlanModificationWarnings, f as ResearchStepOutcome, S as StreamEvent, i as StreamStepEvent, j as StreamThinkingEvent, k as TaskMode, l as TaskModelAssignment, m as TaskOutputSummary, o as TaskStatus, p as TaskType, q as ThinkingBlock, U as UserPromptEntry, r as UserStep, V as ValidationCheck, s as ValidationContext, t as ValidationResult, v as VerificationCheck, w as addTaskToPlan, x as createEmptyPlan, y as createTask, z as emptyWarnings, B as flattenTasks, E as migrateLegacyPlan, F as migratePlanState, G as migrateTask, H as removeTaskFromPlan, I as renumberTasks, J as updateTaskInPlan, K as validateModifiedPlan, N as warningsText } from './Task-1NwmUImI.js';
3
- import { n as IFileSystem, I as IApproval, R as ReadFileOpts, T as ToolOutcome, k as GrepOptions, j as GlobOptions, i as FindSymbolOptions, f as ApprovalRequest, m as IConfig, A as AiProvider, y as ProviderModelLists, c as ApprovalMode, q as ITerminalSession, p as ITerminalRunner, J as RunnerRegistry, s as OrchestratorOption, b as ApprovalKind, g as ApprovalSource, E as RunnerInvocation, o as IPluginStore, H as RunnerPluginManifest, B as ResolveContext } from './ITerminalRunner-Bbpe8uts.js';
4
- export { a as AllProviderModels, d as ApprovalPolicy, e as ApprovalPolicyOptions, C as CatalogModel, D as DENY_ALL, h as DiscoveryCommand, F as FetchAllProviderModelsOptions, G as GREP_DEFAULT_HEAD_LIMIT, l as GrepOutputMode, M as ModelCatalog, r as ModelShortcut, O as ORCHESTRATOR_SHORTCUTS, P as PluginEntry, t as PluginFeatures, u as PluginMode, v as PluginModelDiscovery, w as PluginRunnerDef, x as ProviderCredentialSource, z as ProviderModelsResult, S as SEARCH_EXCLUSIONS, K as collectProviderCredentials, L as enabledRunners, N as fetchAllProviderModels, Q as knownModelId, U as resolveModelShortcut, V as resolveProvider, W as toOrchestratorOptions } from './ITerminalRunner-Bbpe8uts.js';
3
+ import { n as IFileSystem, I as IApproval, R as ReadFileOpts, T as ToolOutcome, k as GrepOptions, j as GlobOptions, i as FindSymbolOptions, f as ApprovalRequest, m as IConfig, A as AiProvider, y as ProviderModelLists, c as ApprovalMode, q as ITerminalSession, p as ITerminalRunner, J as RunnerRegistry, s as OrchestratorOption, b as ApprovalKind, g as ApprovalSource, E as RunnerInvocation, o as IPluginStore, H as RunnerPluginManifest, B as ResolveContext } from './ITerminalRunner-D7DwHYSg.js';
4
+ export { a as AllProviderModels, d as ApprovalPolicy, e as ApprovalPolicyOptions, C as CatalogModel, D as DENY_ALL, h as DiscoveryCommand, F as FetchAllProviderModelsOptions, G as GREP_DEFAULT_HEAD_LIMIT, l as GrepOutputMode, M as ModelCatalog, r as ModelShortcut, O as ORCHESTRATOR_SHORTCUTS, P as PluginEntry, t as PluginFeatures, u as PluginMode, v as PluginModelDiscovery, w as PluginRunnerDef, x as ProviderCredentialSource, z as ProviderModelsResult, S as SEARCH_EXCLUSIONS, K as collectProviderCredentials, L as enabledRunners, N as fetchAllProviderModels, Q as knownModelId, U as resolveModelShortcut, V as resolveProvider, W as toOrchestratorOptions } from './ITerminalRunner-D7DwHYSg.js';
5
5
  import { R as RunnerModeInfo, b as PlanParseError } from './parsing-RjN9JbyG.js';
6
6
  export { M as ManifestLookup, P as PLAN_ENVELOPE_KEY, a as PartialPlanTask, T as TASK_OPS_ENVELOPE_KEY, c as buildModeGuide, d as checkDepsResolve, e as checkImmutableLog, f as checkInProgress, g as checkNoCycles, h as checkUniqueIds, i as escapeControlCharsInStrings, j as extractJsonObject, k as extractObjectWithBalance, l as extractObjectsWithKey, m as filteredBuildModes, n as looksLikePlanAttempt, p as parsePartialPlan, o as parsePlanJson, r as resolveDefaultMode, q as resolveTaskMode, s as runnerModesFrom, t as stripModelNoise, u as stripTrailingCommas, v as validatePlanModification } from './parsing-RjN9JbyG.js';
7
7
  import { T as TaskOp } from './plan-utils-Cgc0cCH1.js';
@@ -789,8 +789,14 @@ declare class HeadlessRunner extends AbstractRunner<HeadlessSession> {
789
789
  private hasScriptCmd;
790
790
  private resolvePath;
791
791
  private launchDeps;
792
- /** Session shape when the caller names none; the VS Code runner overrides it to interactive. */
793
- protected readonly defaultHeadless: boolean;
792
+ /**
793
+ * Session shape: a piped subprocess is not a terminal, so runners get their
794
+ * non-interactive subcommand. The VS Code runner owns a pseudoterminal and
795
+ * overrides this to true. Autonomy is a separate axis (see `ResolveContext`)
796
+ * and stays on either way — no surface has a human answering permission
797
+ * prompts on the orchestrator's behalf.
798
+ */
799
+ protected readonly defaultInteractive: boolean;
794
800
  constructor(deps?: HeadlessRunnerDeps);
795
801
  protected createSession(id: string, taskId: string): HeadlessSession;
796
802
  /** Everything up to, but not including, spawning — so a surface that owns its own child reaches the same decisions. */
@@ -2706,6 +2712,8 @@ declare function buildRunnerInvocation(opts: {
2706
2712
  modelVariants?: string[];
2707
2713
  mode?: RunnerMode;
2708
2714
  headless?: boolean;
2715
+ interactive?: boolean;
2716
+ cwd?: string;
2709
2717
  registry: RunnerRegistry;
2710
2718
  }): RunnerInvocation;
2711
2719
 
package/dist/index.js CHANGED
@@ -11374,8 +11374,12 @@ var Block = /* @__PURE__ */ ((Block2) => {
11374
11374
  Block2[Block2["IfInteractive"] = 7] = "IfInteractive";
11375
11375
  Block2[Block2["IfHeadlessSession"] = 8] = "IfHeadlessSession";
11376
11376
  Block2[Block2["IfInteractiveVariant"] = 9] = "IfInteractiveVariant";
11377
+ Block2[Block2["IfProjectTrust"] = 10] = "IfProjectTrust";
11377
11378
  return Block2;
11378
11379
  })(Block || {});
11380
+ function isInteractive(ctx) {
11381
+ return ctx.interactive ?? !ctx.headless;
11382
+ }
11379
11383
  function resolveArgs(manifest, ctx) {
11380
11384
  const args = [];
11381
11385
  const env = {};
@@ -11444,6 +11448,8 @@ function parseBlockOpen(raw) {
11444
11448
  return 8 /* IfHeadlessSession */;
11445
11449
  case "{{if interactiveVariant}}":
11446
11450
  return 9 /* IfInteractiveVariant */;
11451
+ case "{{if projectTrust}}":
11452
+ return 10 /* IfProjectTrust */;
11447
11453
  default:
11448
11454
  return null;
11449
11455
  }
@@ -11465,19 +11471,24 @@ function shouldIncludeBlock(block, ctx) {
11465
11471
  // `--variant` is a headless-only flag: it exists on `opencode run` but not
11466
11472
  // on the interactive TUI, which exits immediately if handed an unknown flag.
11467
11473
  case 6 /* IfVariant */:
11468
- return !!ctx.thinkingEffort && !!ctx.headless;
11474
+ return !!ctx.thinkingEffort && !isInteractive(ctx);
11469
11475
  // Session-shape blocks: unlike IfHeadless (a permission-flag gate with a
11470
11476
  // plan-mode carve-out), these pick the CLI invocation shape — e.g.
11471
11477
  // opencode's `run` subcommand vs its interactive TUI — so no mode carve-out.
11472
11478
  case 7 /* IfInteractive */:
11473
- return !ctx.headless;
11479
+ return isInteractive(ctx);
11474
11480
  case 8 /* IfHeadlessSession */:
11475
- return !!ctx.headless;
11481
+ return !isInteractive(ctx);
11476
11482
  // Interactive counterpart of IfVariant, for runners whose TUI has no
11477
11483
  // variant flag and must receive the variant via env config instead.
11478
11484
  // Requires a provider-prefixed model: the config pins variant per model.
11479
11485
  case 9 /* IfInteractiveVariant */:
11480
- return !!ctx.thinkingEffort && !!ctx.model?.includes("/") && !ctx.headless;
11486
+ return !!ctx.thinkingEffort && !!ctx.model?.includes("/") && isInteractive(ctx);
11487
+ // Guards the flag/value pair that pre-trusts the workspace directory. The
11488
+ // condition, not the token, is what keeps a bare `-c` out of the args when
11489
+ // there is no cwd to name.
11490
+ case 10 /* IfProjectTrust */:
11491
+ return isInteractive(ctx) && !!ctx.cwd;
11481
11492
  default:
11482
11493
  return true;
11483
11494
  }
@@ -11498,6 +11509,10 @@ function resolveToken(token, manifest, ctx) {
11498
11509
  if (token === "{{model}}") return ctx.model || "";
11499
11510
  if (token === "{{thinkingEffort}}") return ctx.thinkingEffort || "";
11500
11511
  if (token === "{{mode}}") return ctx.mode || "build";
11512
+ if (token === "{{projectTrust}}") {
11513
+ if (!ctx.cwd) return "";
11514
+ return `projects.${JSON.stringify(ctx.cwd)}.trust_level="trusted"`;
11515
+ }
11501
11516
  if (token === "{{feature:thinking}}") {
11502
11517
  return manifest.features.thinkingFlag || "";
11503
11518
  }
@@ -11584,7 +11599,9 @@ function buildRunnerInvocation(opts) {
11584
11599
  thinkingEffort: opts.thinkingEffort,
11585
11600
  modelVariants: opts.modelVariants,
11586
11601
  mode: opts.mode ?? "build",
11587
- headless: opts.headless
11602
+ headless: opts.headless,
11603
+ interactive: opts.interactive,
11604
+ cwd: opts.cwd
11588
11605
  });
11589
11606
  }
11590
11607
 
@@ -11705,8 +11722,14 @@ var HeadlessRunner = class extends AbstractRunner {
11705
11722
  hasScriptCmd;
11706
11723
  resolvePath;
11707
11724
  launchDeps;
11708
- /** Session shape when the caller names none; the VS Code runner overrides it to interactive. */
11709
- defaultHeadless = true;
11725
+ /**
11726
+ * Session shape: a piped subprocess is not a terminal, so runners get their
11727
+ * non-interactive subcommand. The VS Code runner owns a pseudoterminal and
11728
+ * overrides this to true. Autonomy is a separate axis (see `ResolveContext`)
11729
+ * and stays on either way — no surface has a human answering permission
11730
+ * prompts on the orchestrator's behalf.
11731
+ */
11732
+ defaultInteractive = false;
11710
11733
  constructor(deps = {}) {
11711
11734
  super();
11712
11735
  this.spawnImpl = deps.spawnImpl ?? import_child_process6.spawn;
@@ -11719,7 +11742,7 @@ var HeadlessRunner = class extends AbstractRunner {
11719
11742
  }
11720
11743
  /** Everything up to, but not including, spawning — so a surface that owns its own child reaches the same decisions. */
11721
11744
  async prepareLaunch(opts) {
11722
- const headless = opts.headless ?? this.defaultHeadless;
11745
+ const interactive = this.defaultInteractive;
11723
11746
  const invocation = buildRunnerInvocation({
11724
11747
  runner: opts.runner,
11725
11748
  prompt: opts.prompt,
@@ -11727,11 +11750,13 @@ var HeadlessRunner = class extends AbstractRunner {
11727
11750
  thinkingEffort: opts.thinkingEffort,
11728
11751
  modelVariants: opts.modelVariants,
11729
11752
  mode: opts.mode,
11730
- headless,
11753
+ headless: opts.headless ?? true,
11754
+ interactive,
11755
+ cwd: opts.cwd,
11731
11756
  registry: opts.registry
11732
11757
  });
11733
11758
  const manifest = opts.registry?.get(opts.runner)?.manifest;
11734
- const pty = (manifest?.runner.requiresTty === true || !headless) && this.hasScriptCmd();
11759
+ const pty = (manifest?.runner.requiresTty === true || interactive) && this.hasScriptCmd();
11735
11760
  const resolvedPath = await this.resolvePath();
11736
11761
  const { command, args } = pty ? wrapWithPty(invocation.command, invocation.args) : { command: invocation.command, args: invocation.args };
11737
11762
  return {
@@ -11975,7 +12000,14 @@ var TmuxRunner = class extends AbstractRunner {
11975
12000
  thinkingEffort: opts.thinkingEffort,
11976
12001
  modelVariants: opts.modelVariants,
11977
12002
  mode: opts.mode,
12003
+ // A tmux window is a real TTY the user can attach to (ADR-0007), so the
12004
+ // runner's own TUI is the point — `interactive`. It is still an
12005
+ // unattended run, so autonomy stays on: the two are separate axes, and
12006
+ // conflating them is what made Codex tasks stream `codex exec` output
12007
+ // into a window that was supposed to show its TUI.
11978
12008
  headless: opts.headless ?? true,
12009
+ interactive: true,
12010
+ cwd: opts.cwd,
11979
12011
  registry: opts.registry
11980
12012
  });
11981
12013
  await this.ensureSession();
@@ -12213,18 +12245,33 @@ var CODEX_MANIFEST = {
12213
12245
  version: "1.0.0",
12214
12246
  runner: {
12215
12247
  command: "codex",
12216
- // Headless surfaces (CLI/web) use the non-interactive `exec` subcommand
12217
- // (`--skip-git-repo-check`: task workspaces aren't always git repos, and
12218
- // exec hard-fails outside one by default); interactive surfaces (VS Code
12219
- // terminal) launch the TUI with the prompt as its positional argument.
12220
- // Reasoning effort has no dedicated flag — it travels as a `-c` config
12221
- // override (see {{feature:reasoningEffortConfig}}). Both `exec` and the
12222
- // TUI accept `-m`, `-c`, and `--sandbox`.
12248
+ // Headless surfaces (a piped subprocess) use the non-interactive `exec`
12249
+ // subcommand (`--skip-git-repo-check`: task workspaces aren't always git
12250
+ // repos, and exec hard-fails outside one by default); interactive surfaces
12251
+ // (a tmux window, the VS Code terminal) launch the TUI with the prompt as
12252
+ // its positional argument. Reasoning effort has no dedicated flag — it
12253
+ // travels as a `-c` config override (see {{feature:reasoningEffortConfig}}).
12254
+ // Both `exec` and the TUI accept `-m`, `-c`, and `--sandbox`.
12255
+ //
12256
+ // The two interactive-only entries restore what `exec` gives implicitly and
12257
+ // the TUI does not: `exec` is non-interactive, so it can neither ask for
12258
+ // command approval nor ask whether the directory is trusted. The TUI asks
12259
+ // for both by default, and an orchestrated task has nobody to answer —
12260
+ // without these it stalls on a menu instead of working. `-a` does not exist
12261
+ // on `exec`, hence the shape gate rather than a `{{if headless}}` one.
12223
12262
  argsTemplate: [
12224
12263
  "{{if headlessSession}}",
12225
12264
  "exec",
12226
12265
  "--skip-git-repo-check",
12227
12266
  "{{/if}}",
12267
+ "{{if interactive}}",
12268
+ "-a",
12269
+ "never",
12270
+ "{{/if}}",
12271
+ "{{if projectTrust}}",
12272
+ "-c",
12273
+ "{{projectTrust}}",
12274
+ "{{/if}}",
12228
12275
  "{{if model}}",
12229
12276
  "-m",
12230
12277
  "{{model}}",
@@ -12244,9 +12291,9 @@ var CODEX_MANIFEST = {
12244
12291
  planMode: true,
12245
12292
  planModeFlag: "--sandbox",
12246
12293
  permissionModeValues: {
12247
- // Map mode IDs to --sandbox CLI values. `codex exec` is non-interactive
12248
- // and has no approval flag, so the sandbox axis is the whole permission
12249
- // story for headless runs.
12294
+ // Map mode IDs to --sandbox CLI values. Approvals are off on both shapes
12295
+ // (implicitly under `exec`, via `-a never` in the TUI), so the sandbox
12296
+ // axis is the whole permission story for a Codex task.
12250
12297
  "agent": "workspace-write",
12251
12298
  "plan": "read-only",
12252
12299
  "fullAccess": "danger-full-access",