@useorgx/openclaw-plugin 0.4.6 → 0.4.9
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 +310 -24
- package/dashboard/dist/assets/B5NEElEI.css +1 -0
- package/dashboard/dist/assets/BhapSNAs.js +215 -0
- package/dashboard/dist/assets/iFdvE7lx.js +1 -0
- package/dashboard/dist/assets/jRJsmpYM.js +1 -0
- package/dashboard/dist/index.html +2 -2
- package/dist/activity-actor-fields.d.ts +3 -0
- package/dist/activity-actor-fields.js +128 -0
- package/dist/activity-store.js +12 -19
- package/dist/agent-context-store.js +5 -25
- package/dist/agent-run-store.js +5 -25
- package/dist/agent-suite.js +1 -8
- package/dist/artifacts/register-artifact.d.ts +47 -0
- package/dist/artifacts/register-artifact.js +271 -0
- package/dist/auth/flows.d.ts +47 -0
- package/dist/auth/flows.js +169 -0
- package/dist/auth-store.js +14 -39
- package/dist/byok-store.js +5 -19
- package/dist/cli/orgx.d.ts +66 -0
- package/dist/cli/orgx.js +91 -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 +1 -0
- package/dist/contracts/client.js +7 -5
- package/dist/contracts/shared-types.d.ts +147 -0
- package/dist/contracts/shared-types.js +3 -0
- package/dist/contracts/types.d.ts +1 -130
- package/dist/contracts/types.js +5 -0
- package/dist/entities/auto-assignment.d.ts +36 -0
- package/dist/entities/auto-assignment.js +115 -0
- package/dist/entity-comment-store.js +5 -25
- package/dist/hash-utils.d.ts +2 -0
- package/dist/hash-utils.js +12 -0
- package/dist/http/helpers/activity-headline.d.ts +10 -0
- package/dist/http/helpers/activity-headline.js +192 -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 +298 -0
- package/dist/http/helpers/auto-continue-engine.js +1218 -0
- package/dist/http/helpers/autopilot-operations.d.ts +157 -0
- package/dist/http/helpers/autopilot-operations.js +403 -0
- package/dist/http/helpers/autopilot-runtime.d.ts +42 -0
- package/dist/http/helpers/autopilot-runtime.js +319 -0
- package/dist/http/helpers/autopilot-slice-utils.d.ts +38 -0
- package/dist/http/helpers/autopilot-slice-utils.js +476 -0
- package/dist/http/helpers/decision-mapper.d.ts +12 -0
- package/dist/http/helpers/decision-mapper.js +44 -0
- package/dist/http/helpers/dispatch-lifecycle.d.ts +102 -0
- package/dist/http/helpers/dispatch-lifecycle.js +604 -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 +154 -0
- package/dist/http/helpers/mission-control.d.ts +94 -0
- package/dist/http/helpers/mission-control.js +894 -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/value-utils.d.ts +6 -0
- package/dist/http/helpers/value-utils.js +67 -0
- package/dist/http/index.d.ts +88 -0
- package/dist/http/index.js +2353 -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 +29 -0
- package/dist/http/routes/agent-suite.js +198 -0
- package/dist/http/routes/agents-catalog.d.ts +40 -0
- package/dist/http/routes/agents-catalog.js +83 -0
- package/dist/http/routes/billing.d.ts +23 -0
- package/dist/http/routes/billing.js +55 -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 +13 -0
- package/dist/http/routes/decision-actions.js +66 -0
- package/dist/http/routes/delegation.d.ts +19 -0
- package/dist/http/routes/delegation.js +32 -0
- package/dist/http/routes/entities.d.ts +47 -0
- package/dist/http/routes/entities.js +152 -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 +110 -0
- package/dist/http/routes/live-legacy.js +598 -0
- package/dist/http/routes/live-misc.d.ts +69 -0
- package/dist/http/routes/live-misc.js +206 -0
- package/dist/http/routes/live-snapshot.d.ts +90 -0
- package/dist/http/routes/live-snapshot.js +297 -0
- package/dist/http/routes/mission-control-actions.d.ts +83 -0
- package/dist/http/routes/mission-control-actions.js +541 -0
- package/dist/http/routes/mission-control-read.d.ts +28 -0
- package/dist/http/routes/mission-control-read.js +67 -0
- package/dist/http/routes/onboarding.d.ts +34 -0
- package/dist/http/routes/onboarding.js +101 -0
- package/dist/http/routes/run-control.d.ts +24 -0
- package/dist/http/routes/run-control.js +86 -0
- package/dist/http/routes/runtime-hooks.d.ts +69 -0
- package/dist/http/routes/runtime-hooks.js +437 -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 +42 -0
- package/dist/http/routes/work-artifacts.d.ts +9 -0
- package/dist/http/routes/work-artifacts.js +36 -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 -9664
- package/dist/index.js +122 -2121
- package/dist/json-utils.d.ts +1 -0
- package/dist/json-utils.js +8 -0
- package/dist/local-openclaw.js +8 -0
- package/dist/mcp-client-setup.js +75 -90
- package/dist/next-up-queue-store.js +4 -18
- package/dist/runtime-instance-store.js +8 -34
- package/dist/services/background.d.ts +23 -0
- package/dist/services/background.js +23 -0
- package/dist/services/instrumentation.d.ts +29 -0
- package/dist/services/instrumentation.js +136 -0
- 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/outbox-replay.d.ts +55 -0
- package/dist/sync/outbox-replay.js +514 -0
- package/dist/tools/core-tools.d.ts +76 -0
- package/dist/tools/core-tools.js +1005 -0
- package/dist/worker-supervisor.js +15 -0
- package/package.json +6 -1
- package/dashboard/dist/assets/0tOC3wSN.js +0 -214
- package/dashboard/dist/assets/Bm8QnMJ_.js +0 -1
- package/dashboard/dist/assets/CyxZio4Y.js +0 -1
- package/dashboard/dist/assets/DaAIOik3.css +0 -1
|
@@ -0,0 +1,1005 @@
|
|
|
1
|
+
import { randomUUID as randomUuidFn } from "node:crypto";
|
|
2
|
+
import { registerArtifact } from "../artifacts/register-artifact.js";
|
|
3
|
+
import { appendToOutbox } from "../outbox.js";
|
|
4
|
+
export function registerCoreTools(deps) {
|
|
5
|
+
const { registerTool, client, config, getCachedSnapshot, getLastSnapshotAt, doSync, text, json, formatSnapshot, autoAssignEntityForCreate, toReportingPhase, inferReportingInitiativeId, isUuid, pickNonEmptyString, resolveReportingContext, readSkillPackState, } = deps;
|
|
6
|
+
const randomUUID = deps.randomUUID ?? randomUuidFn;
|
|
7
|
+
const mcpToolRegistry = new Map();
|
|
8
|
+
const registerMcpTool = (tool, options) => {
|
|
9
|
+
mcpToolRegistry.set(tool.name, tool);
|
|
10
|
+
registerTool(tool, options);
|
|
11
|
+
};
|
|
12
|
+
// --- orgx_status ---
|
|
13
|
+
registerMcpTool({
|
|
14
|
+
name: "orgx_status",
|
|
15
|
+
description: "Get current OrgX org status: active initiatives, agent states, pending decisions, active tasks.",
|
|
16
|
+
parameters: {
|
|
17
|
+
type: "object",
|
|
18
|
+
properties: {},
|
|
19
|
+
additionalProperties: false,
|
|
20
|
+
},
|
|
21
|
+
async execute(_callId) {
|
|
22
|
+
let snapshot = getCachedSnapshot();
|
|
23
|
+
if (!snapshot || Date.now() - getLastSnapshotAt() > config.syncIntervalMs) {
|
|
24
|
+
await doSync();
|
|
25
|
+
snapshot = getCachedSnapshot();
|
|
26
|
+
}
|
|
27
|
+
if (!snapshot) {
|
|
28
|
+
return text("❌ Failed to fetch OrgX status. Check API key and connectivity.");
|
|
29
|
+
}
|
|
30
|
+
return text(formatSnapshot(snapshot));
|
|
31
|
+
},
|
|
32
|
+
}, { optional: true });
|
|
33
|
+
// --- orgx_sync ---
|
|
34
|
+
registerMcpTool({
|
|
35
|
+
name: "orgx_sync",
|
|
36
|
+
description: "Push/pull memory sync with OrgX. Send local memory/daily log; receive initiatives, tasks, decisions, model routing policy.",
|
|
37
|
+
parameters: {
|
|
38
|
+
type: "object",
|
|
39
|
+
properties: {
|
|
40
|
+
memory: {
|
|
41
|
+
type: "string",
|
|
42
|
+
description: "Local memory snapshot to push",
|
|
43
|
+
},
|
|
44
|
+
dailyLog: {
|
|
45
|
+
type: "string",
|
|
46
|
+
description: "Today's session log to push",
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
async execute(_callId, params = {}) {
|
|
51
|
+
try {
|
|
52
|
+
const resp = await client.syncMemory({
|
|
53
|
+
memory: params.memory,
|
|
54
|
+
dailyLog: params.dailyLog,
|
|
55
|
+
});
|
|
56
|
+
return json("Sync complete:", resp);
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
return text(`❌ Sync failed: ${err instanceof Error ? err.message : err}`);
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
}, { optional: true });
|
|
63
|
+
// --- orgx_delegation_preflight ---
|
|
64
|
+
registerMcpTool({
|
|
65
|
+
name: "orgx_delegation_preflight",
|
|
66
|
+
description: "Run delegation preflight to score scope quality, estimate ETA/cost, and suggest a split before autonomous execution.",
|
|
67
|
+
parameters: {
|
|
68
|
+
type: "object",
|
|
69
|
+
properties: {
|
|
70
|
+
intent: {
|
|
71
|
+
type: "string",
|
|
72
|
+
description: "Task intent in natural language",
|
|
73
|
+
},
|
|
74
|
+
acceptanceCriteria: {
|
|
75
|
+
type: "array",
|
|
76
|
+
items: { type: "string" },
|
|
77
|
+
description: "Optional acceptance criteria to reduce ambiguity",
|
|
78
|
+
},
|
|
79
|
+
constraints: {
|
|
80
|
+
type: "array",
|
|
81
|
+
items: { type: "string" },
|
|
82
|
+
description: "Optional constraints (deadline, stack, policy)",
|
|
83
|
+
},
|
|
84
|
+
domains: {
|
|
85
|
+
type: "array",
|
|
86
|
+
items: { type: "string" },
|
|
87
|
+
description: "Optional preferred owner domains",
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
required: ["intent"],
|
|
91
|
+
additionalProperties: false,
|
|
92
|
+
},
|
|
93
|
+
async execute(_callId, params = { intent: "" }) {
|
|
94
|
+
try {
|
|
95
|
+
const result = await client.delegationPreflight({
|
|
96
|
+
intent: params.intent,
|
|
97
|
+
acceptanceCriteria: Array.isArray(params.acceptanceCriteria)
|
|
98
|
+
? params.acceptanceCriteria.filter((item) => typeof item === "string")
|
|
99
|
+
: undefined,
|
|
100
|
+
constraints: Array.isArray(params.constraints)
|
|
101
|
+
? params.constraints.filter((item) => typeof item === "string")
|
|
102
|
+
: undefined,
|
|
103
|
+
domains: Array.isArray(params.domains)
|
|
104
|
+
? params.domains.filter((item) => typeof item === "string")
|
|
105
|
+
: undefined,
|
|
106
|
+
});
|
|
107
|
+
return json("Delegation preflight:", result.data ?? result);
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
return text(`❌ Delegation preflight failed: ${err instanceof Error ? err.message : err}`);
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
}, { optional: true });
|
|
114
|
+
// --- orgx_run_action ---
|
|
115
|
+
registerMcpTool({
|
|
116
|
+
name: "orgx_run_action",
|
|
117
|
+
description: "Apply a control action to a run: pause, resume, cancel, or rollback (rollback requires checkpointId).",
|
|
118
|
+
parameters: {
|
|
119
|
+
type: "object",
|
|
120
|
+
properties: {
|
|
121
|
+
runId: {
|
|
122
|
+
type: "string",
|
|
123
|
+
description: "Run UUID",
|
|
124
|
+
},
|
|
125
|
+
action: {
|
|
126
|
+
type: "string",
|
|
127
|
+
enum: ["pause", "resume", "cancel", "rollback"],
|
|
128
|
+
description: "Control action",
|
|
129
|
+
},
|
|
130
|
+
checkpointId: {
|
|
131
|
+
type: "string",
|
|
132
|
+
description: "Checkpoint UUID (required for rollback)",
|
|
133
|
+
},
|
|
134
|
+
reason: {
|
|
135
|
+
type: "string",
|
|
136
|
+
description: "Optional reason for audit trail",
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
required: ["runId", "action"],
|
|
140
|
+
additionalProperties: false,
|
|
141
|
+
},
|
|
142
|
+
async execute(_callId, params = { runId: "", action: "pause" }) {
|
|
143
|
+
try {
|
|
144
|
+
if (params.action === "rollback" && !params.checkpointId) {
|
|
145
|
+
return text("❌ rollback requires checkpointId");
|
|
146
|
+
}
|
|
147
|
+
const result = await client.runAction(params.runId, params.action, {
|
|
148
|
+
checkpointId: params.checkpointId,
|
|
149
|
+
reason: params.reason,
|
|
150
|
+
});
|
|
151
|
+
return json("Run action applied:", result.data ?? result);
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
return text(`❌ Run action failed: ${err instanceof Error ? err.message : err}`);
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
}, { optional: true });
|
|
158
|
+
// --- orgx_checkpoints_list ---
|
|
159
|
+
registerMcpTool({
|
|
160
|
+
name: "orgx_checkpoints_list",
|
|
161
|
+
description: "List checkpoints for a run.",
|
|
162
|
+
parameters: {
|
|
163
|
+
type: "object",
|
|
164
|
+
properties: {
|
|
165
|
+
runId: {
|
|
166
|
+
type: "string",
|
|
167
|
+
description: "Run UUID",
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
required: ["runId"],
|
|
171
|
+
additionalProperties: false,
|
|
172
|
+
},
|
|
173
|
+
async execute(_callId, params = { runId: "" }) {
|
|
174
|
+
try {
|
|
175
|
+
const result = await client.listRunCheckpoints(params.runId);
|
|
176
|
+
return json("Run checkpoints:", result.data ?? result);
|
|
177
|
+
}
|
|
178
|
+
catch (err) {
|
|
179
|
+
return text(`❌ Failed to list checkpoints: ${err instanceof Error ? err.message : err}`);
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
}, { optional: true });
|
|
183
|
+
// --- orgx_checkpoint_restore ---
|
|
184
|
+
registerMcpTool({
|
|
185
|
+
name: "orgx_checkpoint_restore",
|
|
186
|
+
description: "Restore a run to a specific checkpoint.",
|
|
187
|
+
parameters: {
|
|
188
|
+
type: "object",
|
|
189
|
+
properties: {
|
|
190
|
+
runId: {
|
|
191
|
+
type: "string",
|
|
192
|
+
description: "Run UUID",
|
|
193
|
+
},
|
|
194
|
+
checkpointId: {
|
|
195
|
+
type: "string",
|
|
196
|
+
description: "Checkpoint UUID",
|
|
197
|
+
},
|
|
198
|
+
reason: {
|
|
199
|
+
type: "string",
|
|
200
|
+
description: "Optional restoration reason",
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
required: ["runId", "checkpointId"],
|
|
204
|
+
additionalProperties: false,
|
|
205
|
+
},
|
|
206
|
+
async execute(_callId, params = {
|
|
207
|
+
runId: "",
|
|
208
|
+
checkpointId: "",
|
|
209
|
+
}) {
|
|
210
|
+
try {
|
|
211
|
+
const result = await client.restoreRunCheckpoint(params.runId, {
|
|
212
|
+
checkpointId: params.checkpointId,
|
|
213
|
+
reason: params.reason,
|
|
214
|
+
});
|
|
215
|
+
return json("Checkpoint restored:", result.data ?? result);
|
|
216
|
+
}
|
|
217
|
+
catch (err) {
|
|
218
|
+
return text(`❌ Checkpoint restore failed: ${err instanceof Error ? err.message : err}`);
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
}, { optional: true });
|
|
222
|
+
// --- orgx_spawn_check ---
|
|
223
|
+
registerMcpTool({
|
|
224
|
+
name: "orgx_spawn_check",
|
|
225
|
+
description: "Check quality gate + get model routing before spawning a sub-agent. Returns allowed/denied, model tier, and check details.",
|
|
226
|
+
parameters: {
|
|
227
|
+
type: "object",
|
|
228
|
+
properties: {
|
|
229
|
+
domain: {
|
|
230
|
+
type: "string",
|
|
231
|
+
description: "Agent domain (engineering, product, marketing, data, operations, design)",
|
|
232
|
+
},
|
|
233
|
+
taskId: {
|
|
234
|
+
type: "string",
|
|
235
|
+
description: "OrgX task ID to check",
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
required: ["domain"],
|
|
239
|
+
},
|
|
240
|
+
async execute(_callId, params = { domain: "" }) {
|
|
241
|
+
try {
|
|
242
|
+
const result = await client.checkSpawnGuard(params.domain, params.taskId);
|
|
243
|
+
const status = result.allowed ? "✅ Allowed" : "🚫 Blocked";
|
|
244
|
+
return json(`${status} — model tier: ${result.modelTier}`, result);
|
|
245
|
+
}
|
|
246
|
+
catch (err) {
|
|
247
|
+
return text(`❌ Spawn check failed: ${err instanceof Error ? err.message : err}`);
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
}, { optional: true });
|
|
251
|
+
// --- orgx_quality_score ---
|
|
252
|
+
registerMcpTool({
|
|
253
|
+
name: "orgx_quality_score",
|
|
254
|
+
description: "Record a quality score (1-5) for completed agent work. Used to gate future spawns and track performance.",
|
|
255
|
+
parameters: {
|
|
256
|
+
type: "object",
|
|
257
|
+
properties: {
|
|
258
|
+
taskId: {
|
|
259
|
+
type: "string",
|
|
260
|
+
description: "ID of the completed task",
|
|
261
|
+
},
|
|
262
|
+
domain: {
|
|
263
|
+
type: "string",
|
|
264
|
+
description: "Agent domain that did the work",
|
|
265
|
+
},
|
|
266
|
+
score: {
|
|
267
|
+
type: "number",
|
|
268
|
+
description: "Quality 1 (poor) to 5 (excellent)",
|
|
269
|
+
minimum: 1,
|
|
270
|
+
maximum: 5,
|
|
271
|
+
},
|
|
272
|
+
notes: {
|
|
273
|
+
type: "string",
|
|
274
|
+
description: "Notes on the assessment",
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
required: ["taskId", "domain", "score"],
|
|
278
|
+
},
|
|
279
|
+
async execute(_callId, params = { taskId: "", domain: "", score: 0 }) {
|
|
280
|
+
try {
|
|
281
|
+
await client.recordQuality(params);
|
|
282
|
+
return text(`✅ Quality score recorded: ${params.score}/5 for task ${params.taskId} (${params.domain})`);
|
|
283
|
+
}
|
|
284
|
+
catch (err) {
|
|
285
|
+
return text(`❌ Quality recording failed: ${err instanceof Error ? err.message : err}`);
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
}, { optional: true });
|
|
289
|
+
// --- orgx_create_entity ---
|
|
290
|
+
registerMcpTool({
|
|
291
|
+
name: "orgx_create_entity",
|
|
292
|
+
description: "Create an OrgX entity (initiative, workstream, task, decision, milestone, etc.).",
|
|
293
|
+
parameters: {
|
|
294
|
+
type: "object",
|
|
295
|
+
properties: {
|
|
296
|
+
type: {
|
|
297
|
+
type: "string",
|
|
298
|
+
description: "Entity type: initiative, workstream, task, decision, milestone, artifact, agent, blocker",
|
|
299
|
+
},
|
|
300
|
+
title: {
|
|
301
|
+
type: "string",
|
|
302
|
+
description: "Entity title",
|
|
303
|
+
},
|
|
304
|
+
summary: {
|
|
305
|
+
type: "string",
|
|
306
|
+
description: "Description",
|
|
307
|
+
},
|
|
308
|
+
status: {
|
|
309
|
+
type: "string",
|
|
310
|
+
description: "Initial status (active, not_started, todo)",
|
|
311
|
+
},
|
|
312
|
+
initiative_id: {
|
|
313
|
+
type: "string",
|
|
314
|
+
description: "Parent initiative ID (for workstreams/tasks)",
|
|
315
|
+
},
|
|
316
|
+
workstream_id: {
|
|
317
|
+
type: "string",
|
|
318
|
+
description: "Parent workstream ID (for tasks)",
|
|
319
|
+
},
|
|
320
|
+
command_center_id: {
|
|
321
|
+
type: "string",
|
|
322
|
+
description: "Command center ID (for initiatives)",
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
required: ["type", "title"],
|
|
326
|
+
},
|
|
327
|
+
async execute(_callId, params = {}) {
|
|
328
|
+
try {
|
|
329
|
+
const { type, ...data } = params;
|
|
330
|
+
let entity = await client.createEntity(type, data);
|
|
331
|
+
let assignmentSummary = null;
|
|
332
|
+
const entityType = String(type ?? "");
|
|
333
|
+
if (entityType === "initiative" || entityType === "workstream") {
|
|
334
|
+
const entityRecord = entity;
|
|
335
|
+
const assignment = await autoAssignEntityForCreate({
|
|
336
|
+
entityType,
|
|
337
|
+
entityId: String(entityRecord.id ?? ""),
|
|
338
|
+
initiativeId: entityType === "initiative"
|
|
339
|
+
? String(entityRecord.id ?? "")
|
|
340
|
+
: (typeof data.initiative_id === "string"
|
|
341
|
+
? data.initiative_id
|
|
342
|
+
: null),
|
|
343
|
+
title: (typeof entityRecord.title === "string" && entityRecord.title) ||
|
|
344
|
+
(typeof entityRecord.name === "string" && entityRecord.name) ||
|
|
345
|
+
(typeof data.title === "string" && data.title) ||
|
|
346
|
+
"Untitled",
|
|
347
|
+
summary: (typeof entityRecord.summary === "string" && entityRecord.summary) ||
|
|
348
|
+
(typeof data.summary === "string" && data.summary) ||
|
|
349
|
+
null,
|
|
350
|
+
});
|
|
351
|
+
if (assignment.updatedEntity) {
|
|
352
|
+
entity = assignment.updatedEntity;
|
|
353
|
+
}
|
|
354
|
+
assignmentSummary = {
|
|
355
|
+
assignment_source: assignment.assignmentSource,
|
|
356
|
+
assigned_agents: assignment.assignedAgents,
|
|
357
|
+
warnings: assignment.warnings,
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
return json(`✅ Created ${type}: ${entity.title ?? entity.id}`, {
|
|
361
|
+
entity,
|
|
362
|
+
...(assignmentSummary
|
|
363
|
+
? {
|
|
364
|
+
auto_assignment: assignmentSummary,
|
|
365
|
+
}
|
|
366
|
+
: {}),
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
catch (err) {
|
|
370
|
+
return text(`❌ Creation failed: ${err instanceof Error ? err.message : err}`);
|
|
371
|
+
}
|
|
372
|
+
},
|
|
373
|
+
}, { optional: true });
|
|
374
|
+
// --- orgx_update_entity ---
|
|
375
|
+
registerMcpTool({
|
|
376
|
+
name: "orgx_update_entity",
|
|
377
|
+
description: "Update an existing OrgX entity by type and ID.",
|
|
378
|
+
parameters: {
|
|
379
|
+
type: "object",
|
|
380
|
+
properties: {
|
|
381
|
+
type: {
|
|
382
|
+
type: "string",
|
|
383
|
+
description: "Entity type",
|
|
384
|
+
},
|
|
385
|
+
id: {
|
|
386
|
+
type: "string",
|
|
387
|
+
description: "Entity UUID",
|
|
388
|
+
},
|
|
389
|
+
status: {
|
|
390
|
+
type: "string",
|
|
391
|
+
description: "New status",
|
|
392
|
+
},
|
|
393
|
+
title: {
|
|
394
|
+
type: "string",
|
|
395
|
+
description: "New title",
|
|
396
|
+
},
|
|
397
|
+
summary: {
|
|
398
|
+
type: "string",
|
|
399
|
+
description: "New summary",
|
|
400
|
+
},
|
|
401
|
+
},
|
|
402
|
+
required: ["type", "id"],
|
|
403
|
+
},
|
|
404
|
+
async execute(_callId, params = {}) {
|
|
405
|
+
try {
|
|
406
|
+
const { type, id, ...updates } = params;
|
|
407
|
+
const entity = await client.updateEntity(type, id, updates);
|
|
408
|
+
return json(`✅ Updated ${type} ${id.slice(0, 8)}`, entity);
|
|
409
|
+
}
|
|
410
|
+
catch (err) {
|
|
411
|
+
return text(`❌ Update failed: ${err instanceof Error ? err.message : err}`);
|
|
412
|
+
}
|
|
413
|
+
},
|
|
414
|
+
}, { optional: true });
|
|
415
|
+
// --- orgx_list_entities ---
|
|
416
|
+
registerMcpTool({
|
|
417
|
+
name: "orgx_list_entities",
|
|
418
|
+
description: "List OrgX entities of a given type with optional status filter.",
|
|
419
|
+
parameters: {
|
|
420
|
+
type: "object",
|
|
421
|
+
properties: {
|
|
422
|
+
type: {
|
|
423
|
+
type: "string",
|
|
424
|
+
description: "Entity type: initiative, workstream, task, decision, agent",
|
|
425
|
+
},
|
|
426
|
+
status: {
|
|
427
|
+
type: "string",
|
|
428
|
+
description: "Filter by status",
|
|
429
|
+
},
|
|
430
|
+
limit: {
|
|
431
|
+
type: "number",
|
|
432
|
+
description: "Max results (default 20)",
|
|
433
|
+
default: 20,
|
|
434
|
+
},
|
|
435
|
+
},
|
|
436
|
+
required: ["type"],
|
|
437
|
+
},
|
|
438
|
+
async execute(_callId, params = { type: "" }) {
|
|
439
|
+
try {
|
|
440
|
+
const { type, ...filters } = params;
|
|
441
|
+
const resp = await client.listEntities(type, filters);
|
|
442
|
+
const entities = resp.data ?? resp;
|
|
443
|
+
const count = Array.isArray(entities) ? entities.length : "?";
|
|
444
|
+
return json(`${count} ${type}(s):`, entities);
|
|
445
|
+
}
|
|
446
|
+
catch (err) {
|
|
447
|
+
return text(`❌ List failed: ${err instanceof Error ? err.message : err}`);
|
|
448
|
+
}
|
|
449
|
+
},
|
|
450
|
+
}, { optional: true });
|
|
451
|
+
function withProvenanceMetadata(metadata) {
|
|
452
|
+
const input = metadata ?? {};
|
|
453
|
+
const out = { ...input };
|
|
454
|
+
if (out.orgx_plugin_version === undefined) {
|
|
455
|
+
out.orgx_plugin_version = (config.pluginVersion ?? "").trim() || null;
|
|
456
|
+
}
|
|
457
|
+
try {
|
|
458
|
+
const state = readSkillPackState();
|
|
459
|
+
const overrides = state.overrides;
|
|
460
|
+
if (out.skill_pack_name === undefined) {
|
|
461
|
+
out.skill_pack_name = overrides?.name ?? state.pack?.name ?? null;
|
|
462
|
+
}
|
|
463
|
+
if (out.skill_pack_version === undefined) {
|
|
464
|
+
out.skill_pack_version = overrides?.version ?? state.pack?.version ?? null;
|
|
465
|
+
}
|
|
466
|
+
if (out.skill_pack_checksum === undefined) {
|
|
467
|
+
out.skill_pack_checksum = overrides?.checksum ?? state.pack?.checksum ?? null;
|
|
468
|
+
}
|
|
469
|
+
if (out.skill_pack_source === undefined) {
|
|
470
|
+
out.skill_pack_source = overrides?.source ?? null;
|
|
471
|
+
}
|
|
472
|
+
if (out.skill_pack_etag === undefined) {
|
|
473
|
+
out.skill_pack_etag = state.etag ?? null;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
catch {
|
|
477
|
+
// best effort
|
|
478
|
+
}
|
|
479
|
+
if (out.orgx_provenance === undefined) {
|
|
480
|
+
out.orgx_provenance = {
|
|
481
|
+
plugin_version: out.orgx_plugin_version ?? null,
|
|
482
|
+
skill_pack: {
|
|
483
|
+
name: out.skill_pack_name ?? null,
|
|
484
|
+
version: out.skill_pack_version ?? null,
|
|
485
|
+
checksum: out.skill_pack_checksum ?? null,
|
|
486
|
+
source: out.skill_pack_source ?? null,
|
|
487
|
+
etag: out.skill_pack_etag ?? null,
|
|
488
|
+
},
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
return out;
|
|
492
|
+
}
|
|
493
|
+
async function emitActivityWithFallback(source, payload) {
|
|
494
|
+
if (!payload.message || payload.message.trim().length === 0) {
|
|
495
|
+
return text("❌ message is required");
|
|
496
|
+
}
|
|
497
|
+
const context = resolveReportingContext(payload);
|
|
498
|
+
if (!context.ok) {
|
|
499
|
+
return text(`❌ ${context.error}`);
|
|
500
|
+
}
|
|
501
|
+
const now = new Date().toISOString();
|
|
502
|
+
const id = `progress:${randomUUID().slice(0, 8)}`;
|
|
503
|
+
const normalizedPayload = {
|
|
504
|
+
initiative_id: context.value.initiativeId,
|
|
505
|
+
run_id: context.value.runId,
|
|
506
|
+
correlation_id: context.value.correlationId,
|
|
507
|
+
source_client: context.value.sourceClient,
|
|
508
|
+
message: payload.message,
|
|
509
|
+
phase: payload.phase ?? "execution",
|
|
510
|
+
progress_pct: payload.progress_pct,
|
|
511
|
+
level: payload.level ?? "info",
|
|
512
|
+
next_step: payload.next_step,
|
|
513
|
+
metadata: withProvenanceMetadata({
|
|
514
|
+
...(payload.metadata ?? {}),
|
|
515
|
+
source,
|
|
516
|
+
}),
|
|
517
|
+
};
|
|
518
|
+
const activityItem = {
|
|
519
|
+
id,
|
|
520
|
+
type: "delegation",
|
|
521
|
+
title: payload.message,
|
|
522
|
+
description: payload.next_step ?? null,
|
|
523
|
+
agentId: null,
|
|
524
|
+
agentName: null,
|
|
525
|
+
requesterAgentId: null,
|
|
526
|
+
requesterAgentName: null,
|
|
527
|
+
executorAgentId: null,
|
|
528
|
+
executorAgentName: null,
|
|
529
|
+
runId: context.value.runId ?? null,
|
|
530
|
+
initiativeId: context.value.initiativeId,
|
|
531
|
+
timestamp: now,
|
|
532
|
+
phase: normalizedPayload.phase,
|
|
533
|
+
summary: payload.next_step ? `Next: ${payload.next_step}` : payload.message,
|
|
534
|
+
metadata: normalizedPayload.metadata,
|
|
535
|
+
};
|
|
536
|
+
try {
|
|
537
|
+
const result = await client.emitActivity(normalizedPayload);
|
|
538
|
+
return text(`Activity emitted: ${payload.message} [${normalizedPayload.phase}${payload.progress_pct != null ? ` ${payload.progress_pct}%` : ""}] (run ${result.run_id.slice(0, 8)}...)`);
|
|
539
|
+
}
|
|
540
|
+
catch {
|
|
541
|
+
await appendToOutbox("progress", {
|
|
542
|
+
id,
|
|
543
|
+
type: "progress",
|
|
544
|
+
timestamp: now,
|
|
545
|
+
payload: normalizedPayload,
|
|
546
|
+
activityItem,
|
|
547
|
+
});
|
|
548
|
+
return text(`Activity saved locally: ${payload.message} [${normalizedPayload.phase}${payload.progress_pct != null ? ` ${payload.progress_pct}%` : ""}] (will sync when connected)`);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
async function applyChangesetWithFallback(source, payload) {
|
|
552
|
+
const context = resolveReportingContext(payload);
|
|
553
|
+
if (!context.ok) {
|
|
554
|
+
return text(`❌ ${context.error}`);
|
|
555
|
+
}
|
|
556
|
+
if (!Array.isArray(payload.operations) || payload.operations.length === 0) {
|
|
557
|
+
return text("❌ operations must contain at least one change");
|
|
558
|
+
}
|
|
559
|
+
const idempotencyKey = pickNonEmptyString(payload.idempotency_key) ??
|
|
560
|
+
`${source}:${Date.now()}:${randomUUID().slice(0, 8)}`;
|
|
561
|
+
const requestPayload = {
|
|
562
|
+
initiative_id: context.value.initiativeId,
|
|
563
|
+
run_id: context.value.runId,
|
|
564
|
+
correlation_id: context.value.correlationId,
|
|
565
|
+
source_client: context.value.sourceClient,
|
|
566
|
+
idempotency_key: idempotencyKey,
|
|
567
|
+
operations: payload.operations,
|
|
568
|
+
};
|
|
569
|
+
const now = new Date().toISOString();
|
|
570
|
+
const id = `changeset:${randomUUID().slice(0, 8)}`;
|
|
571
|
+
const activityItem = {
|
|
572
|
+
id,
|
|
573
|
+
type: "milestone_completed",
|
|
574
|
+
title: "Changeset queued",
|
|
575
|
+
description: `${payload.operations.length} operation${payload.operations.length === 1 ? "" : "s"}`,
|
|
576
|
+
agentId: null,
|
|
577
|
+
agentName: null,
|
|
578
|
+
requesterAgentId: null,
|
|
579
|
+
requesterAgentName: null,
|
|
580
|
+
executorAgentId: null,
|
|
581
|
+
executorAgentName: null,
|
|
582
|
+
runId: context.value.runId ?? null,
|
|
583
|
+
initiativeId: context.value.initiativeId,
|
|
584
|
+
timestamp: now,
|
|
585
|
+
phase: "review",
|
|
586
|
+
summary: `${payload.operations.length} operation${payload.operations.length === 1 ? "" : "s"}`,
|
|
587
|
+
metadata: withProvenanceMetadata({
|
|
588
|
+
source,
|
|
589
|
+
idempotency_key: idempotencyKey,
|
|
590
|
+
}),
|
|
591
|
+
};
|
|
592
|
+
try {
|
|
593
|
+
const result = await client.applyChangeset(requestPayload);
|
|
594
|
+
return text(`Changeset ${result.replayed ? "replayed" : "applied"}: ${result.applied_count} op${result.applied_count === 1 ? "" : "s"} (run ${result.run_id.slice(0, 8)}...)`);
|
|
595
|
+
}
|
|
596
|
+
catch {
|
|
597
|
+
await appendToOutbox("decisions", {
|
|
598
|
+
id,
|
|
599
|
+
type: "changeset",
|
|
600
|
+
timestamp: now,
|
|
601
|
+
payload: requestPayload,
|
|
602
|
+
activityItem,
|
|
603
|
+
});
|
|
604
|
+
return text(`Changeset saved locally (${payload.operations.length} op${payload.operations.length === 1 ? "" : "s"}) (will sync when connected)`);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
// --- orgx_emit_activity ---
|
|
608
|
+
registerMcpTool({
|
|
609
|
+
name: "orgx_emit_activity",
|
|
610
|
+
description: "Emit append-only OrgX activity telemetry (launch reporting contract primary write tool).",
|
|
611
|
+
parameters: {
|
|
612
|
+
type: "object",
|
|
613
|
+
properties: {
|
|
614
|
+
initiative_id: {
|
|
615
|
+
type: "string",
|
|
616
|
+
description: "Initiative UUID (required unless ORGX_INITIATIVE_ID is set)",
|
|
617
|
+
},
|
|
618
|
+
message: {
|
|
619
|
+
type: "string",
|
|
620
|
+
description: "Human-readable activity update",
|
|
621
|
+
},
|
|
622
|
+
run_id: {
|
|
623
|
+
type: "string",
|
|
624
|
+
description: "Optional run UUID",
|
|
625
|
+
},
|
|
626
|
+
correlation_id: {
|
|
627
|
+
type: "string",
|
|
628
|
+
description: "Required when run_id is omitted",
|
|
629
|
+
},
|
|
630
|
+
source_client: {
|
|
631
|
+
type: "string",
|
|
632
|
+
enum: ["openclaw", "codex", "claude-code", "api"],
|
|
633
|
+
description: "Required when run_id is omitted",
|
|
634
|
+
},
|
|
635
|
+
phase: {
|
|
636
|
+
type: "string",
|
|
637
|
+
enum: ["intent", "execution", "blocked", "review", "handoff", "completed"],
|
|
638
|
+
description: "Reporting phase",
|
|
639
|
+
},
|
|
640
|
+
progress_pct: {
|
|
641
|
+
type: "number",
|
|
642
|
+
minimum: 0,
|
|
643
|
+
maximum: 100,
|
|
644
|
+
description: "Optional progress percentage",
|
|
645
|
+
},
|
|
646
|
+
level: {
|
|
647
|
+
type: "string",
|
|
648
|
+
enum: ["info", "warn", "error"],
|
|
649
|
+
description: "Optional level (default info)",
|
|
650
|
+
},
|
|
651
|
+
next_step: {
|
|
652
|
+
type: "string",
|
|
653
|
+
description: "Optional next step",
|
|
654
|
+
},
|
|
655
|
+
metadata: {
|
|
656
|
+
type: "object",
|
|
657
|
+
description: "Optional structured metadata",
|
|
658
|
+
},
|
|
659
|
+
},
|
|
660
|
+
required: ["message"],
|
|
661
|
+
additionalProperties: false,
|
|
662
|
+
},
|
|
663
|
+
async execute(_callId, params = { message: "" }) {
|
|
664
|
+
return emitActivityWithFallback("orgx_emit_activity", params);
|
|
665
|
+
},
|
|
666
|
+
}, { optional: true });
|
|
667
|
+
// --- orgx_apply_changeset ---
|
|
668
|
+
registerMcpTool({
|
|
669
|
+
name: "orgx_apply_changeset",
|
|
670
|
+
description: "Apply an idempotent transactional OrgX changeset (launch reporting contract primary mutation tool).",
|
|
671
|
+
parameters: {
|
|
672
|
+
type: "object",
|
|
673
|
+
properties: {
|
|
674
|
+
initiative_id: {
|
|
675
|
+
type: "string",
|
|
676
|
+
description: "Initiative UUID (required unless ORGX_INITIATIVE_ID is set)",
|
|
677
|
+
},
|
|
678
|
+
idempotency_key: {
|
|
679
|
+
type: "string",
|
|
680
|
+
description: "Idempotency key (<=120 chars). Auto-generated if omitted.",
|
|
681
|
+
},
|
|
682
|
+
operations: {
|
|
683
|
+
type: "array",
|
|
684
|
+
minItems: 1,
|
|
685
|
+
maxItems: 25,
|
|
686
|
+
description: "Changeset operations (task.create, task.update, milestone.update, decision.create)",
|
|
687
|
+
items: { type: "object" },
|
|
688
|
+
},
|
|
689
|
+
run_id: {
|
|
690
|
+
type: "string",
|
|
691
|
+
description: "Optional run UUID",
|
|
692
|
+
},
|
|
693
|
+
correlation_id: {
|
|
694
|
+
type: "string",
|
|
695
|
+
description: "Required when run_id is omitted",
|
|
696
|
+
},
|
|
697
|
+
source_client: {
|
|
698
|
+
type: "string",
|
|
699
|
+
enum: ["openclaw", "codex", "claude-code", "api"],
|
|
700
|
+
description: "Required when run_id is omitted",
|
|
701
|
+
},
|
|
702
|
+
},
|
|
703
|
+
required: ["operations"],
|
|
704
|
+
additionalProperties: false,
|
|
705
|
+
},
|
|
706
|
+
async execute(_callId, params = { operations: [] }) {
|
|
707
|
+
return applyChangesetWithFallback("orgx_apply_changeset", params);
|
|
708
|
+
},
|
|
709
|
+
}, { optional: true });
|
|
710
|
+
// --- orgx_report_progress (alias -> orgx_emit_activity) ---
|
|
711
|
+
registerMcpTool({
|
|
712
|
+
name: "orgx_report_progress",
|
|
713
|
+
description: "Alias for orgx_emit_activity. Report progress at key milestones so the team can track your work.",
|
|
714
|
+
parameters: {
|
|
715
|
+
type: "object",
|
|
716
|
+
properties: {
|
|
717
|
+
initiative_id: {
|
|
718
|
+
type: "string",
|
|
719
|
+
description: "Initiative UUID (required unless ORGX_INITIATIVE_ID is set)",
|
|
720
|
+
},
|
|
721
|
+
run_id: {
|
|
722
|
+
type: "string",
|
|
723
|
+
description: "Optional run UUID",
|
|
724
|
+
},
|
|
725
|
+
correlation_id: {
|
|
726
|
+
type: "string",
|
|
727
|
+
description: "Required when run_id is omitted",
|
|
728
|
+
},
|
|
729
|
+
source_client: {
|
|
730
|
+
type: "string",
|
|
731
|
+
enum: ["openclaw", "codex", "claude-code", "api"],
|
|
732
|
+
},
|
|
733
|
+
summary: {
|
|
734
|
+
type: "string",
|
|
735
|
+
description: "What was accomplished (1-2 sentences, human-readable)",
|
|
736
|
+
},
|
|
737
|
+
phase: {
|
|
738
|
+
type: "string",
|
|
739
|
+
enum: ["researching", "implementing", "testing", "reviewing", "blocked"],
|
|
740
|
+
description: "Current work phase",
|
|
741
|
+
},
|
|
742
|
+
progress_pct: {
|
|
743
|
+
type: "number",
|
|
744
|
+
description: "Progress percentage (0-100)",
|
|
745
|
+
minimum: 0,
|
|
746
|
+
maximum: 100,
|
|
747
|
+
},
|
|
748
|
+
next_step: {
|
|
749
|
+
type: "string",
|
|
750
|
+
description: "What you plan to do next",
|
|
751
|
+
},
|
|
752
|
+
},
|
|
753
|
+
required: ["summary", "phase"],
|
|
754
|
+
additionalProperties: false,
|
|
755
|
+
},
|
|
756
|
+
async execute(_callId, params = { summary: "", phase: "implementing" }) {
|
|
757
|
+
return emitActivityWithFallback("orgx_report_progress", {
|
|
758
|
+
initiative_id: params.initiative_id,
|
|
759
|
+
run_id: params.run_id,
|
|
760
|
+
correlation_id: params.correlation_id,
|
|
761
|
+
source_client: params.source_client,
|
|
762
|
+
message: params.summary,
|
|
763
|
+
phase: toReportingPhase(params.phase, params.progress_pct),
|
|
764
|
+
progress_pct: params.progress_pct,
|
|
765
|
+
next_step: params.next_step,
|
|
766
|
+
level: params.phase === "blocked" ? "warn" : "info",
|
|
767
|
+
metadata: {
|
|
768
|
+
legacy_phase: params.phase,
|
|
769
|
+
},
|
|
770
|
+
});
|
|
771
|
+
},
|
|
772
|
+
}, { optional: true });
|
|
773
|
+
// --- orgx_request_decision (alias -> orgx_apply_changeset decision.create) ---
|
|
774
|
+
registerMcpTool({
|
|
775
|
+
name: "orgx_request_decision",
|
|
776
|
+
description: "Alias for orgx_apply_changeset with decision.create. Request a human decision before proceeding.",
|
|
777
|
+
parameters: {
|
|
778
|
+
type: "object",
|
|
779
|
+
properties: {
|
|
780
|
+
initiative_id: {
|
|
781
|
+
type: "string",
|
|
782
|
+
description: "Initiative UUID (required unless ORGX_INITIATIVE_ID is set)",
|
|
783
|
+
},
|
|
784
|
+
run_id: {
|
|
785
|
+
type: "string",
|
|
786
|
+
description: "Optional run UUID",
|
|
787
|
+
},
|
|
788
|
+
correlation_id: {
|
|
789
|
+
type: "string",
|
|
790
|
+
description: "Required when run_id is omitted",
|
|
791
|
+
},
|
|
792
|
+
source_client: {
|
|
793
|
+
type: "string",
|
|
794
|
+
enum: ["openclaw", "codex", "claude-code", "api"],
|
|
795
|
+
},
|
|
796
|
+
question: {
|
|
797
|
+
type: "string",
|
|
798
|
+
description: "The decision question (e.g., 'Deploy to production?')",
|
|
799
|
+
},
|
|
800
|
+
context: {
|
|
801
|
+
type: "string",
|
|
802
|
+
description: "Background context to help the human decide",
|
|
803
|
+
},
|
|
804
|
+
options: {
|
|
805
|
+
type: "array",
|
|
806
|
+
items: { type: "string" },
|
|
807
|
+
description: "Available choices (e.g., ['Yes, deploy now', 'Wait for more testing', 'Cancel'])",
|
|
808
|
+
},
|
|
809
|
+
urgency: {
|
|
810
|
+
type: "string",
|
|
811
|
+
enum: ["low", "medium", "high", "urgent"],
|
|
812
|
+
description: "How urgent this decision is",
|
|
813
|
+
},
|
|
814
|
+
blocking: {
|
|
815
|
+
type: "boolean",
|
|
816
|
+
description: "Whether work should pause until this is decided (default: true)",
|
|
817
|
+
},
|
|
818
|
+
},
|
|
819
|
+
required: ["question", "urgency"],
|
|
820
|
+
additionalProperties: false,
|
|
821
|
+
},
|
|
822
|
+
async execute(_callId, params = { question: "", urgency: "medium" }) {
|
|
823
|
+
const requestId = `decision:${randomUUID().slice(0, 8)}`;
|
|
824
|
+
const changesetResult = await applyChangesetWithFallback("orgx_request_decision", {
|
|
825
|
+
initiative_id: params.initiative_id,
|
|
826
|
+
run_id: params.run_id,
|
|
827
|
+
correlation_id: params.correlation_id,
|
|
828
|
+
source_client: params.source_client,
|
|
829
|
+
idempotency_key: `decision:${requestId}`,
|
|
830
|
+
operations: [
|
|
831
|
+
{
|
|
832
|
+
op: "decision.create",
|
|
833
|
+
title: params.question,
|
|
834
|
+
summary: params.context,
|
|
835
|
+
urgency: params.urgency,
|
|
836
|
+
options: params.options,
|
|
837
|
+
blocking: params.blocking ?? true,
|
|
838
|
+
},
|
|
839
|
+
],
|
|
840
|
+
});
|
|
841
|
+
await emitActivityWithFallback("orgx_request_decision", {
|
|
842
|
+
initiative_id: params.initiative_id,
|
|
843
|
+
run_id: params.run_id,
|
|
844
|
+
correlation_id: params.correlation_id,
|
|
845
|
+
source_client: params.source_client,
|
|
846
|
+
message: `Decision requested: ${params.question}`,
|
|
847
|
+
phase: "review",
|
|
848
|
+
level: "info",
|
|
849
|
+
metadata: {
|
|
850
|
+
urgency: params.urgency,
|
|
851
|
+
blocking: params.blocking ?? true,
|
|
852
|
+
options: params.options ?? [],
|
|
853
|
+
},
|
|
854
|
+
});
|
|
855
|
+
return changesetResult;
|
|
856
|
+
},
|
|
857
|
+
}, { optional: true });
|
|
858
|
+
// --- orgx_register_artifact ---
|
|
859
|
+
registerMcpTool({
|
|
860
|
+
name: "orgx_register_artifact",
|
|
861
|
+
description: "Register a work output (PR, document, config change, report, etc.) as a work_artifact in OrgX. Makes it visible in the dashboard activity timeline and entity detail modals.",
|
|
862
|
+
parameters: {
|
|
863
|
+
type: "object",
|
|
864
|
+
properties: {
|
|
865
|
+
initiative_id: {
|
|
866
|
+
type: "string",
|
|
867
|
+
description: "Convenience: initiative UUID. Used as entity_type='initiative', entity_id=<this> when entity_type/entity_id are not provided.",
|
|
868
|
+
},
|
|
869
|
+
entity_type: {
|
|
870
|
+
type: "string",
|
|
871
|
+
enum: ["initiative", "milestone", "task", "decision", "project"],
|
|
872
|
+
description: "The type of entity this artifact is attached to",
|
|
873
|
+
},
|
|
874
|
+
entity_id: {
|
|
875
|
+
type: "string",
|
|
876
|
+
description: "UUID of the entity this artifact is attached to",
|
|
877
|
+
},
|
|
878
|
+
name: {
|
|
879
|
+
type: "string",
|
|
880
|
+
description: "Human-readable artifact name (e.g., 'PR #107: Fix build size')",
|
|
881
|
+
},
|
|
882
|
+
artifact_type: {
|
|
883
|
+
type: "string",
|
|
884
|
+
description: "Artifact type code (e.g., 'eng.diff_pack', 'pr', 'document'). Falls back to 'shared.project_handbook' if the type is not recognized by OrgX.",
|
|
885
|
+
},
|
|
886
|
+
description: {
|
|
887
|
+
type: "string",
|
|
888
|
+
description: "What this artifact is and why it matters",
|
|
889
|
+
},
|
|
890
|
+
url: {
|
|
891
|
+
type: "string",
|
|
892
|
+
description: "External link to the artifact (PR URL, file path, etc.)",
|
|
893
|
+
},
|
|
894
|
+
content: {
|
|
895
|
+
type: "string",
|
|
896
|
+
description: "Inline preview content (markdown/text). At least one of url or content is required.",
|
|
897
|
+
},
|
|
898
|
+
},
|
|
899
|
+
required: ["name", "artifact_type"],
|
|
900
|
+
additionalProperties: false,
|
|
901
|
+
},
|
|
902
|
+
async execute(_callId, params = { name: "", artifact_type: "other" }) {
|
|
903
|
+
const now = new Date().toISOString();
|
|
904
|
+
const id = `artifact:${randomUUID().slice(0, 8)}`;
|
|
905
|
+
// Resolve entity association: explicit entity_type+entity_id > initiative_id > inferred
|
|
906
|
+
let resolvedEntityType = null;
|
|
907
|
+
let resolvedEntityId = null;
|
|
908
|
+
if (params.entity_type && isUuid(params.entity_id)) {
|
|
909
|
+
resolvedEntityType = params.entity_type;
|
|
910
|
+
resolvedEntityId = params.entity_id;
|
|
911
|
+
}
|
|
912
|
+
else if (isUuid(params.initiative_id)) {
|
|
913
|
+
resolvedEntityType = "initiative";
|
|
914
|
+
resolvedEntityId = params.initiative_id;
|
|
915
|
+
}
|
|
916
|
+
else {
|
|
917
|
+
const inferred = inferReportingInitiativeId(params);
|
|
918
|
+
if (inferred) {
|
|
919
|
+
resolvedEntityType = "initiative";
|
|
920
|
+
resolvedEntityId = inferred;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
if (!resolvedEntityType || !resolvedEntityId) {
|
|
924
|
+
return text("❌ Cannot register artifact: provide entity_type + entity_id, or initiative_id, so the artifact can be attached to an entity.");
|
|
925
|
+
}
|
|
926
|
+
if (!params.url && !params.content) {
|
|
927
|
+
return text("❌ Cannot register artifact: provide at least one of url or content.");
|
|
928
|
+
}
|
|
929
|
+
const baseUrl = client.getBaseUrl();
|
|
930
|
+
const artifactId = randomUUID();
|
|
931
|
+
const activityItem = {
|
|
932
|
+
id,
|
|
933
|
+
type: "artifact_created",
|
|
934
|
+
title: params.name,
|
|
935
|
+
description: params.description ?? null,
|
|
936
|
+
agentId: null,
|
|
937
|
+
agentName: null,
|
|
938
|
+
requesterAgentId: null,
|
|
939
|
+
requesterAgentName: null,
|
|
940
|
+
executorAgentId: null,
|
|
941
|
+
executorAgentName: null,
|
|
942
|
+
runId: null,
|
|
943
|
+
initiativeId: resolvedEntityType === "initiative" ? resolvedEntityId : null,
|
|
944
|
+
timestamp: now,
|
|
945
|
+
summary: params.url ?? null,
|
|
946
|
+
metadata: withProvenanceMetadata({
|
|
947
|
+
source: "orgx_register_artifact",
|
|
948
|
+
artifact_type: params.artifact_type,
|
|
949
|
+
url: params.url,
|
|
950
|
+
entity_type: resolvedEntityType,
|
|
951
|
+
entity_id: resolvedEntityId,
|
|
952
|
+
}),
|
|
953
|
+
};
|
|
954
|
+
try {
|
|
955
|
+
const result = await registerArtifact(client, baseUrl, {
|
|
956
|
+
artifact_id: artifactId,
|
|
957
|
+
entity_type: resolvedEntityType,
|
|
958
|
+
entity_id: resolvedEntityId,
|
|
959
|
+
name: params.name,
|
|
960
|
+
artifact_type: params.artifact_type,
|
|
961
|
+
description: params.description ?? null,
|
|
962
|
+
external_url: params.url ?? null,
|
|
963
|
+
preview_markdown: params.content ?? null,
|
|
964
|
+
status: "draft",
|
|
965
|
+
metadata: {
|
|
966
|
+
source: "orgx_register_artifact",
|
|
967
|
+
artifact_id: artifactId,
|
|
968
|
+
},
|
|
969
|
+
validate_persistence: true,
|
|
970
|
+
});
|
|
971
|
+
if (!result.ok) {
|
|
972
|
+
throw new Error(result.persistence.last_error ?? "Artifact registration failed");
|
|
973
|
+
}
|
|
974
|
+
activityItem.metadata = withProvenanceMetadata({
|
|
975
|
+
...activityItem.metadata,
|
|
976
|
+
artifact_id: result.artifact_id,
|
|
977
|
+
entity_type: resolvedEntityType,
|
|
978
|
+
entity_id: resolvedEntityId,
|
|
979
|
+
});
|
|
980
|
+
return json(`Artifact registered: ${params.name} [${params.artifact_type}] → ${resolvedEntityType}/${resolvedEntityId} (id: ${result.artifact_id})`, result);
|
|
981
|
+
}
|
|
982
|
+
catch (firstError) {
|
|
983
|
+
// Outbox fallback for offline/error scenarios
|
|
984
|
+
await appendToOutbox("artifacts", {
|
|
985
|
+
id,
|
|
986
|
+
type: "artifact",
|
|
987
|
+
timestamp: now,
|
|
988
|
+
payload: {
|
|
989
|
+
artifact_id: artifactId,
|
|
990
|
+
name: params.name,
|
|
991
|
+
artifact_type: params.artifact_type,
|
|
992
|
+
description: params.description,
|
|
993
|
+
url: params.url,
|
|
994
|
+
content: params.content,
|
|
995
|
+
entity_type: resolvedEntityType,
|
|
996
|
+
entity_id: resolvedEntityId,
|
|
997
|
+
},
|
|
998
|
+
activityItem,
|
|
999
|
+
});
|
|
1000
|
+
return text(`Artifact saved locally: ${params.name} [${params.artifact_type}] (will sync when connected)`);
|
|
1001
|
+
}
|
|
1002
|
+
},
|
|
1003
|
+
}, { optional: true });
|
|
1004
|
+
return mcpToolRegistry;
|
|
1005
|
+
}
|