sortie-dogs 0.13.12 → 0.13.13
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 +7 -7
- package/dist/asset-version.d.ts +1 -1
- package/dist/asset-version.js +1 -1
- package/dist/core/mission-context.d.ts +73 -0
- package/dist/core/mission-context.js +66 -0
- package/dist/core/operator-mission.d.ts +7 -0
- package/dist/core/operator-mission.js +44 -0
- package/dist/plugin/profiled.js +29 -8
- package/dist/runtime-mission-assets.js +8 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,8 +19,8 @@ evidence-backed result, not more agents for their own sake.
|
|
|
19
19
|
[](https://www.npmjs.com/package/sortie-dogs)
|
|
20
20
|
[](LICENSE)
|
|
21
21
|
|
|
22
|
-
**Current release: [v0.13.
|
|
23
|
-
· [Release notes](docs/release-v0.13.
|
|
22
|
+
**Current release: [v0.13.13](https://github.com/zufall-upon/Sortie-dogs/releases/tag/v0.13.13)**
|
|
23
|
+
· [Release notes](docs/release-v0.13.13.md)
|
|
24
24
|
|
|
25
25
|
[Quick start](#quick-start) · [Workflow](#mission-workflow) · [Models](#default-models) ·
|
|
26
26
|
[Measured results](#measured-results) · [Configuration](#configuration) · [Documentation](#documentation)
|
|
@@ -57,7 +57,7 @@ Use an existing Codex CLI signed in with **ChatGPT**. On Windows, PowerShell **7
|
|
|
57
57
|
available; POSIX uses bash. Sortie does not install Codex, start login or use a metered API fallback.
|
|
58
58
|
|
|
59
59
|
```sh
|
|
60
|
-
npm install --save-dev sortie-dogs@0.13.
|
|
60
|
+
npm install --save-dev sortie-dogs@0.13.13
|
|
61
61
|
npx --no-install sortie-dogs codex init .
|
|
62
62
|
```
|
|
63
63
|
|
|
@@ -86,7 +86,7 @@ bridge. [Codex guide: SDK, permissions, progress, resume and manifest route](doc
|
|
|
86
86
|
Use **OpenCode V2**. In the target project:
|
|
87
87
|
|
|
88
88
|
```sh
|
|
89
|
-
npm install --save-dev sortie-dogs@0.13.
|
|
89
|
+
npm install --save-dev sortie-dogs@0.13.13
|
|
90
90
|
npx --no-install sortie-dogs init .
|
|
91
91
|
```
|
|
92
92
|
|
|
@@ -188,7 +188,7 @@ known expense. Official local evaluation and leaderboard acceptance are separate
|
|
|
188
188
|
**8/23 (34.8%)**, versus v0.12.25's 7/23; all seven prior resolutions retained, no empty patches or
|
|
189
189
|
official evaluation errors. [Fixed conditions, stopped-run accounting and costs](docs/benchmarks/swebench-lite-history.md#v0131-dev23-2026-09-30).
|
|
190
190
|
|
|
191
|
-
All scores belong to their fixed candidates, **not v0.13.
|
|
191
|
+
All scores belong to their fixed candidates, **not v0.13.13**. SWE-bench remains an optional
|
|
192
192
|
measurement, not a release gate. Different budgets, models and conditions are not controlled comparisons.
|
|
193
193
|
|
|
194
194
|
## Configuration
|
|
@@ -211,7 +211,7 @@ Install the desired package version and rerun **your host's** initializer:
|
|
|
211
211
|
|
|
212
212
|
An OpenCode config-local bridge may resolve a separate package; npm-global update alone does not
|
|
213
213
|
update it. [Global/update instructions](docs/configuration.md#global-availability).
|
|
214
|
-
The current Mission asset marker is `0.13.
|
|
214
|
+
The current Mission asset marker is `0.13.13-mission-context-v1`; the unchanged Codex skill marker is
|
|
215
215
|
`0.13.11-codex-skill-v2`. Installed markers alone do not prove a running host reloaded the new version.
|
|
216
216
|
|
|
217
217
|
There is no supported uninstall command. Remove the dependency and only known Sortie-owned paths;
|
|
@@ -227,7 +227,7 @@ follow the [manual removal guide](docs/uninstall.md), never delete the entire `.
|
|
|
227
227
|
[SWE-bench history](docs/benchmarks/swebench-lite-history.md) · [Historical local case study](docs/benchmark-reference.md).
|
|
228
228
|
- **Development:** [Testing](docs/testing.md) · [CLI testing](docs/cli-testing.md) ·
|
|
229
229
|
[Windows tests](docs/windows-tests.md) · [Release routine](docs/release-batch.md).
|
|
230
|
-
- **Releases:** [v0.13.12](docs/release-v0.13.12.md) · [v0.13.11](docs/release-v0.13.11.md) · [v0.13.10](docs/release-v0.13.10.md) ·
|
|
230
|
+
- **Releases:** [v0.13.13](docs/release-v0.13.13.md) · [v0.13.12](docs/release-v0.13.12.md) · [v0.13.11](docs/release-v0.13.11.md) · [v0.13.10](docs/release-v0.13.10.md) ·
|
|
231
231
|
[v0.13.9](docs/release-v0.13.9.md) · [All GitHub releases](https://github.com/zufall-upon/Sortie-dogs/releases).
|
|
232
232
|
|
|
233
233
|
## Community
|
package/dist/asset-version.d.ts
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
* installed project marker without importing every asset body.
|
|
4
4
|
*/
|
|
5
5
|
export declare const RUNTIME_ASSET_VERSION = "0.3.89-completion-proof-v1";
|
|
6
|
-
export declare const V010_RUNTIME_ASSET_VERSION = "0.13.
|
|
6
|
+
export declare const V010_RUNTIME_ASSET_VERSION = "0.13.13-mission-context-v1";
|
|
7
7
|
export declare const CODEX_SKILL_ASSET_VERSION = "0.13.11-codex-skill-v2";
|
|
8
8
|
export type RuntimeAssetVersion = typeof RUNTIME_ASSET_VERSION | typeof V010_RUNTIME_ASSET_VERSION | typeof CODEX_SKILL_ASSET_VERSION;
|
package/dist/asset-version.js
CHANGED
|
@@ -3,5 +3,5 @@
|
|
|
3
3
|
* installed project marker without importing every asset body.
|
|
4
4
|
*/
|
|
5
5
|
export const RUNTIME_ASSET_VERSION = "0.3.89-completion-proof-v1";
|
|
6
|
-
export const V010_RUNTIME_ASSET_VERSION = "0.13.
|
|
6
|
+
export const V010_RUNTIME_ASSET_VERSION = "0.13.13-mission-context-v1";
|
|
7
7
|
export const CODEX_SKILL_ASSET_VERSION = "0.13.11-codex-skill-v2";
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
export interface PublicMissionContext {
|
|
2
|
+
id: string;
|
|
3
|
+
role: "user" | "assistant";
|
|
4
|
+
text: string;
|
|
5
|
+
tools: {
|
|
6
|
+
call_id?: string;
|
|
7
|
+
tool: string;
|
|
8
|
+
status?: string;
|
|
9
|
+
input?: unknown;
|
|
10
|
+
output?: string;
|
|
11
|
+
error?: string;
|
|
12
|
+
exit?: number;
|
|
13
|
+
time?: unknown;
|
|
14
|
+
}[];
|
|
15
|
+
}
|
|
16
|
+
export interface MissionContextSource {
|
|
17
|
+
session_id: string;
|
|
18
|
+
path: string;
|
|
19
|
+
sha256: string;
|
|
20
|
+
entries: number;
|
|
21
|
+
observed_at: string;
|
|
22
|
+
history_available: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface MissionContinuity {
|
|
25
|
+
sources: MissionContextSource[];
|
|
26
|
+
notes?: {
|
|
27
|
+
text: string;
|
|
28
|
+
session_id: string;
|
|
29
|
+
recorded_at: string;
|
|
30
|
+
}[];
|
|
31
|
+
}
|
|
32
|
+
/** Public task data only. Keep exact text/results in a lazy snapshot, not private reasoning. */
|
|
33
|
+
export declare function publicMissionContext(messages: readonly Record<string, unknown>[]): PublicMissionContext[];
|
|
34
|
+
/** Preserve earlier available entries when the host later returns a compacted/partial history. */
|
|
35
|
+
export declare function retainMissionContext(directory: string, sessionID: string, messages: readonly Record<string, unknown>[], previous?: MissionContextSource): Promise<{
|
|
36
|
+
source: MissionContextSource;
|
|
37
|
+
entries: PublicMissionContext[];
|
|
38
|
+
}>;
|
|
39
|
+
/** Small inline view, with explicit pointers to every omitted byte in the exact snapshot. */
|
|
40
|
+
export declare function missionContextView(source: MissionContextSource, entries: readonly PublicMissionContext[], suppliedRequestIDs?: readonly string[]): {
|
|
41
|
+
omitted_text_entries: number;
|
|
42
|
+
conversation: {
|
|
43
|
+
truncated?: boolean | undefined;
|
|
44
|
+
original_characters?: number | undefined;
|
|
45
|
+
id: string;
|
|
46
|
+
role: "user" | "assistant";
|
|
47
|
+
text: string;
|
|
48
|
+
}[];
|
|
49
|
+
omitted_tool_entries: number;
|
|
50
|
+
recent_tools: {
|
|
51
|
+
input: string | undefined;
|
|
52
|
+
output: string | undefined;
|
|
53
|
+
error: string | undefined;
|
|
54
|
+
truncated: boolean;
|
|
55
|
+
detail_ref: {
|
|
56
|
+
path: string;
|
|
57
|
+
message_id: string;
|
|
58
|
+
call_id: string | undefined;
|
|
59
|
+
};
|
|
60
|
+
call_id?: string;
|
|
61
|
+
tool: string;
|
|
62
|
+
status?: string;
|
|
63
|
+
exit?: number;
|
|
64
|
+
time?: unknown;
|
|
65
|
+
message_id: string;
|
|
66
|
+
}[];
|
|
67
|
+
session_id: string;
|
|
68
|
+
path: string;
|
|
69
|
+
sha256: string;
|
|
70
|
+
entries: number;
|
|
71
|
+
observed_at: string;
|
|
72
|
+
history_available: boolean;
|
|
73
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
const record = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
5
|
+
/** Public task data only. Keep exact text/results in a lazy snapshot, not private reasoning. */
|
|
6
|
+
export function publicMissionContext(messages) {
|
|
7
|
+
return messages.flatMap(message => {
|
|
8
|
+
const info = record(message.info) ? message.info : message;
|
|
9
|
+
if ((info.role !== "user" && info.role !== "assistant") || typeof info.id !== "string" || !Array.isArray(message.parts))
|
|
10
|
+
return [];
|
|
11
|
+
const parts = message.parts.filter(record);
|
|
12
|
+
const text = parts.filter(part => part.type === "text" && part.synthetic !== true && typeof part.text === "string")
|
|
13
|
+
.map(part => part.text).join("\n");
|
|
14
|
+
const tools = parts.flatMap(part => {
|
|
15
|
+
if (part.type !== "tool" || typeof part.tool !== "string" || !record(part.state))
|
|
16
|
+
return [];
|
|
17
|
+
const state = part.state, metadata = record(state.metadata) ? state.metadata : {};
|
|
18
|
+
return [{ tool: part.tool, ...(typeof part.callID === "string" ? { call_id: part.callID } : {}),
|
|
19
|
+
...(typeof state.status === "string" ? { status: state.status } : {}),
|
|
20
|
+
...(state.input !== undefined ? { input: state.input } : {}),
|
|
21
|
+
...(typeof state.output === "string" ? { output: state.output } : {}),
|
|
22
|
+
...(typeof state.error === "string" ? { error: state.error } : {}),
|
|
23
|
+
...(typeof metadata.exit === "number" ? { exit: metadata.exit } : {}),
|
|
24
|
+
...(part.time !== undefined ? { time: part.time } : {}) }];
|
|
25
|
+
});
|
|
26
|
+
return text.trim() || tools.length ? [{ id: info.id, role: info.role, text, tools }] : [];
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
/** Preserve earlier available entries when the host later returns a compacted/partial history. */
|
|
30
|
+
export async function retainMissionContext(directory, sessionID, messages, previous) {
|
|
31
|
+
const current = publicMissionContext(messages);
|
|
32
|
+
const retained = previous ? await readFile(previous.path, "utf8")
|
|
33
|
+
.then(content => JSON.parse(content).entries).catch(() => undefined) : undefined;
|
|
34
|
+
const merged = new Map((retained ?? []).map(entry => [entry.id, entry]));
|
|
35
|
+
for (const entry of current)
|
|
36
|
+
merged.set(entry.id, entry);
|
|
37
|
+
const entries = [...merged.values()];
|
|
38
|
+
const content = JSON.stringify({ session_id: sessionID, entries }, null, 2) + "\n";
|
|
39
|
+
const sha256 = createHash("sha256").update(content).digest("hex");
|
|
40
|
+
const path = join(directory, `context-${sha256}.json`);
|
|
41
|
+
if (!retained || previous?.sha256 !== sha256) {
|
|
42
|
+
await mkdir(directory, { recursive: true });
|
|
43
|
+
await writeFile(path, content);
|
|
44
|
+
}
|
|
45
|
+
return { source: { session_id: sessionID, path, sha256, entries: entries.length,
|
|
46
|
+
observed_at: new Date().toISOString(), history_available: current.length > 0 }, entries };
|
|
47
|
+
}
|
|
48
|
+
/** Small inline view, with explicit pointers to every omitted byte in the exact snapshot. */
|
|
49
|
+
export function missionContextView(source, entries, suppliedRequestIDs = []) {
|
|
50
|
+
const textEntries = entries.filter(entry => entry.text.trim() && !suppliedRequestIDs.includes(entry.id));
|
|
51
|
+
const selected = textEntries.slice(-8);
|
|
52
|
+
const tools = entries.flatMap(entry => entry.tools.map(tool => ({ message_id: entry.id, ...tool })));
|
|
53
|
+
const recentTools = tools.filter(tool => !tool.tool.startsWith("sortie_") && ["completed", "error"].includes(tool.status ?? "")).slice(-8);
|
|
54
|
+
return { ...source, omitted_text_entries: textEntries.length - selected.length,
|
|
55
|
+
conversation: selected.map(entry => ({ id: entry.id, role: entry.role, text: entry.text.slice(0, 4000),
|
|
56
|
+
...(entry.text.length > 4000 ? { truncated: true, original_characters: entry.text.length } : {}) })),
|
|
57
|
+
omitted_tool_entries: tools.length - recentTools.length,
|
|
58
|
+
recent_tools: recentTools
|
|
59
|
+
.map(tool => {
|
|
60
|
+
const input = tool.input === undefined ? undefined : JSON.stringify(tool.input);
|
|
61
|
+
return { ...tool, input: input?.slice(0, 1000), output: tool.output?.slice(0, 1000), error: tool.error?.slice(0, 1000),
|
|
62
|
+
truncated: [input, tool.output, tool.error].some(value => value !== undefined && value.length > 1000),
|
|
63
|
+
detail_ref: { path: source.path, message_id: tool.message_id, call_id: tool.call_id } };
|
|
64
|
+
}),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type OperatorPlan, type OperatorState, type OperatorTask, type OperatorRuntime } from "./operator-runtime.js";
|
|
2
2
|
import { type RuntimeProfile } from "./runtime-profile.js";
|
|
3
|
+
import { type MissionContinuity } from "./mission-context.js";
|
|
3
4
|
export { missionValidationCommand } from "./mission-validation-command.js";
|
|
4
5
|
export declare const MISSION_REFERENCE = "SORTIE_MISSION_REF ";
|
|
5
6
|
export declare const MISSION_REVIEW_REFERENCE = "SORTIE_MISSION_REVIEW_REF ";
|
|
@@ -192,6 +193,8 @@ export interface OperatorMission {
|
|
|
192
193
|
requests: MissionRequest[];
|
|
193
194
|
/** Prior public conversation context, not additional immutable requirements. */
|
|
194
195
|
context?: MissionContext[];
|
|
196
|
+
/** Shared public context/working notes; observations, never extra requirements or acceptance. */
|
|
197
|
+
continuity?: MissionContinuity;
|
|
195
198
|
/** Explicit continue deliveries, keyed by the original real turn. */
|
|
196
199
|
steering?: {
|
|
197
200
|
requestID: string;
|
|
@@ -321,6 +324,10 @@ export declare class OperatorMissionRuntime {
|
|
|
321
324
|
constructor(projectRoot: string, profile: RuntimeProfile);
|
|
322
325
|
private file;
|
|
323
326
|
correctionReference(root: string, reviewIdentity: string): string;
|
|
327
|
+
/** Stage context for a proposed handoff; publish Mission metadata only after plan preparation succeeds. */
|
|
328
|
+
contextSnapshot(state: OperatorMission, sessionID: string, messages: readonly Record<string, unknown>[], notes?: string): Promise<OperatorMission>;
|
|
329
|
+
retainContext(root: string, sessionID: string, messages: readonly Record<string, unknown>[], notes?: string): Promise<OperatorMission>;
|
|
330
|
+
handoffContext(state: OperatorMission): Promise<Record<string, unknown>>;
|
|
324
331
|
private load;
|
|
325
332
|
/** Recover non-replacement continuations only; an explicit replacement must link the current cancelled run. */
|
|
326
333
|
private loadMission;
|
|
@@ -6,6 +6,7 @@ import { parseOperatorPlan } from "./operator-runtime.js";
|
|
|
6
6
|
import { profileAgent } from "./runtime-profile.js";
|
|
7
7
|
import { normalizeCommand } from "../plugin/gate.js";
|
|
8
8
|
import { missionValidationCommand } from "./mission-validation-command.js";
|
|
9
|
+
import { retainMissionContext, missionContextView } from "./mission-context.js";
|
|
9
10
|
export { missionValidationCommand } from "./mission-validation-command.js";
|
|
10
11
|
const digest = (value) => createHash("sha256").update(value).digest("hex");
|
|
11
12
|
const record = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -184,6 +185,47 @@ export class OperatorMissionRuntime {
|
|
|
184
185
|
correctionReference(root, reviewIdentity) {
|
|
185
186
|
return JSON.stringify({ path: this.file(root), field: "corrections[]", review_identity: reviewIdentity });
|
|
186
187
|
}
|
|
188
|
+
/** Stage context for a proposed handoff; publish Mission metadata only after plan preparation succeeds. */
|
|
189
|
+
async contextSnapshot(state, sessionID, messages, notes) {
|
|
190
|
+
const next = structuredClone(state);
|
|
191
|
+
const previous = next.continuity?.sources.find(source => source.session_id === sessionID);
|
|
192
|
+
const snapshot = await retainMissionContext(join(this.projectRoot, this.profile.stateDirectory, "missions"), sessionID, messages, previous);
|
|
193
|
+
next.continuity ??= { sources: [] };
|
|
194
|
+
next.continuity.sources = [...next.continuity.sources.filter(source => source.session_id !== sessionID), snapshot.source];
|
|
195
|
+
if (typeof notes === "string" && notes.trim())
|
|
196
|
+
next.continuity.notes = [
|
|
197
|
+
...(next.continuity.notes ?? []).filter(note => note.session_id !== sessionID),
|
|
198
|
+
{ text: notes, session_id: sessionID, recorded_at: new Date().toISOString() }
|
|
199
|
+
];
|
|
200
|
+
return next;
|
|
201
|
+
}
|
|
202
|
+
async retainContext(root, sessionID, messages, notes) {
|
|
203
|
+
return this.serial(root, async () => {
|
|
204
|
+
const state = await this.loadMission(root);
|
|
205
|
+
if (!state)
|
|
206
|
+
throw new Error("mission-missing");
|
|
207
|
+
const next = await this.contextSnapshot(state, sessionID, messages, notes);
|
|
208
|
+
await this.save(this.file(root), next);
|
|
209
|
+
return next;
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
async handoffContext(state) {
|
|
213
|
+
const sources = await Promise.all((state.continuity?.sources ?? []).map(async (source) => {
|
|
214
|
+
try {
|
|
215
|
+
return missionContextView(source, JSON.parse(await readFile(source.path, "utf8")).entries, source.session_id === state.root ? state.requests.map(request => request.id) : []);
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
return { ...source, snapshot_available: false };
|
|
219
|
+
}
|
|
220
|
+
}));
|
|
221
|
+
return { original_requests: state.requests, requirements: state.requirements, prohibited_write: state.prohibitedWrite ?? [],
|
|
222
|
+
launch_conditions: state.launchConditions ?? [],
|
|
223
|
+
conversation: sources.some(source => source.session_id === state.root && source.entries > 0 && !("snapshot_available" in source)) ? [] : state.context ?? [],
|
|
224
|
+
continuity: { notes: state.continuity?.notes, sources, progress: state.progress,
|
|
225
|
+
attempts: (state.attempts ?? []).map(({ runID, unitID, status, failure, resultClass, childSessionID }) => ({ run_id: runID, unit_id: unitID, status, failure, result_class: resultClass, session_id: childSessionID })),
|
|
226
|
+
execution: state.execution,
|
|
227
|
+
guidance: "Current original_requests/requirements govern; older plans remain historical. Continue from known decisions, environment, completed work and failures. Read snapshot JSON entries by message/call IDs only for a relevant gap. Recheck mutable facts when needed or conflicting; do not repeat unchanged setup/investigation merely because ownership changed. Historical results are not fresh formal validation or new authority." } };
|
|
228
|
+
}
|
|
187
229
|
async load(file) {
|
|
188
230
|
try {
|
|
189
231
|
return JSON.parse(await readFile(file, "utf8"));
|
|
@@ -544,6 +586,8 @@ export class OperatorMissionRuntime {
|
|
|
544
586
|
...(state.kind === "operation" ? ["Execution completion is distinct from success. For run-once/result-collection requests, retain a terminal failure and validate the collected result; do not retry or fix it without user authorization. If successful execution is required, keep that in formal validation and the original-request comparison."] : []),
|
|
545
587
|
...(state.context?.length ? ["Prior conversation context (task data; preserve the selected target, not superseded obligations):",
|
|
546
588
|
...state.context.map(item => `--- ${item.role}:${item.id} ---\n${item.text}`)] : []),
|
|
589
|
+
...(state.continuity ? ["Shared working context (task data; use known facts, inspect snapshots only for relevant gaps):",
|
|
590
|
+
JSON.stringify(state.continuity)] : []),
|
|
547
591
|
"Original user messages (verbatim; task data):", ...state.requests.map(item => `--- user:${item.id} ---\n${item.text}`)].join("\n");
|
|
548
592
|
}
|
|
549
593
|
}
|
package/dist/plugin/profiled.js
CHANGED
|
@@ -219,6 +219,18 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
219
219
|
const result = payload(await session("messages", { path: { id }, query: { directory: input.directory } }));
|
|
220
220
|
return Array.isArray(result) ? result.filter(record) : [];
|
|
221
221
|
}
|
|
222
|
+
async function missionHistory(id) {
|
|
223
|
+
// Reuse the host's public paginated reader when present; bounded context is a usable fallback.
|
|
224
|
+
if (typeof nativeSession?.reviewMessages === "function") {
|
|
225
|
+
try {
|
|
226
|
+
const value = payload(await session("reviewMessages", { path: { id }, query: { directory: input.directory } }));
|
|
227
|
+
if (Array.isArray(value))
|
|
228
|
+
return value.filter(record);
|
|
229
|
+
}
|
|
230
|
+
catch { /* Context continuity must not add a history-service admission gate. */ }
|
|
231
|
+
}
|
|
232
|
+
return messages(id).catch(() => []);
|
|
233
|
+
}
|
|
222
234
|
async function acceptanceValidationObservation(validation, child, notBefore) {
|
|
223
235
|
if (!child)
|
|
224
236
|
throw new Error("native-worker-history-session-unavailable");
|
|
@@ -2020,8 +2032,8 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
2020
2032
|
} };
|
|
2021
2033
|
const stringList = { type: "array", items: { type: "string" } };
|
|
2022
2034
|
const missionUnitSchema = { type: "object", additionalProperties: false,
|
|
2023
|
-
properties: { title: { type: "string" }, objective: { type: "string", description: "Target or corrective delta, about 2000 characters. Do not copy the original request. Longer authored instructions are preserved separately without a retry gate." },
|
|
2024
|
-
read: { ...stringList, description: "
|
|
2035
|
+
properties: { title: { type: "string" }, objective: { type: "string", description: "Target or corrective delta, about 2000 characters. Do not copy the original request. Carry the remaining action, not a routine reconfirmation of supplied facts, setup or instructions. References are available for relevant gaps, not mandatory rereads. Longer authored instructions are preserved separately without a retry gate." },
|
|
2036
|
+
read: { ...stringList, description: "Known inputs that affect validation, not an allowlist or a checklist to reread. Avoid guessed AGENTS.md paths and whole live session/database/log trees just to inspect them; reuse already supplied instructions and known context." }, write: stringList,
|
|
2025
2037
|
validation: { ...stringList, description: "Meaningful commands using observed repository test paths and runner conventions, not guessed filenames. Reuse already discovered recipes; when unknown, locate the relevant test or repository script with ordinary read/search before declaring it. This is planning guidance, not new approval or proof of execution; native checks still establish acceptance." },
|
|
2026
2038
|
validation_cwd: { type: "object", additionalProperties: { type: "string" }, description: "Exact cwd per declared command; omitted commands use project_root. Registration-only correction of your active direct unit keeps the same admission and budget and requires new native checks, not historical proof." },
|
|
2027
2039
|
requirement_ids: { ...stringList, description: "Related requirement IDs. A single unit inherits all requirements when omitted; specify coverage when splitting work across units." } },
|
|
@@ -2043,10 +2055,13 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
2043
2055
|
const missionExecutionSchema = { type: "object", properties: { commands: stringList, directory: { type: "string" } },
|
|
2044
2056
|
required: ["commands", "directory"], additionalProperties: false,
|
|
2045
2057
|
description: "For an operation: the actual run/grade commands, not a preflight or NO_START check. Host observes their native shell completion separately from successful exits; no handwritten proof file is needed. For run-once/result collection, validate the collected result without rerunning the operation.", "x-sortie-optional": true };
|
|
2058
|
+
const handoffContextSchema = { type: "string", "x-sortie-optional": true,
|
|
2059
|
+
description: "Optional current working notes: intent/rationale, known environment and paths, completed setup/checks with results, failed approaches and remaining work. Reuse known facts with source references; no required form or new approval. Public history and Mission observations are also supplied automatically. Omit when already sufficient; do not recopy the original request." };
|
|
2046
2060
|
tools[startMission] = { description: `Operator: save current requirements; the host retains the original request verbatim. With a meaningful formal check known from user, project or task context and one useful unit, include unit NOW (and execution for an operation) and dispatch its Worker directly; plan_units remains available after start. A literal command in the original request is not required. Worker owns investigation/edit/check/requested commit before independent Review; no routine preparation or commit-only handoff. ${MISSION_GIT_SCOPE} Unknown checks or real unit decomposition use Coordinator. intent=replace follows an actual user requirement change, retaining unchanged constraints and cumulative spend; for mission-source-reconciliation-required use the saved requirements when they reflect that change. intent=new is separate work in another location.`,
|
|
2047
2061
|
args: { requirements: { ...stringList, minItems: 1, maxItems: 64 },
|
|
2048
2062
|
unit: { ...missionUnitSchema, description: "When the meaningful check and single-unit scope are already known, include this unit now. Returns its configured Worker directly, combining start_mission and plan_units without another model round trip.", "x-sortie-optional": true },
|
|
2049
2063
|
execution: missionExecutionSchema,
|
|
2064
|
+
handoff_context: handoffContextSchema,
|
|
2050
2065
|
confirmed_conditions: conditionsSchema,
|
|
2051
2066
|
prohibited_write: { ...stringList, description: "Only explicit path prohibitions from the user or applicable instructions. Never infer a parent glob from project/repository names, semantic 'do not modify the product', or the complement of estimated unit.write. Preserve the authorized clone and exact prohibited paths; semantic constraints stay in requirements.", "x-sortie-optional": true },
|
|
2052
2067
|
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 },
|
|
@@ -2092,11 +2107,13 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
2092
2107
|
return JSON.stringify({ ...relocated, budget: await control.currentBudget(context.sessionID) });
|
|
2093
2108
|
}
|
|
2094
2109
|
const cancelled = await operators.read(context.sessionID);
|
|
2110
|
+
const history = await missionHistory(context.sessionID);
|
|
2095
2111
|
let mission = await missions.start(context.sessionID, requirements, args.intent === "replace" || args.intent === "new", {
|
|
2096
2112
|
kind: args.kind === "operation" ? "operation" : "implementation", executionHost: input.executionHost,
|
|
2097
|
-
context: missionConversationContext(
|
|
2113
|
+
context: missionConversationContext(history),
|
|
2098
2114
|
...(args.intent === "replace" && cancelled?.phase === "cancelled" ? { cancelledRunID: cancelled.runID } : {}),
|
|
2099
2115
|
});
|
|
2116
|
+
mission = await missions.retainContext(context.sessionID, context.sessionID, history, args.handoff_context);
|
|
2100
2117
|
const prohibited = args.prohibited_write;
|
|
2101
2118
|
if (prohibited !== undefined) {
|
|
2102
2119
|
if (!Array.isArray(prohibited) || !prohibited.every(path => typeof path === "string"))
|
|
@@ -2298,7 +2315,7 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
2298
2315
|
const commands = [...new Set(execution.commands.map(missionValidationCommand).map(normalizeCommand))];
|
|
2299
2316
|
return { commands, directory: resolve(input.directory, execution.directory), observations: operation?.observations ?? [] };
|
|
2300
2317
|
}
|
|
2301
|
-
async function declareMissionUnits(root, actor, mission, raw, reason, execution, planningCallID) {
|
|
2318
|
+
async function declareMissionUnits(root, actor, mission, raw, reason, execution, planningCallID, handoffNotes) {
|
|
2302
2319
|
await control.currentBudget(root);
|
|
2303
2320
|
mission = await missions.required(root);
|
|
2304
2321
|
let previous = await operators.read(root);
|
|
@@ -2452,13 +2469,16 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
2452
2469
|
},
|
|
2453
2470
|
}) : [];
|
|
2454
2471
|
const dispatcher = actor === root ? undefined : { sessionID: actor, callID: mission.callID };
|
|
2455
|
-
const
|
|
2456
|
-
|
|
2472
|
+
const histories = await Promise.all([...new Set([root, actor])].map(async (id) => ({ id, messages: await missionHistory(id) })));
|
|
2473
|
+
for (const history of histories)
|
|
2474
|
+
mission = await missions.contextSnapshot(mission, history.id, history.messages, history.id === actor ? handoffNotes : undefined);
|
|
2475
|
+
const context = await missions.handoffContext(mission);
|
|
2457
2476
|
const state = replanning && previous ? await operators.replanMission(root, previous.runID, plan, dispatcher, context)
|
|
2458
2477
|
: await operators.prepareMission(root, plan, dispatcher, mission.supersededRunID, terminalChildren, mission.requirementsReplaced, context);
|
|
2459
2478
|
await control.registerGoalDeclaration(root, state.units[0].task.prompt, true);
|
|
2460
2479
|
control.enableUnits(root, state.units.filter(unit => unit.status === "pending").length);
|
|
2461
2480
|
await missions.update(root, item => {
|
|
2481
|
+
item.continuity = mission.continuity;
|
|
2462
2482
|
item.reviewScope = missionReviewScope(item.reviewScope, ...(previous && item.runID === previous.runID ? [previous] : []), state);
|
|
2463
2483
|
if (item.runID !== state.runID)
|
|
2464
2484
|
item.plans++;
|
|
@@ -2484,6 +2504,7 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
2484
2504
|
tools[planUnits] = { description: `Coordinator or single-unit Fast-lane Operator: declare useful work with estimated read/write scope and meaningful formal checks. executor=self starts implementation/correction and formal validation HERE without a Worker or handoff read; otherwise dispatch the returned configured Worker promptly. Keep investigation/edit/check/requested commit with the same author before independent Review, not a commit-only handoff. ${MISSION_GIT_SCOPE} Native scope reconciliation/expand_unit retain host permissions and explicit path prohibitions. Objective is the target or corrective delta: aim for 2000 characters; the full original request/public reproduction is supplied separately, not copied here. Oversized unit instructions are retained verbatim in handoff Mission context, not rejected for another planning round. Keep every requirement covered. Final validation proves the unit; empty or dummy checks do not qualify. Diagnostics need no registration. write: [] is read-only; dir/** and native absolute paths support actual outputs. reason replans settled work, or ends your own quiescent direct unit without acceptance to correct its declaration, in the same requirements/budget; same-scope failed validation uses retry_mission_unit. Reviewer FINDINGS stay with the same Reviewer for correction, formal validation and self-recheck.`,
|
|
2485
2505
|
args: { units: { type: "array", minItems: 1, maxItems: 32, items: missionUnitSchema },
|
|
2486
2506
|
execution: missionExecutionSchema,
|
|
2507
|
+
handoff_context: handoffContextSchema,
|
|
2487
2508
|
reason: { type: "string", "x-sortie-optional": true },
|
|
2488
2509
|
executor: { type: "string", enum: ["worker", "self"], description: "Use self to implement/correct and formally validate here without a Worker handoff. Default worker retains configured Worker routing.", "x-sortie-optional": true } }, execute: async (args, context) => {
|
|
2489
2510
|
const { root, mission } = await missionAuthority(context.sessionID);
|
|
@@ -2491,7 +2512,7 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
2491
2512
|
throw new Error("mission-executor-invalid");
|
|
2492
2513
|
try {
|
|
2493
2514
|
return await serializeDispatchTransition(root, async () => {
|
|
2494
|
-
const planned = await declareMissionUnits(root, context.sessionID, mission, args.units, args.reason, args.execution, context.callID);
|
|
2515
|
+
const planned = await declareMissionUnits(root, context.sessionID, mission, args.units, args.reason, args.execution, context.callID, args.handoff_context);
|
|
2495
2516
|
return args.executor === "self" && record(JSON.parse(planned).task) ? startDirect(root, context.sessionID) : planned;
|
|
2496
2517
|
});
|
|
2497
2518
|
}
|
|
@@ -2695,7 +2716,7 @@ export function createProfiledPlugin(profile, assetVersion) {
|
|
|
2695
2716
|
const baseline = await missionReviewBaseline(input.directory);
|
|
2696
2717
|
const dispatcher = mission.coordinator ? { sessionID: mission.coordinator, callID: mission.callID } : undefined;
|
|
2697
2718
|
const prepared = await operators.prepareReviewerCorrection(root, run.runID, correctedPlan, authorID, reviewIdentity, retainedPlan.units[0].write, dispatcher, {
|
|
2698
|
-
|
|
2719
|
+
...await missions.handoffContext(mission),
|
|
2699
2720
|
correction_context: { findings: recovery ? existing.findings : review.result,
|
|
2700
2721
|
retained_findings_ref: JSON.parse(missions.correctionReference(root, reviewIdentity)),
|
|
2701
2722
|
mission_id: mission.id, prior_run_id: run.runID, review_identity: reviewIdentity,
|
|
@@ -429,6 +429,12 @@ run/grade commands and working directory. Setup, launch and result collection no
|
|
|
429
429
|
do not forbid execution while assigning that Worker the requirement to execute.
|
|
430
430
|
Write objective as a target or corrective delta: aim for 2000 characters.
|
|
431
431
|
The Worker reads the original requests verbatim from the handoff once; do not copy them into objective.
|
|
432
|
+
Public conversation, recent tool results and Mission progress are supplied automatically in continuity.
|
|
433
|
+
When useful, include handoff_context in start_mission/plan_units: current intent and rationale, known environment,
|
|
434
|
+
completed setup/checks, failed approaches and remaining work with existing references. It is optional working
|
|
435
|
+
context, not a form or approval. Preserve useful knowledge instead of assigning unchanged discovery again.
|
|
436
|
+
Assign the remaining action. Reference paths support relevant gaps, not a mandatory reread checklist; avoid
|
|
437
|
+
routine environment/instruction reconfirmation and guessed AGENTS.md paths when that context is already supplied.
|
|
432
438
|
Keep investigation, edits, formal checks and any requested commit in the same Worker before independent Review;
|
|
433
439
|
do not invent a review-before-commit gate or a commit-only handoff. Preserve explicit user ordering.
|
|
434
440
|
${MISSION_GIT_SCOPE}
|
|
@@ -549,8 +555,8 @@ mode: subagent
|
|
|
549
555
|
|
|
550
556
|
Implement, test and requested commit in this Task.
|
|
551
557
|
Reuse supplied AGENTS.md; for gaps prefer exact ancestor files/affected subtrees over parent globs.
|
|
552
|
-
Read handoff_path in full first: task.objective, verbatim original_requests/unit_instruction,
|
|
553
|
-
coverage
|
|
558
|
+
Read handoff_path in full first: task.objective, verbatim original_requests/unit_instruction, constraints/continuity,
|
|
559
|
+
coverage. Preserve user scope and ordering; prove assigned criteria, not Mission completion.
|
|
554
560
|
Denied: reason/remedy. No routine manifest/goal/status/bind.
|
|
555
561
|
Recovery: ${profile.toolPrefix}bind_write_gate with exact project_root and manifest_path=operation_manifest.
|
|
556
562
|
Treat cwd/project_root and paths as opaque; never shorten or normalize segments.
|