sortie-dogs 0.10.14 → 0.10.18
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 +31 -0
- package/dist/core/operator-proposal.d.ts +5 -0
- package/dist/core/operator-proposal.js +25 -0
- package/dist/core/operator-runtime.d.ts +2 -0
- package/dist/core/operator-runtime.js +32 -1
- package/dist/core/worktree-lifecycle.d.ts +2 -0
- package/dist/core/worktree-lifecycle.js +10 -1
- package/dist/plugin/profiled.js +12 -4
- package/dist/plugin/v2.js +5 -0
- package/dist/runtime-assets-v010.js +4 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -106,6 +106,37 @@ The pre-v0.10.6 local benchmark condition is closed historical evidence. New
|
|
|
106
106
|
development decisions use SWE-bench measurements collected under the frozen
|
|
107
107
|
contract in [Coding benchmark completion and correctness](docs/benchmark-completion-contract.md).
|
|
108
108
|
|
|
109
|
+
### v0.10.14 SWE-bench Lite dev23 baseline
|
|
110
|
+
|
|
111
|
+
The first complete v0.10.14 baseline was run against all 23 `dev` instances and
|
|
112
|
+
graded with the pinned official harness. It is a fixed candidate baseline, not a
|
|
113
|
+
leaderboard or general success-rate claim.
|
|
114
|
+
|
|
115
|
+
- Candidate: `sortie-dogs@0.10.14`, package SHA-256
|
|
116
|
+
`f2caf67268a5f69b67a252a0d84bfc5ad09db82684aa8c1e0f5c49f8d2986993`.
|
|
117
|
+
- Dataset: `princeton-nlp/SWE-bench_Lite`, revision
|
|
118
|
+
`6ec7bb89b9342f664a54a6e0a6ea6501d3437cc2`, split `dev`.
|
|
119
|
+
- Conditions: pass@1, `attempts_per_instance=1`, `retry_count=0`, per-instance
|
|
120
|
+
inference limit `$1.50`, four independent inference slots, and official
|
|
121
|
+
scoring with `max_workers=1`.
|
|
122
|
+
- Official score: **6/23 (26.1%)**. All 23 rows received an official result.
|
|
123
|
+
- Resolved instances: `marshmallow-code__marshmallow-1343`,
|
|
124
|
+
`marshmallow-code__marshmallow-1359`, `pydicom__pydicom-1256`,
|
|
125
|
+
`pydicom__pydicom-1694`, `pylint-dev__astroid-1333`, and
|
|
126
|
+
`sqlfluff__sqlfluff-1733`.
|
|
127
|
+
- Inference spend: `$15.7542788` under the separate `$50` v0.10.14 campaign.
|
|
128
|
+
- Environment deviations retained in the result: five `pvlib` rows hit the
|
|
129
|
+
NumPy 2.0 removal of `np.Inf`, and `pyvista__pyvista-4315` lacked
|
|
130
|
+
`libGL.so.1`. These rows remain in the denominator and were not silently
|
|
131
|
+
removed.
|
|
132
|
+
- Four non-successful inference terminals (`timeout` or `patch-index-failed`)
|
|
133
|
+
produced empty patches and are counted as unresolved official results.
|
|
134
|
+
|
|
135
|
+
This baseline is the comparison point for any later v0.10.15 candidate. A
|
|
136
|
+
later candidate must use its own package digest and preserve the same dataset,
|
|
137
|
+
official scoring boundary, and declared budget conditions before its score is
|
|
138
|
+
compared with this value.
|
|
139
|
+
|
|
109
140
|
## Historical local case study
|
|
110
141
|
|
|
111
142
|
These are completion-filtered local references on one frozen DeepSWE task,
|
|
@@ -73,6 +73,11 @@ export interface OperatorProposalState {
|
|
|
73
73
|
readonly intent: OperatorIntent;
|
|
74
74
|
goal_binding: OperatorProposalGoalBinding | null;
|
|
75
75
|
readonly created_at: string;
|
|
76
|
+
/** Frozen carry-in spend; unlike live counters it must not change the admitted Task identity. */
|
|
77
|
+
readonly prior_spend?: {
|
|
78
|
+
readonly reads: number;
|
|
79
|
+
readonly submissions: number;
|
|
80
|
+
};
|
|
76
81
|
phase: "investigating" | "submitted" | "approved";
|
|
77
82
|
proposal_call_id: string | null;
|
|
78
83
|
proposal_session_id: string | null;
|
|
@@ -101,6 +101,25 @@ export class OperatorProposalBudgetError extends Error {
|
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
function parseIntent(value) {
|
|
104
|
+
if (record(value) && Array.isArray(value.allow_read)) {
|
|
105
|
+
const diagnostics = [];
|
|
106
|
+
value.allow_read.forEach((path, index) => {
|
|
107
|
+
if (normalizedRelativePath(path))
|
|
108
|
+
return;
|
|
109
|
+
let expected = "existing repository-relative file or directory; external absolute paths are not proposal read scopes";
|
|
110
|
+
if (typeof path === "string") {
|
|
111
|
+
try {
|
|
112
|
+
expected = normalizeRelativePath(path);
|
|
113
|
+
}
|
|
114
|
+
catch { /* Never authorize an external or traversal path. */ }
|
|
115
|
+
}
|
|
116
|
+
diagnostics.push({ document: "proposal", pointer: `/allow_read/${index}`, code: "operator-intent-read-path-invalid",
|
|
117
|
+
rule: "normalized-relative-path", repair_kind: "repair-field", repair_paths: [`/allow_read/${index}`],
|
|
118
|
+
expected, actual_type: jsonType(path) });
|
|
119
|
+
});
|
|
120
|
+
if (diagnostics.length)
|
|
121
|
+
throw new OperatorContractError(diagnostics);
|
|
122
|
+
}
|
|
104
123
|
const proposalBudget = record(value) && Object.hasOwn(value, "proposal_budget")
|
|
105
124
|
? value.proposal_budget
|
|
106
125
|
: DEFAULT_OPERATOR_PROPOSAL_BUDGET;
|
|
@@ -392,6 +411,11 @@ export class OperatorProposalRuntime {
|
|
|
392
411
|
`original_request: ${state.intent.original_request.text}`, "ordered_requirements:", ...state.intent.requirements.map(item => `- ${item.id} [${item.kind}]: ${item.text}`),
|
|
393
412
|
`authoritative_refs: ${JSON.stringify(state.intent.authoritative_refs)}`, `allow_read: ${JSON.stringify(state.intent.allow_read)}`,
|
|
394
413
|
`proposal_budget: ${JSON.stringify(state.intent.proposal_budget)}`,
|
|
414
|
+
"要件に相談や利用者判断が含まれる場合、workerから追加Taskを呼ぶ計画にしない。rootが派遣前に必要な相談を行い、確認済みの素材許諾・選択・画像参照・決定をunit.objectiveと許可された入力へ明記する。子はroot会話を自動継承しない。未確定の判断だけを未確定として保持し、確認済み事項を再質問させない。実機測定や利用者Goの未達をfixtureで代替しない。",
|
|
415
|
+
...(state.prior_spend ? [`prior_proposal_spend: ${JSON.stringify(state.prior_spend)}; remaining_at_start: ${JSON.stringify({
|
|
416
|
+
reads: state.intent.proposal_budget.max_reads - state.prior_spend.reads,
|
|
417
|
+
submissions: state.intent.proposal_budget.max_submissions - state.prior_spend.submissions
|
|
418
|
+
})}. These are cumulative limits, not fresh grants. budget_estimate.proposal_reads must include prior_proposal_spend.reads plus reads charged in this investigation. Prior read paths are not evidence for this new child.`] : []),
|
|
395
419
|
"proposal packet fields: schema_version, revision, coverage[{requirement_id,approach,validation}], existing_surface[{requirement_id,path,form}], uncovered[{requirement_id,reason}], negative_handling[{requirement_id,handling}], read_scope, write_scope, budget_estimate{proposal_reads,execution_units}, plan{schema_version,acceptance_proof,source_refs,goal_declaration,units, optional git_lifecycle}. revisionは正のJSON整数。例: \"revision\":1。文字列\"revision\":\"1\"は禁止し、hostは型変換しない。plan.acceptanceは生成・提出せず省略し、hostがdurable intent.requirementsのtextを同順・完全一致でmaterializeする。legacy互換として明示する場合だけ同じ全文・順序を完全一致で使い、null・部分集合・並べ替え・空白/大小文字変更は禁止。acceptance_ids等の代替field禁止。",
|
|
396
420
|
"schema_versionはpacketとplanの両方で必ず文字列\"0.1\"。coverageはnegative/qualityを含む全ordered requirement IDを各1回含める(未対応だけuncoveredへ移す)。全declared criterion IDをacceptance_proofから参照し、各unitのacceptance_indicesはnonempty、全acceptance indexとexact validation commandを既存parser規則どおり完全coverageする。",
|
|
397
421
|
"plan.acceptance_proofは各acceptanceに対応するcriterion IDの配列を並べた二重配列。複数acceptanceを同じexact commandが証明する場合はcriterion IDを共有可で、acceptanceごとの固有criterion作成は不要。goal_declarationはobject。delivery_intent=\"implementation\", delivery_mode=\"mvp-first\", usable_path_established=false, controlled_change=false, goal_budget_unitsは正整数。criteria各要素はcriterion_idとvalidation_commandを持つ。",
|
|
@@ -466,6 +490,7 @@ export class OperatorProposalRuntime {
|
|
|
466
490
|
const state = { schema_version: "0.1", profile: this.profile.id, root_session_id: root,
|
|
467
491
|
intent_id: `intent-${intentHash.slice(0, 24)}`, intent_hash: intentHash, intent, created_at: new Date().toISOString(), phase: "investigating",
|
|
468
492
|
goal_binding: null, proposal_call_id: null, proposal_session_id: null, read_count: spend.reads, read_paths: [], submission_count: spend.submissions, proposal_id: null,
|
|
493
|
+
...(spend.reads > 0 || spend.submissions > 0 ? { prior_spend: { reads: spend.reads, submissions: spend.submissions } } : {}),
|
|
469
494
|
proposal_revision: null, proposal_hash: null, proposal: null, approval_rationale: null };
|
|
470
495
|
this.assertBudgetAvailable(state);
|
|
471
496
|
await this.save(state);
|
|
@@ -237,6 +237,8 @@ export declare class OperatorRuntime {
|
|
|
237
237
|
propose(root: string, raw: unknown, scopeApprovalTurnID?: string): Promise<OperatorProposal>;
|
|
238
238
|
private proposeOnce;
|
|
239
239
|
repair(root: string, draftID: string, patches: unknown): Promise<OperatorProposal>;
|
|
240
|
+
/** A diagnosed spelling repair may not add, widen, or redirect a read grant. */
|
|
241
|
+
private sameReadTarget;
|
|
240
242
|
private prepareOnce;
|
|
241
243
|
operatorTask(state: OperatorState): OperatorTask;
|
|
242
244
|
/** Root-visible handle for the bounded operations delegate; its contract stays host-internal. */
|
|
@@ -5,7 +5,7 @@ import { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
|
5
5
|
import { promisify } from "node:util";
|
|
6
6
|
import { acceptanceContinuityFingerprint, inspectAcceptanceContinuity, normalizeAcceptanceCriteria, ACCEPTANCE_CONTINUITY_EXTENSION, MAX_ACCEPTANCE_CONTINUITY_BYTES, MAX_ACCEPTANCE_CRITERIA } from "./acceptance-continuity.js";
|
|
7
7
|
import { expandGoalDeclaration, goalDeclarationDefaults, hasGoalCommandAliasConflict } from "./goal-declaration-format.js";
|
|
8
|
-
import { normalizeRelativePath } from "./path.js";
|
|
8
|
+
import { normalizeManifestPath, normalizeRelativePath } from "./path.js";
|
|
9
9
|
import { CONTRACT_TEXT_LIMITS, validateHandoffSchema, validateOperationManifestSchema } from "./validate-schema.js";
|
|
10
10
|
import { validateManifest } from "./validate-manifest.js";
|
|
11
11
|
import { profileAgent, RUNTIME_PROFILES } from "./runtime-profile.js";
|
|
@@ -574,6 +574,17 @@ export class OperatorRuntime {
|
|
|
574
574
|
if (!record(patch) || !exactKeys(patch, ["op", "path", "value"]) || !["replace", "add"].includes(String(patch.op)) || typeof patch.path !== "string" || !Object.hasOwn(patch, "value")) {
|
|
575
575
|
throw new Error("operator-repair-invalid");
|
|
576
576
|
}
|
|
577
|
+
const readMatch = /^\/units\/(0|[1-9]\d*)\/read\/(0|[1-9]\d*)$/.exec(patch.path);
|
|
578
|
+
if (readMatch) {
|
|
579
|
+
const unitIndex = Number(readMatch[1]), pathIndex = Number(readMatch[2]);
|
|
580
|
+
const diagnosed = Array.isArray(draft.diagnostics) && draft.diagnostics.some((item) => item.document === "plan" && item.pointer === patch.path && item.code === "operator-scope-invalid");
|
|
581
|
+
if (patch.op !== "replace" || !diagnosed ||
|
|
582
|
+
!await this.sameReadTarget(original.units[unitIndex]?.read?.[pathIndex], patch.value)) {
|
|
583
|
+
throw new Error("operator-repair-path-forbidden");
|
|
584
|
+
}
|
|
585
|
+
draft.plan.units[unitIndex].read[pathIndex] = patch.value;
|
|
586
|
+
continue;
|
|
587
|
+
}
|
|
577
588
|
if (patch.path === "/git_lifecycle/post_commit_validation") {
|
|
578
589
|
const finalValidation = original.units.at(-1)?.validation;
|
|
579
590
|
if (patch.op !== "replace" || !strings(patch.value, true) || !Array.isArray(finalValidation) ||
|
|
@@ -625,6 +636,25 @@ export class OperatorRuntime {
|
|
|
625
636
|
return this.proposeOnce(root, draft.plan);
|
|
626
637
|
});
|
|
627
638
|
}
|
|
639
|
+
/** A diagnosed spelling repair may not add, widen, or redirect a read grant. */
|
|
640
|
+
async sameReadTarget(before, after) {
|
|
641
|
+
if (!text(before) || !text(after))
|
|
642
|
+
return false;
|
|
643
|
+
try {
|
|
644
|
+
if (normalizeRelativePath(after) !== after)
|
|
645
|
+
return false;
|
|
646
|
+
const original = normalizeManifestPath(before);
|
|
647
|
+
if (original.kind === "relative")
|
|
648
|
+
return original.path === after;
|
|
649
|
+
if (!isAbsolute(original.path))
|
|
650
|
+
return false;
|
|
651
|
+
const [left, right] = await Promise.all([realpath(original.path), realpath(resolve(this.projectRoot, after))]);
|
|
652
|
+
return process.platform === "win32" ? left.toLowerCase() === right.toLowerCase() : left === right;
|
|
653
|
+
}
|
|
654
|
+
catch {
|
|
655
|
+
return false;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
628
658
|
async prepareOnce(root, raw, scopeApprovalTurnID) {
|
|
629
659
|
const previous = await this.read(root);
|
|
630
660
|
let immutableReplacement = previous?.phase === "cancelled" &&
|
|
@@ -797,6 +827,7 @@ export class OperatorRuntime {
|
|
|
797
827
|
"validation:", ...unit.validation.map(value => ` - ${value}`),
|
|
798
828
|
"Execute validation in its declared order. Earlier entries may be approved generator, build, formatter, or exact cleanup commands required before canonical criterion tests. Every persistent or transient generator output must be declared in unit.write. Cleanup may remove only declared unit.write outputs and must be an explicit ordered command after generation and before post-commit or canonical validation; never add an ignore rule or remove an undeclared path. If any necessary command, input, output, or cleanup is missing, do not run an undeclared command or variant and do not use resume evidence tooling to invent permission; return a contract-repair decision.",
|
|
799
829
|
"Preserve existing public API success and error return semantics unless acceptance explicitly changes them, and cover those compatibility boundaries in the declared validation.",
|
|
830
|
+
"Do not spawn nested subagents for consultation. Required consultations belong to the root before dispatch; use the confirmed decisions and evidence declared in the unit objective and inputs. If required consultation results or user decisions are missing, return the exact contract gap to the parent instead of attempting a deeper Task, inventing consent, or asking the user to repeat an already recorded decision.",
|
|
800
831
|
...(plan.git_lifecycle !== undefined && index === plan.units.length - 1 ? [
|
|
801
832
|
`git_post_commit_validation: ${JSON.stringify(plan.git_lifecycle.post_commit_validation)}`,
|
|
802
833
|
"Complete every source write before invoking any git_post_commit_validation command. Its first exact invocation is the host commit boundary; after it, source mutation and undeclared shell commands are denied. Run every listed command and return its real evidence.",
|
|
@@ -51,6 +51,7 @@ export declare class WorktreeLifecycle {
|
|
|
51
51
|
private readonly inFlight;
|
|
52
52
|
private writeQueue;
|
|
53
53
|
private transactionQueue;
|
|
54
|
+
private preparationQueue;
|
|
54
55
|
private transactionLease;
|
|
55
56
|
private readonly directParentAttestations;
|
|
56
57
|
private constructor();
|
|
@@ -83,6 +84,7 @@ export declare class WorktreeLifecycle {
|
|
|
83
84
|
private attestDirectParent;
|
|
84
85
|
private exactEntry;
|
|
85
86
|
private createAndLock;
|
|
87
|
+
private createAndLockOnce;
|
|
86
88
|
private addWorktree;
|
|
87
89
|
private requireOwnedRecord;
|
|
88
90
|
private isDirectManagedPath;
|
|
@@ -139,6 +139,7 @@ export class WorktreeLifecycle {
|
|
|
139
139
|
inFlight = new Set();
|
|
140
140
|
writeQueue = Promise.resolve();
|
|
141
141
|
transactionQueue = Promise.resolve();
|
|
142
|
+
preparationQueue = Promise.resolve();
|
|
142
143
|
transactionLease;
|
|
143
144
|
directParentAttestations = new Map();
|
|
144
145
|
constructor(repositoryRoot, worktreeRoot, gitPath, commonGitDir, rootIdentity) {
|
|
@@ -831,7 +832,15 @@ export class WorktreeLifecycle {
|
|
|
831
832
|
return undefined;
|
|
832
833
|
return entry;
|
|
833
834
|
}
|
|
834
|
-
|
|
835
|
+
createAndLock(record) {
|
|
836
|
+
// Git scans sibling administrative directories while adding and attesting worktrees. A peer's
|
|
837
|
+
// not-yet-written commondir can make Git fail after branch creation. Publish one complete entry
|
|
838
|
+
// at a time under the inventory lease; setup hooks and later worker execution remain concurrent.
|
|
839
|
+
const prepared = this.preparationQueue.then(() => this.createAndLockOnce(record));
|
|
840
|
+
this.preparationQueue = prepared.then(() => undefined, () => undefined);
|
|
841
|
+
return prepared;
|
|
842
|
+
}
|
|
843
|
+
async createAndLockOnce(record) {
|
|
835
844
|
await this.assertRootIdentity();
|
|
836
845
|
if (!this.isDirectManagedRecord(record) || await lstat(record.path).catch(() => undefined) !== undefined ||
|
|
837
846
|
await this.branchExists(record.branch))
|
package/dist/plugin/profiled.js
CHANGED
|
@@ -449,6 +449,7 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
449
449
|
return JSON.stringify(operators.packet(state));
|
|
450
450
|
control.enableUnits(state.rootSessionID, state.units.length);
|
|
451
451
|
return JSON.stringify({ profile: profile.id, run_id: state.runID, acceptance_fingerprint: state.acceptanceFingerprint,
|
|
452
|
+
dispatch_instruction: "Dispatch this exact Task in the foreground. Omit background or set it to false; background=true is not supported by the operator lifecycle. Required consultations belong to the root before dispatch; confirmed user decisions must already be in the approved unit objective and inputs, not appended to this Task reference.",
|
|
452
453
|
fast_path: state.units.length === 1, task: state.units.length === 1 ? operators.nextWorkerTask(state) : operators.dispatchTask(state) });
|
|
453
454
|
}
|
|
454
455
|
tools[prepare] = { description: `Freeze an approved serial operator plan. Invalid plans return bounded diagnostics and a draft_id for field-only repair; no worker is started. After operator-acceptance-remediation-required, cancel first and prepare only the same exact acceptance, a write scope within the prior approved union, and a Git lifecycle starting at the failed committed head; consumed goal budget is retained. Coordinator only. ${planContract}`,
|
|
@@ -485,7 +486,7 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
485
486
|
await registerPreparedGoal(context.sessionID, state);
|
|
486
487
|
return preparedTask(state);
|
|
487
488
|
} };
|
|
488
|
-
tools[repair] = { description: "Repair a root-owned draft with named fields, git_lifecycle branch/start/message fields, or an already-declared criterion command. Empty patches revalidate the saved draft without resending it. Acceptance, unit count and write scope remain fixed.",
|
|
489
|
+
tools[repair] = { description: "Repair a root-owned draft with named fields, git_lifecycle branch/start/message fields, or an already-declared criterion command. A diagnosed /units/i/read/j scope error may be replaced with a normalized repository-relative spelling of the same resource only: absolute inputs require an existing relative alias whose realpath is identical; read expansion, redirection, whole-array replacement and write-scope repair are forbidden. Empty patches revalidate the saved draft without resending it. Acceptance, unit count and write scope remain fixed.",
|
|
489
490
|
args: { draft_id: stringSchema, patches_json: stringSchema }, execute: async (args, context) => {
|
|
490
491
|
await requireRoot(context.sessionID);
|
|
491
492
|
let patches;
|
|
@@ -549,17 +550,18 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
549
550
|
const proposal = await proposals.read(context.sessionID);
|
|
550
551
|
const proposalNextAction = proposal?.phase === "investigating"
|
|
551
552
|
? proposal.proposal_call_id !== null
|
|
552
|
-
? "proposal Task is already admitted; do not redispatch it or call operator_next. Continue submission repairs only in the same active claimed child. If that child terminated without submission,
|
|
553
|
+
? "proposal Task is already admitted; do not redispatch it or call operator_next. Continue submission repairs only in the same active claimed child. If that child terminated or was interrupted without submission, an explicit root decision to retry may call cancel_operator with reason=plain to release this grant. Cancellation stops owned children and durably preserves cumulative reads/submissions and goal spend. Pass the returned retry_intent unchanged to begin_operator_proposal, then dispatch only its new exact Task within the remaining budget. Never reuse the terminated child, reset spend, or replace the goal or ordered requirements. Exhaustion requires an explicit cumulative budget revision, not a reset."
|
|
553
554
|
: proposal.submission_count >= proposal.intent.proposal_budget.max_submissions
|
|
554
555
|
? "proposal submission budget exhausted; do not call operator_next; report the bounded proposal failure"
|
|
555
556
|
: "proposal is not submitted; do not call operator_next; complete or repair the bounded proposal submission"
|
|
556
557
|
: proposal?.phase === "submitted"
|
|
557
|
-
? "compare and approve the exact proposal; do not call operator_next before approval prepares a run"
|
|
558
|
+
? "compare and approve the exact proposal; do not call operator_next before approval prepares a run. Approval authorizes implementation, not product acceptance. Preserve every required live measurement, consultation and user approval as pending acceptance obligations; canonical commands do not substitute for them. Use the host budget counters, not plan.goal_declaration.goal_budget_units, for remaining capacity; approval validates the retained goal binding and execution budget. A bounded diagnostic run may use cancel_operator after admission without inventing a plan validation command for cancellation."
|
|
558
559
|
: "approved proposal has no operator run; do not call operator_next; reconcile the approval or preparation failure";
|
|
559
560
|
return JSON.stringify(state ? { ...await operatorPacket(state), ...(draft ? { pending_draft: draft } : {}),
|
|
560
561
|
...(proposal ? { proposal: proposalIdentity(proposal) } : {}) }
|
|
561
562
|
: draft ? { ...draft, ...(proposal ? { proposal: proposals.packet(proposal) } : {}) }
|
|
562
563
|
: proposal ? { profile: profile.id, proposal: proposals.packet(proposal),
|
|
564
|
+
goal_binding: proposal.goal_binding, budget: await control.currentBudget(context.sessionID),
|
|
563
565
|
...(proposal.phase === "investigating" && proposal.proposal_call_id === null ? { task: proposals.referenceTask(proposal),
|
|
564
566
|
dispatch_instruction: "Pass this short Task reference verbatim; the host expands it only inside the directly claimed proposal child." } : {}),
|
|
565
567
|
next_action: proposalNextAction }
|
|
@@ -587,8 +589,11 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
587
589
|
operatorTurnLifecycle.set(context.sessionID, "cancelled");
|
|
588
590
|
return JSON.stringify({ profile: profile.id, status: "cancelled", scope: "proposal",
|
|
589
591
|
released_proposal: discarded ? proposals.packet(discarded) : null,
|
|
592
|
+
retry_intent: discarded?.intent ?? null,
|
|
593
|
+
retained_goal_binding: discarded?.goal_binding ?? null,
|
|
590
594
|
next_action: "the bounded proposal grant is released and its spent reads/submissions are not restored; " +
|
|
591
|
-
"
|
|
595
|
+
"cumulative proposal and goal spend are preserved. On an explicit root decision to retry, pass retry_intent unchanged to begin_operator_proposal, " +
|
|
596
|
+
"preserve the same goal and ordered requirements, and dispatch only the new exact Task within the remaining budget. Never reuse the cancelled child or reset any budget" });
|
|
592
597
|
}
|
|
593
598
|
const current = pending;
|
|
594
599
|
if (requestedReason === "acceptance-remediation" && current.decision !== "operator-acceptance-remediation-required")
|
|
@@ -772,6 +777,9 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
772
777
|
catch (error) {
|
|
773
778
|
if (error instanceof OperatorProposalBudgetError)
|
|
774
779
|
return JSON.stringify(error.diagnostic);
|
|
780
|
+
if (error instanceof OperatorContractError)
|
|
781
|
+
return JSON.stringify({ status: "invalid-intent",
|
|
782
|
+
diagnostics: error.diagnostics, next_action: "Repair only the diagnosed fields and retry begin_operator_proposal; preserve original_request and every ordered requirement. No proposal was admitted and no budget was spent." });
|
|
775
783
|
throw error;
|
|
776
784
|
}
|
|
777
785
|
state = await proposals.bindGoal(context.sessionID, await control.proposalGoalBinding(context.sessionID));
|
package/dist/plugin/v2.js
CHANGED
|
@@ -136,6 +136,11 @@ function legacyToolName(name) {
|
|
|
136
136
|
function legacyToolInput(name, input) {
|
|
137
137
|
const value = record(input) ? { ...input } : {};
|
|
138
138
|
if (name === "subagent") {
|
|
139
|
+
if (value.background === true && typeof value.prompt === "string" && value.prompt.startsWith("SORTIE_OPERATOR_")) {
|
|
140
|
+
throw new Error("operator-background-dispatch-not-supported: retry the same exact Task with background omitted or false; no admission or budget reservation occurred. Operator dispatch requires foreground completion for lifecycle accounting.");
|
|
141
|
+
}
|
|
142
|
+
if (value.background === false)
|
|
143
|
+
delete value.background;
|
|
139
144
|
if (typeof value.agent === "string")
|
|
140
145
|
value.subagent_type = value.agent;
|
|
141
146
|
if (typeof value.sessionID === "string")
|
|
@@ -187,6 +187,10 @@ ${profile.toolPrefix}operator_status to recover the existing draft_id after an i
|
|
|
187
187
|
Call ${profile.toolPrefix}repair_operator_plan with that draft_id and patches_json (field operations)
|
|
188
188
|
to repair only /units/<index>/validation, acceptance_indices, title, or objective. This tool cannot change acceptance,
|
|
189
189
|
unit count or write scope. Do not regenerate the full plan or collapse units to work around a mapping error.
|
|
190
|
+
A diagnosed operator-scope-invalid at /units/<index>/read/<index> also permits replace with a normalized relative
|
|
191
|
+
spelling of the same resource. An absolute input requires an existing relative alias with identical realpath.
|
|
192
|
+
Repair each diagnosed entry using the newly returned draft_id; never widen or redirect reads, replace the whole
|
|
193
|
+
read array, create an alias through this repair tool, or repair write scope.
|
|
190
194
|
An empty patches_json array revalidates the saved draft without resending the full plan; prefer this after a runtime repair.
|
|
191
195
|
The repair tool also permits add/replace of /goal_declaration/criteria/<index>/goal_validation_command (or validation_command)
|
|
192
196
|
only when the exact command is already declared in the validation list of a unit assigned to that criterion.
|