sortie-dogs 0.13.0 → 0.13.1

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
@@ -1,5 +1,9 @@
1
1
  # Sortie-dogs
2
2
 
3
+ <p align="center">
4
+ <img src="docs/assets/sortie-dogs-logo.png" alt="Sortie-dogs logo" width="640">
5
+ </p>
6
+
3
7
  **A goal-preserving, adaptive execution harness for OpenCode that optimizes cost,
4
8
  time, and proof without taking your setup over.**
5
9
 
@@ -28,6 +32,14 @@ implementation, validation, review, and model routing.
28
32
  Guides: [日本語](docs/guide-ja.md) · [简体中文](docs/guide-zh-CN.md) ·
29
33
  [Testing](docs/testing.md) · [CLI testing](docs/cli-testing.md)
30
34
 
35
+ ## SWE-bench Lite: 170/300 (56.67%)
36
+
37
+ The fixed **Sortie-dogs v0.12.24** harness resolved **170 of 300 SWE-bench Lite test issues** in one pass@1 campaign, with 9 empty patches and no official evaluation errors. Every instance has a frozen prediction and an inference-time trajectory. The task Workers ran `openai/gpt-6-luna-fast#max`; operator, coordinator and review roles ran `openai/gpt-6-sol#xhigh`. This is a system result, **not** a Luna-only model comparison or a Verified/full SWE-bench score.
38
+
39
+ [Technical report and per-repository results](docs/benchmarks/swebench-lite-v01224-test300-2026-09-29.md) · [Public predictions, logs and trajectories](https://github.com/zufall-upon/sortie-dogs-swebench-lite-20260929)
40
+
41
+ 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.
42
+
31
43
  > **Beta:** v0.12.2 builds on the v0.10.23 execution engine. Runtime behavior,
32
44
  > configuration, and generated assets may still change before 1.0.
33
45
 
@@ -202,7 +214,7 @@ not invent, probe, or translate variant names.
202
214
  - `freeTierFallbackModels`: ordered global last-resort model IDs. Default:
203
215
  `opencode/deepseek-v4-flash-free`; `[]` disables this fallback.
204
216
  - `dedicatedWorkerModel`: canonical stable serial target, default
205
- `openai/gpt-6-sol` / `medium`. The v0.10 profile also supplies its explicit
217
+ `openai/gpt-6.1-sol` / `medium`. The v0.10 profile also supplies its explicit
206
218
  role routes below; do not infer the v0.10 worker route from this stable setting.
207
219
  - `consultation.strategy`: fixed advisor identity, optional `required`, and
208
220
  positive `maxCallsPerCandidate`; default one call and not required.
@@ -239,14 +251,14 @@ or explicit risk. Workers own static, targeted, and related checks; the root own
239
251
  canonical and full-suite checks. An unchanged candidate, command, and environment
240
252
  reuse the same evidence instead of spending the validation budget again.
241
253
 
242
- ### Default v0.12 routes
254
+ ### Default routes
243
255
 
244
- - `dog-operator`: `openai/gpt-6-sol` / `xhigh`
245
- - `dogs-coordinator`: `openai/gpt-6-sol` / `xhigh`
256
+ - `dog-operator`: `openai/gpt-6.1-sol` / `xhigh`
257
+ - `dogs-coordinator`: `openai/gpt-6.1-sol` / `xhigh`
246
258
  - `dog-worker-v010`: `openai/gpt-6-luna-fast` / `max`
247
259
  - `dog-scout-v010`: `openai/gpt-6-luna-fast` / `max`
248
- - `dog-reviewer-v010`: `openai/gpt-6-sol` / `xhigh`
249
- - `dog-advisor-v010`: `openai/gpt-6-sol` / `xhigh`
260
+ - `dog-reviewer-v010`: `openai/gpt-6.1-sol` / `xhigh`
261
+ - `dog-advisor-v010`: `openai/gpt-6.1-sol` / `xhigh`
250
262
 
251
263
  An explicit model and variant selected in OpenCode remains authoritative for that
252
264
  session. Child role defaults fill absent native settings and may be overridden by
@@ -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.13.0-fast-first-v1";
6
+ export declare const V010_RUNTIME_ASSET_VERSION = "0.13.1-quality-first-review-v1";
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.13.0-fast-first-v1";
6
+ export const V010_RUNTIME_ASSET_VERSION = "0.13.1-quality-first-review-v1";
@@ -11,7 +11,7 @@ export declare class ProjectInitializationError extends Error {
11
11
  readonly code: ProjectInitializationErrorCode;
12
12
  constructor(code: ProjectInitializationErrorCode, message: string, options?: ErrorOptions);
13
13
  }
14
- export declare const V010_COORDINATOR_MODEL = "openai/gpt-6-sol#xhigh";
14
+ export declare const V010_COORDINATOR_MODEL = "openai/gpt-6.1-sol#xhigh";
15
15
  /**
16
16
  * Report a user config that routes the v0.10 Coordinator away from its packaged model. Measured Luna
17
17
  * Coordinators repeated plans and review dispatches until timeout, so init surfaces it without editing it.
@@ -542,7 +542,7 @@ async function initializeRoot(requestedRoot, layout, installAssets = runtimeAsse
542
542
  preservedLegacyPaths,
543
543
  };
544
544
  }
545
- export const V010_COORDINATOR_MODEL = "openai/gpt-6-sol#xhigh";
545
+ export const V010_COORDINATOR_MODEL = "openai/gpt-6.1-sol#xhigh";
546
546
  /**
547
547
  * Report a user config that routes the v0.10 Coordinator away from its packaged model. Measured Luna
548
548
  * Coordinators repeated plans and review dispatches until timeout, so init surfaces it without editing it.
@@ -80,7 +80,8 @@ export interface MissionExecution {
80
80
  startedAt: string;
81
81
  completedAt?: string;
82
82
  exit?: number;
83
- status?: "completed" | "error";
83
+ status?: "running" | "completed" | "error";
84
+ shellID?: string;
84
85
  outcome?: "not-started" | "execution-failed" | "executed";
85
86
  result?: Record<string, unknown>;
86
87
  }[];
@@ -141,15 +142,10 @@ export interface OperatorMission {
141
142
  child?: string;
142
143
  /** Completed, independent initial review for this mission, not merely an inherited child ID. */
143
144
  initialPrompt?: string;
144
- /** Reviews on this mission that found only missing evidence; bounded by MISSION_EVIDENCE_GAP_REVIEW_LIMIT. */
145
+ /** Observed evidence-only reviews; reporting only, never an acceptance threshold. */
145
146
  evidenceGapReviews?: number;
146
147
  };
