@useorgx/openclaw-plugin 0.4.0 → 0.4.3
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 +4 -0
- package/dashboard/dist/assets/BqukHQH-.js +8 -0
- package/dashboard/dist/assets/{motion-x9c01cgK.js → CE5pVdev.js} +1 -1
- package/dashboard/dist/assets/Cpr7n8fE.js +1 -0
- package/dashboard/dist/assets/Nip3CrNC.js +1 -0
- package/dashboard/dist/assets/TN5wE36J.js +1 -0
- package/dashboard/dist/assets/X6IcjS74.js +212 -0
- package/dashboard/dist/assets/jyFhCND-.css +1 -0
- package/dashboard/dist/index.html +9 -6
- package/dist/adapters/outbox.d.ts +0 -1
- package/dist/adapters/outbox.js +0 -1
- package/dist/agent-context-store.d.ts +0 -1
- package/dist/agent-context-store.js +0 -1
- package/dist/agent-run-store.d.ts +0 -1
- package/dist/agent-run-store.js +0 -1
- package/dist/api.d.ts +0 -1
- package/dist/api.js +0 -1
- package/dist/auth-store.d.ts +0 -1
- package/dist/auth-store.js +0 -1
- package/dist/byok-store.d.ts +0 -1
- package/dist/byok-store.js +181 -55
- package/dist/contracts/client.d.ts +0 -1
- package/dist/contracts/client.js +0 -1
- package/dist/contracts/types.d.ts +0 -1
- package/dist/contracts/types.js +0 -1
- package/dist/dashboard-api.d.ts +0 -1
- package/dist/dashboard-api.js +0 -1
- package/dist/fs-utils.d.ts +0 -1
- package/dist/fs-utils.js +0 -1
- package/dist/gateway-watchdog-runner.d.ts +1 -0
- package/dist/gateway-watchdog-runner.js +6 -0
- package/dist/gateway-watchdog.d.ts +11 -0
- package/dist/gateway-watchdog.js +221 -0
- package/dist/http-handler.d.ts +0 -1
- package/dist/http-handler.js +729 -79
- package/dist/index.d.ts +0 -1
- package/dist/index.js +7 -1
- package/dist/local-openclaw.d.ts +0 -1
- package/dist/local-openclaw.js +0 -1
- package/dist/openclaw-settings.d.ts +17 -0
- package/dist/openclaw-settings.js +118 -0
- package/dist/outbox.d.ts +0 -1
- package/dist/outbox.js +0 -1
- package/dist/paths.d.ts +0 -1
- package/dist/paths.js +0 -1
- package/dist/reporting/outbox-replay.d.ts +0 -1
- package/dist/reporting/outbox-replay.js +0 -1
- package/dist/reporting/rollups.d.ts +0 -1
- package/dist/reporting/rollups.js +0 -1
- package/dist/runtime-instance-store.d.ts +0 -1
- package/dist/runtime-instance-store.js +0 -1
- package/dist/snapshot-store.d.ts +0 -1
- package/dist/snapshot-store.js +0 -1
- package/dist/types.d.ts +0 -1
- package/dist/types.js +0 -1
- package/package.json +2 -2
- package/dashboard/dist/assets/MissionControlView-DVNfDWKZ.js +0 -1
- package/dashboard/dist/assets/SessionInspector-BaqnAys4.js +0 -1
- package/dashboard/dist/assets/index-B4Yix84X.js +0 -212
- package/dashboard/dist/assets/index-BWSvw1HR.css +0 -1
- package/dashboard/dist/assets/react-vendor-C2t2w4r2.js +0 -32
- package/dashboard/dist/assets/vendor-C-AHK0Ly.js +0 -9
- package/dist/adapters/outbox.d.ts.map +0 -1
- package/dist/adapters/outbox.js.map +0 -1
- package/dist/agent-context-store.d.ts.map +0 -1
- package/dist/agent-context-store.js.map +0 -1
- package/dist/agent-run-store.d.ts.map +0 -1
- package/dist/agent-run-store.js.map +0 -1
- package/dist/api.d.ts.map +0 -1
- package/dist/api.js.map +0 -1
- package/dist/auth-store.d.ts.map +0 -1
- package/dist/auth-store.js.map +0 -1
- package/dist/byok-store.d.ts.map +0 -1
- package/dist/byok-store.js.map +0 -1
- package/dist/contracts/client.d.ts.map +0 -1
- package/dist/contracts/client.js.map +0 -1
- package/dist/contracts/types.d.ts.map +0 -1
- package/dist/contracts/types.js.map +0 -1
- package/dist/dashboard-api.d.ts.map +0 -1
- package/dist/dashboard-api.js.map +0 -1
- package/dist/fs-utils.d.ts.map +0 -1
- package/dist/fs-utils.js.map +0 -1
- package/dist/http-handler.d.ts.map +0 -1
- package/dist/http-handler.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/local-openclaw.d.ts.map +0 -1
- package/dist/local-openclaw.js.map +0 -1
- package/dist/mcp-apps/orgx-live.html +0 -690
- package/dist/outbox.d.ts.map +0 -1
- package/dist/outbox.js.map +0 -1
- package/dist/paths.d.ts.map +0 -1
- package/dist/paths.js.map +0 -1
- package/dist/reporting/outbox-replay.d.ts.map +0 -1
- package/dist/reporting/outbox-replay.js.map +0 -1
- package/dist/reporting/rollups.d.ts.map +0 -1
- package/dist/reporting/rollups.js.map +0 -1
- package/dist/runtime-instance-store.d.ts.map +0 -1
- package/dist/runtime-instance-store.js.map +0 -1
- package/dist/snapshot-store.d.ts.map +0 -1
- package/dist/snapshot-store.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- /package/dashboard/dist/assets/{tanstack-C-KIc3Wc.js → C-KIc3Wc.js} +0 -0
- /package/dashboard/dist/assets/{orgx-logo-Fm0FhtnV.png → Fm0FhtnV.png} +0 -0
package/dist/http-handler.js
CHANGED
|
@@ -30,6 +30,7 @@ import { getAgentRun, markAgentRunStopped, readAgentRuns, upsertAgentRun, } from
|
|
|
30
30
|
import { readByokKeys, writeByokKeys } from "./byok-store.js";
|
|
31
31
|
import { computeMilestoneRollup, computeWorkstreamRollup, } from "./reporting/rollups.js";
|
|
32
32
|
import { listRuntimeInstances, resolveRuntimeHookToken, upsertRuntimeInstanceFromHook, } from "./runtime-instance-store.js";
|
|
33
|
+
import { readOpenClawSettingsSnapshot, resolvePreferredOpenClawProvider, } from "./openclaw-settings.js";
|
|
33
34
|
// =============================================================================
|
|
34
35
|
// Helpers
|
|
35
36
|
// =============================================================================
|
|
@@ -203,48 +204,60 @@ async function setOpenClawAgentModel(input) {
|
|
|
203
204
|
}
|
|
204
205
|
}
|
|
205
206
|
async function listOpenClawProviderModels(input) {
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
"
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
const tags = Array.isArray(row.tags)
|
|
241
|
-
? row.tags.filter((t) => typeof t === "string")
|
|
207
|
+
const providerArgs = input.provider === "openai" ? ["openai-codex", "openai"] : [input.provider];
|
|
208
|
+
let lastError = null;
|
|
209
|
+
for (const providerArg of providerArgs) {
|
|
210
|
+
const result = await runCommandCollect({
|
|
211
|
+
command: "openclaw",
|
|
212
|
+
args: [
|
|
213
|
+
"models",
|
|
214
|
+
"--agent",
|
|
215
|
+
input.agentId,
|
|
216
|
+
"list",
|
|
217
|
+
"--provider",
|
|
218
|
+
providerArg,
|
|
219
|
+
"--json",
|
|
220
|
+
],
|
|
221
|
+
timeoutMs: 10_000,
|
|
222
|
+
env: resolveByokEnvOverrides(),
|
|
223
|
+
});
|
|
224
|
+
if (result.exitCode !== 0) {
|
|
225
|
+
lastError = new Error(result.stderr.trim() || "openclaw models list failed");
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
const parsed = parseJsonSafe(result.stdout);
|
|
229
|
+
if (!parsed || typeof parsed !== "object") {
|
|
230
|
+
const trimmed = result.stdout.trim();
|
|
231
|
+
if (!trimmed || /no models found/i.test(trimmed)) {
|
|
232
|
+
if (providerArg === providerArgs[providerArgs.length - 1])
|
|
233
|
+
return [];
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
lastError = new Error("openclaw models list returned invalid JSON");
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
const modelsRaw = "models" in parsed && Array.isArray(parsed.models)
|
|
240
|
+
? parsed.models
|
|
242
241
|
: [];
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
242
|
+
const models = modelsRaw
|
|
243
|
+
.map((entry) => {
|
|
244
|
+
if (!entry || typeof entry !== "object")
|
|
245
|
+
return null;
|
|
246
|
+
const row = entry;
|
|
247
|
+
const key = typeof row.key === "string" ? row.key.trim() : "";
|
|
248
|
+
const tags = Array.isArray(row.tags)
|
|
249
|
+
? row.tags.filter((t) => typeof t === "string")
|
|
250
|
+
: [];
|
|
251
|
+
if (!key)
|
|
252
|
+
return null;
|
|
253
|
+
return { key, tags };
|
|
254
|
+
})
|
|
255
|
+
.filter((entry) => Boolean(entry));
|
|
256
|
+
if (models.length > 0 || providerArg === providerArgs[providerArgs.length - 1]) {
|
|
257
|
+
return models;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
throw lastError ?? new Error("openclaw models list failed");
|
|
248
261
|
}
|
|
249
262
|
function pickPreferredModel(models) {
|
|
250
263
|
if (models.length === 0)
|
|
@@ -256,7 +269,7 @@ async function configureOpenClawProviderRouting(input) {
|
|
|
256
269
|
const requestedModel = (input.requestedModel ?? "").trim() || null;
|
|
257
270
|
// Fast path: use known aliases where possible.
|
|
258
271
|
const aliasByProvider = {
|
|
259
|
-
anthropic: "
|
|
272
|
+
anthropic: "sonnet",
|
|
260
273
|
openrouter: "sonnet",
|
|
261
274
|
openai: null,
|
|
262
275
|
};
|
|
@@ -281,6 +294,18 @@ async function configureOpenClawProviderRouting(input) {
|
|
|
281
294
|
await setOpenClawAgentModel({ agentId: input.agentId, model: selected });
|
|
282
295
|
return { provider: input.provider, model: selected };
|
|
283
296
|
}
|
|
297
|
+
function resolveAutoOpenClawProvider() {
|
|
298
|
+
try {
|
|
299
|
+
const settings = readOpenClawSettingsSnapshot();
|
|
300
|
+
const provider = resolvePreferredOpenClawProvider(settings.raw);
|
|
301
|
+
if (!provider)
|
|
302
|
+
return null;
|
|
303
|
+
return provider;
|
|
304
|
+
}
|
|
305
|
+
catch {
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
284
309
|
function isPidAlive(pid) {
|
|
285
310
|
if (!Number.isFinite(pid) || pid <= 0)
|
|
286
311
|
return false;
|
|
@@ -808,11 +833,30 @@ const CORS_HEADERS = {
|
|
|
808
833
|
"Access-Control-Allow-Headers": "Content-Type, Authorization, X-OrgX-Api-Key, X-API-Key, X-OrgX-User-Id, X-OrgX-Hook-Token, X-Hook-Token",
|
|
809
834
|
Vary: "Origin",
|
|
810
835
|
};
|
|
836
|
+
const CONTENT_SECURITY_POLICY = [
|
|
837
|
+
"default-src 'self'",
|
|
838
|
+
"base-uri 'self'",
|
|
839
|
+
"frame-ancestors 'none'",
|
|
840
|
+
"form-action 'self'",
|
|
841
|
+
"object-src 'none'",
|
|
842
|
+
"script-src 'self'",
|
|
843
|
+
"style-src 'self' 'unsafe-inline'",
|
|
844
|
+
"img-src 'self' data: blob:",
|
|
845
|
+
"font-src 'self' data:",
|
|
846
|
+
"media-src 'self'",
|
|
847
|
+
"connect-src 'self' https://*.useorgx.com https://*.openclaw.ai http://127.0.0.1:* http://localhost:*",
|
|
848
|
+
].join("; ");
|
|
811
849
|
const SECURITY_HEADERS = {
|
|
812
850
|
"X-Content-Type-Options": "nosniff",
|
|
813
851
|
"X-Frame-Options": "DENY",
|
|
814
852
|
"Referrer-Policy": "same-origin",
|
|
853
|
+
"X-Robots-Tag": "noindex, nofollow, noarchive, nosnippet, noimageindex",
|
|
854
|
+
"Permissions-Policy": "camera=(), microphone=(), geolocation=(), payment=(), usb=(), midi=(), magnetometer=(), gyroscope=()",
|
|
855
|
+
"Cross-Origin-Opener-Policy": "same-origin",
|
|
815
856
|
"Cross-Origin-Resource-Policy": "same-origin",
|
|
857
|
+
"Origin-Agent-Cluster": "?1",
|
|
858
|
+
"X-Permitted-Cross-Domain-Policies": "none",
|
|
859
|
+
"Content-Security-Policy": CONTENT_SECURITY_POLICY,
|
|
816
860
|
};
|
|
817
861
|
function normalizeHost(value) {
|
|
818
862
|
return value.trim().toLowerCase().replace(/^\[|\]$/g, "");
|
|
@@ -1215,6 +1259,86 @@ function idempotencyKey(parts) {
|
|
|
1215
1259
|
const suffix = stableHash(raw).slice(0, 20);
|
|
1216
1260
|
return `${cleaned}:${suffix}`.slice(0, 120);
|
|
1217
1261
|
}
|
|
1262
|
+
const ORGX_SKILL_BY_DOMAIN = {
|
|
1263
|
+
engineering: "orgx-engineering-agent",
|
|
1264
|
+
product: "orgx-product-agent",
|
|
1265
|
+
marketing: "orgx-marketing-agent",
|
|
1266
|
+
sales: "orgx-sales-agent",
|
|
1267
|
+
operations: "orgx-operations-agent",
|
|
1268
|
+
design: "orgx-design-agent",
|
|
1269
|
+
orchestration: "orgx-orchestrator-agent",
|
|
1270
|
+
};
|
|
1271
|
+
function normalizeExecutionDomain(value) {
|
|
1272
|
+
const raw = (value ?? "").trim().toLowerCase();
|
|
1273
|
+
if (!raw)
|
|
1274
|
+
return null;
|
|
1275
|
+
if (raw === "orchestrator")
|
|
1276
|
+
return "orchestration";
|
|
1277
|
+
if (raw === "ops")
|
|
1278
|
+
return "operations";
|
|
1279
|
+
return Object.prototype.hasOwnProperty.call(ORGX_SKILL_BY_DOMAIN, raw)
|
|
1280
|
+
? raw
|
|
1281
|
+
: null;
|
|
1282
|
+
}
|
|
1283
|
+
function inferExecutionDomainFromText(...values) {
|
|
1284
|
+
const text = values
|
|
1285
|
+
.map((value) => (value ?? "").trim().toLowerCase())
|
|
1286
|
+
.filter((value) => value.length > 0)
|
|
1287
|
+
.join(" ");
|
|
1288
|
+
if (!text)
|
|
1289
|
+
return "engineering";
|
|
1290
|
+
if (/\b(marketing|campaign|copy|ad|content)\b/.test(text))
|
|
1291
|
+
return "marketing";
|
|
1292
|
+
if (/\b(sales|meddic|pipeline|deal|outreach)\b/.test(text))
|
|
1293
|
+
return "sales";
|
|
1294
|
+
if (/\b(design|ui|ux|brand|wcag)\b/.test(text))
|
|
1295
|
+
return "design";
|
|
1296
|
+
if (/\b(product|prd|roadmap|prioritization)\b/.test(text))
|
|
1297
|
+
return "product";
|
|
1298
|
+
if (/\b(ops|operations|incident|reliability|oncall|slo)\b/.test(text))
|
|
1299
|
+
return "operations";
|
|
1300
|
+
if (/\b(orchestration|dispatch|handoff)\b/.test(text))
|
|
1301
|
+
return "orchestration";
|
|
1302
|
+
return "engineering";
|
|
1303
|
+
}
|
|
1304
|
+
function deriveExecutionPolicy(taskNode, workstreamNode) {
|
|
1305
|
+
const domainCandidate = taskNode.assignedAgents
|
|
1306
|
+
.map((agent) => normalizeExecutionDomain(agent.domain))
|
|
1307
|
+
.find((domain) => Boolean(domain)) ??
|
|
1308
|
+
(workstreamNode
|
|
1309
|
+
? workstreamNode.assignedAgents
|
|
1310
|
+
.map((agent) => normalizeExecutionDomain(agent.domain))
|
|
1311
|
+
.find((domain) => Boolean(domain))
|
|
1312
|
+
: null) ??
|
|
1313
|
+
inferExecutionDomainFromText(taskNode.title, workstreamNode?.title ?? null);
|
|
1314
|
+
const domain = normalizeExecutionDomain(domainCandidate) ?? "engineering";
|
|
1315
|
+
const requiredSkill = ORGX_SKILL_BY_DOMAIN[domain] ?? ORGX_SKILL_BY_DOMAIN.engineering;
|
|
1316
|
+
return { domain, requiredSkills: [requiredSkill] };
|
|
1317
|
+
}
|
|
1318
|
+
function spawnGuardIsRateLimited(result) {
|
|
1319
|
+
if (!result || typeof result !== "object")
|
|
1320
|
+
return false;
|
|
1321
|
+
const record = result;
|
|
1322
|
+
const checks = record.checks;
|
|
1323
|
+
if (!checks || typeof checks !== "object")
|
|
1324
|
+
return false;
|
|
1325
|
+
const rateLimit = checks.rateLimit;
|
|
1326
|
+
if (!rateLimit || typeof rateLimit !== "object")
|
|
1327
|
+
return false;
|
|
1328
|
+
return rateLimit.passed === false;
|
|
1329
|
+
}
|
|
1330
|
+
function summarizeSpawnGuardBlockReason(result) {
|
|
1331
|
+
if (!result || typeof result !== "object")
|
|
1332
|
+
return "Spawn guard denied dispatch.";
|
|
1333
|
+
const record = result;
|
|
1334
|
+
const blockedReason = pickString(record, ["blockedReason", "blocked_reason"]);
|
|
1335
|
+
if (blockedReason)
|
|
1336
|
+
return blockedReason;
|
|
1337
|
+
if (spawnGuardIsRateLimited(result)) {
|
|
1338
|
+
return "Spawn guard rate limit reached.";
|
|
1339
|
+
}
|
|
1340
|
+
return "Spawn guard denied dispatch.";
|
|
1341
|
+
}
|
|
1218
1342
|
const DEFAULT_DURATION_HOURS = {
|
|
1219
1343
|
initiative: 40,
|
|
1220
1344
|
workstream: 16,
|
|
@@ -2106,6 +2230,263 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
2106
2230
|
}
|
|
2107
2231
|
}
|
|
2108
2232
|
}
|
|
2233
|
+
async function requestDecisionSafe(input) {
|
|
2234
|
+
const initiativeId = input.initiativeId?.trim() ?? "";
|
|
2235
|
+
const title = input.title.trim();
|
|
2236
|
+
if (!initiativeId || !title)
|
|
2237
|
+
return;
|
|
2238
|
+
try {
|
|
2239
|
+
await client.applyChangeset({
|
|
2240
|
+
initiative_id: initiativeId,
|
|
2241
|
+
correlation_id: input.correlationId?.trim() || undefined,
|
|
2242
|
+
source_client: "openclaw",
|
|
2243
|
+
idempotency_key: idempotencyKey([
|
|
2244
|
+
"openclaw",
|
|
2245
|
+
"decision",
|
|
2246
|
+
initiativeId,
|
|
2247
|
+
title,
|
|
2248
|
+
input.correlationId ?? null,
|
|
2249
|
+
]),
|
|
2250
|
+
operations: [
|
|
2251
|
+
{
|
|
2252
|
+
op: "decision.create",
|
|
2253
|
+
title,
|
|
2254
|
+
summary: input.summary ?? undefined,
|
|
2255
|
+
urgency: input.urgency ?? "high",
|
|
2256
|
+
options: input.options ?? [],
|
|
2257
|
+
blocking: input.blocking ?? true,
|
|
2258
|
+
},
|
|
2259
|
+
],
|
|
2260
|
+
});
|
|
2261
|
+
}
|
|
2262
|
+
catch {
|
|
2263
|
+
// best effort
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
2266
|
+
async function checkSpawnGuardSafe(input) {
|
|
2267
|
+
const scopedClient = client;
|
|
2268
|
+
if (typeof scopedClient.checkSpawnGuard !== "function") {
|
|
2269
|
+
return null;
|
|
2270
|
+
}
|
|
2271
|
+
const taskId = input.taskId?.trim() ?? "";
|
|
2272
|
+
const targetLabel = input.targetLabel?.trim() ||
|
|
2273
|
+
(taskId ? `task ${taskId}` : "dispatch target");
|
|
2274
|
+
try {
|
|
2275
|
+
return await scopedClient.checkSpawnGuard(input.domain, taskId || undefined);
|
|
2276
|
+
}
|
|
2277
|
+
catch (err) {
|
|
2278
|
+
await emitActivitySafe({
|
|
2279
|
+
initiativeId: input.initiativeId,
|
|
2280
|
+
correlationId: input.correlationId,
|
|
2281
|
+
phase: "blocked",
|
|
2282
|
+
level: "warn",
|
|
2283
|
+
message: `Spawn guard check degraded for ${targetLabel}; continuing with local policy.`,
|
|
2284
|
+
metadata: {
|
|
2285
|
+
event: "spawn_guard_degraded",
|
|
2286
|
+
task_id: taskId || null,
|
|
2287
|
+
domain: input.domain,
|
|
2288
|
+
error: safeErrorMessage(err),
|
|
2289
|
+
},
|
|
2290
|
+
});
|
|
2291
|
+
return null;
|
|
2292
|
+
}
|
|
2293
|
+
}
|
|
2294
|
+
function extractSpawnGuardModelTier(result) {
|
|
2295
|
+
if (!result || typeof result !== "object")
|
|
2296
|
+
return null;
|
|
2297
|
+
return (pickString(result, ["modelTier", "model_tier"]) ??
|
|
2298
|
+
null);
|
|
2299
|
+
}
|
|
2300
|
+
function formatRequiredSkills(requiredSkills) {
|
|
2301
|
+
const normalized = requiredSkills
|
|
2302
|
+
.map((entry) => entry.trim())
|
|
2303
|
+
.filter((entry) => entry.length > 0)
|
|
2304
|
+
.map((entry) => (entry.startsWith("$") ? entry : `$${entry}`));
|
|
2305
|
+
return normalized.length > 0
|
|
2306
|
+
? normalized.join(", ")
|
|
2307
|
+
: "$orgx-engineering-agent";
|
|
2308
|
+
}
|
|
2309
|
+
function buildPolicyEnforcedMessage(input) {
|
|
2310
|
+
const modelTier = extractSpawnGuardModelTier(input.spawnGuardResult ?? null);
|
|
2311
|
+
return [
|
|
2312
|
+
`Execution policy: ${input.executionPolicy.domain}`,
|
|
2313
|
+
`Required skills: ${formatRequiredSkills(input.executionPolicy.requiredSkills)}`,
|
|
2314
|
+
modelTier ? `Spawn guard model tier: ${modelTier}` : null,
|
|
2315
|
+
"",
|
|
2316
|
+
input.baseMessage,
|
|
2317
|
+
]
|
|
2318
|
+
.filter((entry) => Boolean(entry))
|
|
2319
|
+
.join("\n");
|
|
2320
|
+
}
|
|
2321
|
+
async function resolveDispatchExecutionPolicy(input) {
|
|
2322
|
+
const initiativeId = input.initiativeId?.trim() ?? "";
|
|
2323
|
+
const taskId = input.taskId?.trim() ?? "";
|
|
2324
|
+
const workstreamId = input.workstreamId?.trim() ?? "";
|
|
2325
|
+
let resolvedTaskTitle = input.taskTitle?.trim() || null;
|
|
2326
|
+
let resolvedWorkstreamTitle = input.workstreamTitle?.trim() || null;
|
|
2327
|
+
if (initiativeId && (taskId || workstreamId)) {
|
|
2328
|
+
try {
|
|
2329
|
+
const graph = await buildMissionControlGraph(client, initiativeId);
|
|
2330
|
+
const nodeById = new Map(graph.nodes.map((node) => [node.id, node]));
|
|
2331
|
+
const taskNode = taskId ? nodeById.get(taskId) ?? null : null;
|
|
2332
|
+
const workstreamNode = workstreamId ? nodeById.get(workstreamId) ?? null : null;
|
|
2333
|
+
if (taskNode && taskNode.type === "task") {
|
|
2334
|
+
resolvedTaskTitle = resolvedTaskTitle ?? taskNode.title;
|
|
2335
|
+
const relatedWorkstream = (taskNode.workstreamId ? nodeById.get(taskNode.workstreamId) ?? null : null) ??
|
|
2336
|
+
workstreamNode;
|
|
2337
|
+
const normalizedWorkstream = relatedWorkstream && relatedWorkstream.type === "workstream"
|
|
2338
|
+
? relatedWorkstream
|
|
2339
|
+
: null;
|
|
2340
|
+
resolvedWorkstreamTitle =
|
|
2341
|
+
resolvedWorkstreamTitle ?? normalizedWorkstream?.title ?? null;
|
|
2342
|
+
return {
|
|
2343
|
+
executionPolicy: deriveExecutionPolicy(taskNode, normalizedWorkstream),
|
|
2344
|
+
taskTitle: resolvedTaskTitle,
|
|
2345
|
+
workstreamTitle: resolvedWorkstreamTitle,
|
|
2346
|
+
};
|
|
2347
|
+
}
|
|
2348
|
+
if (workstreamNode && workstreamNode.type === "workstream") {
|
|
2349
|
+
resolvedWorkstreamTitle = resolvedWorkstreamTitle ?? workstreamNode.title;
|
|
2350
|
+
const assignedDomain = workstreamNode.assignedAgents
|
|
2351
|
+
.map((agent) => normalizeExecutionDomain(agent.domain))
|
|
2352
|
+
.find((entry) => Boolean(entry));
|
|
2353
|
+
const domain = assignedDomain ??
|
|
2354
|
+
inferExecutionDomainFromText(workstreamNode.title, input.initiativeTitle, input.message);
|
|
2355
|
+
const normalizedDomain = normalizeExecutionDomain(domain) ?? "engineering";
|
|
2356
|
+
return {
|
|
2357
|
+
executionPolicy: {
|
|
2358
|
+
domain: normalizedDomain,
|
|
2359
|
+
requiredSkills: [
|
|
2360
|
+
ORGX_SKILL_BY_DOMAIN[normalizedDomain] ??
|
|
2361
|
+
ORGX_SKILL_BY_DOMAIN.engineering,
|
|
2362
|
+
],
|
|
2363
|
+
},
|
|
2364
|
+
taskTitle: resolvedTaskTitle,
|
|
2365
|
+
workstreamTitle: resolvedWorkstreamTitle,
|
|
2366
|
+
};
|
|
2367
|
+
}
|
|
2368
|
+
}
|
|
2369
|
+
catch {
|
|
2370
|
+
// best effort
|
|
2371
|
+
}
|
|
2372
|
+
}
|
|
2373
|
+
const inferredDomain = normalizeExecutionDomain(inferExecutionDomainFromText(resolvedTaskTitle, resolvedWorkstreamTitle, input.initiativeTitle, input.message)) ?? "engineering";
|
|
2374
|
+
return {
|
|
2375
|
+
executionPolicy: {
|
|
2376
|
+
domain: inferredDomain,
|
|
2377
|
+
requiredSkills: [
|
|
2378
|
+
ORGX_SKILL_BY_DOMAIN[inferredDomain] ?? ORGX_SKILL_BY_DOMAIN.engineering,
|
|
2379
|
+
],
|
|
2380
|
+
},
|
|
2381
|
+
taskTitle: resolvedTaskTitle,
|
|
2382
|
+
workstreamTitle: resolvedWorkstreamTitle,
|
|
2383
|
+
};
|
|
2384
|
+
}
|
|
2385
|
+
async function enforceSpawnGuardForDispatch(input) {
|
|
2386
|
+
const taskId = input.taskId?.trim() ?? "";
|
|
2387
|
+
const workstreamId = input.workstreamId?.trim() ?? "";
|
|
2388
|
+
const taskTitle = input.taskTitle?.trim() || null;
|
|
2389
|
+
const workstreamTitle = input.workstreamTitle?.trim() || null;
|
|
2390
|
+
const targetLabel = taskId
|
|
2391
|
+
? `task ${taskTitle ?? taskId}`
|
|
2392
|
+
: workstreamId
|
|
2393
|
+
? `workstream ${workstreamTitle ?? workstreamId}`
|
|
2394
|
+
: "dispatch target";
|
|
2395
|
+
const spawnGuardResult = await checkSpawnGuardSafe({
|
|
2396
|
+
domain: input.executionPolicy.domain,
|
|
2397
|
+
taskId: taskId || workstreamId || null,
|
|
2398
|
+
initiativeId: input.initiativeId,
|
|
2399
|
+
correlationId: input.correlationId,
|
|
2400
|
+
targetLabel,
|
|
2401
|
+
});
|
|
2402
|
+
if (!spawnGuardResult || typeof spawnGuardResult !== "object") {
|
|
2403
|
+
return {
|
|
2404
|
+
allowed: true,
|
|
2405
|
+
retryable: false,
|
|
2406
|
+
blockedReason: null,
|
|
2407
|
+
spawnGuardResult,
|
|
2408
|
+
};
|
|
2409
|
+
}
|
|
2410
|
+
const allowed = spawnGuardResult.allowed;
|
|
2411
|
+
if (allowed !== false) {
|
|
2412
|
+
return {
|
|
2413
|
+
allowed: true,
|
|
2414
|
+
retryable: false,
|
|
2415
|
+
blockedReason: null,
|
|
2416
|
+
spawnGuardResult,
|
|
2417
|
+
};
|
|
2418
|
+
}
|
|
2419
|
+
const blockedReason = summarizeSpawnGuardBlockReason(spawnGuardResult);
|
|
2420
|
+
const retryable = spawnGuardIsRateLimited(spawnGuardResult);
|
|
2421
|
+
const blockedEvent = retryable
|
|
2422
|
+
? `${input.sourceEventPrefix}_spawn_guard_rate_limited`
|
|
2423
|
+
: `${input.sourceEventPrefix}_spawn_guard_blocked`;
|
|
2424
|
+
await emitActivitySafe({
|
|
2425
|
+
initiativeId: input.initiativeId,
|
|
2426
|
+
correlationId: input.correlationId,
|
|
2427
|
+
phase: "blocked",
|
|
2428
|
+
level: retryable ? "warn" : "error",
|
|
2429
|
+
message: retryable
|
|
2430
|
+
? `Spawn guard rate-limited ${targetLabel}; deferring launch.`
|
|
2431
|
+
: `Spawn guard blocked ${targetLabel}.`,
|
|
2432
|
+
metadata: {
|
|
2433
|
+
event: blockedEvent,
|
|
2434
|
+
agent_id: input.agentId ?? null,
|
|
2435
|
+
task_id: taskId || null,
|
|
2436
|
+
task_title: taskTitle,
|
|
2437
|
+
workstream_id: workstreamId || null,
|
|
2438
|
+
workstream_title: workstreamTitle,
|
|
2439
|
+
domain: input.executionPolicy.domain,
|
|
2440
|
+
required_skills: input.executionPolicy.requiredSkills,
|
|
2441
|
+
blocked_reason: blockedReason,
|
|
2442
|
+
spawn_guard: spawnGuardResult,
|
|
2443
|
+
},
|
|
2444
|
+
nextStep: retryable
|
|
2445
|
+
? "Retry dispatch when spawn rate limits recover."
|
|
2446
|
+
: "Review decision and unblock guard checks before retry.",
|
|
2447
|
+
});
|
|
2448
|
+
if (!retryable && input.initiativeId && taskId) {
|
|
2449
|
+
try {
|
|
2450
|
+
await client.updateEntity("task", taskId, { status: "blocked" });
|
|
2451
|
+
}
|
|
2452
|
+
catch {
|
|
2453
|
+
// best effort
|
|
2454
|
+
}
|
|
2455
|
+
await syncParentRollupsForTask({
|
|
2456
|
+
initiativeId: input.initiativeId,
|
|
2457
|
+
taskId,
|
|
2458
|
+
workstreamId: workstreamId || null,
|
|
2459
|
+
milestoneId: input.milestoneId ?? null,
|
|
2460
|
+
correlationId: input.correlationId,
|
|
2461
|
+
});
|
|
2462
|
+
}
|
|
2463
|
+
if (!retryable) {
|
|
2464
|
+
await requestDecisionSafe({
|
|
2465
|
+
initiativeId: input.initiativeId,
|
|
2466
|
+
correlationId: input.correlationId,
|
|
2467
|
+
title: `Unblock ${targetLabel}`,
|
|
2468
|
+
summary: [
|
|
2469
|
+
`${targetLabel} failed spawn guard checks.`,
|
|
2470
|
+
`Reason: ${blockedReason}`,
|
|
2471
|
+
`Domain: ${input.executionPolicy.domain}`,
|
|
2472
|
+
`Required skills: ${input.executionPolicy.requiredSkills.join(", ")}`,
|
|
2473
|
+
].join(" "),
|
|
2474
|
+
urgency: "high",
|
|
2475
|
+
options: [
|
|
2476
|
+
"Approve exception and continue",
|
|
2477
|
+
"Reassign task/domain",
|
|
2478
|
+
"Pause and investigate quality gate",
|
|
2479
|
+
],
|
|
2480
|
+
blocking: true,
|
|
2481
|
+
});
|
|
2482
|
+
}
|
|
2483
|
+
return {
|
|
2484
|
+
allowed: false,
|
|
2485
|
+
retryable,
|
|
2486
|
+
blockedReason,
|
|
2487
|
+
spawnGuardResult,
|
|
2488
|
+
};
|
|
2489
|
+
}
|
|
2109
2490
|
async function syncParentRollupsForTask(input) {
|
|
2110
2491
|
const initiativeId = input.initiativeId?.trim() ?? "";
|
|
2111
2492
|
const taskId = input.taskId?.trim() ?? "";
|
|
@@ -2445,25 +2826,61 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
2445
2826
|
async function dispatchFallbackWorkstreamTurn(input) {
|
|
2446
2827
|
const now = new Date().toISOString();
|
|
2447
2828
|
const sessionId = randomUUID();
|
|
2448
|
-
const
|
|
2829
|
+
const policyResolution = await resolveDispatchExecutionPolicy({
|
|
2830
|
+
initiativeId: input.initiativeId,
|
|
2831
|
+
initiativeTitle: input.initiativeTitle,
|
|
2832
|
+
workstreamId: input.workstreamId,
|
|
2833
|
+
workstreamTitle: input.workstreamTitle,
|
|
2834
|
+
message: "Continue this workstream from the latest context. Identify and execute the next concrete task.",
|
|
2835
|
+
});
|
|
2836
|
+
const executionPolicy = policyResolution.executionPolicy;
|
|
2837
|
+
const resolvedWorkstreamTitle = policyResolution.workstreamTitle ?? input.workstreamTitle;
|
|
2838
|
+
const guard = await enforceSpawnGuardForDispatch({
|
|
2839
|
+
sourceEventPrefix: "next_up_fallback",
|
|
2840
|
+
initiativeId: input.initiativeId,
|
|
2841
|
+
correlationId: sessionId,
|
|
2842
|
+
executionPolicy,
|
|
2843
|
+
agentId: input.agentId,
|
|
2844
|
+
workstreamId: input.workstreamId,
|
|
2845
|
+
workstreamTitle: resolvedWorkstreamTitle,
|
|
2846
|
+
});
|
|
2847
|
+
if (!guard.allowed) {
|
|
2848
|
+
return {
|
|
2849
|
+
sessionId: null,
|
|
2850
|
+
pid: null,
|
|
2851
|
+
blockedReason: guard.blockedReason,
|
|
2852
|
+
retryable: guard.retryable,
|
|
2853
|
+
executionPolicy,
|
|
2854
|
+
spawnGuardResult: guard.spawnGuardResult,
|
|
2855
|
+
};
|
|
2856
|
+
}
|
|
2857
|
+
const baseMessage = [
|
|
2449
2858
|
`Initiative: ${input.initiativeTitle}`,
|
|
2450
|
-
`Workstream: ${
|
|
2859
|
+
`Workstream: ${resolvedWorkstreamTitle}`,
|
|
2451
2860
|
"",
|
|
2452
2861
|
"Continue this workstream from the latest context.",
|
|
2453
2862
|
"Identify and execute the next concrete task, then provide a concise progress summary.",
|
|
2454
2863
|
].join("\n");
|
|
2864
|
+
const message = buildPolicyEnforcedMessage({
|
|
2865
|
+
baseMessage,
|
|
2866
|
+
executionPolicy,
|
|
2867
|
+
spawnGuardResult: guard.spawnGuardResult,
|
|
2868
|
+
});
|
|
2455
2869
|
await emitActivitySafe({
|
|
2456
2870
|
initiativeId: input.initiativeId,
|
|
2457
2871
|
correlationId: sessionId,
|
|
2458
2872
|
phase: "execution",
|
|
2459
2873
|
level: "info",
|
|
2460
|
-
message: `Next Up dispatched ${
|
|
2874
|
+
message: `Next Up dispatched ${resolvedWorkstreamTitle}.`,
|
|
2461
2875
|
metadata: {
|
|
2462
2876
|
event: "next_up_manual_dispatch_started",
|
|
2463
2877
|
agent_id: input.agentId,
|
|
2464
2878
|
session_id: sessionId,
|
|
2465
2879
|
workstream_id: input.workstreamId,
|
|
2466
|
-
workstream_title:
|
|
2880
|
+
workstream_title: resolvedWorkstreamTitle,
|
|
2881
|
+
domain: executionPolicy.domain,
|
|
2882
|
+
required_skills: executionPolicy.requiredSkills,
|
|
2883
|
+
spawn_guard_model_tier: extractSpawnGuardModelTier(guard.spawnGuardResult),
|
|
2467
2884
|
fallback: true,
|
|
2468
2885
|
},
|
|
2469
2886
|
});
|
|
@@ -2493,7 +2910,14 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
2493
2910
|
startedAt: now,
|
|
2494
2911
|
status: "running",
|
|
2495
2912
|
});
|
|
2496
|
-
return {
|
|
2913
|
+
return {
|
|
2914
|
+
sessionId,
|
|
2915
|
+
pid: spawned.pid,
|
|
2916
|
+
blockedReason: null,
|
|
2917
|
+
retryable: false,
|
|
2918
|
+
executionPolicy,
|
|
2919
|
+
spawnGuardResult: guard.spawnGuardResult,
|
|
2920
|
+
};
|
|
2497
2921
|
}
|
|
2498
2922
|
async function tickAutoContinueRun(run) {
|
|
2499
2923
|
if (run.status !== "running" && run.status !== "stopping")
|
|
@@ -2560,6 +2984,27 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
2560
2984
|
error_message: summary.errorMessage,
|
|
2561
2985
|
},
|
|
2562
2986
|
});
|
|
2987
|
+
if (summary.hadError && record.taskId) {
|
|
2988
|
+
await requestDecisionSafe({
|
|
2989
|
+
initiativeId: run.initiativeId,
|
|
2990
|
+
correlationId: record.runId,
|
|
2991
|
+
title: `Unblock auto-continue task ${record.taskId}`,
|
|
2992
|
+
summary: [
|
|
2993
|
+
`Task ${record.taskId} finished with runtime error in session ${record.runId}.`,
|
|
2994
|
+
summary.errorMessage ? `Error: ${summary.errorMessage}` : null,
|
|
2995
|
+
`Workstream: ${record.workstreamId ?? "unknown"}.`,
|
|
2996
|
+
]
|
|
2997
|
+
.filter((line) => Boolean(line))
|
|
2998
|
+
.join(" "),
|
|
2999
|
+
urgency: "high",
|
|
3000
|
+
options: [
|
|
3001
|
+
"Retry task in auto-continue",
|
|
3002
|
+
"Assign manual recovery owner",
|
|
3003
|
+
"Pause initiative until fixed",
|
|
3004
|
+
],
|
|
3005
|
+
blocking: true,
|
|
3006
|
+
});
|
|
3007
|
+
}
|
|
2563
3008
|
run.lastRunId = record.runId;
|
|
2564
3009
|
run.lastTaskId = record.taskId ?? run.lastTaskId;
|
|
2565
3010
|
run.activeRunId = null;
|
|
@@ -2682,30 +3127,120 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
2682
3127
|
const milestoneTitle = nextTaskNode.milestoneId
|
|
2683
3128
|
? nodeById.get(nextTaskNode.milestoneId)?.title ?? null
|
|
2684
3129
|
: null;
|
|
3130
|
+
const workstreamNode = nextTaskNode.workstreamId
|
|
3131
|
+
? nodeById.get(nextTaskNode.workstreamId) ?? null
|
|
3132
|
+
: null;
|
|
3133
|
+
const executionPolicy = deriveExecutionPolicy(nextTaskNode, workstreamNode);
|
|
3134
|
+
const spawnGuardResult = await checkSpawnGuardSafe({
|
|
3135
|
+
domain: executionPolicy.domain,
|
|
3136
|
+
taskId: nextTaskNode.id,
|
|
3137
|
+
initiativeId: run.initiativeId,
|
|
3138
|
+
correlationId: sessionId,
|
|
3139
|
+
});
|
|
3140
|
+
if (spawnGuardResult && typeof spawnGuardResult === "object") {
|
|
3141
|
+
const allowed = spawnGuardResult.allowed;
|
|
3142
|
+
if (allowed === false) {
|
|
3143
|
+
const blockedReason = summarizeSpawnGuardBlockReason(spawnGuardResult);
|
|
3144
|
+
if (spawnGuardIsRateLimited(spawnGuardResult)) {
|
|
3145
|
+
run.lastError = blockedReason;
|
|
3146
|
+
run.updatedAt = now;
|
|
3147
|
+
await emitActivitySafe({
|
|
3148
|
+
initiativeId: run.initiativeId,
|
|
3149
|
+
correlationId: sessionId,
|
|
3150
|
+
phase: "blocked",
|
|
3151
|
+
level: "warn",
|
|
3152
|
+
message: `Spawn guard rate-limited task ${nextTaskNode.id}; waiting to retry.`,
|
|
3153
|
+
metadata: {
|
|
3154
|
+
event: "auto_continue_spawn_guard_rate_limited",
|
|
3155
|
+
task_id: nextTaskNode.id,
|
|
3156
|
+
task_title: nextTaskNode.title,
|
|
3157
|
+
domain: executionPolicy.domain,
|
|
3158
|
+
required_skills: executionPolicy.requiredSkills,
|
|
3159
|
+
spawn_guard: spawnGuardResult,
|
|
3160
|
+
},
|
|
3161
|
+
});
|
|
3162
|
+
return;
|
|
3163
|
+
}
|
|
3164
|
+
try {
|
|
3165
|
+
await client.updateEntity("task", nextTaskNode.id, {
|
|
3166
|
+
status: "blocked",
|
|
3167
|
+
});
|
|
3168
|
+
}
|
|
3169
|
+
catch {
|
|
3170
|
+
// best effort
|
|
3171
|
+
}
|
|
3172
|
+
await syncParentRollupsForTask({
|
|
3173
|
+
initiativeId: run.initiativeId,
|
|
3174
|
+
taskId: nextTaskNode.id,
|
|
3175
|
+
workstreamId: nextTaskNode.workstreamId,
|
|
3176
|
+
milestoneId: nextTaskNode.milestoneId,
|
|
3177
|
+
correlationId: sessionId,
|
|
3178
|
+
});
|
|
3179
|
+
await emitActivitySafe({
|
|
3180
|
+
initiativeId: run.initiativeId,
|
|
3181
|
+
correlationId: sessionId,
|
|
3182
|
+
phase: "blocked",
|
|
3183
|
+
level: "error",
|
|
3184
|
+
message: `Auto-continue blocked by spawn guard on task ${nextTaskNode.id}.`,
|
|
3185
|
+
metadata: {
|
|
3186
|
+
event: "auto_continue_spawn_guard_blocked",
|
|
3187
|
+
task_id: nextTaskNode.id,
|
|
3188
|
+
task_title: nextTaskNode.title,
|
|
3189
|
+
domain: executionPolicy.domain,
|
|
3190
|
+
required_skills: executionPolicy.requiredSkills,
|
|
3191
|
+
blocked_reason: blockedReason,
|
|
3192
|
+
spawn_guard: spawnGuardResult,
|
|
3193
|
+
},
|
|
3194
|
+
});
|
|
3195
|
+
await requestDecisionSafe({
|
|
3196
|
+
initiativeId: run.initiativeId,
|
|
3197
|
+
correlationId: sessionId,
|
|
3198
|
+
title: `Unblock auto-continue task ${nextTaskNode.title}`,
|
|
3199
|
+
summary: [
|
|
3200
|
+
`Task ${nextTaskNode.id} failed spawn guard checks.`,
|
|
3201
|
+
`Reason: ${blockedReason}`,
|
|
3202
|
+
`Domain: ${executionPolicy.domain}`,
|
|
3203
|
+
`Required skills: ${executionPolicy.requiredSkills.join(", ")}`,
|
|
3204
|
+
].join(" "),
|
|
3205
|
+
urgency: "high",
|
|
3206
|
+
options: [
|
|
3207
|
+
"Approve exception and continue",
|
|
3208
|
+
"Reassign task/domain",
|
|
3209
|
+
"Pause and investigate quality gate",
|
|
3210
|
+
],
|
|
3211
|
+
blocking: true,
|
|
3212
|
+
});
|
|
3213
|
+
await stopAutoContinueRun({
|
|
3214
|
+
run,
|
|
3215
|
+
reason: "blocked",
|
|
3216
|
+
error: blockedReason,
|
|
3217
|
+
});
|
|
3218
|
+
return;
|
|
3219
|
+
}
|
|
3220
|
+
}
|
|
2685
3221
|
const message = [
|
|
2686
3222
|
initiativeNode ? `Initiative: ${initiativeNode.title}` : null,
|
|
2687
3223
|
workstreamTitle ? `Workstream: ${workstreamTitle}` : null,
|
|
2688
3224
|
milestoneTitle ? `Milestone: ${milestoneTitle}` : null,
|
|
2689
3225
|
"",
|
|
2690
3226
|
`Task: ${nextTaskNode.title}`,
|
|
3227
|
+
`Execution policy: ${executionPolicy.domain}`,
|
|
3228
|
+
`Required skills: ${executionPolicy.requiredSkills.map((skill) => `$${skill}`).join(", ")}`,
|
|
2691
3229
|
"",
|
|
2692
3230
|
"Execute this task. When finished, provide a concise completion summary and any relevant commands/notes.",
|
|
2693
3231
|
]
|
|
2694
3232
|
.filter((line) => typeof line === "string")
|
|
2695
3233
|
.join("\n");
|
|
2696
|
-
if (
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
catch {
|
|
2707
|
-
// best effort
|
|
2708
|
-
}
|
|
3234
|
+
if (workstreamNode &&
|
|
3235
|
+
!isInProgressStatus(workstreamNode.status) &&
|
|
3236
|
+
isDispatchableWorkstreamStatus(workstreamNode.status)) {
|
|
3237
|
+
try {
|
|
3238
|
+
await client.updateEntity("workstream", workstreamNode.id, {
|
|
3239
|
+
status: "active",
|
|
3240
|
+
});
|
|
3241
|
+
}
|
|
3242
|
+
catch {
|
|
3243
|
+
// best effort
|
|
2709
3244
|
}
|
|
2710
3245
|
}
|
|
2711
3246
|
try {
|
|
@@ -2744,6 +3279,11 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
2744
3279
|
workstream_title: workstreamTitle,
|
|
2745
3280
|
milestone_id: nextTaskNode.milestoneId,
|
|
2746
3281
|
milestone_title: milestoneTitle,
|
|
3282
|
+
domain: executionPolicy.domain,
|
|
3283
|
+
required_skills: executionPolicy.requiredSkills,
|
|
3284
|
+
spawn_guard_model_tier: spawnGuardResult && typeof spawnGuardResult === "object"
|
|
3285
|
+
? pickString(spawnGuardResult, ["modelTier", "model_tier"]) ?? null
|
|
3286
|
+
: null,
|
|
2747
3287
|
},
|
|
2748
3288
|
});
|
|
2749
3289
|
upsertAgentContext({
|
|
@@ -3561,6 +4101,7 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
3561
4101
|
searchParams.get("model_id") ??
|
|
3562
4102
|
"")
|
|
3563
4103
|
.trim() || null;
|
|
4104
|
+
const routingProvider = provider ?? (!provider && !requestedModel ? resolveAutoOpenClawProvider() : null);
|
|
3564
4105
|
const dryRunRaw = payload.dryRun ??
|
|
3565
4106
|
payload.dry_run ??
|
|
3566
4107
|
searchParams.get("dryRun") ??
|
|
@@ -3569,7 +4110,7 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
3569
4110
|
const dryRun = typeof dryRunRaw === "boolean"
|
|
3570
4111
|
? dryRunRaw
|
|
3571
4112
|
: parseBooleanQuery(typeof dryRunRaw === "string" ? dryRunRaw : null);
|
|
3572
|
-
let requiresPremiumLaunch = Boolean(
|
|
4113
|
+
let requiresPremiumLaunch = Boolean(routingProvider) || modelImpliesByok(requestedModel);
|
|
3573
4114
|
if (!requiresPremiumLaunch) {
|
|
3574
4115
|
try {
|
|
3575
4116
|
const agents = await listAgents();
|
|
@@ -3609,12 +4150,23 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
3609
4150
|
searchParams.get("text") ??
|
|
3610
4151
|
"")
|
|
3611
4152
|
.trim();
|
|
3612
|
-
const
|
|
4153
|
+
const baseMessage = messageInput ||
|
|
3613
4154
|
(initiativeTitle
|
|
3614
4155
|
? `Kick off: ${initiativeTitle}`
|
|
3615
4156
|
: initiativeId
|
|
3616
4157
|
? `Kick off initiative ${initiativeId}`
|
|
3617
4158
|
: `Kick off agent ${agentId}`);
|
|
4159
|
+
const policyResolution = await resolveDispatchExecutionPolicy({
|
|
4160
|
+
initiativeId,
|
|
4161
|
+
initiativeTitle,
|
|
4162
|
+
workstreamId,
|
|
4163
|
+
taskId,
|
|
4164
|
+
message: baseMessage,
|
|
4165
|
+
});
|
|
4166
|
+
const executionPolicy = policyResolution.executionPolicy;
|
|
4167
|
+
const resolvedTaskTitle = policyResolution.taskTitle;
|
|
4168
|
+
const resolvedWorkstreamTitle = policyResolution.workstreamTitle ??
|
|
4169
|
+
(workstreamId ? `Workstream ${workstreamId}` : null);
|
|
3618
4170
|
if (dryRun) {
|
|
3619
4171
|
sendJson(res, 200, {
|
|
3620
4172
|
ok: true,
|
|
@@ -3624,11 +4176,48 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
3624
4176
|
workstreamId,
|
|
3625
4177
|
taskId,
|
|
3626
4178
|
requiresPremiumLaunch,
|
|
4179
|
+
provider: routingProvider,
|
|
4180
|
+
model: requestedModel,
|
|
3627
4181
|
startedAt: new Date().toISOString(),
|
|
3628
|
-
message,
|
|
4182
|
+
message: baseMessage,
|
|
4183
|
+
domain: executionPolicy.domain,
|
|
4184
|
+
requiredSkills: executionPolicy.requiredSkills,
|
|
4185
|
+
});
|
|
4186
|
+
return true;
|
|
4187
|
+
}
|
|
4188
|
+
const guard = await enforceSpawnGuardForDispatch({
|
|
4189
|
+
sourceEventPrefix: "agent_launch",
|
|
4190
|
+
initiativeId,
|
|
4191
|
+
correlationId: sessionId,
|
|
4192
|
+
executionPolicy,
|
|
4193
|
+
agentId,
|
|
4194
|
+
taskId,
|
|
4195
|
+
taskTitle: resolvedTaskTitle,
|
|
4196
|
+
workstreamId,
|
|
4197
|
+
workstreamTitle: resolvedWorkstreamTitle,
|
|
4198
|
+
});
|
|
4199
|
+
if (!guard.allowed) {
|
|
4200
|
+
sendJson(res, guard.retryable ? 429 : 409, {
|
|
4201
|
+
ok: false,
|
|
4202
|
+
code: guard.retryable
|
|
4203
|
+
? "spawn_guard_rate_limited"
|
|
4204
|
+
: "spawn_guard_blocked",
|
|
4205
|
+
error: guard.blockedReason ??
|
|
4206
|
+
"Spawn guard denied this agent launch.",
|
|
4207
|
+
retryable: guard.retryable,
|
|
4208
|
+
initiativeId,
|
|
4209
|
+
workstreamId,
|
|
4210
|
+
taskId,
|
|
4211
|
+
domain: executionPolicy.domain,
|
|
4212
|
+
requiredSkills: executionPolicy.requiredSkills,
|
|
3629
4213
|
});
|
|
3630
4214
|
return true;
|
|
3631
4215
|
}
|
|
4216
|
+
const message = buildPolicyEnforcedMessage({
|
|
4217
|
+
baseMessage,
|
|
4218
|
+
executionPolicy,
|
|
4219
|
+
spawnGuardResult: guard.spawnGuardResult,
|
|
4220
|
+
});
|
|
3632
4221
|
if (initiativeId) {
|
|
3633
4222
|
try {
|
|
3634
4223
|
await client.updateEntity("initiative", initiativeId, { status: "active" });
|
|
@@ -3665,16 +4254,19 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
3665
4254
|
session_id: sessionId,
|
|
3666
4255
|
workstream_id: workstreamId,
|
|
3667
4256
|
task_id: taskId,
|
|
3668
|
-
provider,
|
|
4257
|
+
provider: routingProvider,
|
|
3669
4258
|
model: requestedModel,
|
|
4259
|
+
domain: executionPolicy.domain,
|
|
4260
|
+
required_skills: executionPolicy.requiredSkills,
|
|
4261
|
+
spawn_guard_model_tier: extractSpawnGuardModelTier(guard.spawnGuardResult),
|
|
3670
4262
|
},
|
|
3671
4263
|
});
|
|
3672
4264
|
let routedProvider = null;
|
|
3673
4265
|
let routedModel = null;
|
|
3674
|
-
if (
|
|
4266
|
+
if (routingProvider) {
|
|
3675
4267
|
const routed = await configureOpenClawProviderRouting({
|
|
3676
4268
|
agentId,
|
|
3677
|
-
provider,
|
|
4269
|
+
provider: routingProvider,
|
|
3678
4270
|
requestedModel,
|
|
3679
4271
|
});
|
|
3680
4272
|
routedProvider = routed.provider;
|
|
@@ -3718,6 +4310,8 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
3718
4310
|
workstreamId,
|
|
3719
4311
|
taskId,
|
|
3720
4312
|
startedAt: new Date().toISOString(),
|
|
4313
|
+
domain: executionPolicy.domain,
|
|
4314
|
+
requiredSkills: executionPolicy.requiredSkills,
|
|
3721
4315
|
});
|
|
3722
4316
|
}
|
|
3723
4317
|
catch (err) {
|
|
@@ -3806,7 +4400,9 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
3806
4400
|
record.model ??
|
|
3807
4401
|
"")
|
|
3808
4402
|
.trim() || null;
|
|
3809
|
-
|
|
4403
|
+
const routingProvider = providerOverride ??
|
|
4404
|
+
(!providerOverride && !requestedModel ? resolveAutoOpenClawProvider() : null);
|
|
4405
|
+
let requiresPremiumRestart = Boolean(routingProvider) ||
|
|
3810
4406
|
modelImpliesByok(requestedModel) ||
|
|
3811
4407
|
modelImpliesByok(record.model ?? null);
|
|
3812
4408
|
if (!requiresPremiumRestart) {
|
|
@@ -3842,13 +4438,52 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
3842
4438
|
}
|
|
3843
4439
|
}
|
|
3844
4440
|
const sessionId = randomUUID();
|
|
3845
|
-
const
|
|
3846
|
-
|
|
4441
|
+
const baseMessage = messageOverride ?? record.message ?? `Restart agent ${record.agentId}`;
|
|
4442
|
+
const policyResolution = await resolveDispatchExecutionPolicy({
|
|
4443
|
+
initiativeId: record.initiativeId,
|
|
4444
|
+
initiativeTitle: record.initiativeTitle,
|
|
4445
|
+
workstreamId: record.workstreamId,
|
|
4446
|
+
taskId: record.taskId,
|
|
4447
|
+
message: baseMessage,
|
|
4448
|
+
});
|
|
4449
|
+
const executionPolicy = policyResolution.executionPolicy;
|
|
4450
|
+
const guard = await enforceSpawnGuardForDispatch({
|
|
4451
|
+
sourceEventPrefix: "agent_restart",
|
|
4452
|
+
initiativeId: record.initiativeId,
|
|
4453
|
+
correlationId: sessionId,
|
|
4454
|
+
executionPolicy,
|
|
4455
|
+
agentId: record.agentId,
|
|
4456
|
+
taskId: record.taskId,
|
|
4457
|
+
taskTitle: policyResolution.taskTitle,
|
|
4458
|
+
workstreamId: record.workstreamId,
|
|
4459
|
+
workstreamTitle: policyResolution.workstreamTitle,
|
|
4460
|
+
});
|
|
4461
|
+
if (!guard.allowed) {
|
|
4462
|
+
sendJson(res, guard.retryable ? 429 : 409, {
|
|
4463
|
+
ok: false,
|
|
4464
|
+
code: guard.retryable
|
|
4465
|
+
? "spawn_guard_rate_limited"
|
|
4466
|
+
: "spawn_guard_blocked",
|
|
4467
|
+
error: guard.blockedReason ??
|
|
4468
|
+
"Spawn guard denied this restart.",
|
|
4469
|
+
retryable: guard.retryable,
|
|
4470
|
+
previousRunId,
|
|
4471
|
+
domain: executionPolicy.domain,
|
|
4472
|
+
requiredSkills: executionPolicy.requiredSkills,
|
|
4473
|
+
});
|
|
4474
|
+
return true;
|
|
4475
|
+
}
|
|
4476
|
+
const message = buildPolicyEnforcedMessage({
|
|
4477
|
+
baseMessage,
|
|
4478
|
+
executionPolicy,
|
|
4479
|
+
spawnGuardResult: guard.spawnGuardResult,
|
|
4480
|
+
});
|
|
4481
|
+
let routedProvider = routingProvider ?? null;
|
|
3847
4482
|
let routedModel = requestedModel ?? null;
|
|
3848
|
-
if (
|
|
4483
|
+
if (routingProvider) {
|
|
3849
4484
|
const routed = await configureOpenClawProviderRouting({
|
|
3850
4485
|
agentId: record.agentId,
|
|
3851
|
-
provider:
|
|
4486
|
+
provider: routingProvider,
|
|
3852
4487
|
requestedModel,
|
|
3853
4488
|
});
|
|
3854
4489
|
routedProvider = routed.provider;
|
|
@@ -3888,6 +4523,8 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
3888
4523
|
pid: spawned.pid,
|
|
3889
4524
|
provider: routedProvider,
|
|
3890
4525
|
model: routedModel,
|
|
4526
|
+
domain: executionPolicy.domain,
|
|
4527
|
+
requiredSkills: executionPolicy.requiredSkills,
|
|
3891
4528
|
});
|
|
3892
4529
|
}
|
|
3893
4530
|
catch (err) {
|
|
@@ -3988,24 +4625,33 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
3988
4625
|
agentId,
|
|
3989
4626
|
});
|
|
3990
4627
|
}
|
|
4628
|
+
const fallbackStarted = Boolean(fallbackDispatch?.sessionId);
|
|
3991
4629
|
const dispatchMode = run.activeRunId
|
|
3992
4630
|
? "task"
|
|
3993
|
-
:
|
|
4631
|
+
: fallbackStarted
|
|
3994
4632
|
? "fallback"
|
|
3995
4633
|
: "none";
|
|
3996
4634
|
if (dispatchMode === "none") {
|
|
3997
|
-
const
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
? "No
|
|
4001
|
-
:
|
|
4002
|
-
|
|
4635
|
+
const fallbackBlockedReason = fallbackDispatch?.blockedReason ?? null;
|
|
4636
|
+
const reason = fallbackBlockedReason ??
|
|
4637
|
+
(run.stopReason === "blocked"
|
|
4638
|
+
? "No dispatchable task is ready for this workstream yet."
|
|
4639
|
+
: run.stopReason === "completed"
|
|
4640
|
+
? "No queued task is available for this workstream."
|
|
4641
|
+
: "Unable to dispatch this workstream right now.");
|
|
4642
|
+
sendJson(res, fallbackDispatch?.retryable ? 429 : 409, {
|
|
4003
4643
|
ok: false,
|
|
4644
|
+
code: fallbackBlockedReason
|
|
4645
|
+
? fallbackDispatch?.retryable
|
|
4646
|
+
? "spawn_guard_rate_limited"
|
|
4647
|
+
: "spawn_guard_blocked"
|
|
4648
|
+
: undefined,
|
|
4004
4649
|
error: reason,
|
|
4005
4650
|
run,
|
|
4006
4651
|
initiativeId,
|
|
4007
4652
|
workstreamId,
|
|
4008
4653
|
agentId,
|
|
4654
|
+
fallbackDispatch,
|
|
4009
4655
|
});
|
|
4010
4656
|
return true;
|
|
4011
4657
|
}
|
|
@@ -5932,6 +6578,11 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
5932
6578
|
// Requests under /orgx/live
|
|
5933
6579
|
if (url === "/orgx/live" || url.startsWith("/orgx/live/")) {
|
|
5934
6580
|
const subPath = url.replace(/^\/orgx\/live\/?/, "");
|
|
6581
|
+
// Never expose source maps in shipped plugin dashboards.
|
|
6582
|
+
if (/\.map$/i.test(subPath)) {
|
|
6583
|
+
send404(res);
|
|
6584
|
+
return true;
|
|
6585
|
+
}
|
|
5935
6586
|
// Static assets: /orgx/live/assets/* → dashboard/dist/assets/*
|
|
5936
6587
|
// Hashed filenames get long-lived cache
|
|
5937
6588
|
if (subPath.startsWith("assets/")) {
|
|
@@ -5977,4 +6628,3 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
5977
6628
|
return true;
|
|
5978
6629
|
};
|
|
5979
6630
|
}
|
|
5980
|
-
//# sourceMappingURL=http-handler.js.map
|