sortie-dogs 0.13.7 → 0.13.9

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 (57) hide show
  1. package/.agents/skills/sortie-dogs/SKILL.md +53 -0
  2. package/.agents/skills/sortie-dogs/agents/openai.yaml +6 -0
  3. package/.agents/skills/sortie-dogs/sortie-dogs.version +1 -0
  4. package/README.md +227 -9
  5. package/dist/asset-version.d.ts +3 -2
  6. package/dist/asset-version.js +2 -1
  7. package/dist/cli/codex-mission.d.ts +7 -0
  8. package/dist/cli/codex-mission.js +118 -0
  9. package/dist/cli/main.js +96 -0
  10. package/dist/codex/app-server.d.ts +135 -0
  11. package/dist/codex/app-server.js +366 -0
  12. package/dist/codex/mission-session.d.ts +86 -0
  13. package/dist/codex/mission-session.js +598 -0
  14. package/dist/codex/mission-settlement.d.ts +53 -0
  15. package/dist/codex/mission-settlement.js +176 -0
  16. package/dist/codex/mission-telemetry.d.ts +9 -0
  17. package/dist/codex/mission-telemetry.js +37 -0
  18. package/dist/codex/protected-evidence.d.ts +27 -0
  19. package/dist/codex/protected-evidence.js +77 -0
  20. package/dist/codex/run-mission.d.ts +28 -0
  21. package/dist/codex/run-mission.js +310 -0
  22. package/dist/codex/skill-assets.d.ts +2 -0
  23. package/dist/codex/skill-assets.js +27 -0
  24. package/dist/core/durable-scope-root.d.ts +2 -0
  25. package/dist/core/durable-scope-root.js +43 -0
  26. package/dist/core/goal-bound.d.ts +10 -0
  27. package/dist/core/goal-bound.js +5 -0
  28. package/dist/core/initialize.d.ts +2 -0
  29. package/dist/core/initialize.js +15 -0
  30. package/dist/core/observed-goal-evidence.d.ts +1 -1
  31. package/dist/core/operator-mission.d.ts +73 -1
  32. package/dist/core/operator-mission.js +124 -9
  33. package/dist/core/operator-runtime.d.ts +6 -0
  34. package/dist/core/operator-runtime.js +45 -1
  35. package/dist/core/validation-budget.d.ts +1 -0
  36. package/dist/core/worktree-scope.js +1 -1
  37. package/dist/index.d.ts +11 -1
  38. package/dist/index.js +6 -1
  39. package/dist/plugin/index.d.ts +3 -0
  40. package/dist/plugin/index.js +33 -62
  41. package/dist/plugin/mission-review.d.ts +10 -6
  42. package/dist/plugin/mission-review.js +31 -18
  43. package/dist/plugin/profiled.d.ts +2 -1
  44. package/dist/plugin/profiled.js +145 -39
  45. package/dist/plugin/protected-snapshot.js +13 -10
  46. package/dist/plugin/run-metrics.d.ts +1 -1
  47. package/dist/plugin/run-metrics.js +17 -6
  48. package/dist/plugin/runtime-bridge.d.ts +4 -1
  49. package/dist/plugin/sortie-debrief.js +3 -0
  50. package/dist/plugin/tool-schema.d.ts +4 -0
  51. package/dist/plugin/tool-schema.js +58 -0
  52. package/dist/plugin/v2-session-history.d.ts +2 -0
  53. package/dist/plugin/v2-session-history.js +5 -0
  54. package/dist/plugin/v2.d.ts +1 -0
  55. package/dist/plugin/v2.js +5 -59
  56. package/dist/runtime-mission-assets.js +3 -1
  57. package/package.json +3 -2
