@agentproto/runtime 5.1.0 → 5.3.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.mjs CHANGED
@@ -11,11 +11,12 @@ import '@agentproto/provider-presets';
11
11
  import '@agentproto/secrets/provision/recipe';
12
12
  import { z } from 'zod';
13
13
  import '@agentproto/auth';
14
- import 'child_process';
15
14
  import '@agentproto/command-sandbox';
15
+ import 'child_process';
16
16
  import '@agentproto/plugin-local-browser';
17
17
  import '@modelcontextprotocol/sdk/types.js';
18
18
  import '@agentproto/provider-kit';
19
+ import '@agentproto/llm-endpoint';
19
20
  import '@modelcontextprotocol/sdk/client/index.js';
20
21
  import '@modelcontextprotocol/sdk/client/streamableHttp.js';
21
22
  import 'net';
@@ -56,11 +57,13 @@ var __copyProps = (to, from, except, desc) => {
56
57
  var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget);
57
58
 
58
59
  // src/worktree-isolation.ts
59
- var WORKTREE_ISOLATION_ENV, DEFAULT_WORKTREE_ISOLATION;
60
+ var WORKTREE_ISOLATION_ENV, WORKTREE_PROVISION_CONCURRENCY_ENV, DEFAULT_WORKTREE_PROVISION_CONCURRENCY, DEFAULT_WORKTREE_ISOLATION;
60
61
  var init_worktree_isolation = __esm({
61
62
  "src/worktree-isolation.ts"() {
62
63
  init_config();
63
64
  WORKTREE_ISOLATION_ENV = "AGENTPROTO_WORKTREES_ISOLATION";
65
+ WORKTREE_PROVISION_CONCURRENCY_ENV = "AGENTPROTO_WORKTREES_PROVISION_CONCURRENCY";
66
+ DEFAULT_WORKTREE_PROVISION_CONCURRENCY = 2;
64
67
  DEFAULT_WORKTREE_ISOLATION = "on-request";
65
68
  }
66
69
  });
