sortie-dogs 0.12.22 → 0.12.24
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/dist/asset-version.d.ts +1 -1
- package/dist/asset-version.js +1 -1
- package/dist/plugin/declared-artifacts.d.ts +4 -1
- package/dist/plugin/declared-artifacts.js +23 -3
- package/dist/plugin/index.js +26 -0
- package/dist/plugin/mission-review.d.ts +1 -0
- package/dist/plugin/mission-review.js +17 -6
- package/dist/plugin/profiled.js +40 -4
- package/dist/plugin/runtime-bridge.d.ts +7 -0
- package/dist/runtime-mission-assets.js +11 -2
- package/package.json +1 -1
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.12.
|
|
6
|
+
export declare const V010_RUNTIME_ASSET_VERSION = "0.12.24-review-evidence-recovery-v1";
|
|
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.12.
|
|
6
|
+
export const V010_RUNTIME_ASSET_VERSION = "0.12.24-review-evidence-recovery-v1";
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
/** Explicit external artifacts are filesystem inputs, not Git pathspecs. Stream complete bytes;
|
|
2
2
|
* previews are bounded independently. Follow package links only inside a declared physical root.
|
|
3
3
|
*/
|
|
4
|
-
export declare function declaredArtifacts(paths: readonly string[], previewLimit?: number
|
|
4
|
+
export declare function declaredArtifacts(paths: readonly string[], previewLimit?: number, options?: {
|
|
5
|
+
reportUnreadableDirectories?: boolean;
|
|
6
|
+
}): Promise<{
|
|
5
7
|
entries: (readonly [string, string, (string | undefined)?])[];
|
|
6
8
|
excerpt: string;
|
|
7
9
|
truncated: boolean;
|
|
10
|
+
unreadable: string[];
|
|
8
11
|
}>;
|
|
@@ -9,7 +9,7 @@ const within = (root, path) => {
|
|
|
9
9
|
/** Explicit external artifacts are filesystem inputs, not Git pathspecs. Stream complete bytes;
|
|
10
10
|
* previews are bounded independently. Follow package links only inside a declared physical root.
|
|
11
11
|
*/
|
|
12
|
-
export async function declaredArtifacts(paths, previewLimit = 0) {
|
|
12
|
+
export async function declaredArtifacts(paths, previewLimit = 0, options = {}) {
|
|
13
13
|
const roots = [...new Set(paths.map(path => resolve(path)))].sort();
|
|
14
14
|
const physical = await Promise.all(roots.map(path => realpath(path).catch((error) => {
|
|
15
15
|
if (error.code === "ENOENT")
|
|
@@ -18,6 +18,7 @@ export async function declaredArtifacts(paths, previewLimit = 0) {
|
|
|
18
18
|
})));
|
|
19
19
|
const entries = [];
|
|
20
20
|
const excerpts = [];
|
|
21
|
+
const unreadable = [];
|
|
21
22
|
let remaining = previewLimit, truncated = false;
|
|
22
23
|
const visited = new Set();
|
|
23
24
|
const visit = async (path, ancestors) => {
|
|
@@ -47,7 +48,26 @@ export async function declaredArtifacts(paths, previewLimit = 0) {
|
|
|
47
48
|
if (metadata.isDirectory()) {
|
|
48
49
|
entries.push([label, "directory", canonical]);
|
|
49
50
|
const next = new Set([...ancestors, canonical]);
|
|
50
|
-
|
|
51
|
+
let children;
|
|
52
|
+
try {
|
|
53
|
+
children = await readdir(path);
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
if (!options.reportUnreadableDirectories || !["EACCES", "EPERM"].includes(error.code ?? ""))
|
|
57
|
+
throw error;
|
|
58
|
+
entries.push([label, `unreadable:${error.code}`, canonical]);
|
|
59
|
+
unreadable.push(label);
|
|
60
|
+
const notice = `\n--- external directory: ${label} ---\n[not inspected: permission denied; select specific result files as review evidence]\n`;
|
|
61
|
+
const bytes = Buffer.from(notice), bounded = bytes.subarray(0, remaining);
|
|
62
|
+
if (bounded.length) {
|
|
63
|
+
excerpts.push(bounded.toString("utf8"));
|
|
64
|
+
remaining -= bounded.length;
|
|
65
|
+
}
|
|
66
|
+
if (bounded.length < bytes.length)
|
|
67
|
+
truncated = true;
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
for (const child of children.sort())
|
|
51
71
|
await visit(join(path, child), next);
|
|
52
72
|
return;
|
|
53
73
|
}
|
|
@@ -76,5 +96,5 @@ export async function declaredArtifacts(paths, previewLimit = 0) {
|
|
|
76
96
|
};
|
|
77
97
|
for (const path of roots)
|
|
78
98
|
await visit(path, new Set());
|
|
79
|
-
return { entries, excerpt: excerpts.join(""), truncated };
|
|
99
|
+
return { entries, excerpt: excerpts.join(""), truncated, unreadable };
|
|
80
100
|
}
|
package/dist/plugin/index.js
CHANGED
|
@@ -7884,6 +7884,32 @@ export const SortieDogsPlugin = async (input, options) => {
|
|
|
7884
7884
|
cost_note: "Worker token-price estimates from complete native usage; missing requests remain unknown and are reconciled on later status. Excludes orchestration/review and external campaign spend. Zero settled cost does not mean free execution." };
|
|
7885
7885
|
return snapshot;
|
|
7886
7886
|
},
|
|
7887
|
+
extendMissionUnitBudget: (root, maxUnits) => serializeChatTransition(root, async () => {
|
|
7888
|
+
if (!isCoordinatorSession(root) && !await recoverCoordinatorRoot(root))
|
|
7889
|
+
throw new Error("operator-coordinator-required");
|
|
7890
|
+
await recoverPendingRealGoalTurn(root);
|
|
7891
|
+
await recoverCompletedGoalReservations(root);
|
|
7892
|
+
const ledger = await goalLedger(root), state = (await ledger.readGoal()).state;
|
|
7893
|
+
if (state.phase !== "active" || state.receipt !== null || state.goal_id === null || state.budget === null ||
|
|
7894
|
+
state.latest_user_message_id === null)
|
|
7895
|
+
throw new Error("mission-budget-goal-not-active");
|
|
7896
|
+
if (!Number.isSafeInteger(maxUnits) || maxUnits <= 0 || maxUnits < state.budget.max_units ||
|
|
7897
|
+
maxUnits < state.consumed_units + state.outstanding_reservations.length)
|
|
7898
|
+
throw new Error("mission-budget-limit-invalid");
|
|
7899
|
+
const changed = maxUnits > state.budget.max_units;
|
|
7900
|
+
if (changed) {
|
|
7901
|
+
await ledger.appendGoal({ kind: "goal.revised", at: new Date().toISOString(), goal_id: state.goal_id,
|
|
7902
|
+
revision: state.revision + 1, scope_epoch: state.scope_epoch + 1,
|
|
7903
|
+
acceptance_fingerprint: state.acceptance_fingerprint, origin_user_message_id: state.latest_user_message_id,
|
|
7904
|
+
session_id: root, selected_agent: state.selected_agent, delivery: state.delivery,
|
|
7905
|
+
budget: { ...state.budget, max_units: maxUnits, source: "user-revision" },
|
|
7906
|
+
acceptance_contract: state.acceptance_contract });
|
|
7907
|
+
}
|
|
7908
|
+
const updated = (await ledger.readGoal()).state;
|
|
7909
|
+
const reserved = updated.outstanding_reservations.length;
|
|
7910
|
+
return { status: changed ? "extended" : "unchanged", max_units: updated.budget.max_units, consumed_units: updated.consumed_units,
|
|
7911
|
+
reserved_units: reserved, remaining_units: updated.budget.max_units - updated.consumed_units - reserved };
|
|
7912
|
+
}),
|
|
7887
7913
|
...{
|
|
7888
7914
|
missionWorkerTerminal: async (childSessionID, writeScopes) => {
|
|
7889
7915
|
const active = activeSessions.get(childSessionID), authorization = sessionAuthorizations.get(childSessionID);
|
|
@@ -13,4 +13,5 @@ export declare function completedMissionReviewPrompts(mission: OperatorMission |
|
|
|
13
13
|
export declare function missionReviewSource(directory: string, run: OperatorState, evidence?: readonly MissionEvidenceExcerpt[], baseline?: string, priorScope?: MissionReviewScope): Promise<{
|
|
14
14
|
fingerprint: string;
|
|
15
15
|
excerpt: string;
|
|
16
|
+
truncatedEvidence: string[];
|
|
16
17
|
}>;
|
|
@@ -214,9 +214,11 @@ export async function missionReviewSource(directory, run, evidence = [], baselin
|
|
|
214
214
|
if (needsNotice[index])
|
|
215
215
|
selected += truncated(item.entry);
|
|
216
216
|
}
|
|
217
|
+
const truncatedEvidence = focused.flatMap(({ entry }, index) => needsNotice[index] ? [`${entry.path}:${entry.offset}`] : []);
|
|
217
218
|
if (writes.length === 0)
|
|
218
219
|
return { fingerprint: `sha256:${hash.digest("hex")}`,
|
|
219
|
-
excerpt: selected + "[Read-only units: no declared output files. Review the supplied observations, traces and validation evidence.]"
|
|
220
|
+
excerpt: selected + "[Read-only units: no declared output files. Review the supplied observations, traces and validation evidence.]",
|
|
221
|
+
truncatedEvidence };
|
|
220
222
|
// The shared dependency environment is local tooling, never reviewed or pinned source.
|
|
221
223
|
const external = [], local = [];
|
|
222
224
|
for (const path of writes) {
|
|
@@ -230,12 +232,15 @@ export async function missionReviewSource(directory, run, evidence = [], baselin
|
|
|
230
232
|
const git = async (args) => (await exec("git", args, { cwd: directory, maxBuffer: 8 * 1024 * 1024 })).stdout;
|
|
231
233
|
const names = local.length ? await git(["ls-files", "-z", "--cached", "--others", "--exclude-standard", "--", ...scopes]) : "";
|
|
232
234
|
const untracked = new Set((local.length ? await git(["ls-files", "-z", "--others", "--exclude-standard", "--", ...scopes]) : "").split("\0").filter(Boolean));
|
|
233
|
-
//
|
|
234
|
-
//
|
|
235
|
-
|
|
235
|
+
// Go's ignored in-project caches may be writable during validation but are not candidate
|
|
236
|
+
// output. Keep every other ignored declared output visible and fingerprinted, including
|
|
237
|
+
// directories of generated artifacts; focused references can still pin a cache file.
|
|
238
|
+
const ignored = local.length ? await git(["ls-files", "-z", "--others", "--ignored", "--exclude-standard", "--",
|
|
239
|
+
...scopes, ...[".gocache", ".gomodcache", ".gopath"].map(path => `:(exclude)${path}`)]) : "";
|
|
236
240
|
for (const path of ignored.split("\0").filter(Boolean))
|
|
237
241
|
untracked.add(path);
|
|
238
242
|
const omitted = [];
|
|
243
|
+
const unreadable = [];
|
|
239
244
|
let diff = "";
|
|
240
245
|
if (local.length && baseline) {
|
|
241
246
|
// HEAD-only diffs are empty once the Worker commits. Show bounded changes from the
|
|
@@ -305,13 +310,19 @@ export async function missionReviewSource(directory, run, evidence = [], baselin
|
|
|
305
310
|
}
|
|
306
311
|
}
|
|
307
312
|
if (external.length) {
|
|
308
|
-
|
|
313
|
+
// A declared write scope can also be a host-managed runtime directory (e.g. Docker's data root).
|
|
314
|
+
// Do not make review depend on listing it; record the missing coverage instead. Protected
|
|
315
|
+
// validation still requires readable outputs and does not use this review-only option.
|
|
316
|
+
const artifacts = await declaredArtifacts(external, Math.max(1, 24_000 - Buffer.byteLength(excerpt)), { reportUnreadableDirectories: true });
|
|
309
317
|
hash.update(JSON.stringify(artifacts.entries));
|
|
310
318
|
excerpt += artifacts.excerpt;
|
|
311
319
|
if (artifacts.truncated)
|
|
312
320
|
omitted.push("external artifacts");
|
|
321
|
+
unreadable.push(...artifacts.unreadable);
|
|
313
322
|
}
|
|
314
323
|
const bytes = Buffer.from(excerpt);
|
|
315
324
|
return { fingerprint: `sha256:${hash.digest("hex")}`, excerpt: (bytes.length > 24_000 ? bytes.subarray(0, 24_000).toString("utf8") : excerpt) +
|
|
316
|
-
(bytes.length > 24_000 || omitted.length ? `\n[EXCERPT TRUNCATED: ${omitted.slice(0, 20).join(", ")}; supply focused traces for missing sections, not another implementation unit]` : "")
|
|
325
|
+
(bytes.length > 24_000 || omitted.length ? `\n[EXCERPT TRUNCATED: ${omitted.slice(0, 20).join(", ")}; supply focused traces for missing sections, not another implementation unit]` : "") +
|
|
326
|
+
(unreadable.length ? `\n[UNINSPECTED EXTERNAL DIRECTORIES: ${unreadable.slice(0, 20).join(", ")}; select specific result files as review evidence]` : ""),
|
|
327
|
+
truncatedEvidence };
|
|
317
328
|
}
|
package/dist/plugin/profiled.js
CHANGED
|
@@ -663,6 +663,14 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
663
663
|
function missionDispatchPacket(mission, run) {
|
|
664
664
|
const packet = { ...missionPacket(mission, run), project_root: input.directory,
|
|
665
665
|
coordinator_dispatch: mission.dispatchOpen ? "active" : ["completed", "cancelled"].includes(mission.phase) ? "terminal" : "resumable" };
|
|
666
|
+
if (mission.phase === "submitted" && mission.submission?.status === "blocked" &&
|
|
667
|
+
run?.units.some(unit => unit.status === "running")) {
|
|
668
|
+
return { ...packet, next_action: `If the native Worker Task is still active, wait for it; do not start a duplicate. ` +
|
|
669
|
+
`If the parent Coordinator Task has finished or was interrupted, and the user chose to stop/replan, Operator root (not Coordinator): ` +
|
|
670
|
+
`call ${profile.toolPrefix}cancel_operator with reason=plain to stop owned children, then ` +
|
|
671
|
+
`${profile.toolPrefix}start_mission with intent=replace and the saved requirements. ` +
|
|
672
|
+
`Use the returned Coordinator Task; the previous Mission is archived and cumulative spend is retained.` };
|
|
673
|
+
}
|
|
666
674
|
if (sourceReconciliationRequired(mission, run))
|
|
667
675
|
return { ...packet, status: "mission-source-reconciliation-required",
|
|
668
676
|
next_action: mission.dispatchOpen ? "The Coordinator Task is still active. Do not redispatch; wait for its native completion and reconcile via operator_status."
|
|
@@ -1072,7 +1080,10 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
1072
1080
|
const mission = await missions.read(context.sessionID);
|
|
1073
1081
|
if (mission && !["completed", "cancelled"].includes(mission.phase)) {
|
|
1074
1082
|
await stop(context.sessionID, "explicit-cancellation", false);
|
|
1075
|
-
return JSON.stringify(missionPacket(await missions.required(context.sessionID), await operators.read(context.sessionID))
|
|
1083
|
+
return JSON.stringify({ ...missionPacket(await missions.required(context.sessionID), await operators.read(context.sessionID)),
|
|
1084
|
+
next_action: `Owned children were stopped; cumulative spend is retained. To continue the same request, ` +
|
|
1085
|
+
`Operator root calls ${profile.toolPrefix}start_mission with intent=replace and the saved requirements, ` +
|
|
1086
|
+
`then dispatches its returned Coordinator Task. The cancelled Mission is historical; do not reuse its Worker.` });
|
|
1076
1087
|
}
|
|
1077
1088
|
const rawReason = args.reason;
|
|
1078
1089
|
const requestedReason = rawReason === "plain" || rawReason === undefined ? undefined : rawReason;
|
|
@@ -1471,8 +1482,22 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
1471
1482
|
return JSON.stringify({ ...proposalIdentity(committed), execution: JSON.parse(preparedTask(prepared.state)) });
|
|
1472
1483
|
});
|
|
1473
1484
|
} };
|
|
1474
|
-
const startMission = `${profile.toolPrefix}start_mission`, planUnits = `${profile.toolPrefix}plan_units`, reviewMission = `${profile.toolPrefix}review_mission`, submitMission = `${profile.toolPrefix}submit_mission`, completeMission = `${profile.toolPrefix}complete_mission`, expandUnit = `${profile.toolPrefix}expand_unit`, skipMissionConsultation = `${profile.toolPrefix}skip_mission_consultation`;
|
|
1485
|
+
const startMission = `${profile.toolPrefix}start_mission`, planUnits = `${profile.toolPrefix}plan_units`, reviewMission = `${profile.toolPrefix}review_mission`, submitMission = `${profile.toolPrefix}submit_mission`, completeMission = `${profile.toolPrefix}complete_mission`, expandUnit = `${profile.toolPrefix}expand_unit`, skipMissionConsultation = `${profile.toolPrefix}skip_mission_consultation`, extendMissionBudget = `${profile.toolPrefix}extend_mission_budget`;
|
|
1475
1486
|
const stringList = { type: "array", items: { type: "string" } };
|
|
1487
|
+
tools[extendMissionBudget] = { description: "Root-only: after the user explicitly approves a cumulative Worker-unit increase, extend the same active Mission's host goal budget. max_units is the new cumulative total, not an increment. Preserve consumed/reserved units, requirements and execution; this does not dispatch a Worker or change the campaign cost cap. Read operator_status for the exact mission ID and counters, then resume its existing Coordinator.",
|
|
1488
|
+
args: { mission_id: stringSchema, max_units: { type: "integer", minimum: 1 } }, execute: async (args, context) => {
|
|
1489
|
+
await requireRoot(context.sessionID);
|
|
1490
|
+
return serializeDispatchTransition(context.sessionID, async () => {
|
|
1491
|
+
const mission = await missions.required(context.sessionID);
|
|
1492
|
+
if (mission.id !== args.mission_id || ["cancelled", "completed"].includes(mission.phase))
|
|
1493
|
+
throw new Error("mission-budget-mission-not-active");
|
|
1494
|
+
const maxUnits = Number(args.max_units);
|
|
1495
|
+
const result = await control.extendMissionUnitBudget(context.sessionID, maxUnits);
|
|
1496
|
+
const { status, ...budget } = result;
|
|
1497
|
+
return JSON.stringify({ status, mission_id: mission.id, budget,
|
|
1498
|
+
next_action: "Read operator_status: if the Coordinator dispatch is active, continue it; otherwise dispatch the returned task to resume the same Coordinator. Retain requirements, spend and candidate; no new mission or plan approval." });
|
|
1499
|
+
});
|
|
1500
|
+
} };
|
|
1476
1501
|
tools[startMission] = { description: "Operator: save the current user requirements. Use intent=replace when the user changes an existing request (version, parallelism, target): host cancels the previous run and archives its requirements/results while retaining spend. If status reports mission-source-reconciliation-required and the saved requirements reflect that changed scope, call intent=replace with those exact requirements: the host repairs this mission in place and keeps its Coordinator. Supply the complete current requirements, retaining constraints the user has not changed. For separate work in a different location use intent=new. Dispatch the Coordinator immediately, or plan_units for a known single-unit procedure; item count and runtime alone do not require a Coordinator.",
|
|
1477
1502
|
args: { requirements: { ...stringList, minItems: 1, maxItems: 64 },
|
|
1478
1503
|
kind: { type: "string", enum: ["implementation", "operation"], description: "Use operation for running an existing benchmark, command or procedure. The host records its actual execution separately from setup and checks.", "x-sortie-optional": true },
|
|
@@ -1771,6 +1796,14 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
1771
1796
|
return await serializeDispatchTransition(root, () => declareMissionUnits(root, context.sessionID, mission, args.units, args.reason, args.execution));
|
|
1772
1797
|
}
|
|
1773
1798
|
catch (error) {
|
|
1799
|
+
if (error instanceof Error && error.message === "mission-replan-worker-still-active") {
|
|
1800
|
+
return JSON.stringify({ status: error.message, mission_id: mission.id,
|
|
1801
|
+
next_action: `Coordinator: do not repeat plan_units or try ${cancel} (root-only). ` +
|
|
1802
|
+
`If the Worker Task is still active, wait for its native completion. If its parent Task was interrupted, ` +
|
|
1803
|
+
`submit_mission with status=blocked and report this code, the Worker/Task IDs and what did not run. ` +
|
|
1804
|
+
`Operator root can then use ${cancel} with reason=plain to stop owned children and resume the request ` +
|
|
1805
|
+
`through ${startMission} with intent=replace, preserving cumulative spend. No duplicate Worker.` });
|
|
1806
|
+
}
|
|
1774
1807
|
if (!(error instanceof OperatorContractError))
|
|
1775
1808
|
throw error;
|
|
1776
1809
|
return JSON.stringify({ status: "invalid-plan", diagnostics: error.diagnostics, diagnostics_truncated: error.diagnostics_truncated,
|
|
@@ -1792,7 +1825,7 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
1792
1825
|
return declareMissionUnits(root, context.sessionID, mission, units, args.reason);
|
|
1793
1826
|
});
|
|
1794
1827
|
} };
|
|
1795
|
-
tools[reviewMission] = { description: "Coordinator: prepare the independent Reviewer task from current source, requirements and observed checks. Supply risk_tags (empty only for genuinely low risk) and concise criterion-level changed-code/test traces. Host supplies diff, IDs, manifest, mappings and evidence;
|
|
1828
|
+
tools[reviewMission] = { description: "Coordinator: prepare the independent Reviewer task from current source, requirements and observed checks. Supply risk_tags (empty only for genuinely low risk) and concise criterion-level changed-code/test traces. Host supplies diff, IDs, manifest, mappings and evidence; if truncated_evidence is returned, narrow those ranges before dispatching the Reviewer. Keep candidate lineage across corrections. A low-risk skip is recorded, never inferred from a missing review.",
|
|
1796
1829
|
args: { risk_tags: { type: "array", items: { type: "string", enum: SOURCE_REVIEW_RISK_TAGS } },
|
|
1797
1830
|
evidence: { type: "array", maxItems: 6, items: { type: "object", additionalProperties: false,
|
|
1798
1831
|
properties: { path: { type: "string" }, offset: { type: "integer", minimum: 1 }, limit: { type: "integer", minimum: 1, maximum: 200 } },
|
|
@@ -1848,7 +1881,10 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
1848
1881
|
...(initialPrompt ? { initialPrompt } : {}),
|
|
1849
1882
|
...(mission.review?.evidenceGapReviews ? { evidenceGapReviews: mission.review.evidenceGapReviews } : {}) };
|
|
1850
1883
|
});
|
|
1851
|
-
return JSON.stringify(task ? { status: "review-required", task: missionReviewTask(reviewed)
|
|
1884
|
+
return JSON.stringify(task ? { status: "review-required", task: missionReviewTask(reviewed),
|
|
1885
|
+
...(source.truncatedEvidence.length ? { truncated_evidence: source.truncatedEvidence,
|
|
1886
|
+
next_action: "Narrow these focused evidence ranges with review_mission before dispatching the Reviewer; no Worker or new validation is needed." } : {}) }
|
|
1887
|
+
: { status: "skipped-low-risk" });
|
|
1852
1888
|
} };
|
|
1853
1889
|
async function assertMissionReview(root, mission) {
|
|
1854
1890
|
const run = await operators.required(root), review = mission.review;
|
|
@@ -105,6 +105,13 @@ export interface RuntimeBridge {
|
|
|
105
105
|
readonly reserved_units: number;
|
|
106
106
|
readonly remaining_units: number;
|
|
107
107
|
} | null>;
|
|
108
|
+
extendMissionUnitBudget(rootSessionID: string, maxUnits: number): Promise<{
|
|
109
|
+
readonly status: "extended" | "unchanged";
|
|
110
|
+
readonly max_units: number;
|
|
111
|
+
readonly consumed_units: number;
|
|
112
|
+
readonly reserved_units: number;
|
|
113
|
+
readonly remaining_units: number;
|
|
114
|
+
}>;
|
|
108
115
|
renderReturnReport(rootSessionID: string, text: string, receiptFingerprint: string): Promise<string | undefined>;
|
|
109
116
|
recoverUnitEvidence(rootSessionID: string, request: {
|
|
110
117
|
unitID: string;
|
|
@@ -42,7 +42,7 @@ permission:
|
|
|
42
42
|
${profileAgent(profile, "dog-advisor")}: allow
|
|
43
43
|
tools:
|
|
44
44
|
"sortie_*": false
|
|
45
|
-
${controls(profile, ["start_mission", "plan_units", "operator_next", "operator_status", "expand_unit", "review_mission", "complete_mission", "cancel_operator", "reflection"])}
|
|
45
|
+
${controls(profile, ["start_mission", "plan_units", "operator_next", "operator_status", "extend_mission_budget", "expand_unit", "review_mission", "complete_mission", "cancel_operator", "reflection"])}
|
|
46
46
|
---
|
|
47
47
|
# ${profileAgent(profile, "dog-coordinator")}
|
|
48
48
|
|
|
@@ -70,6 +70,12 @@ quality threshold and explicit model/budget choice. Follow AGENTS.md and use the
|
|
|
70
70
|
SAME mission before any Worker starts, retains its Coordinator and cumulative spend, and checks
|
|
71
71
|
old children before preparing a Worker. This is not permission to discard unchanged acceptance:
|
|
72
72
|
if the scope is uncertain, ask the user which requirements remain instead of inferring a replacement.
|
|
73
|
+
If a Coordinator reports mission-replan-worker-still-active after an interrupted native Task,
|
|
74
|
+
it cannot call cancel_operator (root-only). Inspect operator_status and the native Task outcome.
|
|
75
|
+
If the Worker is still active, wait, not duplicate. If the user chose to stop/replan, root calls
|
|
76
|
+
${profile.toolPrefix}cancel_operator with reason: "plain" to stop its owned children, then
|
|
77
|
+
${profile.toolPrefix}start_mission with intent: "replace" and the saved requirements. The cancelled
|
|
78
|
+
Mission is archived; cumulative spend is retained. Dispatch only the returned Coordinator Task.
|
|
73
79
|
2. Dispatch the returned ${profileAgent(profile, "dog-operator")} task immediately. It owns investigation,
|
|
74
80
|
unit boundaries, Worker/Scout/Advisor/independent Reviewer calls, write-scope extensions and corrections
|
|
75
81
|
within the original request and cumulative budget. Do not investigate or approve each unit at the root.
|
|
@@ -107,7 +113,10 @@ Use foreground delegation. Unit progress is displayed on the running Task withou
|
|
|
107
113
|
Do not poll or re-run successful checks. Inspect operator_status only to recover missing durable state.
|
|
108
114
|
Ordinary defects return to Coordinator, not the user. Ask through question only for a user-only choice,
|
|
109
115
|
an extension beyond the original requirements, or a cumulative budget increase. Resume the same work after
|
|
110
|
-
the answer.
|
|
116
|
+
the answer. For an approved Mission Worker-unit increase, root calls ${profile.toolPrefix}extend_mission_budget
|
|
117
|
+
with operator_status.mission_id and the new cumulative max_units (not the increment), then resumes the same
|
|
118
|
+
Coordinator. This does not change a separate campaign dollar cap or dispatch a Worker. Do not reset spend,
|
|
119
|
+
silently shrink acceptance, or create substitute goals.
|
|
111
120
|
|
|
112
121
|
When bounded process reflections are injected, pass the relevant prevention in Coordinator feedback while
|
|
113
122
|
preserving the original requirements. After a resolved repeated process failure, record its verified cause/prevention
|