tickmarkr 1.71.0 → 1.73.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/adapters/codex.js +13 -3
  2. package/dist/cli/commands/approve.js +21 -3
  3. package/dist/cli/commands/fleet.d.ts +2 -0
  4. package/dist/cli/commands/fleet.js +212 -449
  5. package/dist/cli/commands/resume.js +10 -2
  6. package/dist/cli/commands/status.d.ts +2 -0
  7. package/dist/cli/commands/status.js +219 -30
  8. package/dist/cli/commands/ui.d.ts +6 -2
  9. package/dist/cli/commands/ui.js +2 -4
  10. package/dist/config/config.d.ts +4 -0
  11. package/dist/config/config.js +6 -1
  12. package/dist/drivers/herdr.d.ts +6 -0
  13. package/dist/drivers/herdr.js +169 -71
  14. package/dist/drivers/subprocess.d.ts +3 -3
  15. package/dist/drivers/subprocess.js +10 -5
  16. package/dist/gates/acceptance.js +41 -17
  17. package/dist/route/preference.d.ts +2 -1
  18. package/dist/route/preference.js +8 -2
  19. package/dist/run/daemon.d.ts +1 -0
  20. package/dist/run/daemon.js +139 -4
  21. package/dist/run/git.d.ts +2 -0
  22. package/dist/run/git.js +55 -0
  23. package/dist/run/journal.d.ts +6 -1
  24. package/dist/run/journal.js +28 -1
  25. package/dist/tui/ink/components.d.ts +38 -0
  26. package/dist/tui/ink/components.js +42 -0
  27. package/dist/tui/ink/fleet-app.d.ts +113 -0
  28. package/dist/tui/ink/fleet-app.js +678 -0
  29. package/dist/tui/ink/studio-app.d.ts +59 -0
  30. package/dist/tui/ink/studio-app.js +320 -0
  31. package/package.json +4 -1
  32. package/dist/tui/app.d.ts +0 -72
  33. package/dist/tui/app.js +0 -334
  34. package/dist/tui/engine.d.ts +0 -52
  35. package/dist/tui/engine.js +0 -92
  36. package/dist/tui/frame.d.ts +0 -22
  37. package/dist/tui/frame.js +0 -56
  38. package/dist/tui/input.d.ts +0 -41
  39. package/dist/tui/input.js +0 -100
  40. package/dist/tui/views/consult-dossier.d.ts +0 -49
  41. package/dist/tui/views/consult-dossier.js +0 -169
  42. package/dist/tui/views/diff-modal.d.ts +0 -9
  43. package/dist/tui/views/diff-modal.js +0 -19
  44. package/dist/tui/views/fleet-view.d.ts +0 -91
  45. package/dist/tui/views/fleet-view.js +0 -337
  46. package/dist/tui/views/preview-view.d.ts +0 -7
  47. package/dist/tui/views/preview-view.js +0 -80
  48. package/dist/tui/views/profile-view.d.ts +0 -15
  49. package/dist/tui/views/profile-view.js +0 -93
  50. package/dist/tui/views/routing-view.d.ts +0 -48
  51. package/dist/tui/views/routing-view.js +0 -234
  52. package/dist/tui/views/runs-view.d.ts +0 -70
  53. package/dist/tui/views/runs-view.js +0 -387