@@ -318,9 +321,9 @@ async function exportClaudeCodeSession(adapterSessionId, cwd, configDir) {
318
321
  let stream;
319
322
  try {
320
323
  stream = createReadStream(filePath, { encoding: "utf8" });
321
- await new Promise((resolve16, reject) => {
324
+ await new Promise((resolve17, reject) => {
322
325
  stream.once("error", reject);
323
- stream.once("open", resolve16);
326
+ stream.once("open", resolve17);
324
327
  });
325
328
  } catch (err) {
326
329
  const code = err.code;
@@ -594,7 +597,7 @@ async function discoverHermesSessions(cwd, since, expectedId) {
594
597
  }
595
598
  async function defaultHermesRunner(adapterSessionId) {
596
599
  const { spawn: spawn5 } = await import('child_process');
597
- return new Promise((resolve16, reject) => {
600
+ return new Promise((resolve17, reject) => {
598
601
  const chunks = [];
599
602
  const errChunks = [];
600
603
  const proc = spawn5(
@@ -613,7 +616,7 @@ async function defaultHermesRunner(adapterSessionId) {
613
616
  )
614
617
  );
615
618
  } else {
616
- resolve16(Buffer.concat(chunks).toString("utf8"));
619
+ resolve17(Buffer.concat(chunks).toString("utf8"));
617
620
  }
618
621
  });
619
622
  });
@@ -650,9 +653,9 @@ async function exportDaemonEventsSession(sessionId, desc) {
650
653
  let stream;
651
654
  try {
652
655
  stream = createReadStream(filePath, { encoding: "utf8" });
653
- await new Promise((resolve16, reject) => {
656
+ await new Promise((resolve17, reject) => {
654
657
  stream.once("error", reject);
655
- stream.once("open", resolve16);
658
+ stream.once("open", resolve17);
656
659
  });
657
660
  } catch (err) {
658
661
  const code = err.code;
@@ -1923,6 +1926,10 @@ var init_pr_provenance_stamp = __esm({
1923
1926
  init_pr_provenance();
1924
1927
  }
1925
1928
  });
1929
+ var init_app_boundary = __esm({
1930
+ "src/app-boundary.ts"() {
1931
+ }
1932
+ });
1926
1933
  var init_command_allowlist = __esm({
1927
1934
  "src/command-allowlist.ts"() {
1928
1935
  }
@@ -1931,10 +1938,16 @@ var init_command_tools = __esm({
1931
1938
  "src/command-tools.ts"() {
1932
1939
  init_sessions();
1933
1940
  init_pr_provenance_stamp();
1941
+ init_app_boundary();
1934
1942
  init_command_allowlist();
1935
1943
  init_command_allowlist();
1936
1944
  }
1937
1945
  });
1946
+ var init_process_stats = __esm({
1947
+ "src/process-stats.ts"() {
1948
+ z.preprocess((v) => v === "true" ? true : v === "false" ? false : v, z.union([z.boolean(), z.literal("full")])).optional();
1949
+ }
1950
+ });
1938
1951
  var init_workspaces_config = __esm({
1939
1952
  "src/workspaces-config.ts"() {
1940
1953
  }
@@ -1957,7 +1970,7 @@ var init_bundles = __esm({
1957
1970
  id: bundleIdSchema,
1958
1971
  label: z.string().min(1),
1959
1972
  description: z.string().min(1).optional(),
1960
- mcpImports: z.array(z.string().min(1)),
1973
+ mcpImports: z.union([z.array(z.string().min(1)), z.literal("*")]),
1961
1974
  includeDaemon: z.boolean().optional(),
1962
1975
  skills: z.array(z.string().min(1))
1963
1976
  });
@@ -1986,6 +1999,12 @@ var init_launch_config = __esm({
1986
1999
  }
1987
2000
  });
1988
2001
 
2002
+ // src/adapter-slug-hint.ts
2003
+ var init_adapter_slug_hint = __esm({
2004
+ "src/adapter-slug-hint.ts"() {
2005
+ }
2006
+ });
2007
+
1989
2008
  // src/context-continuity.ts
1990
2009
  var init_context_continuity = __esm({
1991
2010
  "src/context-continuity.ts"() {
@@ -2019,6 +2038,41 @@ var init_canonical_posture = __esm({
2019
2038
  })();
2020
2039
  }
2021
2040
  });
2041
+ var effortSchema, postureSchema, routeSchema, userPresetSchema;
2042
+ var init_user_presets = __esm({
2043
+ "src/user-presets.ts"() {
2044
+ effortSchema = z.enum(["low", "medium", "high", "xhigh", "max", "ultracode"]);
2045
+ postureSchema = z.union([
2046
+ z.enum(["default", "plan", "accept-edits", "bypass", "read-only"]),
2047
+ z.object({ harnessModeId: z.string().min(1) })
2048
+ ]);
2049
+ routeSchema = z.object({
2050
+ gateway: z.string().min(1),
2051
+ baseUrl: z.string().url().optional()
2052
+ });
2053
+ userPresetSchema = z.object({
2054
+ id: z.string().regex(/^[a-z0-9][a-z0-9-]*$/),
2055
+ label: z.string().min(1),
2056
+ adapter: z.string().min(1).optional(),
2057
+ harness: z.string().min(1).optional(),
2058
+ model: z.string().min(1).optional(),
2059
+ route: routeSchema.optional(),
2060
+ access: z.object({ profileRef: z.string().min(1).optional() }).optional(),
2061
+ posture: postureSchema.optional(),
2062
+ effort: effortSchema.optional(),
2063
+ contextProfile: z.string().min(1).optional(),
2064
+ cwd: z.string().min(1).optional(),
2065
+ skills: z.array(z.string().min(1)).optional(),
2066
+ bundles: z.array(z.string().min(1)).optional(),
2067
+ browser: z.union([z.literal("headless"), z.literal(false)]).optional(),
2068
+ lastUsedAt: z.string().min(1).optional()
2069
+ });
2070
+ z.object({
2071
+ version: z.literal(1),
2072
+ presets: z.array(userPresetSchema)
2073
+ });
2074
+ }
2075
+ });
2022
2076
  var harnessPresetSchema;
2023
2077
  var init_harness_preset_store = __esm({
2024
2078
  "src/harness-preset-store.ts"() {
@@ -2119,6 +2173,10 @@ var init_session_title = __esm({
2119
2173
  "src/session-title.ts"() {
2120
2174
  }
2121
2175
  });
2176
+ var init_inference_binding = __esm({
2177
+ "src/inference-binding.ts"() {
2178
+ }
2179
+ });
2122
2180
 
2123
2181
  // src/mcp-credential-deps.ts
2124
2182
  var init_mcp_credential_deps = __esm({
@@ -2169,6 +2227,7 @@ var init_sandbox_adapters = __esm({
2169
2227
  });
2170
2228
  var init_session_spawn = __esm({
2171
2229
  "src/session-spawn.ts"() {
2230
+ init_process_stats();
2172
2231
  init_sessions();
2173
2232
  init_transcript_writer();
2174
2233
  init_workspaces_config();
@@ -2181,8 +2240,10 @@ var init_session_spawn = __esm({
2181
2240
  init_claude_code_oauth_source();
2182
2241
  init_providers_store();
2183
2242
  init_catalog_models();
2243
+ init_adapter_slug_hint();
2184
2244
  init_context_continuity();
2185
2245
  init_canonical_posture();
2246
+ init_user_presets();
2186
2247
  init_harness_preset_store();
2187
2248
  init_browser_mount();
2188
2249
  init_role();
@@ -2191,6 +2252,8 @@ var init_session_spawn = __esm({
2191
2252
  init_agents_md();
2192
2253
  init_workspace_rules();
2193
2254
  init_session_title();
2255
+ init_app_boundary();
2256
+ init_inference_binding();
2194
2257
  init_mcp_credential_deps();
2195
2258
  init_sandbox_agent_session_proxy();
2196
2259
  init_sandbox_ledger();
@@ -2354,6 +2417,16 @@ var init_agent_start_schema = __esm({
2354
2417
  route: jsonTolerant(
2355
2418
  z.object({ gateway: z.string().min(1), baseUrl: z.string().url().optional() })
2356
2419
  ).optional().describe("Billing route/gateway. Independent of `model` and `access`."),
2420
+ inference: jsonTolerant(
2421
+ z.object({
2422
+ endpoint: z.string().min(1).optional().describe('A registered local/LAN inference endpoint id (`agentproto llm endpoints list`), or "<id>@<device>" for a paired device\'s own shared endpoint.'),
2423
+ model: z.string().min(1).optional().describe('A bare model id (paired with `endpoint`), or the shorthand "<model>@<device|endpoint>" when `endpoint` is omitted.'),
2424
+ force: mcpBool.optional().describe("Skip the fit-check refusal on a clear miss (the check still runs and still warns)."),
2425
+ headroomPct: z.number().min(0).max(95).optional().describe("Headroom percentage held back from the endpoint's loaded ctx for the fit check. Default 25.")
2426
+ })
2427
+ ).optional().describe(
2428
+ `Bind this spawn to a local/LAN inference endpoint instead of a cloud model: \`{endpoint}\`, \`{endpoint, model}\`, or the shorthand \`{model: "<model>@<device|endpoint>"}\`. Runs a fit check (the harness's known first-request size vs the endpoint's loaded ctx) BEFORE spawning and refuses with an actionable error on a clear miss (\`force\` overrides). Defaults \`adapter\`/\`harness\` to "pi" when neither is set. Mutually exclusive with \`model\`/\`route\`/\`access\` \u2014 set those directly instead of \`inference\`, never both. ${help("inference")}`
2429
+ ),
2357
2430
  access: jsonTolerant(z.object({ profileRef: z.string().min(1).optional() })).optional().describe("Named auth profile to bill at initial spawn; resolved from the local keychain."),
2358
2431
  posture: z.string().optional().describe("Canonical agent posture (plan, bypass, accept-edits, read-only) or native harness mode."),
2359
2432
  contextProfile: z.string().optional().describe("Context intake profile (for example full or lean)."),
@@ -2492,40 +2565,6 @@ var init_agent_start_schema = __esm({
2492
2565
  agentStartInputSchema.omit({ wait: true });
2493
2566
  }
2494
2567
  });
2495
- var effortSchema, postureSchema, routeSchema, userPresetSchema;
2496
- var init_user_presets = __esm({
2497
- "src/user-presets.ts"() {
2498
- effortSchema = z.enum(["low", "medium", "high", "xhigh", "max", "ultracode"]);
2499
- postureSchema = z.union([
2500
- z.enum(["default", "plan", "accept-edits", "bypass", "read-only"]),
2501
- z.object({ harnessModeId: z.string().min(1) })
2502
- ]);
2503
- routeSchema = z.object({
2504
- gateway: z.string().min(1),
2505
- baseUrl: z.string().url().optional()
2506
- });
2507
- userPresetSchema = z.object({
2508
- id: z.string().regex(/^[a-z0-9][a-z0-9-]*$/),
2509
- label: z.string().min(1),
2510
- adapter: z.string().min(1).optional(),
2511
- harness: z.string().min(1).optional(),
2512
- model: z.string().min(1).optional(),
2513
- route: routeSchema.optional(),
2514
- access: z.object({ profileRef: z.string().min(1).optional() }).optional(),
2515
- posture: postureSchema.optional(),
2516
- effort: effortSchema.optional(),
2517
- contextProfile: z.string().min(1).optional(),
2518
- cwd: z.string().min(1).optional(),
2519
- skills: z.array(z.string().min(1)).optional(),
2520
- bundles: z.array(z.string().min(1)).optional(),
2521
- browser: z.union([z.literal("headless"), z.literal(false)]).optional()
2522
- });
2523
- z.object({
2524
- version: z.literal(1),
2525
- presets: z.array(userPresetSchema)
2526
- });
2527
- }
2528
- });
2529
2568
  var init_catalog_provider_models = __esm({
2530
2569
  "src/catalog-provider-models.ts"() {
2531
2570
  }
@@ -2565,6 +2604,7 @@ var init_agent_tools = __esm({
2565
2604
  "src/agent-tools.ts"() {
2566
2605
  init_transcript_export();
2567
2606
  init_agent_start_schema();
2607
+ init_process_stats();
2568
2608
  init_spawn_field_schemas();
2569
2609
  init_session_spawn();
2570
2610
  init_canonical_posture();
@@ -2655,6 +2695,7 @@ var init_context_checkpoint = __esm({
2655
2695
  // src/session-continue-fresh.ts
2656
2696
  var init_session_continue_fresh = __esm({
2657
2697
  "src/session-continue-fresh.ts"() {
2698
+ init_app_boundary();
2658
2699
  init_context_checkpoint();
2659
2700
  init_context_continuity();
2660
2701
  init_session_spawn();
@@ -2715,6 +2756,21 @@ var init_gh_provenance_shim = __esm({
2715
2756
  DEFAULT_WRAP_GH = false;
2716
2757
  }
2717
2758
  });
2759
+ var DEFAULT_MODEL_ROLES;
2760
+ var init_model_roles = __esm({
2761
+ "src/model-roles.ts"() {
2762
+ DEFAULT_MODEL_ROLES = {
2763
+ /** Reviewer for a small residual (repo-maintenance `review` step). */
2764
+ "review.small": "claude-sonnet-5-5",
2765
+ /** Reviewer for a large residual + the retry reviewer. */
2766
+ "review.large": "claude-opus-5-5",
2767
+ /** CI pull-request reviewer (mirrors `.github/agentic-review.json`'s `reviewerModel`). */
2768
+ "review.pr": "openrouter/z-ai/glm-5.3-flash",
2769
+ /** The session-steward's agent judge. */
2770
+ "judge.session": "claude-sonnet-5-5"
2771
+ };
2772
+ }
2773
+ });
2718
2774
  function validateConfig(raw) {
2719
2775
  const result = agentprotoConfigSchema.safeParse(raw);
2720
2776
  if (result.success) return { ok: true, issues: [] };
@@ -2724,7 +2780,7 @@ function validateConfig(raw) {
2724
2780
  });
2725
2781
  return { ok: false, issues };
2726
2782
  }
2727
- var contextContinuityPolicySchema, deferredToolsConfigSchema, spawnBrowserModeSchema, optionsMapSchema, defaultsAdapterAuthConfigSchema, defaultsAdapterConfigSchema, mcpDefaultsConfigSchema, spawnDefaultsConfigSchema, daemonConfigSchema, titlerConfigSchema, tunnelConfigSchema, featuresConfigSchema, worktreesConfigSchema, spawnConfigSchema, provenanceConfigSchema, sessionsConfigSchema, agentsMdConfigSchema, pairPageSchema, pairingConfigSchema, profileConfigSchema, acpAgentConfigEntrySchema, terminalPresetSchema, agentprotoConfigSchema, bool, str, num, strArray;
2783
+ var contextContinuityPolicySchema, deferredToolsConfigSchema, spawnBrowserModeSchema, optionsMapSchema, defaultsAdapterAuthConfigSchema, defaultsAdapterConfigSchema, mcpDefaultsConfigSchema, spawnDefaultsConfigSchema, daemonConfigSchema, titlerConfigSchema, catalogConfigSchema, tunnelConfigSchema, featuresConfigSchema, worktreesConfigSchema, spawnConfigSchema, provenanceConfigSchema, approvalsConfigSchema, sessionsConfigSchema, agentsMdConfigSchema, pairPageSchema, pairingConfigSchema, modelRoleValueSchema, profileConfigSchema, acpAgentConfigEntrySchema, terminalPresetSchema, agentprotoConfigSchema, bool, str, num, strArray;
2728
2784
  var init_config_schema = __esm({
2729
2785
  "src/config-schema.ts"() {
2730
2786
  init_worktree_isolation();
@@ -2734,6 +2790,7 @@ var init_config_schema = __esm({
2734
2790
  init_gh_provenance_shim();
2735
2791
  init_agents_md();
2736
2792
  init_session_titler();
2793
+ init_model_roles();
2737
2794
  init_background_task_wake();
2738
2795
  init_role();
2739
2796
  contextContinuityPolicySchema = z.object({
@@ -2819,6 +2876,9 @@ var init_config_schema = __esm({
2819
2876
  enabled: z.boolean().optional(),
2820
2877
  model: z.string().optional()
2821
2878
  }).passthrough();
2879
+ catalogConfigSchema = z.object({
2880
+ sources: z.array(z.object({ url: z.string() }).passthrough()).optional()
2881
+ }).passthrough();
2822
2882
  tunnelConfigSchema = z.object({
2823
2883
  host: z.string().optional(),
2824
2884
  token: z.string().optional(),
@@ -2828,17 +2888,22 @@ var init_config_schema = __esm({
2828
2888
  featuresConfigSchema = z.object({
2829
2889
  pty: z.boolean().optional(),
2830
2890
  llmEndpoint: z.boolean().optional(),
2831
- deviceInferenceShare: z.boolean().optional()
2891
+ deviceInferenceShare: z.boolean().optional(),
2892
+ deviceSpawnAllow: z.boolean().optional()
2832
2893
  }).passthrough();
2833
2894
  worktreesConfigSchema = z.object({
2834
2895
  root: z.string().optional(),
2835
- isolation: z.enum(["always", "on-request", "never"]).optional()
2896
+ isolation: z.enum(["always", "on-request", "never"]).optional(),
2897
+ provisionConcurrency: z.number().int().min(0).optional(),
2898
+ provisionConcurrencyByRepo: z.record(z.string(), z.number().int().min(0)).optional(),
2899
+ provisionLoadFactor: z.number().min(0).optional()
2836
2900
  }).passthrough();
2837
2901
  spawnConfigSchema = z.object({
2838
2902
  attach: z.enum(["always", "on-request"]).optional(),
2839
2903
  dedupe: z.enum(["always", "on-request"]).optional()
2840
2904
  }).passthrough();
2841
2905
  provenanceConfigSchema = z.object({ wrapGh: z.boolean().optional() }).passthrough();
2906
+ approvalsConfigSchema = z.object({ webOrigins: z.array(z.string()).optional() }).passthrough();
2842
2907
  sessionsConfigSchema = z.object({
2843
2908
  attentionDelaySec: z.number().optional(),
2844
2909
  eventsDir: z.string().optional()
@@ -2856,6 +2921,14 @@ var init_config_schema = __esm({
2856
2921
  autoconnect: z.boolean().optional(),
2857
2922
  pairPage: pairPageSchema.optional()
2858
2923
  }).passthrough();
2924
+ modelRoleValueSchema = z.union([
2925
+ z.string().min(1),
2926
+ z.object({
2927
+ model: z.string().min(1),
2928
+ route: z.string().optional(),
2929
+ profile: z.string().optional()
2930
+ }).passthrough()
2931
+ ]);
2859
2932
  profileConfigSchema = z.object({
2860
2933
  daemon: daemonConfigSchema.optional(),
2861
2934
  tunnel: tunnelConfigSchema.optional(),
@@ -2894,8 +2967,11 @@ var init_config_schema = __esm({
2894
2967
  spawn: spawnConfigSchema.optional(),
2895
2968
  sessions: sessionsConfigSchema.optional(),
2896
2969
  provenance: provenanceConfigSchema.optional(),
2970
+ approvals: approvalsConfigSchema.optional(),
2897
2971
  agentsMd: agentsMdConfigSchema.optional(),
2972
+ models: z.record(z.string(), modelRoleValueSchema).optional(),
2898
2973
  titler: titlerConfigSchema.optional(),
2974
+ catalog: catalogConfigSchema.optional(),
2899
2975
  profiles: z.record(z.string(), profileConfigSchema).optional(),
2900
2976
  activeProfile: z.string().optional(),
2901
2977
  defaults: spawnDefaultsConfigSchema.optional(),
@@ -2946,6 +3022,16 @@ var init_config_schema = __esm({
2946
3022
  label: "Allowed origins",
2947
3023
  help: "Trusted browser origins for mutating /sessions/* routes, in addition to the hardcoded localhost defaults. Lockout: a bad value can lock a browser UI out."
2948
3024
  },
3025
+ {
3026
+ path: "approvals.webOrigins",
3027
+ schema: strArray,
3028
+ apply: "restart",
3029
+ writable: false,
3030
+ section: "daemon",
3031
+ label: "Approval web-click origins",
3032
+ help: "Origins allowed to decide a pending approval via POST /approvals/:id/decision (web_click channel). Empty (default) turns the channel off entirely. A SEPARATE allowlist from daemon.allowedOrigins, which is not automatically trusted to decide approvals. Lockout: a bad value can lock the web decision UI out.",
3033
+ default: []
3034
+ },
2949
3035
  {
2950
3036
  path: "daemon.strictOrigins",
2951
3037
  schema: bool,
@@ -3139,6 +3225,20 @@ var init_config_schema = __esm({
3139
3225
  default: DEFAULT_PAIR_PAGE
3140
3226
  },
3141
3227
  // ── models ──
3228
+ {
3229
+ // One row per configured role. Role names are dotted (`review.pr`), so a
3230
+ // concrete key is `models.review.pr` — `findConfigKey` maps any
3231
+ // `models.<role>` onto this entry, and `splitConfigPath` (config.ts) keeps
3232
+ // the role as ONE segment. The full role list (incl. built-in defaults and
3233
+ // where each value comes from) is the `model_roles` tool.
3234
+ path: "models.*",
3235
+ schema: modelRoleValueSchema,
3236
+ apply: "hot",
3237
+ writable: true,
3238
+ section: "models",
3239
+ label: "Model role",
3240
+ help: `Model for a role (review.small, review.large, review.pr, judge.session, \u2026): a model id, or { model, route?, profile? }. Precedence: explicit run input > the repo's agentproto.json \`models\` > this daemon config > built-in default (\`model_roles\` lists every role and its source). Built-in defaults: ${Object.entries(DEFAULT_MODEL_ROLES).map(([r, m]) => `${r}=${m}`).join(", ")}.`
3241
+ },
3142
3242
  {
3143
3243
  path: "titler.model",
3144
3244
  schema: str,
@@ -3149,6 +3249,16 @@ var init_config_schema = __esm({
3149
3249
  help: "OpenRouter model id used to generate session titles. Requires OPENROUTER_API_KEY.",
3150
3250
  default: DEFAULT_TITLER_MODEL
3151
3251
  },
3252
+ // ── daemon: app catalog ──
3253
+ {
3254
+ path: "catalog.sources",
3255
+ schema: z.array(z.object({ url: str }).passthrough()),
3256
+ apply: "hot",
3257
+ writable: true,
3258
+ section: "daemon",
3259
+ label: "App catalog sources",
3260
+ 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."
3261
+ },
3152
3262
  // ── defaults: spawn ──
3153
3263
  {
3154
3264
  path: "defaults.skills",
@@ -3228,6 +3338,36 @@ var init_config_schema = __esm({
3228
3338
  help: "Whether a freshly-spawned agent_start session is isolated into its own git worktree.",
3229
3339
  default: DEFAULT_WORKTREE_ISOLATION
3230
3340
  },
3341
+ {
3342
+ path: "worktrees.provisionConcurrency",
3343
+ schema: z.number().int().min(0),
3344
+ apply: "hot",
3345
+ env: WORKTREE_PROVISION_CONCURRENCY_ENV,
3346
+ writable: true,
3347
+ section: "defaults",
3348
+ label: "Worktree provisioning concurrency",
3349
+ help: "Max worktree provisionings running their heavy phases (depsCmd, clones, setup hooks) at once; the rest queue FIFO. 0 = unlimited.",
3350
+ default: DEFAULT_WORKTREE_PROVISION_CONCURRENCY
3351
+ },
3352
+ {
3353
+ path: "worktrees.provisionConcurrencyByRepo",
3354
+ schema: z.record(str, z.number().int().min(0)),
3355
+ apply: "hot",
3356
+ writable: true,
3357
+ section: "defaults",
3358
+ label: "Per-repo provisioning concurrency",
3359
+ help: "Per-repo caps on concurrent heavy provisioning, keyed by repo path or directory name; never looser than the global cap."
3360
+ },
3361
+ {
3362
+ path: "worktrees.provisionLoadFactor",
3363
+ schema: z.number().min(0),
3364
+ apply: "hot",
3365
+ writable: true,
3366
+ section: "defaults",
3367
+ label: "Provisioning load guard factor",
3368
+ help: "Hold back a new heavy provisioning while the 1-minute load average exceeds cores x this factor. 0 or unset = off.",
3369
+ default: 0
3370
+ },
3231
3371
  {
3232
3372
  path: "spawn.attach",
3233
3373
  schema: z.enum(["always", "on-request"]),
@@ -3675,6 +3815,16 @@ var init_config_schema = __esm({
3675
3815
  label: "Device inference sharing",
3676
3816
  help: "Expose this daemon's local inference endpoint(s) to a paired HOST-scoped controller. Toggle via `agentproto devices share-inference on|off`.",
3677
3817
  default: false
3818
+ },
3819
+ {
3820
+ path: "features.deviceSpawnAllow",
3821
+ schema: bool,
3822
+ apply: "restart",
3823
+ writable: true,
3824
+ section: "daemon",
3825
+ label: "Device spawn allow",
3826
+ help: 'Allow a paired HOST-scoped controller to spawn/drive agent sessions on this daemon (`agent_start({ sandbox: "device:<name>" })`). Toggle via `agentproto devices allow-spawn on|off`.',
3827
+ default: false
3678
3828
  }
3679
3829
  ];
3680
3830
  }
@@ -3748,16 +3898,22 @@ async function saveConfig(next, path) {
3748
3898
  await promises.writeFile(tmp, JSON.stringify(payload, null, 2) + "\n", "utf8");
3749
3899
  await promises.rename(tmp, target);
3750
3900
  }
3901
+ function splitConfigPath(dotted) {
3902
+ if (dotted.startsWith("models.") && dotted.length > "models.".length) {
3903
+ return ["models", dotted.slice("models.".length)];
3904
+ }
3905
+ return dotted.split(".");
3906
+ }
3751
3907
  function getConfigKey(cfg, dotted) {
3752
3908
  let cur = cfg;
3753
- for (const part of dotted.split(".")) {
3909
+ for (const part of splitConfigPath(dotted)) {
3754
3910
  if (cur == null || typeof cur !== "object") return void 0;
3755
3911
  cur = cur[part];
3756
3912
  }
3757
3913
  return cur;
3758
3914
  }
3759
3915
  function setConfigKey(cfg, dotted, value) {
3760
- const parts = dotted.split(".");
3916
+ const parts = splitConfigPath(dotted);
3761
3917
  const out = { ...cfg };
3762
3918
  let cur = out;
3763
3919
  for (let i = 0; i < parts.length - 1; i++) {
@@ -3789,6 +3945,6 @@ var init_config = __esm({
3789
3945
  });
3790
3946
  init_config();
3791
3947
 
3792
- export { CONFIG_FILE_PATH, CONFIG_VERSION, getConfigKey, loadConfig, saveConfig, setConfigKey };
3948
+ export { CONFIG_FILE_PATH, CONFIG_VERSION, getConfigKey, loadConfig, saveConfig, setConfigKey, splitConfigPath };
3793
3949
  //# sourceMappingURL=config.mjs.map
3794
3950
  //# sourceMappingURL=config.mjs.map