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/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
- serializable.mcpServers = config.mcpServers;
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
- function sanitizeOptionalJson(value) {
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 (Array.isArray(value)) {
2464
- const sanitized = value.map((item) => sanitizeOptionalJson(item)).filter((item) => item !== void 0);
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 === "object") {
2471
- const result = {};
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 (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
2481
- return value;
2481
+ if (depth >= SANITIZE_MAX_DEPTH) {
2482
+ return void 0;
2482
2483
  }
2483
- return void 0;
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 tool = entry?.name ?? block.tool_name ?? "tool";
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, tool, content, input);
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, tool, output, input) {
20624
+ buildStructuredToolResult(server, tool2, output, input) {
20510
20625
  const normalizedServer = server.toLowerCase();
20511
- const normalizedTool = tool.toLowerCase();
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, tool) {
21826
- const trimmedTool = tool.trim();
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 tool = item.tool.trim();
21997
- if (!tool) {
22111
+ const tool2 = item.tool.trim();
22112
+ if (!tool2) {
21998
22113
  return null;
21999
22114
  }
22000
- const name = buildMcpToolName(item.server, tool);
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
- mcpServers[name] = toCodexMcpConfig(serverConfig);
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
- return Object.entries(servers).map(([name, config]) => {
27267
+ const out = [];
27268
+ for (const [name, config] of Object.entries(servers)) {
27150
27269
  if (config.type === "stdio") {
27151
- return {
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
- return {
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
- return {
27173
- type: "sse",
27174
- name,
27175
- url: config.url,
27176
- headers: Object.entries(config.headers ?? {}).map(([headerName, value]) => ({
27177
- name: headerName,
27178
- value
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
- return {
28013
- type: "remote",
28014
- url: config.url,
28015
- ...config.headers ? { headers: config.headers } : {},
28016
- enabled: true
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 tool = readOpenCodeRecord(event.properties.tool);
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) && !record.internal).map((record) => this.buildStoredAgentPayload(record));
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 { appendFile, mkdir as mkdir8, readFile as readFile5, writeFile as writeFile7 } from "node:fs/promises";
56408
+ import { access, readFile as readFile5 } from "node:fs/promises";
56180
56409
  import path26 from "node:path";
56181
- function nowIso4() {
56182
- return (/* @__PURE__ */ new Date()).toISOString();
56183
- }
56184
- async function resolveAllRoles(record) {
56185
- const out = [];
56186
- for (const card of record.cards) {
56187
- if (!card.role) {
56188
- out.push({
56189
- cardIndex: card.index,
56190
- roleName: `card-${card.index}`,
56191
- body: null,
56192
- allowedTools: []
56193
- });
56194
- continue;
56195
- }
56196
- const resolved = await resolveRole({ roleName: card.role, workspaceRoot: record.cwd });
56197
- out.push({
56198
- cardIndex: card.index,
56199
- roleName: card.role,
56200
- body: resolved?.body ?? null,
56201
- allowedTools: resolved?.allowedTools ?? []
56202
- });
56203
- }
56204
- return out;
56205
- }
56206
- function buildQueenSystemPrompt(args) {
56207
- const roleSections = args.roles.map((r) => {
56208
- const body = r.body ? r.body.trim() : "(no role body found \u2014 this card has no system prompt)";
56209
- return [`### Card ${r.cardIndex} \xB7 role: \`${r.roleName}\``, "", body].join("\n");
56210
- }).join("\n\n---\n\n");
56211
- return [
56212
- "You are the QUEEN BEE \u2014 the planner of a multi-agent orchestration.",
56213
- "",
56214
- "You do NOT do the work yourself. Your one and only job is to read the",
56215
- "user's task plus the role roster below and emit ONE JSON plan that",
56216
- "delegates the work across your team.",
56217
- "",
56218
- "A plan is an ORDERED list of waves. Each wave is a SET of role dispatches",
56219
- "that the runner will execute IN PARALLEL. Wave N+1 starts only after",
56220
- "every dispatch in wave N has finished.",
56221
- "",
56222
- "Use parallelism aggressively. Place roles in the SAME wave whenever they",
56223
- "do not depend on each other's output. Place a role in a LATER wave only",
56224
- "when it genuinely needs the result of an earlier wave.",
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
- ok: false,
56313
- reason: `wave ${i + 1}: every dispatch needs string \`role\` and string \`prompt\``
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
- const role = d.role.trim();
56317
- const prompt = d.prompt;
56318
- if (!knownRoles.has(role)) {
56319
- return { ok: false, reason: `wave ${i + 1}: unknown role \`${role}\`` };
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
- if (seenRoles.has(role)) {
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
- ok: false,
56324
- reason: `wave ${i + 1}: role \`${role}\` was already scheduled in an earlier wave`
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
- waves.push({ dispatches });
56331
- }
56332
- return { ok: true, plan: waves };
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 resolveFinalTextFromRun2(run) {
56335
- if (run.finalText.trim()) {
56336
- return run.finalText;
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
- for (let i = run.timeline.length - 1; i >= 0; i -= 1) {
56339
- const item = run.timeline[i];
56340
- if (item && typeof item === "object" && "type" in item && item.type === "assistant_message" && "text" in item && typeof item.text === "string") {
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 resolveHivemindPaths(cwd, questId) {
56350
- const dir = path26.join(cwd, ".hiveminds", "orchestrator");
56351
- return {
56352
- dir,
56353
- filePath: path26.join(dir, `orchestrator-${questId}.md`)
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
- async function initializeHivemind(args) {
56357
- await mkdir8(args.paths.dir, { recursive: true });
56358
- const roster = args.roles.map((r) => `- card ${r.cardIndex} \xB7 \`${r.roleName}\``).join("\n");
56359
- const planSummary = args.plan.map(
56360
- (wave, i) => `**wave ${i + 1}** \u2014 ${wave.dispatches.map((d) => `\`${d.role}\``).join(", ")}`
56361
- ).join("\n");
56362
- const body = [
56363
- `# Orchestrator ${args.record.id}`,
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
- async function appendHivemind(filePath, chunk) {
56382
- await appendFile(filePath, chunk, "utf8");
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 readHivemind(filePath) {
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
- return await readFile5(filePath, "utf8");
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
- systemPrompt: args.systemPrompt,
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 hivemindPaths = resolveHivemindPaths(initial.cwd, initial.id);
56451
- const resolvedRoles = await resolveAllRoles(initial);
56452
- const knownRoles = new Set(resolvedRoles.map((r) => r.roleName));
56453
- const cardIndexByRole = /* @__PURE__ */ new Map();
56454
- for (const r of resolvedRoles) {
56455
- if (!cardIndexByRole.has(r.roleName)) {
56456
- cardIndexByRole.set(r.roleName, r.cardIndex);
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, systemPrompt: queenSystemPrompt })
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 plan;
56830
+ let finalStatus = "succeeded";
56487
56831
  try {
56488
- const queenRun = await service.agentManager.runAgent(queenAgent.id, initial.prompt);
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
- const queenText = resolveFinalTextFromRun2(queenRun);
56499
- const result = parseQueenPlan(queenText, knownRoles);
56500
- if (!result.ok) {
56501
- await mkdir8(hivemindPaths.dir, { recursive: true });
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
- await this.markRemainingCardsStopped(questId, service);
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 (!current) return;
56632
- for (const card of current.cards) {
56633
- if (card.status === "queued" || card.status === "running") {
56634
- await service.updateCard(questId, card.index, {
56635
- status: "stopped",
56636
- completedAt: nowIso4()
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 as appendFile2, mkdir as mkdir9, writeFile as writeFile8 } from "node:fs/promises";
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 resolveFinalTextFromRun3(run) {
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 = resolveFinalTextFromRun3(run);
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 mkdir9(paths.dir, { recursive: true });
56801
- await writeFile8(
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 appendFile2(paths.logPath, `- [${nowIso5()}] started
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 appendFile2(paths.logPath, `- [${nowIso5()}] ${message}
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 z41 } from "zod";
57981
- var ConnectionOfferV2Schema = z41.object({
57982
- v: z41.literal(2),
57983
- serverId: z41.string().min(1),
57984
- daemonPublicKeyB64: z41.string().min(1),
57985
- relay: z41.object({
57986
- endpoint: z41.string().min(1)
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 fileURLToPath3 } from "node:url";
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(fileURLToPath3(moduleUrl));
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 z45 } from "zod";
59690
+ import { z as z47 } from "zod";
59530
59691
 
59531
59692
  // ../server/src/server/speech/speech-config-resolver.ts
59532
- import { z as z44 } from "zod";
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 z42 } from "zod";
59697
+ import { z as z44 } from "zod";
59537
59698
  var DEFAULT_LOCAL_MODELS_SUBDIR = path30.join("models", "local-speech");
59538
- var NumberLikeSchema2 = z42.union([z42.number(), z42.string().trim().min(1)]);
59539
- var OptionalFiniteNumberSchema2 = NumberLikeSchema2.pipe(z42.coerce.number().finite()).optional();
59540
- var OptionalIntegerSchema = NumberLikeSchema2.pipe(z42.coerce.number().int()).optional();
59541
- var LocalSpeechResolutionSchema = z42.object({
59542
- includeProviderConfig: z42.boolean(),
59543
- modelsDir: z42.string().trim().min(1),
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 z43 } from "zod";
59600
- var SpeechProviderIdSchema2 = z43.enum(["openai", "local"]);
59601
- var RequestedSpeechProviderSchema = z43.object({
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: z43.boolean(),
59604
- enabled: z43.boolean().optional()
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 = z44.string().trim().toLowerCase().pipe(SpeechProviderIdSchema2).optional();
59609
- var OptionalBooleanFlagSchema = z44.union([z44.boolean(), z44.string().trim().toLowerCase()]).optional().transform((value) => {
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 = z44.object({
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 = z45.union([z45.string(), z45.null(), z45.undefined()]).transform(
59894
+ var OptionalVoiceLlmProviderSchema = z47.union([z47.string(), z47.null(), z47.undefined()]).transform(
59734
59895
  (value) => typeof value === "string" ? value.trim().toLowerCase() : null
59735
- ).pipe(z45.union([AgentProviderSchema, z45.null()]));
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 mkdir10 } from "node:fs/promises";
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 mkdir10(appostleHome, { recursive: true });
60202
+ await mkdir9(appostleHome, { recursive: true });
60042
60203
  }
60043
60204
  let existingLock = null;
60044
60205
  try {