sortie-dogs 0.10.1 → 0.10.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 CHANGED
@@ -59,9 +59,13 @@ See [configuration details](#configuration) for model selection and other setup
59
59
  > **Project status: Beta.** v0.10.x is under active stabilization. Runtime
60
60
  > behavior, configuration, and runtime assets may still change before 1.0.
61
61
 
62
- [![npm](https://img.shields.io/npm/v/sortie-dogs)](https://www.npmjs.com/package/sortie-dogs)
63
- [![license](https://img.shields.io/npm/l/sortie-dogs)](LICENSE)
64
- [![Node.js](https://img.shields.io/node/v/sortie-dogs)](https://www.npmjs.com/package/sortie-dogs)
62
+ [![GitHub Release](https://img.shields.io/github/v/release/zufall-upon/Sortie-dogs)](https://github.com/zufall-upon/Sortie-dogs/releases/latest)
63
+ [![npm](https://img.shields.io/npm/v/sortie-dogs?label=npm)](https://www.npmjs.com/package/sortie-dogs)
64
+ [![Tests](https://github.com/zufall-upon/Sortie-dogs/actions/workflows/test.yml/badge.svg)](https://github.com/zufall-upon/Sortie-dogs/actions/workflows/test.yml)
65
+ [![OpenCode Plugin](https://img.shields.io/badge/OpenCode-Plugin-5C5CFF)](https://opencode.ai/)
66
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.9-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
67
+ [![Node.js](https://img.shields.io/node/v/sortie-dogs)](https://www.npmjs.com/package/sortie-dogs)
68
+ [![MIT License](https://img.shields.io/npm/l/sortie-dogs)](LICENSE)
65
69
 
66
70
  ![Sortie-dogs coordinating a bounded implementation workflow](https://raw.githubusercontent.com/zufall-upon/Sortie-dogs/main/docs/assets/sortie-workflow.gif)
67
71
 
@@ -70,7 +74,7 @@ bounded implementation, canonical validation, and evidence-backed completion.
70
74
 
71
75
  Guides: [日本語](docs/guide-ja.md) · [简体中文](docs/guide-zh-CN.md) · [テスト実行](docs/testing.md) · [CLI testing](docs/cli-testing.md)
72
76
 
73
- Release: [v0.10.1](https://github.com/zufall-upon/Sortie-dogs/releases/tag/v0.10.1)
77
+ Release: [v0.10.2](https://github.com/zufall-upon/Sortie-dogs/releases/tag/v0.10.2)
74
78
 
75
79
  ## Latest local benchmark case study
76
80
 
@@ -110,23 +114,28 @@ time, and cost aggregates; their attempt count and estimated cost remain visible
110
114
 
111
115
  The v0.10.1 RC value is a standalone qualification-only reference, not a fourth matched run or a
112
116
  FrontierHarness leaderboard result. It used no fresh Bare control and a localized Docker-free
113
- verifier. The RC label identifies an unreleased working-tree snapshot; its packed metadata remained
114
- `0.10.0`, fixed by package SHA-256 `67582ee023c3bd57813262e292c067a3f90f30d61c01d8caf6f91f09ae78f61d`.
117
+ verifier. The RC label identifies an unreleased working-tree snapshot; the benchmark tarball's
118
+ package metadata remained `0.10.0`, fixed by package SHA-256
119
+ `67582ee023c3bd57813262e292c067a3f90f30d61c01d8caf6f91f09ae78f61d`.
115
120
 
116
121
  Its estimated API-equivalent cost is **$4.478613**: $3.23 Sol and $1.25 Terra when rounded to cents.
117
122
  The audit prices all root and descendant assistant messages: 84 priced requests, 4,215,698 tokens,
118
123
  100% pricing coverage, and no unpriced requests. It uses the product's 2026-09-14 Standard schedule
119
124
  per request, including cached input, reasoning as output, and the long-context rate band. The earlier
120
- delivery-incomplete run used a different packed source snapshot, so it is not folded into this RC's
121
- same-snapshot acquisition cost. Historical Bare and v0.9.12 costs retain their 2026-07-30 schedule;
122
- their cost cells are not same-rate comparisons with this RC estimate.
125
+ delivery-incomplete package was a different source snapshot (`32dea0a3…`): it spent **$4.276578**
126
+ across 90 priced requests and 3,704,649 tokens, with no terminal outcome or verifier. It is not folded
127
+ into this RC's same-snapshot acquisition cost. Observed spend across both distinct snapshots was
128
+ **$8.755191**, which is development spend rather than this RC's acquisition cost. Historical Bare and
129
+ v0.9.12 costs retain their 2026-07-30 schedule; their cost cells are not same-rate comparisons with
130
+ this RC estimate.
123
131
 
124
132
  ![Latest local case study: Bare completed 11.1 percent of task checks at a median estimated API-equivalent cost of $3.53; Sortie completed 85.2 percent at $2.85. Sortie needed five attempts and $15.94 to collect three completed runs. Neither configuration achieved a Verified PASS.](docs/assets/quality-cost-reference.svg)
125
133
 
126
- Cost uses exported root and child session tokens, grouped by the model that produced each message,
127
- with a fixed standard short-context rate schedule. Completed-run cost shows execution efficiency;
128
- total acquisition cost includes the two interrupted Sortie attempts and shows reliability overhead.
129
- These are API-equivalent estimates, not invoices.
134
+ Cost audits use deduplicated root and descendant session tokens, grouped by the model that produced
135
+ each message. Historical values retain their frozen 2026-07-30 short-context schedule; the RC uses
136
+ the product's 2026-09-14 per-request schedule described above. Completed-run cost shows execution
137
+ efficiency; same-snapshot acquisition cost adds interrupted attempts. These are API-equivalent
138
+ estimates, not invoices.
130
139
 
131
140
  The product objective is **more verified outcomes per unit of cost and time without weakening the
132
141
  accepted goal**. This small, single-task local case study does not establish that claim, isolate
@@ -3,5 +3,5 @@
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.10.0-v0912-language4-cost-rpt10-compaction-ref1-proposal1-review-remediation1-surface3-proposal-recovery2-quality1";
6
+ export declare const V010_RUNTIME_ASSET_VERSION = "0.10.0-v0912-language4-cost-rpt10-compaction-ref1-proposal1-review-remediation1-surface3-proposal-recovery2-quality1-terminal1";
7
7
  export type RuntimeAssetVersion = typeof RUNTIME_ASSET_VERSION | typeof V010_RUNTIME_ASSET_VERSION;
@@ -3,4 +3,4 @@
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.10.0-v0912-language4-cost-rpt10-compaction-ref1-proposal1-review-remediation1-surface3-proposal-recovery2-quality1";
6
+ export const V010_RUNTIME_ASSET_VERSION = "0.10.0-v0912-language4-cost-rpt10-compaction-ref1-proposal1-review-remediation1-surface3-proposal-recovery2-quality1-terminal1";
@@ -380,6 +380,14 @@ export function createProfiledPlugin(profile, assetVersion) {
380
380
  catch (error) {
381
381
  if (error instanceof OperatorContractError)
382
382
  return JSON.stringify({ status: "invalid-plan", diagnostics: error.diagnostics, diagnostics_truncated: error.diagnostics_truncated });
383
+ // An immutable active contract is a local routing defect, not an external blocker. Return the
384
+ // existing durable state and its next action so the root continues instead of retrying prepare.
385
+ if (error instanceof Error && error.message === "operator-active-contract-immutable") {
386
+ return JSON.stringify({ status: "active-contract-immutable", code: error.message,
387
+ packet: await operatorPacket(await operators.required(context.sessionID)),
388
+ next_action: `This root already owns an immutable active contract. Do not resend a plan or cancel an unchanged contract: ` +
389
+ `read ${status} and continue the existing run's next_task_ref or next_action. Cancel only for an actual scope change or explicit stop.` });
390
+ }
383
391
  throw error;
384
392
  }
385
393
  await registerPreparedGoal(context.sessionID, state);
@@ -510,8 +518,14 @@ export function createProfiledPlugin(profile, assetVersion) {
510
518
  await control.authorizeOperatorContractRepairValidation(context.sessionID, taskID, unit.childSessionID, args.repair_fingerprint);
511
519
  }
512
520
  catch (error) {
521
+ // The repair is aborted and the run keeps a durable decision. Return that preserved state so an
522
+ // unauthorized resume cannot leave the root without a recognizable terminal or next action.
513
523
  applied = await operators.abortAppliedContractRepair(context.sessionID, "operator-contract-repair-validation-resume-unavailable");
514
- throw error;
524
+ return JSON.stringify({ status: "contract-repair-validation-unavailable",
525
+ code: error instanceof Error ? error.message : "operator-contract-repair-validation-resume-unavailable",
526
+ packet: await operatorPacket(applied),
527
+ next_action: `The validation-only resume was not authorized and this repair is closed. Do not retry ${resolveContractRepair} or ` +
528
+ `${resume} for the same fingerprint: read ${status}, then either continue the reported decision or return one terminal checkpoint naming this refusal.` });
515
529
  }
516
530
  return JSON.stringify({ status: "repair-applied", run_id: applied.runID, unit_id: unit.unit.id,
517
531
  repair_generation: applied.repairGeneration, task: operators.nextWorkerTask(applied) });
@@ -1,4 +1,5 @@
1
1
  import { type RuntimeAsset } from "./runtime-assets.ts";
2
2
  export declare const PREVIEW_PRESENTATION_POLICY: string;
3
+ export declare const PREVIEW_TERMINAL_REPORT_POLICY: string;
3
4
  export declare const COMMUNICATION_LANGUAGE_POLICY = "\n## Communication language continuity\n\nUse the language of the user's latest instruction sentences for all user-visible communication,\nincluding progress, Task descriptions/titles, delegated questions, handoff prose, findings and final replies.\nJapanese instructions require Japanese communication; English instructions require English communication.\nMixed-language identifiers or quoted English documents do not change the user's instruction language.\nIf the latest message supplies no detectable language, preserve the previous instruction language.\n\nBefore delegating, write the question and every prose field in that same language and explicitly ask the\nchild to reply in it. Do not translate a Japanese request into English merely because these agent instructions\nor examples are English. Child sessions and their prompts are visible to the user: parent-only translation\nafter an English exchange is not sufficient. All descendants preserve the requested language; it takes\nprecedence over the language of protocol boilerplate. Do not add a separate translation pass or model call.\n\nKeep protocol keys, enums, commands, paths, identifiers, model names, exact quoted evidence and code verbatim.\nFor example strategy_trigger, architecture-choice, review_phase and PASS must not be localized. Translate\ntheir explanatory prose, not these tokens. Preserve immutable criteria and host-generated Task packets\nverbatim; author their user-controlled prose in the correct language before the contract is frozen.\nGenerated control labels are not a reason to switch the surrounding explanation to English.\n";
4
5
  export declare const runtimeAssets: readonly RuntimeAsset[];
@@ -8,10 +8,14 @@ const coordinator = profileAgent(profile, "dog-coordinator");
8
8
  const operator = profileAgent(profile, "dog-operator");
9
9
  const worker = profileAgent(profile, "dog-worker");
10
10
  const canonicalCoordinator = canonicalAssets.find(asset => asset.name === "dog-coordinator").content;
11
- const presentationStart = canonicalCoordinator.indexOf("READABLE_OUTPUT_FIXTURE\n");
12
- const presentationEnd = canonicalCoordinator.indexOf("END_READABLE_OUTPUT_FIXTURE", presentationStart);
13
- if (presentationStart < 0 || presentationEnd < 0)
14
- throw new Error("canonical-presentation-fixture-missing");
11
+ /** Reuse the canonical fixture bodies so this profile cannot drift from the shared terminal vocabulary. */
12
+ function canonicalFixture(marker) {
13
+ const start = canonicalCoordinator.indexOf(`${marker}\n`);
14
+ const end = start < 0 ? -1 : canonicalCoordinator.indexOf(`END_${marker}`, start);
15
+ if (start < 0 || end < 0)
16
+ throw new Error(`canonical-fixture-missing:${marker}`);
17
+ return canonicalCoordinator.slice(start, end + `END_${marker}`.length);
18
+ }
15
19
  export const PREVIEW_PRESENTATION_POLICY = `
16
20
  ## Sortie presentation continuity
17
21
 
@@ -20,7 +24,34 @@ Use the user's language for prose, concise icon-led plan/progress/evidence block
20
24
  after the host accepts the result. The host supplies the 🐾 return report, mission/proof, cost/pack and career panels;
21
25
  do not fabricate scores, counts, medals or success, and do not suppress these panels as redundant decoration.
22
26
 
23
- ${canonicalCoordinator.slice(presentationStart, presentationEnd + "END_READABLE_OUTPUT_FIXTURE".length)}
27
+ ${canonicalFixture("READABLE_OUTPUT_FIXTURE")}
28
+ `;
29
+ export const PREVIEW_TERMINAL_REPORT_POLICY = `
30
+ ## Terminal report contract
31
+
32
+ A task turn that ends without another tool call is a terminal return. Its first non-empty line must be one
33
+ machine checkpoint: exactly one of DONE, INTERRUPTED, BLOCKED, or NEED_DECISION with that status icon,
34
+ followed by a short conclusion in the user's language. Never close a task turn with bare prose, an unlabeled
35
+ summary, a plan, a progress note, or a preamble, and never leave the run without one of these four tokens.
36
+ The status token, its icon, TRUE_INTERRUPTION and TRUE_BLOCKER are protocol tokens: keep them verbatim
37
+ even when the surrounding conclusion is translated. Translate only the display labels and keep their order.
38
+
39
+ DONE requires a succeeded ${profile.toolPrefix}complete_operator receipt for the accepted goal; the host renders
40
+ the measured return report from that receipt. Without it, return INTERRUPTED, BLOCKED, or NEED_DECISION naming
41
+ the exact unresolved condition. An exhausted budget, an unapproved or failed proposal, a terminated child, a
42
+ refused contract operation, or an unreachable acceptance is an INTERRUPTED return, never a silent stop.
43
+ A genuine interruption also requires the canonical machine line \`TRUE_INTERRUPTION: user: <condition>\` or
44
+ \`TRUE_INTERRUPTION: internal: <condition>\`; without it the host keeps the run on its same-session continuation path.
45
+
46
+ A refused control operation is a local process defect, not a terminal blocker. Read the returned status and
47
+ next_action, apply that one correction, and continue in the same turn. Never reissue an unchanged refused
48
+ request: an active contract returns ${profile.toolPrefix}operator_status and the existing next Task, and an
49
+ unavailable contract-repair validation resume returns the preserved run state and its decision. When the same
50
+ refusal repeats with unchanged state, stop retrying and return one INTERRUPTED checkpoint naming that refusal.
51
+
52
+ ${canonicalFixture("TERMINAL_STATUS_SEMANTICS_FIXTURE")}
53
+
54
+ ${canonicalFixture("TERMINAL_OUTPUT_TEMPLATE")}
24
55
  `;
25
56
  export const COMMUNICATION_LANGUAGE_POLICY = `
26
57
  ## Communication language continuity
@@ -255,7 +286,7 @@ Use ${profile.toolPrefix}cancel_operator to stop an active grant before changing
255
286
  operator-acceptance-remediation-required replacement action, or performing the bounded awaiting-acceptance
256
287
  reason=review-blocking replacement above. Agent switching revokes this
257
288
  runtime's ownership; do not restart it from a stale summary. The initial preview supports the serial lane only.
258
- ${PREVIEW_PRESENTATION_POLICY}
289
+ ${PREVIEW_PRESENTATION_POLICY}${PREVIEW_TERMINAL_REPORT_POLICY}
259
290
  `;
260
291
  const operatorContent = `---
261
292
  description: Sortie-dogs ${V010_RUNTIME_ASSET_VERSION} hidden dogs-coordinator operations delegate; no source or acceptance authority.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sortie-dogs",
3
- "version": "0.10.1",
3
+ "version": "0.10.2",
4
4
  "description": "Bounded agent harness and validated orchestration loop plugin for OpenCode",
5
5
  "keywords": [
6
6
  "opencode",
@@ -23,7 +23,7 @@
23
23
  "homepage": "https://github.com/zufall-upon/Sortie-dogs#readme",
24
24
  "publishConfig": {
25
25
  "access": "public",
26
- "tag": "beta"
26
+ "tag": "latest"
27
27
  },
28
28
  "type": "module",
29
29
  "engines": {