@@ -0,0 +1,53 @@
1
+ ---
2
+ name: sortie-dogs
3
+ description: Run a task through the installed Sortie-dogs Codex Mission adapter when the user explicitly invokes $sortie-dogs. Preserve the existing Mission ledger and keep OpenCode configuration separate.
4
+ ---
5
+
6
+ # Sortie-dogs
7
+
8
+ Treat the user's text accompanying `$sortie-dogs` as the Mission request. If no task was supplied, ask for it and stop.
9
+
10
+ ## Boundaries
11
+
12
+ - Use the reviewed, locally installed `sortie-dogs` package. Never install or update a package, use `@latest`, publish, or switch authentication as part of invocation.
13
+ - Require the existing Codex CLI ChatGPT authentication. Do not use an API key or metered API fallback.
14
+ - Do not run `sortie-dogs init`; that command configures OpenCode. This skill uses only `sortie-dogs codex ...` and never edits `.opencode`.
15
+ - Do not create another ledger. The Codex adapter must reuse Sortie's existing Mission state, protected evidence, and scope leases.
16
+ - After starting a child Mission, observe it; do not edit the same project concurrently from the parent Codex chat.
17
+ - Preserve the user's authority boundaries. Invocation does not authorize push, publication, dependency installation, broader permissions, or unrelated changes.
18
+
19
+ ## Resolve the local entrypoint
20
+
21
+ Prefer `npx --no-install sortie-dogs` when the current project has the package installed. In the Sortie-dogs source repository itself, use `node dist/cli/main.js` after the repository's normal build when needed. Do not resolve a missing command from the network.
22
+
23
+ Check the selected entrypoint with `<entrypoint> codex --help` before starting work. If the packaged command lacks `codex mission` and `codex run`, report that the installed candidate is too old.
24
+
25
+ ## Choose one route
26
+
27
+ On a POSIX host with `/bin/bash`, use the natural-language Mission route:
28
+
29
+ ```text
30
+ <entrypoint> codex mission --project-root <repository-root> --prompt <exact-user-request>
31
+ ```
32
+
33
+ Forward `--model`, `--effort`, or `--permissions` only when the user selected those values. Never silently widen the native permission profile. If an existing Mission is reported, resume only its exact root with `--resume`; do not launch replacement work.
34
+
35
+ On Windows, use the manifest-bound route until the full Mission shell path supports Windows natively:
36
+
37
+ 1. Read repository instructions and inspect only enough to identify bounded project-relative read scope, write scope, and meaningful validation commands.
38
+ 2. Create one temporary manifest under `.sortie-dogs/codex-skill/` with `version`, `task_id`, `read`, `write`, and non-empty `validation`. Reject external or uncertain write scope instead of broadening it.
39
+ 3. Resolve the exact PowerShell executable with `(Get-Command pwsh.exe -ErrorAction Stop).Source`. Invoke:
40
+
41
+ ```text
42
+ <entrypoint> codex run --project-root <repository-root> --manifest <temporary-manifest> --prompt <exact-user-request> --trusted-pwsh <resolved-pwsh.exe>
43
+ ```
44
+
45
+ 4. Remove only the temporary manifest created by this invocation after the command reaches a terminal result. Preserve `.git/sortie-dogs` evidence and any other state.
46
+
47
+ If no defensible validation or bounded write scope can be derived, explain the missing contract and stop without starting Codex.
48
+
49
+ ## Completion
50
+
51
+ Treat only Mission acceptance or manifest settlement `succeeded` with admitted validation evidence as success. A completed native turn, model prose, or validation exit zero without admitted evidence is not completion.
52
+
53
+ Report the route, terminal status, root/thread identity, validation evidence count, changed paths, and any explicit remaining boundary. Subscription monetary cost is unavailable unless the host supplies it; never fabricate one.
@@ -0,0 +1,6 @@
1
+ interface:
2
+ display_name: "Sortie-dogs"
3
+ short_description: "Run a verified Sortie Mission through Codex"
4
+ default_prompt: "Use $sortie-dogs to execute and verify this task through the existing Mission workflow."
5
+ policy:
6
+ allow_implicit_invocation: false
@@ -0,0 +1 @@
1
+ 0.13.9-codex-skill-v1
package/README.md CHANGED
@@ -35,10 +35,10 @@ implementation, validation, review, and model routing.
35
35
  Guides: [日本語](docs/guide-ja.md) · [简体中文](docs/guide-zh-CN.md) ·
36
36
  [Testing](docs/testing.md) · [CLI testing](docs/cli-testing.md)
37
37
 
