sortie-dogs 0.10.12 → 0.10.14
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
|
@@ -50,7 +50,7 @@ two-level subagent depth to `.opencode/opencode.json`, preserving existing value
|
|
|
50
50
|
```json
|
|
51
51
|
{
|
|
52
52
|
"plugins": ["sortie-dogs"],
|
|
53
|
-
"subagent_depth": 2
|
|
53
|
+
"experimental": { "subagent_depth": 2 }
|
|
54
54
|
}
|
|
55
55
|
```
|
|
56
56
|
|
|
@@ -302,7 +302,7 @@ npm install --global sortie-dogs
|
|
|
302
302
|
sortie-dogs init --global --profile v010
|
|
303
303
|
```
|
|
304
304
|
|
|
305
|
-
Then add `sortie-dogs` and `subagent_depth: 2` to the global OpenCode config.
|
|
305
|
+
Then add `sortie-dogs` and `experimental.subagent_depth: 2` to the global OpenCode config.
|
|
306
306
|
Global initialization installs assets only; it does not silently change the
|
|
307
307
|
default agent or merge user settings.
|
|
308
308
|
|
package/dist/asset-version.d.ts
CHANGED
|
@@ -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-ref2-proposal1-review-remediation1-surface4-proposal-recovery2-quality1-terminal2-route1-path1-permission2-prerequisite1-v2-bridge1-intent1-reflection1-cancel1-proposal-budget1-goal-recovery1-delegate-execute1";
|
|
6
|
+
export declare const V010_RUNTIME_ASSET_VERSION = "0.10.0-v0912-language4-cost-rpt10-compaction-ref2-proposal1-review-remediation1-surface4-proposal-recovery2-quality1-terminal2-route1-path1-permission2-prerequisite1-v2-bridge1-intent1-reflection1-cancel1-proposal-budget1-goal-recovery1-delegate-execute1-v2-depth1-process-replacement1-unstarted-v2-1";
|
|
7
7
|
export type RuntimeAssetVersion = typeof RUNTIME_ASSET_VERSION | typeof V010_RUNTIME_ASSET_VERSION;
|
package/dist/asset-version.js
CHANGED
|
@@ -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-ref2-proposal1-review-remediation1-surface4-proposal-recovery2-quality1-terminal2-route1-path1-permission2-prerequisite1-v2-bridge1-intent1-reflection1-cancel1-proposal-budget1-goal-recovery1-delegate-execute1";
|
|
6
|
+
export const V010_RUNTIME_ASSET_VERSION = "0.10.0-v0912-language4-cost-rpt10-compaction-ref2-proposal1-review-remediation1-surface4-proposal-recovery2-quality1-terminal2-route1-path1-permission2-prerequisite1-v2-bridge1-intent1-reflection1-cancel1-proposal-budget1-goal-recovery1-delegate-execute1-v2-depth1-process-replacement1-unstarted-v2-1";
|
|
@@ -303,6 +303,7 @@ export declare class OperatorRuntime {
|
|
|
303
303
|
observeChild(root: string, callID: string, childID: string): Promise<void>;
|
|
304
304
|
private observeChildOnce;
|
|
305
305
|
operatorReturned(root: string): Promise<OperatorState>;
|
|
306
|
+
processReplacementRequired(root: string, runID: string): Promise<OperatorState>;
|
|
306
307
|
private operatorReturnedOnce;
|
|
307
308
|
interrupted(root: string, reason: string): Promise<readonly string[]>;
|
|
308
309
|
private interruptedOnce;
|
|
@@ -29,6 +29,7 @@ export class OperatorContractError extends Error {
|
|
|
29
29
|
const OPERATOR_TASK_REFERENCE = "SORTIE_OPERATOR_TASK_REF";
|
|
30
30
|
const OPERATOR_DELEGATE_TASK_REFERENCE = "SORTIE_OPERATOR_DELEGATE_REF";
|
|
31
31
|
const REPAIR_VALIDATION_STALE_PROOF_DECISION = "operator-contract-repair-validation-incomplete:operator-recovery-proof-unavailable-or-stale";
|
|
32
|
+
const PROCESS_REPLACEMENT_DECISION = "operator-process-remediation-replacement-required";
|
|
32
33
|
const ACCEPTANCE_REMEDIATION_DECISION = "operator-acceptance-remediation-required";
|
|
33
34
|
const REVIEW_REMEDIATION_DECISION = "operator-review-remediation-required";
|
|
34
35
|
const record = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -739,7 +740,7 @@ export class OperatorRuntime {
|
|
|
739
740
|
[ACCEPTANCE_CONTINUITY_EXTENSION]: { schema_version: "0.1", authority: "dispatch", task_id: taskID,
|
|
740
741
|
criteria: plan.acceptance, fingerprint: acceptanceFingerprint,
|
|
741
742
|
parent_fingerprint: index === 0
|
|
742
|
-
?
|
|
743
|
+
? priorAcceptedUnits.length === 0 ? "none" : parent?.acceptanceFingerprint ?? "none"
|
|
743
744
|
: acceptanceFingerprint },
|
|
744
745
|
"sortie-dogs/unit-coverage": { schema_version: "0.1", task_id: taskID,
|
|
745
746
|
acceptance_fingerprint: acceptanceFingerprint, indices: unit.acceptance_indices },
|
|
@@ -980,7 +981,10 @@ export class OperatorRuntime {
|
|
|
980
981
|
}
|
|
981
982
|
matchesRecordedWorkerTask(state, unitID, args) {
|
|
982
983
|
const unit = state.units.find(item => item.unit.id === unitID);
|
|
983
|
-
|
|
984
|
+
const normalized = record(args) && typeof args.agent === "string" && args.subagent_type === undefined
|
|
985
|
+
? { ...args, subagent_type: args.agent }
|
|
986
|
+
: args;
|
|
987
|
+
return unit !== undefined && this.matchesWorkerTask(state, unit, normalized);
|
|
984
988
|
}
|
|
985
989
|
admitOperator(root, callID, args) {
|
|
986
990
|
return this.serial(root, () => this.admitOperatorOnce(root, callID, args));
|
|
@@ -1407,6 +1411,17 @@ export class OperatorRuntime {
|
|
|
1407
1411
|
operatorReturned(root) {
|
|
1408
1412
|
return this.serial(root, () => this.operatorReturnedOnce(root));
|
|
1409
1413
|
}
|
|
1414
|
+
processReplacementRequired(root, runID) {
|
|
1415
|
+
return this.serial(root, async () => {
|
|
1416
|
+
const state = await this.required(root);
|
|
1417
|
+
if (state.runID !== runID || state.phase !== "awaiting-decision" || state.decision !== "process-defect") {
|
|
1418
|
+
throw new Error("operator-process-remediation-replacement-not-ready");
|
|
1419
|
+
}
|
|
1420
|
+
state.decision = PROCESS_REPLACEMENT_DECISION;
|
|
1421
|
+
await this.save(state);
|
|
1422
|
+
return state;
|
|
1423
|
+
});
|
|
1424
|
+
}
|
|
1410
1425
|
async operatorReturnedOnce(root) {
|
|
1411
1426
|
const state = await this.required(root);
|
|
1412
1427
|
if (state.phase === "running") {
|
|
@@ -1651,8 +1666,9 @@ export class OperatorRuntime {
|
|
|
1651
1666
|
const repairValidationRetryAvailable = this.canRetryRepairValidation(state);
|
|
1652
1667
|
const acceptanceRemediation = state.decision === ACCEPTANCE_REMEDIATION_DECISION;
|
|
1653
1668
|
const reviewRemediation = state.decision === REVIEW_REMEDIATION_DECISION;
|
|
1669
|
+
const processReplacement = state.decision === PROCESS_REPLACEMENT_DECISION;
|
|
1654
1670
|
const hostReconciliationRequired = state.phase === "awaiting-decision" && state.contractRepair === null &&
|
|
1655
|
-
!repairValidationRetryAvailable && !acceptanceRemediation && !reviewRemediation;
|
|
1671
|
+
!repairValidationRetryAvailable && !acceptanceRemediation && !reviewRemediation && !processReplacement;
|
|
1656
1672
|
const failedAcceptanceUnit = acceptanceRemediation
|
|
1657
1673
|
? state.units.find(unit => unit.status === "failed" && ["acceptance", "process-defect"].includes(unit.resultClass ?? "")) : undefined;
|
|
1658
1674
|
const failedEvidence = failedAcceptanceUnit?.failure;
|
|
@@ -1725,6 +1741,9 @@ export class OperatorRuntime {
|
|
|
1725
1741
|
...(hostReconciliationRequired ? {
|
|
1726
1742
|
next_action: `call ${this.profile.toolPrefix}resume_operator once with this run_id and acceptance_fingerprint; if it returns a Task, dispatch that exact Task in this turn`,
|
|
1727
1743
|
} : {}),
|
|
1744
|
+
...(processReplacement ? {
|
|
1745
|
+
next_action: `this immutable run is not resumable: call ${this.profile.toolPrefix}cancel_operator with reason=plain, then call ${this.profile.toolPrefix}prepare_operator for a replacement run under the same goal; copy this packet's acceptance array verbatim, retain consumed cumulative budget, and repair only the read, write, preparation, and validation contract required by that acceptance; do not call resume_operator again or claim evidence`,
|
|
1746
|
+
} : {}),
|
|
1728
1747
|
...(acceptanceRemediation || reviewRemediation ? { next_action: remediationNextAction } : {}),
|
|
1729
1748
|
...(state.repairResidualPaths.length === 0 ? {} : { repair_residual_paths: state.repairResidualPaths }),
|
|
1730
1749
|
...(state.phase === "cancelled" && state.decision === "operator-contract-repair-unavailable-after-cancel" &&
|
|
@@ -4,6 +4,9 @@ import { type CatalogModel } from "./model-routing.js";
|
|
|
4
4
|
export declare function processRemediationReplacementPacket(code: string, packet: unknown, cancelTool: string, prepareTool: string): {
|
|
5
5
|
status: string;
|
|
6
6
|
code: string;
|
|
7
|
+
same_run_resumable: boolean;
|
|
8
|
+
replacement_preserves_goal: boolean;
|
|
9
|
+
cumulative_spend_resets: boolean;
|
|
7
10
|
packet: unknown;
|
|
8
11
|
next_action: string;
|
|
9
12
|
};
|
package/dist/plugin/profiled.js
CHANGED
|
@@ -39,10 +39,13 @@ const PREVIEW_ROUTES = Object.freeze([
|
|
|
39
39
|
PREVIEW_REVIEW_ROUTE,
|
|
40
40
|
]);
|
|
41
41
|
export function processRemediationReplacementPacket(code, packet, cancelTool, prepareTool) {
|
|
42
|
-
|
|
43
|
-
next_action: `Call ${cancelTool} with reason=plain, then call ${prepareTool}
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
const durable = record(packet) ? { ...packet, resume_requires_host_reconciliation: false,
|
|
43
|
+
next_action: `This immutable run is not resumable. Call ${cancelTool} with reason=plain, then call ${prepareTool} for a replacement run under the same goal.` } : packet;
|
|
44
|
+
return { status: "operator-process-remediation-replacement-required", code, same_run_resumable: false,
|
|
45
|
+
replacement_preserves_goal: true, cumulative_spend_resets: false, packet: durable,
|
|
46
|
+
next_action: `Call ${cancelTool} with reason=plain, then call ${prepareTool} for a replacement run under the same goal. ` +
|
|
47
|
+
"Copy the packet acceptance array byte-for-byte and retain cumulative spend. Repair only the read, write, preparation, and validation contract required by that acceptance. " +
|
|
48
|
+
"Do not call resume_operator again, claim evidence, or treat the replacement as a new goal." };
|
|
46
49
|
}
|
|
47
50
|
/**
|
|
48
51
|
* Declare every preview route in the catalog, adding the variant to a listed model and the whole model
|
|
@@ -570,10 +573,11 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
570
573
|
if (requestedReason !== undefined && requestedReason !== "review-blocking" && requestedReason !== "acceptance-remediation")
|
|
571
574
|
throw new Error("operator-cancel-reason-invalid");
|
|
572
575
|
const pending = await operators.read(context.sessionID);
|
|
573
|
-
|
|
576
|
+
const pendingProposal = await proposals.read(context.sessionID);
|
|
577
|
+
if (!pending || (["cancelled", "completed"].includes(pending.phase) && pendingProposal?.phase !== "approved" && pendingProposal !== undefined)) {
|
|
574
578
|
// A pre-approval proposal owns no execution lane. Its admitted child can never be rebound, so without
|
|
575
579
|
// this release the root can neither resume, redispatch, nor start any replacement investigation.
|
|
576
|
-
const proposal =
|
|
580
|
+
const proposal = pendingProposal;
|
|
577
581
|
if (!proposal || proposal.phase === "approved")
|
|
578
582
|
throw new Error("operator-run-missing");
|
|
579
583
|
if (requestedReason !== undefined)
|
|
@@ -704,7 +708,7 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
704
708
|
if (unit.resultClass === "process-defect" && unit.childSessionID === null && unit.callID && state.decision === "dispatch-admission-rejected") {
|
|
705
709
|
const history = await messages(state.units.length > 1 ? delegate : context.sessionID);
|
|
706
710
|
const matching = history.flatMap(message => Array.isArray(message.parts) ? message.parts : []).filter(part => record(part) &&
|
|
707
|
-
part.type === "tool" && part.tool
|
|
711
|
+
part.type === "tool" && ["task", "subagent"].includes(String(part.tool)) && part.callID === unit.callID && record(part.state) && part.state.status === "error" &&
|
|
708
712
|
record(part.state.input) && operators.matchesRecordedWorkerTask(state, unit.unit.id, part.state.input) &&
|
|
709
713
|
taskChildSessionID({ metadata: part.state.metadata, output: typeof part.state.output === "string" ? part.state.output : "" }) === undefined);
|
|
710
714
|
if (matching.length !== 1 || !await control.hasNoGoalReservation(context.sessionID, taskID, unit.callID))
|
|
@@ -736,7 +740,8 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
736
740
|
// A process defect without an authorized Git lifecycle has no committed remediation
|
|
737
741
|
// baseline. Preserve the failed run and return an explicit replacement route instead
|
|
738
742
|
// of turning this local routing limitation into a terminal tool exception.
|
|
739
|
-
|
|
743
|
+
const replacement = await operators.processReplacementRequired(context.sessionID, state.runID);
|
|
744
|
+
return JSON.stringify(processRemediationReplacementPacket(remediationError.message, operators.packet(replacement), cancel, prepare));
|
|
740
745
|
}
|
|
741
746
|
}
|
|
742
747
|
const retried = await operators.resumeRepairValidation(context.sessionID, state.runID, source => control.authorizeOperatorContractRepairValidationRetry(context.sessionID, { ...source, declarationFingerprint: goalFingerprint }));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sortie-dogs",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.14",
|
|
4
4
|
"description": "Bounded agent harness and validated orchestration loop plugin for OpenCode",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"opencode",
|
|
@@ -62,14 +62,13 @@
|
|
|
62
62
|
"build": "tsc -p tsconfig.json",
|
|
63
63
|
"postbuild": "node --input-type=module --eval \"import fs from 'node:fs'; const path = 'dist/cli/main.js'; const source = fs.readFileSync(path, 'utf8'); if (!source.startsWith('#!')) fs.writeFileSync(path, '#!/usr/bin/env node\\n' + source); if (process.platform !== 'win32') fs.chmodSync(path, 0o755);\"",
|
|
64
64
|
"prepack": "npm run build",
|
|
65
|
-
"
|
|
66
|
-
"test": "node
|
|
65
|
+
"test": "node scripts/test-router.mjs quick",
|
|
66
|
+
"test:windows": "node scripts/test-router.mjs windows",
|
|
67
67
|
"pretest:dispatch": "npm run build",
|
|
68
68
|
"test:dispatch": "node --experimental-strip-types --import ./test/setup.ts --test --test-timeout=100000 --test-reporter=spec \"test/execution-plan.test.ts\" \"test/worktree-parallel-dispatch.test.ts\" \"test/acceptance-compiler.test.ts\" \"test/run-flight-ledger.test.ts\" \"test/evidence-capsule.test.ts\"",
|
|
69
69
|
"pretest:integration": "npm run build",
|
|
70
70
|
"test:integration": "node --experimental-strip-types --import ./test/setup.ts --test --test-timeout=1200000 --test-reporter=spec \"test/integration/worktree-parallel-dispatch.test.ts\"",
|
|
71
|
-
"
|
|
72
|
-
"test:full": "node --experimental-strip-types test/helpers/full-test-runner.ts"
|
|
71
|
+
"test:full": "node scripts/test-router.mjs full"
|
|
73
72
|
},
|
|
74
73
|
"dependencies": {
|
|
75
74
|
"ajv": "^8.17.1",
|