@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.
package/dist/config.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import './spawn-defaults-Cp9SGZex.js';
2
- export { f as AcpAgentConfigEntry, t as AgentprotoConfig, c as AgentsMdConfig, A as ApprovalsConfig, u as CONFIG_FILE_PATH, v as CONFIG_VERSION, C as CatalogConfig, w as CatalogSourceConfig, D as DaemonConfig, F as FeaturesConfig, J as JevConfig, P as PairingConfig, e as ProfileConfig, b as ProvenanceConfig, x as ReviewConfig, y as SentinelConfig, a as SessionsConfig, i as SpawnAttachMode, S as SpawnConfig, j as SpawnDedupeMode, g as TerminalPreset, d as TitlerConfig, T as TunnelConfig, h as WorktreeIsolationMode, W as WorktreesConfig, z as getConfigKey, B as loadConfig, E as saveConfig, G as setConfigKey, H as splitConfigPath } from './config-Gz7q9yfJ.js';
1
+ import './spawn-defaults-Dm7nbxcP.js';
2
+ export { f as AcpAgentConfigEntry, t as AgentprotoConfig, c as AgentsMdConfig, A as ApprovalsConfig, u as CONFIG_FILE_PATH, v as CONFIG_VERSION, C as CatalogConfig, w as CatalogSourceConfig, D as DaemonConfig, F as FeaturesConfig, J as JevConfig, P as PairingConfig, e as ProfileConfig, b as ProvenanceConfig, x as ReviewConfig, y as SentinelConfig, a as SessionsConfig, i as SpawnAttachMode, S as SpawnConfig, j as SpawnDedupeMode, g as TerminalPreset, d as TitlerConfig, T as TunnelConfig, h as WorktreeIsolationMode, W as WorktreesConfig, z as getConfigKey, B as loadConfig, E as saveConfig, G as setConfigKey, H as splitConfigPath } from './config-Dsmv-jri.js';
3
3
  import '@agentproto/acp';
4
4
  import '@agentproto/command-sandbox';
5
5
  import '@agentproto/model-catalog';
package/dist/config.mjs CHANGED
@@ -103,6 +103,15 @@ var init_pr_provenance = __esm({
103
103
  }
104
104
  });
105
105
 
106
+ // src/handoff-markers.ts
107
+ var HANDOFF_PROMPT_SOURCE, HANDOFF_PROMPT_OPENER;
108
+ var init_handoff_markers = __esm({
109
+ "src/handoff-markers.ts"() {
110
+ HANDOFF_PROMPT_SOURCE = "daemon:handoff";
111
+ HANDOFF_PROMPT_OPENER = "[handoff request from the agentproto daemon]";
112
+ }
113
+ });
114
+
106
115
  // src/tool-presenter.ts
