appostle-installer 0.0.8 → 0.0.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/dist/appostle-installer.js +25 -14
- package/dist/appostle-installer.js.map +2 -2
- package/dist/appostle.js +263 -81
- package/dist/appostle.js.map +4 -4
- package/dist/worker.js +698 -537
- package/dist/worker.js.map +4 -4
- package/package.json +1 -1
package/dist/worker.js
CHANGED
|
@@ -2302,7 +2302,8 @@ function toAgentPayload(agent, options) {
|
|
|
2302
2302
|
pendingPermissions: sanitizePendingPermissions(agent.pendingPermissions),
|
|
2303
2303
|
persistence: sanitizePersistenceHandle(agent.persistence),
|
|
2304
2304
|
title: options?.title ?? null,
|
|
2305
|
-
labels: agent.labels
|
|
2305
|
+
labels: agent.labels,
|
|
2306
|
+
internal: agent.internal
|
|
2306
2307
|
};
|
|
2307
2308
|
const usage = sanitizeUsage(agent.lastUsage);
|
|
2308
2309
|
if (usage !== void 0) {
|
|
@@ -2372,7 +2373,8 @@ function buildStoredAgentPayload(record, providerRegistry, logger) {
|
|
|
2372
2373
|
attentionReason: record.attentionReason ?? null,
|
|
2373
2374
|
attentionTimestamp: record.attentionTimestamp ?? null,
|
|
2374
2375
|
archivedAt: record.archivedAt ?? null,
|
|
2375
|
-
labels: record.labels
|
|
2376
|
+
labels: record.labels,
|
|
2377
|
+
internal: record.internal
|
|
2376
2378
|
};
|
|
2377
2379
|
}
|
|
2378
2380
|
function resolveStoredAgentPayloadUpdatedAt(record) {
|
|
@@ -2414,7 +2416,13 @@ function buildSerializableConfig(config) {
|
|
|
2414
2416
|
serializable.systemPrompt = config.systemPrompt;
|
|
2415
2417
|
}
|
|
2416
2418
|
if (config.mcpServers) {
|
|
2417
|
-
|
|
2419
|
+
const persistable = {};
|
|
2420
|
+
for (const [name, server] of Object.entries(config.mcpServers)) {
|
|
2421
|
+
if (server.type !== "sdk") persistable[name] = server;
|
|
2422
|
+
}
|
|
2423
|
+
if (Object.keys(persistable).length > 0) {
|
|
2424
|
+
serializable.mcpServers = persistable;
|
|
2425
|
+
}
|
|
2418
2426
|
}
|
|
2419
2427
|
return Object.keys(serializable).length ? serializable : null;
|
|
2420
2428
|
}
|
|
@@ -2453,34 +2461,42 @@ function cloneAvailableModes(modes) {
|
|
|
2453
2461
|
function normalizeFeatures(features) {
|
|
2454
2462
|
return Array.isArray(features) ? features.map((feature) => ({ ...feature })) : [];
|
|
2455
2463
|
}
|
|
2456
|
-
|
|
2464
|
+
var SANITIZE_MAX_DEPTH = 32;
|
|
2465
|
+
function sanitizeOptionalJson(value, seen = /* @__PURE__ */ new WeakSet(), depth = 0) {
|
|
2457
2466
|
if (value === void 0) {
|
|
2458
2467
|
return void 0;
|
|
2459
2468
|
}
|
|
2460
2469
|
if (value === null) {
|
|
2461
2470
|
return null;
|
|
2462
2471
|
}
|
|
2463
|
-
if (
|
|
2464
|
-
|
|
2465
|
-
return sanitized;
|
|
2472
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
2473
|
+
return value;
|
|
2466
2474
|
}
|
|
2467
2475
|
if (value instanceof Date) {
|
|
2468
2476
|
return value.toISOString();
|
|
2469
2477
|
}
|
|
2470
|
-
if (typeof value
|
|
2471
|
-
|
|
2472
|
-
for (const [key, val] of Object.entries(value)) {
|
|
2473
|
-
const sanitized = sanitizeOptionalJson(val);
|
|
2474
|
-
if (sanitized !== void 0) {
|
|
2475
|
-
result[key] = sanitized;
|
|
2476
|
-
}
|
|
2477
|
-
}
|
|
2478
|
-
return Object.keys(result).length ? result : void 0;
|
|
2478
|
+
if (typeof value !== "object") {
|
|
2479
|
+
return void 0;
|
|
2479
2480
|
}
|
|
2480
|
-
if (
|
|
2481
|
-
return
|
|
2481
|
+
if (depth >= SANITIZE_MAX_DEPTH) {
|
|
2482
|
+
return void 0;
|
|
2482
2483
|
}
|
|
2483
|
-
|
|
2484
|
+
if (seen.has(value)) {
|
|
2485
|
+
return void 0;
|
|
2486
|
+
}
|
|
2487
|
+
seen.add(value);
|
|
2488
|
+
if (Array.isArray(value)) {
|
|
2489
|
+
const sanitized = value.map((item) => sanitizeOptionalJson(item, seen, depth + 1)).filter((item) => item !== void 0);
|
|
2490
|
+
return sanitized;
|
|
2491
|
+
}
|
|
2492
|
+
const result = {};
|
|
2493
|
+
for (const [key, val] of Object.entries(value)) {
|
|
2494
|
+
const sanitized = sanitizeOptionalJson(val, seen, depth + 1);
|
|
2495
|
+
if (sanitized !== void 0) {
|
|
2496
|
+
result[key] = sanitized;
|
|
2497
|
+
}
|
|
2498
|
+
}
|
|
2499
|
+
return Object.keys(result).length ? result : void 0;
|
|
2484
2500
|
}
|
|
2485
2501
|
function isJsonObject(value) {
|
|
2486
2502
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -2541,6 +2557,34 @@ function sanitizeUsage(value) {
|
|
|
2541
2557
|
} else if (contextWindowUsedTokens !== void 0 && contextWindowUsedTokens !== null) {
|
|
2542
2558
|
return void 0;
|
|
2543
2559
|
}
|
|
2560
|
+
const rateLimits = sanitized.rateLimits;
|
|
2561
|
+
if (Array.isArray(rateLimits)) {
|
|
2562
|
+
const VALID_RATE_LIMIT_TYPES = /* @__PURE__ */ new Set([
|
|
2563
|
+
"five_hour",
|
|
2564
|
+
"seven_day",
|
|
2565
|
+
"seven_day_opus",
|
|
2566
|
+
"seven_day_sonnet",
|
|
2567
|
+
"overage"
|
|
2568
|
+
]);
|
|
2569
|
+
const sanitizedRateLimits = [];
|
|
2570
|
+
for (const entry of rateLimits) {
|
|
2571
|
+
if (!entry || typeof entry !== "object") continue;
|
|
2572
|
+
const { rateLimitType, utilization, resetsAt } = entry;
|
|
2573
|
+
if (typeof rateLimitType !== "string" || !VALID_RATE_LIMIT_TYPES.has(rateLimitType)) continue;
|
|
2574
|
+
if (typeof utilization !== "number" || !Number.isFinite(utilization)) continue;
|
|
2575
|
+
const limit = {
|
|
2576
|
+
rateLimitType,
|
|
2577
|
+
utilization
|
|
2578
|
+
};
|
|
2579
|
+
if (typeof resetsAt === "number" && Number.isFinite(resetsAt)) {
|
|
2580
|
+
limit.resetsAt = resetsAt;
|
|
2581
|
+
}
|
|
2582
|
+
sanitizedRateLimits.push(limit);
|
|
2583
|
+
}
|
|
2584
|
+
if (sanitizedRateLimits.length > 0) {
|
|
2585
|
+
result.rateLimits = sanitizedRateLimits;
|
|
2586
|
+
}
|
|
2587
|
+
}
|
|
2544
2588
|
return Object.keys(result).length ? result : void 0;
|
|
2545
2589
|
}
|
|
2546
2590
|
function sanitizeRuntimeInfo(runtimeInfo) {
|
|
@@ -3327,6 +3371,13 @@ var QuestRecordSchema = z8.object({
|
|
|
3327
3371
|
cwd: z8.string(),
|
|
3328
3372
|
/** User's task prompt — fed to every card unless overridden per-card. */
|
|
3329
3373
|
prompt: z8.string(),
|
|
3374
|
+
/**
|
|
3375
|
+
* Short auto-generated label for the quest, derived from the user prompt by a
|
|
3376
|
+
* small internal metadata agent (mirrors how single-agent sessions get a
|
|
3377
|
+
* title). Empty string until generation finishes (or if it fails). Optional +
|
|
3378
|
+
* default so old persisted records remain valid.
|
|
3379
|
+
*/
|
|
3380
|
+
name: z8.string().optional().default(""),
|
|
3330
3381
|
defaultProvider: AgentProviderSchema,
|
|
3331
3382
|
defaultModel: z8.string().nullable(),
|
|
3332
3383
|
termination: QuestTerminationSchema,
|
|
@@ -3397,6 +3448,8 @@ var QuestListItemSchema = z9.object({
|
|
|
3397
3448
|
status: QuestStatusSchema,
|
|
3398
3449
|
cwd: z9.string(),
|
|
3399
3450
|
prompt: z9.string(),
|
|
3451
|
+
/** Auto-generated short label. Optional for backward compat with old daemons. */
|
|
3452
|
+
name: z9.string().optional().default(""),
|
|
3400
3453
|
createdAt: z9.string(),
|
|
3401
3454
|
updatedAt: z9.string()
|
|
3402
3455
|
});
|
|
@@ -3748,13 +3801,25 @@ var AgentCapabilityFlagsSchema = z10.object({
|
|
|
3748
3801
|
supportsReasoningStream: z10.boolean(),
|
|
3749
3802
|
supportsToolInvocations: z10.boolean()
|
|
3750
3803
|
});
|
|
3804
|
+
var AgentRateLimitSchema = z10.object({
|
|
3805
|
+
rateLimitType: z10.enum([
|
|
3806
|
+
"five_hour",
|
|
3807
|
+
"seven_day",
|
|
3808
|
+
"seven_day_opus",
|
|
3809
|
+
"seven_day_sonnet",
|
|
3810
|
+
"overage"
|
|
3811
|
+
]),
|
|
3812
|
+
utilization: z10.number(),
|
|
3813
|
+
resetsAt: z10.number().optional()
|
|
3814
|
+
});
|
|
3751
3815
|
var AgentUsageSchema = z10.object({
|
|
3752
3816
|
inputTokens: z10.number().optional(),
|
|
3753
3817
|
cachedInputTokens: z10.number().optional(),
|
|
3754
3818
|
outputTokens: z10.number().optional(),
|
|
3755
3819
|
totalCostUsd: z10.number().optional(),
|
|
3756
3820
|
contextWindowMaxTokens: z10.number().optional(),
|
|
3757
|
-
contextWindowUsedTokens: z10.number().optional()
|
|
3821
|
+
contextWindowUsedTokens: z10.number().optional(),
|
|
3822
|
+
rateLimits: z10.array(AgentRateLimitSchema).optional()
|
|
3758
3823
|
});
|
|
3759
3824
|
var McpStdioServerConfigSchema = z10.object({
|
|
3760
3825
|
type: z10.literal("stdio"),
|
|
@@ -4140,7 +4205,15 @@ var AgentSnapshotPayloadSchema = z10.object({
|
|
|
4140
4205
|
archivedAt: z10.string().nullable().optional(),
|
|
4141
4206
|
// Fork lineage — optional for backward compat with pre-fork agents.
|
|
4142
4207
|
parentAgentId: z10.string().optional(),
|
|
4143
|
-
forkedFromMessageUuid: z10.string().optional()
|
|
4208
|
+
forkedFromMessageUuid: z10.string().optional(),
|
|
4209
|
+
/**
|
|
4210
|
+
* True when the agent is a system-spawned helper (orchestrator queens,
|
|
4211
|
+
* handoff workers). Optional for backward compat — older clients ignore
|
|
4212
|
+
* the field. Newer clients filter these out of workspace tab lists / agent
|
|
4213
|
+
* lists at display time. The agent itself is a real, full-featured session
|
|
4214
|
+
* in every other respect; `internal` is a UI visibility hint, nothing more.
|
|
4215
|
+
*/
|
|
4216
|
+
internal: z10.boolean().optional()
|
|
4144
4217
|
});
|
|
4145
4218
|
var VoiceAudioChunkMessageSchema = z10.object({
|
|
4146
4219
|
type: z10.literal("voice_audio_chunk"),
|
|
@@ -18090,6 +18163,8 @@ function toClaudeSdkMcpConfig(config) {
|
|
|
18090
18163
|
url: config.url,
|
|
18091
18164
|
headers: config.headers
|
|
18092
18165
|
};
|
|
18166
|
+
case "sdk":
|
|
18167
|
+
return config.config;
|
|
18093
18168
|
}
|
|
18094
18169
|
}
|
|
18095
18170
|
var homeMcpServersCache = null;
|
|
@@ -20009,6 +20084,43 @@ ${error.stack ?? ""}` : JSON.stringify(error);
|
|
|
20009
20084
|
}
|
|
20010
20085
|
break;
|
|
20011
20086
|
}
|
|
20087
|
+
case "rate_limit_event": {
|
|
20088
|
+
const info = message.rate_limit_info;
|
|
20089
|
+
if (info && typeof info === "object") {
|
|
20090
|
+
const { rateLimitType, utilization, resetsAt } = info;
|
|
20091
|
+
const VALID_TYPES = /* @__PURE__ */ new Set([
|
|
20092
|
+
"five_hour",
|
|
20093
|
+
"seven_day",
|
|
20094
|
+
"seven_day_opus",
|
|
20095
|
+
"seven_day_sonnet",
|
|
20096
|
+
"overage"
|
|
20097
|
+
]);
|
|
20098
|
+
if (typeof rateLimitType === "string" && VALID_TYPES.has(rateLimitType) && typeof utilization === "number") {
|
|
20099
|
+
const rateLimit = {
|
|
20100
|
+
rateLimitType,
|
|
20101
|
+
utilization
|
|
20102
|
+
};
|
|
20103
|
+
if (typeof resetsAt === "number") {
|
|
20104
|
+
rateLimit.resetsAt = resetsAt;
|
|
20105
|
+
}
|
|
20106
|
+
const existingLimits = this.lastRateLimits ?? [];
|
|
20107
|
+
const updated = existingLimits.filter((l) => l.rateLimitType !== rateLimitType);
|
|
20108
|
+
updated.push(rateLimit);
|
|
20109
|
+
this.lastRateLimits = updated;
|
|
20110
|
+
const usage = {
|
|
20111
|
+
rateLimits: updated
|
|
20112
|
+
};
|
|
20113
|
+
if (this.lastContextWindowMaxTokens !== void 0) {
|
|
20114
|
+
usage.contextWindowMaxTokens = this.lastContextWindowMaxTokens;
|
|
20115
|
+
}
|
|
20116
|
+
if (this.lastContextWindowUsedTokens !== void 0) {
|
|
20117
|
+
usage.contextWindowUsedTokens = this.lastContextWindowUsedTokens;
|
|
20118
|
+
}
|
|
20119
|
+
events.push({ type: "usage_updated", provider: "claude", usage });
|
|
20120
|
+
}
|
|
20121
|
+
}
|
|
20122
|
+
break;
|
|
20123
|
+
}
|
|
20012
20124
|
default:
|
|
20013
20125
|
break;
|
|
20014
20126
|
}
|
|
@@ -20117,6 +20229,9 @@ ${error.stack ?? ""}` : JSON.stringify(error);
|
|
|
20117
20229
|
outputTokens: message.usage.output_tokens,
|
|
20118
20230
|
totalCostUsd: message.total_cost_usd
|
|
20119
20231
|
};
|
|
20232
|
+
if (this.lastRateLimits !== void 0) {
|
|
20233
|
+
usage.rateLimits = this.lastRateLimits;
|
|
20234
|
+
}
|
|
20120
20235
|
const contextWindowMaxTokens = extractContextWindowSize(modelUsage ?? message.modelUsage);
|
|
20121
20236
|
if (contextWindowMaxTokens !== void 0) {
|
|
20122
20237
|
this.lastContextWindowMaxTokens = contextWindowMaxTokens;
|
|
@@ -20486,10 +20601,10 @@ ${error.stack ?? ""}` : JSON.stringify(error);
|
|
|
20486
20601
|
return void 0;
|
|
20487
20602
|
}
|
|
20488
20603
|
const server = entry?.server ?? block.server ?? "tool";
|
|
20489
|
-
const
|
|
20604
|
+
const tool2 = entry?.name ?? block.tool_name ?? "tool";
|
|
20490
20605
|
const content = coerceToolResultContentToString(block.content);
|
|
20491
20606
|
const input = entry?.input;
|
|
20492
|
-
const structured = this.buildStructuredToolResult(server,
|
|
20607
|
+
const structured = this.buildStructuredToolResult(server, tool2, content, input);
|
|
20493
20608
|
if (structured) {
|
|
20494
20609
|
return structured;
|
|
20495
20610
|
}
|
|
@@ -20506,9 +20621,9 @@ ${error.stack ?? ""}` : JSON.stringify(error);
|
|
|
20506
20621
|
}
|
|
20507
20622
|
return Object.keys(result).length > 0 ? result : void 0;
|
|
20508
20623
|
}
|
|
20509
|
-
buildStructuredToolResult(server,
|
|
20624
|
+
buildStructuredToolResult(server, tool2, output, input) {
|
|
20510
20625
|
const normalizedServer = server.toLowerCase();
|
|
20511
|
-
const normalizedTool =
|
|
20626
|
+
const normalizedTool = tool2.toLowerCase();
|
|
20512
20627
|
if (normalizedServer.includes("bash") || normalizedServer.includes("shell") || normalizedServer.includes("command") || normalizedTool.includes("bash") || normalizedTool.includes("shell") || normalizedTool.includes("command") || input && (typeof input.command === "string" || Array.isArray(input.command))) {
|
|
20513
20628
|
const command = this.extractCommandText(input ?? {}) ?? "command";
|
|
20514
20629
|
return {
|
|
@@ -21822,8 +21937,8 @@ function resolveStatus(rawStatus, error, output) {
|
|
|
21822
21937
|
}
|
|
21823
21938
|
return output !== null && output !== void 0 ? "completed" : "running";
|
|
21824
21939
|
}
|
|
21825
|
-
function buildMcpToolName(server,
|
|
21826
|
-
const trimmedTool =
|
|
21940
|
+
function buildMcpToolName(server, tool2) {
|
|
21941
|
+
const trimmedTool = tool2.trim();
|
|
21827
21942
|
if (!trimmedTool) {
|
|
21828
21943
|
return "tool";
|
|
21829
21944
|
}
|
|
@@ -21993,11 +22108,11 @@ function mapFileChangeItem(item, options) {
|
|
|
21993
22108
|
};
|
|
21994
22109
|
}
|
|
21995
22110
|
function mapMcpToolCallItem(item, options) {
|
|
21996
|
-
const
|
|
21997
|
-
if (!
|
|
22111
|
+
const tool2 = item.tool.trim();
|
|
22112
|
+
if (!tool2) {
|
|
21998
22113
|
return null;
|
|
21999
22114
|
}
|
|
22000
|
-
const name = buildMcpToolName(item.server,
|
|
22115
|
+
const name = buildMcpToolName(item.server, tool2);
|
|
22001
22116
|
const input = item.arguments ?? null;
|
|
22002
22117
|
const output = item.result ?? null;
|
|
22003
22118
|
const error = item.error ?? null;
|
|
@@ -22958,6 +23073,8 @@ function toCodexMcpConfig(config) {
|
|
|
22958
23073
|
url: config.url,
|
|
22959
23074
|
http_headers: config.headers
|
|
22960
23075
|
};
|
|
23076
|
+
case "sdk":
|
|
23077
|
+
return null;
|
|
22961
23078
|
}
|
|
22962
23079
|
}
|
|
22963
23080
|
var CodexAppServerClient = class {
|
|
@@ -25100,7 +25217,8 @@ var CodexAppServerAgentSession = class {
|
|
|
25100
25217
|
if (this.config.mcpServers) {
|
|
25101
25218
|
const mcpServers = {};
|
|
25102
25219
|
for (const [name, serverConfig] of Object.entries(this.config.mcpServers)) {
|
|
25103
|
-
|
|
25220
|
+
const codexConfig = toCodexMcpConfig(serverConfig);
|
|
25221
|
+
if (codexConfig) mcpServers[name] = codexConfig;
|
|
25104
25222
|
}
|
|
25105
25223
|
innerConfig.mcp_servers = mcpServers;
|
|
25106
25224
|
}
|
|
@@ -27146,9 +27264,10 @@ function normalizeMcpServers(servers) {
|
|
|
27146
27264
|
if (!servers) {
|
|
27147
27265
|
return [];
|
|
27148
27266
|
}
|
|
27149
|
-
|
|
27267
|
+
const out = [];
|
|
27268
|
+
for (const [name, config] of Object.entries(servers)) {
|
|
27150
27269
|
if (config.type === "stdio") {
|
|
27151
|
-
|
|
27270
|
+
out.push({
|
|
27152
27271
|
name,
|
|
27153
27272
|
command: config.command,
|
|
27154
27273
|
args: config.args ?? [],
|
|
@@ -27156,29 +27275,35 @@ function normalizeMcpServers(servers) {
|
|
|
27156
27275
|
name: envName,
|
|
27157
27276
|
value
|
|
27158
27277
|
}))
|
|
27159
|
-
};
|
|
27278
|
+
});
|
|
27279
|
+
continue;
|
|
27160
27280
|
}
|
|
27161
27281
|
if (config.type === "http") {
|
|
27162
|
-
|
|
27282
|
+
out.push({
|
|
27163
27283
|
type: "http",
|
|
27164
27284
|
name,
|
|
27165
27285
|
url: config.url,
|
|
27166
27286
|
headers: Object.entries(config.headers ?? {}).map(([headerName, value]) => ({
|
|
27167
27287
|
name: headerName,
|
|
27168
|
-
value
|
|
27288
|
+
value: String(value)
|
|
27169
27289
|
}))
|
|
27170
|
-
};
|
|
27290
|
+
});
|
|
27291
|
+
continue;
|
|
27171
27292
|
}
|
|
27172
|
-
|
|
27173
|
-
|
|
27174
|
-
|
|
27175
|
-
|
|
27176
|
-
|
|
27177
|
-
|
|
27178
|
-
|
|
27179
|
-
|
|
27180
|
-
|
|
27181
|
-
|
|
27293
|
+
if (config.type === "sse") {
|
|
27294
|
+
out.push({
|
|
27295
|
+
type: "sse",
|
|
27296
|
+
name,
|
|
27297
|
+
url: config.url,
|
|
27298
|
+
headers: Object.entries(config.headers ?? {}).map(([headerName, value]) => ({
|
|
27299
|
+
name: headerName,
|
|
27300
|
+
value: String(value)
|
|
27301
|
+
}))
|
|
27302
|
+
});
|
|
27303
|
+
continue;
|
|
27304
|
+
}
|
|
27305
|
+
}
|
|
27306
|
+
return out;
|
|
27182
27307
|
}
|
|
27183
27308
|
function toACPContentBlocks(prompt) {
|
|
27184
27309
|
if (typeof prompt === "string") {
|
|
@@ -28009,12 +28134,15 @@ function toOpenCodeMcpConfig(config) {
|
|
|
28009
28134
|
enabled: true
|
|
28010
28135
|
};
|
|
28011
28136
|
}
|
|
28012
|
-
|
|
28013
|
-
|
|
28014
|
-
|
|
28015
|
-
|
|
28016
|
-
|
|
28017
|
-
|
|
28137
|
+
if (config.type === "http" || config.type === "sse") {
|
|
28138
|
+
return {
|
|
28139
|
+
type: "remote",
|
|
28140
|
+
url: config.url,
|
|
28141
|
+
...config.headers ? { headers: config.headers } : {},
|
|
28142
|
+
enabled: true
|
|
28143
|
+
};
|
|
28144
|
+
}
|
|
28145
|
+
return null;
|
|
28018
28146
|
}
|
|
28019
28147
|
function stringifyUnknownError(error) {
|
|
28020
28148
|
if (typeof error === "string") {
|
|
@@ -28957,7 +29085,7 @@ function translateOpenCodeEvent(event, state) {
|
|
|
28957
29085
|
break;
|
|
28958
29086
|
}
|
|
28959
29087
|
const metadata = readOpenCodeRecord(event.properties.metadata);
|
|
28960
|
-
const
|
|
29088
|
+
const tool2 = readOpenCodeRecord(event.properties.tool);
|
|
28961
29089
|
const patterns = Array.isArray(event.properties.patterns) ? event.properties.patterns.filter((value) => typeof value === "string") : [];
|
|
28962
29090
|
const command = readPermissionField(metadata, PERMISSION_COMMAND_KEYS);
|
|
28963
29091
|
const cwd = readPermissionField(metadata, PERMISSION_CWD_KEYS);
|
|
@@ -28965,7 +29093,7 @@ function translateOpenCodeEvent(event, state) {
|
|
|
28965
29093
|
const input = buildOpenCodePermissionInput({
|
|
28966
29094
|
patterns,
|
|
28967
29095
|
metadata,
|
|
28968
|
-
tool,
|
|
29096
|
+
tool: tool2,
|
|
28969
29097
|
command
|
|
28970
29098
|
});
|
|
28971
29099
|
const detail = buildOpenCodePermissionDetail({
|
|
@@ -29713,6 +29841,7 @@ var OpenCodeAgentSession = class {
|
|
|
29713
29841
|
async configureMcpServers(mcpServers) {
|
|
29714
29842
|
for (const [name, serverConfig] of Object.entries(mcpServers)) {
|
|
29715
29843
|
const mappedConfig = toOpenCodeMcpConfig(serverConfig);
|
|
29844
|
+
if (!mappedConfig) continue;
|
|
29716
29845
|
await this.registerMcpServer(name, mappedConfig);
|
|
29717
29846
|
}
|
|
29718
29847
|
}
|
|
@@ -41250,7 +41379,7 @@ ${details}`.trim());
|
|
|
41250
41379
|
);
|
|
41251
41380
|
const registryRecords = await this.agentStorage.list();
|
|
41252
41381
|
const liveIds = new Set(agentSnapshots.map((a) => a.id));
|
|
41253
|
-
const persistedAgents = registryRecords.filter((record) => !liveIds.has(record.id)
|
|
41382
|
+
const persistedAgents = registryRecords.filter((record) => !liveIds.has(record.id)).map((record) => this.buildStoredAgentPayload(record));
|
|
41254
41383
|
let agents = [...liveAgents, ...persistedAgents];
|
|
41255
41384
|
agents = agents.filter((agent) => this.isProviderVisibleToClient(agent.provider));
|
|
41256
41385
|
if (filter?.labels) {
|
|
@@ -43852,6 +43981,7 @@ ${details}`.trim());
|
|
|
43852
43981
|
status: w.status,
|
|
43853
43982
|
cwd: w.cwd,
|
|
43854
43983
|
prompt: w.prompt,
|
|
43984
|
+
name: w.name,
|
|
43855
43985
|
createdAt: w.createdAt,
|
|
43856
43986
|
updatedAt: w.updatedAt
|
|
43857
43987
|
}));
|
|
@@ -51684,7 +51814,7 @@ var AgentManager = class {
|
|
|
51684
51814
|
}
|
|
51685
51815
|
break;
|
|
51686
51816
|
case "usage_updated":
|
|
51687
|
-
agent.lastUsage = event.usage;
|
|
51817
|
+
agent.lastUsage = { ...agent.lastUsage, ...event.usage };
|
|
51688
51818
|
this.emitState(agent);
|
|
51689
51819
|
break;
|
|
51690
51820
|
case "timeline":
|
|
@@ -51734,7 +51864,7 @@ var AgentManager = class {
|
|
|
51734
51864
|
},
|
|
51735
51865
|
"handleStreamEvent: turn_completed"
|
|
51736
51866
|
);
|
|
51737
|
-
agent.lastUsage = event.usage;
|
|
51867
|
+
agent.lastUsage = { ...agent.lastUsage, ...event.usage };
|
|
51738
51868
|
agent.lastError = void 0;
|
|
51739
51869
|
if (!isForegroundEvent && agent.lifecycle !== "idle" && !agent.pendingReplacement) {
|
|
51740
51870
|
agent.lifecycle = "idle";
|
|
@@ -52024,17 +52154,6 @@ var AgentManager = class {
|
|
|
52024
52154
|
if (subscriber.agentId && event.type === "agent_state" && subscriber.agentId !== event.agent.id) {
|
|
52025
52155
|
continue;
|
|
52026
52156
|
}
|
|
52027
|
-
if (!subscriber.agentId) {
|
|
52028
|
-
if (event.type === "agent_state" && event.agent.internal) {
|
|
52029
|
-
continue;
|
|
52030
|
-
}
|
|
52031
|
-
if (event.type === "agent_stream") {
|
|
52032
|
-
const agent = this.agents.get(event.agentId);
|
|
52033
|
-
if (agent?.internal) {
|
|
52034
|
-
continue;
|
|
52035
|
-
}
|
|
52036
|
-
}
|
|
52037
|
-
}
|
|
52038
52157
|
subscriber.callback(event);
|
|
52039
52158
|
}
|
|
52040
52159
|
}
|
|
@@ -55580,6 +55699,97 @@ function getRulesForKind(kind) {
|
|
|
55580
55699
|
}
|
|
55581
55700
|
}
|
|
55582
55701
|
|
|
55702
|
+
// ../server/src/server/quest/quest-metadata-generator.ts
|
|
55703
|
+
import { z as z41 } from "zod";
|
|
55704
|
+
var MAX_AUTO_QUEST_NAME_CHARS = 40;
|
|
55705
|
+
function hasExplicitName(name) {
|
|
55706
|
+
return Boolean(name && name.trim().length > 0);
|
|
55707
|
+
}
|
|
55708
|
+
function normalizeAutoName(name) {
|
|
55709
|
+
const normalized = name.trim();
|
|
55710
|
+
if (!normalized) {
|
|
55711
|
+
return null;
|
|
55712
|
+
}
|
|
55713
|
+
return normalized.slice(0, MAX_AUTO_QUEST_NAME_CHARS).trim() || null;
|
|
55714
|
+
}
|
|
55715
|
+
function buildPrompt2(prompt) {
|
|
55716
|
+
return [
|
|
55717
|
+
"Generate a short label for a multi-agent quest based on the user prompt.",
|
|
55718
|
+
`Name: short descriptive label (<= ${MAX_AUTO_QUEST_NAME_CHARS} chars).`,
|
|
55719
|
+
"Return JSON only with a single field 'name'.",
|
|
55720
|
+
"",
|
|
55721
|
+
"User prompt:",
|
|
55722
|
+
prompt
|
|
55723
|
+
].join("\n");
|
|
55724
|
+
}
|
|
55725
|
+
async function generateAndApplyQuestMetadata(options) {
|
|
55726
|
+
const prompt = options.prompt.trim();
|
|
55727
|
+
if (!prompt) {
|
|
55728
|
+
return;
|
|
55729
|
+
}
|
|
55730
|
+
if (hasExplicitName(options.explicitName)) {
|
|
55731
|
+
return;
|
|
55732
|
+
}
|
|
55733
|
+
const schema = z41.object({
|
|
55734
|
+
name: z41.string().min(1).max(MAX_AUTO_QUEST_NAME_CHARS)
|
|
55735
|
+
});
|
|
55736
|
+
const generator = options.deps?.generateStructuredAgentResponseWithFallback ?? generateStructuredAgentResponseWithFallback;
|
|
55737
|
+
let result;
|
|
55738
|
+
try {
|
|
55739
|
+
result = await generator({
|
|
55740
|
+
manager: options.questService.agentManager,
|
|
55741
|
+
cwd: options.cwd,
|
|
55742
|
+
prompt: buildPrompt2(prompt),
|
|
55743
|
+
schema,
|
|
55744
|
+
schemaName: "QuestMetadata",
|
|
55745
|
+
maxRetries: 2,
|
|
55746
|
+
providers: DEFAULT_STRUCTURED_GENERATION_PROVIDERS,
|
|
55747
|
+
agentConfigOverrides: {
|
|
55748
|
+
title: "Quest metadata generator",
|
|
55749
|
+
internal: true
|
|
55750
|
+
}
|
|
55751
|
+
});
|
|
55752
|
+
} catch (error) {
|
|
55753
|
+
if (error instanceof StructuredAgentResponseError || error instanceof StructuredAgentFallbackError) {
|
|
55754
|
+
options.logger.warn(
|
|
55755
|
+
{ err: error, questId: options.questId },
|
|
55756
|
+
"Structured quest metadata generation failed"
|
|
55757
|
+
);
|
|
55758
|
+
return;
|
|
55759
|
+
}
|
|
55760
|
+
options.logger.error(
|
|
55761
|
+
{ err: error, questId: options.questId },
|
|
55762
|
+
"Quest metadata generation failed"
|
|
55763
|
+
);
|
|
55764
|
+
return;
|
|
55765
|
+
}
|
|
55766
|
+
if (typeof result.name !== "string") {
|
|
55767
|
+
return;
|
|
55768
|
+
}
|
|
55769
|
+
const normalized = normalizeAutoName(result.name);
|
|
55770
|
+
if (!normalized) {
|
|
55771
|
+
return;
|
|
55772
|
+
}
|
|
55773
|
+
try {
|
|
55774
|
+
await options.questService.setName(options.questId, normalized);
|
|
55775
|
+
} catch (error) {
|
|
55776
|
+
options.logger.warn(
|
|
55777
|
+
{ err: error, questId: options.questId },
|
|
55778
|
+
"Failed to apply generated quest name"
|
|
55779
|
+
);
|
|
55780
|
+
}
|
|
55781
|
+
}
|
|
55782
|
+
function scheduleQuestMetadataGeneration(options) {
|
|
55783
|
+
queueMicrotask(() => {
|
|
55784
|
+
void generateAndApplyQuestMetadata(options).catch((error) => {
|
|
55785
|
+
options.logger.error(
|
|
55786
|
+
{ err: error, questId: options.questId },
|
|
55787
|
+
"Quest metadata generation crashed"
|
|
55788
|
+
);
|
|
55789
|
+
});
|
|
55790
|
+
});
|
|
55791
|
+
}
|
|
55792
|
+
|
|
55583
55793
|
// ../server/src/server/quest/service.ts
|
|
55584
55794
|
var QUEST_ID_LENGTH = 8;
|
|
55585
55795
|
var DEFAULT_PROVIDER = "claude";
|
|
@@ -55748,6 +55958,7 @@ var QuestService = class {
|
|
|
55748
55958
|
rules,
|
|
55749
55959
|
cwd: options.cwd,
|
|
55750
55960
|
prompt,
|
|
55961
|
+
name: "",
|
|
55751
55962
|
defaultProvider: options.defaultProvider ?? DEFAULT_PROVIDER,
|
|
55752
55963
|
defaultModel: options.defaultModel ?? null,
|
|
55753
55964
|
termination: options.termination,
|
|
@@ -55762,6 +55973,13 @@ var QuestService = class {
|
|
|
55762
55973
|
});
|
|
55763
55974
|
await this.store.upsert(record);
|
|
55764
55975
|
this.publish(record);
|
|
55976
|
+
scheduleQuestMetadataGeneration({
|
|
55977
|
+
questService: this,
|
|
55978
|
+
questId: record.id,
|
|
55979
|
+
cwd: record.cwd,
|
|
55980
|
+
prompt,
|
|
55981
|
+
logger: this.logger
|
|
55982
|
+
});
|
|
55765
55983
|
const runner = this.runners[options.kind];
|
|
55766
55984
|
if (!runner) {
|
|
55767
55985
|
const failed = QuestRecordSchema.parse({
|
|
@@ -55896,6 +56114,17 @@ var QuestService = class {
|
|
|
55896
56114
|
}
|
|
55897
56115
|
return this.persistAndPublish(record, { hivemindPath });
|
|
55898
56116
|
}
|
|
56117
|
+
/** Set the auto-generated quest name. No-op if the name hasn't actually changed. */
|
|
56118
|
+
async setName(questId, name) {
|
|
56119
|
+
const record = this.store.get(questId);
|
|
56120
|
+
if (!record) {
|
|
56121
|
+
throw new Error(`Quest "${questId}" not found`);
|
|
56122
|
+
}
|
|
56123
|
+
if (record.name === name) {
|
|
56124
|
+
return record;
|
|
56125
|
+
}
|
|
56126
|
+
return this.persistAndPublish(record, { name });
|
|
56127
|
+
}
|
|
55899
56128
|
/** Mark the quest's terminal status. Idempotent — running→stopped is fine. */
|
|
55900
56129
|
async markStatus(questId, status) {
|
|
55901
56130
|
const record = this.store.get(questId);
|
|
@@ -56176,257 +56405,329 @@ ${text || "(no output)"}`).join("\n\n");
|
|
|
56176
56405
|
}
|
|
56177
56406
|
|
|
56178
56407
|
// ../server/src/server/quest/runner-orchestrator.ts
|
|
56179
|
-
import {
|
|
56408
|
+
import { access, readFile as readFile5 } from "node:fs/promises";
|
|
56180
56409
|
import path26 from "node:path";
|
|
56181
|
-
|
|
56182
|
-
|
|
56183
|
-
|
|
56184
|
-
|
|
56185
|
-
|
|
56186
|
-
|
|
56187
|
-
|
|
56188
|
-
|
|
56189
|
-
|
|
56190
|
-
|
|
56191
|
-
|
|
56192
|
-
|
|
56193
|
-
|
|
56194
|
-
|
|
56195
|
-
|
|
56196
|
-
|
|
56197
|
-
|
|
56198
|
-
|
|
56199
|
-
|
|
56200
|
-
|
|
56201
|
-
|
|
56202
|
-
|
|
56203
|
-
}
|
|
56204
|
-
|
|
56205
|
-
|
|
56206
|
-
|
|
56207
|
-
|
|
56208
|
-
|
|
56209
|
-
|
|
56210
|
-
|
|
56211
|
-
|
|
56212
|
-
"
|
|
56213
|
-
|
|
56214
|
-
|
|
56215
|
-
|
|
56216
|
-
|
|
56217
|
-
|
|
56218
|
-
|
|
56219
|
-
|
|
56220
|
-
|
|
56221
|
-
|
|
56222
|
-
|
|
56223
|
-
|
|
56224
|
-
|
|
56225
|
-
"",
|
|
56226
|
-
"Each role on the roster runs AT MOST ONCE \u2014 do not schedule the same",
|
|
56227
|
-
"role twice anywhere in the plan. You may also OMIT roles that are not",
|
|
56228
|
-
"needed for the user's task.",
|
|
56229
|
-
"",
|
|
56230
|
-
"## Your team",
|
|
56231
|
-
"",
|
|
56232
|
-
"Read every role description carefully so you understand what each worker",
|
|
56233
|
-
"can and cannot do, and what inputs they need.",
|
|
56234
|
-
"",
|
|
56235
|
-
roleSections,
|
|
56236
|
-
"",
|
|
56237
|
-
"## Hivemind doc",
|
|
56238
|
-
"",
|
|
56239
|
-
`The hivemind doc lives at: ${args.hivemindPath}`,
|
|
56240
|
-
"",
|
|
56241
|
-
"After every wave the runner appends each worker's final output to that",
|
|
56242
|
-
"doc, and feeds the hivemind contents inline into the prompts of any",
|
|
56243
|
-
"workers in later waves so they can build on prior results. You do not",
|
|
56244
|
-
"need to think about this \u2014 just write self-contained dispatch prompts.",
|
|
56245
|
-
"",
|
|
56246
|
-
"## Response contract \u2014 STRICT",
|
|
56247
|
-
"",
|
|
56248
|
-
"Respond with EXACTLY one fenced JSON code block and nothing else. No",
|
|
56249
|
-
"prose before or after. The JSON must match this shape:",
|
|
56250
|
-
"",
|
|
56251
|
-
"```json",
|
|
56252
|
-
"{",
|
|
56253
|
-
' "plan": [',
|
|
56254
|
-
' { "dispatches": [',
|
|
56255
|
-
' { "role": "<roleName>", "prompt": "<concrete sub-task>" }',
|
|
56256
|
-
" ]}",
|
|
56257
|
-
" ]",
|
|
56258
|
-
"}",
|
|
56259
|
-
"```",
|
|
56260
|
-
"",
|
|
56261
|
-
"Rules:",
|
|
56262
|
-
"- `role` must match one of the role names listed above EXACTLY.",
|
|
56263
|
-
"- A role appears AT MOST ONCE across the entire plan.",
|
|
56264
|
-
"- `prompt` is a precise, self-contained task for the worker. Cite any",
|
|
56265
|
-
" expected upstream inputs by role name (the runner injects the hivemind",
|
|
56266
|
-
" contents automatically).",
|
|
56267
|
-
"- The plan ends when its last wave finishes. There is no `done` action."
|
|
56268
|
-
].join("\n");
|
|
56269
|
-
}
|
|
56270
|
-
function extractFencedJson(text) {
|
|
56271
|
-
const fence = /```(?:json)?\s*([\s\S]*?)```/i.exec(text);
|
|
56272
|
-
if (fence?.[1]) return fence[1].trim();
|
|
56273
|
-
const brace = /\{[\s\S]*\}/.exec(text);
|
|
56274
|
-
return brace ? brace[0].trim() : null;
|
|
56275
|
-
}
|
|
56276
|
-
function parseQueenPlan(text, knownRoles) {
|
|
56277
|
-
const block = extractFencedJson(text);
|
|
56278
|
-
if (!block) {
|
|
56279
|
-
return { ok: false, reason: "no JSON block found in queen response" };
|
|
56280
|
-
}
|
|
56281
|
-
let parsed;
|
|
56282
|
-
try {
|
|
56283
|
-
parsed = JSON.parse(block);
|
|
56284
|
-
} catch (err) {
|
|
56285
|
-
return {
|
|
56286
|
-
ok: false,
|
|
56287
|
-
reason: `JSON parse error: ${err instanceof Error ? err.message : String(err)}`
|
|
56288
|
-
};
|
|
56289
|
-
}
|
|
56290
|
-
if (!parsed || typeof parsed !== "object") {
|
|
56291
|
-
return { ok: false, reason: "queen response is not an object" };
|
|
56292
|
-
}
|
|
56293
|
-
const planRaw = parsed.plan;
|
|
56294
|
-
if (!Array.isArray(planRaw) || planRaw.length === 0) {
|
|
56295
|
-
return { ok: false, reason: "plan must be a non-empty array of waves" };
|
|
56296
|
-
}
|
|
56297
|
-
const seenRoles = /* @__PURE__ */ new Set();
|
|
56298
|
-
const waves = [];
|
|
56299
|
-
for (let i = 0; i < planRaw.length; i += 1) {
|
|
56300
|
-
const waveRaw = planRaw[i];
|
|
56301
|
-
if (!waveRaw || typeof waveRaw !== "object") {
|
|
56302
|
-
return { ok: false, reason: `wave ${i + 1} is not an object` };
|
|
56303
|
-
}
|
|
56304
|
-
const dispatchesRaw = waveRaw.dispatches;
|
|
56305
|
-
if (!Array.isArray(dispatchesRaw) || dispatchesRaw.length === 0) {
|
|
56306
|
-
return { ok: false, reason: `wave ${i + 1} has no dispatches` };
|
|
56307
|
-
}
|
|
56308
|
-
const dispatches = [];
|
|
56309
|
-
for (const d of dispatchesRaw) {
|
|
56310
|
-
if (!d || typeof d !== "object" || typeof d.role !== "string" || typeof d.prompt !== "string") {
|
|
56410
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
56411
|
+
|
|
56412
|
+
// ../server/src/server/quest/orchestrator-mcp.ts
|
|
56413
|
+
import { createSdkMcpServer, tool } from "@anthropic-ai/claude-agent-sdk";
|
|
56414
|
+
import { z as z42 } from "zod";
|
|
56415
|
+
var HANDOFF_INPUT_SHAPE = {
|
|
56416
|
+
rolePath: z42.string().min(1).describe(
|
|
56417
|
+
"Absolute filesystem path to the role .md file the worker should adopt as its system prompt. Must be one of the role file paths listed in your team roster \u2014 do not invent paths."
|
|
56418
|
+
),
|
|
56419
|
+
task: z42.string().min(1).describe(
|
|
56420
|
+
"Concrete, self-contained instruction for the worker. The worker has its own toolbelt and reads the role at rolePath as its system prompt \u2014 write the task as a normal user message describing what to do, what inputs to read, and where to write outputs. Reference the hivemind doc by absolute path if relevant."
|
|
56421
|
+
)
|
|
56422
|
+
};
|
|
56423
|
+
function buildOrchestratorMcpServer(options) {
|
|
56424
|
+
const {
|
|
56425
|
+
questId,
|
|
56426
|
+
agentManager,
|
|
56427
|
+
resolveWorkerConfig,
|
|
56428
|
+
onHandoffSpawned,
|
|
56429
|
+
onHandoffCompleted,
|
|
56430
|
+
logger,
|
|
56431
|
+
signal
|
|
56432
|
+
} = options;
|
|
56433
|
+
const handoffTool = tool(
|
|
56434
|
+
"handoff",
|
|
56435
|
+
[
|
|
56436
|
+
"Spawn a fresh full Appostle agent session, give it a role and a task, wait for it to finish, and return its final text.",
|
|
56437
|
+
"",
|
|
56438
|
+
"Use this tool \u2014 and ONLY this tool \u2014 to delegate work. Do not attempt to do the work yourself, do not shell out, do not invoke ephemeral subagents.",
|
|
56439
|
+
"",
|
|
56440
|
+
"Each call spawns one independent session with its own clean context (the whole point: it saves YOUR context budget). To run multiple workers in parallel, emit multiple handoff tool calls in the same response \u2014 they will execute concurrently and you will see all the results in the next turn. To run sequentially, wait for one call's result before issuing the next."
|
|
56441
|
+
].join("\n"),
|
|
56442
|
+
HANDOFF_INPUT_SHAPE,
|
|
56443
|
+
async (args) => {
|
|
56444
|
+
const log = logger.child({ tool: "appostle.handoff", questId });
|
|
56445
|
+
log.info({ rolePath: args.rolePath }, "handoff: spawning worker");
|
|
56446
|
+
let workerConfig;
|
|
56447
|
+
try {
|
|
56448
|
+
workerConfig = await resolveWorkerConfig({
|
|
56449
|
+
rolePath: args.rolePath,
|
|
56450
|
+
taskTitle: deriveTaskTitle(args.task)
|
|
56451
|
+
});
|
|
56452
|
+
} catch (err) {
|
|
56453
|
+
log.warn({ err }, "handoff: failed to resolve worker config");
|
|
56311
56454
|
return {
|
|
56312
|
-
|
|
56313
|
-
|
|
56455
|
+
isError: true,
|
|
56456
|
+
content: [
|
|
56457
|
+
{
|
|
56458
|
+
type: "text",
|
|
56459
|
+
text: `handoff failed: could not resolve role at "${args.rolePath}" \u2014 ${err instanceof Error ? err.message : String(err)}`
|
|
56460
|
+
}
|
|
56461
|
+
]
|
|
56314
56462
|
};
|
|
56315
56463
|
}
|
|
56316
|
-
|
|
56317
|
-
|
|
56318
|
-
|
|
56319
|
-
|
|
56464
|
+
let workerAgentId;
|
|
56465
|
+
try {
|
|
56466
|
+
const created = await agentManager.createAgent(workerConfig);
|
|
56467
|
+
workerAgentId = created.id;
|
|
56468
|
+
} catch (err) {
|
|
56469
|
+
log.error({ err }, "handoff: createAgent failed");
|
|
56470
|
+
return {
|
|
56471
|
+
isError: true,
|
|
56472
|
+
content: [
|
|
56473
|
+
{
|
|
56474
|
+
type: "text",
|
|
56475
|
+
text: `handoff failed: could not spawn worker \u2014 ${err instanceof Error ? err.message : String(err)}`
|
|
56476
|
+
}
|
|
56477
|
+
]
|
|
56478
|
+
};
|
|
56320
56479
|
}
|
|
56321
|
-
|
|
56480
|
+
onHandoffSpawned?.({
|
|
56481
|
+
rolePath: args.rolePath,
|
|
56482
|
+
task: args.task,
|
|
56483
|
+
workerAgentId
|
|
56484
|
+
});
|
|
56485
|
+
const onAbort = () => {
|
|
56486
|
+
void agentManager.cancelAgentRun(workerAgentId).catch(() => {
|
|
56487
|
+
});
|
|
56488
|
+
};
|
|
56489
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
56490
|
+
try {
|
|
56491
|
+
const run = await agentManager.runAgent(workerAgentId, args.task);
|
|
56492
|
+
const finalText = run.finalText.trim();
|
|
56493
|
+
if (run.canceled) {
|
|
56494
|
+
onHandoffCompleted?.({
|
|
56495
|
+
rolePath: args.rolePath,
|
|
56496
|
+
workerAgentId,
|
|
56497
|
+
status: "stopped",
|
|
56498
|
+
errorMessage: null
|
|
56499
|
+
});
|
|
56500
|
+
return {
|
|
56501
|
+
content: [
|
|
56502
|
+
{
|
|
56503
|
+
type: "text",
|
|
56504
|
+
text: `Worker ${workerAgentId} was canceled before completing.
|
|
56505
|
+
|
|
56506
|
+
Partial output (may be empty):
|
|
56507
|
+
|
|
56508
|
+
${finalText || "(no output)"}`
|
|
56509
|
+
}
|
|
56510
|
+
]
|
|
56511
|
+
};
|
|
56512
|
+
}
|
|
56513
|
+
onHandoffCompleted?.({
|
|
56514
|
+
rolePath: args.rolePath,
|
|
56515
|
+
workerAgentId,
|
|
56516
|
+
status: "succeeded",
|
|
56517
|
+
errorMessage: null
|
|
56518
|
+
});
|
|
56322
56519
|
return {
|
|
56323
|
-
|
|
56324
|
-
|
|
56520
|
+
content: [
|
|
56521
|
+
{
|
|
56522
|
+
type: "text",
|
|
56523
|
+
text: finalText ? `Worker ${workerAgentId} finished. Final output:
|
|
56524
|
+
|
|
56525
|
+
${finalText}` : `Worker ${workerAgentId} finished but produced no final assistant text. Inspect the session if you need the full timeline.`
|
|
56526
|
+
}
|
|
56527
|
+
]
|
|
56325
56528
|
};
|
|
56529
|
+
} catch (err) {
|
|
56530
|
+
const errorMessage2 = err instanceof Error ? err.message : String(err);
|
|
56531
|
+
log.error({ err, workerAgentId }, "handoff: runAgent threw");
|
|
56532
|
+
onHandoffCompleted?.({
|
|
56533
|
+
rolePath: args.rolePath,
|
|
56534
|
+
workerAgentId,
|
|
56535
|
+
status: "failed",
|
|
56536
|
+
errorMessage: errorMessage2
|
|
56537
|
+
});
|
|
56538
|
+
return {
|
|
56539
|
+
isError: true,
|
|
56540
|
+
content: [
|
|
56541
|
+
{
|
|
56542
|
+
type: "text",
|
|
56543
|
+
text: `handoff failed mid-run for worker ${workerAgentId}: ${errorMessage2}`
|
|
56544
|
+
}
|
|
56545
|
+
]
|
|
56546
|
+
};
|
|
56547
|
+
} finally {
|
|
56548
|
+
signal.removeEventListener("abort", onAbort);
|
|
56326
56549
|
}
|
|
56327
|
-
seenRoles.add(role);
|
|
56328
|
-
dispatches.push({ role, prompt });
|
|
56329
56550
|
}
|
|
56330
|
-
|
|
56331
|
-
|
|
56332
|
-
|
|
56551
|
+
);
|
|
56552
|
+
const sdkInstance = createSdkMcpServer({
|
|
56553
|
+
name: "appostle",
|
|
56554
|
+
version: "0.1.0",
|
|
56555
|
+
tools: [handoffTool],
|
|
56556
|
+
// Always load — we never want the queen to have to "discover" the handoff
|
|
56557
|
+
// tool. Without this it could be deferred behind tool search.
|
|
56558
|
+
alwaysLoad: true
|
|
56559
|
+
});
|
|
56560
|
+
return {
|
|
56561
|
+
type: "sdk",
|
|
56562
|
+
name: "appostle",
|
|
56563
|
+
config: sdkInstance
|
|
56564
|
+
};
|
|
56333
56565
|
}
|
|
56334
|
-
function
|
|
56335
|
-
|
|
56336
|
-
|
|
56566
|
+
function deriveTaskTitle(task) {
|
|
56567
|
+
const firstLine = task.split("\n")[0]?.trim() ?? "";
|
|
56568
|
+
if (firstLine.length === 0) return "handoff";
|
|
56569
|
+
return firstLine.length > 80 ? `${firstLine.slice(0, 77)}\u2026` : firstLine;
|
|
56570
|
+
}
|
|
56571
|
+
|
|
56572
|
+
// ../server/src/server/quest/runner-orchestrator.ts
|
|
56573
|
+
var QUEEN_ALLOWED_TOOLS = ["Read", "Glob", "Grep", "Write", "mcp__appostle__handoff"];
|
|
56574
|
+
function nowIso4() {
|
|
56575
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
56576
|
+
}
|
|
56577
|
+
async function resolveTeam(record) {
|
|
56578
|
+
let roleIndex = [];
|
|
56579
|
+
try {
|
|
56580
|
+
roleIndex = await listRoles({ workspaceRoot: record.cwd });
|
|
56581
|
+
} catch {
|
|
56337
56582
|
}
|
|
56338
|
-
|
|
56339
|
-
|
|
56340
|
-
|
|
56341
|
-
const text = item.text;
|
|
56342
|
-
if (text.trim()) {
|
|
56343
|
-
return text;
|
|
56344
|
-
}
|
|
56583
|
+
return record.cards.map((card) => {
|
|
56584
|
+
if (!card.role) {
|
|
56585
|
+
return { cardIndex: card.index, name: null, path: null, description: "" };
|
|
56345
56586
|
}
|
|
56587
|
+
const match = roleIndex.find((r) => r.name === card.role);
|
|
56588
|
+
return {
|
|
56589
|
+
cardIndex: card.index,
|
|
56590
|
+
name: card.role,
|
|
56591
|
+
path: match?.path ?? null,
|
|
56592
|
+
description: match?.description ?? ""
|
|
56593
|
+
};
|
|
56594
|
+
});
|
|
56595
|
+
}
|
|
56596
|
+
var FALLBACK_QUEEN_PROMPT_TEMPLATE = [
|
|
56597
|
+
"you are going to send out agents, but first you get aquanted with 'the team'",
|
|
56598
|
+
"You don't go do the work yourself!",
|
|
56599
|
+
"",
|
|
56600
|
+
"this team has very deep knowledge of the task at hand. they have very detailed skills and output prefs so they are smarter than you. don't assume otherwise.",
|
|
56601
|
+
"",
|
|
56602
|
+
"the team is split up in categories. you will try to consolidate all of their knowledge into an approach for the task below.",
|
|
56603
|
+
"",
|
|
56604
|
+
"",
|
|
56605
|
+
"The team:",
|
|
56606
|
+
"",
|
|
56607
|
+
"{{team}}",
|
|
56608
|
+
"",
|
|
56609
|
+
"===",
|
|
56610
|
+
"",
|
|
56611
|
+
"The task at hand:",
|
|
56612
|
+
"",
|
|
56613
|
+
"",
|
|
56614
|
+
">>>>>",
|
|
56615
|
+
"{{task}}",
|
|
56616
|
+
"<<<<<<<<<<",
|
|
56617
|
+
"",
|
|
56618
|
+
"Again, whatever you are thinking of now I repeat: You don't go do the work yourself!",
|
|
56619
|
+
"What your job IS tho:",
|
|
56620
|
+
"",
|
|
56621
|
+
"Now you have full knowledge of what the user wants and how he composed his 'team'",
|
|
56622
|
+
"you do not delegate this team as subagents but you spawn new handoff sessions, you decide on what can be done in paralel and what needs to happen before the next can start. stage. the amount of handof sessions you spawn is equal to the amount of team entries in your prompt.",
|
|
56623
|
+
"",
|
|
56624
|
+
"",
|
|
56625
|
+
"",
|
|
56626
|
+
"We are going to use a hivemind dock in the repo.",
|
|
56627
|
+
"What you can do, is now startup that hivemind doc in /.hivemind/orchestrator-{{questId}}.md",
|
|
56628
|
+
"",
|
|
56629
|
+
"",
|
|
56630
|
+
"You instruct every handoff with a clear instruction on it's task.",
|
|
56631
|
+
"But you also make sure that each handoff knows the greater context of the end goal and knows of the existence of the hivemind document.",
|
|
56632
|
+
"it should know that it can write to it but first needs to check if there are no conflicts.",
|
|
56633
|
+
"",
|
|
56634
|
+
"",
|
|
56635
|
+
"do not use subagents because these are emphetic. we need full sessions."
|
|
56636
|
+
].join("\n");
|
|
56637
|
+
var QUEEN_PROMPT_FILENAME = "queen-prompt.md";
|
|
56638
|
+
var QUEEN_PROMPT_MAX_LOOKUP_LEVELS = 10;
|
|
56639
|
+
async function findQueenPromptOnDisk() {
|
|
56640
|
+
const start = path26.dirname(fileURLToPath3(import.meta.url));
|
|
56641
|
+
let cursor = start;
|
|
56642
|
+
for (let i = 0; i < QUEEN_PROMPT_MAX_LOOKUP_LEVELS; i += 1) {
|
|
56643
|
+
const candidate = path26.join(cursor, QUEEN_PROMPT_FILENAME);
|
|
56644
|
+
try {
|
|
56645
|
+
await access(candidate);
|
|
56646
|
+
return candidate;
|
|
56647
|
+
} catch {
|
|
56648
|
+
}
|
|
56649
|
+
const parent = path26.dirname(cursor);
|
|
56650
|
+
if (parent === cursor) break;
|
|
56651
|
+
cursor = parent;
|
|
56346
56652
|
}
|
|
56347
|
-
return
|
|
56653
|
+
return null;
|
|
56348
56654
|
}
|
|
56349
|
-
function
|
|
56350
|
-
const
|
|
56351
|
-
|
|
56352
|
-
|
|
56353
|
-
|
|
56354
|
-
|
|
56655
|
+
async function loadQueenPromptTemplate(logger) {
|
|
56656
|
+
const onDisk = await findQueenPromptOnDisk();
|
|
56657
|
+
if (onDisk) {
|
|
56658
|
+
try {
|
|
56659
|
+
const template = await readFile5(onDisk, "utf8");
|
|
56660
|
+
return { template, source: onDisk };
|
|
56661
|
+
} catch (err) {
|
|
56662
|
+
logger.warn(
|
|
56663
|
+
{ err, path: onDisk },
|
|
56664
|
+
"orchestrator: queen prompt found on disk but could not be read; using embedded fallback"
|
|
56665
|
+
);
|
|
56666
|
+
}
|
|
56667
|
+
} else {
|
|
56668
|
+
logger.warn("orchestrator: queen prompt not found on disk; using embedded fallback");
|
|
56669
|
+
}
|
|
56670
|
+
return { template: FALLBACK_QUEEN_PROMPT_TEMPLATE, source: "<embedded fallback>" };
|
|
56355
56671
|
}
|
|
56356
|
-
|
|
56357
|
-
|
|
56358
|
-
|
|
56359
|
-
|
|
56360
|
-
|
|
56361
|
-
|
|
56362
|
-
|
|
56363
|
-
|
|
56364
|
-
"",
|
|
56365
|
-
`- createdAt: ${args.record.createdAt}`,
|
|
56366
|
-
`- prompt: ${args.record.prompt}`,
|
|
56367
|
-
"",
|
|
56368
|
-
"## Roster",
|
|
56369
|
-
"",
|
|
56370
|
-
roster,
|
|
56371
|
-
"",
|
|
56372
|
-
"## Plan",
|
|
56373
|
-
"",
|
|
56374
|
-
planSummary,
|
|
56375
|
-
"",
|
|
56376
|
-
"## Waves",
|
|
56377
|
-
""
|
|
56378
|
-
].join("\n");
|
|
56379
|
-
await writeFile7(args.paths.filePath, body, "utf8");
|
|
56672
|
+
function renderTeamLines(team) {
|
|
56673
|
+
return team.map((m, i) => {
|
|
56674
|
+
const idx = i + 1;
|
|
56675
|
+
if (!m.name) return `[value ${idx}, (no role assigned to this card)]`;
|
|
56676
|
+
const filePath = m.path ?? "(role file not resolved on disk)";
|
|
56677
|
+
const desc = m.description ? ` \u2014 ${m.description}` : "";
|
|
56678
|
+
return `[value ${idx}, ${m.name}${desc}, ${filePath}]`;
|
|
56679
|
+
}).join("\n\n");
|
|
56380
56680
|
}
|
|
56381
|
-
|
|
56382
|
-
|
|
56681
|
+
function buildMetaPrompt(args) {
|
|
56682
|
+
return args.template.split("{{team}}").join(renderTeamLines(args.team)).split("{{task}}").join(args.userPrompt).split("{{questId}}").join(args.questId);
|
|
56383
56683
|
}
|
|
56384
|
-
async function
|
|
56684
|
+
async function resolveWorkerConfigForHandoff(args) {
|
|
56685
|
+
const { record, rolePath, taskTitle } = args;
|
|
56686
|
+
const modeId = getProviderFullAccessModeId(record.defaultProvider) ?? void 0;
|
|
56687
|
+
const baseConfig = {
|
|
56688
|
+
provider: record.defaultProvider,
|
|
56689
|
+
cwd: record.cwd,
|
|
56690
|
+
model: record.defaultModel ?? void 0,
|
|
56691
|
+
modeId,
|
|
56692
|
+
title: `quest ${record.id} handoff: ${taskTitle}`,
|
|
56693
|
+
internal: true
|
|
56694
|
+
};
|
|
56695
|
+
const roleName = path26.basename(rolePath, path26.extname(rolePath));
|
|
56385
56696
|
try {
|
|
56386
|
-
|
|
56697
|
+
const resolved = await resolveRole({
|
|
56698
|
+
roleName,
|
|
56699
|
+
workspaceRoot: record.cwd
|
|
56700
|
+
});
|
|
56701
|
+
if (resolved) {
|
|
56702
|
+
const config = { ...baseConfig };
|
|
56703
|
+
if (resolved.body) config.systemPrompt = resolved.body;
|
|
56704
|
+
if (resolved.allowedTools.length > 0) config.allowedTools = resolved.allowedTools;
|
|
56705
|
+
return config;
|
|
56706
|
+
}
|
|
56387
56707
|
} catch {
|
|
56388
|
-
return "";
|
|
56389
56708
|
}
|
|
56709
|
+
return baseConfig;
|
|
56390
56710
|
}
|
|
56391
56711
|
function buildQueenAgentConfig(args) {
|
|
56392
56712
|
const modeId = getProviderFullAccessModeId(args.record.defaultProvider) ?? void 0;
|
|
56393
56713
|
return {
|
|
56394
56714
|
provider: args.record.defaultProvider,
|
|
56395
56715
|
cwd: args.record.cwd,
|
|
56716
|
+
// Queen runs on whatever model the user picked at the quest level. We
|
|
56717
|
+
// don't hard-code a model here — observe behaviour across model choices.
|
|
56396
56718
|
model: args.record.defaultModel ?? void 0,
|
|
56397
56719
|
modeId,
|
|
56398
|
-
|
|
56720
|
+
// No system prompt overlay. The queen's instruction is the first user
|
|
56721
|
+
// message (the meta-prompt). She gets a vanilla agent identity from the
|
|
56722
|
+
// provider preset.
|
|
56723
|
+
allowedTools: QUEEN_ALLOWED_TOOLS,
|
|
56724
|
+
// In-process MCP server with the `handoff` tool. Registered under the
|
|
56725
|
+
// name `appostle`, so the queen sees `mcp__appostle__handoff`.
|
|
56726
|
+
mcpServers: { appostle: args.mcpServer },
|
|
56399
56727
|
title: `quest ${args.record.id} queen`,
|
|
56400
56728
|
internal: true
|
|
56401
56729
|
};
|
|
56402
56730
|
}
|
|
56403
|
-
function buildWorkerAgentConfig(args) {
|
|
56404
|
-
const modeId = getProviderFullAccessModeId(args.record.defaultProvider) ?? void 0;
|
|
56405
|
-
return {
|
|
56406
|
-
provider: args.record.defaultProvider,
|
|
56407
|
-
cwd: args.record.cwd,
|
|
56408
|
-
model: args.card.model ?? args.record.defaultModel ?? void 0,
|
|
56409
|
-
modeId,
|
|
56410
|
-
systemPrompt: args.roleBody ?? void 0,
|
|
56411
|
-
allowedTools: args.allowedTools.length > 0 ? args.allowedTools : void 0,
|
|
56412
|
-
title: `quest ${args.record.id} card ${args.card.index} ${args.card.role ?? ""}`.trim(),
|
|
56413
|
-
internal: true
|
|
56414
|
-
};
|
|
56415
|
-
}
|
|
56416
|
-
function buildWorkerPrompt(args) {
|
|
56417
|
-
if (args.isFirstWave) {
|
|
56418
|
-
return args.dispatchPrompt;
|
|
56419
|
-
}
|
|
56420
|
-
return [
|
|
56421
|
-
args.dispatchPrompt,
|
|
56422
|
-
"",
|
|
56423
|
-
"---",
|
|
56424
|
-
"",
|
|
56425
|
-
"## Hivemind context (outputs from earlier waves)",
|
|
56426
|
-
"",
|
|
56427
|
-
args.hivemindMarkdown
|
|
56428
|
-
].join("\n");
|
|
56429
|
-
}
|
|
56430
56731
|
var OrchestratorRunner = class {
|
|
56431
56732
|
constructor(options) {
|
|
56432
56733
|
this.logger = options.logger.child({ module: "quest-runner-orchestrator" });
|
|
@@ -56447,23 +56748,66 @@ var OrchestratorRunner = class {
|
|
|
56447
56748
|
await service.markStatus(questId, "failed");
|
|
56448
56749
|
throw new Error("Orchestrate quest requires at least one role card");
|
|
56449
56750
|
}
|
|
56450
|
-
const
|
|
56451
|
-
const
|
|
56452
|
-
|
|
56453
|
-
const
|
|
56454
|
-
|
|
56455
|
-
|
|
56456
|
-
|
|
56751
|
+
const team = await resolveTeam(initial);
|
|
56752
|
+
const { template, source: templateSource } = await loadQueenPromptTemplate(this.logger);
|
|
56753
|
+
this.logger.info({ questId, templateSource }, "orchestrator: queen prompt loaded");
|
|
56754
|
+
const metaPrompt = buildMetaPrompt({
|
|
56755
|
+
template,
|
|
56756
|
+
team,
|
|
56757
|
+
userPrompt: initial.prompt,
|
|
56758
|
+
questId: initial.id
|
|
56759
|
+
});
|
|
56760
|
+
const cardIndexByRolePath = /* @__PURE__ */ new Map();
|
|
56761
|
+
for (const member of team) {
|
|
56762
|
+
if (member.path) cardIndexByRolePath.set(member.path, member.cardIndex);
|
|
56763
|
+
}
|
|
56764
|
+
const mcpServer = buildOrchestratorMcpServer({
|
|
56765
|
+
questId: initial.id,
|
|
56766
|
+
agentManager: service.agentManager,
|
|
56767
|
+
logger: this.logger,
|
|
56768
|
+
signal,
|
|
56769
|
+
resolveWorkerConfig: ({ rolePath, taskTitle }) => resolveWorkerConfigForHandoff({
|
|
56770
|
+
record: initial,
|
|
56771
|
+
rolePath,
|
|
56772
|
+
taskTitle
|
|
56773
|
+
}),
|
|
56774
|
+
onHandoffSpawned: ({ rolePath, workerAgentId }) => {
|
|
56775
|
+
const cardIndex = cardIndexByRolePath.get(rolePath);
|
|
56776
|
+
if (cardIndex === void 0) {
|
|
56777
|
+
this.logger.warn(
|
|
56778
|
+
{ rolePath, questId: initial.id },
|
|
56779
|
+
"orchestrator: handoff for unknown rolePath \u2014 no card to attach"
|
|
56780
|
+
);
|
|
56781
|
+
return;
|
|
56782
|
+
}
|
|
56783
|
+
void service.updateCard(initial.id, cardIndex, {
|
|
56784
|
+
status: "running",
|
|
56785
|
+
agentId: workerAgentId,
|
|
56786
|
+
startedAt: nowIso4()
|
|
56787
|
+
}).catch(
|
|
56788
|
+
(err) => this.logger.warn(
|
|
56789
|
+
{ err, cardIndex },
|
|
56790
|
+
"orchestrator: failed to stamp card with worker agentId"
|
|
56791
|
+
)
|
|
56792
|
+
);
|
|
56793
|
+
},
|
|
56794
|
+
onHandoffCompleted: ({ rolePath, status: workerStatus }) => {
|
|
56795
|
+
const cardIndex = cardIndexByRolePath.get(rolePath);
|
|
56796
|
+
if (cardIndex === void 0) return;
|
|
56797
|
+
void service.updateCard(initial.id, cardIndex, {
|
|
56798
|
+
status: workerStatus,
|
|
56799
|
+
completedAt: nowIso4()
|
|
56800
|
+
}).catch(
|
|
56801
|
+
(err) => this.logger.warn(
|
|
56802
|
+
{ err, cardIndex, workerStatus },
|
|
56803
|
+
"orchestrator: failed to stamp card with worker completion"
|
|
56804
|
+
)
|
|
56805
|
+
);
|
|
56457
56806
|
}
|
|
56458
|
-
}
|
|
56459
|
-
await service.setHivemindPath(questId, hivemindPaths.filePath);
|
|
56460
|
-
const queenSystemPrompt = buildQueenSystemPrompt({
|
|
56461
|
-
roles: resolvedRoles,
|
|
56462
|
-
hivemindPath: hivemindPaths.filePath
|
|
56463
56807
|
});
|
|
56464
56808
|
const queenStartedAt = nowIso4();
|
|
56465
56809
|
const queenAgent = await service.agentManager.createAgent(
|
|
56466
|
-
buildQueenAgentConfig({ record: initial,
|
|
56810
|
+
buildQueenAgentConfig({ record: initial, mcpServer })
|
|
56467
56811
|
);
|
|
56468
56812
|
const queenCard = QuestCardSchema.parse({
|
|
56469
56813
|
index: 0,
|
|
@@ -56483,233 +56827,50 @@ var OrchestratorRunner = class {
|
|
|
56483
56827
|
});
|
|
56484
56828
|
};
|
|
56485
56829
|
signal.addEventListener("abort", onAbort);
|
|
56486
|
-
let
|
|
56830
|
+
let finalStatus = "succeeded";
|
|
56487
56831
|
try {
|
|
56488
|
-
const queenRun = await service.agentManager.runAgent(queenAgent.id,
|
|
56832
|
+
const queenRun = await service.agentManager.runAgent(queenAgent.id, metaPrompt);
|
|
56489
56833
|
if (queenRun.canceled || signal.aborted) {
|
|
56490
56834
|
await service.updateQueenCard(questId, {
|
|
56491
56835
|
status: "stopped",
|
|
56492
56836
|
completedAt: nowIso4()
|
|
56493
56837
|
});
|
|
56494
|
-
await this.markRemainingCardsStopped(questId, service);
|
|
56495
56838
|
await service.markStatus(questId, "stopped");
|
|
56496
56839
|
return;
|
|
56497
56840
|
}
|
|
56498
|
-
|
|
56499
|
-
|
|
56500
|
-
|
|
56501
|
-
|
|
56502
|
-
await writeFile7(
|
|
56503
|
-
hivemindPaths.filePath,
|
|
56504
|
-
[
|
|
56505
|
-
`# Orchestrator ${initial.id}`,
|
|
56506
|
-
"",
|
|
56507
|
-
`- createdAt: ${initial.createdAt}`,
|
|
56508
|
-
`- prompt: ${initial.prompt}`,
|
|
56509
|
-
"",
|
|
56510
|
-
"## Plan parse failed",
|
|
56511
|
-
"",
|
|
56512
|
-
`Reason: ${result.reason}`,
|
|
56513
|
-
"",
|
|
56514
|
-
"### Queen response",
|
|
56515
|
-
"",
|
|
56516
|
-
"```",
|
|
56517
|
-
queenText.slice(0, 4e3),
|
|
56518
|
-
"```",
|
|
56519
|
-
""
|
|
56520
|
-
].join("\n"),
|
|
56521
|
-
"utf8"
|
|
56522
|
-
);
|
|
56523
|
-
await service.updateQueenCard(questId, {
|
|
56524
|
-
status: "failed",
|
|
56525
|
-
completedAt: nowIso4()
|
|
56526
|
-
});
|
|
56527
|
-
await this.markRemainingCardsStopped(questId, service);
|
|
56528
|
-
await service.markStatus(questId, "failed");
|
|
56529
|
-
return;
|
|
56530
|
-
}
|
|
56531
|
-
plan = result.plan;
|
|
56841
|
+
await service.updateQueenCard(questId, {
|
|
56842
|
+
status: "succeeded",
|
|
56843
|
+
completedAt: nowIso4()
|
|
56844
|
+
});
|
|
56532
56845
|
} catch (err) {
|
|
56533
56846
|
this.logger.error({ err, questId }, "orchestrator: queen errored");
|
|
56534
56847
|
await service.updateQueenCard(questId, {
|
|
56535
56848
|
status: "failed",
|
|
56536
56849
|
completedAt: nowIso4()
|
|
56537
56850
|
});
|
|
56538
|
-
|
|
56539
|
-
await service.markStatus(questId, "failed");
|
|
56540
|
-
return;
|
|
56851
|
+
finalStatus = "failed";
|
|
56541
56852
|
} finally {
|
|
56542
56853
|
signal.removeEventListener("abort", onAbort);
|
|
56543
56854
|
}
|
|
56544
|
-
await service.updateQueenCard(questId, {
|
|
56545
|
-
status: "succeeded",
|
|
56546
|
-
completedAt: nowIso4()
|
|
56547
|
-
});
|
|
56548
|
-
await initializeHivemind({
|
|
56549
|
-
paths: hivemindPaths,
|
|
56550
|
-
record: initial,
|
|
56551
|
-
roles: resolvedRoles,
|
|
56552
|
-
plan
|
|
56553
|
-
});
|
|
56554
|
-
const scheduledRoles = /* @__PURE__ */ new Set();
|
|
56555
|
-
for (const wave of plan) {
|
|
56556
|
-
for (const d of wave.dispatches) scheduledRoles.add(d.role);
|
|
56557
|
-
}
|
|
56558
|
-
for (const card of initial.cards) {
|
|
56559
|
-
if (card.role && !scheduledRoles.has(card.role) && card.status === "queued") {
|
|
56560
|
-
await service.updateCard(questId, card.index, {
|
|
56561
|
-
status: "stopped",
|
|
56562
|
-
completedAt: nowIso4()
|
|
56563
|
-
});
|
|
56564
|
-
}
|
|
56565
|
-
}
|
|
56566
|
-
let aborted = false;
|
|
56567
|
-
let anyWaveFailed = false;
|
|
56568
|
-
for (let i = 0; i < plan.length; i += 1) {
|
|
56569
|
-
const wave = plan[i];
|
|
56570
|
-
const waveNumber = i + 1;
|
|
56571
|
-
const current = service.getRecord(questId);
|
|
56572
|
-
if (!current || current.status !== "running" || signal.aborted) {
|
|
56573
|
-
aborted = true;
|
|
56574
|
-
break;
|
|
56575
|
-
}
|
|
56576
|
-
const waveHeader = [
|
|
56577
|
-
`
|
|
56578
|
-
### wave ${waveNumber} \u2014 dispatch`,
|
|
56579
|
-
"",
|
|
56580
|
-
...wave.dispatches.map(
|
|
56581
|
-
(d) => `- \`${d.role}\` \u2190 ${d.prompt.replace(/\s+/g, " ").slice(0, 200)}`
|
|
56582
|
-
),
|
|
56583
|
-
""
|
|
56584
|
-
].join("\n");
|
|
56585
|
-
await appendHivemind(hivemindPaths.filePath, waveHeader);
|
|
56586
|
-
const hivemindMarkdown = await readHivemind(hivemindPaths.filePath);
|
|
56587
|
-
const outcomes = await Promise.all(
|
|
56588
|
-
wave.dispatches.map((dispatch) => {
|
|
56589
|
-
const cardIndex = cardIndexByRole.get(dispatch.role);
|
|
56590
|
-
const role = resolvedRoles.find((r) => r.cardIndex === cardIndex);
|
|
56591
|
-
return this.runWorker({
|
|
56592
|
-
record: initial,
|
|
56593
|
-
cardIndex,
|
|
56594
|
-
workerPrompt: buildWorkerPrompt({
|
|
56595
|
-
dispatchPrompt: dispatch.prompt,
|
|
56596
|
-
hivemindMarkdown,
|
|
56597
|
-
isFirstWave: i === 0
|
|
56598
|
-
}),
|
|
56599
|
-
roleBody: role?.body ?? null,
|
|
56600
|
-
allowedTools: role?.allowedTools ?? [],
|
|
56601
|
-
service,
|
|
56602
|
-
signal
|
|
56603
|
-
});
|
|
56604
|
-
})
|
|
56605
|
-
);
|
|
56606
|
-
for (const outcome of outcomes) {
|
|
56607
|
-
const heading = `
|
|
56608
|
-
#### card ${outcome.card.index} \xB7 \`${outcome.card.role ?? "(no role)"}\` \u2014 ${outcome.status}
|
|
56609
|
-
|
|
56610
|
-
`;
|
|
56611
|
-
const bodyText = outcome.status === "succeeded" ? outcome.finalText.trim() || "_(empty output)_" : `_(${outcome.errorMessage ?? outcome.status})_`;
|
|
56612
|
-
await appendHivemind(hivemindPaths.filePath, heading + bodyText + "\n");
|
|
56613
|
-
}
|
|
56614
|
-
if (outcomes.some((o) => o.status === "stopped") || signal.aborted) {
|
|
56615
|
-
aborted = true;
|
|
56616
|
-
break;
|
|
56617
|
-
}
|
|
56618
|
-
if (outcomes.some((o) => o.status === "failed")) {
|
|
56619
|
-
anyWaveFailed = true;
|
|
56620
|
-
}
|
|
56621
|
-
}
|
|
56622
|
-
if (aborted) {
|
|
56623
|
-
await this.markRemainingCardsStopped(questId, service);
|
|
56624
|
-
await service.markStatus(questId, "stopped");
|
|
56625
|
-
return;
|
|
56626
|
-
}
|
|
56627
|
-
await service.markStatus(questId, anyWaveFailed ? "failed" : "succeeded");
|
|
56628
|
-
}
|
|
56629
|
-
async markRemainingCardsStopped(questId, service) {
|
|
56630
56855
|
const current = service.getRecord(questId);
|
|
56631
|
-
if (
|
|
56632
|
-
|
|
56633
|
-
|
|
56634
|
-
|
|
56635
|
-
|
|
56636
|
-
|
|
56637
|
-
|
|
56638
|
-
|
|
56639
|
-
}
|
|
56640
|
-
}
|
|
56641
|
-
async runWorker(args) {
|
|
56642
|
-
const { record, cardIndex, workerPrompt, roleBody, allowedTools, service, signal } = args;
|
|
56643
|
-
const fresh = service.getRecord(record.id);
|
|
56644
|
-
const card = fresh?.cards.find((c) => c.index === cardIndex) ?? record.cards.find((c) => c.index === cardIndex);
|
|
56645
|
-
if (!card) {
|
|
56646
|
-
return {
|
|
56647
|
-
card: record.cards[0],
|
|
56648
|
-
status: "failed",
|
|
56649
|
-
finalText: "",
|
|
56650
|
-
errorMessage: `card ${cardIndex} not found`
|
|
56651
|
-
};
|
|
56652
|
-
}
|
|
56653
|
-
const startedAt = nowIso4();
|
|
56654
|
-
let agent;
|
|
56655
|
-
try {
|
|
56656
|
-
agent = await service.agentManager.createAgent(
|
|
56657
|
-
buildWorkerAgentConfig({ record, card, roleBody, allowedTools })
|
|
56658
|
-
);
|
|
56659
|
-
} catch (err) {
|
|
56660
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
56661
|
-
this.logger.error({ err, questId: record.id, cardIndex }, "orchestrator: spawn failed");
|
|
56662
|
-
await service.updateCard(record.id, cardIndex, {
|
|
56663
|
-
status: "failed",
|
|
56664
|
-
completedAt: nowIso4(),
|
|
56665
|
-
prompt: workerPrompt
|
|
56666
|
-
});
|
|
56667
|
-
return { card, status: "failed", finalText: "", errorMessage: msg };
|
|
56668
|
-
}
|
|
56669
|
-
await service.updateCard(record.id, cardIndex, {
|
|
56670
|
-
status: "running",
|
|
56671
|
-
agentId: agent.id,
|
|
56672
|
-
startedAt,
|
|
56673
|
-
prompt: workerPrompt
|
|
56674
|
-
});
|
|
56675
|
-
const onAbort = () => {
|
|
56676
|
-
void service.agentManager.cancelAgentRun(agent.id).catch(() => {
|
|
56677
|
-
});
|
|
56678
|
-
};
|
|
56679
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
56680
|
-
try {
|
|
56681
|
-
const run = await service.agentManager.runAgent(agent.id, workerPrompt);
|
|
56682
|
-
const finalText = resolveFinalTextFromRun2(run);
|
|
56683
|
-
if (run.canceled) {
|
|
56684
|
-
await service.updateCard(record.id, cardIndex, {
|
|
56685
|
-
status: "stopped",
|
|
56686
|
-
completedAt: nowIso4()
|
|
56687
|
-
});
|
|
56688
|
-
return { card, status: "stopped", finalText, errorMessage: null };
|
|
56856
|
+
if (current) {
|
|
56857
|
+
for (const card of current.cards) {
|
|
56858
|
+
if (card.status === "queued") {
|
|
56859
|
+
await service.updateCard(questId, card.index, {
|
|
56860
|
+
status: "stopped",
|
|
56861
|
+
completedAt: nowIso4()
|
|
56862
|
+
});
|
|
56863
|
+
}
|
|
56689
56864
|
}
|
|
56690
|
-
await service.updateCard(record.id, cardIndex, {
|
|
56691
|
-
status: "succeeded",
|
|
56692
|
-
completedAt: nowIso4()
|
|
56693
|
-
});
|
|
56694
|
-
return { card, status: "succeeded", finalText, errorMessage: null };
|
|
56695
|
-
} catch (err) {
|
|
56696
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
56697
|
-
this.logger.error({ err, questId: record.id, cardIndex }, "orchestrator: worker failed");
|
|
56698
|
-
await service.updateCard(record.id, cardIndex, {
|
|
56699
|
-
status: "failed",
|
|
56700
|
-
completedAt: nowIso4()
|
|
56701
|
-
});
|
|
56702
|
-
return { card, status: "failed", finalText: "", errorMessage: msg };
|
|
56703
|
-
} finally {
|
|
56704
|
-
signal.removeEventListener("abort", onAbort);
|
|
56705
56865
|
}
|
|
56866
|
+
await service.markStatus(questId, finalStatus);
|
|
56706
56867
|
}
|
|
56707
56868
|
};
|
|
56708
56869
|
|
|
56709
56870
|
// ../server/src/server/quest/runner-ralph.ts
|
|
56710
56871
|
import { promisify as promisify4 } from "node:util";
|
|
56711
56872
|
import { execFile as execFile3 } from "node:child_process";
|
|
56712
|
-
import { appendFile
|
|
56873
|
+
import { appendFile, mkdir as mkdir8, writeFile as writeFile7 } from "node:fs/promises";
|
|
56713
56874
|
import path27 from "node:path";
|
|
56714
56875
|
var execFileAsync3 = promisify4(execFile3);
|
|
56715
56876
|
var MAX_VERIFY_OUTPUT_BYTES2 = 64 * 1024;
|
|
@@ -56765,7 +56926,7 @@ function buildRalphIterationPrompt(args) {
|
|
|
56765
56926
|
"- If not complete yet, continue making changes and do not output the token."
|
|
56766
56927
|
].join("\n");
|
|
56767
56928
|
}
|
|
56768
|
-
function
|
|
56929
|
+
function resolveFinalTextFromRun2(run) {
|
|
56769
56930
|
if (run.finalText.trim()) {
|
|
56770
56931
|
return run.finalText;
|
|
56771
56932
|
}
|
|
@@ -56781,7 +56942,7 @@ function resolveFinalTextFromRun3(run) {
|
|
|
56781
56942
|
return "";
|
|
56782
56943
|
}
|
|
56783
56944
|
function runContainsPromise(run, completionPromise) {
|
|
56784
|
-
const finalText =
|
|
56945
|
+
const finalText = resolveFinalTextFromRun2(run);
|
|
56785
56946
|
return finalText.includes(completionPromise);
|
|
56786
56947
|
}
|
|
56787
56948
|
function resolveRalphStatePaths(cwd) {
|
|
@@ -56797,8 +56958,8 @@ async function initializeRalphState(args) {
|
|
|
56797
56958
|
dir: base.dir,
|
|
56798
56959
|
logPath: path27.join(base.dir, `ralphloop_${args.record.id}.md`)
|
|
56799
56960
|
};
|
|
56800
|
-
await
|
|
56801
|
-
await
|
|
56961
|
+
await mkdir8(paths.dir, { recursive: true });
|
|
56962
|
+
await writeFile7(
|
|
56802
56963
|
paths.logPath,
|
|
56803
56964
|
[
|
|
56804
56965
|
`# Ralph Loop ${args.record.id}`,
|
|
@@ -56815,12 +56976,12 @@ async function initializeRalphState(args) {
|
|
|
56815
56976
|
].join("\n") + "\n",
|
|
56816
56977
|
"utf8"
|
|
56817
56978
|
);
|
|
56818
|
-
await
|
|
56979
|
+
await appendFile(paths.logPath, `- [${nowIso5()}] started
|
|
56819
56980
|
`, "utf8");
|
|
56820
56981
|
return paths;
|
|
56821
56982
|
}
|
|
56822
56983
|
async function appendRalphProgress(paths, message) {
|
|
56823
|
-
await
|
|
56984
|
+
await appendFile(paths.logPath, `- [${nowIso5()}] ${message}
|
|
56824
56985
|
`, "utf8");
|
|
56825
56986
|
}
|
|
56826
56987
|
function newCard(index) {
|
|
@@ -57977,13 +58138,13 @@ function createTerminalManager() {
|
|
|
57977
58138
|
}
|
|
57978
58139
|
|
|
57979
58140
|
// ../server/src/shared/connection-offer.ts
|
|
57980
|
-
import { z as
|
|
57981
|
-
var ConnectionOfferV2Schema =
|
|
57982
|
-
v:
|
|
57983
|
-
serverId:
|
|
57984
|
-
daemonPublicKeyB64:
|
|
57985
|
-
relay:
|
|
57986
|
-
endpoint:
|
|
58141
|
+
import { z as z43 } from "zod";
|
|
58142
|
+
var ConnectionOfferV2Schema = z43.object({
|
|
58143
|
+
v: z43.literal(2),
|
|
58144
|
+
serverId: z43.string().min(1),
|
|
58145
|
+
daemonPublicKeyB64: z43.string().min(1),
|
|
58146
|
+
relay: z43.object({
|
|
58147
|
+
endpoint: z43.string().min(1)
|
|
57987
58148
|
})
|
|
57988
58149
|
});
|
|
57989
58150
|
|
|
@@ -58582,7 +58743,7 @@ function createAuthServerClient(options) {
|
|
|
58582
58743
|
// ../server/src/server/package-version.ts
|
|
58583
58744
|
import { existsSync as existsSync17, readFileSync as readFileSync9 } from "node:fs";
|
|
58584
58745
|
import path28 from "node:path";
|
|
58585
|
-
import { fileURLToPath as
|
|
58746
|
+
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
58586
58747
|
var PackageVersionResolutionError = class extends Error {
|
|
58587
58748
|
constructor(params) {
|
|
58588
58749
|
super(`Unable to resolve ${params.packageName} version from module URL ${params.moduleUrl}.`);
|
|
@@ -58591,7 +58752,7 @@ var PackageVersionResolutionError = class extends Error {
|
|
|
58591
58752
|
};
|
|
58592
58753
|
function resolvePackageVersion(params) {
|
|
58593
58754
|
const moduleUrl = params.moduleUrl ?? import.meta.url;
|
|
58594
|
-
let currentDir = path28.dirname(
|
|
58755
|
+
let currentDir = path28.dirname(fileURLToPath4(moduleUrl));
|
|
58595
58756
|
while (true) {
|
|
58596
58757
|
const packageJsonPath = path28.join(currentDir, "package.json");
|
|
58597
58758
|
if (existsSync17(packageJsonPath)) {
|
|
@@ -59526,21 +59687,21 @@ async function closeAllAgents(logger, agentManager) {
|
|
|
59526
59687
|
|
|
59527
59688
|
// ../server/src/server/config.ts
|
|
59528
59689
|
import path31 from "node:path";
|
|
59529
|
-
import { z as
|
|
59690
|
+
import { z as z47 } from "zod";
|
|
59530
59691
|
|
|
59531
59692
|
// ../server/src/server/speech/speech-config-resolver.ts
|
|
59532
|
-
import { z as
|
|
59693
|
+
import { z as z46 } from "zod";
|
|
59533
59694
|
|
|
59534
59695
|
// ../server/src/server/speech/providers/local/config.ts
|
|
59535
59696
|
import path30 from "node:path";
|
|
59536
|
-
import { z as
|
|
59697
|
+
import { z as z44 } from "zod";
|
|
59537
59698
|
var DEFAULT_LOCAL_MODELS_SUBDIR = path30.join("models", "local-speech");
|
|
59538
|
-
var NumberLikeSchema2 =
|
|
59539
|
-
var OptionalFiniteNumberSchema2 = NumberLikeSchema2.pipe(
|
|
59540
|
-
var OptionalIntegerSchema = NumberLikeSchema2.pipe(
|
|
59541
|
-
var LocalSpeechResolutionSchema =
|
|
59542
|
-
includeProviderConfig:
|
|
59543
|
-
modelsDir:
|
|
59699
|
+
var NumberLikeSchema2 = z44.union([z44.number(), z44.string().trim().min(1)]);
|
|
59700
|
+
var OptionalFiniteNumberSchema2 = NumberLikeSchema2.pipe(z44.coerce.number().finite()).optional();
|
|
59701
|
+
var OptionalIntegerSchema = NumberLikeSchema2.pipe(z44.coerce.number().int()).optional();
|
|
59702
|
+
var LocalSpeechResolutionSchema = z44.object({
|
|
59703
|
+
includeProviderConfig: z44.boolean(),
|
|
59704
|
+
modelsDir: z44.string().trim().min(1),
|
|
59544
59705
|
dictationLocalSttModel: LocalSttModelIdSchema.default(DEFAULT_LOCAL_STT_MODEL),
|
|
59545
59706
|
voiceLocalSttModel: LocalSttModelIdSchema.default(DEFAULT_LOCAL_STT_MODEL),
|
|
59546
59707
|
voiceLocalTtsModel: LocalTtsModelIdSchema.default(DEFAULT_LOCAL_TTS_MODEL),
|
|
@@ -59596,17 +59757,17 @@ function resolveLocalSpeechConfig(params) {
|
|
|
59596
59757
|
}
|
|
59597
59758
|
|
|
59598
59759
|
// ../server/src/server/speech/speech-types.ts
|
|
59599
|
-
import { z as
|
|
59600
|
-
var SpeechProviderIdSchema2 =
|
|
59601
|
-
var RequestedSpeechProviderSchema =
|
|
59760
|
+
import { z as z45 } from "zod";
|
|
59761
|
+
var SpeechProviderIdSchema2 = z45.enum(["openai", "local"]);
|
|
59762
|
+
var RequestedSpeechProviderSchema = z45.object({
|
|
59602
59763
|
provider: SpeechProviderIdSchema2,
|
|
59603
|
-
explicit:
|
|
59604
|
-
enabled:
|
|
59764
|
+
explicit: z45.boolean(),
|
|
59765
|
+
enabled: z45.boolean().optional()
|
|
59605
59766
|
});
|
|
59606
59767
|
|
|
59607
59768
|
// ../server/src/server/speech/speech-config-resolver.ts
|
|
59608
|
-
var OptionalSpeechProviderSchema =
|
|
59609
|
-
var OptionalBooleanFlagSchema =
|
|
59769
|
+
var OptionalSpeechProviderSchema = z46.string().trim().toLowerCase().pipe(SpeechProviderIdSchema2).optional();
|
|
59770
|
+
var OptionalBooleanFlagSchema = z46.union([z46.boolean(), z46.string().trim().toLowerCase()]).optional().transform((value) => {
|
|
59610
59771
|
if (typeof value === "boolean") {
|
|
59611
59772
|
return value;
|
|
59612
59773
|
}
|
|
@@ -59621,7 +59782,7 @@ var OptionalBooleanFlagSchema = z44.union([z44.boolean(), z44.string().trim().to
|
|
|
59621
59782
|
}
|
|
59622
59783
|
return void 0;
|
|
59623
59784
|
});
|
|
59624
|
-
var RequestedSpeechProvidersSchema =
|
|
59785
|
+
var RequestedSpeechProvidersSchema = z46.object({
|
|
59625
59786
|
dictationStt: OptionalSpeechProviderSchema.default("local"),
|
|
59626
59787
|
voiceTurnDetection: OptionalSpeechProviderSchema.default("local"),
|
|
59627
59788
|
voiceStt: OptionalSpeechProviderSchema.default("local"),
|
|
@@ -59730,9 +59891,9 @@ function parseBooleanEnv(value) {
|
|
|
59730
59891
|
}
|
|
59731
59892
|
return void 0;
|
|
59732
59893
|
}
|
|
59733
|
-
var OptionalVoiceLlmProviderSchema =
|
|
59894
|
+
var OptionalVoiceLlmProviderSchema = z47.union([z47.string(), z47.null(), z47.undefined()]).transform(
|
|
59734
59895
|
(value) => typeof value === "string" ? value.trim().toLowerCase() : null
|
|
59735
|
-
).pipe(
|
|
59896
|
+
).pipe(z47.union([AgentProviderSchema, z47.null()]));
|
|
59736
59897
|
function parseOptionalVoiceLlmProvider(value) {
|
|
59737
59898
|
const parsed = OptionalVoiceLlmProviderSchema.safeParse(value);
|
|
59738
59899
|
return parsed.success ? parsed.data : null;
|
|
@@ -60007,7 +60168,7 @@ function createRootLogger(configInput, options) {
|
|
|
60007
60168
|
}
|
|
60008
60169
|
|
|
60009
60170
|
// ../server/src/server/pid-lock.ts
|
|
60010
|
-
import { open, readFile as readFile7, unlink as unlink3, mkdir as
|
|
60171
|
+
import { open, readFile as readFile7, unlink as unlink3, mkdir as mkdir9 } from "node:fs/promises";
|
|
60011
60172
|
import { existsSync as existsSync20 } from "node:fs";
|
|
60012
60173
|
import { join as join18 } from "node:path";
|
|
60013
60174
|
import { hostname } from "node:os";
|
|
@@ -60038,7 +60199,7 @@ function resolveOwnerPid(ownerPid) {
|
|
|
60038
60199
|
async function acquirePidLock(appostleHome, listen, options) {
|
|
60039
60200
|
const pidPath = getPidFilePath(appostleHome);
|
|
60040
60201
|
if (!existsSync20(appostleHome)) {
|
|
60041
|
-
await
|
|
60202
|
+
await mkdir9(appostleHome, { recursive: true });
|
|
60042
60203
|
}
|
|
60043
60204
|
let existingLock = null;
|
|
60044
60205
|
try {
|