@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,5 +1,5 @@
1
1
  import { CostBudget } from '@agentproto/auth';
2
- import { C as ContextContinuityPolicy } from './spawn-defaults-Cp9SGZex.js';
2
+ import { C as ContextContinuityPolicy } from './spawn-defaults-Dm7nbxcP.js';
3
3
 
4
4
  /**
5
5
  * SessionConfig — the unified per-session config surface, decomposed into
@@ -93,6 +93,15 @@ var init_pr_provenance = __esm({
93
93
  }
94
94
  });
95
95
 
96
+ // src/handoff-markers.ts
97
+ var HANDOFF_PROMPT_SOURCE, HANDOFF_PROMPT_OPENER;
98
+ var init_handoff_markers = __esm({
99
+ "src/handoff-markers.ts"() {
100
+ HANDOFF_PROMPT_SOURCE = "daemon:handoff";
101
+ HANDOFF_PROMPT_OPENER = "[handoff request from the agentproto daemon]";
102
+ }
103
+ });
104
+
96
105
  // src/tool-presenter.ts
97
106
  function isRecord(value) {
98
107
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -314,9 +323,9 @@ async function exportClaudeCodeSession(adapterSessionId, cwd, configDir) {
314
323
  let stream;
315
324
  try {
316
325
  stream = createReadStream(filePath, { encoding: "utf8" });
317
- await new Promise((resolve17, reject) => {
326
+ await new Promise((resolve18, reject) => {
318
327
  stream.once("error", reject);
319
- stream.once("open", resolve17);
328
+ stream.once("open", resolve18);
320
329
  });
321
330
  } catch (err) {
322
331
  const code = err.code;
@@ -590,7 +599,7 @@ async function discoverHermesSessions(cwd, since, expectedId) {
590
599
  }
591
600
  async function defaultHermesRunner(adapterSessionId) {
592
601
  const { spawn: spawn5 } = await import('child_process');
593
- return new Promise((resolve17, reject) => {
602
+ return new Promise((resolve18, reject) => {
594
603
  const chunks = [];
595
604
  const errChunks = [];
596
605
  const proc = spawn5(
@@ -609,7 +618,7 @@ async function defaultHermesRunner(adapterSessionId) {
609
618
  )
610
619
  );
611
620
  } else {
612
- resolve17(Buffer.concat(chunks).toString("utf8"));
621
+ resolve18(Buffer.concat(chunks).toString("utf8"));
613
622
  }
614
623
  });
615
624
  });
@@ -646,9 +655,9 @@ async function exportDaemonEventsSession(sessionId, desc) {
646
655
  let stream;
647
656
  try {
648
657
  stream = createReadStream(filePath, { encoding: "utf8" });
649
- await new Promise((resolve17, reject) => {
658
+ await new Promise((resolve18, reject) => {
650
659
  stream.once("error", reject);
651
- stream.once("open", resolve17);
660
+ stream.once("open", resolve18);
652
661
  });
653
662
  } catch (err) {
654
663
  const code = err.code;
@@ -681,6 +690,15 @@ Either the session never drove an agent-cli turn, or it predates this feature.`
681
690
  asmToolCalls = [];
682
691
  asmTs = void 0;
683
692
  };
693
+ let handoffStart;
694
+ const closeHandoffTurn = () => {
695
+ if (handoffStart === void 0) return;
696
+ for (let i = handoffStart; i < messages.length; i++) {
697
+ const m = messages[i];
698
+ if (m) m.internal = "handoff";
699
+ }
700
+ handoffStart = void 0;
701
+ };
684
702
  const rl = createInterface({ input: stream, crlfDelay: Infinity });
685
703
  for await (const line of rl) {
686
704
  const trimmed = line.trim();
@@ -727,10 +745,15 @@ ${rec.stderr}`);
727
745
  switch (rec.kind) {
728
746
  case "user-prompt":
729
747
  flushAssistant();
748
+ closeHandoffTurn();
749
+ if (rec.source === HANDOFF_PROMPT_SOURCE || rec.text?.startsWith(HANDOFF_PROMPT_OPENER)) {
750
+ handoffStart = messages.length;
751
+ }
730
752
  messages.push({ role: "user", text: rec.text ?? "", ...tsOrUndefined !== void 0 ? { ts: tsOrUndefined } : {} });
731
753
  break;
732
754
  case "session-message": {
733
755
  flushAssistant();
756
+ closeHandoffTurn();
734
757
  const m = rec.message;
735
758
  if (!m) break;
736
759
  const relation = m.from?.relation ?? "system";
@@ -750,7 +773,13 @@ ${rec.stderr}`);
750
773
  }
751
774
  case "system-prompt":
752
775
  flushAssistant();
753
- messages.push({ role: "system", text: rec.text ?? "", ...tsOrUndefined !== void 0 ? { ts: tsOrUndefined } : {} });
776
+ closeHandoffTurn();
777
+ messages.push({
778
+ role: "system",
779
+ text: rec.text ?? "",
780
+ internal: "preamble",
781
+ ...tsOrUndefined !== void 0 ? { ts: tsOrUndefined } : {}
782
+ });
754
783
  break;
755
784
  case "text-delta":
756
785
  if (asmTs === void 0) asmTs = tsOrUndefined;
@@ -810,6 +839,7 @@ ${rec.stderr}`);
810
839
  }
811
840
  }
812
841
  flushAssistant();
842
+ closeHandoffTurn();
813
843
  const meta = { source: "daemon-events" };
814
844
  if (desc?.label) meta.title = desc.label;
815
845
  else if (desc?.command) {
@@ -1530,6 +1560,7 @@ Only sessions spawned via claude-code or hermes can be exported.`
1530
1560
  var ROLE_ICON, IGNORED_CLAUDE_TYPES, NODE_SQLITE, EXPORT_STRATEGIES;
1531
1561
  var init_transcript_export = __esm({
1532
1562
  "src/transcript-export.ts"() {
1563
+ init_handoff_markers();
1533
1564
  init_tool_presenter();
1534
1565
  init_transcript_writer();
1535
1566
  init_conversation_store();
@@ -1973,9 +2004,15 @@ var init_bundles = __esm({
1973
2004
  });
1974
2005
  }
1975
2006
  });
2007
+ var init_eligibility_manifest = __esm({
2008
+ "src/eligibility-manifest.ts"() {
2009
+ init_spawn_defaults();
2010
+ }
2011
+ });
1976
2012
  var init_catalog_models = __esm({
1977
2013
  "src/catalog-models.ts"() {
1978
2014
  init_spawn_defaults();
2015
+ init_eligibility_manifest();
1979
2016
  }
1980
2017
  });
1981
2018
  var init_model_wire = __esm({
@@ -2239,6 +2276,7 @@ var init_session_spawn = __esm({
2239
2276
  init_bundles();
2240
2277
  init_mcp_imports();
2241
2278
  init_launch_config();
2279
+ init_eligibility_manifest();
2242
2280
  init_claude_code_oauth_source();
2243
2281
  init_providers_store();
2244
2282
  init_catalog_models();
@@ -2316,6 +2354,7 @@ var init_spawn_field_schemas = __esm({
2316
2354
  compactAtPct: z.number().int().min(0).max(100).optional(),
2317
2355
  continueFreshAtPct: z.number().int().min(0).max(100).optional(),
2318
2356
  hardStopAtPct: z.number().int().min(0).max(100).optional(),
2357
+ handoffAtQuotaRemaining: z.number().min(0).optional(),
2319
2358
  goal: z.boolean().optional(),
2320
2359
  plan: z.boolean().optional(),
2321
2360
  decisions: z.boolean().optional(),
@@ -2330,12 +2369,34 @@ var init_spawn_field_schemas = __esm({
2330
2369
  });
2331
2370
  }
2332
2371
  });
2372
+ function decodeWhsecSecret(secret) {
2373
+ if (typeof secret !== "string" || !secret.startsWith(WHSEC_PREFIX)) return null;
2374
+ const encoded = secret.slice(WHSEC_PREFIX.length);
2375
+ if (encoded.length === 0 || !/^[A-Za-z0-9+/]*={0,2}$/.test(encoded) || encoded.length % 4 !== 0) {
2376
+ return null;
2377
+ }
2378
+ let key;
2379
+ try {
2380
+ key = Buffer.from(encoded, "base64");
2381
+ } catch {
2382
+ return null;
2383
+ }
2384
+ if (key.length < 24 || key.length > 64) return null;
2385
+ return key;
2386
+ }
2387
+ var WHSEC_PREFIX;
2388
+ var init_signing = __esm({
2389
+ "src/webhook-egress/signing.ts"() {
2390
+ WHSEC_PREFIX = "whsec_";
2391
+ }
2392
+ });
2333
2393
  var mcpBool, mcpPositiveNumber, help, agentStartInputShape, agentStartInputSchema;
2334
2394
  var init_agent_start_schema = __esm({
2335
2395
  "src/agent-start-schema.ts"() {
2336
2396
  init_json_tolerant();
2337
2397
  init_sandbox_spec_schema();
2338
2398
  init_spawn_field_schemas();
2399
+ init_signing();
2339
2400
  mcpBool = z.preprocess(
2340
2401
  (v) => v === "true" ? true : v === "false" ? false : v,
2341
2402
  z.boolean()
@@ -2441,7 +2502,7 @@ var init_agent_start_schema = __esm({
2441
2502
  apiKey: z.string().optional()
2442
2503
  })
2443
2504
  ).optional().describe(
2444
- `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")}`
2505
+ `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")}`
2445
2506
  ),
2446
2507
  mcpServers: jsonTolerant(
2447
2508
  z.array(
@@ -2475,6 +2536,9 @@ var init_agent_start_schema = __esm({
2475
2536
  notifyUrl: z.string().url().optional().describe(
2476
2537
  "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."
2477
2538
  ),
2539
+ notifySecret: z.string().refine((s) => decodeWhsecSecret(s) !== null, {
2540
+ message: "must be a whsec_<base64> Standard Webhooks secret (24-64 decoded bytes)"
2541
+ }).optional().describe(`Standard Webhooks secret (\`whsec_...\`) signing \`notifyUrl\` POSTs. ${help("notifySecret")}`),
2478
2542
  wait: mcpBool.optional().describe(
2479
2543
  `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.`
2480
2544
  ),
@@ -2513,7 +2577,7 @@ var init_agent_start_schema = __esm({
2513
2577
  "One-off runtime text layered onto the resolved role's disposition and prepended to `prompt` \u2014 specializes it, cannot reopen a denied tool gate."
2514
2578
  ),
2515
2579
  deferredTools: z.boolean().optional().describe(
2516
- `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")}`
2580
+ `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")}`
2517
2581
  ),
2518
2582
  browser: z.preprocess(
2519
2583
  // `true` is sugar for the only mode; stringified booleans tolerated.
@@ -2651,6 +2715,12 @@ var init_session_activity = __esm({
2651
2715
  "src/session-activity.ts"() {
2652
2716
  }
2653
2717
  });
2718
+
2719
+ // src/handoff-suggestion.ts
2720
+ var init_handoff_suggestion = __esm({
2721
+ "src/handoff-suggestion.ts"() {
2722
+ }
2723
+ });
2654
2724
  var DEFAULT_BG_TASK_WAKE_GRACE_MS;
2655
2725
  var init_background_task_wake = __esm({
2656
2726
  "src/background-task-wake.ts"() {
@@ -2698,10 +2768,99 @@ var init_context_window = __esm({
2698
2768
  "src/context-window.ts"() {
2699
2769
  }
2700
2770
  });
2771
+ var CHECKPOINT_SCHEMA_VERSION, pct, contextCheckpointPolicySchema, contextCheckpointSectionsSchema, checkpointHandoffTurnSchema;
2772
+ var init_checkpoint_schema = __esm({
2773
+ "src/checkpoint-schema.ts"() {
2774
+ CHECKPOINT_SCHEMA_VERSION = 1;
2775
+ pct = z.number().min(0).max(100);
2776
+ contextCheckpointPolicySchema = z.looseObject({
2777
+ mode: z.enum(["manual", "ask", "auto"]),
2778
+ warnAtPct: pct,
2779
+ compactAtPct: pct,
2780
+ continueFreshAtPct: pct,
2781
+ hardStopAtPct: pct,
2782
+ goal: z.boolean(),
2783
+ plan: z.boolean(),
2784
+ decisions: z.boolean(),
2785
+ changedFiles: z.boolean(),
2786
+ gitStatus: z.boolean(),
2787
+ tests: z.boolean(),
2788
+ errors: z.boolean(),
2789
+ risks: z.boolean(),
2790
+ nextStep: z.boolean(),
2791
+ config: z.boolean(),
2792
+ label: z.string()
2793
+ });
2794
+ contextCheckpointSectionsSchema = z.looseObject({
2795
+ goal: z.string().optional(),
2796
+ plan: z.string().optional(),
2797
+ decisions: z.string().optional(),
2798
+ changedFiles: z.string().optional(),
2799
+ gitStatus: z.string().optional(),
2800
+ tests: z.string().optional(),
2801
+ errors: z.string().optional(),
2802
+ risks: z.string().optional(),
2803
+ nextStep: z.string().optional(),
2804
+ config: z.string().optional(),
2805
+ notes: z.string().optional().describe("Free-text notes the operator attached to the handoff.")
2806
+ });
2807
+ checkpointHandoffTurnSchema = z.looseObject({
2808
+ status: z.enum(["answered", "skipped", "failed"]).describe(
2809
+ "answered: the source session replied with a valid summary; skipped: not requested or no live idle session; failed: asked but timed out / unusable reply."
2810
+ ),
2811
+ reason: z.string().optional()
2812
+ });
2813
+ z.looseObject({
2814
+ schemaVersion: z.literal(CHECKPOINT_SCHEMA_VERSION),
2815
+ checkpointId: z.string(),
2816
+ sourceSessionId: z.string(),
2817
+ createdAt: z.string().describe("ISO 8601 creation timestamp."),
2818
+ contextPct: z.number().describe("Context percentage that triggered the checkpoint."),
2819
+ policy: contextCheckpointPolicySchema,
2820
+ sections: contextCheckpointSectionsSchema.describe(
2821
+ "Sections requested and present. A section with nothing real to say is omitted, never a placeholder."
2822
+ ),
2823
+ handoffTurn: checkpointHandoffTurnSchema.optional(),
2824
+ recentDigest: z.string(),
2825
+ originalTranscriptPath: z.string(),
2826
+ checkpointPath: z.string(),
2827
+ nextAction: z.enum(["continue", "compact_then_continue", "ask"])
2828
+ }).meta({
2829
+ title: "AgentProto context checkpoint",
2830
+ description: "Structured handoff document persisted next to a session's events.jsonl and used to seed a fresh continuation session."
2831
+ });
2832
+ }
2833
+ });
2834
+ var init_session_outcome = __esm({
2835
+ "src/session-outcome.ts"() {
2836
+ }
2837
+ });
2838
+ var nonEmpty;
2839
+ var init_checkpoint_extract = __esm({
2840
+ "src/checkpoint-extract.ts"() {
2841
+ init_session_outcome();
2842
+ init_supervisor();
2843
+ init_handoff_markers();
2844
+ init_transcript_writer();
2845
+ nonEmpty = z.string().trim().min(1);
2846
+ z.object({
2847
+ goal: nonEmpty.optional(),
2848
+ decisions: z.array(nonEmpty).default([]),
2849
+ tests: z.object({ command: z.string().default(""), result: z.string().default("") }).optional(),
2850
+ openRisks: z.array(nonEmpty).default([]),
2851
+ nextStep: nonEmpty.optional()
2852
+ }).refine((r) => r.goal || r.decisions.length || r.tests || r.openRisks.length || r.nextStep, {
2853
+ message: "handoff reply is empty"
2854
+ });
2855
+ }
2856
+ });
2701
2857
  var init_context_checkpoint = __esm({
2702
2858
  "src/context-checkpoint.ts"() {
2859
+ init_checkpoint_schema();
2860
+ init_checkpoint_extract();
2703
2861
  init_transcript_export();
2704
2862
  init_transcript_writer();
2863
+ init_context_continuity();
2705
2864
  }
2706
2865
  });
2707
2866
 
@@ -2714,16 +2873,17 @@ var init_session_continue_fresh = __esm({
2714
2873
  init_session_spawn();
2715
2874
  }
2716
2875
  });
2717
- var init_session_outcome = __esm({
2718
- "src/session-outcome.ts"() {
2719
- }
2720
- });
2721
2876
 
2722
2877
  // src/session-end-reason.ts
2723
2878
  var init_session_end_reason = __esm({
2724
2879
  "src/session-end-reason.ts"() {
2725
2880
  }
2726
2881
  });
2882
+ var init_session_index = __esm({
2883
+ "src/session-index.ts"() {
2884
+ init_transcript_writer();
2885
+ }
2886
+ });
2727
2887
  var init_sessions = __esm({
2728
2888
  "src/sessions.ts"() {
2729
2889
  init_resume_strategies();
@@ -2737,6 +2897,7 @@ var init_sessions = __esm({
2737
2897
  init_supervisor();
2738
2898
  init_session_title();
2739
2899
  init_session_activity();
2900
+ init_handoff_suggestion();
2740
2901
  init_canonical_posture();
2741
2902
  init_background_task_wake();
2742
2903
  init_catalog_models();
@@ -2757,6 +2918,7 @@ var init_sessions = __esm({
2757
2918
  init_session_continue_fresh();
2758
2919
  init_session_outcome();
2759
2920
  init_session_end_reason();
2921
+ init_session_index();
2760
2922
  init_session_message();
2761
2923
  }
2762
2924
  });
@@ -2812,6 +2974,7 @@ var init_config_schema = __esm({
2812
2974
  compactAtPct: z.number().optional(),
2813
2975
  continueFreshAtPct: z.number().optional(),
2814
2976
  hardStopAtPct: z.number().optional(),
2977
+ handoffAtQuotaRemaining: z.number().min(0).optional(),
2815
2978
  goal: z.boolean().optional(),
2816
2979
  plan: z.boolean().optional(),
2817
2980
  decisions: z.boolean().optional(),
@@ -2865,7 +3028,8 @@ var init_config_schema = __esm({
2865
3028
  agentPromptInterrupt: z.boolean().optional(),
2866
3029
  messaging: z.object({
2867
3030
  allowSiblings: z.boolean().optional(),
2868
- agentInterrupt: z.enum(["allow", "deny"]).optional()
3031
+ agentInterrupt: z.enum(["allow", "deny"]).optional(),
3032
+ pendingPromptStaleMinutes: z.number().positive().optional()
2869
3033
  }).passthrough().optional(),
2870
3034
  mcp: mcpDefaultsConfigSchema.optional(),
2871
3035
  spawn: z.object({ browser: spawnBrowserModeSchema.optional() }).passthrough().optional()
@@ -2890,7 +3054,8 @@ var init_config_schema = __esm({
2890
3054
  model: z.string().optional()
2891
3055
  }).passthrough();
2892
3056
  catalogConfigSchema = z.object({
2893
- sources: z.array(z.object({ url: z.string() }).passthrough()).optional()
3057
+ sources: z.array(z.object({ url: z.string() }).passthrough()).optional(),
3058
+ defaultSource: z.union([z.string(), z.literal(false)]).optional()
2894
3059
  }).passthrough();
2895
3060
  tunnelConfigSchema = z.object({
2896
3061
  host: z.string().optional(),
@@ -3270,7 +3435,16 @@ var init_config_schema = __esm({
3270
3435
  writable: true,
3271
3436
  section: "daemon",
3272
3437
  label: "App catalog sources",
3273
- 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."
3438
+ 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."
3439
+ },
3440
+ {
3441
+ path: "catalog.defaultSource",
3442
+ schema: z.union([str, z.literal(false)]),
3443
+ apply: "hot",
3444
+ writable: true,
3445
+ section: "daemon",
3446
+ label: "Default app catalog",
3447
+ 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."
3274
3448
  },
3275
3449
  // ── defaults: spawn ──
3276
3450
  {
@@ -3537,6 +3711,16 @@ var init_config_schema = __esm({
3537
3711
  help: 'Whether a SESSION sender may use urgency "interrupt" (vs. downgraded to "steer"). Takes effect on the next call.',
3538
3712
  default: "deny"
3539
3713
  },
3714
+ {
3715
+ path: "defaults.messaging.pendingPromptStaleMinutes",
3716
+ schema: z.number().positive(),
3717
+ apply: "hot",
3718
+ writable: true,
3719
+ section: "defaults",
3720
+ label: "Pending prompt staleness (minutes)",
3721
+ 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.",
3722
+ default: 5
3723
+ },
3540
3724
  // ── harnesses: per-adapter defaults ──
3541
3725
  {
3542
3726
  path: "defaults.adapters.*.skills",