107
116
  function isRecord(value) {
108
117
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -324,9 +333,9 @@ async function exportClaudeCodeSession(adapterSessionId, cwd, configDir) {
324
333
  let stream;
325
334
  try {
326
335
  stream = createReadStream(filePath, { encoding: "utf8" });
327
- await new Promise((resolve17, reject) => {
336
+ await new Promise((resolve18, reject) => {
328
337
  stream.once("error", reject);
329
- stream.once("open", resolve17);
338
+ stream.once("open", resolve18);
330
339
  });
331
340
  } catch (err) {
332
341
  const code = err.code;
@@ -600,7 +609,7 @@ async function discoverHermesSessions(cwd, since, expectedId) {
600
609
  }
601
610
  async function defaultHermesRunner(adapterSessionId) {
602
611
  const { spawn: spawn5 } = await import('child_process');
603
- return new Promise((resolve17, reject) => {
612
+ return new Promise((resolve18, reject) => {
604
613
  const chunks = [];
605
614
  const errChunks = [];
606
615
  const proc = spawn5(
@@ -619,7 +628,7 @@ async function defaultHermesRunner(adapterSessionId) {
619
628
  )
620
629
  );
621
630
  } else {
622
- resolve17(Buffer.concat(chunks).toString("utf8"));
631
+ resolve18(Buffer.concat(chunks).toString("utf8"));
623
632
  }
624
633
  });
625
634
  });
@@ -656,9 +665,9 @@ async function exportDaemonEventsSession(sessionId, desc) {
656
665
  let stream;
657
666
  try {
658
667
  stream = createReadStream(filePath, { encoding: "utf8" });
659
- await new Promise((resolve17, reject) => {
668
+ await new Promise((resolve18, reject) => {
660
669
  stream.once("error", reject);
661
- stream.once("open", resolve17);
670
+ stream.once("open", resolve18);
662
671
  });
663
672
  } catch (err) {
664
673
  const code = err.code;
@@ -691,6 +700,15 @@ Either the session never drove an agent-cli turn, or it predates this feature.`
691
700
  asmToolCalls = [];
692
701
  asmTs = void 0;
693
702
  };
703
+ let handoffStart;
704
+ const closeHandoffTurn = () => {
705
+ if (handoffStart === void 0) return;
706
+ for (let i = handoffStart; i < messages.length; i++) {
707
+ const m = messages[i];
708
+ if (m) m.internal = "handoff";
709
+ }
710
+ handoffStart = void 0;
711
+ };
694
712
  const rl = createInterface({ input: stream, crlfDelay: Infinity });
695
713
  for await (const line of rl) {
696
714
  const trimmed = line.trim();
@@ -737,10 +755,15 @@ ${rec.stderr}`);
737
755
  switch (rec.kind) {
738
756
  case "user-prompt":
739
757
  flushAssistant();
758
+ closeHandoffTurn();
759
+ if (rec.source === HANDOFF_PROMPT_SOURCE || rec.text?.startsWith(HANDOFF_PROMPT_OPENER)) {
760
+ handoffStart = messages.length;
761
+ }
740
762
  messages.push({ role: "user", text: rec.text ?? "", ...tsOrUndefined !== void 0 ? { ts: tsOrUndefined } : {} });
741
763
  break;
742
764
  case "session-message": {
743
765
  flushAssistant();
766
+ closeHandoffTurn();
744
767
  const m = rec.message;
745
768
  if (!m) break;
746
769
  const relation = m.from?.relation ?? "system";
@@ -760,7 +783,13 @@ ${rec.stderr}`);
760
783
  }
761
784
  case "system-prompt":
762
785
  flushAssistant();
763
- messages.push({ role: "system", text: rec.text ?? "", ...tsOrUndefined !== void 0 ? { ts: tsOrUndefined } : {} });
786
+ closeHandoffTurn();
787
+ messages.push({
788
+ role: "system",
789
+ text: rec.text ?? "",
790
+ internal: "preamble",
791
+ ...tsOrUndefined !== void 0 ? { ts: tsOrUndefined } : {}
792
+ });
764
793
  break;
765
794
  case "text-delta":
766
795
  if (asmTs === void 0) asmTs = tsOrUndefined;
@@ -820,6 +849,7 @@ ${rec.stderr}`);
820
849
  }
821
850
  }
822
851
  flushAssistant();
852
+ closeHandoffTurn();
823
853
  const meta = { source: "daemon-events" };
824
854
  if (desc?.label) meta.title = desc.label;
825
855
  else if (desc?.command) {
@@ -1540,6 +1570,7 @@ Only sessions spawned via claude-code or hermes can be exported.`
1540
1570
  var ROLE_ICON, IGNORED_CLAUDE_TYPES, NODE_SQLITE, EXPORT_STRATEGIES;
1541
1571
  var init_transcript_export = __esm({
1542
1572
  "src/transcript-export.ts"() {
1573
+ init_handoff_markers();
1543
1574
  init_tool_presenter();
1544
1575
  init_transcript_writer();
1545
1576
  init_conversation_store();
@@ -1983,9 +2014,15 @@ var init_bundles = __esm({
1983
2014
  });
1984
2015
  }
1985
2016
  });
2017
+ var init_eligibility_manifest = __esm({
2018
+ "src/eligibility-manifest.ts"() {
2019
+ init_spawn_defaults();
2020
+ }
2021
+ });
1986
2022
  var init_catalog_models = __esm({
1987
2023
  "src/catalog-models.ts"() {
1988
2024
  init_spawn_defaults();
2025
+ init_eligibility_manifest();
1989
2026
  }
1990
2027
  });
1991
2028
  var init_model_wire = __esm({
@@ -2249,6 +2286,7 @@ var init_session_spawn = __esm({
2249
2286
  init_bundles();
2250
2287
  init_mcp_imports();
2251
2288
  init_launch_config();
2289
+ init_eligibility_manifest();
2252
2290
  init_claude_code_oauth_source();
2253
2291
  init_providers_store();
2254
2292
  init_catalog_models();
@@ -2326,6 +2364,7 @@ var init_spawn_field_schemas = __esm({
2326
2364
  compactAtPct: z.number().int().min(0).max(100).optional(),
2327
2365
  continueFreshAtPct: z.number().int().min(0).max(100).optional(),
2328
2366
  hardStopAtPct: z.number().int().min(0).max(100).optional(),
2367
+ handoffAtQuotaRemaining: z.number().min(0).optional(),
2329
2368
  goal: z.boolean().optional(),
2330
2369
  plan: z.boolean().optional(),
2331
2370
  decisions: z.boolean().optional(),
@@ -2340,12 +2379,34 @@ var init_spawn_field_schemas = __esm({
2340
2379
  });
2341
2380
  }
2342
2381
  });
2382
+ function decodeWhsecSecret(secret) {
2383
+ if (typeof secret !== "string" || !secret.startsWith(WHSEC_PREFIX)) return null;
2384
+ const encoded = secret.slice(WHSEC_PREFIX.length);
2385
+ if (encoded.length === 0 || !/^[A-Za-z0-9+/]*={0,2}$/.test(encoded) || encoded.length % 4 !== 0) {
2386
+ return null;
2387
+ }
2388
+ let key;
2389
+ try {
2390
+ key = Buffer.from(encoded, "base64");
2391
+ } catch {
2392
+ return null;
2393
+ }
2394
+ if (key.length < 24 || key.length > 64) return null;
2395
+ return key;
2396
+ }
2397
+ var WHSEC_PREFIX;
2398
+ var init_signing = __esm({
2399
+ "src/webhook-egress/signing.ts"() {
2400
+ WHSEC_PREFIX = "whsec_";
2401
+ }
2402
+ });
2343
2403
  var mcpBool, mcpPositiveNumber, help, agentStartInputShape, agentStartInputSchema;
2344
2404
  var init_agent_start_schema = __esm({
2345
2405
  "src/agent-start-schema.ts"() {
2346
2406
  init_json_tolerant();
2347
2407
  init_sandbox_spec_schema();
2348
2408
  init_spawn_field_schemas();
2409
+ init_signing();
2349
2410
  mcpBool = z.preprocess(
2350
2411
  (v) => v === "true" ? true : v === "false" ? false : v,
2351
2412
  z.boolean()
@@ -2451,7 +2512,7 @@ var init_agent_start_schema = __esm({
2451
2512
  apiKey: z.string().optional()
2452
2513
  })
2453
2514
  ).optional().describe(
2454
- `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")}`
2515
+ `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")}`
2455
2516
  ),
2456
2517
  mcpServers: jsonTolerant(
2457
2518
  z.array(
@@ -2485,6 +2546,9 @@ var init_agent_start_schema = __esm({
2485
2546
  notifyUrl: z.string().url().optional().describe(
2486
2547
  "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."
2487
2548
  ),
2549
+ notifySecret: z.string().refine((s) => decodeWhsecSecret(s) !== null, {
2550
+ message: "must be a whsec_<base64> Standard Webhooks secret (24-64 decoded bytes)"
2551
+ }).optional().describe(`Standard Webhooks secret (\`whsec_...\`) signing \`notifyUrl\` POSTs. ${help("notifySecret")}`),
2488
2552
  wait: mcpBool.optional().describe(
2489
2553
  `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.`
2490
2554
  ),
@@ -2523,7 +2587,7 @@ var init_agent_start_schema = __esm({
2523
2587
  "One-off runtime text layered onto the resolved role's disposition and prepended to `prompt` \u2014 specializes it, cannot reopen a denied tool gate."
2524
2588
  ),
2525
2589
  deferredTools: z.boolean().optional().describe(
2526
- `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")}`
2590
+ `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")}`
2527
2591
  ),
2528
2592
  browser: z.preprocess(
2529
2593
  // `true` is sugar for the only mode; stringified booleans tolerated.
@@ -2661,6 +2725,12 @@ var init_session_activity = __esm({
2661
2725
  "src/session-activity.ts"() {
2662
2726
  }
2663
2727
  });
2728
+
2729
+ // src/handoff-suggestion.ts
2730
+ var init_handoff_suggestion = __esm({
2731
+ "src/handoff-suggestion.ts"() {
2732
+ }
2733
+ });
2664
2734
  var DEFAULT_BG_TASK_WAKE_GRACE_MS;
2665
2735
  var init_background_task_wake = __esm({
2666
2736
  "src/background-task-wake.ts"() {
@@ -2708,10 +2778,99 @@ var init_context_window = __esm({
2708
2778
  "src/context-window.ts"() {
2709
2779
  }
2710
2780
  });
2781
+ var CHECKPOINT_SCHEMA_VERSION, pct, contextCheckpointPolicySchema, contextCheckpointSectionsSchema, checkpointHandoffTurnSchema;
2782
+ var init_checkpoint_schema = __esm({
2783
+ "src/checkpoint-schema.ts"() {
2784
+ CHECKPOINT_SCHEMA_VERSION = 1;
2785
+ pct = z.number().min(0).max(100);
2786
+ contextCheckpointPolicySchema = z.looseObject({
2787
+ mode: z.enum(["manual", "ask", "auto"]),
2788
+ warnAtPct: pct,
2789
+ compactAtPct: pct,
2790
+ continueFreshAtPct: pct,
2791
+ hardStopAtPct: pct,
2792
+ goal: z.boolean(),
2793
+ plan: z.boolean(),
2794
+ decisions: z.boolean(),
2795
+ changedFiles: z.boolean(),
2796
+ gitStatus: z.boolean(),
2797
+ tests: z.boolean(),
2798
+ errors: z.boolean(),
2799
+ risks: z.boolean(),
2800
+ nextStep: z.boolean(),
2801
+ config: z.boolean(),
2802
+ label: z.string()
2803
+ });
2804
+ contextCheckpointSectionsSchema = z.looseObject({
2805
+ goal: z.string().optional(),
2806
+ plan: z.string().optional(),
2807
+ decisions: z.string().optional(),
2808
+ changedFiles: z.string().optional(),
2809
+ gitStatus: z.string().optional(),
2810
+ tests: z.string().optional(),
2811
+ errors: z.string().optional(),
2812
+ risks: z.string().optional(),
2813
+ nextStep: z.string().optional(),
2814
+ config: z.string().optional(),
2815
+ notes: z.string().optional().describe("Free-text notes the operator attached to the handoff.")
2816
+ });
2817
+ checkpointHandoffTurnSchema = z.looseObject({
2818
+ status: z.enum(["answered", "skipped", "failed"]).describe(
2819
+ "answered: the source session replied with a valid summary; skipped: not requested or no live idle session; failed: asked but timed out / unusable reply."
2820
+ ),
2821
+ reason: z.string().optional()
2822
+ });
2823
+ z.looseObject({
2824
+ schemaVersion: z.literal(CHECKPOINT_SCHEMA_VERSION),
2825
+ checkpointId: z.string(),
2826
+ sourceSessionId: z.string(),
2827
+ createdAt: z.string().describe("ISO 8601 creation timestamp."),
2828
+ contextPct: z.number().describe("Context percentage that triggered the checkpoint."),
2829
+ policy: contextCheckpointPolicySchema,
2830
+ sections: contextCheckpointSectionsSchema.describe(
2831
+ "Sections requested and present. A section with nothing real to say is omitted, never a placeholder."
2832
+ ),
2833
+ handoffTurn: checkpointHandoffTurnSchema.optional(),
2834
+ recentDigest: z.string(),
2835
+ originalTranscriptPath: z.string(),
2836
+ checkpointPath: z.string(),
2837
+ nextAction: z.enum(["continue", "compact_then_continue", "ask"])
2838
+ }).meta({
2839
+ title: "AgentProto context checkpoint",
2840
+ description: "Structured handoff document persisted next to a session's events.jsonl and used to seed a fresh continuation session."
2841
+ });
2842
+ }
2843
+ });
2844
+ var init_session_outcome = __esm({
2845
+ "src/session-outcome.ts"() {
2846
+ }
2847
+ });
2848
+ var nonEmpty;
2849
+ var init_checkpoint_extract = __esm({
2850
+ "src/checkpoint-extract.ts"() {
2851
+ init_session_outcome();
2852
+ init_supervisor();
2853
+ init_handoff_markers();
2854
+ init_transcript_writer();
2855
+ nonEmpty = z.string().trim().min(1);
2856
+ z.object({
2857
+ goal: nonEmpty.optional(),
2858
+ decisions: z.array(nonEmpty).default([]),
2859
+ tests: z.object({ command: z.string().default(""), result: z.string().default("") }).optional(),
2860
+ openRisks: z.array(nonEmpty).default([]),
2861
+ nextStep: nonEmpty.optional()
2862
+ }).refine((r) => r.goal || r.decisions.length || r.tests || r.openRisks.length || r.nextStep, {
2863
+ message: "handoff reply is empty"
2864
+ });
2865
+ }
2866
+ });
2711
2867
  var init_context_checkpoint = __esm({
2712
2868
  "src/context-checkpoint.ts"() {
2869
+ init_checkpoint_schema();
2870
+ init_checkpoint_extract();
2713
2871
  init_transcript_export();
2714
2872
  init_transcript_writer();
2873
+ init_context_continuity();
2715
2874
  }
2716
2875
  });
2717
2876
 
@@ -2724,16 +2883,17 @@ var init_session_continue_fresh = __esm({
2724
2883
  init_session_spawn();
2725
2884
  }
2726
2885
  });
2727
- var init_session_outcome = __esm({
2728
- "src/session-outcome.ts"() {
2729
- }
2730
- });
2731
2886
 
2732
2887
  // src/session-end-reason.ts
2733
2888
  var init_session_end_reason = __esm({
2734
2889
  "src/session-end-reason.ts"() {
2735
2890
  }
2736
2891
  });
2892
+ var init_session_index = __esm({
2893
+ "src/session-index.ts"() {
2894
+ init_transcript_writer();
2895
+ }
2896
+ });
2737
2897
  var init_sessions = __esm({
2738
2898
  "src/sessions.ts"() {
2739
2899
  init_resume_strategies();
@@ -2747,6 +2907,7 @@ var init_sessions = __esm({
2747
2907
  init_supervisor();
2748
2908
  init_session_title();
2749
2909
  init_session_activity();
2910
+ init_handoff_suggestion();
2750
2911
  init_canonical_posture();
2751
2912
  init_background_task_wake();
2752
2913
  init_catalog_models();
@@ -2767,6 +2928,7 @@ var init_sessions = __esm({
2767
2928
  init_session_continue_fresh();
2768
2929
  init_session_outcome();
2769
2930
  init_session_end_reason();
2931
+ init_session_index();
2770
2932
  init_session_message();
2771
2933
  }
2772
2934
  });
@@ -2822,6 +2984,7 @@ var init_config_schema = __esm({
2822
2984
  compactAtPct: z.number().optional(),
2823
2985
  continueFreshAtPct: z.number().optional(),
2824
2986
  hardStopAtPct: z.number().optional(),
2987
+ handoffAtQuotaRemaining: z.number().min(0).optional(),
2825
2988
  goal: z.boolean().optional(),
2826
2989
  plan: z.boolean().optional(),
2827
2990
  decisions: z.boolean().optional(),
@@ -2875,7 +3038,8 @@ var init_config_schema = __esm({
2875
3038
  agentPromptInterrupt: z.boolean().optional(),
2876
3039
  messaging: z.object({
2877
3040
  allowSiblings: z.boolean().optional(),
2878
- agentInterrupt: z.enum(["allow", "deny"]).optional()
3041
+ agentInterrupt: z.enum(["allow", "deny"]).optional(),
3042
+ pendingPromptStaleMinutes: z.number().positive().optional()
2879
3043
  }).passthrough().optional(),
2880
3044
  mcp: mcpDefaultsConfigSchema.optional(),
2881
3045
  spawn: z.object({ browser: spawnBrowserModeSchema.optional() }).passthrough().optional()
@@ -2900,7 +3064,8 @@ var init_config_schema = __esm({
2900
3064
  model: z.string().optional()
2901
3065
  }).passthrough();
2902
3066
  catalogConfigSchema = z.object({
2903
- sources: z.array(z.object({ url: z.string() }).passthrough()).optional()
3067
+ sources: z.array(z.object({ url: z.string() }).passthrough()).optional(),
3068
+ defaultSource: z.union([z.string(), z.literal(false)]).optional()
2904
3069
  }).passthrough();
2905
3070
  tunnelConfigSchema = z.object({
2906
3071
  host: z.string().optional(),
@@ -3280,7 +3445,16 @@ var init_config_schema = __esm({
3280
3445
  writable: true,
3281
3446
  section: "daemon",
3282
3447
  label: "App catalog sources",
3283
- 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."
3448
+ 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."
3449
+ },
3450
+ {
3451
+ path: "catalog.defaultSource",
3452
+ schema: z.union([str, z.literal(false)]),
3453
+ apply: "hot",
3454
+ writable: true,
3455
+ section: "daemon",
3456
+ label: "Default app catalog",
3457
+ 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."
3284
3458
  },
3285
3459
  // ── defaults: spawn ──
3286
3460
  {
@@ -3547,6 +3721,16 @@ var init_config_schema = __esm({
3547
3721
  help: 'Whether a SESSION sender may use urgency "interrupt" (vs. downgraded to "steer"). Takes effect on the next call.',
3548
3722
  default: "deny"
3549
3723
  },
3724
+ {
3725
+ path: "defaults.messaging.pendingPromptStaleMinutes",
3726
+ schema: z.number().positive(),
3727
+ apply: "hot",
3728
+ writable: true,
3729
+ section: "defaults",
3730
+ label: "Pending prompt staleness (minutes)",
3731
+ 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.",
3732
+ default: 5
3733
+ },
3550
3734
  // ── harnesses: per-adapter defaults ──
3551
3735
  {
3552
3736
  path: "defaults.adapters.*.skills",