@agentproto/runtime 5.8.0 → 5.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { S as SpawnDefaultsConfig } from './spawn-defaults-Cp9SGZex.js';
1
+ import { S as SpawnDefaultsConfig } from './spawn-defaults-Dm7nbxcP.js';
2
2
 
3
3
  /**
4
4
  * Model roles — ONE place to say "which model does role X use".
@@ -231,9 +231,13 @@ interface CatalogSourceConfig {
231
231
  url: string;
232
232
  }
233
233
  interface CatalogConfig {
234
- /** Remote app-catalog sources merged into `app_catalog`. When set, these
235
- * win over `sources` in `~/.agentproto/app-catalog.json`. */
234
+ /** Remote app-catalog sources merged into `app_catalog`, IN ADDITION to the
235
+ * default catalog (`defaultSource`). When set, these win over (replace)
236
+ * `sources` in `~/.agentproto/app-catalog.json`. */
236
237
  sources?: CatalogSourceConfig[];
238
+ /** The default public catalog (`DEFAULT_CATALOG_SOURCE_URL` in
239
+ * app-catalog.ts): `false` turns it off, a URL replaces it. Absent = on. */
240
+ defaultSource?: string | false;
237
241
  }
238
242
  interface TunnelConfig {
239
243
  /** Cloud WS URL. When set + autoconnect=true, `agentproto serve`
@@ -482,6 +486,11 @@ interface ReviewConfig {
482
486
  * then a host-derived value; this never blocks signing, it only lets an
483
487
  * operator pin one identity across every repo the daemon reviews. */
484
488
  principal?: string;
489
+ /** Extra attempts for an agent lane whose reviewer turn ends in a
490
+ * transient error (dropped socket, 5xx, overloaded provider). Auth,
491
+ * quota and unknown-model errors are never retried. Default 1; `0`
492
+ * disables; capped at 5. All attempts share the lane's `timeoutMs`. */
493
+ laneRetries?: number;
485
494
  }
