@deftai/directive-core 0.85.0 → 0.87.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/check/gate-lists.js +1 -0
- package/dist/doctor/constants.d.ts +2 -2
- package/dist/doctor/constants.js +2 -2
- package/dist/doctor/main.d.ts +8 -6
- package/dist/doctor/main.js +102 -36
- package/dist/doctor/taskfile.d.ts +8 -0
- package/dist/doctor/taskfile.js +19 -0
- package/dist/doctor/types.d.ts +3 -0
- package/dist/hooks/dispatcher.d.ts +21 -1
- package/dist/hooks/dispatcher.js +85 -15
- package/dist/init-deposit/agent-hooks.js +14 -6
- package/dist/init-deposit/gitignore.js +5 -0
- package/dist/intake/issue-emit.d.ts +45 -2
- package/dist/intake/issue-emit.js +420 -17
- package/dist/intake/issue-ingest.js +54 -4
- package/dist/platform/platform-capabilities.js +3 -0
- package/dist/policy/org-force-on-migration.js +2 -0
- package/dist/render/framework-commands.d.ts +1 -1
- package/dist/render/framework-commands.js +6 -6
- package/dist/render/roadmap-render.d.ts +5 -1
- package/dist/render/roadmap-render.js +20 -2
- package/dist/render/rule-map.js +5 -0
- package/dist/review-monitor/constants.js +3 -2
- package/dist/review-monitor/tier-detection.d.ts +6 -2
- package/dist/review-monitor/tier-detection.js +27 -2
- package/dist/scope/transition.js +43 -0
- package/dist/session/release-availability.d.ts +2 -0
- package/dist/session/release-availability.js +23 -8
- package/dist/swarm/routing-set-cli.js +5 -10
- package/dist/swarm/routing.d.ts +3 -2
- package/dist/swarm/routing.js +16 -4
- package/dist/triage/help/registry-data.d.ts +7 -7
- package/dist/triage/help/registry-data.js +15 -6
- package/dist/triage/queue/index.d.ts +1 -0
- package/dist/triage/queue/index.js +1 -0
- package/dist/triage/queue/show.d.ts +69 -0
- package/dist/triage/queue/show.js +293 -0
- package/dist/triage/scope/cli.js +3 -0
- package/dist/triage/scope/coverage.d.ts +2 -0
- package/dist/triage/scope/coverage.js +18 -3
- package/dist/verify-env/agent-hooks-live-probe.d.ts +32 -0
- package/dist/verify-env/agent-hooks-live-probe.js +216 -0
- package/dist/verify-env/index.d.ts +1 -0
- package/dist/verify-env/index.js +1 -0
- package/dist/verify-source/index.d.ts +1 -0
- package/dist/verify-source/index.js +1 -0
- package/dist/verify-source/openclaw-tier1.d.ts +37 -0
- package/dist/verify-source/openclaw-tier1.js +100 -0
- package/dist/xbrief-migrate/migrate-project.js +35 -22
- package/package.json +3 -3
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
3
|
import { readCorePackageVersion } from "../engine-version.js";
|
|
4
|
+
import { assertWriteTargetSafe } from "../fs/projection-containment.js";
|
|
4
5
|
import { atomicWriteProjectDefinition, projectDefinitionMutationLock, } from "../vbrief-build/project-definition-io.js";
|
|
5
6
|
import { migrateLegacyPolicyKey, PLAN_POLICY_KEY, readPlanPolicy } from "./plan-extensions.js";
|
|
6
7
|
import { appendAuditLog, loadProjectDefinition, projectDefinitionPath } from "./resolve.js";
|
|
@@ -56,6 +57,7 @@ export function readOrgForceOnMarker(projectRoot) {
|
|
|
56
57
|
}
|
|
57
58
|
function writeOrgForceOnMarker(projectRoot, marker) {
|
|
58
59
|
const path = markerPath(projectRoot);
|
|
60
|
+
assertWriteTargetSafe(projectRoot, path);
|
|
59
61
|
mkdirSync(join(path, ".."), { recursive: true });
|
|
60
62
|
writeFileSync(path, `${JSON.stringify(marker, null, 2)}\n`, "utf8");
|
|
61
63
|
}
|
|
@@ -25,7 +25,7 @@ export declare function formatFrameworkCommand(args: readonly string[], options?
|
|
|
25
25
|
surface?: string;
|
|
26
26
|
taskPrefix?: string | null;
|
|
27
27
|
}): string;
|
|
28
|
-
export declare function cmdCoreValidate(argv: readonly string[]): number;
|
|
28
|
+
export declare function cmdCoreValidate(argv: readonly string[], root?: string): number;
|
|
29
29
|
export declare function cmdCoreLint(_argv: readonly string[]): number;
|
|
30
30
|
export declare function cmdCoreTest(_argv: readonly string[]): number;
|
|
31
31
|
export declare function resolveFrameworkRoot(): string;
|
|
@@ -154,7 +154,7 @@ export function formatFrameworkCommand(args, options = {}) {
|
|
|
154
154
|
}
|
|
155
155
|
return [surface, ...parts].join(" ");
|
|
156
156
|
}
|
|
157
|
-
const EXCLUDE_PARTS = new Set([".git", "backup"]);
|
|
157
|
+
const EXCLUDE_PARTS = new Set([".git", "backup", "node_modules", ".deft-scratch"]);
|
|
158
158
|
function collectMarkdownFiles(root) {
|
|
159
159
|
const out = [];
|
|
160
160
|
const walk = (dir, parts) => {
|
|
@@ -189,12 +189,12 @@ function collectMarkdownFiles(root) {
|
|
|
189
189
|
walk(root, []);
|
|
190
190
|
return out.sort();
|
|
191
191
|
}
|
|
192
|
-
export function cmdCoreValidate(argv) {
|
|
192
|
+
export function cmdCoreValidate(argv, root = ".") {
|
|
193
193
|
if (argv.length > 0) {
|
|
194
194
|
process.stderr.write(`error: core:validate does not accept arguments: ${argv.join(" ")}\n`);
|
|
195
195
|
return 2;
|
|
196
196
|
}
|
|
197
|
-
const files = collectMarkdownFiles(
|
|
197
|
+
const files = collectMarkdownFiles(root);
|
|
198
198
|
for (const path of files)
|
|
199
199
|
process.stdout.write(`✓ ${path}\n`);
|
|
200
200
|
process.stdout.write(`✓ All ${files.length} markdown files validated\n`);
|
|
@@ -271,7 +271,7 @@ function runBuildDistArgv(argv) {
|
|
|
271
271
|
return result.status ?? 1;
|
|
272
272
|
}
|
|
273
273
|
const TS_INLINE = {
|
|
274
|
-
"framework_commands:_cmd_core_validate": (argv) => cmdCoreValidate(argv),
|
|
274
|
+
"framework_commands:_cmd_core_validate": (argv, cwd) => cmdCoreValidate(argv, cwd),
|
|
275
275
|
"doctor:cmd_doctor": (argv) => cmdDoctor(argv),
|
|
276
276
|
"build_dist:main": (argv) => runBuildDistArgv(argv),
|
|
277
277
|
};
|
|
@@ -349,7 +349,7 @@ function invokeEntrypoint(entrypoint, argv, cwd, frameworkRoot, noArgv, capture)
|
|
|
349
349
|
};
|
|
350
350
|
let code;
|
|
351
351
|
try {
|
|
352
|
-
code = inline(argv);
|
|
352
|
+
code = inline(argv, cwd);
|
|
353
353
|
}
|
|
354
354
|
finally {
|
|
355
355
|
process.stdout.write = prevOut;
|
|
@@ -357,7 +357,7 @@ function invokeEntrypoint(entrypoint, argv, cwd, frameworkRoot, noArgv, capture)
|
|
|
357
357
|
}
|
|
358
358
|
return { code, stdout: chunks.out, stderr: chunks.err };
|
|
359
359
|
}
|
|
360
|
-
return { code: inline(argv), stdout: "", stderr: "" };
|
|
360
|
+
return { code: inline(argv, cwd), stdout: "", stderr: "" };
|
|
361
361
|
}
|
|
362
362
|
const verb = ENTRYPOINT_VERB[entrypoint];
|
|
363
363
|
if (verb) {
|
|
@@ -3,7 +3,11 @@ export declare function renderRoadmapToBuffer(pendingDir: string, completedDir?:
|
|
|
3
3
|
/** @deprecated Prefer ``renderRoadmapToBuffer`` — kept for existing imports and parity harnesses. */
|
|
4
4
|
export declare function generateRoadmapContent(pendingDir: string, completedDir?: string): string;
|
|
5
5
|
export type RenderRoadmapResult = readonly [boolean, string];
|
|
6
|
-
export
|
|
6
|
+
export type RenderRoadmapOptions = {
|
|
7
|
+
completedDir?: string;
|
|
8
|
+
projectRoot?: string;
|
|
9
|
+
};
|
|
10
|
+
export declare function renderRoadmap(pendingDir: string, outPath: string, completedDirOrOptions?: string | RenderRoadmapOptions): RenderRoadmapResult;
|
|
7
11
|
export declare function checkDrift(pendingDir: string, roadmapPath: string, completedDir?: string): RenderRoadmapResult;
|
|
8
12
|
/** CLI entry (mirrors ``scripts/roadmap_render.main``). */
|
|
9
13
|
export declare function main(argv: readonly string[]): number;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { dirname, join, resolve } from "node:path";
|
|
3
|
+
import { assertWriteTargetSafe } from "../fs/projection-containment.js";
|
|
3
4
|
import { hasArtifactSuffix, resolveLifecycleRoot } from "../layout/resolve.js";
|
|
4
5
|
import { MIGRATOR_METADATA_KEY, ROADMAP_BANNER } from "./constants.js";
|
|
5
6
|
import { phaseSortKey } from "./text-utils.js";
|
|
@@ -351,9 +352,22 @@ export function renderRoadmapToBuffer(pendingDir, completedDir) {
|
|
|
351
352
|
export function generateRoadmapContent(pendingDir, completedDir) {
|
|
352
353
|
return renderRoadmapToBuffer(pendingDir, completedDir);
|
|
353
354
|
}
|
|
354
|
-
export function renderRoadmap(pendingDir, outPath,
|
|
355
|
+
export function renderRoadmap(pendingDir, outPath, completedDirOrOptions) {
|
|
356
|
+
let completedDir;
|
|
357
|
+
let projectRoot;
|
|
358
|
+
if (typeof completedDirOrOptions === "string") {
|
|
359
|
+
completedDir = completedDirOrOptions;
|
|
360
|
+
}
|
|
361
|
+
else if (completedDirOrOptions !== undefined) {
|
|
362
|
+
completedDir = completedDirOrOptions.completedDir;
|
|
363
|
+
projectRoot = completedDirOrOptions.projectRoot;
|
|
364
|
+
}
|
|
355
365
|
try {
|
|
356
366
|
const content = renderRoadmapToBuffer(pendingDir, completedDir);
|
|
367
|
+
// Trust boundary is the project root — never dirname(outPath), which follows a
|
|
368
|
+
// diverted parent symlink and would make containment pass outside the checkout.
|
|
369
|
+
const projectDir = projectRoot !== undefined ? resolve(projectRoot) : resolve(pendingDir, "..", "..");
|
|
370
|
+
assertWriteTargetSafe(projectDir, resolve(outPath));
|
|
357
371
|
writeFileSync(outPath, content, "utf8");
|
|
358
372
|
return [true, `✓ Rendered ROADMAP.md to ${outPath}`];
|
|
359
373
|
}
|
|
@@ -415,7 +429,11 @@ export function main(argv) {
|
|
|
415
429
|
process.stdout.write(`${msg}\n`);
|
|
416
430
|
return ok ? 0 : 1;
|
|
417
431
|
}
|
|
418
|
-
|
|
432
|
+
// When --project-root is set, use it; otherwise renderRoadmap derives from pendingDir
|
|
433
|
+
// (…/xbrief|vbrief/pending → project root). Never use dirname(outPath).
|
|
434
|
+
const [ok, msg] = renderRoadmap(pendingDir, outPath, {
|
|
435
|
+
projectRoot: projectRoot !== undefined ? resolve(projectRoot) : undefined,
|
|
436
|
+
});
|
|
419
437
|
process.stdout.write(`${msg}\n`);
|
|
420
438
|
return ok ? 0 : 1;
|
|
421
439
|
}
|
package/dist/render/rule-map.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
14
14
|
import { basename, dirname, join, relative, resolve } from "node:path";
|
|
15
15
|
import { fileURLToPath } from "node:url";
|
|
16
|
+
import { assertWriteTargetSafe } from "../fs/projection-containment.js";
|
|
16
17
|
import { TEMPLATE } from "./rule-map-template.js";
|
|
17
18
|
const MARKER_RE = /^\s*[-*]\s*([!~⊗≉?])\s/;
|
|
18
19
|
const MARKER_LABEL = {
|
|
@@ -684,6 +685,10 @@ export function main(argv) {
|
|
|
684
685
|
process.stdout.write("ok: docs/RULE-MAP.md is up to date\n");
|
|
685
686
|
return 0;
|
|
686
687
|
}
|
|
688
|
+
// Preflight both destinations before either write so a later HTML refusal
|
|
689
|
+
// cannot leave RULE-MAP.md updated while docs/rule-map/index.html stays stale.
|
|
690
|
+
assertWriteTargetSafe(repo, mdPath);
|
|
691
|
+
assertWriteTargetSafe(repo, htmlPath);
|
|
687
692
|
mkdirSync(dirname(mdPath), { recursive: true });
|
|
688
693
|
writeFileSync(mdPath, md, "utf8");
|
|
689
694
|
mkdirSync(htmlDir, { recursive: true });
|
|
@@ -40,7 +40,7 @@ export const REVIEW_MONITOR_HELP = "usage: task verify:review-monitor -- --pr <N
|
|
|
40
40
|
"\n" +
|
|
41
41
|
"Claim a lease after spawning Approach 1:\n" +
|
|
42
42
|
" task review-monitor:register -- --pr <N> --monitor-agent-id <id> \\\n" +
|
|
43
|
-
" --platform-primitive cursor-task|spawn_subagent|start_agent \\\n" +
|
|
43
|
+
" --platform-primitive cursor-task|spawn_subagent|start_agent|sessions_spawn \\\n" +
|
|
44
44
|
" [--head-sha SHA] [--repo OWNER/REPO] [--force]\n" +
|
|
45
45
|
"\n" +
|
|
46
46
|
"Release when done:\n" +
|
|
@@ -54,7 +54,8 @@ export const REGISTER_HELP = "usage: task review-monitor:register -- --pr <N> --
|
|
|
54
54
|
"required:\n" +
|
|
55
55
|
" --pr N Pull request number\n" +
|
|
56
56
|
" --monitor-agent-id ID Stable poller agent id / Task handle\n" +
|
|
57
|
-
" --platform-primitive P start_agent | spawn_subagent | cursor-task
|
|
57
|
+
" --platform-primitive P start_agent | spawn_subagent | cursor-task |\n" +
|
|
58
|
+
" sessions_spawn | openclaw-sessions-spawn (#2876)\n" +
|
|
58
59
|
"\n" +
|
|
59
60
|
"options:\n" +
|
|
60
61
|
" --repo OWNER/REPO Repository (default: origin / DEFT_TRIAGE_REPO)\n" +
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { MONITORING_TIER_1, MONITORING_TIER_2, MONITORING_TIER_3 } from "./constants.js";
|
|
2
|
-
|
|
2
|
+
/** Canonical Approach-1 platform primitives for review-monitor register/verify (#2655 / #2876). */
|
|
3
|
+
export type PlatformPrimitive = "start_agent" | "spawn_subagent" | "cursor-task" | "sessions_spawn" | "openclaw-sessions-spawn";
|
|
4
|
+
/** Accepted `--platform-primitive` values (register CLI + help text). */
|
|
5
|
+
export declare const PLATFORM_PRIMITIVES: readonly PlatformPrimitive[];
|
|
6
|
+
export declare const PLATFORM_PRIMITIVE_SET: Set<string>;
|
|
3
7
|
export interface MonitoringTierProbe {
|
|
4
8
|
readonly tier: typeof MONITORING_TIER_1 | typeof MONITORING_TIER_2 | typeof MONITORING_TIER_3;
|
|
5
9
|
readonly primitive: PlatformPrimitive | null;
|
|
@@ -7,7 +11,7 @@ export interface MonitoringTierProbe {
|
|
|
7
11
|
}
|
|
8
12
|
/**
|
|
9
13
|
* Inline Tier-1 detection aligned with the swarm Phase 3 / review-cycle matrix
|
|
10
|
-
* (#1877 / #2655). Prefer `task platform:capabilities` when available (#1357);
|
|
14
|
+
* (#1877 / #2655 / #2876). Prefer `task platform:capabilities` when available (#1357);
|
|
11
15
|
* this probe does not block MVP.
|
|
12
16
|
*/
|
|
13
17
|
export declare function probeMonitoringTier(environ?: NodeJS.ProcessEnv): MonitoringTierProbe;
|
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
import { MONITORING_TIER_1, MONITORING_TIER_2, MONITORING_TIER_3 } from "./constants.js";
|
|
2
2
|
const TRUTHY = new Set(["1", "true", "yes", "on"]);
|
|
3
|
+
/** Accepted `--platform-primitive` values (register CLI + help text). */
|
|
4
|
+
export const PLATFORM_PRIMITIVES = [
|
|
5
|
+
"start_agent",
|
|
6
|
+
"spawn_subagent",
|
|
7
|
+
"cursor-task",
|
|
8
|
+
"sessions_spawn",
|
|
9
|
+
"openclaw-sessions-spawn",
|
|
10
|
+
];
|
|
11
|
+
export const PLATFORM_PRIMITIVE_SET = new Set(PLATFORM_PRIMITIVES);
|
|
3
12
|
function envTruthy(environ, name) {
|
|
4
13
|
return TRUTHY.has((environ[name] ?? "").trim().toLowerCase());
|
|
5
14
|
}
|
|
6
15
|
function probeOverride(environ) {
|
|
7
16
|
const raw = (environ.DEFT_MONITOR_TIER ?? environ.DEFT_MONITOR_TIER_OVERRIDE ?? "").trim();
|
|
8
17
|
if (raw === "1" || raw.toLowerCase() === "tier1") {
|
|
9
|
-
const
|
|
18
|
+
const requested = (environ.DEFT_MONITOR_TIER1_PRIMITIVE ?? "cursor-task").trim();
|
|
19
|
+
const primitive = PLATFORM_PRIMITIVE_SET.has(requested)
|
|
20
|
+
? requested
|
|
21
|
+
: "cursor-task";
|
|
10
22
|
return { tier: MONITORING_TIER_1, primitive, descriptor: "override-tier1" };
|
|
11
23
|
}
|
|
12
24
|
if (raw === "3" || raw.toLowerCase() === "tier3") {
|
|
@@ -16,7 +28,7 @@ function probeOverride(environ) {
|
|
|
16
28
|
}
|
|
17
29
|
/**
|
|
18
30
|
* Inline Tier-1 detection aligned with the swarm Phase 3 / review-cycle matrix
|
|
19
|
-
* (#1877 / #2655). Prefer `task platform:capabilities` when available (#1357);
|
|
31
|
+
* (#1877 / #2655 / #2876). Prefer `task platform:capabilities` when available (#1357);
|
|
20
32
|
* this probe does not block MVP.
|
|
21
33
|
*/
|
|
22
34
|
export function probeMonitoringTier(environ = process.env) {
|
|
@@ -41,6 +53,19 @@ export function probeMonitoringTier(environ = process.env) {
|
|
|
41
53
|
};
|
|
42
54
|
}
|
|
43
55
|
const runtime = (environ.DEFT_AGENT_RUNTIME ?? "").trim().toLowerCase();
|
|
56
|
+
// OpenClaw: sessions_spawn is the Tier-1 Approach 1 primitive (#2876).
|
|
57
|
+
// Alias openclaw-sessions-spawn accepted on register for explicit naming.
|
|
58
|
+
if (envTruthy(environ, "DEFT_PROBE_SESSIONS_SPAWN") ||
|
|
59
|
+
envTruthy(environ, "DEFT_HAS_SESSIONS_SPAWN") ||
|
|
60
|
+
envTruthy(environ, "DEFT_PROBE_OPENCLAW") ||
|
|
61
|
+
envTruthy(environ, "OPENCLAW") ||
|
|
62
|
+
runtime === "openclaw" ||
|
|
63
|
+
runtime === "openclaw-sessions-spawn") {
|
|
64
|
+
const alias = (environ.DEFT_MONITOR_TIER1_PRIMITIVE ?? "").trim() === "openclaw-sessions-spawn"
|
|
65
|
+
? "openclaw-sessions-spawn"
|
|
66
|
+
: "sessions_spawn";
|
|
67
|
+
return { tier: MONITORING_TIER_1, primitive: alias, descriptor: "openclaw" };
|
|
68
|
+
}
|
|
44
69
|
if (envTruthy(environ, "DEFT_PROBE_GROK_BUILD") ||
|
|
45
70
|
envTruthy(environ, "GROK_BUILD") ||
|
|
46
71
|
runtime === "grok-build") {
|
package/dist/scope/transition.js
CHANGED
|
@@ -11,6 +11,43 @@ import { detectLifecycleFolder, updateDecomposedChildBackReferences, updateDecom
|
|
|
11
11
|
import { syncProjectDefinitionAfterScopeMove } from "./project-definition-sync.js";
|
|
12
12
|
import { syncSpecificationAfterScopeMove } from "./specification-sync.js";
|
|
13
13
|
import { utcNowIso } from "./vbrief-json.js";
|
|
14
|
+
/** Item statuses that still represent unfinished work and should advance on terminal transitions (#2862). */
|
|
15
|
+
const NON_TERMINAL_ITEM_STATUSES = new Set(["pending", "proposed", "running"]);
|
|
16
|
+
/** Terminal lifecycle actions that reconcile the brief's own plan.items (#2862). */
|
|
17
|
+
const OWN_ITEMS_RECONCILE_ACTIONS = new Set(["complete", "fail", "cancel"]);
|
|
18
|
+
/**
|
|
19
|
+
* Advance non-terminal plan.items / subItems to the terminal target status.
|
|
20
|
+
* Leaves cancelled / failed / completed / other non-pending-proposed-running items alone (#2862).
|
|
21
|
+
*/
|
|
22
|
+
function advanceNonTerminalOwnItems(items, targetStatus) {
|
|
23
|
+
if (!Array.isArray(items)) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
for (const item of items) {
|
|
27
|
+
if (item === null || typeof item !== "object" || Array.isArray(item)) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const obj = item;
|
|
31
|
+
const status = String(obj.status ?? "");
|
|
32
|
+
if (NON_TERMINAL_ITEM_STATUSES.has(status)) {
|
|
33
|
+
obj.status = targetStatus;
|
|
34
|
+
}
|
|
35
|
+
advanceNonTerminalOwnItems(obj.subItems, targetStatus);
|
|
36
|
+
advanceNonTerminalOwnItems(obj.items, targetStatus);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Refresh the document envelope `updated` stamp to match `plan.updated`.
|
|
41
|
+
* Stamps whichever of xBRIEFInfo (v0.8) / vBRIEFInfo (v0.6) is present — never creates one (#2862 / #2346).
|
|
42
|
+
*/
|
|
43
|
+
function stampEnvelopeUpdated(data, nowIso) {
|
|
44
|
+
for (const key of ["xBRIEFInfo", "vBRIEFInfo"]) {
|
|
45
|
+
const env = data[key];
|
|
46
|
+
if (typeof env === "object" && env !== null && !Array.isArray(env)) {
|
|
47
|
+
env.updated = nowIso;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
14
51
|
export function runTransition(action, filePath, now = new Date()) {
|
|
15
52
|
if (!(action in TRANSITIONS)) {
|
|
16
53
|
const valid = Object.keys(TRANSITIONS).sort().join(", ");
|
|
@@ -96,6 +133,12 @@ export function runTransition(action, filePath, now = new Date()) {
|
|
|
96
133
|
const nowIso = utcNowIso(now);
|
|
97
134
|
planObj.status = targetStatus;
|
|
98
135
|
planObj.updated = nowIso;
|
|
136
|
+
// Keep the envelope clock aligned with plan.updated on every mutating transition (#2862).
|
|
137
|
+
stampEnvelopeUpdated(data, nowIso);
|
|
138
|
+
// Reconcile the completing brief's own plan.items (mirrors #1527 / #2566 registry sync) (#2862).
|
|
139
|
+
if (OWN_ITEMS_RECONCILE_ACTIONS.has(act)) {
|
|
140
|
+
advanceNonTerminalOwnItems(planObj.items, targetStatus);
|
|
141
|
+
}
|
|
99
142
|
if (act === "complete") {
|
|
100
143
|
stampCompletionMetadata(planObj, projectRoot, nowIso);
|
|
101
144
|
}
|
|
@@ -4,9 +4,15 @@ import { dirname, join } from "node:path";
|
|
|
4
4
|
import { locateManifest, parseInstallManifest } from "../doctor/manifest.js";
|
|
5
5
|
import { runningInsideDeftRepo } from "../doctor/paths.js";
|
|
6
6
|
import { evaluateReleaseAvailability } from "../doctor/release-availability.js";
|
|
7
|
+
import { resolveTriageCachePath } from "../triage/cache-path.js";
|
|
7
8
|
const THROTTLE_MS = 24 * 60 * 60 * 1000;
|
|
8
9
|
const PUBLIC_NPM_REGISTRY = "https://registry.npmjs.org/";
|
|
9
|
-
const
|
|
10
|
+
const STATE_FILE_NAME = "release-availability-state.json";
|
|
11
|
+
/** Display/back-compat constant; resolution flows through resolveTriageCachePath (#2869). */
|
|
12
|
+
export const STATE_RELATIVE_PATH = join("xbrief", ".triage-cache", STATE_FILE_NAME);
|
|
13
|
+
function resolveReleaseAvailabilityStatePath(projectRoot) {
|
|
14
|
+
return resolveTriageCachePath(projectRoot, STATE_FILE_NAME);
|
|
15
|
+
}
|
|
10
16
|
function defaultReadText(path) {
|
|
11
17
|
try {
|
|
12
18
|
return readFileSync(path, "utf8");
|
|
@@ -91,18 +97,27 @@ export function probeSessionReleaseAvailability(projectRoot, options = {}) {
|
|
|
91
97
|
const availability = evaluateReleaseAvailability(installed, npmResult.ok ? npmResult.version : null);
|
|
92
98
|
if (availability.status !== "available")
|
|
93
99
|
return { lines };
|
|
94
|
-
|
|
95
|
-
|
|
100
|
+
let statePath = null;
|
|
101
|
+
try {
|
|
102
|
+
statePath = resolveReleaseAvailabilityStatePath(projectRoot);
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
// Symlink-escaping triage-cache path: skip throttle state; still emit advisory (#2869).
|
|
106
|
+
statePath = null;
|
|
107
|
+
}
|
|
108
|
+
const state = parseState(statePath !== null ? (options.readState ?? defaultReadText)(statePath) : null);
|
|
96
109
|
const now = options.now ?? new Date();
|
|
97
110
|
if (isThrottled(state, availability.latestVersion, now))
|
|
98
111
|
return { lines: [] };
|
|
99
112
|
const message = `[deft release] Newer Directive release available: v${availability.latestVersion} ` +
|
|
100
113
|
`(installed v${availability.installedVersion}). Run \`npm i -g @deftai/directive@latest\`.`;
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
114
|
+
if (statePath !== null) {
|
|
115
|
+
try {
|
|
116
|
+
(options.writeState ?? defaultWriteState)(statePath, `${JSON.stringify({ latestVersion: availability.latestVersion, notifiedAt: now.toISOString() }, null, 2)}\n`);
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
// The advisory remains useful if its best-effort throttle state cannot persist.
|
|
120
|
+
}
|
|
106
121
|
}
|
|
107
122
|
return { lines: [...lines, message] };
|
|
108
123
|
}
|
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { PROJECTION_CONTAINMENT_REFUSED_EXIT_CODE, ProjectionContainmentError, } from "../fs/projection-containment.js";
|
|
5
|
-
import { getPlatformCapabilities } from "../intake/platform-capabilities.js";
|
|
6
5
|
import { EXIT_CONFIG_ERROR, EXIT_OK } from "./constants.js";
|
|
7
|
-
import {
|
|
6
|
+
import { HARNESS_BOUND_PROVIDERS, ROUTING_MODE_HARNESS_DEFAULT, ROUTING_MODE_PINNED, resolveDispatchProvider, resolveRoutingPath, SWARM_WORKER_ROLES, writeModelDecision, } from "./routing.js";
|
|
8
7
|
export function routingSetMain(argv = process.argv.slice(2)) {
|
|
9
8
|
let projectRoot = ".";
|
|
10
9
|
let provider = null;
|
|
@@ -47,14 +46,10 @@ export function routingSetMain(argv = process.argv.slice(2)) {
|
|
|
47
46
|
}
|
|
48
47
|
let resolvedProvider = provider;
|
|
49
48
|
if (resolvedProvider === null || resolvedProvider.length === 0) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
catch {
|
|
55
|
-
runtimeMode = "";
|
|
56
|
-
}
|
|
57
|
-
resolvedProvider = dispatchProviderFromRuntime(runtimeMode);
|
|
49
|
+
// Same key as launch + verify:routing: OPENCLAW / sessions_spawn → openclaw
|
|
50
|
+
// (#2875 Greptile P1). Do not map via runtimeMode alone — OpenClaw-only envs
|
|
51
|
+
// are often local-unsandboxed while still dispatching under provider openclaw.
|
|
52
|
+
resolvedProvider = resolveDispatchProvider(process.env);
|
|
58
53
|
}
|
|
59
54
|
if (harnessDefault) {
|
|
60
55
|
if (model !== null) {
|
package/dist/swarm/routing.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export declare const ROUTING_MODE_HARNESS_DEFAULT = "harness-default";
|
|
|
9
9
|
export declare const ROUTING_FILENAME = "routing.local.json";
|
|
10
10
|
/** Providers whose model is harness-bound -- deft cannot pin or verify a slug. */
|
|
11
11
|
export declare const HARNESS_BOUND_PROVIDERS: Set<string>;
|
|
12
|
-
/** Providers whose per-role model must be decided before sub-agent dispatch (#1739 / #1877). */
|
|
12
|
+
/** Providers whose per-role model must be decided before sub-agent dispatch (#1739 / #1877 / #2875). */
|
|
13
13
|
export declare const ROUTING_GATED_DISPATCH_PROVIDERS: Set<string>;
|
|
14
14
|
export interface RouteDecision {
|
|
15
15
|
model: string | null;
|
|
@@ -54,7 +54,8 @@ export declare function dispatchProviderFromRuntime(runtimeMode: string): string
|
|
|
54
54
|
* Resolve the `dispatch_provider` routing key from the active runtime envelope.
|
|
55
55
|
* Separate from `runtime_mode` (#1557): Cursor sessions may carry
|
|
56
56
|
* `runtime_mode=cloud-headless` for gh-auth purposes but route under provider
|
|
57
|
-
* `cursor` for model selection (#1877).
|
|
57
|
+
* `cursor` for model selection (#1877). OpenClaw routes under `openclaw` when
|
|
58
|
+
* `sessions_spawn` / OPENCLAW signals are present (#2875).
|
|
58
59
|
*/
|
|
59
60
|
export declare function resolveDispatchProvider(environ?: NodeJS.ProcessEnv): string;
|
|
60
61
|
/**
|
package/dist/swarm/routing.js
CHANGED
|
@@ -32,8 +32,8 @@ export const ROUTING_MODE_HARNESS_DEFAULT = "harness-default";
|
|
|
32
32
|
export const ROUTING_FILENAME = "routing.local.json";
|
|
33
33
|
/** Providers whose model is harness-bound -- deft cannot pin or verify a slug. */
|
|
34
34
|
export const HARNESS_BOUND_PROVIDERS = new Set(["grok"]);
|
|
35
|
-
/** Providers whose per-role model must be decided before sub-agent dispatch (#1739 / #1877). */
|
|
36
|
-
export const ROUTING_GATED_DISPATCH_PROVIDERS = new Set(["cursor", "grok"]);
|
|
35
|
+
/** Providers whose per-role model must be decided before sub-agent dispatch (#1739 / #1877 / #2875). */
|
|
36
|
+
export const ROUTING_GATED_DISPATCH_PROVIDERS = new Set(["cursor", "grok", "openclaw"]);
|
|
37
37
|
const TRUTHY_ENV = new Set(["1", "true", "yes", "on"]);
|
|
38
38
|
function envTruthy(environ, name) {
|
|
39
39
|
return TRUTHY_ENV.has((environ[name] ?? "").trim().toLowerCase());
|
|
@@ -149,6 +149,9 @@ export function dispatchProviderFromRuntime(runtimeMode) {
|
|
|
149
149
|
if (normalized.length === 0) {
|
|
150
150
|
return "unknown";
|
|
151
151
|
}
|
|
152
|
+
if (normalized.includes("openclaw")) {
|
|
153
|
+
return "openclaw";
|
|
154
|
+
}
|
|
152
155
|
if (normalized.includes("grok")) {
|
|
153
156
|
return "grok";
|
|
154
157
|
}
|
|
@@ -161,13 +164,20 @@ export function dispatchProviderFromRuntime(runtimeMode) {
|
|
|
161
164
|
* Resolve the `dispatch_provider` routing key from the active runtime envelope.
|
|
162
165
|
* Separate from `runtime_mode` (#1557): Cursor sessions may carry
|
|
163
166
|
* `runtime_mode=cloud-headless` for gh-auth purposes but route under provider
|
|
164
|
-
* `cursor` for model selection (#1877).
|
|
167
|
+
* `cursor` for model selection (#1877). OpenClaw routes under `openclaw` when
|
|
168
|
+
* `sessions_spawn` / OPENCLAW signals are present (#2875).
|
|
165
169
|
*/
|
|
166
170
|
export function resolveDispatchProvider(environ = process.env) {
|
|
167
171
|
if (envTruthy(environ, "CURSOR_COMPOSER") || envTruthy(environ, "CURSOR_AGENT")) {
|
|
168
172
|
return "cursor";
|
|
169
173
|
}
|
|
170
174
|
const runtime = (environ.DEFT_AGENT_RUNTIME ?? "").trim().toLowerCase();
|
|
175
|
+
if (envTruthy(environ, "OPENCLAW") ||
|
|
176
|
+
envTruthy(environ, "DEFT_HAS_SESSIONS_SPAWN") ||
|
|
177
|
+
envTruthy(environ, "DEFT_PROBE_SESSIONS_SPAWN") ||
|
|
178
|
+
runtime === "openclaw") {
|
|
179
|
+
return "openclaw";
|
|
180
|
+
}
|
|
171
181
|
if (envTruthy(environ, "GROK_BUILD") || runtime === "grok-build") {
|
|
172
182
|
return "grok";
|
|
173
183
|
}
|
|
@@ -178,7 +188,9 @@ export function resolveDispatchProvider(environ = process.env) {
|
|
|
178
188
|
envTruthy(environ, "BUILDKITE") ||
|
|
179
189
|
(envTruthy(environ, "CI") &&
|
|
180
190
|
!envTruthy(environ, "CURSOR_COMPOSER") &&
|
|
181
|
-
!envTruthy(environ, "CURSOR_AGENT")
|
|
191
|
+
!envTruthy(environ, "CURSOR_AGENT") &&
|
|
192
|
+
!envTruthy(environ, "OPENCLAW") &&
|
|
193
|
+
!envTruthy(environ, "DEFT_HAS_SESSIONS_SPAWN"))) {
|
|
182
194
|
return "cloud-headless";
|
|
183
195
|
}
|
|
184
196
|
return "unknown";
|
|
@@ -135,13 +135,13 @@ export declare const registryData: {
|
|
|
135
135
|
};
|
|
136
136
|
readonly "task triage:show": {
|
|
137
137
|
readonly name: "task triage:show";
|
|
138
|
-
readonly summary: "Per-issue detail
|
|
139
|
-
readonly refs: "(D11 / #1128)";
|
|
140
|
-
readonly description: "Per-issue read-only detail (cached upstream payload + latest triage decision + audit timeline).
|
|
141
|
-
readonly usage: "task triage:show -- <N> [--repo=owner/name]";
|
|
142
|
-
readonly flags: readonly [readonly ["<N>", "(required)", "Issue number (positional)."], readonly ["--repo owner/name", "(git remote)", "Explicit repo override."]];
|
|
143
|
-
readonly examples: readonly ["task triage:show -- 42", "task triage:show -- 42 --repo deftai/directive"];
|
|
144
|
-
readonly see_also: readonly ["task triage:queue", "task triage:status", "#1119 / D11"];
|
|
138
|
+
readonly summary: "Per-issue detail + optional operator brief";
|
|
139
|
+
readonly refs: "(D11 / #1128, #2890)";
|
|
140
|
+
readonly description: "Per-issue read-only detail (cached upstream payload + latest triage decision + audit timeline). --format=operator emits a pasteable Phase 3 candidate brief backbone (title/link/labels/summary/AC/latest decision/active-xBRIEF); agent still owns lean. Exit 0 on hit, 1 on cache miss.";
|
|
141
|
+
readonly usage: "task triage:show -- <N> [--format=default|operator] [--repo=owner/name]";
|
|
142
|
+
readonly flags: readonly [readonly ["<N>", "(required)", "Issue number (positional)."], readonly ["--format default|operator", "default", "default = audit/cache detail; operator = Phase 3 pasteable brief (#2890)."], readonly ["--repo owner/name", "(git remote)", "Explicit repo override."]];
|
|
143
|
+
readonly examples: readonly ["task triage:show -- 42", "task triage:show -- 42 --format=operator", "task triage:show -- 42 --repo deftai/directive"];
|
|
144
|
+
readonly see_also: readonly ["task triage:queue", "task triage:status", "#1119 / D11", "#2890"];
|
|
145
145
|
readonly placeholder: false;
|
|
146
146
|
};
|
|
147
147
|
readonly "task triage:scope": {
|
|
@@ -209,16 +209,25 @@ export const registryData = {
|
|
|
209
209
|
},
|
|
210
210
|
"task triage:show": {
|
|
211
211
|
name: "task triage:show",
|
|
212
|
-
summary: "Per-issue detail
|
|
213
|
-
refs: "(D11 / #1128)",
|
|
214
|
-
description: "Per-issue read-only detail (cached upstream payload + latest triage decision + audit timeline).
|
|
215
|
-
usage: "task triage:show -- <N> [--repo=owner/name]",
|
|
212
|
+
summary: "Per-issue detail + optional operator brief",
|
|
213
|
+
refs: "(D11 / #1128, #2890)",
|
|
214
|
+
description: "Per-issue read-only detail (cached upstream payload + latest triage decision + audit timeline). --format=operator emits a pasteable Phase 3 candidate brief backbone (title/link/labels/summary/AC/latest decision/active-xBRIEF); agent still owns lean. Exit 0 on hit, 1 on cache miss.",
|
|
215
|
+
usage: "task triage:show -- <N> [--format=default|operator] [--repo=owner/name]",
|
|
216
216
|
flags: [
|
|
217
217
|
["<N>", "(required)", "Issue number (positional)."],
|
|
218
|
+
[
|
|
219
|
+
"--format default|operator",
|
|
220
|
+
"default",
|
|
221
|
+
"default = audit/cache detail; operator = Phase 3 pasteable brief (#2890).",
|
|
222
|
+
],
|
|
218
223
|
["--repo owner/name", "(git remote)", "Explicit repo override."],
|
|
219
224
|
],
|
|
220
|
-
examples: [
|
|
221
|
-
|
|
225
|
+
examples: [
|
|
226
|
+
"task triage:show -- 42",
|
|
227
|
+
"task triage:show -- 42 --format=operator",
|
|
228
|
+
"task triage:show -- 42 --repo deftai/directive",
|
|
229
|
+
],
|
|
230
|
+
see_also: ["task triage:queue", "task triage:status", "#1119 / D11", "#2890"],
|
|
222
231
|
placeholder: false,
|
|
223
232
|
},
|
|
224
233
|
"task triage:scope": {
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* triage:show default + operator brief renderers (#1128 / #2890).
|
|
3
|
+
*
|
|
4
|
+
* Default format mirrors the pre-Python-removal `render_show` surface.
|
|
5
|
+
* `--format=operator` emits a pasteable Phase 3 candidate brief backbone;
|
|
6
|
+
* the agent still owns lean (not invented here).
|
|
7
|
+
*/
|
|
8
|
+
/** Loose audit row for show renderers (actions + queue shapes both work). */
|
|
9
|
+
export type ShowAuditRow = {
|
|
10
|
+
readonly decision?: string;
|
|
11
|
+
readonly timestamp?: string;
|
|
12
|
+
readonly actor?: string;
|
|
13
|
+
readonly reason?: string;
|
|
14
|
+
readonly issue_number?: number;
|
|
15
|
+
readonly repo?: string;
|
|
16
|
+
};
|
|
17
|
+
/** One cached issue with body fields needed by show/operator formats. */
|
|
18
|
+
export interface CachedIssueDetail {
|
|
19
|
+
readonly number: number;
|
|
20
|
+
readonly title: string;
|
|
21
|
+
readonly state: string;
|
|
22
|
+
readonly labels: readonly string[];
|
|
23
|
+
readonly updatedAt: string;
|
|
24
|
+
readonly body: string;
|
|
25
|
+
readonly htmlUrl: string | null;
|
|
26
|
+
}
|
|
27
|
+
/** Collapse CR/LF so cached attacker text cannot break markdown bullets (P2). */
|
|
28
|
+
export declare function oneLine(value: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* Resolve a safe issue link. Always construct the canonical github.com path for
|
|
31
|
+
* `owner/name#N` rather than trusting payload URL substrings (CodeQL
|
|
32
|
+
* incomplete-url-substring-sanitization).
|
|
33
|
+
*/
|
|
34
|
+
export declare function resolveIssueHtmlUrl(repo: string, number: number): string;
|
|
35
|
+
/** Load a single cached issue (include closed) or null on miss. */
|
|
36
|
+
export declare function loadCachedIssueDetail(repo: string, number: number, options?: {
|
|
37
|
+
readonly projectRoot: string;
|
|
38
|
+
/** Absolute/relative path to `.deft-cache` root (CLI `--cache-root`). */
|
|
39
|
+
readonly cacheRoot?: string | null;
|
|
40
|
+
readonly source?: string;
|
|
41
|
+
}): CachedIssueDetail | null;
|
|
42
|
+
/** Default triage:show text (audit/cache oriented). */
|
|
43
|
+
export declare function renderShow(options: {
|
|
44
|
+
readonly issue: CachedIssueDetail | null;
|
|
45
|
+
readonly repo: string;
|
|
46
|
+
readonly number: number;
|
|
47
|
+
readonly latestDecision: ShowAuditRow | null;
|
|
48
|
+
readonly history: readonly ShowAuditRow[];
|
|
49
|
+
readonly inActiveXbrief: boolean;
|
|
50
|
+
}): string;
|
|
51
|
+
/**
|
|
52
|
+
* Extract a short problem/context summary (2–5 lines) from issue body.
|
|
53
|
+
* Prefers text before the first `##` section; falls back to leading paragraphs.
|
|
54
|
+
*/
|
|
55
|
+
export declare function extractBodySummary(body: string, maxLines?: number): string;
|
|
56
|
+
/**
|
|
57
|
+
* Extract acceptance-criteria bullets from body, or a thin-body note.
|
|
58
|
+
* Looks for AC / Acceptance headings and checkbox / bullet lists under them.
|
|
59
|
+
*/
|
|
60
|
+
export declare function extractAcceptanceCriteria(body: string): readonly string[];
|
|
61
|
+
/** Operator-facing pasteable brief backbone for Phase 3 decisions (#2890). */
|
|
62
|
+
export declare function renderOperatorBrief(options: {
|
|
63
|
+
readonly issue: CachedIssueDetail | null;
|
|
64
|
+
readonly repo: string;
|
|
65
|
+
readonly number: number;
|
|
66
|
+
readonly latestDecision: ShowAuditRow | null;
|
|
67
|
+
readonly inActiveXbrief: boolean;
|
|
68
|
+
}): string;
|
|
69
|
+
//# sourceMappingURL=show.d.ts.map
|