147
148
  }
148
- /**
149
- * Evidence-only findings do not establish a defect. Each extra round costs a full Reviewer pass and often a
150
- * Worker unit, so after this many evidence-only reviews the candidate may be submitted with the gaps listed.
151
- */
152
- export declare const MISSION_EVIDENCE_GAP_REVIEW_LIMIT = 2;
153
149
  export declare const MISSION_CONSULTATION_LIMIT = 32;
154
150
  /** Review coverage survives a narrower replan; it is not a Worker write grant. */
155
151
  export declare function missionReviewScope(previous: MissionReviewScope | undefined, ...runs: OperatorState[]): MissionReviewScope;
@@ -217,5 +213,5 @@ export declare function missionPlan(mission: OperatorMission, raw: unknown): Ope
217
213
  export declare function missionPacket(mission: OperatorMission, run?: OperatorState): Record<string, unknown>;
218
214
  /** Models forward a short capability, never recopy the host's evidence hashes and source packet. */
219
215
  export declare function missionReviewTask(mission: OperatorMission): OperatorTask;
220
- /** Project grouped R-ID traces, including consecutive ranges, into the existing per-criterion mapping. */
221
- export declare function missionReviewTraces(mission: OperatorMission, raw: unknown): string[];
216
+ /** Optional implementation notes supplement host-observed source/checks; prose is not a coverage gate. */
217
+ export declare function missionReviewTraces(_mission: OperatorMission, raw: unknown): string[];
@@ -18,11 +18,6 @@ export function missionValidationCommand(command) {
18
18
  const expression = `exec(${JSON.stringify(heredoc[4])})`;
19
19
  return `${heredoc[1]} -c '${expression.replaceAll("'", "'\\''")}'`;
20
20
  }
21
- /**
22
- * Evidence-only findings do not establish a defect. Each extra round costs a full Reviewer pass and often a
23
- * Worker unit, so after this many evidence-only reviews the candidate may be submitted with the gaps listed.
24
- */
25
- export const MISSION_EVIDENCE_GAP_REVIEW_LIMIT = 2;
26
21
  export const MISSION_CONSULTATION_LIMIT = 32;
27
22
  /** Review coverage survives a narrower replan; it is not a Worker write grant. */
