@desplega.ai/agent-swarm 1.91.0 → 1.92.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/openapi.json +585 -5
- package/package.json +1 -1
- package/src/be/db.ts +337 -1
- package/src/be/migrations/083_script_workflows.sql +51 -0
- package/src/be/modelsdev-cache.json +42352 -38595
- package/src/be/scripts/typecheck.ts +49 -0
- package/src/be/seed-scripts/catalog/compound-insights.ts +216 -6
- package/src/be/seed-scripts/catalog/ops-catalog-audit.ts +911 -0
- package/src/be/seed-scripts/catalog/task-context-gathering.ts +92 -0
- package/src/be/seed-scripts/catalog/tool-usage.ts +6 -3
- package/src/be/seed-scripts/index.ts +20 -2
- package/src/be/seed-skills/index.ts +7 -0
- package/src/be/swarm-config-guard.ts +17 -0
- package/src/commands/runner.ts +43 -2
- package/src/http/db-query.ts +20 -5
- package/src/http/index.ts +10 -0
- package/src/http/script-runs.ts +555 -0
- package/src/prompts/session-templates.ts +24 -4
- package/src/providers/claude-adapter.ts +60 -13
- package/src/script-workflows/executor.ts +110 -0
- package/src/script-workflows/harness.ts +73 -0
- package/src/script-workflows/label-lint.ts +51 -0
- package/src/script-workflows/limits.ts +22 -0
- package/src/script-workflows/supervisor.ts +139 -0
- package/src/script-workflows/workflow-ctx.ts +205 -0
- package/src/scripts-runtime/sdk-allowlist.ts +3 -0
- package/src/scripts-runtime/types/stdlib.d.ts +60 -0
- package/src/scripts-runtime/types/swarm-sdk.d.ts +60 -0
- package/src/server.ts +2 -0
- package/src/slack/handlers.ts +11 -4
- package/src/slack/message-text.ts +98 -0
- package/src/slack/thread-buffer.ts +5 -3
- package/src/tests/claude-adapter-binary.test.ts +147 -4
- package/src/tests/db-query.test.ts +28 -0
- package/src/tests/error-tracker.test.ts +121 -0
- package/src/tests/harness-provider-resolution.test.ts +33 -0
- package/src/tests/mcp-tools.test.ts +6 -0
- package/src/tests/prompt-template-session.test.ts +34 -5
- package/src/tests/script-runs-http.test.ts +278 -0
- package/src/tests/script-workflows-label-lint.test.ts +43 -0
- package/src/tests/script-workflows-runtime-e2e.test.ts +170 -0
- package/src/tests/scripts-mcp-e2e.test.ts +49 -2
- package/src/tests/seed-scripts.test.ts +347 -2
- package/src/tests/slack-message-text.test.ts +250 -0
- package/src/tests/system-default-skills.test.ts +40 -0
- package/src/tools/db-query.ts +16 -6
- package/src/tools/script-runs.ts +123 -0
- package/src/tools/slack-read.ts +12 -3
- package/src/tools/tool-config.ts +4 -1
- package/src/types.ts +52 -0
- package/src/utils/error-tracker.ts +40 -1
- package/src/utils/internal-ai/complete-structured.ts +10 -4
- package/src/workflows/executors/raw-llm.ts +76 -59
- package/templates/skills/pages/content.md +205 -55
- package/templates/skills/script-workflows/config.json +14 -0
- package/templates/skills/script-workflows/content.md +68 -0
- package/templates/skills/swarm-scripts/content.md +2 -3
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export const argsSchema = z.object({
|
|
4
|
+
taskId: z.string().describe("Task ID to fetch details for"),
|
|
5
|
+
queries: z
|
|
6
|
+
.array(z.string())
|
|
7
|
+
.min(1)
|
|
8
|
+
.describe("Search queries from the task description — 2-4 recommended"),
|
|
9
|
+
scope: z
|
|
10
|
+
.enum(["all", "agent", "swarm"])
|
|
11
|
+
.optional()
|
|
12
|
+
.describe("Memory scope filter (default all)"),
|
|
13
|
+
memoryLimit: z
|
|
14
|
+
.number()
|
|
15
|
+
.int()
|
|
16
|
+
.positive()
|
|
17
|
+
.optional()
|
|
18
|
+
.describe("Max memories per query before dedup (default 8)"),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
function slimTask(task: any) {
|
|
22
|
+
if (!task || typeof task !== "object") return null;
|
|
23
|
+
return {
|
|
24
|
+
id: task.id,
|
|
25
|
+
status: task.status,
|
|
26
|
+
description: task.task,
|
|
27
|
+
dependsOn: task.dependsOn,
|
|
28
|
+
slackChannelId: task.slackChannelId,
|
|
29
|
+
slackThreadTs: task.slackThreadTs,
|
|
30
|
+
createdAt: task.createdAt,
|
|
31
|
+
finishedAt: task.finishedAt,
|
|
32
|
+
agentId: task.agentId,
|
|
33
|
+
output: task.output,
|
|
34
|
+
failureReason: task.failureReason,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Fetch slim task details plus deduped multi-query memories for task onboarding. */
|
|
39
|
+
export default async function taskContextGathering(args: any, ctx: any) {
|
|
40
|
+
const parsed = argsSchema.safeParse(args);
|
|
41
|
+
if (!parsed.success) return { error: "invalid args: " + parsed.error.message };
|
|
42
|
+
const { taskId, queries, scope = "all", memoryLimit = 8 } = parsed.data;
|
|
43
|
+
|
|
44
|
+
const taskRes: any = await ctx.swarm.task_get({ taskId });
|
|
45
|
+
const taskPayload = taskRes?.data ?? taskRes;
|
|
46
|
+
if (taskPayload?.success === false) {
|
|
47
|
+
return { error: taskPayload.message ?? "task_get failed", taskId };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const allResults: any[] = [];
|
|
51
|
+
for (const query of queries) {
|
|
52
|
+
const res: any = await ctx.swarm.memory_search({ query, scope, limit: memoryLimit });
|
|
53
|
+
const payload = res?.data ?? res;
|
|
54
|
+
const results = payload?.results ?? [];
|
|
55
|
+
for (const memory of results) {
|
|
56
|
+
allResults.push({ ...memory, querySource: query });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const byId = new Map<string, any>();
|
|
61
|
+
const hitCounts = new Map<string, number>();
|
|
62
|
+
for (const memory of allResults) {
|
|
63
|
+
const id = typeof memory.id === "string" ? memory.id : JSON.stringify(memory);
|
|
64
|
+
hitCounts.set(id, (hitCounts.get(id) ?? 0) + 1);
|
|
65
|
+
const existing = byId.get(id);
|
|
66
|
+
const similarity = typeof memory.similarity === "number" ? memory.similarity : 0;
|
|
67
|
+
const existingSimilarity =
|
|
68
|
+
existing && typeof existing.similarity === "number" ? existing.similarity : -Infinity;
|
|
69
|
+
if (!existing || similarity > existingSimilarity) byId.set(id, memory);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const memories = Array.from(byId.entries()).map(([id, memory]) => {
|
|
73
|
+
const hits = hitCounts.get(id) ?? 1;
|
|
74
|
+
const similarity = typeof memory.similarity === "number" ? memory.similarity : 0;
|
|
75
|
+
return {
|
|
76
|
+
...memory,
|
|
77
|
+
hits,
|
|
78
|
+
compositeScore: similarity + 0.05 * hits,
|
|
79
|
+
};
|
|
80
|
+
});
|
|
81
|
+
memories.sort((a: any, b: any) => b.compositeScore - a.compositeScore);
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
task: slimTask(taskPayload?.task),
|
|
85
|
+
requestedBy: taskPayload?.requestedBy,
|
|
86
|
+
attachments: taskPayload?.attachments ?? [],
|
|
87
|
+
queriesRun: queries.length,
|
|
88
|
+
totalCandidates: allResults.length,
|
|
89
|
+
uniqueMemories: memories.length,
|
|
90
|
+
memories: memories.slice(0, memoryLimit * 2),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
@@ -25,7 +25,7 @@ export default async function toolUsage(args: any, ctx: any) {
|
|
|
25
25
|
const agentId = parsed.data.agentId;
|
|
26
26
|
|
|
27
27
|
const agentFilter = agentId ? `AND agent_id = '${agentId}'` : "";
|
|
28
|
-
const
|
|
28
|
+
const sql = `
|
|
29
29
|
SELECT tool_name, count(*) as calls
|
|
30
30
|
FROM session_logs
|
|
31
31
|
WHERE tool_name IS NOT NULL
|
|
@@ -36,9 +36,12 @@ export default async function toolUsage(args: any, ctx: any) {
|
|
|
36
36
|
LIMIT ${limit}
|
|
37
37
|
`;
|
|
38
38
|
|
|
39
|
-
const res: any = await ctx.swarm.db_query({
|
|
39
|
+
const res: any = await ctx.swarm.db_query({ sql });
|
|
40
40
|
const payload = res?.data ?? res;
|
|
41
|
-
const
|
|
41
|
+
const columns: string[] = payload?.columns ?? [];
|
|
42
|
+
const rows: any[] = (payload?.rows ?? []).map((row: any[]) =>
|
|
43
|
+
Object.fromEntries(columns.map((column, index) => [column, row[index]])),
|
|
44
|
+
);
|
|
42
45
|
|
|
43
46
|
const totalCalls = rows.reduce((sum: number, r: any) => sum + (r.calls ?? 0), 0);
|
|
44
47
|
|
|
@@ -30,9 +30,11 @@ import groupCountSrc from "./catalog/group-count.ts" with { type: "text" };
|
|
|
30
30
|
import jsonQuerySrc from "./catalog/json-query.ts" with { type: "text" };
|
|
31
31
|
import linearIssueSrc from "./catalog/linear-issue.ts" with { type: "text" };
|
|
32
32
|
import memoryDedupCheckSrc from "./catalog/memory-dedup-check.ts" with { type: "text" };
|
|
33
|
+
import opsCatalogAuditSrc from "./catalog/ops-catalog-audit.ts" with { type: "text" };
|
|
33
34
|
import scheduleHealthSrc from "./catalog/schedule-health.ts" with { type: "text" };
|
|
34
35
|
import slackThreadFlattenSrc from "./catalog/slack-thread-flatten.ts" with { type: "text" };
|
|
35
36
|
import smartRecallSrc from "./catalog/smart-recall.ts" with { type: "text" };
|
|
37
|
+
import taskContextGatheringSrc from "./catalog/task-context-gathering.ts" with { type: "text" };
|
|
36
38
|
import taskFailureAuditSrc from "./catalog/task-failure-audit.ts" with { type: "text" };
|
|
37
39
|
import textDiffSrc from "./catalog/text-diff.ts" with { type: "text" };
|
|
38
40
|
import toolUsageSrc from "./catalog/tool-usage.ts" with { type: "text" };
|
|
@@ -134,6 +136,14 @@ export const SEED_SCRIPTS: SeedScript[] = [
|
|
|
134
136
|
"Recall relevant memories using multiple search angles — better coverage than a single query. Use for task onboarding, context gathering, or before writing new memories.",
|
|
135
137
|
source: asText(smartRecallSrc),
|
|
136
138
|
},
|
|
139
|
+
{
|
|
140
|
+
name: "task-context-gathering",
|
|
141
|
+
description:
|
|
142
|
+
"Get task details and recall relevant memories in one call — returns a slimmed task projection plus deduped and reranked memories from multi-query fan-out.",
|
|
143
|
+
intent:
|
|
144
|
+
"Task onboarding: one call instead of task_get plus multiple memory_search calls. Pass the task description split into 2-4 natural-language queries.",
|
|
145
|
+
source: asText(taskContextGatheringSrc),
|
|
146
|
+
},
|
|
137
147
|
{
|
|
138
148
|
name: "schedule-health",
|
|
139
149
|
description:
|
|
@@ -153,11 +163,19 @@ export const SEED_SCRIPTS: SeedScript[] = [
|
|
|
153
163
|
{
|
|
154
164
|
name: "compound-insights",
|
|
155
165
|
description:
|
|
156
|
-
"All-in-one swarm-wide daily ops snapshot: task completion/failure summary, real failure clusters (excludes superseded/cancelled bookkeeping), schedule health flags, tool usage top-25, memory health stats, and a per-agent breakdown. Aggregates across ALL agents via direct read-only SQL.",
|
|
166
|
+
"All-in-one swarm-wide daily ops snapshot: task completion/failure summary, real failure clusters (excludes superseded/cancelled bookkeeping), schedule health flags, tool usage top-25, memory health/pollution stats, seed-script candidate tool triplets, and a per-agent breakdown. Aggregates across ALL agents via direct read-only SQL.",
|
|
157
167
|
intent:
|
|
158
|
-
"Single-call daily compounding Phase 0 helper — replaces ~25 raw tool roundtrips with one compressed JSON result covering every agent. For daily evolution, ops reviews, or heartbeat context.",
|
|
168
|
+
"Single-call daily compounding Phase 0 helper — replaces ~25 raw tool roundtrips with one compressed JSON result covering every agent. For daily evolution, self-scripting candidates, ops reviews, or heartbeat context.",
|
|
159
169
|
source: asText(compoundInsightsSrc),
|
|
160
170
|
},
|
|
171
|
+
{
|
|
172
|
+
name: "ops-catalog-audit",
|
|
173
|
+
description:
|
|
174
|
+
"Audit-as-code catalog check for schedules, workflows, and prompt/template drift. Clusters actionable findings by goal and can publish an authed HTML report page.",
|
|
175
|
+
intent:
|
|
176
|
+
"Re-run the ops inventory audit in one call: duplicate/dead schedules, code-work routing risks, enabled workflow fixtures, structured-output gate gaps, prompt registry drift, stale hosts, and systemDefault skill duplicates.",
|
|
177
|
+
source: asText(opsCatalogAuditSrc),
|
|
178
|
+
},
|
|
161
179
|
];
|
|
162
180
|
|
|
163
181
|
/** A catalog entry resolved into a generic {@link SeedItem}. */
|
|
@@ -31,6 +31,12 @@ import scheduledTaskResilienceConfig from "../../../templates/skills/scheduled-t
|
|
|
31
31
|
import scheduledTaskResilienceContent from "../../../templates/skills/scheduled-task-resilience/content.md" with {
|
|
32
32
|
type: "text",
|
|
33
33
|
};
|
|
34
|
+
import scriptWorkflowsConfig from "../../../templates/skills/script-workflows/config.json" with {
|
|
35
|
+
type: "text",
|
|
36
|
+
};
|
|
37
|
+
import scriptWorkflowsContent from "../../../templates/skills/script-workflows/content.md" with {
|
|
38
|
+
type: "text",
|
|
39
|
+
};
|
|
34
40
|
import swarmScriptsConfig from "../../../templates/skills/swarm-scripts/config.json" with {
|
|
35
41
|
type: "text",
|
|
36
42
|
};
|
|
@@ -72,6 +78,7 @@ const BUILT_IN_SKILL_SOURCES = [
|
|
|
72
78
|
{ config: kvStorageConfig, body: kvStorageContent },
|
|
73
79
|
{ config: pagesConfig, body: pagesContent },
|
|
74
80
|
{ config: scheduledTaskResilienceConfig, body: scheduledTaskResilienceContent },
|
|
81
|
+
{ config: scriptWorkflowsConfig, body: scriptWorkflowsContent },
|
|
75
82
|
{ config: swarmScriptsConfig, body: swarmScriptsContent },
|
|
76
83
|
{ config: workflowIterateConfig, body: workflowIterateContent },
|
|
77
84
|
{ config: workflowStructuredOutputConfig, body: workflowStructuredOutputContent },
|
|
@@ -41,6 +41,23 @@ const VALIDATED_KEYS: Record<string, (value: unknown) => string | null> = {
|
|
|
41
41
|
if (parsed.success) return null;
|
|
42
42
|
return `Invalid HARNESS_PROVIDER value (must be one of: ${ProviderNameSchema.options.join(", ")})`;
|
|
43
43
|
},
|
|
44
|
+
// Codex credits-exhausted cooldown (ms). Permissive on range here (positive
|
|
45
|
+
// integer) — the worker clamps to [5m, 7d] via resolveCodexCreditsExhaustedCooldownMs.
|
|
46
|
+
CODEX_CREDITS_EXHAUSTED_COOLDOWN_MS: (value) => {
|
|
47
|
+
const str = String(value).trim();
|
|
48
|
+
if (!/^\d+$/.test(str) || Number(str) <= 0) {
|
|
49
|
+
return "Invalid CODEX_CREDITS_EXHAUSTED_COOLDOWN_MS (must be a positive integer of milliseconds)";
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
},
|
|
53
|
+
SWARM_USE_CLAUDE_BRIDGE: (value) => {
|
|
54
|
+
if (typeof value !== "string") {
|
|
55
|
+
return "Invalid SWARM_USE_CLAUDE_BRIDGE value (must be one of: true, false, 1, 0)";
|
|
56
|
+
}
|
|
57
|
+
const normalized = value.trim().toLowerCase();
|
|
58
|
+
if (["true", "false", "1", "0"].includes(normalized)) return null;
|
|
59
|
+
return "Invalid SWARM_USE_CLAUDE_BRIDGE value (must be one of: true, false, 1, 0)";
|
|
60
|
+
},
|
|
44
61
|
};
|
|
45
62
|
|
|
46
63
|
export function validateConfigValue(key: string, value: unknown): string | null {
|
package/src/commands/runner.ts
CHANGED
|
@@ -40,9 +40,11 @@ import { getMcpBaseUrl } from "../utils/constants.ts";
|
|
|
40
40
|
import { getContextWindowSize } from "../utils/context-window.ts";
|
|
41
41
|
import { type CredentialSelection, resolveCredentialPools } from "../utils/credentials.ts";
|
|
42
42
|
import {
|
|
43
|
+
isCodexCreditsExhaustedMessage,
|
|
43
44
|
isRateLimitMessage,
|
|
44
45
|
MAX_RATE_LIMIT_RESET_MS,
|
|
45
46
|
parseRateLimitResetTime,
|
|
47
|
+
resolveCodexCreditsExhaustedCooldownMs,
|
|
46
48
|
} from "../utils/error-tracker.ts";
|
|
47
49
|
import { resolveHarnessProvider } from "../utils/harness-provider.ts";
|
|
48
50
|
import { prettyPrintLine, prettyPrintStderr } from "../utils/pretty-print.ts";
|
|
@@ -424,6 +426,7 @@ async function fetchResolvedEnv(
|
|
|
424
426
|
const RELOADABLE_ENV_KEYS: ReadonlySet<string> = new Set([
|
|
425
427
|
"MODEL_OVERRIDE",
|
|
426
428
|
"AGENT_FS_SHARED_ORG_ID",
|
|
429
|
+
"SWARM_USE_CLAUDE_BRIDGE",
|
|
427
430
|
]);
|
|
428
431
|
|
|
429
432
|
/**
|
|
@@ -1483,6 +1486,13 @@ interface RunnerState {
|
|
|
1483
1486
|
* (per-task live re-resolution) will mutate this between tasks.
|
|
1484
1487
|
*/
|
|
1485
1488
|
harnessProvider: ProviderName;
|
|
1489
|
+
/**
|
|
1490
|
+
* Effective Codex credits-exhausted cooldown (ms), resolved from `swarm_config`
|
|
1491
|
+
* (key `CODEX_CREDITS_EXHAUSTED_COOLDOWN_MS`) > default 2h constant, clamped to
|
|
1492
|
+
* [5m, 7d]. Reconciled live by `applySwarmConfigDrift` — read at the cooldown
|
|
1493
|
+
* application site so a fresh value applies to the next credits-exhausted failure.
|
|
1494
|
+
*/
|
|
1495
|
+
codexCreditsExhaustedCooldownMs: number;
|
|
1486
1496
|
}
|
|
1487
1497
|
|
|
1488
1498
|
/** Buffer for session logs */
|
|
@@ -3209,6 +3219,12 @@ async function checkCompletedProcesses(
|
|
|
3209
3219
|
console.log(
|
|
3210
3220
|
`[credentials] Parsed rate limit reset time from error: ${rateLimitedUntil}`,
|
|
3211
3221
|
);
|
|
3222
|
+
} else if (isCodexCreditsExhaustedMessage(failureReason)) {
|
|
3223
|
+
const cooldownMs = state.codexCreditsExhaustedCooldownMs;
|
|
3224
|
+
rateLimitedUntil = new Date(Date.now() + cooldownMs).toISOString();
|
|
3225
|
+
console.log(
|
|
3226
|
+
`[credentials] Codex credits exhausted — applying cooldown (${cooldownMs}ms): ${rateLimitedUntil}`,
|
|
3227
|
+
);
|
|
3212
3228
|
} else {
|
|
3213
3229
|
rateLimitedUntil = new Date(Date.now() + 5 * 60 * 1000).toISOString();
|
|
3214
3230
|
}
|
|
@@ -3403,10 +3419,22 @@ export async function runAgent(config: RunnerConfig, opts: RunnerOptions) {
|
|
|
3403
3419
|
// Failures (network, API down, malformed value) fall back to env then "claude"
|
|
3404
3420
|
// so a swarm_config outage cannot wedge boot.
|
|
3405
3421
|
let bootProvider: ProviderName;
|
|
3422
|
+
// Codex credits-exhausted cooldown is sourced solely from the global swarm_config
|
|
3423
|
+
// (key `CODEX_CREDITS_EXHAUSTED_COOLDOWN_MS`). Initialize to the default constant
|
|
3424
|
+
// for the case where it is unset, then apply the swarm_config value below; on a
|
|
3425
|
+
// boot-fetch failure it stays at the default. Reconciled live thereafter by
|
|
3426
|
+
// `applySwarmConfigDrift`.
|
|
3427
|
+
let bootCooldownMs = resolveCodexCreditsExhaustedCooldownMs(undefined);
|
|
3406
3428
|
try {
|
|
3407
|
-
|
|
3429
|
+
const bootEnv = await fetchResolvedEnv(apiUrl, apiKey, agentId);
|
|
3430
|
+
bootProvider = bootEnv.resolvedProvider;
|
|
3431
|
+
bootCooldownMs = resolveCodexCreditsExhaustedCooldownMs(
|
|
3432
|
+
bootEnv.env.CODEX_CREDITS_EXHAUSTED_COOLDOWN_MS,
|
|
3433
|
+
);
|
|
3408
3434
|
} catch (err) {
|
|
3409
|
-
console.warn(
|
|
3435
|
+
console.warn(
|
|
3436
|
+
`[runner] fetchResolvedEnv failed at boot, falling back to env for provider and the default cooldown: ${err}`,
|
|
3437
|
+
);
|
|
3410
3438
|
bootProvider = resolveHarnessProvider({}, process.env);
|
|
3411
3439
|
}
|
|
3412
3440
|
console.log(`[runner] Resolved HARNESS_PROVIDER: ${bootProvider}`);
|
|
@@ -3610,6 +3638,7 @@ export async function runAgent(config: RunnerConfig, opts: RunnerOptions) {
|
|
|
3610
3638
|
activeTasks: new Map(),
|
|
3611
3639
|
maxConcurrent,
|
|
3612
3640
|
harnessProvider: bootProvider,
|
|
3641
|
+
codexCreditsExhaustedCooldownMs: bootCooldownMs,
|
|
3613
3642
|
};
|
|
3614
3643
|
|
|
3615
3644
|
// Track tasks already signaled for cancellation to avoid repeated SIGTERM
|
|
@@ -3690,6 +3719,18 @@ export async function runAgent(config: RunnerConfig, opts: RunnerOptions) {
|
|
|
3690
3719
|
agentVisibleChanged = true;
|
|
3691
3720
|
}
|
|
3692
3721
|
|
|
3722
|
+
// (2b) Codex credits-exhausted cooldown — operator-tunable live. Not
|
|
3723
|
+
// agent-visible (doesn't change provider/maxConcurrent → no re-register).
|
|
3724
|
+
const nextCooldown = resolveCodexCreditsExhaustedCooldownMs(
|
|
3725
|
+
freshEnv.CODEX_CREDITS_EXHAUSTED_COOLDOWN_MS,
|
|
3726
|
+
);
|
|
3727
|
+
if (nextCooldown !== state.codexCreditsExhaustedCooldownMs) {
|
|
3728
|
+
console.log(
|
|
3729
|
+
`[${role}] [config] codexCreditsExhaustedCooldownMs: ${state.codexCreditsExhaustedCooldownMs} → ${nextCooldown}`,
|
|
3730
|
+
);
|
|
3731
|
+
state.codexCreditsExhaustedCooldownMs = nextCooldown;
|
|
3732
|
+
}
|
|
3733
|
+
|
|
3693
3734
|
// (3) Apply the small allowlist of safe-to-mutate env keys to process.env.
|
|
3694
3735
|
const changedKeys = applyResolvedEnvToProcessEnv(freshEnv);
|
|
3695
3736
|
if (changedKeys.length > 0) {
|
package/src/http/db-query.ts
CHANGED
|
@@ -11,6 +11,24 @@ export interface DbQueryResult {
|
|
|
11
11
|
total: number;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
export const DbQueryInputShape = {
|
|
15
|
+
sql: z.string().min(1).max(10_000).optional(),
|
|
16
|
+
query: z.string().min(1).max(10_000).optional().describe("Deprecated runtime alias for sql."),
|
|
17
|
+
params: z.array(z.any()).optional().default([]),
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const DbQueryInputSchema = z
|
|
21
|
+
.object(DbQueryInputShape)
|
|
22
|
+
.refine((body) => body.sql !== undefined || body.query !== undefined, {
|
|
23
|
+
message: "Either sql or query is required",
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
export type DbQueryInput = z.infer<typeof DbQueryInputSchema>;
|
|
27
|
+
|
|
28
|
+
export function resolveDbQuerySql(input: Pick<DbQueryInput, "sql" | "query">): string {
|
|
29
|
+
return input.sql ?? input.query ?? "";
|
|
30
|
+
}
|
|
31
|
+
|
|
14
32
|
function stripTrailingSemicolon(sql: string): string {
|
|
15
33
|
return sql.trim().replace(/;\s*$/, "").trim();
|
|
16
34
|
}
|
|
@@ -67,10 +85,7 @@ const dbQueryRoute = route({
|
|
|
67
85
|
pattern: ["api", "db-query"],
|
|
68
86
|
summary: "Execute a read-only SQL query",
|
|
69
87
|
tags: ["Debug"],
|
|
70
|
-
body:
|
|
71
|
-
sql: z.string().min(1).max(10_000),
|
|
72
|
-
params: z.array(z.any()).optional().default([]),
|
|
73
|
-
}),
|
|
88
|
+
body: DbQueryInputSchema,
|
|
74
89
|
responses: {
|
|
75
90
|
200: {
|
|
76
91
|
description: "Query results",
|
|
@@ -100,7 +115,7 @@ export async function handleDbQuery(
|
|
|
100
115
|
if (!parsed) return true;
|
|
101
116
|
|
|
102
117
|
try {
|
|
103
|
-
const result = executeReadOnlyQuery(parsed.body
|
|
118
|
+
const result = executeReadOnlyQuery(resolveDbQuerySql(parsed.body), parsed.body.params);
|
|
104
119
|
json(res, result);
|
|
105
120
|
} catch (err: unknown) {
|
|
106
121
|
const message = err instanceof Error ? err.message : String(err);
|
package/src/http/index.ts
CHANGED
|
@@ -21,9 +21,11 @@ import {
|
|
|
21
21
|
withRemoteContext,
|
|
22
22
|
withSpanContext,
|
|
23
23
|
} from "../otel";
|
|
24
|
+
import { startScriptRunSupervisor, stopScriptRunSupervisor } from "../script-workflows/supervisor";
|
|
24
25
|
import { startSlackApp, stopSlackApp } from "../slack";
|
|
25
26
|
import { initTelemetry, telemetry } from "../telemetry";
|
|
26
27
|
import { getApiKey } from "../utils/api-key";
|
|
28
|
+
import { getMcpBaseUrl } from "../utils/constants";
|
|
27
29
|
import { scrubSecrets } from "../utils/secret-scrubber";
|
|
28
30
|
import { initWorkflows } from "../workflows";
|
|
29
31
|
import { handleActiveSessions } from "./active-sessions";
|
|
@@ -57,6 +59,7 @@ import { handlePromptTemplates } from "./prompt-templates";
|
|
|
57
59
|
import { handleRepos } from "./repos";
|
|
58
60
|
import { describeRequestRoute } from "./route-def";
|
|
59
61
|
import { handleSchedules } from "./schedules";
|
|
62
|
+
import { handleScriptRuns } from "./script-runs";
|
|
60
63
|
import { handleScripts } from "./scripts";
|
|
61
64
|
import { handleSessionData } from "./session-data";
|
|
62
65
|
import { handleSessions } from "./sessions";
|
|
@@ -272,6 +275,7 @@ const httpServer = createHttpServer(async (req, res) => {
|
|
|
272
275
|
() => handleMetrics(req, res, pathSegments, queryParams, myAgentId),
|
|
273
276
|
() => handleRepos(req, res, pathSegments, queryParams),
|
|
274
277
|
() => handleSkills(req, res, pathSegments, queryParams, myAgentId),
|
|
278
|
+
() => handleScriptRuns(req, res, pathSegments, queryParams, myAgentId),
|
|
275
279
|
() => handleScripts(req, res, pathSegments, queryParams, myAgentId),
|
|
276
280
|
() => handleMcpBridge(req, res, pathSegments, queryParams, myAgentId),
|
|
277
281
|
() => handleMcpServers(req, res, pathSegments, queryParams),
|
|
@@ -343,6 +347,9 @@ async function shutdown() {
|
|
|
343
347
|
// Stop heartbeat triage
|
|
344
348
|
stopHeartbeat();
|
|
345
349
|
|
|
350
|
+
// Stop durable script workflow subprocesses
|
|
351
|
+
stopScriptRunSupervisor();
|
|
352
|
+
|
|
346
353
|
// Stop Slack bot
|
|
347
354
|
await stopSlackApp();
|
|
348
355
|
|
|
@@ -487,6 +494,9 @@ httpServer
|
|
|
487
494
|
// Initialize workflow engine (trigger subscriptions + resume listener)
|
|
488
495
|
initWorkflows();
|
|
489
496
|
|
|
497
|
+
// Reconcile durable script workflow subprocesses
|
|
498
|
+
startScriptRunSupervisor(getMcpBaseUrl());
|
|
499
|
+
|
|
490
500
|
// Start scheduler (if enabled)
|
|
491
501
|
if (hasCapability("scheduling")) {
|
|
492
502
|
const { startScheduler } = await import("../scheduler");
|