sortie-dogs 0.14.1 → 0.14.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/dist/asset-version.d.ts +1 -1
- package/dist/asset-version.js +1 -1
- package/dist/cli/codex-mission.js +3 -0
- package/dist/codex/mission-session.d.ts +2 -1
- package/dist/codex/mission-session.js +57 -5
- package/dist/core/operator-mission.d.ts +3 -0
- package/dist/plugin/profiled.js +3 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,8 +19,8 @@ evidence-backed result, not more agents for their own sake.
|
|
|
19
19
|
[](https://www.npmjs.com/package/sortie-dogs)
|
|
20
20
|
[](LICENSE)
|
|
21
21
|
|
|
22
|
-
**Current release: [v0.14.
|
|
23
|
-
· [Release notes](docs/release-v0.14.
|
|
22
|
+
**Current release: [v0.14.2](https://github.com/zufall-upon/Sortie-dogs/releases/tag/v0.14.2)**
|
|
23
|
+
· [Release notes](docs/release-v0.14.2.md) · [Execution vision](docs/v0.14.0-execution-vision.md)
|
|
24
24
|
|
|
25
25
|
[Quick start](#quick-start) · [Workflow](#mission-workflow) · [Models](#default-models) ·
|
|
26
26
|
[Measured results](#measured-results) · [Configuration](#configuration) · [Documentation](#documentation)
|
|
@@ -57,7 +57,7 @@ Use an existing Codex CLI signed in with **ChatGPT**. On Windows, PowerShell **7
|
|
|
57
57
|
available; POSIX uses bash. Sortie does not install Codex, start login or use a metered API fallback.
|
|
58
58
|
|
|
59
59
|
```sh
|
|
60
|
-
npm install --save-dev sortie-dogs@0.14.
|
|
60
|
+
npm install --save-dev sortie-dogs@0.14.2
|
|
61
61
|
npx --no-install sortie-dogs codex init .
|
|
62
62
|
```
|
|
63
63
|
|
|
@@ -86,7 +86,7 @@ bridge. [Codex guide: SDK, permissions, progress, resume and manifest route](doc
|
|
|
86
86
|
Use **OpenCode V2**. In the target project:
|
|
87
87
|
|
|
88
88
|
```sh
|
|
89
|
-
npm install --save-dev sortie-dogs@0.14.
|
|
89
|
+
npm install --save-dev sortie-dogs@0.14.2
|
|
90
90
|
npx --no-install sortie-dogs init .
|
|
91
91
|
```
|
|
92
92
|
|
|
@@ -242,7 +242,7 @@ Install the desired package version and rerun **your host's** initializer:
|
|
|
242
242
|
|
|
243
243
|
An OpenCode config-local bridge may resolve a separate package; npm-global update alone does not
|
|
244
244
|
update it. [Global/update instructions](docs/configuration.md#global-availability).
|
|
245
|
-
The current Mission asset marker is `0.14.
|
|
245
|
+
The current Mission asset marker is `0.14.2-cold-resume-v1`; the unchanged Codex skill marker is
|
|
246
246
|
`0.13.11-codex-skill-v2`. Installed markers alone do not prove a running host reloaded the new version.
|
|
247
247
|
|
|
248
248
|
There is no supported uninstall command. Remove the dependency and only known Sortie-owned paths;
|
|
@@ -258,7 +258,7 @@ follow the [manual removal guide](docs/uninstall.md), never delete the entire `.
|
|
|
258
258
|
[SWE-bench history](docs/benchmarks/swebench-lite-history.md) · [Historical local case study](docs/benchmark-reference.md).
|
|
259
259
|
- **Development:** [Testing](docs/testing.md) · [CLI testing](docs/cli-testing.md) ·
|
|
260
260
|
[Windows tests](docs/windows-tests.md) · [Release routine](docs/release-batch.md).
|
|
261
|
-
- **Releases:** [v0.14.1](docs/release-v0.14.1.md) · [v0.14.0](docs/release-v0.14.0.md) · [v0.13.22](docs/release-v0.13.22.md) · [v0.13.21](docs/release-v0.13.21.md) · [v0.13.20](docs/release-v0.13.20.md) · [v0.13.19](docs/release-v0.13.19.md) · [v0.13.18](docs/release-v0.13.18.md) · [v0.13.17](docs/release-v0.13.17.md) · [v0.13.16](docs/release-v0.13.16.md) · [v0.13.15](docs/release-v0.13.15.md) · [v0.13.14](docs/release-v0.13.14.md) · [v0.13.13](docs/release-v0.13.13.md) · [v0.13.12](docs/release-v0.13.12.md) · [v0.13.11](docs/release-v0.13.11.md) · [v0.13.10](docs/release-v0.13.10.md) ·
|
|
261
|
+
- **Releases:** [v0.14.2](docs/release-v0.14.2.md) · [v0.14.1](docs/release-v0.14.1.md) · [v0.14.0](docs/release-v0.14.0.md) · [v0.13.22](docs/release-v0.13.22.md) · [v0.13.21](docs/release-v0.13.21.md) · [v0.13.20](docs/release-v0.13.20.md) · [v0.13.19](docs/release-v0.13.19.md) · [v0.13.18](docs/release-v0.13.18.md) · [v0.13.17](docs/release-v0.13.17.md) · [v0.13.16](docs/release-v0.13.16.md) · [v0.13.15](docs/release-v0.13.15.md) · [v0.13.14](docs/release-v0.13.14.md) · [v0.13.13](docs/release-v0.13.13.md) · [v0.13.12](docs/release-v0.13.12.md) · [v0.13.11](docs/release-v0.13.11.md) · [v0.13.10](docs/release-v0.13.10.md) ·
|
|
262
262
|
[v0.13.9](docs/release-v0.13.9.md) · [All GitHub releases](https://github.com/zufall-upon/Sortie-dogs/releases).
|
|
263
263
|
|
|
264
264
|
## Community
|
package/dist/asset-version.d.ts
CHANGED
|
@@ -3,6 +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.14.
|
|
6
|
+
export declare const V010_RUNTIME_ASSET_VERSION = "0.14.2-cold-resume-v1";
|
|
7
7
|
export declare const CODEX_SKILL_ASSET_VERSION = "0.13.11-codex-skill-v2";
|
|
8
8
|
export type RuntimeAssetVersion = typeof RUNTIME_ASSET_VERSION | typeof V010_RUNTIME_ASSET_VERSION | typeof CODEX_SKILL_ASSET_VERSION;
|
package/dist/asset-version.js
CHANGED
|
@@ -3,5 +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.14.
|
|
6
|
+
export const V010_RUNTIME_ASSET_VERSION = "0.14.2-cold-resume-v1";
|
|
7
7
|
export const CODEX_SKILL_ASSET_VERSION = "0.13.11-codex-skill-v2";
|
|
@@ -71,6 +71,9 @@ export function codexMissionProgress(event) {
|
|
|
71
71
|
native_status: event.params.turn?.status };
|
|
72
72
|
if (event.method === "sortie/continuation")
|
|
73
73
|
return { thread_id: event.threadId, turn_id: event.params.turnId, phase: event.params.phase, calls: event.params.calls };
|
|
74
|
+
if (event.method === "sortie/coordinatorRestored")
|
|
75
|
+
return { thread_id: event.threadId, phase: "coordinator-restored", mission_id: event.params.missionId,
|
|
76
|
+
coordinator_id: event.params.coordinatorId, call_id: event.params.callId, native_outcome: event.params.outcome, next_action: event.params.action };
|
|
74
77
|
if (event.method === "sortie/modelRoute")
|
|
75
78
|
return { thread_id: event.threadId, phase: "model-route", agent: event.params.agent, model: event.params.model,
|
|
76
79
|
effort: event.params.effort, service_tier: event.params.serviceTier };
|
|
@@ -79,8 +79,9 @@ export declare class CodexMissionSession {
|
|
|
79
79
|
close(): Promise<void>;
|
|
80
80
|
private claimOwner;
|
|
81
81
|
private rootOf;
|
|
82
|
+
private restoreCoordinator;
|
|
82
83
|
private recoverNotStarted;
|
|
83
|
-
private
|
|
84
|
+
private bindTaskDispatch;
|
|
84
85
|
private recoverCompletedWorker;
|
|
85
86
|
private required;
|
|
86
87
|
private executeHostCommand;
|
|
@@ -62,6 +62,7 @@ export class CodexMissionSession {
|
|
|
62
62
|
this.observations = missions.find(item => item.root === resume)?.codexUsage ?? [];
|
|
63
63
|
await this.restoreSession(resume, "dog-operator");
|
|
64
64
|
this.root = resume;
|
|
65
|
+
await this.restoreCoordinator(resume);
|
|
65
66
|
await this.claimOwner(resume);
|
|
66
67
|
await this.hooks.tool?.sortie_v010_operator_status?.execute({ view: "full" }, { sessionID: resume, agent: "dog-operator" });
|
|
67
68
|
}
|
|
@@ -118,6 +119,45 @@ export class CodexMissionSession {
|
|
|
118
119
|
current = this.required(current.parentID);
|
|
119
120
|
return current.id;
|
|
120
121
|
}
|
|
122
|
+
async restoreCoordinator(root) {
|
|
123
|
+
const missions = new OperatorMissionRuntime(this.directory, profile);
|
|
124
|
+
const mission = await missions.required(root);
|
|
125
|
+
if (!mission.coordinator || ["completed", "cancelled"].includes(mission.phase))
|
|
126
|
+
return;
|
|
127
|
+
if (!await codexOwnerGone(mission.codexOwner))
|
|
128
|
+
throw new Error("codex-mission-owner-active:no-resend");
|
|
129
|
+
// Codex can persist claim() and the child turn but omit the interrupted parent
|
|
130
|
+
// Task item. The existing Mission binding still owns this same Coordinator.
|
|
131
|
+
const coordinator = await this.restoreSession(mission.coordinator, "dogs-coordinator", root);
|
|
132
|
+
if (coordinator.parentID !== root || coordinator.agent !== "dogs-coordinator")
|
|
133
|
+
throw new Error("Retained Coordinator lineage mismatch.");
|
|
134
|
+
if (mission.dispatchOpen && mission.callID) {
|
|
135
|
+
// A retained terminal parent receipt already has the shared reconciliation path.
|
|
136
|
+
// The new binding is required only when that exact receipt is missing.
|
|
137
|
+
const parentReceipts = this.required(root).history.flatMap(message => Array.isArray(message.parts) ? message.parts.filter(object) : [])
|
|
138
|
+
.filter(part => part.type === "tool" && part.tool === "task" && part.callID === mission.callID && object(part.state) &&
|
|
139
|
+
["completed", "error"].includes(String(part.state.status)) && object(part.state.input) &&
|
|
140
|
+
part.state.input.subagent_type === coordinator.agent &&
|
|
141
|
+
(part.state.input.task_id === undefined || part.state.input.task_id === coordinator.id));
|
|
142
|
+
if (parentReceipts.length === 1)
|
|
143
|
+
return;
|
|
144
|
+
const binding = mission.codexCoordinatorDispatch;
|
|
145
|
+
if (!binding || binding.missionID !== mission.id || binding.threadID !== root || binding.callID !== mission.callID ||
|
|
146
|
+
binding.childThreadID !== coordinator.id || binding.ownerGeneration !== mission.codexOwner?.generation ||
|
|
147
|
+
coordinator.history.filter(message => object(message.info) && message.info.role === "user" &&
|
|
148
|
+
message.info.id === binding.clientUserMessageID).length !== 1)
|
|
149
|
+
throw new Error("Retained Coordinator has unproven execution for the current dispatch; no resend. Reconcile its exact native turn before resuming.");
|
|
150
|
+
await missions.codexRecovery(root, async (current) => {
|
|
151
|
+
if (current.codexOwner?.generation !== binding.ownerGeneration || !await codexOwnerGone(current.codexOwner) ||
|
|
152
|
+
JSON.stringify(current.codexCoordinatorDispatch) !== JSON.stringify(binding))
|
|
153
|
+
throw new Error("codex-mission-recovery-generation-stale");
|
|
154
|
+
await missions.reconcileFinishedDispatch(root, mission.id, mission.callID);
|
|
155
|
+
});
|
|
156
|
+
await this.options.onEvent?.({ method: "sortie/coordinatorRestored", threadId: root,
|
|
157
|
+
params: { missionId: mission.id, coordinatorId: coordinator.id, callId: mission.callID,
|
|
158
|
+
outcome: coordinator.outcome, action: "resume-existing-task" } });
|
|
159
|
+
}
|
|
160
|
+
}
|
|
121
161
|
async recoverNotStarted(threadID, turn, item) {
|
|
122
162
|
if (item.tool !== "task" || !object(item.arguments) || item.arguments.task_id || typeof item.id !== "string" || typeof turn.id !== "string" ||
|
|
123
163
|
!["completed", "interrupted", "failed"].includes(String(turn.status)))
|
|
@@ -156,7 +196,18 @@ export class CodexMissionSession {
|
|
|
156
196
|
}
|
|
157
197
|
return false;
|
|
158
198
|
}
|
|
159
|
-
async
|
|
199
|
+
async bindTaskDispatch(parent, child, call, messageID) {
|
|
200
|
+
if (child.agent === "dogs-coordinator") {
|
|
201
|
+
await new OperatorMissionRuntime(this.directory, profile).update(this.rootOf(parent), state => {
|
|
202
|
+
if (state.root !== call.threadId || state.coordinator !== child.id || state.callID !== call.callId || !state.dispatchOpen ||
|
|
203
|
+
state.codexOwner?.generation !== this.owner.generation || state.codexOwner.closed)
|
|
204
|
+
throw new Error("codex-mission-dispatch-owner-stale");
|
|
205
|
+
state.codexCoordinatorDispatch = { missionID: state.id, threadID: call.threadId, turnID: call.turnId, callID: call.callId,
|
|
206
|
+
inputHash: createHash("sha256").update(JSON.stringify(call.arguments)).digest("hex"),
|
|
207
|
+
childThreadID: child.id, clientUserMessageID: messageID, ownerGeneration: this.owner.generation };
|
|
208
|
+
});
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
160
211
|
if (child.agent !== "dog-worker-v010" || !object(call.arguments) || call.arguments.task_id)
|
|
161
212
|
return;
|
|
162
213
|
const root = this.rootOf(parent), missions = new OperatorMissionRuntime(this.directory, profile);
|
|
@@ -365,7 +416,7 @@ export class CodexMissionSession {
|
|
|
365
416
|
throw new Error(`Codex thread ${id} has no matching repository history.`);
|
|
366
417
|
const turns = thread.turns.filter(object);
|
|
367
418
|
for (const turn of turns) {
|
|
368
|
-
if (!Array.isArray(turn.items) ||
|
|
419
|
+
if (!Array.isArray(turn.items) || !["completed", "failed", "interrupted", "cancelled"].includes(String(turn.status)) || turn.itemsView !== "full")
|
|
369
420
|
throw new Error(`Codex thread ${id} still has unproven execution; no resend. Reconcile its native execution before resuming.`);
|
|
370
421
|
for (const item of turn.items.filter(object)) {
|
|
371
422
|
// A terminal parent turn does not prove that its native executions or children stopped.
|
|
@@ -373,7 +424,8 @@ export class CodexMissionSession {
|
|
|
373
424
|
["commandExecution", "fileChange", "mcpToolCall"].includes(String(item.type)) &&
|
|
374
425
|
!["completed", "failed", ...(item.type === "mcpToolCall" ? [] : ["declined"])].includes(String(item.status)))
|
|
375
426
|
throw new Error(`Codex thread ${id} has an unresolved native execution; no resend. Reconcile its native execution before resuming.`);
|
|
376
|
-
if (item.type !== "dynamicToolCall" ||
|
|
427
|
+
if (item.type !== "dynamicToolCall" || ["completed", "failed"].includes(String(item.status)) &&
|
|
428
|
+
(turn.status === "completed" || item.success === true))
|
|
377
429
|
continue;
|
|
378
430
|
const unique = turns.flatMap(entry => Array.isArray(entry.items) ? entry.items.filter(object) : []).filter(entry => entry.id === item.id).length === 1;
|
|
379
431
|
if (!unique)
|
|
@@ -475,7 +527,7 @@ export class CodexMissionSession {
|
|
|
475
527
|
result = await this.promptTurn(session, "The previous native response ended while existing host tools were still running. Those exact calls have now returned:\n" +
|
|
476
528
|
JSON.stringify(result.deferredToolResults) +
|
|
477
529
|
"\nContinue this same Mission from these results through the remaining validation, delivery, review and root acceptance. " +
|
|
478
|
-
"Do not resend the original request or redispatch the completed Tasks. Preserve failures, ownership and cumulative budget; use existing Mission status for the next action.", undefined, true);
|
|
530
|
+
"Do not resend the original request or redispatch the completed Tasks. Preserve failures, ownership and cumulative budget; use existing Mission status for the next action.", session.agent === "dogs-coordinator" ? onDispatch : undefined, true);
|
|
479
531
|
}
|
|
480
532
|
return result;
|
|
481
533
|
}
|
|
@@ -577,7 +629,7 @@ export class CodexMissionSession {
|
|
|
577
629
|
});
|
|
578
630
|
await this.hooks["tool.execute.after"]?.({ tool: call.tool, sessionID: session.id, callID: call.callId, args }, { output: "", metadata: { sessionId: child.id, status: "running" } });
|
|
579
631
|
const result = await this.prompt(child, String(args.prompt), async (messageID) => {
|
|
580
|
-
await this.
|
|
632
|
+
await this.bindTaskDispatch(session, child, call, messageID);
|
|
581
633
|
dispatched = true;
|
|
582
634
|
});
|
|
583
635
|
text = result.finalResponse ?? "";
|
|
@@ -49,6 +49,7 @@ export interface CodexWorkerDispatch {
|
|
|
49
49
|
clientUserMessageID: string;
|
|
50
50
|
ownerGeneration: string;
|
|
51
51
|
}
|
|
52
|
+
export type CodexCoordinatorDispatch = Omit<CodexWorkerDispatch, "runID" | "unitID">;
|
|
52
53
|
export interface CodexCompletedTaskProof {
|
|
53
54
|
dispatch: CodexWorkerDispatch;
|
|
54
55
|
childTurnID: string;
|
|
@@ -209,6 +210,8 @@ export interface OperatorMission {
|
|
|
209
210
|
codexNotStarted?: CodexNotStartedProof[];
|
|
210
211
|
/** Missing parent receipts reconciled from exact completed native Worker turns. */
|
|
211
212
|
codexCompletedTasks?: CodexCompletedTaskProof[];
|
|
213
|
+
/** Current Coordinator dispatch bound before its exact native turn is sent. */
|
|
214
|
+
codexCoordinatorDispatch?: CodexCoordinatorDispatch;
|
|
212
215
|
version: "0.12";
|
|
213
216
|
id: string;
|
|
214
217
|
root: string;
|
package/dist/plugin/profiled.js
CHANGED
|
@@ -1228,6 +1228,9 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
1228
1228
|
return { ...packet, next_action: "Fast-lane: dispatch the exact Reviewer Task from review_mission if not yet active; otherwise wait for that Reviewer. Do not start a Coordinator or another Worker while review is pending." };
|
|
1229
1229
|
}
|
|
1230
1230
|
if (mission.runID === run?.runID && run?.units.some(unit => unit.status === "running")) {
|
|
1231
|
+
if (mission.coordinator && !mission.dispatchOpen && !["completed", "cancelled"].includes(mission.phase))
|
|
1232
|
+
return { ...packet, task: missions.task(mission),
|
|
1233
|
+
next_action: "Resume this existing Coordinator Task with its task_id to continue the retained unit. Do not dispatch a replacement or wait for background work that has already ended." };
|
|
1231
1234
|
return { ...packet, next_action: "The current unit is active. Continue its owning session; do not dispatch a replacement Worker or Coordinator. Native background completion will return to the parent." };
|
|
1232
1235
|
}
|
|
1233
1236
|
if (mission.coordinator === null && mission.runID === run?.runID && !mission.dispatchOpen &&
|