28
23
  export function missionReviewScope(previous, ...runs) {
@@ -38,7 +33,7 @@ export function missionReviewVerdict(text) {
38
33
  /** Whether the recorded review permits submission and acceptance of the current candidate. */
39
34
  export function missionReviewAccepted(review) {
40
35
  return review.verdict === "PASS" || review.verdict === "skipped-low-risk" ||
41
- (review.verdict === "evidence-gaps" && (review.evidenceGapReviews ?? 0) >= MISSION_EVIDENCE_GAP_REVIEW_LIMIT);
36
+ review.verdict === "evidence-gaps";
42
37
  }
43
38
  export function missionExecutionStatus(mission) {
44
39
  if (mission.kind !== "operation")
@@ -469,7 +464,7 @@ export function missionPacket(mission, run) {
469
464
  run_id: mission.review.runID, current_run: currentReview,
470
465
  source_fingerprint: mission.review.source, reviewer_session_id: mission.review.child ?? null,
471
466
  result: mission.review.result ?? null, evidence_gap_reviews: mission.review.evidenceGapReviews ?? 0,
472
- evidence_gap_review_limit: MISSION_EVIDENCE_GAP_REVIEW_LIMIT, accepted: reviewAccepted,
467
+ evidence_gaps_advisory: true, accepted: reviewAccepted,
473
468
  passed: currentReview && mission.review.verdict === "PASS", permits_submission: reviewAccepted && operationComplete } : null,
474
469
  ...(run ? { run_id: run.runID, status: run.phase, decision: run.decision,
475
470
  units: run.units.map(unit => ({ id: unit.unit.id, title: unit.unit.title, status: unit.status,
@@ -482,21 +477,21 @@ export function missionPacket(mission, run) {
482
477
  next_action: mission.phase === "completed" ? "Mission completed. Report the accepted result and retained review gaps; no further dispatch or completion call is needed."
483
478
  : mission.phase === "submitted" && mission.submission?.status === "ready"
484
479
  ? "Operator: compare the submitted candidate with the original requirements and actual evidence, then complete_mission if satisfied. Report remaining evidence gaps; they are not a review PASS."
485
- : run?.phase === "awaiting-decision" ? (run.units.some(unit => unit.dispatchDenial)
486
- ? "Coordinator: inspect units[].dispatch_denial before changing the plan. Correct only its diagnosed cause; do not repeat an unchanged refused Task or replan for a host-state mismatch. Report an unresolved runtime mismatch with the loaded runtime identity; preserve requirements and cumulative spend."
487
- : run.units.some(unit => unit.status === "failed" && unit.resultClass === "acceptance" && unit.failure?.outcome === "fail" && !unit.normalRemediationUsed)
488
- ? "Coordinator: one exact declared validation failed. Reconcile the returned Worker and cumulative budget, then call retry_mission_unit once for that unit before replanning. This preserves the same scope and acceptance."
489
- : run.units.some(unit => unit.status === "failed" && unit.resultClass === "acceptance" && unit.failure?.outcome === "fail" && unit.normalRemediationUsed && !unit.terminalRescue)
490
- ? "Coordinator: the one ordinary remediation failed the declared validation again. If its native terminal, writer release, exact candidate and cumulative budget permit, call rescue_mission_unit once; inspect and report a non_rescue reason, then continue ordinary correction within budget. Rescue does not bypass validation, review or root acceptance."
491
- : "Coordinator: correct the cause and call plan_units with the remaining work and all requirements; budget is cumulative.")
492
- : run?.phase === "awaiting-acceptance" && !operationComplete
493
- ? `Requested operation is ${operationStatus}. Continue the actual operation or report its blocker; auxiliary checks and review disposition cannot complete it.`
494
- : run?.phase === "awaiting-acceptance" ? (currentReview && mission.review?.verdict === "evidence-gaps" && !reviewAccepted
495
- ? "Coordinator: the Reviewer found only missing evidence. Supply focused original-file excerpts through review_mission evidence and traces; do not re-implement. At the evidence-gap limit, review ends with the gaps listed, not with execution proof."
496
- : reviewAccepted
497
- ? "Coordinator: review permits submission. Submit the candidate with any remaining gaps; do not repeat passed validation or review. Operator performs final acceptance."
480
+ : mission.kind === "operation" && operationStatus === "running"
481
+ ? "The declared operation is already running. Inspect its native shell/progress; do not start another Worker or run. Wait for a terminal result, or report the existing run as blocked if its completion cannot be observed."
482
+ : run?.phase === "awaiting-decision" ? (run.units.some(unit => unit.dispatchDenial)
483
+ ? "Coordinator: inspect units[].dispatch_denial before changing the plan. Correct only its diagnosed cause; do not repeat an unchanged refused Task or replan for a host-state mismatch. Report an unresolved runtime mismatch with the loaded runtime identity; preserve requirements and cumulative spend."
484
+ : run.units.some(unit => unit.status === "failed" && unit.resultClass === "acceptance" && unit.failure?.outcome === "fail" && !unit.normalRemediationUsed)
485
+ ? "Coordinator: one exact declared validation failed. Reconcile the returned Worker and cumulative budget, then call retry_mission_unit once for that unit before replanning. This preserves the same scope and acceptance."
486
+ : run.units.some(unit => unit.status === "failed" && unit.resultClass === "acceptance" && unit.failure?.outcome === "fail" && unit.normalRemediationUsed && !unit.terminalRescue)
487
+ ? "Coordinator: the one ordinary remediation failed the declared validation again. If its native terminal, writer release, exact candidate and cumulative budget permit, call rescue_mission_unit once; inspect and report a non_rescue reason, then continue ordinary correction within budget. Rescue does not bypass validation, review or root acceptance."
488
+ : "Coordinator: correct the cause and call plan_units with the remaining work and all requirements; budget is cumulative.")
489
+ : run?.phase === "awaiting-acceptance" && !operationComplete
490
+ ? `Requested operation is ${operationStatus}. Continue the actual operation or report its blocker; auxiliary checks and review disposition cannot complete it.`
491
+ : run?.phase === "awaiting-acceptance" ? (reviewAccepted
492
+ ? "Coordinator: review permits submission. Retain advisory review notes; do not repeat passed validation or review for evidence formatting. Operator performs final acceptance against the original requirements."
498
493
  : "Coordinator: address recorded findings or obtain the required independent review, then submit_mission. Operator compares all requirements with source/evidence before complete_mission.")
499
- : "Coordinator: continue the next useful unit within original requirements. Return only a completion candidate, user-only decision, or scope/budget extension." };
494
+ : "Coordinator: continue the next useful unit within original requirements. Return only a completion candidate, user-only decision, or scope/budget extension." };
500
495
  }
501
496
  /** Models forward a short capability, never recopy the host's evidence hashes and source packet. */
502
497
  export function missionReviewTask(mission) {
@@ -506,35 +501,12 @@ export function missionReviewTask(mission) {
506
501
  return { ...review.task, prompt: `${MISSION_REVIEW_REFERENCE}${JSON.stringify({ r: mission.root,
507
502
  m: mission.id, n: review.runID, h: digest(review.task.prompt) })}` };
508
503
  }
509
- /** Project grouped R-ID traces, including consecutive ranges, into the existing per-criterion mapping. */
510
- export function missionReviewTraces(mission, raw) {
511
- if (!Array.isArray(raw) || raw.length === 0 || !raw.every(item => typeof item === "string" && item.trim())) {
512
- throw new Error("mission-review-traces: supply concise implementation/test traces");
504
+ /** Optional implementation notes supplement host-observed source/checks; prose is not a coverage gate. */
505
+ export function missionReviewTraces(_mission, raw) {
506
+ if (raw === undefined)
507
+ return [];
508
+ if (!Array.isArray(raw) || !raw.every(item => typeof item === "string")) {
509
+ throw new Error("mission-review-traces: optional notes must be strings");
513
510
  }
514
- const grouped = raw.map(text => ({ text,
515
- ids: [...text.matchAll(/(?:^|[.;]\s+|[\r\n])\s*((?:R\d+\s*[-/,]?\s*)+):/gu)]
516
- .flatMap(match => [...match[1].matchAll(/R(\d+)(?:\s*-\s*R(\d+))?/gu)]
517
- .flatMap(([, first, last]) => {
518
- const firstID = `R${first}`;
519
- if (last === undefined)
520
- return [firstID];
521
- const lastID = `R${last}`;
522
- const start = mission.requirements.findIndex(item => item.id === firstID);
523
- const end = mission.requirements.findIndex(item => item.id === lastID);
524
- if (start < 0 || end < 0)
525
- return [firstID, lastID];
526
- if (start > end)
527
- throw new Error(`mission-review-traces: descending range ${firstID}-${lastID}`);
528
- return mission.requirements.slice(start, end + 1).map(item => item.id);
529
- })) }));
530
- if (grouped.every(item => item.ids.length === 0) && raw.length === mission.requirements.length)
531
- return raw;
532
- const unknown = grouped.flatMap(item => item.ids).filter(id => !mission.requirements.some(requirement => requirement.id === id));
533
- const missing = mission.requirements.filter(requirement => !grouped.some(item => item.ids.includes(requirement.id)));
534
- if (unknown.length || missing.length)
535
- throw new Error(`mission-review-traces: name the existing R IDs; ${[
536
- ...(missing.length ? [`missing ${missing.map(item => item.id).join(", ")}`] : []),
537
- ...(unknown.length ? [`unknown ${unknown.join(", ")}`] : []),
538
- ].join("; ")}`);
539
- return mission.requirements.map(requirement => grouped.filter(item => item.ids.includes(requirement.id)).map(item => item.text).join("\n"));
511
+ return raw.map(text => text.trim()).filter(Boolean);
540
512
  }
@@ -227,6 +227,22 @@ export interface OperatorState {
227
227
  decision: string | null;
228
228
  receipt: GoalTerminalReceipt | null;
229
229
  }
230
+ export interface OperatorProgress {
231
+ readonly profile: string;
232
+ readonly view: "progress";
233
+ readonly run_id: string;
234
+ readonly stage: OperatorPhase;
235
+ readonly decision: string | null;
236
+ readonly current_unit: {
237
+ readonly id: string;
238
+ readonly title: string;
239
+ readonly status: UnitState["status"];
240
+ } | null;
241
+ readonly completed_units: number;
242
+ readonly total_units: number;
243
+ readonly budget_remaining_units: number | null;
244
+ readonly next_action: string | null;
245
+ }
230
246
  /** A later mission may replace an unexecuted cancellation, but must retain settled work's acceptance. */
231
247
  export declare function cancelledMissionRetainsAcceptance(state: OperatorState): boolean;
232
248
  export declare function operatorGitPathAuthorized(path: string, scopes: readonly string[], platform?: NodeJS.Platform): boolean;
@@ -371,10 +387,13 @@ export declare class OperatorRuntime {
371
387
  draftStatus(root: string): Promise<unknown | undefined>;
372
388
  completionGoalFingerprint(state: OperatorState): Promise<string>;
373
389
  verifyContinuityControls(state: OperatorState): Promise<void>;
390
+ /** Compact, read-only projection of the same durable run and decision as the complete packet. */
391
+ progress(state: OperatorState, budgetRemainingUnits: number | null, nextAction?: string | null): OperatorProgress;
374
392
  packet(state: OperatorState): unknown;
375
393
  /** Reconstruct the admitted child's instructions without trusting a lossy conversation summary. */
376
394
  workerContext(root: string, child: string): Promise<string | undefined>;
377
395
  continuationCheckpoint(root: string): Promise<string | undefined>;
396
+ private continuationNextAction;
378
397
  private controlReference;
379
398
  private verifyControls;
380
399
  /** Follow only the host's authorized parent-link repair; keep the durable control pin in sync. */
@@ -1904,6 +1904,16 @@ export class OperatorRuntime {
1904
1904
  for (const unit of current.units)
1905
1905
  await this.verifyControls(unit);
1906
1906
  }
1907
+ /** Compact, read-only projection of the same durable run and decision as the complete packet. */
1908
+ progress(state, budgetRemainingUnits, nextAction) {
1909
+ const current = state.units.find(unit => unit.status !== "succeeded");
1910
+ const authoritativeAction = nextAction !== undefined ? nextAction
1911
+ : this.continuationNextAction(state);
1912
+ return { profile: state.profile, view: "progress", run_id: state.runID, stage: state.phase, decision: state.decision,
1913
+ current_unit: current === undefined ? null : { id: current.unit.id, title: current.unit.title, status: current.status },
1914
+ completed_units: state.units.filter(unit => unit.status === "succeeded").length, total_units: state.units.length,
1915
+ budget_remaining_units: budgetRemainingUnits, next_action: typeof authoritativeAction === "string" ? authoritativeAction : null };
1916
+ }
1907
1917
  packet(state) {
1908
1918
  const proved = new Set(state.units.flatMap(unit => unit.evidence.flatMap(item => item.measurement.criterion_ids)));
1909
1919
  const current = state.units.find(unit => unit.status !== "succeeded");
@@ -2035,9 +2045,10 @@ export class OperatorRuntime {
2035
2045
  }
2036
2046
  async continuationCheckpoint(root) {
2037
2047
  const state = await this.read(root);
2038
- const cancelledRemediation = state?.phase === "cancelled" &&
2039
- [ACCEPTANCE_REMEDIATION_DECISION, REVIEW_REMEDIATION_DECISION].includes(state.decision ?? "");
2040
- if (!state || state.phase === "completed" || (state.phase === "cancelled" && !cancelledRemediation))
2048
+ if (!state)
2049
+ return undefined;
2050
+ const nextAction = this.continuationNextAction(state);
2051
+ if (nextAction === null)
2041
2052
  return undefined;
2042
2053
  const current = state.units.find(unit => unit.status !== "succeeded");
2043
2054
  return JSON.stringify({ authority: "durable-operator-state", root_session_id: state.rootSessionID,
@@ -2046,14 +2057,21 @@ export class OperatorRuntime {
2046
2057
  next_task_ref: current?.status === "pending" ? (state.units.length > 1
2047
2058
  ? state.operatorSessionID === null ? this.dispatchTask(state).prompt : null
2048
2059
  : current.repairValidation === null ? this.workerTask(state, current).prompt : this.repairWorkerTask(state, current).prompt) : null,
2049
- next_action: state.decision === ACCEPTANCE_REMEDIATION_DECISION || state.decision === REVIEW_REMEDIATION_DECISION
2050
- ? `call ${this.profile.toolPrefix}operator_status and execute its cancel-then-replacement next_action in this turn; do not stop after reporting status or call resume_operator`
2051
- : state.phase === "awaiting-acceptance" ? `call ${this.profile.toolPrefix}operator_status and follow its independent-review next_action; do not complete before review disposition`
2052
- : state.phase === "prepared" ? "call operator_status, then operator_next; dispatch only the exact returned Task"
2053
- : state.phase === "running" ? "call operator_status; do not duplicate the running Task"
2054
- : state.phase === "cancelled"
2055
- ? `call ${this.profile.toolPrefix}operator_status and execute its prepare_operator next_action in this turn; do not stop after reporting status`
2056
- : `call ${this.profile.toolPrefix}operator_status and execute its exact next_action in this turn; do not stop after reporting status` });
2060
+ next_action: nextAction });
2061
+ }
2062
+ continuationNextAction(state) {
2063
+ const cancelledRemediation = state.phase === "cancelled" &&
2064
+ [ACCEPTANCE_REMEDIATION_DECISION, REVIEW_REMEDIATION_DECISION].includes(state.decision ?? "");
2065
+ if (state.phase === "completed" || (state.phase === "cancelled" && !cancelledRemediation))
2066
+ return null;
2067
+ return state.decision === ACCEPTANCE_REMEDIATION_DECISION || state.decision === REVIEW_REMEDIATION_DECISION
2068
+ ? `call ${this.profile.toolPrefix}operator_status and execute its cancel-then-replacement next_action in this turn; do not stop after reporting status or call resume_operator`
2069
+ : state.phase === "awaiting-acceptance" ? `call ${this.profile.toolPrefix}operator_status and follow its independent-review next_action; do not complete before review disposition`
2070
+ : state.phase === "prepared" ? "call operator_status, then operator_next; dispatch only the exact returned Task"
2071
+ : state.phase === "running" ? "call operator_status; do not duplicate the running Task"
2072
+ : state.phase === "cancelled"
2073
+ ? `call ${this.profile.toolPrefix}operator_status and execute its prepare_operator next_action in this turn; do not stop after reporting status`
2074
+ : `call ${this.profile.toolPrefix}operator_status and execute its exact next_action in this turn; do not stop after reporting status`;
2057
2075
  }
2058
2076
  controlReference(path) {
2059
2077
  const local = relative(this.projectRoot, path);
@@ -23,7 +23,7 @@ export interface AdaptiveReviewProvider {
23
23
  readonly fingerprint: string;
24
24
  }>;
25
25
  }
26
- export declare const DEFAULT_ADAPTIVE_REMEDIATION_MODEL = "openai/gpt-6-sol";
26
+ export declare const DEFAULT_ADAPTIVE_REMEDIATION_MODEL = "openai/gpt-6.1-sol";
27
27
  export interface AdaptiveRemediationSessionClient extends OpenCodeModelAvailabilityClient {
28
28
  session: {
29
29
  create(input: {
@@ -10,7 +10,7 @@ import { produceWorktreeCommitArtifact, runContainedValidation } from "../core/w
10
10
  import { openCodeModel } from "./model-routing-hook.js";
11
11
  const SHA = /^[a-f0-9]{40}(?:[a-f0-9]{24})?$/u;
12
12
  const HASH = /^[a-f0-9]{64}$/u;
13
- export const DEFAULT_ADAPTIVE_REMEDIATION_MODEL = "openai/gpt-6-sol";
13
+ export const DEFAULT_ADAPTIVE_REMEDIATION_MODEL = "openai/gpt-6.1-sol";
14
14
  const responseData = (value) => {
15
15
  if (value === null || typeof value !== "object")
16
16
  return {};
@@ -1,8 +1,8 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { execFile } from "node:child_process";
3
3
  import { createReadStream } from "node:fs";
4
- import { lstat, readlink } from "node:fs/promises";
5
- import { isAbsolute, relative, resolve, sep } from "node:path";
4
+ import { lstat, readlink, readdir } from "node:fs/promises";
5
+ import { isAbsolute, join, relative, resolve, sep } from "node:path";
6
6
  import { promisify } from "node:util";
7
7
  import { createInterface } from "node:readline";
8
8
  import { TOOL_ENVIRONMENT } from "../runtime-mission-assets.js";
@@ -218,7 +218,7 @@ export async function missionReviewSource(directory, run, evidence = [], baselin
218
218
  // Keep room for the baseline diff. Reserve notices only after the allocated whole lines show
219
219
  // truncation; pre-reserving every possible notice can hide all six otherwise fitting references.
220
220
  const heading = (entry) => `\n--- evidence: ${entry.path}:${entry.offset} ---\n`;
221
- const truncated = (entry) => `[FOCUSED EXCERPT TRUNCATED: ${entry.path}:${entry.offset}; request a smaller range]\n`;
221
+ const truncated = (entry) => `[FOCUSED EXCERPT TRUNCATED: ${entry.path}:${entry.offset}; Reviewer can read the remaining lines directly]\n`;
222
222
  // Read-only reviews retain their smaller existing envelope; code reviews may use the space
223
223
  // previously reserved for automatic diff prefixes, while leaving room for changed-file context.
224
224
  const limit = writes.length ? 18_000 : 11_000;
@@ -305,13 +305,17 @@ export async function missionReviewSource(directory, run, evidence = [], baselin
305
305
  const unchanged = diff.length === 0;
306
306
  let excerpt = selected + diff;
307
307
  const paths = [...new Set([...names.split("\0"), ...ignored.split("\0")].filter(Boolean))].sort();
308
- for (const path of paths) {
308
+ const visited = new Set();
309
+ const visit = async (path, includedByParent = false) => {
310
+ if (visited.has(path))
311
+ return;
312
+ visited.add(path);
309
313
  hash.update(JSON.stringify(path));
310
314
  try {
311
315
  const absolute = resolve(directory, path), stat = await lstat(absolute);
312
316
  hash.update(String(stat.mode));
313
- const include = untracked.has(path) || unchanged;
314
- const heading = `\n--- ${untracked.has(path) ? "new file" : "current file"}: ${path} ---\n`;
317
+ const include = includedByParent || untracked.has(path) || unchanged;
318
+ const heading = `\n--- ${includedByParent || untracked.has(path) ? "new file" : "current file"}: ${path} ---\n`;
315
319
  const room = include ? Math.max(0, 24_000 - Buffer.byteLength(excerpt) - Buffer.byteLength(heading)) : 0;
316
320
  let preview = Buffer.alloc(0);
317
321
  if (stat.isSymbolicLink()) {
@@ -319,7 +323,28 @@ export async function missionReviewSource(directory, run, evidence = [], baselin
319
323
  hash.update(String(content.length)).update(content);
320
324
  preview = content.subarray(0, room);
321
325
  }
326
+ else if (stat.isDirectory()) {
327
+ hash.update("directory");
328
+ if (include) {
329
+ const marker = "[directory; contained artifacts follow]\n";
330
+ const headingFits = Buffer.byteLength(excerpt) + Buffer.byteLength(heading) + Buffer.byteLength(marker) <= 24_000;
331
+ if (headingFits)
332
+ excerpt += heading + marker;
333
+ else
334
+ omitted.push(path);
335
+ }
336
+ // Nested repository metadata is not candidate output and can crowd out its actual files.
337
+ const children = (await readdir(absolute, { withFileTypes: true }))
338
+ .filter(child => child.name !== ".git")
339
+ .sort((left, right) => Number(left.isDirectory()) - Number(right.isDirectory()) ||
340
+ (left.name < right.name ? -1 : left.name > right.name ? 1 : 0));
341
+ for (const child of children) {
342
+ await visit(join(path, child.name).replaceAll("\\", "/"), include);
343
+ }
344
+ }
322
345
  else {
346
+ if (!stat.isFile())
347
+ throw new Error("mission-review-source: unsupported artifact type");
323
348
  hash.update(String(stat.size));
324
349
  for await (const part of createReadStream(absolute)) {
325
350
  const bytes = Buffer.isBuffer(part) ? part : Buffer.from(part);
@@ -328,7 +353,7 @@ export async function missionReviewSource(directory, run, evidence = [], baselin
328
353
  preview = Buffer.concat([preview, bytes.subarray(0, room - preview.length)]);
329
354
  }
330
355
  }
331
- if (include) {
356
+ if (include && !stat.isDirectory()) {
332
357
  const headingFits = Buffer.byteLength(excerpt) + Buffer.byteLength(heading) <= 24_000;
333
358
  if (headingFits) {
334
359
  const rendered = preview.includes(0) ? "[binary artifact: bytes fingerprinted]" : preview.toString("utf8");
@@ -358,7 +383,9 @@ export async function missionReviewSource(directory, run, evidence = [], baselin
358
383
  throw error;
359
384
  hash.update("deleted");
360
385
  }
361
- }
386
+ };
387
+ for (const path of paths)
388
+ await visit(path);
362
389
  if (external.length) {
363
390
  // A declared write scope can also be a host-managed runtime directory (e.g. Docker's data root).
364
391
  // Do not make review depend on listing it; record the missing coverage instead. Protected
@@ -375,7 +402,7 @@ export async function missionReviewSource(directory, run, evidence = [], baselin
375
402
  if (bytes.length > 24_000 && truncatedSource.length === 0)
376
403
  truncatedSource.push("(source diff exceeds excerpt budget)");
377
404
  return { fingerprint: `sha256:${hash.digest("hex")}`, excerpt: (bytes.length > 24_000 ? bytes.subarray(0, 24_000).toString("utf8") : excerpt) +
378
- (truncatedSource.length ? `\n[EXCERPT TRUNCATED: ${truncatedSource.slice(0, 20).join(", ")}; supply focused traces for missing sections, not another implementation unit]` : "") +
405
+ (truncatedSource.length ? `\n[EXCERPT TRUNCATED: ${truncatedSource.slice(0, 20).join(", ")}; Reviewer can read/search the relevant source directly]` : "") +
379
406
  (unreadable.length ? `\n[UNINSPECTED EXTERNAL DIRECTORIES: ${unreadable.slice(0, 20).join(", ")}; select specific result files as review evidence]` : ""),
380
407
  truncatedEvidence, truncatedSource };
381
408
  }
@@ -1,9 +1,9 @@
1
1
  export declare const MODEL_COST_PRICING_SNAPSHOT: {
2
- readonly checkedAt: "2026-09-23";
2
+ readonly checkedAt: "2026-09-30";
3
3
  readonly currency: "USD";
4
4
  readonly unit: "1M tokens";
5
- readonly sources: readonly ["https://developers.openai.com/api/docs/pricing", "https://developers.openai.com/api/docs/models/gpt-6-astra", "https://developers.openai.com/api/docs/models/gpt-6-sol", "https://developers.openai.com/api/docs/models/gpt-6-luna", "https://developers.openai.com/api/docs/guides/fast-mode", "https://developers.openai.com/api/docs/models/gpt-5.6-sol", "https://developers.openai.com/api/docs/models/gpt-5.6-terra", "https://developers.openai.com/api/docs/models/gpt-5.6-luna", "https://platform.claude.com/docs/en/about-claude/pricing"];
6
- readonly assumptions: readonly ["OpenAI Standard pricing; requests above 272,000 total input/cache tokens use 2x input/cache and 1.5x output pricing.", "Anthropic Standard pricing with the 5-minute cache-write rate; Batch, Flex, regional, tool, and subscription charges are excluded.", "variant and serviceTier are separate; model variants do not alter the selected Standard token price.", "GPT-6 Sol and GPT-6 Luna use their official Standard schedules; the OpenCode gpt-6-luna-fast selector sends API model gpt-6-luna with priority service tier.", "GPT-6 Sol/Luna Fast (service_tier fast or priority) is priced at 2x the applicable Standard rates, including cache and long-context multipliers.", "gpt-5.6-luna-fast is retained only as a compatibility price for historical host catalog entries; it has no published model page and is exactly twice the GPT-5.6 Luna Standard schedule."];
5
+ readonly sources: readonly ["https://developers.openai.com/api/docs/pricing", "https://developers.openai.com/api/docs/models/gpt-6-astra", "https://developers.openai.com/api/docs/models/gpt-6-sol", "https://developers.openai.com/api/docs/models/gpt-6.1-sol", "https://developers.openai.com/api/docs/models/gpt-6-luna", "https://developers.openai.com/api/docs/guides/fast-mode", "https://developers.openai.com/api/docs/models/gpt-5.6-sol", "https://developers.openai.com/api/docs/models/gpt-5.6-terra", "https://developers.openai.com/api/docs/models/gpt-5.6-luna", "https://platform.claude.com/docs/en/about-claude/pricing"];
6
+ readonly assumptions: readonly ["OpenAI Standard pricing; requests above 272,000 total input/cache tokens use 2x input/cache and 1.5x output pricing.", "Anthropic Standard pricing with the 5-minute cache-write rate; Batch, Flex, regional, tool, and subscription charges are excluded.", "variant and serviceTier are separate; model variants do not alter the selected Standard token price.", "GPT-6 Sol, GPT-6.1 Sol, and GPT-6 Luna use their official Standard schedules; the OpenCode gpt-6-luna-fast selector sends API model gpt-6-luna with priority service tier.", "For GPT-6 Sol, GPT-6.1 Sol, and GPT-6 Luna, Fast mode (service_tier fast or priority, including the gpt-6.1-sol-fast selector) multiplies applicable Standard rates by 2, including cache and long-context multipliers.", "gpt-5.6-luna-fast is retained only as a compatibility price for historical host catalog entries; it has no published model page and is exactly twice the GPT-5.6 Luna Standard schedule."];
7
7
  };
8
8
  export interface ModelCostUsage {
9
9
  readonly providerID: string | undefined;
@@ -1,11 +1,12 @@
1
1
  export const MODEL_COST_PRICING_SNAPSHOT = {
2
- checkedAt: "2026-09-23",
2
+ checkedAt: "2026-09-30",
3
3
  currency: "USD",
4
4
  unit: "1M tokens",
5
5
  sources: [
6
6
  "https://developers.openai.com/api/docs/pricing",
7
7
  "https://developers.openai.com/api/docs/models/gpt-6-astra",
8
8
  "https://developers.openai.com/api/docs/models/gpt-6-sol",
9
+ "https://developers.openai.com/api/docs/models/gpt-6.1-sol",
9
10
  "https://developers.openai.com/api/docs/models/gpt-6-luna",
10
11
  "https://developers.openai.com/api/docs/guides/fast-mode",
11
12
  "https://developers.openai.com/api/docs/models/gpt-5.6-sol",
@@ -17,14 +18,15 @@ export const MODEL_COST_PRICING_SNAPSHOT = {
17
18
  "OpenAI Standard pricing; requests above 272,000 total input/cache tokens use 2x input/cache and 1.5x output pricing.",
18
19
  "Anthropic Standard pricing with the 5-minute cache-write rate; Batch, Flex, regional, tool, and subscription charges are excluded.",
19
20
  "variant and serviceTier are separate; model variants do not alter the selected Standard token price.",
20
- "GPT-6 Sol and GPT-6 Luna use their official Standard schedules; the OpenCode gpt-6-luna-fast selector sends API model gpt-6-luna with priority service tier.",
21
- "GPT-6 Sol/Luna Fast (service_tier fast or priority) is priced at 2x the applicable Standard rates, including cache and long-context multipliers.",
21
+ "GPT-6 Sol, GPT-6.1 Sol, and GPT-6 Luna use their official Standard schedules; the OpenCode gpt-6-luna-fast selector sends API model gpt-6-luna with priority service tier.",
22
+ "For GPT-6 Sol, GPT-6.1 Sol, and GPT-6 Luna, Fast mode (service_tier fast or priority, including the gpt-6.1-sol-fast selector) multiplies applicable Standard rates by 2, including cache and long-context multipliers.",
22
23
  "gpt-5.6-luna-fast is retained only as a compatibility price for historical host catalog entries; it has no published model page and is exactly twice the GPT-5.6 Luna Standard schedule.",
23
24
  ],
24
25
  };
25
26
  const OPENAI = {
26
27
  "gpt-6-astra": { input: 10, cacheRead: 1, cacheWrite: 12.5, output: 50 },
27
28
  "gpt-6-sol": { input: 2, cacheRead: 0.2, cacheWrite: 2.5, output: 10 },
29
+ "gpt-6.1-sol": { input: 2, cacheRead: 0.1, cacheWrite: 2.5, output: 10 },
28
30
  "gpt-6-luna": { input: 0.1, cacheRead: 0.01, cacheWrite: 0.125, output: 0.5 },
29
31
  "gpt-5.6-sol": { input: 4, cacheRead: 0.4, cacheWrite: 5, output: 20 },
30
32
  "gpt-5.6": { input: 4, cacheRead: 0.4, cacheWrite: 5, output: 20 },
@@ -43,14 +45,16 @@ export function estimateModelUsageCost(usage) {
43
45
  usage.outputTokens, usage.reasoningTokens];
44
46
  if (!counts.every(tokenCount))
45
47
  return { status: "unpriced", reason: "missing-usage" };
46
- const alias = usage.providerID?.toLowerCase() === "openai" && usage.modelID === "gpt-6-luna-fast";
48
+ const lunaFastAlias = usage.providerID?.toLowerCase() === "openai" && usage.modelID === "gpt-6-luna-fast";
49
+ const sol61FastAlias = usage.providerID?.toLowerCase() === "openai" && usage.modelID === "gpt-6.1-sol-fast";
50
+ const alias = lunaFastAlias || sol61FastAlias;
47
51
  const fast = alias || (["fast", "priority"].includes(usage.serviceTier?.toLowerCase() ?? "") &&
48
- usage.providerID?.toLowerCase() === "openai" && ["gpt-6-sol", "gpt-6-luna"].includes(usage.modelID ?? ""));
52
+ usage.providerID?.toLowerCase() === "openai" && ["gpt-6-sol", "gpt-6.1-sol", "gpt-6-luna"].includes(usage.modelID ?? ""));
49
53
  if (usage.serviceTier !== undefined && !(fast ? ["standard", "default", "fast", "priority"] : ["standard", "default"]).includes(usage.serviceTier.toLowerCase())) {
50
54
  return { status: "unpriced", reason: "unsupported-service-tier" };
51
55
  }
52
56
  const provider = usage.providerID?.toLowerCase();
53
- const model = alias ? "gpt-6-luna" : usage.modelID;
57
+ const model = lunaFastAlias ? "gpt-6-luna" : sol61FastAlias ? "gpt-6.1-sol" : usage.modelID;
54
58
  const base = provider === "openai" && model !== undefined && Object.hasOwn(OPENAI, model) ? OPENAI[model]
55
59
  : provider === "anthropic" && model !== undefined && Object.hasOwn(ANTHROPIC, model) ? ANTHROPIC[model] : undefined;
56
60
  if (base === undefined)
@@ -7,18 +7,18 @@ export interface RoleModelRoute {
7
7
  readonly fallback?: readonly ModelTarget[];
8
8
  }
9
9
  export type ModelRoutingConfig = Readonly<Record<string, RoleModelRoute>>;
10
- /** The stable serial implementation route stays on Sol when Luna fan-out is unsafe or unavailable. */
11
- export declare const DEDICATED_WORKER_MODEL = "openai/gpt-6-sol";
10
+ /** The stable serial implementation route stays on GPT-6.1 Sol when Luna fan-out is unsafe or unavailable. */
11
+ export declare const DEDICATED_WORKER_MODEL = "openai/gpt-6.1-sol";
12
12
  export declare const DEDICATED_WORKER_VARIANT = "medium";
13
13
  /** Luna executes only units admitted to the fabric; it is not the serial worker's fallback target. */
14
14
  export declare const LUNA_FABRIC_WORKER_MODEL = "openai/gpt-6-luna";
15
15
  export declare const LUNA_FABRIC_WORKER_VARIANT = "max";
16
16
  export declare const LUNA_FABRIC_WORKER_ROLE = "dog-luna-worker";
17
- /** The coordinator uses Sol 6 High so routing and progress decisions do not bottleneck the workflow. */
18
- export declare const DEFAULT_COORDINATOR_MODEL = "openai/gpt-6-sol";
17
+ /** The coordinator uses GPT-6.1 Sol High so routing and progress decisions do not bottleneck the workflow. */
18
+ export declare const DEFAULT_COORDINATOR_MODEL = "openai/gpt-6.1-sol";
19
19
  export declare const DEFAULT_COORDINATOR_VARIANT = "high";
20
- /** Compatibility names retained for the pre-v0.8 explicit Sol target. */
21
- export declare const ESCALATION_WORKER_MODEL = "openai/gpt-6-sol";
20
+ /** Compatibility names retained for the pre-v0.8 explicit Sol route. */
21
+ export declare const ESCALATION_WORKER_MODEL = "openai/gpt-6.1-sol";
22
22
  export declare const ESCALATION_WORKER_VARIANT = "medium";
23
23
  export declare const DEDICATED_WORKER_ROLES: readonly ["implementation", "remediation", "blocker-resolution", "sol-worker-mk2a2", "dog-worker"];
24
24
  /** Ordered last-resort targets used only when the host proves a policy target unavailable. */
@@ -38,7 +38,7 @@ export declare const LUNA_FABRIC_WORKER_ROUTING: ModelRoutingConfig;
38
38
  export declare function fixedWorkerRouting(dedicatedTarget?: ModelTarget): ModelRoutingConfig;
39
39
  export declare const FIXED_MODEL_ROUTING: ModelRoutingConfig;
40
40
  export declare const RECOMMENDED_LUNA_MODEL = "openai/gpt-6-luna";
41
- /** Coordinator state and routing use Sol 6 High; bounded evidence retrieval uses Luna High. */
41
+ /** Coordinator state and routing use GPT-6.1 Sol High; bounded evidence retrieval uses Luna High. */
42
42
  export declare const DEFAULT_COORDINATOR_ROUTING: ModelRoutingConfig;
43
43
  /** Evidence gathering is retrieval rather than reasoning, so the scout stays one effort tier lower. */
44
44
  export declare const RECOMMENDED_SCOUT_VARIANT = "high";