@@ -80,6 +80,15 @@ export function readCodexModelsCache(path) {
80
80
  // P87-08: "Unable to create …/.git/worktrees/…/index.lock"). Expanded by the pane shell at the
81
81
  // worktree cwd; in a plain repo it resolves to ./.git (already writable, harmless).
82
82
  const GITDIR_WRITABLE = `-c "sandbox_workspace_write.writable_roots=[\\"$(git rev-parse --path-format=absolute --git-common-dir)\\"]"`;
83
+ // OBS-125: codex 0.144.x gates enabled hooks behind PER-PROJECT-PATH hook trust — a NEW gate distinct
84
+ // from the directory trust seedCodexTrust seeds. Every worker runs in a fresh ephemeral worktree, an
85
+ // untrusted project, so codex re-prompts "Hooks need review" and the interactive worker stalls forever
86
+ // (run-20260722/23 incidents). This flag runs the operator's OWN already-trusted hooks — including the
87
+ // ~/.codex/hooks.json herdr-agent-state hook herdr relies on for agent detection, so the hooks can't
88
+ // just be disabled — WITHOUT requiring persisted per-path trust for this invocation. It KEEPS the
89
+ // -s/--sandbox workspace-write sandbox (deliberately NOT --dangerously-bypass-approvals-and-sandbox,
90
+ // which would drop the sandbox). Listed by `codex --help` and `codex exec --help` (verified 2026-07-23).
91
+ const CODEX_HOOK_TRUST = "--dangerously-bypass-hook-trust";
83
92
  // v1.22 T5 / OBS-16: codex keys trust on absolute path under [projects."<root>"] trust_level="trusted"
84
93
  // in ~/.codex/config.toml (CODEX_HOME relocates the dir). Worktrees inherit parent-project trust when
85
94
  // the REPO ROOT is trusted — seed the root once, cover every future worktree. Idempotent: a second
@@ -171,14 +180,15 @@ export const codex = {
171
180
  channels: (cfg) => channelsFromConfig("codex", cfg),
172
181
  // v1.65 T3: every flag the command builders below hardcode (incl. codexMcpSuppressionFlags' -c/
173
182
  // --disable and GITDIR_WRITABLE's -c) — all listed by top-level `codex --help`, verified 2026-07-22.
174
- hardcodedFlags: { binary: "codex", flags: ["--sandbox", "--model", "-a", "-s", "-c", "--disable"] },
183
+ hardcodedFlags: { binary: "codex", flags: ["--sandbox", "--model", "-a", "-s", "-c", "--disable", "--dangerously-bypass-hook-trust"] },
175
184
  // --sandbox workspace-write is the autonomous sandbox mode (codex v0.144.1+)
176
185
  // MCP suppression built per dispatch (config can change between runs) — see codexMcpSuppressionFlags.
177
- headlessCommand: (promptFile, model) => `codex exec --sandbox workspace-write ${codexMcpSuppressionFlags()} ${GITDIR_WRITABLE} --model ${shq(model)} "$(cat ${shq(promptFile)})"`,
186
+ // CODEX_HOOK_TRUST (OBS-125) clears the per-worktree "Hooks need review" gate while keeping the sandbox.
187
+ headlessCommand: (promptFile, model) => `codex exec --sandbox workspace-write ${CODEX_HOOK_TRUST} ${codexMcpSuppressionFlags()} ${GITDIR_WRITABLE} --model ${shq(model)} "$(cat ${shq(promptFile)})"`,
178
188
  // TUI uses expanded -a never -s workspace-write (exec-only flags do not apply)
179
189
  // (--help 2026-07-09: valid approval policies are untrusted|on-request|never; the previously
180
190
  // used `on-failure` is invalid and made codex exit 2 pre-inference)
181
- interactiveCommand: (promptFile, model) => `codex -a never -s workspace-write ${codexMcpSuppressionFlags()} ${GITDIR_WRITABLE} --model ${shq(model)} "$(cat ${shq(promptFile)})"`,
191
+ interactiveCommand: (promptFile, model) => `codex -a never -s workspace-write ${CODEX_HOOK_TRUST} ${codexMcpSuppressionFlags()} ${GITDIR_WRITABLE} --model ${shq(model)} "$(cat ${shq(promptFile)})"`,
182
192
  invoke(task, _cwd, a, ctx) {
183
193
  return { command: this.headlessCommand(ctx.promptFile, a.model) };
184
194
  },
@@ -1,5 +1,6 @@
1
1
  import { userInfo } from "node:os";
2
- import { ATTEMPT_CAP_RELEASE, Journal } from "../../run/journal.js";
2
+ import { GATE_NAMES } from "../../graph/schema.js";
3
+ import { ATTEMPT_CAP_RELEASE, GATE_SATISFIED_RELEASE, Journal } from "../../run/journal.js";
3
4
  // GATE-08 (v1.12): approve a parked human gate so the next `tickmarkr resume <runId>` dispatches it.
4
5
  //
5
6
  // The approval is a JOURNAL EVENT (task-approved) carrying who and when — it touches ONLY the
@@ -31,15 +32,32 @@ export async function approve(argv, cwd = process.cwd()) {
31
32
  }
32
33
  // OBS-18: only the most recent task-human for this task decides whether this approval grants a
33
34
  // fresh attempt budget. The closed daemon-issued kind, never a human prose string, controls release.
34
- const lastHuman = journal.read().filter((e) => e.event === "task-human" && e.taskId === taskId).at(-1);
35
+ const events = journal.read();
36
+ let lastHumanIndex = -1;
37
+ for (let i = events.length - 1; i >= 0; i--) {
38
+ if (events[i].event === "task-human" && events[i].taskId === taskId) {
39
+ lastHumanIndex = i;
40
+ break;
41
+ }
42
+ }
43
+ const lastHuman = events[lastHumanIndex];
35
44
  const capPark = lastHuman?.data.kind === ATTEMPT_CAP_RELEASE;
45
+ const gateFailPark = lastHuman?.data.kind === "gate-fail";
46
+ const failedGate = gateFailPark
47
+ ? events.slice(0, lastHumanIndex).reverse().find((e) => e.event === "gate-result" && e.taskId === taskId && e.data.pass === false
48
+ && typeof e.data.gate === "string" && GATE_NAMES.includes(e.data.gate))?.data.gate
49
+ : undefined;
50
+ if (gateFailPark && !failedGate) {
51
+ throw new Error(`task ${taskId} is parked on gate-fail but has no failed gate result — refusing to infer one`);
52
+ }
36
53
  journal.append("task-approved", taskId, {
37
54
  by,
38
55
  ...(reason ? { reason } : {}),
39
56
  via: "cli",
40
57
  ...(capPark ? { release: ATTEMPT_CAP_RELEASE } : {}),
58
+ ...(failedGate ? { release: GATE_SATISFIED_RELEASE, gate: failedGate } : {}),
41
59
  });
42
- return `approved ${taskId} in ${runId} — by ${by}; run \`tickmarkr resume ${runId}\` to dispatch it`;
60
+ return `approved ${taskId} in ${runId} — by ${by}; run \`tickmarkr resume ${runId}\` to ${failedGate ? "continue past the approved gate" : "dispatch it"}`;
43
61
  }
44
62
  // hand-parsed argv — no CLI framework (house style). Flags --by <name> and --reason <text>; positionals
45
63
  // are runId then taskId. Throws usage on missing positionals (mirrors resume.ts/unlock.ts).
@@ -12,6 +12,8 @@ export type FleetOutput = {
12
12
  export type FleetIO = {
13
13
  input?: FleetInput;
14
14
  output?: FleetOutput;
15
+ debug?: boolean;
16
+ reloadGuard?: (bytes: string) => string | null;
15
17
  };
16
18
  export declare function fleet(argv: string[], cwd?: string, adapters?: WorkerAdapter[], io?: FleetIO): Promise<string | {
17
19
  out: string;