sortie-dogs 0.12.24 → 0.13.0
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/core/initialize.js +3 -3
- package/dist/core/operator-mission.d.ts +2 -2
- package/dist/core/operator-mission.js +28 -11
- package/dist/core/operator-runtime.js +7 -2
- package/dist/plugin/index.js +8 -4
- package/dist/plugin/mission-review.d.ts +12 -0
- package/dist/plugin/mission-review.js +67 -14
- package/dist/plugin/profiled.js +183 -34
- package/dist/plugin/protected-snapshot.js +19 -3
- package/dist/plugin/run-metrics.d.ts +3 -1
- package/dist/plugin/run-metrics.js +34 -3
- package/dist/plugin/runtime-bridge.d.ts +6 -1
- package/dist/plugin/validation-scratch.d.ts +5 -0
- package/dist/plugin/validation-scratch.js +51 -0
- package/dist/runtime-assets-v010.js +3 -3
- package/dist/runtime-mission-assets.d.ts +1 -1
- package/dist/runtime-mission-assets.js +84 -19
- 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.
|
|
6
|
+
export declare const V010_RUNTIME_ASSET_VERSION = "0.13.0-fast-first-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.
|
|
6
|
+
export const V010_RUNTIME_ASSET_VERSION = "0.13.0-fast-first-v1";
|
package/dist/core/initialize.js
CHANGED
|
@@ -133,10 +133,10 @@ function classifyVersionTransition(installedValue, currentValue, v010Restore = f
|
|
|
133
133
|
// cross-minor migration; skipped lines still fail closed instead of bypassing migration steps.
|
|
134
134
|
const adjacentPreOneLine = installed.major === 0 && current.major === 0 &&
|
|
135
135
|
current.minor === installed.minor + 1;
|
|
136
|
-
// v0.11 used a different runtime.
|
|
137
|
-
// the 0.
|
|
136
|
+
// v0.11 used a different runtime. The v010 asset migration restores the 0.10
|
|
137
|
+
// preview into the 0.12/0.13 Mission line without selecting a v0.11 execution path.
|
|
138
138
|
const restoredPreview = v010Restore && installed.major === 0 && installed.minor === 10 &&
|
|
139
|
-
current.major === 0 && current.minor === 12;
|
|
139
|
+
current.major === 0 && (current.minor === 12 || current.minor === 13);
|
|
140
140
|
return sameLine || adjacentPreOneLine || restoredPreview ? "compatible-update" : "incompatible";
|
|
141
141
|
}
|
|
142
142
|
async function metadata(path) {
|
|
@@ -174,7 +174,7 @@ export declare class OperatorMissionRuntime {
|
|
|
174
174
|
constructor(projectRoot: string, profile: RuntimeProfile);
|
|
175
175
|
private file;
|
|
176
176
|
private load;
|
|
177
|
-
/** Recover
|
|
177
|
+
/** Recover non-replacement continuations only; an explicit replacement must link the current cancelled run. */
|
|
178
178
|
private loadMission;
|
|
179
179
|
/** Find exactly one archived cancelled mission that owned this run; ambiguity never grants recovery. */
|
|
180
180
|
archivedRun(root: string, runID: string): Promise<OperatorMission | undefined>;
|
|
@@ -217,5 +217,5 @@ export declare function missionPlan(mission: OperatorMission, raw: unknown): Ope
|
|
|
217
217
|
export declare function missionPacket(mission: OperatorMission, run?: OperatorState): Record<string, unknown>;
|
|
218
218
|
/** Models forward a short capability, never recopy the host's evidence hashes and source packet. */
|
|
219
219
|
export declare function missionReviewTask(mission: OperatorMission): OperatorTask;
|
|
220
|
-
/** Project
|
|
220
|
+
/** Project grouped R-ID traces, including consecutive ranges, into the existing per-criterion mapping. */
|
|
221
221
|
export declare function missionReviewTraces(mission: OperatorMission, raw: unknown): string[];
|
|
@@ -113,10 +113,10 @@ export class OperatorMissionRuntime {
|
|
|
113
113
|
throw error;
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
|
-
/** Recover
|
|
116
|
+
/** Recover non-replacement continuations only; an explicit replacement must link the current cancelled run. */
|
|
117
117
|
async loadMission(root) {
|
|
118
118
|
const state = await this.load(this.file(root));
|
|
119
|
-
if (!state || state.supersededRunID !== undefined || state.runID !== null ||
|
|
119
|
+
if (!state || state.supersededRunID !== undefined || state.runID !== null || state.requirementsReplaced ||
|
|
120
120
|
!["open", "running", "submitted", "cancelled"].includes(state.phase) || state.requests.length === 0)
|
|
121
121
|
return state;
|
|
122
122
|
const directory = join(this.projectRoot, this.profile.stateDirectory, "missions");
|
|
@@ -241,10 +241,11 @@ export class OperatorMissionRuntime {
|
|
|
241
241
|
}
|
|
242
242
|
if (previous)
|
|
243
243
|
await this.save(this.file(root, `.${previous.id}`), previous);
|
|
244
|
-
//
|
|
245
|
-
// replacement
|
|
246
|
-
|
|
247
|
-
|
|
244
|
+
// The Operator runtime's current cancelled run is authoritative for an explicit
|
|
245
|
+
// replacement. A no-run mission can still carry an older ancestor's run ID.
|
|
246
|
+
// Ordinary continuation retains that predecessor when no current run is supplied.
|
|
247
|
+
const predecessor = (replaceRequirements ? options.cancelledRunID : undefined) ??
|
|
248
|
+
previous?.runID ?? previous?.supersededRunID;
|
|
248
249
|
const state = { version: "0.12", id: `mission-${randomUUID()}`, root, requests: [request],
|
|
249
250
|
kind: options.kind ?? "implementation",
|
|
250
251
|
context: (options.context ?? []).filter(item => item.id !== request.id),
|
|
@@ -252,7 +253,7 @@ export class OperatorMissionRuntime {
|
|
|
252
253
|
...(replaceRequirements ? { requirementsReplaced: true } : {}),
|
|
253
254
|
coordinator: null, callID: null, dispatchOpen: false, runID: null, plans: 0, progress: [], submission: null,
|
|
254
255
|
...((previous === undefined || previous.phase === "cancelled") &&
|
|
255
|
-
(previous?.runID === null || previous === undefined || request.id !== previous.requests[0]?.id) && predecessor
|
|
256
|
+
(replaceRequirements || previous?.runID === null || previous === undefined || request.id !== previous.requests[0]?.id) && predecessor
|
|
256
257
|
? { supersededRunID: predecessor } : {}) };
|
|
257
258
|
await this.save(this.file(root), state);
|
|
258
259
|
return state;
|
|
@@ -505,19 +506,35 @@ export function missionReviewTask(mission) {
|
|
|
505
506
|
return { ...review.task, prompt: `${MISSION_REVIEW_REFERENCE}${JSON.stringify({ r: mission.root,
|
|
506
507
|
m: mission.id, n: review.runID, h: digest(review.task.prompt) })}` };
|
|
507
508
|
}
|
|
508
|
-
/** Project
|
|
509
|
+
/** Project grouped R-ID traces, including consecutive ranges, into the existing per-criterion mapping. */
|
|
509
510
|
export function missionReviewTraces(mission, raw) {
|
|
510
511
|
if (!Array.isArray(raw) || raw.length === 0 || !raw.every(item => typeof item === "string" && item.trim())) {
|
|
511
512
|
throw new Error("mission-review-traces: supply concise implementation/test traces");
|
|
512
513
|
}
|
|
513
514
|
const grouped = raw.map(text => ({ text,
|
|
514
|
-
ids: [...text.matchAll(/(?:^|[.;]\s+|[\r\n])\s*((?:R\d+\s*[
|
|
515
|
-
.flatMap(match => match[1].
|
|
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
|
+
})) }));
|
|
516
530
|
if (grouped.every(item => item.ids.length === 0) && raw.length === mission.requirements.length)
|
|
517
531
|
return raw;
|
|
518
532
|
const unknown = grouped.flatMap(item => item.ids).filter(id => !mission.requirements.some(requirement => requirement.id === id));
|
|
519
533
|
const missing = mission.requirements.filter(requirement => !grouped.some(item => item.ids.includes(requirement.id)));
|
|
520
534
|
if (unknown.length || missing.length)
|
|
521
|
-
throw new Error(`mission-review-traces: name the existing R IDs;
|
|
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("; ")}`);
|
|
522
539
|
return mission.requirements.map(requirement => grouped.filter(item => item.ids.includes(requirement.id)).map(item => item.text).join("\n"));
|
|
523
540
|
}
|
|
@@ -920,11 +920,11 @@ export class OperatorRuntime {
|
|
|
920
920
|
`goal_declaration_path: ${declarationPath}`, "acceptance:", ...plan.acceptance.map(value => ` - ${value}`),
|
|
921
921
|
"validation:", ...unit.validation.map(value => ` - ${value}`),
|
|
922
922
|
...(mission ? ["Read handoff_path and other repository files using their project-relative paths as supplied. The native working directory is project_root; do not prepend or reconstruct its absolute path for read/search/shell. Copy project_root only when binding the write gate. Preserve explicitly declared external paths."] : []),
|
|
923
|
-
mission ? "Read-only investigation commands are unrestricted. Use shell to reproduce and diagnose without asking for command registration. Keep all writes, including generated/transient outputs and cleanup, inside unit.write. Run formal validation exactly as listed, in order and in separate calls, so the host records its real result. If a write scope or formal check must change, return the precise change to your Coordinator; it can extend/redeclare immediately within the original requirements. Diagnostic success is not formal acceptance evidence."
|
|
923
|
+
mission ? "Read-only investigation commands are unrestricted. Use shell to reproduce and diagnose without asking for command registration. Keep all writes, including generated/transient outputs and cleanup, inside unit.write. Run formal validation exactly as listed, in order and in separate calls, so the host records its real result. If a write scope or formal check must change, return the precise change to your parent Operator or Coordinator; it can extend/redeclare immediately within the original requirements. Diagnostic success is not formal acceptance evidence."
|
|
924
924
|
: "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.",
|
|
925
925
|
...(mission ? [] : ["Preserve existing public API success and error return semantics unless acceptance explicitly changes them, and cover those compatibility boundaries in the declared validation."]),
|
|
926
926
|
mission
|
|
927
|
-
? "Complete the assigned work and listed validation within this Task. For a known operation, proceed through setup, execution and result collection; preparation alone is not execution. Preserve existing public behavior when changing source. Do not spawn nested subagents. The Coordinator handles any applicable independent review after your return; review is not a prerequisite to execution. Return actual results, including failed or not-started operations, and any exact contract correction needed."
|
|
927
|
+
? "Complete the assigned work and listed validation within this Task. For a known operation, proceed through setup, execution and result collection; preparation alone is not execution. Preserve existing public behavior when changing source. Do not spawn nested subagents. The parent Operator or Coordinator handles any applicable independent review after your return; review is not a prerequisite to execution. Return actual results, including failed or not-started operations, and any exact contract correction needed."
|
|
928
928
|
: "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.",
|
|
929
929
|
...(plan.git_lifecycle !== undefined && index === plan.units.length - 1 ? [
|
|
930
930
|
`git_post_commit_validation: ${JSON.stringify(plan.git_lifecycle.post_commit_validation)}`,
|
|
@@ -1564,6 +1564,11 @@ export class OperatorRuntime {
|
|
|
1564
1564
|
state.units.some(candidate => candidate.terminalRescue))
|
|
1565
1565
|
throw new Error("operator-mission-normal-remediation-unavailable");
|
|
1566
1566
|
await this.verifyControls(unit);
|
|
1567
|
+
// The ordinary retry keeps its exact scope and validation, but the next Worker needs the
|
|
1568
|
+
// host-observed failed command/exit rather than reconstructing it from an earlier session.
|
|
1569
|
+
unit.task = { ...unit.task, prompt: `${unit.task.prompt}\nnormal_remediation_prior_validation: ${JSON.stringify({
|
|
1570
|
+
command: unit.failure.command, exit_code: unit.failure.exitCode
|
|
1571
|
+
})}` };
|
|
1567
1572
|
unit.normalRemediationUsed = true;
|
|
1568
1573
|
unit.status = "pending";
|
|
1569
1574
|
unit.callID = null;
|
package/dist/plugin/index.js
CHANGED
|
@@ -6060,8 +6060,12 @@ export const SortieDogsPlugin = async (input, options) => {
|
|
|
6060
6060
|
appLogInfo("run-metrics.career-unavailable", textInput.sessionID, { outcome: runOutcome }, "warn");
|
|
6061
6061
|
}
|
|
6062
6062
|
}
|
|
6063
|
-
if (sortieResult !== undefined)
|
|
6064
|
-
|
|
6063
|
+
if (sortieResult !== undefined) {
|
|
6064
|
+
const review = terminal?.receipt?.status === "succeeded"
|
|
6065
|
+
? await input.runtimeBridge?.missionReviewPresentation?.(textInput.sessionID).catch(() => undefined)
|
|
6066
|
+
: undefined;
|
|
6067
|
+
textOutput.text = insertSortieResult(textOutput.text, sortieResult, review?.verdict, review?.evidenceGaps);
|
|
6068
|
+
}
|
|
6065
6069
|
else if (metrics !== undefined && runOutcome === "DONE")
|
|
6066
6070
|
textOutput.text = insertRunMetrics(textOutput.text, metrics);
|
|
6067
6071
|
const { debrief: _debriefObservation, ...metricSummary } = metrics ?? {};
|
|
@@ -7445,7 +7449,7 @@ export const SortieDogsPlugin = async (input, options) => {
|
|
|
7445
7449
|
},
|
|
7446
7450
|
};
|
|
7447
7451
|
input.runtimeBridge?.connected?.({
|
|
7448
|
-
renderReturnReport: async (root, text, expectedReceipt) => {
|
|
7452
|
+
renderReturnReport: async (root, text, expectedReceipt, missionReview, reviewEvidenceGaps) => {
|
|
7449
7453
|
let rendered;
|
|
7450
7454
|
await serializeChatTransition(root, async () => {
|
|
7451
7455
|
if (!isCoordinatorSession(root) && !await recoverCoordinatorRoot(root))
|
|
@@ -7473,7 +7477,7 @@ export const SortieDogsPlugin = async (input, options) => {
|
|
|
7473
7477
|
catch {
|
|
7474
7478
|
appLogInfo("run-metrics.career-unavailable", root, { profile: runtimeProfile.id }, "warn");
|
|
7475
7479
|
}
|
|
7476
|
-
rendered = insertSortieResult(receiptBoundTerminalText(text, receipt), result);
|
|
7480
|
+
rendered = insertSortieResult(receiptBoundTerminalText(text, receipt), result, missionReview, reviewEvidenceGaps);
|
|
7477
7481
|
});
|
|
7478
7482
|
return rendered;
|
|
7479
7483
|
},
|
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import type { OperatorState } from "../core/operator-runtime.js";
|
|
2
2
|
import { type MissionEvidenceExcerpt, type MissionReviewScope, type OperatorMission } from "../core/operator-mission.js";
|
|
3
3
|
import { type RuntimeProfile } from "../core/runtime-profile.js";
|
|
4
|
+
/** Show native command outcomes to the Reviewer without turning non-criterion checks into acceptance evidence. */
|
|
5
|
+
export declare function observedMissionValidation(validation: readonly string[], childSessionID: string | null, history: readonly Record<string, unknown>[]): {
|
|
6
|
+
attempts: readonly {
|
|
7
|
+
command: string;
|
|
8
|
+
exit_code: number | null;
|
|
9
|
+
started_ms: number | null;
|
|
10
|
+
completed_ms: number | null;
|
|
11
|
+
}[];
|
|
12
|
+
not_observed: readonly string[];
|
|
13
|
+
omitted_attempts: number;
|
|
14
|
+
};
|
|
4
15
|
export declare function missionReviewBaseline(directory: string): Promise<string | undefined>;
|
|
5
16
|
/** A child ID alone cannot establish the initial phase after a restart or failed dispatch. */
|
|
6
17
|
export declare function initialMissionReviewPrompt(mission: OperatorMission, prompts: readonly string[]): string | undefined;
|
|
@@ -14,4 +25,5 @@ export declare function missionReviewSource(directory: string, run: OperatorStat
|
|
|
14
25
|
fingerprint: string;
|
|
15
26
|
excerpt: string;
|
|
16
27
|
truncatedEvidence: string[];
|
|
28
|
+
truncatedSource: string[];
|
|
17
29
|
}>;
|
|
@@ -11,8 +11,36 @@ import { canonicalAgent } from "../core/runtime-profile.js";
|
|
|
11
11
|
import { taskChildSessionID } from "./task-result-repair.js";
|
|
12
12
|
import { normalizeManifestScope } from "../core/path.js";
|
|
13
13
|
import { declaredArtifacts } from "./declared-artifacts.js";
|
|
14
|
+
import { normalizeCommand } from "./gate.js";
|
|
14
15
|
const exec = promisify(execFile);
|
|
15
16
|
const record = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
17
|
+
/** Show native command outcomes to the Reviewer without turning non-criterion checks into acceptance evidence. */
|
|
18
|
+
export function observedMissionValidation(validation, childSessionID, history) {
|
|
19
|
+
const declared = validation.map(normalizeCommand), expected = new Set(declared);
|
|
20
|
+
const time = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
|
|
21
|
+
const attempts = childSessionID === null ? [] : history.flatMap(message => {
|
|
22
|
+
if (!record(message.info) || message.info.role !== "assistant" || message.info.sessionID !== childSessionID ||
|
|
23
|
+
!Array.isArray(message.parts))
|
|
24
|
+
return [];
|
|
25
|
+
return message.parts.flatMap(part => {
|
|
26
|
+
if (!record(part) || part.type !== "tool" || !["bash", "shell", "powershell", "pwsh"].includes(String(part.tool)) ||
|
|
27
|
+
!record(part.state) || part.state.status !== "completed" || !record(part.state.input) ||
|
|
28
|
+
typeof part.state.input.command !== "string")
|
|
29
|
+
return [];
|
|
30
|
+
const command = normalizeCommand(part.state.input.command);
|
|
31
|
+
if (!expected.has(command))
|
|
32
|
+
return [];
|
|
33
|
+
const exit = record(part.state.metadata) ? part.state.metadata.exit : undefined;
|
|
34
|
+
return [{ command, exit_code: typeof exit === "number" && Number.isSafeInteger(exit) ? exit : null,
|
|
35
|
+
started_ms: record(part.time) ? time(part.time.ran) : null,
|
|
36
|
+
completed_ms: record(part.time) ? time(part.time.completed) : null }];
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
// Retain early attempts and the latest checks if a Worker retried many times.
|
|
40
|
+
const shown = attempts.length > 32 ? [...attempts.slice(0, 8), ...attempts.slice(-24)] : attempts;
|
|
41
|
+
return { attempts: shown, not_observed: declared.filter(command => !attempts.some(item => item.command === command)),
|
|
42
|
+
omitted_attempts: attempts.length - shown.length };
|
|
43
|
+
}
|
|
16
44
|
/** Use the space left by short references for longer requested branches, without starving later references. */
|
|
17
45
|
function focusedAllowances(sizes, budget) {
|
|
18
46
|
const allowances = sizes.map(() => 0);
|
|
@@ -65,19 +93,22 @@ export function initialMissionReviewPrompt(mission, prompts) {
|
|
|
65
93
|
}
|
|
66
94
|
/** Recover from native completion records, never from the pending review's inherited child ID. */
|
|
67
95
|
export async function completedMissionReviewPrompts(mission, profile, root, requestedPrompt, host) {
|
|
68
|
-
if (!mission || mission.root !== root ||
|
|
96
|
+
if (!mission || mission.root !== root || ["completed", "cancelled"].includes(mission.phase) ||
|
|
69
97
|
mission.review?.task?.prompt !== requestedPrompt)
|
|
70
98
|
return [];
|
|
71
99
|
// The completion hook has already bound this prompt to a real independent Reviewer child.
|
|
72
100
|
// Avoid a second V2 history read when its page/list API is temporarily unavailable.
|
|
73
101
|
if (initialMissionReviewPrompt(mission, []))
|
|
74
102
|
return [mission.review.initialPrompt];
|
|
75
|
-
const
|
|
76
|
-
if (
|
|
77
|
-
|
|
103
|
+
const owner = mission.coordinator ?? root;
|
|
104
|
+
if (mission.coordinator !== null) {
|
|
105
|
+
const coordinator = await host.get(owner);
|
|
106
|
+
if (!record(coordinator) || coordinator.parentID !== root || canonicalAgent(profile, coordinator.agent) !== "dog-operator")
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
78
109
|
const prompts = [];
|
|
79
|
-
for (const message of (await host.messages(
|
|
80
|
-
if (!record(message.info) || message.info.role !== "assistant" || message.info.sessionID !==
|
|
110
|
+
for (const message of (await host.messages(owner)).slice(-1000)) {
|
|
111
|
+
if (!record(message.info) || message.info.role !== "assistant" || message.info.sessionID !== owner ||
|
|
81
112
|
!Array.isArray(message.parts))
|
|
82
113
|
continue;
|
|
83
114
|
for (const part of message.parts) {
|
|
@@ -89,7 +120,7 @@ export async function completedMissionReviewPrompts(mission, profile, root, requ
|
|
|
89
120
|
if (!childID)
|
|
90
121
|
continue;
|
|
91
122
|
const child = await host.get(childID);
|
|
92
|
-
if (!record(child) || child.parentID !==
|
|
123
|
+
if (!record(child) || child.parentID !== owner || canonicalAgent(profile, child.agent) !== "dog-reviewer")
|
|
93
124
|
continue;
|
|
94
125
|
let prompt = part.state.input.prompt;
|
|
95
126
|
if (prompt.startsWith(MISSION_REVIEW_REFERENCE)) {
|
|
@@ -218,7 +249,7 @@ export async function missionReviewSource(directory, run, evidence = [], baselin
|
|
|
218
249
|
if (writes.length === 0)
|
|
219
250
|
return { fingerprint: `sha256:${hash.digest("hex")}`,
|
|
220
251
|
excerpt: selected + "[Read-only units: no declared output files. Review the supplied observations, traces and validation evidence.]",
|
|
221
|
-
truncatedEvidence };
|
|
252
|
+
truncatedEvidence, truncatedSource: [] };
|
|
222
253
|
// The shared dependency environment is local tooling, never reviewed or pinned source.
|
|
223
254
|
const external = [], local = [];
|
|
224
255
|
for (const path of writes) {
|
|
@@ -280,7 +311,8 @@ export async function missionReviewSource(directory, run, evidence = [], baselin
|
|
|
280
311
|
const absolute = resolve(directory, path), stat = await lstat(absolute);
|
|
281
312
|
hash.update(String(stat.mode));
|
|
282
313
|
const include = untracked.has(path) || unchanged;
|
|
283
|
-
const
|
|
314
|
+
const heading = `\n--- ${untracked.has(path) ? "new file" : "current file"}: ${path} ---\n`;
|
|
315
|
+
const room = include ? Math.max(0, 24_000 - Buffer.byteLength(excerpt) - Buffer.byteLength(heading)) : 0;
|
|
284
316
|
let preview = Buffer.alloc(0);
|
|
285
317
|
if (stat.isSymbolicLink()) {
|
|
286
318
|
const content = Buffer.from(await readlink(absolute));
|
|
@@ -297,9 +329,27 @@ export async function missionReviewSource(directory, run, evidence = [], baselin
|
|
|
297
329
|
}
|
|
298
330
|
}
|
|
299
331
|
if (include) {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
332
|
+
const headingFits = Buffer.byteLength(excerpt) + Buffer.byteLength(heading) <= 24_000;
|
|
333
|
+
if (headingFits) {
|
|
334
|
+
const rendered = preview.includes(0) ? "[binary artifact: bytes fingerprinted]" : preview.toString("utf8");
|
|
335
|
+
let shown = rendered;
|
|
336
|
+
if (Buffer.byteLength(rendered) > room) {
|
|
337
|
+
let used = 0;
|
|
338
|
+
const chars = [];
|
|
339
|
+
for (const char of rendered) {
|
|
340
|
+
const bytes = Buffer.byteLength(char);
|
|
341
|
+
if (used + bytes > room)
|
|
342
|
+
break;
|
|
343
|
+
chars.push(char);
|
|
344
|
+
used += bytes;
|
|
345
|
+
}
|
|
346
|
+
shown = chars.join("");
|
|
347
|
+
}
|
|
348
|
+
excerpt += heading + shown;
|
|
349
|
+
if (stat.size > room || Buffer.byteLength(rendered) > room)
|
|
350
|
+
omitted.push(path);
|
|
351
|
+
}
|
|
352
|
+
else
|
|
303
353
|
omitted.push(path);
|
|
304
354
|
}
|
|
305
355
|
}
|
|
@@ -321,8 +371,11 @@ export async function missionReviewSource(directory, run, evidence = [], baselin
|
|
|
321
371
|
unreadable.push(...artifacts.unreadable);
|
|
322
372
|
}
|
|
323
373
|
const bytes = Buffer.from(excerpt);
|
|
374
|
+
const truncatedSource = [...new Set(omitted)];
|
|
375
|
+
if (bytes.length > 24_000 && truncatedSource.length === 0)
|
|
376
|
+
truncatedSource.push("(source diff exceeds excerpt budget)");
|
|
324
377
|
return { fingerprint: `sha256:${hash.digest("hex")}`, excerpt: (bytes.length > 24_000 ? bytes.subarray(0, 24_000).toString("utf8") : excerpt) +
|
|
325
|
-
(
|
|
378
|
+
(truncatedSource.length ? `\n[EXCERPT TRUNCATED: ${truncatedSource.slice(0, 20).join(", ")}; supply focused traces for missing sections, not another implementation unit]` : "") +
|
|
326
379
|
(unreadable.length ? `\n[UNINSPECTED EXTERNAL DIRECTORIES: ${unreadable.slice(0, 20).join(", ")}; select specific result files as review evidence]` : ""),
|
|
327
|
-
truncatedEvidence };
|
|
380
|
+
truncatedEvidence, truncatedSource };
|
|
328
381
|
}
|