@useorgx/openclaw-plugin 0.4.8 → 0.7.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/README.md +35 -0
- package/dashboard/dist/assets/BJgZIVUQ.js +53 -0
- package/dashboard/dist/assets/BJgZIVUQ.js.br +0 -0
- package/dashboard/dist/assets/BJgZIVUQ.js.gz +0 -0
- package/dashboard/dist/assets/BXWDRGm-.js +1 -0
- package/dashboard/dist/assets/BXWDRGm-.js.br +0 -0
- package/dashboard/dist/assets/BXWDRGm-.js.gz +0 -0
- package/dashboard/dist/assets/BgOYB78t.js +4 -0
- package/dashboard/dist/assets/BgOYB78t.js.br +0 -0
- package/dashboard/dist/assets/BgOYB78t.js.gz +0 -0
- package/dashboard/dist/assets/C-KIc3Wc.js.br +0 -0
- package/dashboard/dist/assets/C-KIc3Wc.js.gz +0 -0
- package/dashboard/dist/assets/CE38zU4U.js +1 -0
- package/dashboard/dist/assets/CE38zU4U.js.br +0 -0
- package/dashboard/dist/assets/CE38zU4U.js.gz +0 -0
- package/dashboard/dist/assets/CFGKRAzG.js +1 -0
- package/dashboard/dist/assets/CFGKRAzG.js.br +0 -0
- package/dashboard/dist/assets/CFGKRAzG.js.gz +0 -0
- package/dashboard/dist/assets/CGGR2GZh.js +1 -0
- package/dashboard/dist/assets/CGGR2GZh.js.br +0 -0
- package/dashboard/dist/assets/CGGR2GZh.js.gz +0 -0
- package/dashboard/dist/assets/CL_wXqR7.js +1 -0
- package/dashboard/dist/assets/CL_wXqR7.js.br +0 -0
- package/dashboard/dist/assets/CL_wXqR7.js.gz +0 -0
- package/dashboard/dist/assets/CPFiTmlw.js +8 -0
- package/dashboard/dist/assets/CPFiTmlw.js.br +0 -0
- package/dashboard/dist/assets/CPFiTmlw.js.gz +0 -0
- package/dashboard/dist/assets/CZZTvkQZ.js +1 -0
- package/dashboard/dist/assets/CZZTvkQZ.js.br +0 -0
- package/dashboard/dist/assets/CZZTvkQZ.js.gz +0 -0
- package/dashboard/dist/assets/{CpJsfbXo.js → CxQ08qFN.js} +2 -2
- package/dashboard/dist/assets/CxQ08qFN.js.br +0 -0
- package/dashboard/dist/assets/CxQ08qFN.js.gz +0 -0
- package/dashboard/dist/assets/D-bf6hEI.js +213 -0
- package/dashboard/dist/assets/D-bf6hEI.js.br +0 -0
- package/dashboard/dist/assets/D-bf6hEI.js.gz +0 -0
- package/dashboard/dist/assets/DG6y9wJI.js +2 -0
- package/dashboard/dist/assets/DG6y9wJI.js.br +0 -0
- package/dashboard/dist/assets/DG6y9wJI.js.gz +0 -0
- package/dashboard/dist/assets/DNxKz-GV.js +1 -0
- package/dashboard/dist/assets/DNxKz-GV.js.br +0 -0
- package/dashboard/dist/assets/DNxKz-GV.js.gz +0 -0
- package/dashboard/dist/assets/DW_rKUic.js +11 -0
- package/dashboard/dist/assets/DW_rKUic.js.br +0 -0
- package/dashboard/dist/assets/DW_rKUic.js.gz +0 -0
- package/dashboard/dist/assets/DbNoijHm.js +1 -0
- package/dashboard/dist/assets/DbNoijHm.js.br +0 -0
- package/dashboard/dist/assets/DbNoijHm.js.gz +0 -0
- package/dashboard/dist/assets/DjcdE6jC.js +2 -0
- package/dashboard/dist/assets/DjcdE6jC.js.br +0 -0
- package/dashboard/dist/assets/DjcdE6jC.js.gz +0 -0
- package/dashboard/dist/assets/FZYuCDnt.js +1 -0
- package/dashboard/dist/assets/FZYuCDnt.js.br +0 -0
- package/dashboard/dist/assets/FZYuCDnt.js.gz +0 -0
- package/dashboard/dist/assets/PAUiij_z.js +1 -0
- package/dashboard/dist/assets/PAUiij_z.js.br +0 -0
- package/dashboard/dist/assets/PAUiij_z.js.gz +0 -0
- package/dashboard/dist/assets/cNrhgGc1.js +8 -0
- package/dashboard/dist/assets/cNrhgGc1.js.br +0 -0
- package/dashboard/dist/assets/cNrhgGc1.js.gz +0 -0
- package/dashboard/dist/assets/h5biQs2I.css +1 -0
- package/dashboard/dist/assets/h5biQs2I.css.br +0 -0
- package/dashboard/dist/assets/h5biQs2I.css.gz +0 -0
- package/dashboard/dist/assets/ic2FaMnh.js +1 -0
- package/dashboard/dist/assets/ic2FaMnh.js.br +0 -0
- package/dashboard/dist/assets/ic2FaMnh.js.gz +0 -0
- package/dashboard/dist/assets/nByHNHoW.js +1 -0
- package/dashboard/dist/assets/nByHNHoW.js.br +0 -0
- package/dashboard/dist/assets/nByHNHoW.js.gz +0 -0
- package/dashboard/dist/assets/qm8xLgv-.css +1 -0
- package/dashboard/dist/assets/qm8xLgv-.css.br +0 -0
- package/dashboard/dist/assets/qm8xLgv-.css.gz +0 -0
- package/dashboard/dist/assets/tS9mbYZi.js +1 -0
- package/dashboard/dist/assets/tS9mbYZi.js.br +0 -0
- package/dashboard/dist/assets/tS9mbYZi.js.gz +0 -0
- package/dashboard/dist/brand/anthropic-mark.svg.br +0 -0
- package/dashboard/dist/brand/anthropic-mark.svg.gz +0 -0
- package/dashboard/dist/brand/openai-mark.svg.br +0 -0
- package/dashboard/dist/brand/openai-mark.svg.gz +0 -0
- package/dashboard/dist/brand/openclaw-mark.svg.br +0 -0
- package/dashboard/dist/brand/openclaw-mark.svg.gz +0 -0
- package/dashboard/dist/brand/xandy-orchestrator.png +0 -0
- package/dashboard/dist/index.html +7 -5
- package/dashboard/dist/index.html.br +0 -0
- package/dashboard/dist/index.html.gz +0 -0
- package/dist/activity-actor-fields.js +26 -4
- package/dist/activity-store.js +38 -26
- package/dist/agent-context-store.js +84 -42
- package/dist/agent-run-store.js +49 -28
- package/dist/agent-suite.d.ts +9 -0
- package/dist/agent-suite.js +150 -17
- package/dist/artifacts/artifact-domain-schemas.d.ts +66 -0
- package/dist/artifacts/artifact-domain-schemas.js +357 -0
- package/dist/artifacts/register-artifact.d.ts +4 -3
- package/dist/artifacts/register-artifact.js +170 -57
- package/dist/auth/flows.d.ts +47 -0
- package/dist/auth/flows.js +169 -0
- package/dist/auth-store.js +6 -26
- package/dist/byok-store.js +5 -19
- package/dist/chat-store.d.ts +157 -0
- package/dist/chat-store.js +586 -0
- package/dist/cli/orgx.d.ts +66 -0
- package/dist/cli/orgx.js +102 -0
- package/dist/config/refresh.d.ts +32 -0
- package/dist/config/refresh.js +55 -0
- package/dist/config/resolution.d.ts +37 -0
- package/dist/config/resolution.js +178 -0
- package/dist/contracts/client.d.ts +43 -3
- package/dist/contracts/client.js +159 -30
- package/dist/contracts/retro-schema.d.ts +81 -0
- package/dist/contracts/retro-schema.js +80 -0
- package/dist/contracts/shared-types.d.ts +306 -0
- package/dist/contracts/shared-types.js +179 -0
- package/dist/contracts/skill-pack-schema.d.ts +192 -0
- package/dist/contracts/skill-pack-schema.js +180 -0
- package/dist/contracts/types.d.ts +224 -132
- package/dist/contracts/types.js +5 -0
- package/dist/entities/auto-assignment.d.ts +36 -0
- package/dist/entities/auto-assignment.js +141 -0
- package/dist/entity-comment-store.js +5 -25
- package/dist/event-sanitization.d.ts +11 -0
- package/dist/event-sanitization.js +113 -0
- package/dist/fs-utils.js +13 -1
- package/dist/gateway-watchdog.d.ts +5 -0
- package/dist/gateway-watchdog.js +50 -0
- package/dist/hash-utils.d.ts +2 -0
- package/dist/hash-utils.js +12 -0
- package/dist/hooks/post-reporting-event.mjs +1 -5
- package/dist/http/helpers/activity-headline.d.ts +10 -0
- package/dist/http/helpers/activity-headline.js +73 -0
- package/dist/http/helpers/artifact-fallback.d.ts +13 -0
- package/dist/http/helpers/artifact-fallback.js +148 -0
- package/dist/http/helpers/auto-continue-engine.d.ts +486 -0
- package/dist/http/helpers/auto-continue-engine.js +3563 -0
- package/dist/http/helpers/autopilot-operations.d.ts +176 -0
- package/dist/http/helpers/autopilot-operations.js +554 -0
- package/dist/http/helpers/autopilot-runtime.d.ts +43 -0
- package/dist/http/helpers/autopilot-runtime.js +607 -0
- package/dist/http/helpers/autopilot-slice-utils.d.ts +56 -0
- package/dist/http/helpers/autopilot-slice-utils.js +899 -0
- package/dist/http/helpers/decision-mapper.d.ts +52 -0
- package/dist/http/helpers/decision-mapper.js +260 -0
- package/dist/http/helpers/dispatch-lifecycle.d.ts +119 -0
- package/dist/http/helpers/dispatch-lifecycle.js +809 -0
- package/dist/http/helpers/hash-utils.d.ts +1 -0
- package/dist/http/helpers/hash-utils.js +1 -0
- package/dist/http/helpers/kickoff-context.d.ts +12 -0
- package/dist/http/helpers/kickoff-context.js +228 -0
- package/dist/http/helpers/llm-client.d.ts +47 -0
- package/dist/http/helpers/llm-client.js +256 -0
- package/dist/http/helpers/mission-control.d.ts +193 -0
- package/dist/http/helpers/mission-control.js +1383 -0
- package/dist/http/helpers/openclaw-cli.d.ts +37 -0
- package/dist/http/helpers/openclaw-cli.js +283 -0
- package/dist/http/helpers/runtime-sse.d.ts +20 -0
- package/dist/http/helpers/runtime-sse.js +110 -0
- package/dist/http/helpers/sentinel-catalog.d.ts +23 -0
- package/dist/http/helpers/sentinel-catalog.js +193 -0
- package/dist/http/helpers/session-classification.d.ts +9 -0
- package/dist/http/helpers/session-classification.js +564 -0
- package/dist/http/helpers/slice-experience-v2.d.ts +137 -0
- package/dist/http/helpers/slice-experience-v2.js +677 -0
- package/dist/http/helpers/slice-run-projections.d.ts +72 -0
- package/dist/http/helpers/slice-run-projections.js +860 -0
- package/dist/http/helpers/triage-mapper.d.ts +43 -0
- package/dist/http/helpers/triage-mapper.js +549 -0
- package/dist/http/helpers/value-utils.d.ts +6 -0
- package/dist/http/helpers/value-utils.js +72 -0
- package/dist/http/helpers/workspace-scope.d.ts +15 -0
- package/dist/http/helpers/workspace-scope.js +170 -0
- package/dist/http/index.d.ts +88 -0
- package/dist/http/index.js +3610 -0
- package/dist/http/router.d.ts +23 -0
- package/dist/http/router.js +23 -0
- package/dist/http/routes/agent-control.d.ts +79 -0
- package/dist/http/routes/agent-control.js +684 -0
- package/dist/http/routes/agent-suite.d.ts +38 -0
- package/dist/http/routes/agent-suite.js +397 -0
- package/dist/http/routes/agents-catalog.d.ts +40 -0
- package/dist/http/routes/agents-catalog.js +128 -0
- package/dist/http/routes/billing.d.ts +23 -0
- package/dist/http/routes/billing.js +55 -0
- package/dist/http/routes/chat.d.ts +19 -0
- package/dist/http/routes/chat.js +522 -0
- package/dist/http/routes/debug.d.ts +14 -0
- package/dist/http/routes/debug.js +21 -0
- package/dist/http/routes/decision-actions.d.ts +20 -0
- package/dist/http/routes/decision-actions.js +103 -0
- package/dist/http/routes/delegation.d.ts +19 -0
- package/dist/http/routes/delegation.js +32 -0
- package/dist/http/routes/dispatch-gateway-envelope.d.ts +25 -0
- package/dist/http/routes/dispatch-gateway-envelope.js +26 -0
- package/dist/http/routes/entities.d.ts +63 -0
- package/dist/http/routes/entities.js +440 -0
- package/dist/http/routes/entity-dynamic.d.ts +25 -0
- package/dist/http/routes/entity-dynamic.js +191 -0
- package/dist/http/routes/health.d.ts +22 -0
- package/dist/http/routes/health.js +49 -0
- package/dist/http/routes/live-legacy.d.ts +115 -0
- package/dist/http/routes/live-legacy.js +112 -0
- package/dist/http/routes/live-misc.d.ts +81 -0
- package/dist/http/routes/live-misc.js +426 -0
- package/dist/http/routes/live-snapshot.d.ts +136 -0
- package/dist/http/routes/live-snapshot.js +916 -0
- package/dist/http/routes/live-terminal.d.ts +11 -0
- package/dist/http/routes/live-terminal.js +261 -0
- package/dist/http/routes/live-triage.d.ts +61 -0
- package/dist/http/routes/live-triage.js +248 -0
- package/dist/http/routes/mission-control-actions.d.ts +131 -0
- package/dist/http/routes/mission-control-actions.js +1791 -0
- package/dist/http/routes/mission-control-read.d.ts +73 -0
- package/dist/http/routes/mission-control-read.js +1640 -0
- package/dist/http/routes/onboarding.d.ts +34 -0
- package/dist/http/routes/onboarding.js +101 -0
- package/dist/http/routes/realtime-orchestrator.d.ts +10 -0
- package/dist/http/routes/realtime-orchestrator.js +74 -0
- package/dist/http/routes/run-control.d.ts +27 -0
- package/dist/http/routes/run-control.js +96 -0
- package/dist/http/routes/runtime-hooks.d.ts +69 -0
- package/dist/http/routes/runtime-hooks.js +437 -0
- package/dist/http/routes/sentinels-catalog.d.ts +7 -0
- package/dist/http/routes/sentinels-catalog.js +24 -0
- package/dist/http/routes/settings-byok.d.ts +23 -0
- package/dist/http/routes/settings-byok.js +163 -0
- package/dist/http/routes/summary.d.ts +18 -0
- package/dist/http/routes/summary.js +49 -0
- package/dist/http/routes/usage.d.ts +24 -0
- package/dist/http/routes/usage.js +362 -0
- package/dist/http/routes/work-artifacts.d.ts +9 -0
- package/dist/http/routes/work-artifacts.js +55 -0
- package/dist/http/shared-state.d.ts +16 -0
- package/dist/http/shared-state.js +1 -0
- package/dist/http-handler.d.ts +1 -88
- package/dist/http-handler.js +1 -10605
- package/dist/index.js +287 -2284
- package/dist/json-utils.d.ts +1 -0
- package/dist/json-utils.js +8 -0
- package/dist/local-openclaw.js +29 -6
- package/dist/mcp-client-setup.js +3 -3
- package/dist/mcp-http-handler.js +33 -59
- package/dist/next-up-queue-store.d.ts +16 -1
- package/dist/next-up-queue-store.js +93 -25
- package/dist/outbox.d.ts +5 -0
- package/dist/outbox.js +113 -9
- package/dist/paths.js +24 -5
- package/dist/reporting/rollups.d.ts +53 -0
- package/dist/reporting/rollups.js +148 -0
- package/dist/retro/domain-templates.d.ts +45 -0
- package/dist/retro/domain-templates.js +297 -0
- package/dist/retro/quality-rubric.d.ts +33 -0
- package/dist/retro/quality-rubric.js +213 -0
- package/dist/runtime-cleanup.d.ts +18 -0
- package/dist/runtime-cleanup.js +87 -0
- package/dist/runtime-instance-store.js +5 -31
- package/dist/services/background.d.ts +34 -0
- package/dist/services/background.js +45 -0
- package/dist/services/experiment-randomization.d.ts +21 -0
- package/dist/services/experiment-randomization.js +63 -0
- package/dist/services/instrumentation.d.ts +29 -0
- package/dist/services/instrumentation.js +136 -0
- package/dist/skill-pack-state.d.ts +36 -5
- package/dist/skill-pack-state.js +273 -29
- package/dist/snapshot-store.js +5 -25
- package/dist/stores/json-store.d.ts +11 -0
- package/dist/stores/json-store.js +42 -0
- package/dist/sync/local-agent-telemetry.d.ts +13 -0
- package/dist/sync/local-agent-telemetry.js +128 -0
- package/dist/sync/outbox-replay.d.ts +55 -0
- package/dist/sync/outbox-replay.js +621 -0
- package/dist/team-context-store.d.ts +23 -0
- package/dist/team-context-store.js +116 -0
- package/dist/telemetry/posthog.js +4 -2
- package/dist/tools/core-tools.d.ts +72 -0
- package/dist/tools/core-tools.js +2270 -0
- package/dist/types.d.ts +2 -0
- package/dist/types.js +2 -0
- package/dist/worker-supervisor.js +23 -0
- package/package.json +14 -4
- package/dashboard/dist/assets/B3ziCA02.js +0 -8
- package/dashboard/dist/assets/BNeJ0kpF.js +0 -1
- package/dashboard/dist/assets/BzkiMPmM.js +0 -215
- package/dashboard/dist/assets/CUV9IHHi.js +0 -1
- package/dashboard/dist/assets/Ie7d9Iq2.css +0 -1
- package/dashboard/dist/assets/sAhvFnpk.js +0 -4
|
@@ -0,0 +1,899 @@
|
|
|
1
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync, } from "node:fs";
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { dirname, join, sep } from "node:path";
|
|
5
|
+
import { writeFileAtomicSync } from "../../fs-utils.js";
|
|
6
|
+
import { parseJsonSafe } from "../../json-utils.js";
|
|
7
|
+
import { getOrgxPluginConfigDir } from "../../paths.js";
|
|
8
|
+
function ensurePrivateDirForFile(pathname) {
|
|
9
|
+
const dir = dirname(pathname);
|
|
10
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
11
|
+
try {
|
|
12
|
+
chmodSync(dir, 0o700);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
// best effort
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function autopilotSliceSchema() {
|
|
19
|
+
// Strict enough to keep outputs predictable, but tolerant of older agents.
|
|
20
|
+
const artifactProperties = {
|
|
21
|
+
name: { type: "string", minLength: 1 },
|
|
22
|
+
artifact_type: {
|
|
23
|
+
type: "string",
|
|
24
|
+
enum: ["pr", "commit", "document", "config", "report", "design", "retro", "other"],
|
|
25
|
+
},
|
|
26
|
+
confidence_score: { type: ["number", "null"], minimum: 0, maximum: 1 },
|
|
27
|
+
description: { type: ["string", "null"] },
|
|
28
|
+
url: { type: ["string", "null"] },
|
|
29
|
+
verification_steps: { type: ["array", "null"], items: { type: "string" } },
|
|
30
|
+
milestone_id: { type: ["string", "null"] },
|
|
31
|
+
task_ids: { type: ["array", "null"], items: { type: "string" } },
|
|
32
|
+
};
|
|
33
|
+
const decisionProperties = {
|
|
34
|
+
question: { type: "string", minLength: 1 },
|
|
35
|
+
summary: { type: ["string", "null"] },
|
|
36
|
+
options: { type: ["array", "null"], items: { type: "string" } },
|
|
37
|
+
urgency: {
|
|
38
|
+
type: ["string", "null"],
|
|
39
|
+
enum: ["low", "medium", "high", "urgent", null],
|
|
40
|
+
},
|
|
41
|
+
blocking: { type: "boolean" },
|
|
42
|
+
};
|
|
43
|
+
const skillEvidenceProperties = {
|
|
44
|
+
skill: { type: "string", minLength: 1 },
|
|
45
|
+
skill_file: { type: ["string", "null"] },
|
|
46
|
+
skill_sha256: { type: ["string", "null"] },
|
|
47
|
+
skill_heading: { type: ["string", "null"] },
|
|
48
|
+
};
|
|
49
|
+
const taskUpdateProperties = {
|
|
50
|
+
task_id: { type: "string", minLength: 1 },
|
|
51
|
+
status: { type: "string", enum: ["todo", "in_progress", "done", "blocked"] },
|
|
52
|
+
reason: { type: ["string", "null"] },
|
|
53
|
+
};
|
|
54
|
+
const milestoneUpdateProperties = {
|
|
55
|
+
milestone_id: { type: "string", minLength: 1 },
|
|
56
|
+
status: {
|
|
57
|
+
type: "string",
|
|
58
|
+
enum: ["planned", "in_progress", "completed", "at_risk", "cancelled"],
|
|
59
|
+
},
|
|
60
|
+
reason: { type: ["string", "null"] },
|
|
61
|
+
};
|
|
62
|
+
const topLevelProperties = {
|
|
63
|
+
status: {
|
|
64
|
+
type: "string",
|
|
65
|
+
enum: ["completed", "blocked", "needs_decision", "error"],
|
|
66
|
+
},
|
|
67
|
+
summary: { type: "string", minLength: 1 },
|
|
68
|
+
workstream_id: { type: "string", minLength: 1 },
|
|
69
|
+
workstream_title: { type: ["string", "null"] },
|
|
70
|
+
slice_id: { type: ["string", "null"] },
|
|
71
|
+
artifacts: {
|
|
72
|
+
type: ["array", "null"],
|
|
73
|
+
items: {
|
|
74
|
+
type: "object",
|
|
75
|
+
additionalProperties: false,
|
|
76
|
+
required: Object.keys(artifactProperties),
|
|
77
|
+
properties: artifactProperties,
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
decisions_needed: {
|
|
81
|
+
type: ["array", "null"],
|
|
82
|
+
items: {
|
|
83
|
+
type: "object",
|
|
84
|
+
additionalProperties: false,
|
|
85
|
+
required: Object.keys(decisionProperties),
|
|
86
|
+
properties: decisionProperties,
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
skill_evidence: {
|
|
90
|
+
type: ["array", "null"],
|
|
91
|
+
items: {
|
|
92
|
+
type: "object",
|
|
93
|
+
additionalProperties: false,
|
|
94
|
+
required: Object.keys(skillEvidenceProperties),
|
|
95
|
+
properties: skillEvidenceProperties,
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
task_updates: {
|
|
99
|
+
type: ["array", "null"],
|
|
100
|
+
items: {
|
|
101
|
+
type: "object",
|
|
102
|
+
additionalProperties: false,
|
|
103
|
+
required: Object.keys(taskUpdateProperties),
|
|
104
|
+
properties: taskUpdateProperties,
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
milestone_updates: {
|
|
108
|
+
type: ["array", "null"],
|
|
109
|
+
items: {
|
|
110
|
+
type: "object",
|
|
111
|
+
additionalProperties: false,
|
|
112
|
+
required: Object.keys(milestoneUpdateProperties),
|
|
113
|
+
properties: milestoneUpdateProperties,
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
next_actions: { type: ["array", "null"], items: { type: "string" } },
|
|
117
|
+
};
|
|
118
|
+
return {
|
|
119
|
+
type: "object",
|
|
120
|
+
additionalProperties: false,
|
|
121
|
+
required: Object.keys(topLevelProperties),
|
|
122
|
+
properties: topLevelProperties,
|
|
123
|
+
// Keep schema within Codex structured-output subset (no combinators like allOf/if/then).
|
|
124
|
+
// Status/decision consistency is enforced by coordinator post-parse.
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
export function ensureAutopilotSliceSchemaPath(schemaFilename) {
|
|
128
|
+
const file = join(getOrgxPluginConfigDir(), schemaFilename);
|
|
129
|
+
const nextSchemaRaw = JSON.stringify(autopilotSliceSchema(), null, 2);
|
|
130
|
+
try {
|
|
131
|
+
if (existsSync(file)) {
|
|
132
|
+
try {
|
|
133
|
+
const existingRaw = readFileSync(file, "utf8").trim();
|
|
134
|
+
if (existingRaw === nextSchemaRaw)
|
|
135
|
+
return file;
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
// continue and rewrite
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
ensurePrivateDirForFile(file);
|
|
142
|
+
writeFileAtomicSync(file, nextSchemaRaw, { mode: 0o600 });
|
|
143
|
+
return file;
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
// Fall back to best-effort write.
|
|
147
|
+
try {
|
|
148
|
+
ensurePrivateDirForFile(file);
|
|
149
|
+
writeFileSync(file, `${nextSchemaRaw}\n`, {
|
|
150
|
+
encoding: "utf8",
|
|
151
|
+
mode: 0o600,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
// ignore
|
|
156
|
+
}
|
|
157
|
+
return file;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
export function parseSliceResult(raw) {
|
|
161
|
+
const allowedStatuses = new Set(["completed", "blocked", "needs_decision", "error"]);
|
|
162
|
+
const stripUtf8Bom = (text) => text.replace(/^\uFEFF/, "");
|
|
163
|
+
const extractMarkdownJsonFences = (text) => {
|
|
164
|
+
const matches = text.matchAll(/```(?:json)?\s*([\s\S]*?)\s*```/gi);
|
|
165
|
+
const fences = [];
|
|
166
|
+
for (const match of matches) {
|
|
167
|
+
const inner = typeof match[1] === "string" ? match[1].trim() : "";
|
|
168
|
+
if (inner.length > 0)
|
|
169
|
+
fences.push(inner);
|
|
170
|
+
}
|
|
171
|
+
return fences;
|
|
172
|
+
};
|
|
173
|
+
const stripMarkdownJsonFence = (text) => {
|
|
174
|
+
const fenceCount = text.match(/```/g)?.length ?? 0;
|
|
175
|
+
if (fenceCount !== 2)
|
|
176
|
+
return text;
|
|
177
|
+
const fenced = text.match(/^```(?:json)?\s*([\s\S]*?)\s*```$/i);
|
|
178
|
+
if (!fenced || typeof fenced[1] !== "string")
|
|
179
|
+
return text;
|
|
180
|
+
return fenced[1].trim();
|
|
181
|
+
};
|
|
182
|
+
const parseSliceJsonText = (text) => {
|
|
183
|
+
const normalized = stripUtf8Bom(stripMarkdownJsonFence(text.trim()));
|
|
184
|
+
const parsed = parseJsonSafe(normalized);
|
|
185
|
+
if (parsed && typeof parsed === "object" && isLikelySliceResult(parsed)) {
|
|
186
|
+
return normalizeSliceResult(parsed);
|
|
187
|
+
}
|
|
188
|
+
const fencedCandidates = extractMarkdownJsonFences(normalized);
|
|
189
|
+
for (let i = fencedCandidates.length - 1; i >= 0; i -= 1) {
|
|
190
|
+
const parsedInner = parseJsonSafe(stripUtf8Bom(fencedCandidates[i]));
|
|
191
|
+
if (parsedInner && typeof parsedInner === "object" && isLikelySliceResult(parsedInner)) {
|
|
192
|
+
return normalizeSliceResult(parsedInner);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return null;
|
|
196
|
+
};
|
|
197
|
+
const isLikelySliceResult = (value) => {
|
|
198
|
+
if (!value || typeof value !== "object")
|
|
199
|
+
return false;
|
|
200
|
+
const record = value;
|
|
201
|
+
const status = typeof record.status === "string" ? record.status : "";
|
|
202
|
+
const workstreamId = typeof record.workstream_id === "string" ? record.workstream_id : "";
|
|
203
|
+
const summary = typeof record.summary === "string" ? record.summary : "";
|
|
204
|
+
return (allowedStatuses.has(status) &&
|
|
205
|
+
summary.trim().length > 0 &&
|
|
206
|
+
workstreamId.trim().length > 0);
|
|
207
|
+
};
|
|
208
|
+
const normalizeSliceResult = (value) => {
|
|
209
|
+
const record = value;
|
|
210
|
+
const status = typeof record.status === "string" ? record.status : "";
|
|
211
|
+
const decisions = record.decisions_needed;
|
|
212
|
+
let changed = false;
|
|
213
|
+
let nextRecord = record;
|
|
214
|
+
if (Array.isArray(decisions)) {
|
|
215
|
+
const normalized = decisions.map((decision) => {
|
|
216
|
+
if (!decision || typeof decision !== "object")
|
|
217
|
+
return decision;
|
|
218
|
+
const decisionRecord = decision;
|
|
219
|
+
if (typeof decisionRecord.blocking === "boolean")
|
|
220
|
+
return decision;
|
|
221
|
+
changed = true;
|
|
222
|
+
return { ...decisionRecord, blocking: false };
|
|
223
|
+
});
|
|
224
|
+
if (changed)
|
|
225
|
+
nextRecord = { ...nextRecord, decisions_needed: normalized };
|
|
226
|
+
}
|
|
227
|
+
const hasBlockingDecision = (input) => Array.isArray(input.decisions_needed) &&
|
|
228
|
+
input.decisions_needed.some((decision) => {
|
|
229
|
+
if (!decision || typeof decision !== "object")
|
|
230
|
+
return false;
|
|
231
|
+
return decision.blocking === true;
|
|
232
|
+
});
|
|
233
|
+
if (hasBlockingDecision(nextRecord) && status === "completed") {
|
|
234
|
+
changed = true;
|
|
235
|
+
nextRecord = { ...nextRecord, status: "needs_decision" };
|
|
236
|
+
}
|
|
237
|
+
const normalizedStatus = typeof nextRecord.status === "string" ? nextRecord.status : status;
|
|
238
|
+
if (normalizedStatus === "completed") {
|
|
239
|
+
const hasExplicitOutcomeArrays = Array.isArray(record.artifacts) ||
|
|
240
|
+
Array.isArray(record.task_updates) ||
|
|
241
|
+
Array.isArray(record.milestone_updates) ||
|
|
242
|
+
Array.isArray(record.decisions_needed);
|
|
243
|
+
const artifactsCount = Array.isArray(nextRecord.artifacts) ? nextRecord.artifacts.length : 0;
|
|
244
|
+
const taskUpdatesCount = Array.isArray(nextRecord.task_updates)
|
|
245
|
+
? nextRecord.task_updates.length
|
|
246
|
+
: 0;
|
|
247
|
+
const milestoneUpdatesCount = Array.isArray(nextRecord.milestone_updates)
|
|
248
|
+
? nextRecord.milestone_updates.length
|
|
249
|
+
: 0;
|
|
250
|
+
const hasOutcomes = artifactsCount > 0 ||
|
|
251
|
+
taskUpdatesCount > 0 ||
|
|
252
|
+
milestoneUpdatesCount > 0;
|
|
253
|
+
if (hasExplicitOutcomeArrays && !hasOutcomes) {
|
|
254
|
+
changed = true;
|
|
255
|
+
nextRecord = { ...nextRecord, status: "error" };
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
const finalStatus = typeof nextRecord.status === "string" ? nextRecord.status : "";
|
|
259
|
+
const requiresBlockingDecision = finalStatus === "blocked" || finalStatus === "needs_decision" || finalStatus === "error";
|
|
260
|
+
if (requiresBlockingDecision && !hasBlockingDecision(nextRecord)) {
|
|
261
|
+
const nextDecisions = Array.isArray(nextRecord.decisions_needed)
|
|
262
|
+
? [...nextRecord.decisions_needed]
|
|
263
|
+
: [];
|
|
264
|
+
nextDecisions.push({
|
|
265
|
+
question: "Missing required blocking decision for non-completed status",
|
|
266
|
+
summary: "Parser inserted a blocking decision because blocked/needs_decision/error statuses require blocking=true.",
|
|
267
|
+
options: null,
|
|
268
|
+
urgency: "medium",
|
|
269
|
+
blocking: true,
|
|
270
|
+
});
|
|
271
|
+
changed = true;
|
|
272
|
+
nextRecord = { ...nextRecord, decisions_needed: nextDecisions };
|
|
273
|
+
}
|
|
274
|
+
return changed ? nextRecord : value;
|
|
275
|
+
};
|
|
276
|
+
const unwrapStructuredOutput = (value) => {
|
|
277
|
+
if (!value || typeof value !== "object")
|
|
278
|
+
return null;
|
|
279
|
+
const record = value;
|
|
280
|
+
const parseEmbeddedText = (candidate) => {
|
|
281
|
+
if (typeof candidate === "string") {
|
|
282
|
+
return parseSliceJsonText(candidate);
|
|
283
|
+
}
|
|
284
|
+
if (Array.isArray(candidate)) {
|
|
285
|
+
for (let i = candidate.length - 1; i >= 0; i -= 1) {
|
|
286
|
+
const parsed = parseEmbeddedText(candidate[i]);
|
|
287
|
+
if (parsed)
|
|
288
|
+
return parsed;
|
|
289
|
+
}
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
292
|
+
if (!candidate || typeof candidate !== "object")
|
|
293
|
+
return null;
|
|
294
|
+
const candidateRecord = candidate;
|
|
295
|
+
if (typeof candidateRecord.value === "string") {
|
|
296
|
+
return parseSliceJsonText(candidateRecord.value);
|
|
297
|
+
}
|
|
298
|
+
if (typeof candidateRecord.text === "string") {
|
|
299
|
+
return parseSliceJsonText(candidateRecord.text);
|
|
300
|
+
}
|
|
301
|
+
const fromValue = parseEmbeddedText(candidateRecord.value);
|
|
302
|
+
if (fromValue)
|
|
303
|
+
return fromValue;
|
|
304
|
+
const fromText = parseEmbeddedText(candidateRecord.text);
|
|
305
|
+
if (fromText)
|
|
306
|
+
return fromText;
|
|
307
|
+
const fromOutputText = parseEmbeddedText(candidateRecord.output_text);
|
|
308
|
+
if (fromOutputText)
|
|
309
|
+
return fromOutputText;
|
|
310
|
+
return null;
|
|
311
|
+
};
|
|
312
|
+
const finalOutput = record.final_output;
|
|
313
|
+
if (finalOutput && typeof finalOutput === "object") {
|
|
314
|
+
if (isLikelySliceResult(finalOutput))
|
|
315
|
+
return normalizeSliceResult(finalOutput);
|
|
316
|
+
const parsedFinalOutput = parseEmbeddedText(finalOutput);
|
|
317
|
+
if (parsedFinalOutput)
|
|
318
|
+
return parsedFinalOutput;
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
if (typeof finalOutput === "string") {
|
|
322
|
+
const parsedFinalOutput = parseSliceJsonText(finalOutput);
|
|
323
|
+
if (parsedFinalOutput)
|
|
324
|
+
return parsedFinalOutput;
|
|
325
|
+
}
|
|
326
|
+
const structured = record.structured_output;
|
|
327
|
+
if (structured && typeof structured === "object") {
|
|
328
|
+
if (isLikelySliceResult(structured))
|
|
329
|
+
return normalizeSliceResult(structured);
|
|
330
|
+
const parsedStructured = parseEmbeddedText(structured);
|
|
331
|
+
if (parsedStructured)
|
|
332
|
+
return parsedStructured;
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
if (typeof structured === "string") {
|
|
336
|
+
const parsedStructured = parseSliceJsonText(structured);
|
|
337
|
+
if (parsedStructured)
|
|
338
|
+
return parsedStructured;
|
|
339
|
+
}
|
|
340
|
+
// Claude text-mode envelopes can sometimes return JSON in `result`.
|
|
341
|
+
const parsedResultObject = parseEmbeddedText(record.result);
|
|
342
|
+
if (parsedResultObject)
|
|
343
|
+
return parsedResultObject;
|
|
344
|
+
if (typeof record.result === "string") {
|
|
345
|
+
const parsedResult = parseSliceJsonText(record.result);
|
|
346
|
+
if (parsedResult)
|
|
347
|
+
return parsedResult;
|
|
348
|
+
}
|
|
349
|
+
if (typeof record.output_text === "string") {
|
|
350
|
+
const parsedOutputText = parseSliceJsonText(record.output_text);
|
|
351
|
+
if (parsedOutputText)
|
|
352
|
+
return parsedOutputText;
|
|
353
|
+
}
|
|
354
|
+
// Responses-style envelopes can return text in output/message/content arrays.
|
|
355
|
+
const output = record.output;
|
|
356
|
+
if (Array.isArray(output)) {
|
|
357
|
+
for (let i = output.length - 1; i >= 0; i -= 1) {
|
|
358
|
+
const item = output[i];
|
|
359
|
+
if (!item || typeof item !== "object")
|
|
360
|
+
continue;
|
|
361
|
+
const itemRecord = item;
|
|
362
|
+
const directText = parseEmbeddedText(itemRecord.text);
|
|
363
|
+
if (directText)
|
|
364
|
+
return directText;
|
|
365
|
+
const outputText = parseEmbeddedText(itemRecord.output_text);
|
|
366
|
+
if (outputText)
|
|
367
|
+
return outputText;
|
|
368
|
+
if (!Array.isArray(itemRecord.content))
|
|
369
|
+
continue;
|
|
370
|
+
for (let j = itemRecord.content.length - 1; j >= 0; j -= 1) {
|
|
371
|
+
const content = itemRecord.content[j];
|
|
372
|
+
if (!content || typeof content !== "object")
|
|
373
|
+
continue;
|
|
374
|
+
const contentRecord = content;
|
|
375
|
+
const contentDirect = parseEmbeddedText(contentRecord);
|
|
376
|
+
if (contentDirect)
|
|
377
|
+
return contentDirect;
|
|
378
|
+
const contentText = parseEmbeddedText(contentRecord.text);
|
|
379
|
+
if (contentText)
|
|
380
|
+
return contentText;
|
|
381
|
+
const contentOutputText = parseEmbeddedText(contentRecord.output_text);
|
|
382
|
+
if (contentOutputText)
|
|
383
|
+
return contentOutputText;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
return isLikelySliceResult(record) ? normalizeSliceResult(record) : null;
|
|
388
|
+
};
|
|
389
|
+
const trimmed = stripUtf8Bom(raw).trim();
|
|
390
|
+
if (!trimmed)
|
|
391
|
+
return null;
|
|
392
|
+
const direct = parseJsonSafe(stripMarkdownJsonFence(trimmed));
|
|
393
|
+
const directUnwrapped = unwrapStructuredOutput(direct);
|
|
394
|
+
if (directUnwrapped && typeof directUnwrapped === "object")
|
|
395
|
+
return directUnwrapped;
|
|
396
|
+
const directTextParsed = parseSliceJsonText(trimmed);
|
|
397
|
+
if (directTextParsed && typeof directTextParsed === "object")
|
|
398
|
+
return directTextParsed;
|
|
399
|
+
// Tolerant parse: extract the last complete top-level JSON object from mixed logs.
|
|
400
|
+
const extractTopLevelObjects = (text) => {
|
|
401
|
+
let inString = false;
|
|
402
|
+
let escaped = false;
|
|
403
|
+
let depth = 0;
|
|
404
|
+
let start = -1;
|
|
405
|
+
const objects = [];
|
|
406
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
407
|
+
const ch = text[i];
|
|
408
|
+
if (inString) {
|
|
409
|
+
if (escaped) {
|
|
410
|
+
escaped = false;
|
|
411
|
+
continue;
|
|
412
|
+
}
|
|
413
|
+
if (ch === "\\") {
|
|
414
|
+
escaped = true;
|
|
415
|
+
continue;
|
|
416
|
+
}
|
|
417
|
+
if (ch === "\"") {
|
|
418
|
+
inString = false;
|
|
419
|
+
}
|
|
420
|
+
continue;
|
|
421
|
+
}
|
|
422
|
+
if (ch === "\"") {
|
|
423
|
+
inString = true;
|
|
424
|
+
continue;
|
|
425
|
+
}
|
|
426
|
+
if (ch === "{") {
|
|
427
|
+
if (depth === 0)
|
|
428
|
+
start = i;
|
|
429
|
+
depth += 1;
|
|
430
|
+
continue;
|
|
431
|
+
}
|
|
432
|
+
if (ch === "}") {
|
|
433
|
+
if (depth <= 0)
|
|
434
|
+
continue;
|
|
435
|
+
depth -= 1;
|
|
436
|
+
if (depth === 0 && start >= 0) {
|
|
437
|
+
objects.push(text.slice(start, i + 1));
|
|
438
|
+
start = -1;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
return objects;
|
|
443
|
+
};
|
|
444
|
+
const candidates = extractTopLevelObjects(trimmed);
|
|
445
|
+
for (let i = candidates.length - 1; i >= 0; i -= 1) {
|
|
446
|
+
const candidate = candidates[i];
|
|
447
|
+
const parsed = parseJsonSafe(candidate);
|
|
448
|
+
const unwrapped = unwrapStructuredOutput(parsed);
|
|
449
|
+
if (unwrapped && typeof unwrapped === "object")
|
|
450
|
+
return unwrapped;
|
|
451
|
+
}
|
|
452
|
+
return null;
|
|
453
|
+
}
|
|
454
|
+
export function readSliceOutputFile(pathname) {
|
|
455
|
+
try {
|
|
456
|
+
if (!existsSync(pathname))
|
|
457
|
+
return null;
|
|
458
|
+
const raw = readFileSync(pathname, "utf8");
|
|
459
|
+
return raw.trim().length > 0 ? raw : null;
|
|
460
|
+
}
|
|
461
|
+
catch {
|
|
462
|
+
return null;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
export function readFileTailSafe(pathname, maxChars = 64_000) {
|
|
466
|
+
try {
|
|
467
|
+
if (!existsSync(pathname))
|
|
468
|
+
return "";
|
|
469
|
+
const raw = readFileSync(pathname, "utf8");
|
|
470
|
+
if (raw.length <= maxChars)
|
|
471
|
+
return raw;
|
|
472
|
+
return raw.slice(raw.length - maxChars);
|
|
473
|
+
}
|
|
474
|
+
catch {
|
|
475
|
+
return "";
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
export function fileUpdatedAtEpochMs(pathname, fallbackEpochMs) {
|
|
479
|
+
try {
|
|
480
|
+
const st = statSync(pathname);
|
|
481
|
+
const mtimeMs = st.mtimeMs ?? 0;
|
|
482
|
+
return Number.isFinite(mtimeMs) && mtimeMs > 0 ? mtimeMs : fallbackEpochMs;
|
|
483
|
+
}
|
|
484
|
+
catch {
|
|
485
|
+
return fallbackEpochMs;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
export function normalizeCodexArgs(args) {
|
|
489
|
+
const normalized = Array.isArray(args) ? [...args] : [];
|
|
490
|
+
const first = normalized[0];
|
|
491
|
+
const looksLikeFlag = typeof first === "string" && first.startsWith("-");
|
|
492
|
+
const looksLikeCommand = first === "exec" ||
|
|
493
|
+
first === "e" ||
|
|
494
|
+
first === "review" ||
|
|
495
|
+
first === "resume" ||
|
|
496
|
+
first === "help" ||
|
|
497
|
+
first === "features" ||
|
|
498
|
+
first === "mcp" ||
|
|
499
|
+
first === "mcp-server" ||
|
|
500
|
+
first === "app" ||
|
|
501
|
+
first === "app-server" ||
|
|
502
|
+
first === "debug" ||
|
|
503
|
+
first === "cloud";
|
|
504
|
+
// `codex` without a subcommand expects a TTY; autopilot is headless.
|
|
505
|
+
if (!looksLikeCommand || looksLikeFlag) {
|
|
506
|
+
normalized.unshift("exec");
|
|
507
|
+
}
|
|
508
|
+
if (!normalized.includes("--skip-git-repo-check")) {
|
|
509
|
+
const passthroughIndex = normalized.indexOf("--");
|
|
510
|
+
if (passthroughIndex > -1) {
|
|
511
|
+
normalized.splice(passthroughIndex, 0, "--skip-git-repo-check");
|
|
512
|
+
}
|
|
513
|
+
else {
|
|
514
|
+
normalized.push("--skip-git-repo-check");
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
return normalized;
|
|
518
|
+
}
|
|
519
|
+
function parseCodexVersion(text) {
|
|
520
|
+
const raw = (text ?? "").trim();
|
|
521
|
+
if (!raw)
|
|
522
|
+
return { version: null, raw: null };
|
|
523
|
+
const match = raw.match(/codex-cli\s+(\d+)\.(\d+)\.(\d+)/i);
|
|
524
|
+
if (!match)
|
|
525
|
+
return { version: null, raw };
|
|
526
|
+
return {
|
|
527
|
+
version: [Number(match[1]), Number(match[2]), Number(match[3])],
|
|
528
|
+
raw,
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
function compareSemver(a, b) {
|
|
532
|
+
if (a[0] !== b[0])
|
|
533
|
+
return a[0] - b[0];
|
|
534
|
+
if (a[1] !== b[1])
|
|
535
|
+
return a[1] - b[1];
|
|
536
|
+
return a[2] - b[2];
|
|
537
|
+
}
|
|
538
|
+
function probeCodexBin(bin) {
|
|
539
|
+
const trimmed = (bin ?? "").trim();
|
|
540
|
+
if (!trimmed)
|
|
541
|
+
return null;
|
|
542
|
+
try {
|
|
543
|
+
const env = { ...process.env };
|
|
544
|
+
// NVM-installed codex scripts commonly use `#!/usr/bin/env node`. LaunchAgent PATH may not
|
|
545
|
+
// include the corresponding node binary, so prefer the sibling bin dir for resolution.
|
|
546
|
+
if (trimmed.includes(sep)) {
|
|
547
|
+
const binDir = dirname(trimmed);
|
|
548
|
+
env.PATH = env.PATH ? `${binDir}:${env.PATH}` : binDir;
|
|
549
|
+
}
|
|
550
|
+
const result = spawnSync(trimmed, ["--version"], {
|
|
551
|
+
encoding: "utf8",
|
|
552
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
553
|
+
env,
|
|
554
|
+
});
|
|
555
|
+
const combined = `${result.stdout ?? ""}\n${result.stderr ?? ""}`.trim();
|
|
556
|
+
const parsed = parseCodexVersion(combined || String(result.stdout ?? "").trim());
|
|
557
|
+
return {
|
|
558
|
+
bin: trimmed,
|
|
559
|
+
version: parsed.version,
|
|
560
|
+
versionString: parsed.raw,
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
catch {
|
|
564
|
+
return null;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
function listNvmCodexCandidates() {
|
|
568
|
+
try {
|
|
569
|
+
const bases = new Set();
|
|
570
|
+
bases.add(join(homedir(), ".nvm", "versions", "node"));
|
|
571
|
+
if (process.env.HOME && process.env.HOME.trim()) {
|
|
572
|
+
bases.add(join(process.env.HOME.trim(), ".nvm", "versions", "node"));
|
|
573
|
+
}
|
|
574
|
+
for (const base of bases) {
|
|
575
|
+
if (!existsSync(base))
|
|
576
|
+
continue;
|
|
577
|
+
const raw = readdirSync(base);
|
|
578
|
+
const versionNames = raw.filter((name) => /^v\d+\.\d+\.\d+$/.test(name));
|
|
579
|
+
const entries = [];
|
|
580
|
+
for (const name of versionNames) {
|
|
581
|
+
try {
|
|
582
|
+
if (statSync(join(base, name)).isDirectory())
|
|
583
|
+
entries.push(name);
|
|
584
|
+
}
|
|
585
|
+
catch {
|
|
586
|
+
// ignore
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
const summary = `base=${base} raw=${raw.length} versions=${versionNames.length} dirs=${entries.length}`;
|
|
590
|
+
if (entries.length === 0)
|
|
591
|
+
return { candidates: [], summary };
|
|
592
|
+
const parsed = entries
|
|
593
|
+
.map((name) => {
|
|
594
|
+
const m = name.match(/^v(\d+)\.(\d+)\.(\d+)$/);
|
|
595
|
+
if (!m)
|
|
596
|
+
return null;
|
|
597
|
+
return {
|
|
598
|
+
name,
|
|
599
|
+
ver: [Number(m[1]), Number(m[2]), Number(m[3])],
|
|
600
|
+
};
|
|
601
|
+
})
|
|
602
|
+
.filter((x) => Boolean(x));
|
|
603
|
+
parsed.sort((a, b) => compareSemver(b.ver, a.ver));
|
|
604
|
+
return {
|
|
605
|
+
candidates: parsed
|
|
606
|
+
.slice(0, 8)
|
|
607
|
+
.map((entry) => join(base, entry.name, "bin", "codex")),
|
|
608
|
+
summary,
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
return { candidates: [], summary: null };
|
|
612
|
+
}
|
|
613
|
+
catch {
|
|
614
|
+
return { candidates: [], summary: null };
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
export function createCodexBinResolver() {
|
|
618
|
+
let cachedCodexBinInfo = null;
|
|
619
|
+
let cachedCodexProbeSummary = null;
|
|
620
|
+
function resolveCodexBinInfo() {
|
|
621
|
+
if (cachedCodexBinInfo)
|
|
622
|
+
return cachedCodexBinInfo;
|
|
623
|
+
const candidates = [];
|
|
624
|
+
const explicit = (process.env.ORGX_CODEX_BIN ?? "").trim();
|
|
625
|
+
if (explicit)
|
|
626
|
+
candidates.push(explicit);
|
|
627
|
+
const nvmBin = (process.env.NVM_BIN ?? "").trim();
|
|
628
|
+
if (nvmBin)
|
|
629
|
+
candidates.push(join(nvmBin, "codex"));
|
|
630
|
+
// Whatever is on PATH for the gateway process.
|
|
631
|
+
candidates.push("codex");
|
|
632
|
+
// LaunchAgents often miss shell init (nvm), so check common per-user installs.
|
|
633
|
+
const nvmScan = listNvmCodexCandidates();
|
|
634
|
+
candidates.push(...nvmScan.candidates);
|
|
635
|
+
// Homebrew / legacy paths.
|
|
636
|
+
candidates.push("/opt/homebrew/bin/codex");
|
|
637
|
+
candidates.push("/usr/local/bin/codex");
|
|
638
|
+
const seen = new Set();
|
|
639
|
+
const unique = candidates.filter((candidate) => {
|
|
640
|
+
const key = (candidate ?? "").trim();
|
|
641
|
+
if (!key)
|
|
642
|
+
return false;
|
|
643
|
+
if (seen.has(key))
|
|
644
|
+
return false;
|
|
645
|
+
seen.add(key);
|
|
646
|
+
return true;
|
|
647
|
+
});
|
|
648
|
+
const probeSummary = [];
|
|
649
|
+
try {
|
|
650
|
+
const nvmBase = join(homedir(), ".nvm", "versions", "node");
|
|
651
|
+
const nvmExists = existsSync(nvmBase);
|
|
652
|
+
let nvmEntries = "";
|
|
653
|
+
if (nvmExists) {
|
|
654
|
+
try {
|
|
655
|
+
nvmEntries = readdirSync(nvmBase).slice(0, 6).join(",");
|
|
656
|
+
}
|
|
657
|
+
catch {
|
|
658
|
+
nvmEntries = "(readdir_failed)";
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
probeSummary.push(`nvm_base=${nvmBase} exists=${nvmExists}${nvmEntries ? ` entries=${nvmEntries}` : ""}`);
|
|
662
|
+
}
|
|
663
|
+
catch {
|
|
664
|
+
// best effort
|
|
665
|
+
}
|
|
666
|
+
probeSummary.push(`nvm_codex_candidates=${nvmScan.candidates.length}${nvmScan.candidates.length > 0 ? ` first=${nvmScan.candidates[0]}` : ""}`);
|
|
667
|
+
if (nvmScan.summary) {
|
|
668
|
+
probeSummary.push(`nvm_scan=${nvmScan.summary}`);
|
|
669
|
+
}
|
|
670
|
+
let best = null;
|
|
671
|
+
for (const candidate of unique) {
|
|
672
|
+
const probed = probeCodexBin(candidate);
|
|
673
|
+
probeSummary.push(`${candidate} => ${probed?.versionString ? probed.versionString.replace(/\s+/g, " ") : "unavailable"}`);
|
|
674
|
+
if (!probed)
|
|
675
|
+
continue;
|
|
676
|
+
if (!best) {
|
|
677
|
+
best = probed;
|
|
678
|
+
continue;
|
|
679
|
+
}
|
|
680
|
+
if (probed.version && !best.version) {
|
|
681
|
+
best = probed;
|
|
682
|
+
continue;
|
|
683
|
+
}
|
|
684
|
+
if (!probed.version || !best.version)
|
|
685
|
+
continue;
|
|
686
|
+
if (compareSemver(probed.version, best.version) > 0)
|
|
687
|
+
best = probed;
|
|
688
|
+
}
|
|
689
|
+
cachedCodexBinInfo = best ?? { bin: explicit || "codex", version: null, versionString: null };
|
|
690
|
+
cachedCodexProbeSummary = probeSummary.slice(0, 14).join(" | ");
|
|
691
|
+
return cachedCodexBinInfo;
|
|
692
|
+
}
|
|
693
|
+
function getCachedCodexProbeSummary() {
|
|
694
|
+
return cachedCodexProbeSummary;
|
|
695
|
+
}
|
|
696
|
+
return {
|
|
697
|
+
resolveCodexBinInfo,
|
|
698
|
+
getCachedCodexProbeSummary,
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
function normalizeSkillName(skill) {
|
|
702
|
+
return skill.replace(/^\$/, "").trim();
|
|
703
|
+
}
|
|
704
|
+
function skillAliasesFor(skill) {
|
|
705
|
+
const normalized = normalizeSkillName(skill);
|
|
706
|
+
if (!normalized)
|
|
707
|
+
return [];
|
|
708
|
+
const aliases = [normalized];
|
|
709
|
+
if (normalized.startsWith("orgx-"))
|
|
710
|
+
aliases.push(normalized.slice("orgx-".length));
|
|
711
|
+
if (normalized.endsWith("-agent"))
|
|
712
|
+
aliases.push(normalized.slice(0, -"-agent".length));
|
|
713
|
+
return Array.from(new Set(aliases.filter(Boolean)));
|
|
714
|
+
}
|
|
715
|
+
function buildSkillHints(requiredSkills) {
|
|
716
|
+
return requiredSkills
|
|
717
|
+
.map((skill) => {
|
|
718
|
+
const normalized = normalizeSkillName(skill);
|
|
719
|
+
const aliases = skillAliasesFor(normalized);
|
|
720
|
+
const hintPaths = aliases
|
|
721
|
+
.flatMap((alias) => [
|
|
722
|
+
join(homedir(), ".codex", "skills", alias, "SKILL.md"),
|
|
723
|
+
join(homedir(), ".agents", "skills", alias, "SKILL.md"),
|
|
724
|
+
])
|
|
725
|
+
.filter(Boolean);
|
|
726
|
+
return {
|
|
727
|
+
skill: normalized,
|
|
728
|
+
hintPaths: Array.from(new Set(hintPaths)),
|
|
729
|
+
};
|
|
730
|
+
})
|
|
731
|
+
.filter((entry) => entry.skill.length > 0);
|
|
732
|
+
}
|
|
733
|
+
export function buildSliceOutputInstructions(input) {
|
|
734
|
+
const skillHints = buildSkillHints(input.requiredSkills);
|
|
735
|
+
return [
|
|
736
|
+
"# Slice Execution",
|
|
737
|
+
"",
|
|
738
|
+
`Slice run: ${input.runId}`,
|
|
739
|
+
"",
|
|
740
|
+
"Reporting:",
|
|
741
|
+
"- You MUST emit progress at least twice (start + completion) using an OrgX progress tool.",
|
|
742
|
+
"- Preferred tool: orgx_report_progress. Equivalent aliases are valid (for example mcp__orgx__update_stream_progress).",
|
|
743
|
+
"- If no OrgX progress tool is available, include a blocking decisions_needed entry describing the missing tool.",
|
|
744
|
+
"- Do NOT hunt for OrgX mutation tools to mark tasks done. Instead, request status changes in your FINAL JSON via task_updates/milestone_updates; the coordinator will apply them.",
|
|
745
|
+
"",
|
|
746
|
+
"What to do:",
|
|
747
|
+
"- Choose a coherent slice of work you can complete end-to-end in this run.",
|
|
748
|
+
"- Execute the work (code/docs/config) and produce verifiable outcomes.",
|
|
749
|
+
"- Self-assess confidence when saving artifacts and include `confidence_score` in [0,1].",
|
|
750
|
+
"- If blocked, be explicit about what decision/info is required.",
|
|
751
|
+
"- Keep this run focused: stay inside the current repository/workdir and avoid unrelated exploration.",
|
|
752
|
+
"- Execution budget: prefer <=12 shell commands and <=6 minutes wall time.",
|
|
753
|
+
"- Verification budget: run only targeted checks for changed files. Avoid full-suite commands (for example `npm run test:hooks`, `npm test`, `npm run build`) unless the task explicitly requires them.",
|
|
754
|
+
"- If you hit sandbox/env blockers after one retry, stop and return `status=needs_decision` with the blocker and the smallest unblocking action.",
|
|
755
|
+
"- For each required skill, read the skill document and collect proof (path + sha256 + heading).",
|
|
756
|
+
"",
|
|
757
|
+
"Output requirements:",
|
|
758
|
+
"- Print ONLY a single JSON object as the final output (no interim JSON status messages).",
|
|
759
|
+
`- Your JSON MUST conform to this schema file: ${input.schemaPath}`,
|
|
760
|
+
"- Artifacts must be verifiable: include URLs or local paths, plus verification steps.",
|
|
761
|
+
"- Include `confidence_score` for each artifact (`0` to `1`; use `null` when unknown).",
|
|
762
|
+
"- If you need a human decision, include it in decisions_needed.",
|
|
763
|
+
"- For every decisions_needed entry, ALWAYS set blocking explicitly (true or false).",
|
|
764
|
+
"- If status is blocked, needs_decision, or error: include at least one decisions_needed entry with blocking=true.",
|
|
765
|
+
"- Status/decision consistency is strict:",
|
|
766
|
+
" - If any decision is blocking=true, status MUST be needs_decision or blocked (never completed).",
|
|
767
|
+
" - Only use status=completed when all listed decisions are non-blocking follow-ups.",
|
|
768
|
+
"- Never return status=completed with zero artifacts and zero task/milestone updates.",
|
|
769
|
+
"- skill_evidence is mandatory. Include one object per required skill with:",
|
|
770
|
+
" - skill (exact required skill id without leading $)",
|
|
771
|
+
" - skill_file (absolute SKILL.md path used)",
|
|
772
|
+
" - skill_sha256 (lowercase SHA-256 hex of that file)",
|
|
773
|
+
" - skill_heading (first markdown heading or first non-empty line)",
|
|
774
|
+
"- If you cannot locate/verify a required skill file, return status=needs_decision and a blocking decisions_needed entry.",
|
|
775
|
+
"Skill file hints:",
|
|
776
|
+
...(skillHints.length > 0
|
|
777
|
+
? skillHints.flatMap((entry) => [
|
|
778
|
+
`- ${entry.skill}:`,
|
|
779
|
+
...entry.hintPaths.map((path) => ` - ${path}`),
|
|
780
|
+
])
|
|
781
|
+
: ["- (none)"]),
|
|
782
|
+
"- If you are confident OrgX statuses should change, include task_updates and/or milestone_updates (with a short reason).",
|
|
783
|
+
" - task_updates.status must be one of: todo, in_progress, done, blocked",
|
|
784
|
+
" - milestone_updates.status must be one of: planned, in_progress, completed, at_risk, cancelled",
|
|
785
|
+
].join("\n");
|
|
786
|
+
}
|
|
787
|
+
export function buildWorkstreamSlicePrompt(input) {
|
|
788
|
+
const skillHints = buildSkillHints(input.executionPolicy.requiredSkills);
|
|
789
|
+
const milestones = input.milestoneSummaries
|
|
790
|
+
.map((m) => `- ${m.title} (${m.status}) [${m.id}]`)
|
|
791
|
+
.slice(0, 10)
|
|
792
|
+
.join("\n");
|
|
793
|
+
const tasks = input.taskSummaries
|
|
794
|
+
.map((t) => {
|
|
795
|
+
const milestone = t.milestoneId ? ` milestone=${t.milestoneId}` : "";
|
|
796
|
+
return `- ${t.title} (${t.status}) [${t.id}]${milestone}`;
|
|
797
|
+
})
|
|
798
|
+
.slice(0, 18)
|
|
799
|
+
.join("\n");
|
|
800
|
+
const behaviorConfigLines = [
|
|
801
|
+
input.behaviorConfig?.configId
|
|
802
|
+
? `- behavior_config_id: ${input.behaviorConfig.configId}`
|
|
803
|
+
: null,
|
|
804
|
+
input.behaviorConfig?.version
|
|
805
|
+
? `- behavior_config_version: ${input.behaviorConfig.version}`
|
|
806
|
+
: null,
|
|
807
|
+
input.behaviorConfig?.hash
|
|
808
|
+
? `- behavior_config_hash: ${input.behaviorConfig.hash}`
|
|
809
|
+
: null,
|
|
810
|
+
input.behaviorConfig?.policySource
|
|
811
|
+
? `- policy_source: ${input.behaviorConfig.policySource}`
|
|
812
|
+
: null,
|
|
813
|
+
input.behaviorConfig?.context
|
|
814
|
+
? `- behavior_context: ${input.behaviorConfig.context}`
|
|
815
|
+
: null,
|
|
816
|
+
].filter((line) => Boolean(line));
|
|
817
|
+
return [
|
|
818
|
+
"You are an OrgX execution agent running ONE workstream slice in a background autonomous session.",
|
|
819
|
+
"",
|
|
820
|
+
`Execution policy: ${input.executionPolicy.domain}`,
|
|
821
|
+
`Required skills: ${input.executionPolicy.requiredSkills.map((s) => (s.startsWith("$") ? s : `$${s}`)).join(", ")}`,
|
|
822
|
+
"",
|
|
823
|
+
`Initiative: ${input.initiativeTitle} [${input.initiativeId}]`,
|
|
824
|
+
`Workstream: ${input.workstreamTitle} [${input.workstreamId}]`,
|
|
825
|
+
`Slice run: ${input.runId}`,
|
|
826
|
+
...(behaviorConfigLines.length > 0
|
|
827
|
+
? [
|
|
828
|
+
"",
|
|
829
|
+
"Behavior config (plugin-injected context):",
|
|
830
|
+
...behaviorConfigLines,
|
|
831
|
+
]
|
|
832
|
+
: []),
|
|
833
|
+
"",
|
|
834
|
+
"Milestones (context):",
|
|
835
|
+
milestones || "- (none found)",
|
|
836
|
+
"",
|
|
837
|
+
"Candidate tasks (context only; do NOT assume status is updated unless you explicitly request it in output):",
|
|
838
|
+
tasks || "- (none found)",
|
|
839
|
+
"",
|
|
840
|
+
"Reporting:",
|
|
841
|
+
"- You MUST emit progress at least twice (start + completion) using an OrgX progress tool.",
|
|
842
|
+
"- Preferred tool: orgx_report_progress. Equivalent aliases are valid (for example mcp__orgx__update_stream_progress).",
|
|
843
|
+
"- If no OrgX progress tool is available, include a blocking decisions_needed entry describing the missing tool.",
|
|
844
|
+
"- Do NOT hunt for OrgX mutation tools to mark tasks done. Instead, request status changes in your FINAL JSON via task_updates/milestone_updates; the coordinator will apply them.",
|
|
845
|
+
"",
|
|
846
|
+
"What to do:",
|
|
847
|
+
"- Choose a coherent slice of work you can complete end-to-end in this run.",
|
|
848
|
+
"- Execute the work (code/docs/config) and produce verifiable outcomes.",
|
|
849
|
+
"- Self-assess confidence when saving artifacts and include `confidence_score` in [0,1].",
|
|
850
|
+
"- If blocked, be explicit about what decision/info is required.",
|
|
851
|
+
"- Keep this run focused: stay inside the current repository/workdir and avoid unrelated exploration.",
|
|
852
|
+
"- Execution budget: prefer <=12 shell commands and <=6 minutes wall time.",
|
|
853
|
+
"- Verification budget: run only targeted checks for changed files. Avoid full-suite commands (for example `npm run test:hooks`, `npm test`, `npm run build`) unless the task explicitly requires them.",
|
|
854
|
+
"- If you hit sandbox/env blockers after one retry, stop and return `status=needs_decision` with the blocker and the smallest unblocking action.",
|
|
855
|
+
"- For each required skill, read the skill document and collect proof (path + sha256 + heading).",
|
|
856
|
+
"",
|
|
857
|
+
"Output requirements:",
|
|
858
|
+
"- Print ONLY a single JSON object as the final output (no interim JSON status messages).",
|
|
859
|
+
`- Your JSON MUST conform to this schema file: ${input.schemaPath}`,
|
|
860
|
+
"- Artifacts must be verifiable: include URLs or local paths, plus verification steps.",
|
|
861
|
+
"- Include `confidence_score` for each artifact (`0` to `1`; use `null` when unknown).",
|
|
862
|
+
"- If you need a human decision, include it in decisions_needed.",
|
|
863
|
+
"- For every decisions_needed entry, ALWAYS set blocking explicitly (true or false).",
|
|
864
|
+
"- If status is blocked, needs_decision, or error: include at least one decisions_needed entry with blocking=true.",
|
|
865
|
+
"- Status/decision consistency is strict:",
|
|
866
|
+
" - If any decision is blocking=true, status MUST be needs_decision or blocked (never completed).",
|
|
867
|
+
" - Only use status=completed when all listed decisions are non-blocking follow-ups.",
|
|
868
|
+
"- Never return status=completed with zero artifacts and zero task/milestone updates.",
|
|
869
|
+
"- skill_evidence is mandatory. Include one object per required skill with:",
|
|
870
|
+
" - skill (exact required skill id without leading $)",
|
|
871
|
+
" - skill_file (absolute SKILL.md path used)",
|
|
872
|
+
" - skill_sha256 (lowercase SHA-256 hex of that file)",
|
|
873
|
+
" - skill_heading (first markdown heading or first non-empty line)",
|
|
874
|
+
"- If you cannot locate/verify a required skill file, return status=needs_decision and a blocking decisions_needed entry.",
|
|
875
|
+
"Skill file hints:",
|
|
876
|
+
...(skillHints.length > 0
|
|
877
|
+
? skillHints.flatMap((entry) => [
|
|
878
|
+
`- ${entry.skill}:`,
|
|
879
|
+
...entry.hintPaths.map((path) => ` - ${path}`),
|
|
880
|
+
])
|
|
881
|
+
: ["- (none)"]),
|
|
882
|
+
"- If you are confident OrgX statuses should change, include task_updates and/or milestone_updates (with a short reason).",
|
|
883
|
+
" - task_updates.status must be one of: todo, in_progress, done, blocked",
|
|
884
|
+
" - milestone_updates.status must be one of: planned, in_progress, completed, at_risk, cancelled",
|
|
885
|
+
].join("\n");
|
|
886
|
+
}
|
|
887
|
+
export function buildScopeDirective(scope, meta) {
|
|
888
|
+
switch (scope) {
|
|
889
|
+
case "milestone":
|
|
890
|
+
return (`You are completing milestone "${meta.milestoneTitles?.[0] ?? "Unknown"}" ` +
|
|
891
|
+
`(${meta.taskCount} tasks). Complete all tasks to close the milestone. Report per-task progress.`);
|
|
892
|
+
case "workstream":
|
|
893
|
+
return (`You are advancing the entire "${meta.workstreamTitle ?? "Unknown"}" workstream ` +
|
|
894
|
+
`(${meta.taskCount} tasks across ${meta.milestoneTitles?.length ?? 0} milestones). ` +
|
|
895
|
+
`Prioritize by dependency order. Report per-milestone progress.`);
|
|
896
|
+
default:
|
|
897
|
+
return "";
|
|
898
|
+
}
|
|
899
|
+
}
|