sortie-dogs 0.2.13 → 0.2.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -7
- package/dist/plugin/config.d.ts +1 -0
- package/dist/plugin/config.js +8 -2
- package/dist/plugin/continuation.d.ts +1 -1
- package/dist/plugin/continuation.js +9 -1
- package/dist/plugin/index.js +20 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -144,11 +144,22 @@ Optional settings in `.opencode/sortie-dogs.json`:
|
|
|
144
144
|
{
|
|
145
145
|
"operationManifestPath": "operation-manifest.json",
|
|
146
146
|
"handoffPaths": ["handoff.json"],
|
|
147
|
-
"readOnlyTools": ["my_mcp_search"],
|
|
148
|
-
"dedicatedWorkerModel": { "model": "provider/model", "variant": "deep" },
|
|
149
|
-
"continuation": { "enabled": true, "maxAutoContinues": 3 }
|
|
150
|
-
|
|
151
|
-
|
|
147
|
+
"readOnlyTools": ["my_mcp_search"],
|
|
148
|
+
"dedicatedWorkerModel": { "model": "provider/model", "variant": "deep" },
|
|
149
|
+
"continuation": { "enabled": true, "maxAutoContinues": 3 },
|
|
150
|
+
"reflection": {
|
|
151
|
+
"enabled": false,
|
|
152
|
+
"layers": { "run": true, "project": true, "global": false }
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
The same schema may be saved globally as
|
|
158
|
+
`~/.config/opencode/sortie-dogs.json` (on Windows,
|
|
159
|
+
`%USERPROFILE%\.config\opencode\sortie-dogs.json`). Precedence is built-in
|
|
160
|
+
defaults, global file, project file, `SORTIE_DOGS_CONFIG`, then plugin factory
|
|
161
|
+
options. OpenCode plugin normalization may omit factory options, so use the
|
|
162
|
+
global file for durable global settings.
|
|
152
163
|
|
|
153
164
|
- `operationManifestPath` moves the manifest; the path is project-relative.
|
|
154
165
|
- `handoffPaths` lists the handoff files the plugin inspects. A worker can only
|
|
@@ -170,8 +181,15 @@ Optional settings in `.opencode/sortie-dogs.json`:
|
|
|
170
181
|
`dog-coordinator` session is ever resumed: a child session is never promoted and
|
|
171
182
|
another coordinator is never adopted. Set `enabled` to `false` to keep every
|
|
172
183
|
batch manual, raise or lower `maxAutoContinues` (default `3`, maximum `10`) to
|
|
173
|
-
change the ceiling, and set `summarizeModel` to pin the compaction model when
|
|
174
|
-
the host default is unsuitable.
|
|
184
|
+
change the ceiling, and set `summarizeModel` to pin the compaction model when
|
|
185
|
+
the host default is unsuitable. Normal OpenCode auto-compaction keeps the
|
|
186
|
+
host's auto-continue behavior; Sortie suppresses it only while its own
|
|
187
|
+
explicitly queued rollover owns the resume.
|
|
188
|
+
- `reflection` is an opt-in process-prevention companion for an activated root
|
|
189
|
+
`dog-coordinator`. It is disabled by default. Run and project layers default
|
|
190
|
+
to enabled after opt-in; the cross-project global storage layer remains
|
|
191
|
+
disabled unless explicitly enabled. Child and non-coordinator sessions fail
|
|
192
|
+
closed, and `SORTIE_REFLECTION=0` is an immediate kill switch.
|
|
175
193
|
|
|
176
194
|
## Why Sortie-dogs
|
|
177
195
|
|
package/dist/plugin/config.d.ts
CHANGED
|
@@ -99,3 +99,4 @@ export declare const DEFAULT_PLUGIN_OPTIONS: Readonly<Omit<Required<SortieDogsPl
|
|
|
99
99
|
export declare function resolvePluginConfiguration(...values: readonly unknown[]): PluginConfiguration;
|
|
100
100
|
/** Resolve the plugin's fixed source boundaries: project-local first, environment and host global. */
|
|
101
101
|
export declare function resolvePluginConfigurationSources(projectValue: unknown, environmentValue: unknown, hostValue: unknown): PluginConfigurationSources;
|
|
102
|
+
export declare function resolvePluginConfigurationSourcesWithGlobal(globalValue: unknown, projectValue: unknown, environmentValue: unknown, hostValue: unknown): PluginConfigurationSources;
|
package/dist/plugin/config.js
CHANGED
|
@@ -372,17 +372,23 @@ export function resolvePluginConfiguration(...values) {
|
|
|
372
372
|
}
|
|
373
373
|
/** Resolve the plugin's fixed source boundaries: project-local first, environment and host global. */
|
|
374
374
|
export function resolvePluginConfigurationSources(projectValue, environmentValue, hostValue) {
|
|
375
|
-
|
|
375
|
+
return resolvePluginConfigurationSourcesWithGlobal(undefined, projectValue, environmentValue, hostValue);
|
|
376
|
+
}
|
|
377
|
+
export function resolvePluginConfigurationSourcesWithGlobal(globalValue, projectValue, environmentValue, hostValue) {
|
|
378
|
+
const configured = resolvePluginConfiguration(globalValue, projectValue, environmentValue, hostValue);
|
|
376
379
|
if (configured.kind === "invalid")
|
|
377
380
|
return configured;
|
|
381
|
+
const globalLayer = parseLayer(globalValue);
|
|
378
382
|
const projectLayer = parseLayer(projectValue);
|
|
379
383
|
const environmentLayer = parseLayer(environmentValue);
|
|
380
384
|
const hostLayer = parseLayer(hostValue);
|
|
381
|
-
if (
|
|
385
|
+
if (globalLayer === undefined || projectLayer === undefined ||
|
|
386
|
+
environmentLayer === undefined || hostLayer === undefined) {
|
|
382
387
|
return { kind: "invalid" };
|
|
383
388
|
}
|
|
384
389
|
const globalModelRouting = Object.fromEntries(Object.entries({
|
|
385
390
|
...recommendedRoleRouting(configured.dedicatedWorkerModel),
|
|
391
|
+
...(globalLayer.modelRouting ?? {}),
|
|
386
392
|
...(environmentLayer.modelRouting ?? {}),
|
|
387
393
|
...(hostLayer.modelRouting ?? {}),
|
|
388
394
|
}).filter(([role]) => !isFixedModelRole(role)));
|
|
@@ -125,7 +125,7 @@ export type ContinuationPolicySource = ContinuationPolicy | (() => ContinuationP
|
|
|
125
125
|
* without an agent field, or answers for a different directory, instead of failing silently.
|
|
126
126
|
*/
|
|
127
127
|
export type LocalIdentitySource = (sessionID: string) => ContinuationIdentity | undefined;
|
|
128
|
-
export type RolloverAbort = "identity-unavailable" | "child-session" | "summarize-unavailable" | "terminal-identity-rejected";
|
|
128
|
+
export type RolloverAbort = "identity-unavailable" | "child-session" | "summarize-unavailable" | "retries-exhausted" | "terminal-identity-rejected";
|
|
129
129
|
export interface ContinuationToolContext {
|
|
130
130
|
readonly sessionID: string;
|
|
131
131
|
readonly agent?: string | undefined;
|
|
@@ -304,9 +304,17 @@ export function createContinuationHooks(client, directory, policySource, timings
|
|
|
304
304
|
unrefTimer(setTimeout(async () => {
|
|
305
305
|
const completed = await runRollover(sessionID);
|
|
306
306
|
const state = sessions.get(sessionID);
|
|
307
|
+
if (!completed && (state?.cooldownTimer !== undefined || state?.active === true))
|
|
308
|
+
return;
|
|
307
309
|
if (!completed && state?.pendingRollover === true && attempt < timings.scheduleAttempts) {
|
|
308
310
|
scheduleRollover(sessionID, attempt + 1);
|
|
309
311
|
}
|
|
312
|
+
else if (!completed && state?.pendingRollover === true) {
|
|
313
|
+
state.pendingRollover = false;
|
|
314
|
+
state.promptPending = false;
|
|
315
|
+
state.continueReport = undefined;
|
|
316
|
+
warnRollover(sessionID, "retries-exhausted");
|
|
317
|
+
}
|
|
310
318
|
}, timings.scheduleMilliseconds * (attempt + 1)));
|
|
311
319
|
}
|
|
312
320
|
function queueRollover(sessionID, report, resume) {
|
|
@@ -446,7 +454,7 @@ export function createContinuationHooks(client, directory, policySource, timings
|
|
|
446
454
|
identity.agent !== policy().agent)
|
|
447
455
|
return;
|
|
448
456
|
}
|
|
449
|
-
if (
|
|
457
|
+
if (pending)
|
|
450
458
|
output.enabled = false;
|
|
451
459
|
},
|
|
452
460
|
async sessionIdle(sessionID) {
|
package/dist/plugin/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { RUNTIME_ASSET_VERSION } from "../asset-version.js";
|
|
|
5
5
|
import { normalizeRelativePath, RelativePathError } from "../core/path.js";
|
|
6
6
|
import { validateManifest } from "../core/validate-manifest.js";
|
|
7
7
|
import { safeSchemaPointer, validateHandoffSchema, validateOperationManifestSchema, } from "../core/validate-schema.js";
|
|
8
|
-
import { DEFAULT_PLUGIN_OPTIONS,
|
|
8
|
+
import { DEFAULT_PLUGIN_OPTIONS, resolvePluginConfiguration, resolvePluginConfigurationSourcesWithGlobal, } from "./config.js";
|
|
9
9
|
import { CONTINUATION_CAPABILITY, createContinuationHooks, } from "./continuation.js";
|
|
10
10
|
import { WriteDeniedError, createProjectPaths, createWriteGate, describeUnclassifiedCommand, isKnownReadOnlyTool, normalizeCommand, resolveProjectRoot, safePath, } from "./gate.js";
|
|
11
11
|
import { createModelRoutingHook, } from "./model-routing-hook.js";
|
|
@@ -190,6 +190,22 @@ async function readOptionalProjectConfig(project) {
|
|
|
190
190
|
throw error;
|
|
191
191
|
}
|
|
192
192
|
}
|
|
193
|
+
async function readOptionalGlobalConfig() {
|
|
194
|
+
try {
|
|
195
|
+
const value = await readJson(join(configRoot(), "sortie-dogs.json"), INPUT_LIMITS.config);
|
|
196
|
+
if (resolvePluginConfiguration(value).kind === "invalid") {
|
|
197
|
+
console.warn("[sortie-dogs] global configuration ignored: invalid or unavailable");
|
|
198
|
+
return undefined;
|
|
199
|
+
}
|
|
200
|
+
return value;
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
if (isAbsentPathError(error))
|
|
204
|
+
return undefined;
|
|
205
|
+
console.warn("[sortie-dogs] global configuration ignored: invalid or unavailable");
|
|
206
|
+
return undefined;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
193
209
|
function readEnvironmentConfig() {
|
|
194
210
|
const source = process.env[ENV_CONFIG];
|
|
195
211
|
if (source === undefined || source.length === 0)
|
|
@@ -411,11 +427,12 @@ export const SortieDogsPlugin = async (input, options) => {
|
|
|
411
427
|
let loading;
|
|
412
428
|
let manifestAbsent = false;
|
|
413
429
|
let assetVersionReported = false;
|
|
430
|
+
const globalConfig = await readOptionalGlobalConfig();
|
|
414
431
|
// Project config read is required discovery for its opt-in; no reflection storage/version read
|
|
415
432
|
// occurs unless that resolved config enables reflection. It stays isolated from write-gate load.
|
|
416
433
|
try {
|
|
417
434
|
project = await createProjectPaths(resolveProjectRoot(input));
|
|
418
|
-
const probed =
|
|
435
|
+
const probed = resolvePluginConfigurationSourcesWithGlobal(globalConfig, await readOptionalProjectConfig(project), readEnvironmentConfig(), options);
|
|
419
436
|
if (probed.kind === "configured" && reflectionEnabled(probed.reflection)) {
|
|
420
437
|
reflectionVersion = await nearestPackageVersion();
|
|
421
438
|
reflectionConfiguration = probed.reflection;
|
|
@@ -463,7 +480,7 @@ export const SortieDogsPlugin = async (input, options) => {
|
|
|
463
480
|
await reportAssetVersionSkew(project);
|
|
464
481
|
const projectConfig = await readOptionalProjectConfig(project);
|
|
465
482
|
const environmentConfig = readEnvironmentConfig();
|
|
466
|
-
const parsed =
|
|
483
|
+
const parsed = resolvePluginConfigurationSourcesWithGlobal(globalConfig, projectConfig, environmentConfig, options);
|
|
467
484
|
if (parsed.kind === "invalid")
|
|
468
485
|
throw new WriteDeniedError("manifest-unavailable", "<unknown>");
|
|
469
486
|
loaded = loadConfigured(parsed, input.worktree ?? project.root, input.client);
|