38
- **Current release: [v0.13.7](https://github.com/zufall-upon/Sortie-dogs/releases/tag/v0.13.7)**
39
- ([release notes](docs/release-v0.13.7.md)). The default Mission runtime retains the `v010`
38
+ **Current release: [v0.13.9](https://github.com/zufall-upon/Sortie-dogs/releases/tag/v0.13.9)**
39
+ ([release notes](docs/release-v0.13.9.md)). The default Mission runtime retains the `v010`
40
40
  profile, command and configuration names for compatibility; these names do not mean v0.10 is installed.
41
- The current asset marker is `0.13.7-review-recovery-v1`.
41
+ The current asset marker is `0.13.9-codex-operation-v1`.
42
42
 
43
43
  ## SWE-bench Lite: 170/300 (56.67%)
44
44
 
@@ -48,7 +48,7 @@ The fixed **Sortie-dogs v0.12.24** harness resolved **170 of 300 SWE-bench Lite
48
48
 
49
49
  The single official 300-instance report and frozen predictions are hash-bound in the report. Confirmed inference expense was **$162.99**; a separate **$34.60** of usage has unknown pricing and is held against the campaign cap, **not** counted as known expense. Leaderboard registration and maintainer acceptance are separate from this official local evaluation.
50
50
 
51
- Historical scores below belong to their fixed candidates, not v0.13.7. SWE-bench is a separate,
51
+ Historical scores below belong to their fixed candidates, not v0.13.9. SWE-bench is a separate,
52
52
  optional measurement rather than a mandatory release gate.
53
53
 
54
54
  > **Beta:** v0.13.x is still stabilizing. Runtime behavior,
@@ -56,7 +56,203 @@ optional measurement rather than a mandatory release gate.
56
56
 
57
57
  ## Quick start
58
58
 
59
- Requirements: Node.js 22.6 or newer, npm, and OpenCode V2.
59
+ ### Codex host adapter (experimental)
60
+
61
+ Choose this route for Codex; OpenCode is not required and **do not run `sortie-dogs init` for it**.
62
+ Use Node.js 22.6 or newer and an existing Codex CLI with ChatGPT authentication. Ubuntu validation
63
+ used Node.js 22.22.1 and Codex 0.160.1. Sortie neither installs Codex nor starts a login flow, copies
64
+ credentials, or creates a second host configuration. Mission execution refuses non-ChatGPT auth.
65
+
66
+ The Codex Mission adapter and explicit skill are included in v0.13.9. Install the release in the
67
+ target project and inspect the entrypoint before running a prompt:
68
+
69
+ ```sh
70
+ npm install --save-dev sortie-dogs@0.13.9
71
+ npx --no-install sortie-dogs codex init .
72
+ npx --no-install sortie-dogs codex mission --help
73
+ ```
74
+
75
+ For an unpublished development candidate, a maintainer with checkout dependencies already present
76
+ can produce a local tarball with `npm pack` (build included) and install that tarball instead.
77
+ This does not publish it. Use the same installed version for the CLI and SDK below.
78
+ `codex init` installs only `.agents/skills/sortie-dogs`; it does not create or edit `.opencode`.
79
+ It refuses to overwrite a skill whose ownership cannot be established.
80
+
81
+ After restarting Codex or opening the project in a new chat, invoke the installed skill explicitly:
82
+
83
+ ```text
84
+ $sortie-dogs Implement and verify the requested change
85
+ ```
86
+
87
+ `$sortie-dogs` is the current direct Skills syntax. Codex does not expose arbitrary custom commands
88
+ as exact `/sortie-dogs` slash commands; `/skills` opens the skill picker. The skill is intentionally
89
+ explicit-only so child Codex Mission turns cannot invoke another Sortie Mission recursively.
90
+ On POSIX it selects the natural-language Mission route below. On Windows it derives a bounded
91
+ operation manifest and uses `codex run`, which retains the documented post-execution observation
92
+ boundary instead of claiming a pre-execution guard.
93
+
94
+ Sortie-dogs also exports a host adapter for the official Codex app-server stdio protocol. This is
95
+ separate from selecting an OpenAI model through OpenCode: OpenCode model routing still uses the
96
+ OpenCode host, while `CodexAppServerHost` starts and observes native Codex threads and turns.
97
+
98
+ ```ts
99
+ import { CodexAppServerHost, createCodexAppServerTransport } from "sortie-dogs";
100
+
101
+ const transport = createCodexAppServerTransport({ executable: "codex" });
102
+ const host = new CodexAppServerHost(transport);
103
+ const threadID = await host.startThread({ cwd: process.cwd(), ephemeral: true });
104
+ const result = await host.runTurn(threadID, "Implement the admitted unit.", {
105
+ cwd: process.cwd(),
106
+ approvalPolicy: "unlessTrusted",
107
+ sandboxPolicy: {
108
+ type: "workspaceWrite",
109
+ writableRoots: [process.cwd()],
110
+ networkAccess: false,
111
+ },
112
+ });
113
+ await host.close();
114
+ ```
115
+
116
+ The adapter reuses Sortie's host-neutral core instead of copying its goal, acceptance, evidence, or
117
+ ledger implementation. It exposes completed Codex items as authoritative observations, supports
118
+ exact persisted-thread resume and turn interruption (ephemeral threads have no resumable rollout), and declines command/file approvals unless the caller
119
+ provides an approval handler. Permission-subset requests receive an empty grant unless the caller
120
+ connects `permissionsApproval`; approved responses are limited to the native request. Unsupported
121
+ server-initiated requests fail closed.
122
+
123
+ For the existing Operator → Coordinator → Worker → Reviewer Mission workflow, use:
124
+
125
+ ```sh
126
+ npx --no-install sortie-dogs codex mission --prompt "Implement and review the requested change" --model gpt-6.1-sol --effort medium
127
+ ```
128
+
129
+ The equivalent public SDK entrypoint is:
130
+
131
+ ```ts
132
+ import { CodexMissionSession } from "sortie-dogs";
133
+
134
+ const mission = await CodexMissionSession.create({
135
+ projectRoot: process.cwd(),
136
+ model: "gpt-6.1-sol", // Choose a model available to the existing signed-in account.
137
+ effort: "medium",
138
+ // resumeThreadID: "saved-root-thread-id", // Continue the same Mission when needed.
139
+ });
140
+ try {
141
+ const result = await mission.run("Implement and review the requested change");
142
+ console.log(JSON.stringify(result));
143
+ process.exitCode = result.accepted ? 0 : 1;
144
+ } finally {
145
+ await mission.close();
146
+ }
147
+ ```
148
+
149
+ This Linux-first route uses `CodexMissionSession` and the existing Mission tools, correction grants,
150
+ validation evidence, and final Operator acceptance. It runs saved native Codex threads with existing
151
+ ChatGPT authentication; it does not import OpenCode settings or introduce a second Mission ledger.
152
+ Commands run through `/bin/bash` using the native app-server's configured permissions. Sortie does not
153
+ replace them with a fixed repository-only or network-disabled policy, change host configuration, or select
154
+ full access. Thread turns retain native thread permissions; standalone `command/exec` uses the server's
155
+ configured policy, not a thread's temporary grants. A parent application's in-memory approval is not
156
+ automatically transferred to a separately launched app-server.
157
+ SDK hosts can forward native command/file approval requests through `approval` and permission-subset
158
+ requests through `permissionsApproval`. The native host remains responsible for deciding and enforcing
159
+ the grant. The CLI reports approval requests but has no interactive approval bridge; without a connected
160
+ host callback, no grant is returned. `command/exec` has no thread-scoped approval API, so inheriting the
161
+ configured policy does not add an escalation path for these standalone commands.
162
+ Use SDK `permissions` or CLI `--permissions <native-profile>` only to explicitly select an existing
163
+ native profile for thread start/resume and standalone commands. Omission retains native defaults,
164
+ which may be read-only. Invalid or disallowed profiles fail through the native server without fallback;
165
+ no configuration file is changed. Low-level `CodexAppServerHost` callers must opt into
166
+ `experimentalApi: true` for profile APIs; Mission sessions already negotiate that capability. Progress reports the effective native profile, sandbox and approval
167
+ routing. This selection does not configure a delegated parent-host executor's own permissions.
168
+ See [Ubuntu acceptance evidence and remaining work](docs/codex-mission-acceptance-20261006.md).
169
+
170
+ For a parent application with an existing approval-aware executor, pass the optional SDK
171
+ `executeCommand` callback. It receives the exact post-hook argv, cwd, timeout, thread/turn/call identity,
172
+ and an AbortSignal for bash/read/write operations. The parent owns approval and execution; Sortie does
173
+ not infer a grant from prompt text or create another permission store. Return `completed` only after
174
+ execution ends, with the real integer exitCode, stdout and stderr. Approval alone is not a result.
175
+ `denied` and `not-started` explicitly mean nothing ran and create no validation exit. `interrupted`,
176
+ `unknown`, exceptions and invalid results retain unknown execution and stop resends. The host owns
177
+ command timeout enforcement; Sortie does not add a separate deadline to its approval interaction.
178
+ Closing the adapter signals cancellation; late callback results cannot establish validation, and the
179
+ host must reconcile any still-running process. Omission keeps native `command/exec`; a connected
180
+ executor never falls back to native execution after rejection or failure. Existing Mission before/after
181
+ hooks, command identity and validation acceptance remain shared. CLI progress identifies the selected
182
+ executor; the CLI itself does not attach a parent executor.
183
+ The bash tool accepts `workdir` (absolute or relative to the project root). The shared hooks and both
184
+ executors use that directory; progress and receipts include the effective cwd. A successful command in
185
+ another directory does not satisfy a validation declared for the project root.
186
+
187
+ Packaged role models and reasoning levels apply by default. `--model` and `--effort` explicitly
188
+ override all roles; SDK callers can use `roleModels` for individual roles. Unsupported native model
189
+ names fail rather than silently selecting another model. Availability depends on the signed-in account.
190
+
191
+ Use `--resume <root-thread-id>` to continue a saved Mission root. Without it, a single unfinished Codex
192
+ Mission in this repository is selected automatically; multiple unfinished roots require an explicit
193
+ selection. Resume restores native messages and tool evidence, including child sessions. Completed native
194
+ receipts feed the existing reservation reconciliation without replaying tool execution. A new Task stopped
195
+ before child binding can be recovered when the exact terminal parent thread/turn/call/input matches and
196
+ the prior adapter is closed or its Linux process identity proves it is gone. The host retains that proof in
197
+ the existing Mission and reconciles the reservation before another model prompt; repeated recovery does
198
+ not settle it again. Concurrent recovery claims are serialized.
199
+
200
+ A newly created ordinary Worker also saves its exact dispatch binding before the child prompt is sent.
201
+ If its parent Task result is lost, recovery can match that binding to the child's single, fully loaded,
202
+ completed native turn. The initial recovery path supports leaf Workers only: later turns, nested Tasks,
203
+ untracked native children, missing bindings and ambiguous identities remain unknown. Recovery restores
204
+ execution completion for the existing settlement path; missing validation evidence is a process defect,
205
+ not an invented PASS. It does not rerun the implementation. Continue with only the work or validation
206
+ still needed for normal Operator acceptance.
207
+
208
+ SIGINT/SIGTERM closes the adapter and exits 130/143. Bound children without an exact completed dispatch,
209
+ and commands sent without a saved terminal receipt, remain unknown and are not resent. App-server exit
210
+ does not prove an external command has stopped. Linux process-death checks do not prove external writer
211
+ quiescence either; other platforms require a clean adapter close for automatic prelaunch recovery.
212
+ Inspect the original executor before recovery; do not delete Mission state to force a retry.
213
+ Per-session usage is native thread cumulative usage (including earlier turns), not a Mission total;
214
+ monetary cost is unavailable and reported as `null`.
215
+ The existing Mission report uses observed per-turn token deltas and execution times. Cache and reasoning
216
+ subtotals are counted once. Its pre-terminal snapshot excludes final-answer generation, while final JSON
217
+ retains native thread totals. Terminal accounting observations survive cold reload in the existing Mission;
218
+ missing baselines remain unavailable. Native turn aggregates do not establish model-request counts, API
219
+ costs or remaining subscription allowance, so these values are not inferred.
220
+ CLI exit 0 means Operator acceptance; exit 1 means incomplete work or an execution error; exit 2
221
+ means invalid arguments. A native turn completing alone is not acceptance.
222
+ The CLI writes bounded progress records to stderr: tool start/completion, commands and exit codes,
223
+ replan reasons, next actions, child identity, and public model commentary. Final JSON remains on stdout.
224
+
225
+ For a manifest-bound mission, use the SDK `runCodexMission(...)` or the CLI:
226
+
227
+ ```sh
228
+ sortie-dogs codex run --manifest operation-manifest.json --prompt "Implement the requested change"
229
+ ```
230
+
231
+ The operation manifest remains required. The mission runner reuses Sortie's existing goal ledger,
232
+ protected evidence capture, settlement, and repository-wide scope leases. It does not emulate
233
+ OpenCode plugin hooks, install Codex assets, switch models implicitly, or copy/import/modify OpenCode
234
+ settings and sessions. Codex command and file events are post-execution observations, not a claimed
235
+ pre-execution write guard. The stdio transport is the supported initial integration; experimental
236
+ WebSocket transport is intentionally out of scope.
237
+
238
+ The manifest-run CLI handles SIGINT/SIGTERM by requesting interruption and cleaning up its
239
+ app-server (exit 130/143). SDK callers can pass an `AbortSignal` as `signal`. Before
240
+ turn dispatch, cancellation settles the reservation and releases its lease. After
241
+ dispatch, app-server exit or an interrupt acknowledgement does not prove that an
242
+ externally hosted command stopped. Cancellation or transport loss therefore preserves
243
+ the active goal/reservation as an unknown outcome and stops lease heartbeats without
244
+ claiming completion. SIGKILL can leave the same unresolved state.
245
+
246
+ Lease expiry alone does not authorize a retry: subsequent Codex missions refuse with
247
+ `codex-mission-outcome-unknown:no-resend` and the ledger path, including across
248
+ stable/v010 profiles. Inspect the prior worker and ledger before recovery; do not
249
+ delete the ledger or automatically resend the prompt. This is a Codex admission
250
+ check; it does not establish quiescence of external command executors.
251
+
252
+ ### OpenCode plugin
253
+
254
+ Requirements: Node.js 22.6 or newer, npm, and OpenCode V2. These installation and `init` steps
255
+ configure the OpenCode route; they are not prerequisites for the Codex route above.
60
256
 
61
257
  Run these commands in the target project:
62
258
 
@@ -93,7 +289,29 @@ existing local bridge loads enforcement and model routing. OpenCode can reload w
93
289
  but replacing an installed dependency may require a full restart. A new chat session alone does not
94
290
  prove the newly installed plugin is loaded.
95
291
 
96
- ## v0.13.7 runtime updates
292
+ ## v0.13.9 runtime updates
293
+
294
+ PRs #163–#166 connect native Codex sessions to the existing Mission lifecycle, add the explicit
295
+ `$sortie-dogs` skill and `codex init`, restore operation Reviewer correction within existing write
296
+ scope, and reconcile native terminal observations without reviving completed work from delayed events.
297
+ Codex settings and authentication remain separate from OpenCode. Real Ubuntu Codex acceptance
298
+ evidence and host-specific limits remain in [the acceptance record](docs/codex-mission-acceptance-20261006.md);
299
+ they are not a new all-host or performance benchmark. Release CLI proof observes actual OpenCode
300
+ Worker startup and model identity only, not Mission completion. See [release notes](docs/release-v0.13.9.md).
301
+
302
+ ## v0.13.8 runtime updates
303
+
304
+ PR #161 captures native validation bindings through the readable resolved target of a Windows
305
+ directory junction instead of enumerating its failing logical alias. Logical evidence labels,
306
+ link identity and complete target bytes remain bound; unchanged readable junctions retain their
307
+ evidence identity. External-link/cycle handling, actual source/output freshness and Review remain
308
+ unchanged. No new approval, permissions or model changes. The PR's isolated native Windows fixture
309
+ reached independent Review PASS and accepted completion after resuming the same Mission, without
310
+ rerunning its successful check; this does not claim original product recovery or uninterrupted
311
+ single-turn completion. Release receipts: `_testenv/releases/0.13.8/`;
312
+ see [release notes](docs/release-v0.13.8.md).
313
+
314
+ ## v0.13.7 runtime updates (retained)
97
315
 
98
316
  PRs #158/#159 prevent late write-only reports from falsely invalidating bound non-generating
99
317
  native checks, while preserving actual source/output freshness, failed checks and legacy recipes.
@@ -436,7 +654,7 @@ register stable and `v010` from the same package installation path in one host.
436
654
  Project-local installation is recommended. To expose the current Mission assets globally:
437
655
 
438
656
  ```sh
439
- npm install --global sortie-dogs@0.13.7
657
+ npm install --global sortie-dogs@0.13.9
440
658
  sortie-dogs init --global --profile v010
441
659
  ```
442
660
 
@@ -448,7 +666,7 @@ can resolve a **separate dependency** under that config root. Updating npm-globa
448
666
  it. For that layout, also install the same release at the actual config root, then rerun global init:
449
667
 
450
668
  ```sh
451
- npm install --prefix "$HOME/.config/opencode" sortie-dogs@0.13.7
669
+ npm install --prefix "$HOME/.config/opencode" sortie-dogs@0.13.9
452
670
  sortie-dogs init --global --profile v010
453
671
  ```
454
672
 
@@ -471,7 +689,7 @@ version, preserves user configuration, and stops safely on unknown ownership or
471
689
  conflicting files.
472
690
 
473
691
  Align any exact version pin or separate bridge dependency with the intended release too. An installed
474
- marker of `0.13.7-review-recovery-v1` identifies the assets; it does not prove an already-running
692
+ marker of `0.13.9-codex-operation-v1` identifies the assets; it does not prove an already-running
475
693
  OpenCode process has reloaded the plugin.
476
694
 
477
695
  There is no supported uninstall command. Remove the npm dependency separately,
@@ -3,5 +3,6 @@
3
3
  * installed project marker without importing every asset body.
4
4
  */
5
5
  export declare const RUNTIME_ASSET_VERSION = "0.3.89-completion-proof-v1";
6
- export declare const V010_RUNTIME_ASSET_VERSION = "0.13.7-review-recovery-v1";
7
- export type RuntimeAssetVersion = typeof RUNTIME_ASSET_VERSION | typeof V010_RUNTIME_ASSET_VERSION;
6
+ export declare const V010_RUNTIME_ASSET_VERSION = "0.13.9-codex-operation-v1";
7
+ export declare const CODEX_SKILL_ASSET_VERSION = "0.13.9-codex-skill-v1";
8
+ export type RuntimeAssetVersion = typeof RUNTIME_ASSET_VERSION | typeof V010_RUNTIME_ASSET_VERSION | typeof CODEX_SKILL_ASSET_VERSION;
@@ -3,4 +3,5 @@
3
3
  * installed project marker without importing every asset body.
4
4
  */
5
5
  export const RUNTIME_ASSET_VERSION = "0.3.89-completion-proof-v1";
6
- export const V010_RUNTIME_ASSET_VERSION = "0.13.7-review-recovery-v1";
6
+ export const V010_RUNTIME_ASSET_VERSION = "0.13.9-codex-operation-v1";
7
+ export const CODEX_SKILL_ASSET_VERSION = "0.13.9-codex-skill-v1";
@@ -0,0 +1,7 @@
1
+ import type { CodexTurnEvent } from "../codex/app-server.js";
2
+ export declare const CODEX_MISSION_USAGE = "Usage: sortie-dogs codex mission --prompt <text>\n [--resume <root-thread-id>] [--project-root <path>] [--executable <codex>] [--model <model>] [--effort <effort>] [--permissions <native-profile>]\n\nRuns the existing Mission Operator, Coordinator, Worker and Reviewer through Codex.\n--model and --effort explicitly override all roles; otherwise packaged role defaults apply.\n--permissions explicitly selects an existing native profile; omission retains native defaults.\nUses existing ChatGPT authentication and native Codex permissions.";
3
+ export declare function runCodexMissionCommand(argv: readonly string[]): Promise<number>;
4
+ /** Bounded user-facing progress; do not dump control packets or hidden reasoning. */
5
+ export declare function codexMissionProgress(event: CodexTurnEvent & {
6
+ threadId: string;
7
+ }): Record<string, unknown> | undefined;
@@ -0,0 +1,118 @@
1
+ import { CodexMissionSession } from "../codex/mission-session.js";
2
+ export const CODEX_MISSION_USAGE = `Usage: sortie-dogs codex mission --prompt <text>
3
+ [--resume <root-thread-id>] [--project-root <path>] [--executable <codex>] [--model <model>] [--effort <effort>] [--permissions <native-profile>]
4
+
5
+ Runs the existing Mission Operator, Coordinator, Worker and Reviewer through Codex.
6
+ --model and --effort explicitly override all roles; otherwise packaged role defaults apply.
7
+ --permissions explicitly selects an existing native profile; omission retains native defaults.
8
+ Uses existing ChatGPT authentication and native Codex permissions.`;
9
+ export async function runCodexMissionCommand(argv) {
10
+ if (argv.length === 1 && argv[0] === "--help") {
11
+ process.stdout.write(`${CODEX_MISSION_USAGE}\n`);
12
+ return 0;
13
+ }
14
+ const values = new Map();
15
+ const allowed = new Set(["--prompt", "--resume", "--project-root", "--executable", "--model", "--effort", "--permissions"]);
16
+ for (let index = 0; index < argv.length; index += 2) {
17
+ const key = argv[index], value = argv[index + 1];
18
+ if (!key || !allowed.has(key) || !value || value.startsWith("--") || values.has(key)) {
19
+ process.stderr.write(`${CODEX_MISSION_USAGE}\n`);
20
+ return 2;
21
+ }
22
+ values.set(key, value);
23
+ }
24
+ const prompt = values.get("--prompt");
25
+ if (!prompt) {
26
+ process.stderr.write(`${CODEX_MISSION_USAGE}\n`);
27
+ return 2;
28
+ }
29
+ let adapter;
30
+ let signalExit;
31
+ const stop = (exit) => { signalExit ??= exit; void adapter?.close().catch(() => undefined); };
32
+ const onTerm = () => stop(143), onInt = () => stop(130);
33
+ process.on("SIGTERM", onTerm);
34
+ process.on("SIGINT", onInt);
35
+ try {
36
+ adapter = await CodexMissionSession.create({ projectRoot: values.get("--project-root") ?? process.cwd(),
37
+ resumeThreadID: values.get("--resume"), permissions: values.get("--permissions"), executable: values.get("--executable"), model: values.get("--model"), effort: values.get("--effort"), onEvent: event => {
38
+ const progress = codexMissionProgress(event);
39
+ if (progress)
40
+ process.stderr.write(`${JSON.stringify(progress)}\n`);
41
+ } });
42
+ if (signalExit)
43
+ return signalExit;
44
+ const result = await adapter.run(prompt);
45
+ process.stdout.write(`${JSON.stringify({ status: result.accepted ? "succeeded" : "incomplete", root_session_id: result.rootSessionID,
46
+ thread_id: result.turn.threadID, turn_id: result.turn.turnID, native_status: result.turn.status,
47
+ response: result.turn.finalResponse, sessions: result.sessions, cost: null })}\n`);
48
+ return signalExit ?? (result.accepted ? 0 : 1);
49
+ }
50
+ catch (error) {
51
+ process.stderr.write(`${error instanceof Error ? error.message : "codex-mission-failed"}\n`);
52
+ return signalExit ?? 1;
53
+ }
54
+ finally {
55
+ try {
56
+ await adapter?.close();
57
+ }
58
+ finally {
59
+ process.off("SIGTERM", onTerm);
60
+ process.off("SIGINT", onInt);
61
+ }
62
+ }
63
+ }
64
+ /** Bounded user-facing progress; do not dump control packets or hidden reasoning. */
65
+ export function codexMissionProgress(event) {
66
+ if (event.method === "sortie/permissions")
67
+ return { thread_id: event.threadId, phase: "permissions", requested_profile: event.params.requestedProfile,
68
+ effective_profile: event.params.profile, sandbox: event.params.sandbox, network_access: event.params.networkAccess,
69
+ approval_policy: event.params.approvalPolicy, approvals_reviewer: event.params.approvalsReviewer,
70
+ command_executor: event.params.commandExecutor };
71
+ if (event.method === "sortie/commandExecution")
72
+ return { thread_id: event.threadId, turn_id: event.params.turnId, call_id: event.params.callId,
73
+ tool: event.params.tool, executor: event.params.executor, cwd: event.params.cwd, phase: event.params.status,
74
+ detail: typeof event.params.reason === "string" ? event.params.reason.slice(0, 1600) : undefined };
75
+ if (["item/commandExecution/requestApproval", "item/fileChange/requestApproval", "item/permissions/requestApproval"].includes(event.method))
76
+ return { thread_id: event.threadId, turn_id: event.params.turnId, phase: "approval-required", method: event.method,
77
+ reason: typeof event.params.reason === "string" ? event.params.reason.slice(0, 1600) : undefined,
78
+ command: typeof event.params.command === "string" ? event.params.command.slice(0, 1600) : undefined,
79
+ item_id: event.params.itemId, host_approval_available: event.params.hostApprovalAvailable === true,
80
+ next_action: event.params.hostApprovalAvailable === true ? "Await the host approval decision." :
81
+ "No host approval callback is connected; this request receives no grant. Use a host-integrated SDK client for approval." };
82
+ if (!["item/started", "item/completed"].includes(event.method))
83
+ return;
84
+ const item = event.params.item;
85
+ if (event.method === "item/completed" && item?.type === "agentMessage" && item.phase === "commentary" && typeof item.text === "string")
86
+ return { thread_id: event.threadId, turn_id: event.params.turnId, phase: "commentary", text: item.text.slice(0, 1600) };
87
+ if (item?.type !== "dynamicToolCall")
88
+ return;
89
+ const record = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
90
+ const bounded = (value) => typeof value === "string" ? value.slice(0, 1600) : undefined;
91
+ const args = record(item.arguments) ? item.arguments : {};
92
+ let receipt = {};
93
+ const contents = Array.isArray(item.contentItems) ? item.contentItems : [];
94
+ const text = contents.find(part => record(part) && part.type === "inputText")?.text;
95
+ try {
96
+ const parsed = JSON.parse(String(text));
97
+ if (record(parsed))
98
+ receipt = parsed;
99
+ }
100
+ catch { /* Plain native failure. */ }
101
+ if (typeof receipt.output === "string") {
102
+ try {
103
+ const nested = JSON.parse(receipt.output);
104
+ if (record(nested))
105
+ receipt = { ...nested, ...receipt };
106
+ }
107
+ catch { /* Ordinary command output. */ }
108
+ }
109
+ const metadata = record(receipt.metadata) ? receipt.metadata : {};
110
+ return { thread_id: event.threadId, turn_id: event.params.turnId, tool: item.tool,
111
+ phase: event.method === "item/started" ? "started" : "completed", status: item.status, success: item.success,
112
+ command: bounded(args.command), workdir: bounded(args.workdir), cwd: bounded(metadata.cwd), reason: bounded(args.reason),
113
+ agent: bounded(args.subagent_type), description: bounded(args.description), child_session_id: bounded(metadata.sessionId),
114
+ outcome: bounded(receipt.status), exit: metadata.exit, executor: metadata.executor,
115
+ next_action: bounded(receipt.next_action),
116
+ summary: bounded(args.summary ?? receipt.summary),
117
+ detail: bounded(receipt.reason ?? receipt.error ?? (item.success === false ? text : undefined)) };
118
+ }
package/dist/cli/main.js CHANGED
@@ -32,6 +32,19 @@ const USAGE = `Usage: sortie-dogs lint <handoff.json> [<handoff.json> ...]
32
32
  const INIT_USAGE = `Usage: sortie-dogs init [project-root] [--profile stable|v010]
33
33
  sortie-dogs init --global [--profile stable|v010]
34
34
  This beta package defaults to the v010 profile.`;
35
+ const CODEX_USAGE = `Natural-language Mission: sortie-dogs codex mission --prompt <text> (mission --help for options)
36
+
37
+ Install the direct Codex skill: sortie-dogs codex init [project-root]
38
+
39
+ Usage: sortie-dogs codex run --manifest <operation-manifest.json> --prompt <text>
40
+ [--project-root <path>] [--executable <codex>] [--model <model>] [--effort <effort>]
41
+ [--trusted-pwsh <absolute-pwsh.exe>] [--profile stable|v010]
42
+
43
+ The operation manifest is required. This command reuses Sortie's goal ledger and
44
+ scope leases; it does not import or modify OpenCode settings or sessions.`;
45
+ const CODEX_INIT_USAGE = `Usage: sortie-dogs codex init [project-root]
46
+
47
+ Installs the explicit $sortie-dogs skill under .agents/skills without modifying OpenCode settings.`;
35
48
  class InputFailure extends Error {
36
49
  safeMessage;
37
50
  constructor(safeMessage) {
@@ -225,6 +238,89 @@ function render(output, format) {
225
238
  return output.map(({ file, pointer, code, severity, message }) => `${file} ${neutralizeTextControlCharacters(pointer || "/")} ${code} ${severity} ${neutralizeTextControlCharacters(message)}\n`).join("");
226
239
  }
227
240
  export async function run(argv) {
241
+ if (argv[0] === "codex") {
242
+ if (argv[1] === "init") {
243
+ if (argv[2] === "--help" && argv.length === 3) {
244
+ process.stdout.write(`${CODEX_INIT_USAGE}\n`);
245
+ return 0;
246
+ }
247
+ if (argv.length > 3 || argv[2]?.startsWith("-")) {
248
+ process.stderr.write(`${CODEX_INIT_USAGE}\n`);
249
+ return 2;
250
+ }
251
+ try {
252
+ const initialized = await initializer.initializeCodexSkill(argv[2]);
253
+ process.stdout.write(initialized.status === "installed"
254
+ ? `Installed the $sortie-dogs Codex skill (${initialized.version}).\n`
255
+ : `The $sortie-dogs Codex skill (${initialized.version}) is already installed.\n`);
256
+ return 0;
257
+ }
258
+ catch (error) {
259
+ process.stderr.write(`${error instanceof Error ? error.message : "codex-skill-init-failed"}\n`);
260
+ return 1;
261
+ }
262
+ }
263
+ if (argv[1] === "mission")
264
+ return (await import("./codex-mission.js")).runCodexMissionCommand(argv.slice(2));
265
+ if (argv.length === 3 && argv[1] === "run" && argv[2] === "--help") {
266
+ process.stdout.write(`${CODEX_USAGE}\n`);
267
+ return 0;
268
+ }
269
+ if (argv[1] === "--help" && argv.length === 2) {
270
+ process.stdout.write(`${CODEX_USAGE}\n`);
271
+ return 0;
272
+ }
273
+ if (argv[1] !== "run") {
274
+ process.stderr.write(`${CODEX_USAGE}\n`);
275
+ return 2;
276
+ }
277
+ const values = new Map();
278
+ const allowed = new Set(["--manifest", "--prompt", "--project-root", "--executable", "--model", "--effort", "--trusted-pwsh", "--profile"]);
279
+ for (let index = 2; index < argv.length; index += 2) {
280
+ const key = argv[index], value = argv[index + 1];
281
+ if (!key || !allowed.has(key) || value === undefined || value.startsWith("--") || values.has(key)) {
282
+ process.stderr.write(`${CODEX_USAGE}\n`);
283
+ return 2;
284
+ }
285
+ values.set(key, value);
286
+ }
287
+ const manifest = values.get("--manifest"), prompt = values.get("--prompt"), profile = values.get("--profile") ?? "stable";
288
+ if (!manifest || !prompt || !["stable", "v010"].includes(profile)) {
289
+ process.stderr.write(`${CODEX_USAGE}\n`);
290
+ return 2;
291
+ }
292
+ const cancellation = new AbortController();
293
+ let signalExit;
294
+ const onTerm = () => { signalExit ??= 143; cancellation.abort(); };
295
+ const onInt = () => { signalExit ??= 130; cancellation.abort(); };
296
+ process.on("SIGTERM", onTerm);
297
+ process.on("SIGINT", onInt);
298
+ try {
299
+ const runner = await import("../codex/run-mission.js");
300
+ const profiles = await import("../core/runtime-profile.js");
301
+ const result = await runner.runCodexMission({ signal: cancellation.signal, projectRoot: values.get("--project-root") ?? process.cwd(), manifestPath: manifest,
302
+ prompt, ...(values.get("--executable") ? { executable: values.get("--executable") } : {}),
303
+ ...(values.get("--model") ? { model: values.get("--model") } : {}),
304
+ ...(values.get("--effort") ? { effort: values.get("--effort") } : {}),
305
+ ...(values.get("--trusted-pwsh") ? { trustedPowerShellExecutable: values.get("--trusted-pwsh") } : {}),
306
+ profile: profile === "stable" ? profiles.STABLE_RUNTIME_PROFILE : profiles.V010_RUNTIME_PROFILE });
307
+ process.stdout.write(`${JSON.stringify({ status: result.settlement.disposition, root_session_id: result.rootSessionID,
308
+ thread_id: result.validation.threadID, turn_id: result.validation.turnID, ledger_path: result.ledgerPath,
309
+ evidence: result.settlement.evidence.length,
310
+ validation_commands: result.validation.items.filter(item => item.type === "commandExecution").map(item => ({
311
+ command: item.command, status: item.status, exit_code: item.exitCode,
312
+ })) })}\n`);
313
+ return signalExit ?? (result.settlement.disposition === "succeeded" ? 0 : 1);
314
+ }
315
+ catch (error) {
316
+ process.stderr.write(`${error instanceof Error ? error.message : "codex-mission-failed"}\n`);
317
+ return signalExit ?? 1;
318
+ }
319
+ finally {
320
+ process.off("SIGTERM", onTerm);
321
+ process.off("SIGINT", onInt);
322
+ }
323
+ }
228
324
  if (argv[0] === "init") {
229
325
  if (argv[1] === "--help" && argv.length === 2) {
230
326
  process.stdout.write(`${INIT_USAGE}\n`);