486
495
  /**
487
496
  * Session-presence policy — how the dashboard triages a session into its
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
- import { D as DaemonConfig, T as TunnelConfig, F as FeaturesConfig, P as PairingConfig, W as WorktreesConfig, S as SpawnConfig, a as SessionsConfig, b as ProvenanceConfig, A as ApprovalsConfig, c as AgentsMdConfig, d as TitlerConfig, C as CatalogConfig, e as ProfileConfig, f as AcpAgentConfigEntry, g as TerminalPreset } from './config-Gz7q9yfJ.js';
3
- import { S as SpawnDefaultsConfig } from './spawn-defaults-Cp9SGZex.js';
2
+ import { D as DaemonConfig, T as TunnelConfig, F as FeaturesConfig, P as PairingConfig, W as WorktreesConfig, S as SpawnConfig, a as SessionsConfig, b as ProvenanceConfig, A as ApprovalsConfig, c as AgentsMdConfig, d as TitlerConfig, C as CatalogConfig, e as ProfileConfig, f as AcpAgentConfigEntry, g as TerminalPreset } from './config-Dsmv-jri.js';
3
+ import { S as SpawnDefaultsConfig } from './spawn-defaults-Dm7nbxcP.js';
4
4
  import '@agentproto/acp';
5
5
  import '@agentproto/command-sandbox';
6
6
  import '@agentproto/model-catalog';
@@ -108,6 +108,15 @@ var init_pr_provenance = __esm({
108
108
  }
109
109
  });
110
110
 
111
+ // src/handoff-markers.ts
112
+ var HANDOFF_PROMPT_SOURCE, HANDOFF_PROMPT_OPENER;
113
+ var init_handoff_markers = __esm({
114
+ "src/handoff-markers.ts"() {
115
+ HANDOFF_PROMPT_SOURCE = "daemon:handoff";
116
+ HANDOFF_PROMPT_OPENER = "[handoff request from the agentproto daemon]";
117
+ }
118
+ });
119
+
111
120
  // src/tool-presenter.ts
112
121
  function isRecord(value) {
113
122
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -329,9 +338,9 @@ async function exportClaudeCodeSession(adapterSessionId, cwd, configDir) {
329
338
  let stream;
330
339
  try {
331
340
  stream = createReadStream(filePath, { encoding: "utf8" });
332
- await new Promise((resolve17, reject) => {
341
+ await new Promise((resolve18, reject) => {
333
342
  stream.once("error", reject);
334
- stream.once("open", resolve17);
343
+ stream.once("open", resolve18);
335
344
  });
336
345
  } catch (err) {
337
346
  const code = err.code;
@@ -605,7 +614,7 @@ async function discoverHermesSessions(cwd, since, expectedId) {
605
614
  }
606
615
  async function defaultHermesRunner(adapterSessionId) {
607
616
  const { spawn: spawn5 } = await import('child_process');
608
- return new Promise((resolve17, reject) => {
617
+ return new Promise((resolve18, reject) => {
609
618
  const chunks = [];
610
619
  const errChunks = [];
611
620
  const proc = spawn5(
@@ -624,7 +633,7 @@ async function defaultHermesRunner(adapterSessionId) {
624
633
  )
625
634
  );
626
635
  } else {
627
- resolve17(Buffer.concat(chunks).toString("utf8"));
636
+ resolve18(Buffer.concat(chunks).toString("utf8"));
628
637
  }
629
638
  });
630
639
  });
@@ -661,9 +670,9 @@ async function exportDaemonEventsSession(sessionId, desc) {
661
670
  let stream;
662
671
  try {
663
672
  stream = createReadStream(filePath, { encoding: "utf8" });
664
- await new Promise((resolve17, reject) => {
673
+ await new Promise((resolve18, reject) => {
665
674
  stream.once("error", reject);
666
- stream.once("open", resolve17);
675
+ stream.once("open", resolve18);
667
676
  });
668
677
  } catch (err) {
669
678
  const code = err.code;
@@ -696,6 +705,15 @@ Either the session never drove an agent-cli turn, or it predates this feature.`
696
705
  asmToolCalls = [];
697
706
  asmTs = void 0;
698
707
  };
708
+ let handoffStart;
709
+ const closeHandoffTurn = () => {
710
+ if (handoffStart === void 0) return;
711
+ for (let i = handoffStart; i < messages.length; i++) {
712
+ const m = messages[i];
713
+ if (m) m.internal = "handoff";
714
+ }
715
+ handoffStart = void 0;
716
+ };
699
717
  const rl = createInterface({ input: stream, crlfDelay: Infinity });
700
718
  for await (const line of rl) {
701
719
  const trimmed = line.trim();
@@ -742,10 +760,15 @@ ${rec.stderr}`);
742
760
  switch (rec.kind) {
743
761
  case "user-prompt":
744
762
  flushAssistant();
763
+ closeHandoffTurn();
764
+ if (rec.source === HANDOFF_PROMPT_SOURCE || rec.text?.startsWith(HANDOFF_PROMPT_OPENER)) {
765
+ handoffStart = messages.length;
766
+ }
745
767
  messages.push({ role: "user", text: rec.text ?? "", ...tsOrUndefined !== void 0 ? { ts: tsOrUndefined } : {} });
746
768
  break;
747
769
  case "session-message": {
748
770
  flushAssistant();
771
+ closeHandoffTurn();
749
772
  const m = rec.message;
750
773
  if (!m) break;
751
774
  const relation = m.from?.relation ?? "system";
@@ -765,7 +788,13 @@ ${rec.stderr}`);
765
788
  }
766
789
  case "system-prompt":
767
790
  flushAssistant();
768
- messages.push({ role: "system", text: rec.text ?? "", ...tsOrUndefined !== void 0 ? { ts: tsOrUndefined } : {} });
791
+ closeHandoffTurn();
792
+ messages.push({
793
+ role: "system",
794
+ text: rec.text ?? "",
795
+ internal: "preamble",
796
+ ...tsOrUndefined !== void 0 ? { ts: tsOrUndefined } : {}
797
+ });
769
798
  break;
770
799
  case "text-delta":
771
800
  if (asmTs === void 0) asmTs = tsOrUndefined;
@@ -825,6 +854,7 @@ ${rec.stderr}`);
825
854
  }
826
855
  }
827
856
  flushAssistant();
857
+ closeHandoffTurn();
828
858
  const meta = { source: "daemon-events" };
829
859
  if (desc?.label) meta.title = desc.label;
830
860
  else if (desc?.command) {
@@ -1545,6 +1575,7 @@ Only sessions spawned via claude-code or hermes can be exported.`
1545
1575
  var ROLE_ICON, IGNORED_CLAUDE_TYPES, NODE_SQLITE, EXPORT_STRATEGIES;
1546
1576
  var init_transcript_export = __esm({
1547
1577
  "src/transcript-export.ts"() {
1578
+ init_handoff_markers();
1548
1579
  init_tool_presenter();
1549
1580
  init_transcript_writer();
1550
1581
  init_conversation_store();
@@ -1988,9 +2019,15 @@ var init_bundles = __esm({
1988
2019
  });
1989
2020
  }
1990
2021
  });
2022
+ var init_eligibility_manifest = __esm({
2023
+ "src/eligibility-manifest.ts"() {
2024
+ init_spawn_defaults();
2025
+ }
2026
+ });
1991
2027
  var init_catalog_models = __esm({
1992
2028
  "src/catalog-models.ts"() {
1993
2029
  init_spawn_defaults();
2030
+ init_eligibility_manifest();
1994
2031
  }
1995
2032
  });
1996
2033
  var init_model_wire = __esm({
@@ -2254,6 +2291,7 @@ var init_session_spawn = __esm({
2254
2291
  init_bundles();
2255
2292
  init_mcp_imports();
2256
2293
  init_launch_config();
2294
+ init_eligibility_manifest();
2257
2295
  init_claude_code_oauth_source();
2258
2296
  init_providers_store();
2259
2297
  init_catalog_models();
@@ -2331,6 +2369,7 @@ var init_spawn_field_schemas = __esm({
2331
2369
  compactAtPct: z.number().int().min(0).max(100).optional(),
2332
2370
  continueFreshAtPct: z.number().int().min(0).max(100).optional(),
2333
2371
  hardStopAtPct: z.number().int().min(0).max(100).optional(),
2372
+ handoffAtQuotaRemaining: z.number().min(0).optional(),
2334
2373
  goal: z.boolean().optional(),
2335
2374
  plan: z.boolean().optional(),
2336
2375
  decisions: z.boolean().optional(),
@@ -2345,12 +2384,34 @@ var init_spawn_field_schemas = __esm({
2345
2384
  });
2346
2385
  }
2347
2386
  });
2387
+ function decodeWhsecSecret(secret) {
2388
+ if (typeof secret !== "string" || !secret.startsWith(WHSEC_PREFIX)) return null;
2389
+ const encoded = secret.slice(WHSEC_PREFIX.length);
2390
+ if (encoded.length === 0 || !/^[A-Za-z0-9+/]*={0,2}$/.test(encoded) || encoded.length % 4 !== 0) {
2391
+ return null;
2392
+ }
2393
+ let key;
2394
+ try {
2395
+ key = Buffer.from(encoded, "base64");
2396
+ } catch {
2397
+ return null;
2398
+ }
2399
+ if (key.length < 24 || key.length > 64) return null;
2400
+ return key;
2401
+ }
2402
+ var WHSEC_PREFIX;
2403
+ var init_signing = __esm({
2404
+ "src/webhook-egress/signing.ts"() {
2405
+ WHSEC_PREFIX = "whsec_";
2406
+ }
2407
+ });
2348
2408
  var mcpBool, mcpPositiveNumber, help, agentStartInputShape, agentStartInputSchema;
2349
2409
  var init_agent_start_schema = __esm({
2350
2410
  "src/agent-start-schema.ts"() {
2351
2411
  init_json_tolerant();
2352
2412
  init_sandbox_spec_schema();
2353
2413
  init_spawn_field_schemas();
2414
+ init_signing();
2354
2415
  mcpBool = z.preprocess(
2355
2416
  (v) => v === "true" ? true : v === "false" ? false : v,
2356
2417
  z.boolean()
@@ -2456,7 +2517,7 @@ var init_agent_start_schema = __esm({
2456
2517
  apiKey: z.string().optional()
2457
2518
  })
2458
2519
  ).optional().describe(
2459
- `Explicit billing-auth mode + credential for adapters that declare it (today: claude-code) \u2014 'subscription' (default) bills the Max/Pro plan, 'api-key' bills API credits. FAILS FAST with no fallback if the resolved mode has no credential configured anywhere. ${help("auth")}`
2520
+ `Explicit billing-auth mode + credential for adapters that declare it (those that declare a subscription login and/or an API-key provider; any other fails with unsupported_auth_mode) \u2014 'subscription' (default) bills the user's subscription login, 'api-key' bills API credits. FAILS FAST with no fallback if the resolved mode has no credential configured anywhere. ${help("auth")}`
2460
2521
  ),
2461
2522
  mcpServers: jsonTolerant(
2462
2523
  z.array(
@@ -2490,6 +2551,9 @@ var init_agent_start_schema = __esm({
2490
2551
  notifyUrl: z.string().url().optional().describe(
2491
2552
  "Optional per-session webhook URL. POSTed (fire-and-forget) on this session's turn-end / awaiting-input / exited events, in addition to any global notify URL."
2492
2553
  ),
2554
+ notifySecret: z.string().refine((s) => decodeWhsecSecret(s) !== null, {
2555
+ message: "must be a whsec_<base64> Standard Webhooks secret (24-64 decoded bytes)"
2556
+ }).optional().describe(`Standard Webhooks secret (\`whsec_...\`) signing \`notifyUrl\` POSTs. ${help("notifySecret")}`),
2493
2557
  wait: mcpBool.optional().describe(
2494
2558
  `Block until the spawned session's first turn completes (~40-90s+) and include its output. Default false = return immediately. Batched \`wait:true\` calls do NOT run in parallel \u2014 see \`tool_help {name:"agent_start", topic:"wait"}\` for the fan-out pattern.`
2495
2559
  ),
@@ -2528,7 +2592,7 @@ var init_agent_start_schema = __esm({
2528
2592
  "One-off runtime text layered onto the resolved role's disposition and prepended to `prompt` \u2014 specializes it, cannot reopen a denied tool gate."
2529
2593
  ),
2530
2594
  deferredTools: z.boolean().optional().describe(
2531
- `Override lazy MCP tool loading for this spawn: \`true\` hides most tools from \`tools/list\` (still callable via \`tool_search\`), \`false\` keeps the full eager surface. Omit to use the role/daemon default. ${help("deferredTools")}`
2595
+ `Override lazy MCP tool loading for this spawn: \`true\` hides most tools from \`tools/list\` (still callable via \`tool_search\`), \`false\` keeps the full eager surface. Omit to use the harness/role/daemon default (harnesses with native tool search default to eager). ${help("deferredTools")}`
2532
2596
  ),
2533
2597
  browser: z.preprocess(
2534
2598
  // `true` is sugar for the only mode; stringified booleans tolerated.
@@ -2666,6 +2730,12 @@ var init_session_activity = __esm({
2666
2730
  "src/session-activity.ts"() {
2667
2731
  }
2668
2732
  });
2733
+
2734
+ // src/handoff-suggestion.ts
2735
+ var init_handoff_suggestion = __esm({
2736
+ "src/handoff-suggestion.ts"() {
2737
+ }
2738
+ });
2669
2739
  var DEFAULT_BG_TASK_WAKE_GRACE_MS;
2670
2740
  var init_background_task_wake = __esm({
2671
2741
  "src/background-task-wake.ts"() {
@@ -2713,10 +2783,99 @@ var init_context_window = __esm({
2713
2783
  "src/context-window.ts"() {
2714
2784
  }
2715
2785
  });
2786
+ var CHECKPOINT_SCHEMA_VERSION, pct, contextCheckpointPolicySchema, contextCheckpointSectionsSchema, checkpointHandoffTurnSchema;
2787
+ var init_checkpoint_schema = __esm({
2788
+ "src/checkpoint-schema.ts"() {
2789
+ CHECKPOINT_SCHEMA_VERSION = 1;
2790
+ pct = z.number().min(0).max(100);
2791
+ contextCheckpointPolicySchema = z.looseObject({
2792
+ mode: z.enum(["manual", "ask", "auto"]),
2793
+ warnAtPct: pct,
2794
+ compactAtPct: pct,
2795
+ continueFreshAtPct: pct,
2796
+ hardStopAtPct: pct,
2797
+ goal: z.boolean(),
2798
+ plan: z.boolean(),
2799
+ decisions: z.boolean(),
2800
+ changedFiles: z.boolean(),
2801
+ gitStatus: z.boolean(),
2802
+ tests: z.boolean(),
2803
+ errors: z.boolean(),
2804
+ risks: z.boolean(),
2805
+ nextStep: z.boolean(),
2806
+ config: z.boolean(),
2807
+ label: z.string()
2808
+ });
2809
+ contextCheckpointSectionsSchema = z.looseObject({
2810
+ goal: z.string().optional(),
2811
+ plan: z.string().optional(),
2812
+ decisions: z.string().optional(),
2813
+ changedFiles: z.string().optional(),
2814
+ gitStatus: z.string().optional(),
2815
+ tests: z.string().optional(),
2816
+ errors: z.string().optional(),
2817
+ risks: z.string().optional(),
2818
+ nextStep: z.string().optional(),
2819
+ config: z.string().optional(),
2820
+ notes: z.string().optional().describe("Free-text notes the operator attached to the handoff.")
2821
+ });
2822
+ checkpointHandoffTurnSchema = z.looseObject({
2823
+ status: z.enum(["answered", "skipped", "failed"]).describe(
2824
+ "answered: the source session replied with a valid summary; skipped: not requested or no live idle session; failed: asked but timed out / unusable reply."
2825
+ ),
2826
+ reason: z.string().optional()
2827
+ });
2828
+ z.looseObject({
2829
+ schemaVersion: z.literal(CHECKPOINT_SCHEMA_VERSION),
2830
+ checkpointId: z.string(),
2831
+ sourceSessionId: z.string(),
2832
+ createdAt: z.string().describe("ISO 8601 creation timestamp."),
2833
+ contextPct: z.number().describe("Context percentage that triggered the checkpoint."),
2834
+ policy: contextCheckpointPolicySchema,
2835
+ sections: contextCheckpointSectionsSchema.describe(
2836
+ "Sections requested and present. A section with nothing real to say is omitted, never a placeholder."
2837
+ ),
2838
+ handoffTurn: checkpointHandoffTurnSchema.optional(),
2839
+ recentDigest: z.string(),
2840
+ originalTranscriptPath: z.string(),
2841
+ checkpointPath: z.string(),
2842
+ nextAction: z.enum(["continue", "compact_then_continue", "ask"])
2843
+ }).meta({
2844
+ title: "AgentProto context checkpoint",
2845
+ description: "Structured handoff document persisted next to a session's events.jsonl and used to seed a fresh continuation session."
2846
+ });
2847
+ }
2848
+ });
2849
+ var init_session_outcome = __esm({
2850
+ "src/session-outcome.ts"() {
2851
+ }
2852
+ });
2853
+ var nonEmpty;
2854
+ var init_checkpoint_extract = __esm({
2855
+ "src/checkpoint-extract.ts"() {
2856
+ init_session_outcome();
2857
+ init_supervisor();
2858
+ init_handoff_markers();
2859
+ init_transcript_writer();
2860
+ nonEmpty = z.string().trim().min(1);
2861
+ z.object({
2862
+ goal: nonEmpty.optional(),
2863
+ decisions: z.array(nonEmpty).default([]),
2864
+ tests: z.object({ command: z.string().default(""), result: z.string().default("") }).optional(),
2865
+ openRisks: z.array(nonEmpty).default([]),
2866
+ nextStep: nonEmpty.optional()
2867
+ }).refine((r) => r.goal || r.decisions.length || r.tests || r.openRisks.length || r.nextStep, {
2868
+ message: "handoff reply is empty"
2869
+ });
2870
+ }
2871
+ });
2716
2872
  var init_context_checkpoint = __esm({
2717
2873
  "src/context-checkpoint.ts"() {
2874
+ init_checkpoint_schema();
2875
+ init_checkpoint_extract();
2718
2876
  init_transcript_export();
2719
2877
  init_transcript_writer();
2878
+ init_context_continuity();
2720
2879
  }
2721
2880
  });
2722
2881
 
@@ -2729,16 +2888,17 @@ var init_session_continue_fresh = __esm({
2729
2888
  init_session_spawn();
2730
2889
  }
2731
2890
  });
2732
- var init_session_outcome = __esm({
2733
- "src/session-outcome.ts"() {
2734
- }
2735
- });
2736
2891
 
2737
2892
  // src/session-end-reason.ts
2738
2893
  var init_session_end_reason = __esm({
2739
2894
  "src/session-end-reason.ts"() {
2740
2895
  }
2741
2896
  });
2897
+ var init_session_index = __esm({
2898
+ "src/session-index.ts"() {
2899
+ init_transcript_writer();
2900
+ }
2901
+ });
2742
2902
  var init_sessions = __esm({
2743
2903
  "src/sessions.ts"() {
2744
2904
  init_resume_strategies();
@@ -2752,6 +2912,7 @@ var init_sessions = __esm({
2752
2912
  init_supervisor();
2753
2913
  init_session_title();
2754
2914
  init_session_activity();
2915
+ init_handoff_suggestion();
2755
2916
  init_canonical_posture();
2756
2917
  init_background_task_wake();
2757
2918
  init_catalog_models();
@@ -2772,6 +2933,7 @@ var init_sessions = __esm({
2772
2933
  init_session_continue_fresh();
2773
2934
  init_session_outcome();
2774
2935
  init_session_end_reason();
2936
+ init_session_index();
2775
2937
  init_session_message();
2776
2938
  }
2777
2939
  });
@@ -2912,6 +3074,7 @@ var init_config_schema = __esm({
2912
3074
  compactAtPct: z.number().optional(),
2913
3075
  continueFreshAtPct: z.number().optional(),
2914
3076
  hardStopAtPct: z.number().optional(),
3077
+ handoffAtQuotaRemaining: z.number().min(0).optional(),
2915
3078
  goal: z.boolean().optional(),
2916
3079
  plan: z.boolean().optional(),
2917
3080
  decisions: z.boolean().optional(),
@@ -2965,7 +3128,8 @@ var init_config_schema = __esm({
2965
3128
  agentPromptInterrupt: z.boolean().optional(),
2966
3129
  messaging: z.object({
2967
3130
  allowSiblings: z.boolean().optional(),
2968
- agentInterrupt: z.enum(["allow", "deny"]).optional()
3131
+ agentInterrupt: z.enum(["allow", "deny"]).optional(),
3132
+ pendingPromptStaleMinutes: z.number().positive().optional()
2969
3133
  }).passthrough().optional(),
2970
3134
  mcp: mcpDefaultsConfigSchema.optional(),
2971
3135
  spawn: z.object({ browser: spawnBrowserModeSchema.optional() }).passthrough().optional()
@@ -2990,7 +3154,8 @@ var init_config_schema = __esm({
2990
3154
  model: z.string().optional()
2991
3155
  }).passthrough();
2992
3156
  catalogConfigSchema = z.object({
2993
- sources: z.array(z.object({ url: z.string() }).passthrough()).optional()
3157
+ sources: z.array(z.object({ url: z.string() }).passthrough()).optional(),
3158
+ defaultSource: z.union([z.string(), z.literal(false)]).optional()
2994
3159
  }).passthrough();
2995
3160
  tunnelConfigSchema = z.object({
2996
3161
  host: z.string().optional(),
@@ -3370,7 +3535,16 @@ var init_config_schema = __esm({
3370
3535
  writable: true,
3371
3536
  section: "daemon",
3372
3537
  label: "App catalog sources",
3373
- help: "Remote catalogs merged into `app_catalog`: an array of `{ url }`, each returning `{ entries: AppCatalogEntry[] }`. Wins over `sources` in ~/.agentproto/app-catalog.json when both are set."
3538
+ help: "Remote catalogs merged into `app_catalog` in addition to the default catalog (`catalog.defaultSource`): an array of `{ url }`, each returning an `app-catalog/v1` document (`{ entries: AppCatalogEntry[] }`). Wins over `sources` in ~/.agentproto/app-catalog.json when both are set."
3539
+ },
3540
+ {
3541
+ path: "catalog.defaultSource",
3542
+ schema: z.union([str, z.literal(false)]),
3543
+ apply: "hot",
3544
+ writable: true,
3545
+ section: "daemon",
3546
+ label: "Default app catalog",
3547
+ help: "The built-in public catalog `app_catalog` queries before `catalog.sources`. `false` turns it off; a URL replaces it. When unreachable, the last copy cached under ~/.agentproto/cache/catalog is used, then the embedded first-party list."
3374
3548
  },
3375
3549
  // ── defaults: spawn ──
3376
3550
  {
@@ -3637,6 +3811,16 @@ var init_config_schema = __esm({
3637
3811
  help: 'Whether a SESSION sender may use urgency "interrupt" (vs. downgraded to "steer"). Takes effect on the next call.',
3638
3812
  default: "deny"
3639
3813
  },
3814
+ {
3815
+ path: "defaults.messaging.pendingPromptStaleMinutes",
3816
+ schema: z.number().positive(),
3817
+ apply: "hot",
3818
+ writable: true,
3819
+ section: "defaults",
3820
+ label: "Pending prompt staleness (minutes)",
3821
+ help: "How long a prompt may sit queued behind a running turn before its sender is notified it is stuck and it is flagged stale in session_list/session_recap pendingPrompts. Takes effect on the next sweep.",
3822
+ default: 5
3823
+ },
3640
3824
  // ── harnesses: per-adapter defaults ──
3641
3825
  {
3642
3826
  path: "defaults.adapters.*.skills",