@p4code/cli 0.1.18 → 0.1.20

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.
Files changed (85) hide show
  1. package/dist/bin.mjs +1330 -160
  2. package/dist/client/assets/{DiffPanel-CP_cM9m0.js → DiffPanel-Di8WylWU.js} +2 -2
  3. package/dist/client/assets/{FilePreviewPanel-CLho0PPu.js → FilePreviewPanel-CSqqsbYX.js} +2 -2
  4. package/dist/client/assets/{PreviewPanel-DvnUpMQv.js → PreviewPanel-BcRUQSMy.js} +2 -2
  5. package/dist/client/assets/{angular-html-CEMakyzJ.js → angular-html-CPSNyy71.js} +2 -2
  6. package/dist/client/assets/{angular-ts-YXZHq2uz.js → angular-ts-BgndqOdx.js} +2 -2
  7. package/dist/client/assets/{apl-BTa85Bsd.js → apl-CFWK0bta.js} +2 -2
  8. package/dist/client/assets/arrow-right-wbRntSmq.js +2 -0
  9. package/dist/client/assets/{astro-DIbti84c.js → astro-D2uzatlp.js} +2 -2
  10. package/dist/client/assets/{blade-CKAdvdwG.js → blade-DfrK_8z3.js} +2 -2
  11. package/dist/client/assets/{c-DW17xzCK.js → c-DyORVYNJ.js} +2 -2
  12. package/dist/client/assets/{cobol-DXiicIcZ.js → cobol-BUWePdBc.js} +2 -2
  13. package/dist/client/assets/{coffee-9Pdmmdn2.js → coffee-Ba8BYHli.js} +2 -2
  14. package/dist/client/assets/{cpp-bm3vFVmW.js → cpp-BX0iAGNA.js} +2 -2
  15. package/dist/client/assets/{crystal-DbxZDZf9.js → crystal-BDSdceoQ.js} +2 -2
  16. package/dist/client/assets/{css-BEBnbxDe.js → css-TKd0Sp86.js} +2 -2
  17. package/dist/client/assets/{edge-K97wVEy_.js → edge-Bus7Aab8.js} +2 -2
  18. package/dist/client/assets/{elixir-Rc2JDLLD.js → elixir-BlT2g4EX.js} +2 -2
  19. package/dist/client/assets/{elm-QsA8aREL.js → elm-DTGJZ36e.js} +2 -2
  20. package/dist/client/assets/{erb-CliRJz4v.js → erb-BF4yZGxQ.js} +2 -2
  21. package/dist/client/assets/{fileCommentAnnotations-DtIPA0Fm.js → fileCommentAnnotations-C5BlQ5s_.js} +2 -2
  22. package/dist/client/assets/{git-rebase-Da_Wuhd_.js → git-rebase-GAe0_ZI9.js} +2 -2
  23. package/dist/client/assets/{glimmer-js-XPbTw3tC.js → glimmer-js-GWmPSTNt.js} +2 -2
  24. package/dist/client/assets/{glimmer-ts-BtK3NOkH.js → glimmer-ts-CDLqKTZ9.js} +2 -2
  25. package/dist/client/assets/{glsl-DqEtJptk.js → glsl-Bv3E3r0O.js} +2 -2
  26. package/dist/client/assets/{graphql-BSRTeXs0.js → graphql-B5-LupiK.js} +2 -2
  27. package/dist/client/assets/{hack-Dw9HaNHZ.js → hack-KTQ3DPKm.js} +2 -2
  28. package/dist/client/assets/{haml-BBNabTlb.js → haml-BvC-52XY.js} +2 -2
  29. package/dist/client/assets/{handlebars-BgXzhWSj.js → handlebars-CK9WCq20.js} +2 -2
  30. package/dist/client/assets/{html-wBdSHoow.js → html-BGjxg0hO.js} +2 -2
  31. package/dist/client/assets/{html-derivative-CANLUZ-z.js → html-derivative-C0EaHUuc.js} +2 -2
  32. package/dist/client/assets/{http-BY44QKMy.js → http-hvxfwUee.js} +2 -2
  33. package/dist/client/assets/{hurl-EMZQkkjM.js → hurl-D5cuiBPB.js} +2 -2
  34. package/dist/client/assets/index-QC2FhlVn.js +836 -0
  35. package/dist/client/assets/{java-ByjPbT42.js → java-DOkofowB.js} +2 -2
  36. package/dist/client/assets/{javascript-C9vbUEcm.js → javascript-dCesbfJ7.js} +2 -2
  37. package/dist/client/assets/{jinja-DH-AbsBN.js → jinja-BDrVqT_2.js} +2 -2
  38. package/dist/client/assets/{jison-Bef1Ntr_.js → jison-Dvzh0XRq.js} +2 -2
  39. package/dist/client/assets/{json-Gk_oM0bP.js → json-CpV_dVb2.js} +2 -2
  40. package/dist/client/assets/{jsx-Ds7YHLou.js → jsx-CbaKC498.js} +2 -2
  41. package/dist/client/assets/{julia-Dm-6xpIK.js → julia-BKYdI_5W.js} +2 -2
  42. package/dist/client/assets/{just-BXY93QN4.js → just-FrVMEGvg.js} +2 -2
  43. package/dist/client/assets/{latex-B3-eYkFA.js → latex-BSmwt_7j.js} +2 -2
  44. package/dist/client/assets/{liquid-C10OBLnm.js → liquid-Bb2nFp-U.js} +2 -2
  45. package/dist/client/assets/{lua-BUR8bsxC.js → lua-CEANnQ2c.js} +2 -2
  46. package/dist/client/assets/{marko-COPLM_fz.js → marko-CjJZ1eHg.js} +2 -2
  47. package/dist/client/assets/{mdc-CTAUTBsz.js → mdc-B3VSAYVT.js} +2 -2
  48. package/dist/client/assets/{nginx-D1BLqLRM.js → nginx-BkYJUm4E.js} +2 -2
  49. package/dist/client/assets/{nim-Xs8VbhdY.js → nim-D8xT6_xt.js} +2 -2
  50. package/dist/client/assets/{perl-D7-zmFa1.js → perl-cazo7YHC.js} +2 -2
  51. package/dist/client/assets/{php-BiTVgoaL.js → php-C0LwuOo3.js} +2 -2
  52. package/dist/client/assets/{pug-DphfHoFa.js → pug-OP6SwNFw.js} +2 -2
  53. package/dist/client/assets/{qml-CYcNnFOd.js → qml-C1-WQIQ-.js} +2 -2
  54. package/dist/client/assets/{r-BrJ-p0TS.js → r-PkhwsIgx.js} +2 -2
  55. package/dist/client/assets/{razor-1gio6m_g.js → razor-WONkd3aR.js} +2 -2
  56. package/dist/client/assets/{regexp-CNiuKup7.js → regexp-SDnpa4md.js} +2 -2
  57. package/dist/client/assets/{rst-BPOsO8_U.js → rst-DulobrTh.js} +2 -2
  58. package/dist/client/assets/{ruby-CpfKHKDc.js → ruby-RYHm8H3x.js} +2 -2
  59. package/dist/client/assets/{sas-CngX1we2.js → sas-BBwb67VZ.js} +2 -2
  60. package/dist/client/assets/{scss-B2remLQX.js → scss-y2IZtjKJ.js} +2 -2
  61. package/dist/client/assets/{shellscript-CQmxALRY.js → shellscript-B3vxtpJZ.js} +2 -2
  62. package/dist/client/assets/{shellsession-BB-5CB_r.js → shellsession-CvHfy0TS.js} +2 -2
  63. package/dist/client/assets/{soy-kYUnX51I.js → soy-DWpn3a9x.js} +2 -2
  64. package/dist/client/assets/{sql-Cq9fRkwr.js → sql-CCfTsIvj.js} +2 -2
  65. package/dist/client/assets/{stata-17nboilR.js → stata-Dr5CN-Tp.js} +2 -2
  66. package/dist/client/assets/{surrealql-D1hrcsSn.js → surrealql-DgzXJo35.js} +2 -2
  67. package/dist/client/assets/{svelte-GmGi3eK8.js → svelte-JQb5PCjP.js} +2 -2
  68. package/dist/client/assets/{templ-DySdTLEs.js → templ-DQt5AsQ7.js} +2 -2
  69. package/dist/client/assets/{tex-CblcFqPi.js → tex-B8Qokwwv.js} +2 -2
  70. package/dist/client/assets/textarea-BlxAoGJe.js +46 -0
  71. package/dist/client/assets/{ts-tags-BQC5pzJI.js → ts-tags-B-RrSBzw.js} +2 -2
  72. package/dist/client/assets/{tsx-1bNjXgiV.js → tsx-C7Q0nn_B.js} +2 -2
  73. package/dist/client/assets/{twig-CNwthYXl.js → twig-CzVGXsz7.js} +2 -2
  74. package/dist/client/assets/{typescript-BTKoaKmx.js → typescript-irwEvPgw.js} +2 -2
  75. package/dist/client/assets/{vue-B4XpWxge.js → vue-BE-nMa-3.js} +2 -2
  76. package/dist/client/assets/{vue-html-CcUSqexV.js → vue-html-SXHQOmAo.js} +2 -2
  77. package/dist/client/assets/{vue-vine-JDMd26pY.js → vue-vine-D62pnm9I.js} +2 -2
  78. package/dist/client/assets/{xml-DRuNSJP8.js → xml-BnssPo1i.js} +2 -2
  79. package/dist/client/assets/{xsl-BIJJg1B0.js → xsl-Dg7cN1LO.js} +2 -2
  80. package/dist/client/assets/{yaml-BNEnOsJI.js → yaml-BAeQKrXU.js} +2 -2
  81. package/dist/client/index.html +2 -2
  82. package/package.json +1 -1
  83. package/dist/client/assets/arrow-right-Bi8kADrV.js +0 -2
  84. package/dist/client/assets/index-kwzTYbVb.js +0 -775
  85. package/dist/client/assets/textarea-Cf56oU_6.js +0 -46
package/dist/bin.mjs CHANGED
@@ -121,7 +121,7 @@ const closeServer = (server) => {
121
121
  * NetService - Service tag for startup networking helpers.
122
122
  */
123
123
  var NetService = class extends Context.Service()("@p4code/shared/Net/NetService") {};
124
- const make$75 = () => {
124
+ const make$76 = () => {
125
125
  /**
126
126
  * Returns true when a TCP server can bind to {host, port}.
127
127
  * `EADDRNOTAVAIL` is treated as available so IPv6-absent hosts don't fail
@@ -230,10 +230,10 @@ const make$75 = () => {
230
230
  })
231
231
  };
232
232
  };
233
- const layer$71 = Layer.sync(NetService, make$75);
233
+ const layer$72 = Layer.sync(NetService, make$76);
234
234
  //#endregion
235
235
  //#region package.json
236
- var version = "0.1.18";
236
+ var version = "0.1.20";
237
237
  //#endregion
238
238
  //#region src/config.ts
239
239
  /**
@@ -254,8 +254,8 @@ var ServerConfig$1 = class extends Context.Service()("@p4code/cli/config/ServerC
254
254
  /** @deprecated Import and use `layerTest` from this module. */
255
255
  static layerTest = (cwd, baseDirOrPrefix) => layerTest$2(cwd, baseDirOrPrefix);
256
256
  };
257
- const make$74 = (config) => ServerConfig$1.of(config);
258
- const layer$70 = (config) => Layer.succeed(ServerConfig$1, make$74(config));
257
+ const make$75 = (config) => ServerConfig$1.of(config);
258
+ const layer$71 = (config) => Layer.succeed(ServerConfig$1, make$75(config));
259
259
  const deriveServerPaths = Effect.fn(function* (baseDir, devUrl, options = {}) {
260
260
  const { join } = yield* Path.Path;
261
261
  const stateDir = join(baseDir, devUrl !== void 0 && !options.baseDirIsExplicit ? "dev" : "userdata");
@@ -1838,6 +1838,7 @@ const OrchestrationThread = Schema$1.Struct({
1838
1838
  runtimeMode: RuntimeMode,
1839
1839
  interactionMode: ProviderInteractionMode.pipe(Schema$1.withDecodingDefault(Effect.succeed(DEFAULT_PROVIDER_INTERACTION_MODE))),
1840
1840
  compressMode: CompressMode.pipe(Schema$1.withDecodingDefault(Effect.succeed(DEFAULT_COMPRESS_MODE))),
1841
+ unpromptedSubagents: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true))),
1841
1842
  branch: Schema$1.NullOr(TrimmedNonEmptyString),
1842
1843
  worktreePath: Schema$1.NullOr(TrimmedNonEmptyString),
1843
1844
  latestTurn: Schema$1.NullOr(OrchestrationLatestTurn),
@@ -1879,6 +1880,7 @@ const OrchestrationThreadShell = Schema$1.Struct({
1879
1880
  runtimeMode: RuntimeMode,
1880
1881
  interactionMode: ProviderInteractionMode.pipe(Schema$1.withDecodingDefault(Effect.succeed(DEFAULT_PROVIDER_INTERACTION_MODE))),
1881
1882
  compressMode: CompressMode.pipe(Schema$1.withDecodingDefault(Effect.succeed(DEFAULT_COMPRESS_MODE))),
1883
+ unpromptedSubagents: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true))),
1882
1884
  branch: Schema$1.NullOr(TrimmedNonEmptyString),
1883
1885
  worktreePath: Schema$1.NullOr(TrimmedNonEmptyString),
1884
1886
  latestTurn: Schema$1.NullOr(OrchestrationLatestTurn),
@@ -2002,6 +2004,7 @@ const ThreadCreateCommand = Schema$1.Struct({
2002
2004
  runtimeMode: RuntimeMode,
2003
2005
  interactionMode: ProviderInteractionMode.pipe(Schema$1.withDecodingDefault(Effect.succeed(DEFAULT_PROVIDER_INTERACTION_MODE))),
2004
2006
  compressMode: CompressMode.pipe(Schema$1.withDecodingDefault(Effect.succeed(DEFAULT_COMPRESS_MODE))),
2007
+ unpromptedSubagents: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true))),
2005
2008
  branch: Schema$1.NullOr(TrimmedNonEmptyString),
2006
2009
  worktreePath: Schema$1.NullOr(TrimmedNonEmptyString),
2007
2010
  createdAt: IsoDateTime
@@ -2075,6 +2078,18 @@ const ThreadCompressModeSetCommand = Schema$1.Struct({
2075
2078
  compressMode: CompressMode,
2076
2079
  createdAt: IsoDateTime
2077
2080
  });
2081
+ /**
2082
+ * No decoding default on the value, matching the compress command beside it: a
2083
+ * command is an instruction someone issued, and defaulting the very field it
2084
+ * exists to carry would turn a malformed dispatch into a silent write.
2085
+ */
2086
+ const ThreadUnpromptedSubagentsSetCommand = Schema$1.Struct({
2087
+ type: Schema$1.Literal("thread.unprompted-subagents.set"),
2088
+ commandId: CommandId,
2089
+ threadId: ThreadId,
2090
+ unpromptedSubagents: Schema$1.Boolean,
2091
+ createdAt: IsoDateTime
2092
+ });
2078
2093
  const ThreadTurnStartBootstrapCreateThread = Schema$1.Struct({
2079
2094
  projectId: ProjectId,
2080
2095
  title: TrimmedNonEmptyString,
@@ -2082,6 +2097,7 @@ const ThreadTurnStartBootstrapCreateThread = Schema$1.Struct({
2082
2097
  runtimeMode: RuntimeMode,
2083
2098
  interactionMode: ProviderInteractionMode,
2084
2099
  compressMode: CompressMode.pipe(Schema$1.withDecodingDefault(Effect.succeed(DEFAULT_COMPRESS_MODE))),
2100
+ unpromptedSubagents: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true))),
2085
2101
  branch: Schema$1.NullOr(TrimmedNonEmptyString),
2086
2102
  worktreePath: Schema$1.NullOr(TrimmedNonEmptyString),
2087
2103
  createdAt: IsoDateTime
@@ -2112,6 +2128,7 @@ const ThreadTurnStartCommand = Schema$1.Struct({
2112
2128
  runtimeMode: RuntimeMode.pipe(Schema$1.withDecodingDefault(Effect.succeed(DEFAULT_RUNTIME_MODE$2))),
2113
2129
  interactionMode: ProviderInteractionMode.pipe(Schema$1.withDecodingDefault(Effect.succeed(DEFAULT_PROVIDER_INTERACTION_MODE))),
2114
2130
  compressMode: CompressMode.pipe(Schema$1.withDecodingDefault(Effect.succeed(DEFAULT_COMPRESS_MODE))),
2131
+ unpromptedSubagents: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true))),
2115
2132
  bootstrap: Schema$1.optional(ThreadTurnStartBootstrap),
2116
2133
  sourceProposedPlan: Schema$1.optional(SourceProposedPlanReference),
2117
2134
  createdAt: IsoDateTime
@@ -2131,6 +2148,7 @@ const ClientThreadTurnStartCommand = Schema$1.Struct({
2131
2148
  runtimeMode: RuntimeMode,
2132
2149
  interactionMode: ProviderInteractionMode,
2133
2150
  compressMode: CompressMode.pipe(Schema$1.withDecodingDefault(Effect.succeed(DEFAULT_COMPRESS_MODE))),
2151
+ unpromptedSubagents: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true))),
2134
2152
  bootstrap: Schema$1.optional(ThreadTurnStartBootstrap),
2135
2153
  sourceProposedPlan: Schema$1.optional(SourceProposedPlanReference),
2136
2154
  createdAt: IsoDateTime
@@ -2187,6 +2205,7 @@ const DispatchableClientOrchestrationCommand = Schema$1.Union([
2187
2205
  ThreadRuntimeModeSetCommand,
2188
2206
  ThreadInteractionModeSetCommand,
2189
2207
  ThreadCompressModeSetCommand,
2208
+ ThreadUnpromptedSubagentsSetCommand,
2190
2209
  ThreadTurnStartCommand,
2191
2210
  ThreadTurnInterruptCommand,
2192
2211
  ThreadApprovalRespondCommand,
@@ -2210,6 +2229,7 @@ const ClientOrchestrationCommand = Schema$1.Union([
2210
2229
  ThreadRuntimeModeSetCommand,
2211
2230
  ThreadInteractionModeSetCommand,
2212
2231
  ThreadCompressModeSetCommand,
2232
+ ThreadUnpromptedSubagentsSetCommand,
2213
2233
  ClientThreadTurnStartCommand,
2214
2234
  ThreadTurnInterruptCommand,
2215
2235
  ThreadApprovalRespondCommand,
@@ -2301,6 +2321,7 @@ const OrchestrationEventType = Schema$1.Literals([
2301
2321
  "thread.runtime-mode-set",
2302
2322
  "thread.interaction-mode-set",
2303
2323
  "thread.compress-mode-set",
2324
+ "thread.unprompted-subagents-set",
2304
2325
  "thread.message-sent",
2305
2326
  "thread.turn-start-requested",
2306
2327
  "thread.turn-interrupt-requested",
@@ -2351,6 +2372,7 @@ const ThreadCreatedPayload$1 = Schema$1.Struct({
2351
2372
  runtimeMode: RuntimeMode.pipe(Schema$1.withDecodingDefault(Effect.succeed(DEFAULT_RUNTIME_MODE$2))),
2352
2373
  interactionMode: ProviderInteractionMode.pipe(Schema$1.withDecodingDefault(Effect.succeed(DEFAULT_PROVIDER_INTERACTION_MODE))),
2353
2374
  compressMode: CompressMode.pipe(Schema$1.withDecodingDefault(Effect.succeed(DEFAULT_COMPRESS_MODE))),
2375
+ unpromptedSubagents: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true))),
2354
2376
  branch: Schema$1.NullOr(TrimmedNonEmptyString),
2355
2377
  worktreePath: Schema$1.NullOr(TrimmedNonEmptyString),
2356
2378
  createdAt: IsoDateTime,
@@ -2413,6 +2435,16 @@ const ThreadCompressModeSetPayload$1 = Schema$1.Struct({
2413
2435
  compressMode: CompressMode.pipe(Schema$1.withDecodingDefault(Effect.succeed(DEFAULT_COMPRESS_MODE))),
2414
2436
  updatedAt: IsoDateTime
2415
2437
  });
2438
+ /**
2439
+ * One thread's subagent policy changed. Its own event rather than a field on
2440
+ * the compress one, for the reason every event here is narrow: replaying a
2441
+ * combined event would rewrite whichever half the user did not touch.
2442
+ */
2443
+ const ThreadUnpromptedSubagentsSetPayload$1 = Schema$1.Struct({
2444
+ threadId: ThreadId,
2445
+ unpromptedSubagents: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true))),
2446
+ updatedAt: IsoDateTime
2447
+ });
2416
2448
  const ThreadMessageSentPayload = Schema$1.Struct({
2417
2449
  threadId: ThreadId,
2418
2450
  messageId: MessageId,
@@ -2432,6 +2464,7 @@ const ThreadTurnStartRequestedPayload = Schema$1.Struct({
2432
2464
  runtimeMode: RuntimeMode.pipe(Schema$1.withDecodingDefault(Effect.succeed(DEFAULT_RUNTIME_MODE$2))),
2433
2465
  interactionMode: ProviderInteractionMode.pipe(Schema$1.withDecodingDefault(Effect.succeed(DEFAULT_PROVIDER_INTERACTION_MODE))),
2434
2466
  compressMode: CompressMode.pipe(Schema$1.withDecodingDefault(Effect.succeed(DEFAULT_COMPRESS_MODE))),
2467
+ unpromptedSubagents: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true))),
2435
2468
  sourceProposedPlan: Schema$1.optional(SourceProposedPlanReference),
2436
2469
  createdAt: IsoDateTime
2437
2470
  });
@@ -2581,6 +2614,11 @@ const OrchestrationEvent = Schema$1.Union([
2581
2614
  type: Schema$1.Literal("thread.compress-mode-set"),
2582
2615
  payload: ThreadCompressModeSetPayload$1
2583
2616
  }),
2617
+ Schema$1.Struct({
2618
+ ...EventBaseFields,
2619
+ type: Schema$1.Literal("thread.unprompted-subagents-set"),
2620
+ payload: ThreadUnpromptedSubagentsSetPayload$1
2621
+ }),
2584
2622
  Schema$1.Struct({
2585
2623
  ...EventBaseFields,
2586
2624
  type: Schema$1.Literal("thread.message-sent"),
@@ -3430,6 +3468,36 @@ const PreviewAutomationNetworkEntry = Schema.Struct({
3430
3468
  errorText: Schema.optional(Schema.String),
3431
3469
  timestamp: Schema.String
3432
3470
  });
3471
+ /** Value of an accessibility state such as `expanded`, `checked` or `level`. */
3472
+ const PreviewAccessibilityState = Schema.Union([
3473
+ Schema.String,
3474
+ Schema.Number,
3475
+ Schema.Boolean
3476
+ ]);
3477
+ /**
3478
+ * A node of the compacted accessibility tree. `parentId` points at the nearest
3479
+ * emitted ancestor, so the hierarchy is reconstructible without child lists.
3480
+ */
3481
+ const PreviewAccessibilityNode = Schema.Struct({
3482
+ id: Schema.String,
3483
+ parentId: Schema.optional(Schema.String),
3484
+ role: Schema.String,
3485
+ name: Schema.optional(Schema.String),
3486
+ description: Schema.optional(Schema.String),
3487
+ value: Schema.optional(Schema.String),
3488
+ states: Schema.optional(Schema.Record(Schema.String, PreviewAccessibilityState))
3489
+ });
3490
+ /**
3491
+ * Semantic nodes only: ignored nodes, raw text nodes and unnamed layout
3492
+ * wrappers are dropped, and the node list is capped. `totalNodes` counts the
3493
+ * nodes that survived filtering, so `truncated` distinguishes a clipped tree
3494
+ * from a small page.
3495
+ */
3496
+ const PreviewAccessibilitySnapshot = Schema.Struct({
3497
+ nodes: Schema.Array(PreviewAccessibilityNode),
3498
+ totalNodes: Schema.Int,
3499
+ truncated: Schema.Boolean
3500
+ });
3433
3501
  const PreviewAutomationActionEvent = Schema.Struct({
3434
3502
  id: Schema.String,
3435
3503
  action: Schema.String,
@@ -3449,7 +3517,7 @@ const PreviewAutomationSnapshot = Schema.Struct({
3449
3517
  loading: Schema.Boolean,
3450
3518
  visibleText: Schema.String,
3451
3519
  interactiveElements: Schema.Array(PreviewAutomationElement),
3452
- accessibilityTree: Schema.Unknown,
3520
+ accessibilityTree: PreviewAccessibilitySnapshot,
3453
3521
  consoleEntries: Schema.Array(PreviewAutomationConsoleEntry),
3454
3522
  networkEntries: Schema.Array(PreviewAutomationNetworkEntry),
3455
3523
  actionTimeline: Schema.Array(PreviewAutomationActionEvent),
@@ -4557,7 +4625,8 @@ const ProviderSessionStartInput = Schema$1.Struct({
4557
4625
  approvalPolicy: Schema$1.optional(ProviderApprovalPolicy),
4558
4626
  sandboxMode: Schema$1.optional(ProviderSandboxMode),
4559
4627
  runtimeMode: RuntimeMode,
4560
- compressMode: Schema$1.optional(CompressMode)
4628
+ compressMode: Schema$1.optional(CompressMode),
4629
+ unpromptedSubagents: Schema$1.optional(Schema$1.Boolean)
4561
4630
  });
4562
4631
  const ProviderSendTurnInput = Schema$1.Struct({
4563
4632
  threadId: ThreadId,
@@ -4565,7 +4634,8 @@ const ProviderSendTurnInput = Schema$1.Struct({
4565
4634
  attachments: Schema$1.optional(Schema$1.Array(ChatAttachment).check(Schema$1.isMaxLength(8))),
4566
4635
  modelSelection: Schema$1.optional(ModelSelection),
4567
4636
  interactionMode: Schema$1.optional(ProviderInteractionMode),
4568
- compressMode: Schema$1.optional(CompressMode)
4637
+ compressMode: Schema$1.optional(CompressMode),
4638
+ unpromptedSubagents: Schema$1.optional(Schema$1.Boolean)
4569
4639
  });
4570
4640
  Schema$1.Struct({
4571
4641
  threadId: ThreadId,
@@ -5823,6 +5893,45 @@ const ServerSettings = Schema$1.Struct({
5823
5893
  * instruction can override an earlier general one.
5824
5894
  */
5825
5895
  enableUnpromptedSubagents: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true))),
5896
+ /**
5897
+ * Whether the board offers the scoping agent: the Scope action on a task and
5898
+ * the panel that drafts a new one by interview.
5899
+ *
5900
+ * A switch rather than a permanent fixture because the discipline it imposes
5901
+ * — one question at a time, no file edits, a task body with verifiable
5902
+ * criteria — is a way of working, not a fact about the product. Someone who
5903
+ * writes their own tasks wants the button gone, not ignored.
5904
+ *
5905
+ * Off hides the entry points. It does not delete the skill file already
5906
+ * written into a repository, and it does not stop anyone invoking the same
5907
+ * discipline by name from an ordinary chat: that is what {@link disabledSkills}
5908
+ * is for.
5909
+ */
5910
+ enableScopingAgent: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true))),
5911
+ /**
5912
+ * Whether the board offers the plan phase: splitting one scoped task into
5913
+ * ordered subtasks. Separate from {@link enableScopingAgent} because the two
5914
+ * are useful apart — scoping a task by interview says nothing about wanting
5915
+ * it decomposed, and a board that plans everything into five rows is a board
5916
+ * nobody reads.
5917
+ */
5918
+ enablePlanPhase: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true))),
5919
+ /**
5920
+ * Whether the scoping and planning flows write their generated `SKILL.md`
5921
+ * into the repository they run in.
5922
+ *
5923
+ * Its own switch because it is the only one of the three with a side effect
5924
+ * outside p4code: the file lands in the working tree and shows up in
5925
+ * `git status`, which is unwelcome in a repo whose `.claude/` is checked in
5926
+ * and shared. Off does not cost the discipline — the thread seed carries the
5927
+ * rules inline instead, exactly as the create flow already does for providers
5928
+ * with no notion of a skill — it costs only invoking it by name later from an
5929
+ * ordinary chat.
5930
+ *
5931
+ * Files already written are left alone. Nothing here deletes anything from a
5932
+ * user's repository.
5933
+ */
5934
+ writeGeneratedSkillsToRepo: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true))),
5826
5935
  automaticGitFetchInterval: Schema$1.DurationFromMillis.pipe(Schema$1.withDecodingDefault(Effect.succeed(Duration.toMillis(DEFAULT_AUTOMATIC_GIT_FETCH_INTERVAL)))),
5827
5936
  defaultThreadEnvMode: ThreadEnvMode.pipe(Schema$1.withDecodingDefault(Effect.succeed("local"))),
5828
5937
  newWorktreesStartFromOrigin: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true))),
@@ -5958,6 +6067,9 @@ const ServerSettingsPatch = Schema$1.Struct({
5958
6067
  enableProviderUpdateChecks: Schema$1.optionalKey(Schema$1.Boolean),
5959
6068
  enableToolCallNarration: Schema$1.optionalKey(Schema$1.Boolean),
5960
6069
  enableUnpromptedSubagents: Schema$1.optionalKey(Schema$1.Boolean),
6070
+ enableScopingAgent: Schema$1.optionalKey(Schema$1.Boolean),
6071
+ enablePlanPhase: Schema$1.optionalKey(Schema$1.Boolean),
6072
+ writeGeneratedSkillsToRepo: Schema$1.optionalKey(Schema$1.Boolean),
5961
6073
  automaticGitFetchInterval: Schema$1.optionalKey(Schema$1.DurationFromMillis),
5962
6074
  defaultThreadEnvMode: Schema$1.optionalKey(ThreadEnvMode),
5963
6075
  newWorktreesStartFromOrigin: Schema$1.optionalKey(Schema$1.Boolean),
@@ -7218,6 +7330,36 @@ const RepositoryKeyField = TrimmedNonEmptyString.pipe(Schema$1.annotateEncoded({
7218
7330
  * placed by hand and falls back to the priority/recency order.
7219
7331
  */
7220
7332
  const OrderKeyField = TrimmedNonEmptyString.pipe(Schema$1.annotateEncoded({ description: "Manual board position key. Lexicographic: smaller sorts higher in the column. Null means no manual position." }));
7333
+ /**
7334
+ * Where a task came from when it did not originate on this board.
7335
+ *
7336
+ * The identifier is the tracker's own, not ours: `MOBILE-12262` reads the same
7337
+ * in p4code as it does in Linear, in a commit message, and in conversation,
7338
+ * which is the whole point of carrying it. `url` is what the tracker itself
7339
+ * reported, never a guess assembled from the identifier - a Linear URL embeds
7340
+ * the workspace slug, so a constructed one would 404.
7341
+ */
7342
+ const TaskExternalRef = Schema$1.Struct({
7343
+ source: Schema$1.Literals(["linear"]),
7344
+ identifier: TrimmedNonEmptyString,
7345
+ url: TrimmedNonEmptyString
7346
+ });
7347
+ const ExternalRefField = TaskExternalRef.pipe(Schema$1.annotateEncoded({ description: "The tracker issue this task mirrors, e.g. a Linear ticket. Null for a task that originated on this board." }));
7348
+ /**
7349
+ * The task this one was split out of.
7350
+ *
7351
+ * Carried as a field rather than as a sentence in the body because the plan
7352
+ * phase writes it and the board reads it: a parent chip, a subtask list and
7353
+ * "does this still have unfinished children" are all questions about the graph,
7354
+ * and prose cannot be asked them. Stored as the parent's UUID, but accepted as
7355
+ * either a UUID or a readable id on the way in - a planning agent works in
7356
+ * `P4-12`, and making it look up a UUID first is a round trip for nothing.
7357
+ *
7358
+ * One level is all this promises. Nothing forbids a subtask of a subtask, and
7359
+ * nothing walks the chain either; the board renders the direct children of a
7360
+ * task and stops.
7361
+ */
7362
+ const ParentTaskIdField = TaskId.pipe(Schema$1.annotateEncoded({ description: "The task this one is a subtask of - the parent's UUID or readable id (e.g. P4-12). Null for a task that is not part of a larger one." }));
7221
7363
  const LabelsField = Schema$1.Array(TaskLabel).check(Schema$1.isMaxLength(TASK_LABELS_MAX_COUNT)).pipe(Schema$1.annotateEncoded({ description: "Free-form tags used to group and filter tasks." }));
7222
7364
  const TaskIdField = TaskId.pipe(Schema$1.annotateEncoded({ description: "Id of the task to act on - the UUID or the readable id (e.g. P4-12)." }));
7223
7365
  const Task = Schema$1.Struct({
@@ -7259,6 +7401,20 @@ const Task = Schema$1.Struct({
7259
7401
  * to a history.
7260
7402
  */
7261
7403
  threadId: Schema$1.NullOr(ThreadId),
7404
+ /**
7405
+ * The tracker issue this task mirrors, or `null` for a task that was filed
7406
+ * here. Nullable-with-default for the same version-skew reason as
7407
+ * `readableId` above: a row from a store without the migration decodes to
7408
+ * `null` rather than failing the whole board.
7409
+ */
7410
+ externalRef: Schema$1.NullOr(TaskExternalRef).pipe(Schema$1.withDecodingDefault(Effect.succeed(null))),
7411
+ /**
7412
+ * The task this one was split out of, always as a UUID once stored - a
7413
+ * readable id given on the way in is resolved before the row is written, so
7414
+ * the link survives a board where readable ids were allocated elsewhere.
7415
+ * Nullable-with-default for the same version-skew reason as `readableId`.
7416
+ */
7417
+ parentTaskId: Schema$1.NullOr(TaskId).pipe(Schema$1.withDecodingDefault(Effect.succeed(null))),
7262
7418
  title: TrimmedNonEmptyString.check(Schema$1.isMaxLength(TASK_TITLE_MAX_LENGTH)),
7263
7419
  body: Schema$1.String,
7264
7420
  status: StoredStatusField,
@@ -7272,6 +7428,8 @@ const TaskCreateInput = Schema$1.Struct({
7272
7428
  projectId: Schema$1.optional(ProjectIdField),
7273
7429
  repositoryKey: Schema$1.optional(RepositoryKeyField),
7274
7430
  threadId: Schema$1.optional(ThreadIdField),
7431
+ externalRef: Schema$1.optional(ExternalRefField),
7432
+ parentTaskId: Schema$1.optional(ParentTaskIdField),
7275
7433
  title: TitleField,
7276
7434
  body: Schema$1.optional(BodyField),
7277
7435
  status: Schema$1.optional(StatusField),
@@ -7310,6 +7468,8 @@ const TaskUpdateInput = Schema$1.Struct({
7310
7468
  projectId: Schema$1.optional(Schema$1.NullOr(ProjectIdField)),
7311
7469
  repositoryKey: Schema$1.optional(Schema$1.NullOr(RepositoryKeyField)),
7312
7470
  threadId: Schema$1.optional(Schema$1.NullOr(ThreadIdField)),
7471
+ externalRef: Schema$1.optional(Schema$1.NullOr(ExternalRefField)),
7472
+ parentTaskId: Schema$1.optional(Schema$1.NullOr(ParentTaskIdField)),
7313
7473
  title: Schema$1.optional(TitleField),
7314
7474
  body: Schema$1.optional(BodyField),
7315
7475
  status: Schema$1.optional(StatusField),
@@ -7675,6 +7835,58 @@ var ProjectWriteFileError = class extends Schema$1.TaggedErrorClass()("ProjectWr
7675
7835
  }
7676
7836
  };
7677
7837
  //#endregion
7838
+ //#region ../../packages/contracts/src/ticket.ts
7839
+ /**
7840
+ * Reading an issue out of an external tracker.
7841
+ *
7842
+ * One call, deliberately: p4code does not sync trackers, it recognizes a ticket
7843
+ * somebody pasted and asks the tracker what it is called. Everything durable
7844
+ * that comes back is stored on a task as its {@link TaskExternalRef}.
7845
+ *
7846
+ * The tracker is reached through the MCP server the user already registered for
7847
+ * it, so there is no second place to configure credentials and no p4code-owned
7848
+ * API key. That also bounds what this can ever answer: a tracker with no MCP
7849
+ * server registered, or one never signed in to on this machine, is reported as
7850
+ * unconfigured rather than silently returning nothing.
7851
+ *
7852
+ * @module contracts/ticket
7853
+ */
7854
+ /**
7855
+ * A tracker reference as it was typed, before anything has confirmed it exists.
7856
+ *
7857
+ * Deliberately not the parsed `{ source, identifier }` pair: what a user pastes
7858
+ * is a URL as often as an id, and which tracker a bare `MOBILE-12262` belongs
7859
+ * to is a question only the resolver can answer.
7860
+ */
7861
+ const TicketResolveInput = Schema$1.Struct({ reference: TrimmedNonEmptyString });
7862
+ const TicketResolveResult = Schema$1.Struct({
7863
+ externalRef: TaskExternalRef,
7864
+ /** The issue title as the tracker reports it, for the task that mirrors it. */
7865
+ title: TrimmedNonEmptyString
7866
+ });
7867
+ /**
7868
+ * Why a reference could not be turned into a ticket.
7869
+ *
7870
+ * The reason is separate from the detail because the client acts on it: an
7871
+ * unconfigured tracker is a thing the user can fix in Settings and is worth
7872
+ * saying so about, while `unavailable` is a retry and `not_found` usually means
7873
+ * a typo. `detail` is the sentence to show; the reason decides the shape.
7874
+ */
7875
+ const TicketResolveFailureReason = Schema$1.Literals([
7876
+ "not_configured",
7877
+ "not_authorized",
7878
+ "not_found",
7879
+ "unavailable"
7880
+ ]);
7881
+ var TicketResolveError = class extends Schema$1.TaggedErrorClass()("TicketResolveError", {
7882
+ reason: TicketResolveFailureReason,
7883
+ detail: Schema$1.String
7884
+ }) {
7885
+ get message() {
7886
+ return this.detail;
7887
+ }
7888
+ };
7889
+ //#endregion
7678
7890
  //#region ../../packages/contracts/src/threadControl.ts
7679
7891
  /**
7680
7892
  * Thread control over MCP - the self-scoped toolkit an agent uses to act on
@@ -7693,6 +7905,11 @@ var ProjectWriteFileError = class extends Schema$1.TaggedErrorClass()("ProjectWr
7693
7905
  * Quick-append to memory lives here too: it is the other thing an agent wants
7694
7906
  * to do *from* a thread, and it shares the toolkit's capability and scope.
7695
7907
  *
7908
+ * Compressing an asset joins them for a different reason. It is a write the
7909
+ * agent could perform with its ordinary file tools, and the point of routing it
7910
+ * through a tool instead is that the server gets to check the rewrite before it
7911
+ * lands - and to keep the original somewhere the rewrite cannot reach.
7912
+ *
7696
7913
  * @module contracts/threadControl
7697
7914
  */
7698
7915
  const ThreadRenameInput = Schema$1.Struct({ title: TrimmedNonEmptyString.annotate({ description: "The new thread title. Replaces the current one." }) });
@@ -7719,6 +7936,23 @@ const MemoryAppendResult = Schema$1.Struct({
7719
7936
  /** Absolute path the append landed in, so the agent can cite or inspect it. */
7720
7937
  path: Schema$1.String
7721
7938
  });
7939
+ const AssetCompressInput = Schema$1.Struct({
7940
+ path: TrimmedNonEmptyString.annotate({ description: "Absolute path of the asset file to overwrite. Must sit inside one of this machine's agent asset directories: user skills, the provider config directory that holds the memory files, or the subagent definitions directory." }),
7941
+ content: Schema$1.String.check(Schema$1.isNonEmpty()).annotate({ description: "The compressed file, whole. Headings, fenced code blocks, inline code, URLs, paths and any YAML frontmatter must come back unchanged; only the prose around them may be rewritten." })
7942
+ });
7943
+ const AssetCompressResult = Schema$1.Struct({
7944
+ /** Absolute path that was rewritten. */
7945
+ path: Schema$1.String,
7946
+ /** Where the pre-compression original was kept, outside every synced directory. */
7947
+ backupPath: Schema$1.String,
7948
+ charsBefore: Schema$1.Number,
7949
+ charsAfter: Schema$1.Number,
7950
+ /** Estimated at four characters per token, not measured by a provider tokenizer. */
7951
+ estimatedTokensBefore: Schema$1.Number,
7952
+ estimatedTokensAfter: Schema$1.Number,
7953
+ /** Differences worth reporting that were not worth refusing the write over. */
7954
+ warnings: Schema$1.Array(Schema$1.String)
7955
+ });
7722
7956
  var ThreadToolUnavailableError = class extends Schema$1.TaggedErrorClass()("ThreadToolUnavailableError", {
7723
7957
  capability: Schema$1.Literal("threads"),
7724
7958
  environmentId: EnvironmentId,
@@ -7747,10 +7981,43 @@ var MemoryAppendFailedError = class extends Schema$1.TaggedErrorClass()("MemoryA
7747
7981
  return `Appending to ${this.file} failed: ${this.detail}`;
7748
7982
  }
7749
7983
  };
7984
+ /**
7985
+ * The compression will not be attempted, or the file could not be read.
7986
+ *
7987
+ * Separate from the validation failure below because they want opposite
7988
+ * responses. A refusal is final - the path is not an asset, the name looks like
7989
+ * a credential, the file is not there - and retrying it produces the same
7990
+ * answer. A validation failure is a rewrite that can be corrected.
7991
+ */
7992
+ var AssetCompressRefusedError = class extends Schema$1.TaggedErrorClass()("AssetCompressRefusedError", {
7993
+ path: Schema$1.String,
7994
+ detail: Schema$1.String
7995
+ }) {
7996
+ get message() {
7997
+ return `Compressing ${this.path} was refused: ${this.detail}`;
7998
+ }
7999
+ };
8000
+ /**
8001
+ * The rewrite lost something, and nothing was written.
8002
+ *
8003
+ * Carries the checks that failed rather than a summary, because the agent's
8004
+ * next move is to restore exactly those things from the original and call
8005
+ * again. The file on disk is untouched, so a second failure costs nothing more.
8006
+ */
8007
+ var AssetCompressValidationFailedError = class extends Schema$1.TaggedErrorClass()("AssetCompressValidationFailedError", {
8008
+ path: Schema$1.String,
8009
+ errors: Schema$1.Array(Schema$1.String)
8010
+ }) {
8011
+ get message() {
8012
+ return `The compressed ${this.path} did not validate, so nothing was written: ${this.errors.join(" ")}`;
8013
+ }
8014
+ };
7750
8015
  const ThreadControlToolError = Schema$1.Union([
7751
8016
  ThreadToolUnavailableError,
7752
8017
  ThreadRenameRejectedError,
7753
- MemoryAppendFailedError
8018
+ MemoryAppendFailedError,
8019
+ AssetCompressRefusedError,
8020
+ AssetCompressValidationFailedError
7754
8021
  ]);
7755
8022
  const ThreadWatchEventsInput = Schema$1.Struct({
7756
8023
  threadId: ThreadId.annotate({ description: "The watched thread to read events from. Must be explicitly granted." }),
@@ -8726,6 +8993,7 @@ const WS_METHODS = {
8726
8993
  tasksUpdate: "tasks.update",
8727
8994
  tasksDelete: "tasks.delete",
8728
8995
  tasksStartThread: "tasks.startThread",
8996
+ ticketsResolve: "tickets.resolve",
8729
8997
  hubGetSyncStatus: "hub.getSyncStatus",
8730
8998
  hubConnect: "hub.connect",
8731
8999
  hubDisconnect: "hub.disconnect",
@@ -9033,6 +9301,17 @@ const WsMcpOAuthDisconnectRpc = Rpc.make(WS_METHODS.mcpOAuthDisconnect, {
9033
9301
  error: Schema$1.Union([McpRegistryError, EnvironmentAuthorizationError])
9034
9302
  });
9035
9303
  /**
9304
+ * Asks the tracker what a pasted reference is, without writing anything.
9305
+ *
9306
+ * Read-only on purpose: the caller decides whether the answer becomes a task.
9307
+ * A resolve that filed a row would make an accidental paste a board entry.
9308
+ */
9309
+ const WsTicketsResolveRpc = Rpc.make(WS_METHODS.ticketsResolve, {
9310
+ payload: TicketResolveInput,
9311
+ success: TicketResolveResult,
9312
+ error: Schema$1.Union([TicketResolveError, EnvironmentAuthorizationError])
9313
+ });
9314
+ /**
9036
9315
  * The board, then every change to it, for as long as the client stays
9037
9316
  * subscribed. The leading snapshot is what makes this sufficient on its own —
9038
9317
  * a client does not list first, so there is no window between the two in which
@@ -9410,7 +9689,7 @@ const WsSubscribeAuthAccessRpc = Rpc.make(WS_METHODS.subscribeAuthAccess, {
9410
9689
  error: Schema$1.Union([AuthAccessStreamError, EnvironmentAuthorizationError]),
9411
9690
  stream: true
9412
9691
  });
9413
- const WsRpcGroup = RpcGroup.make(WsServerProbeRpc, WsServerGetConfigRpc, WsServerRefreshProvidersRpc, WsServerUpdateProviderRpc, WsServerUpdateServerRpc, WsServerUpsertKeybindingRpc, WsServerRemoveKeybindingRpc, WsServerGetSettingsRpc, WsServerUpdateSettingsRpc, WsServerDiscoverSourceControlRpc, WsServerGetProviderUsageRpc, WsServerGetTraceDiagnosticsRpc, WsServerGetProcessDiagnosticsRpc, WsServerGetProcessResourceHistoryRpc, WsServerSignalProcessRpc, WsSourceControlLookupRepositoryRpc, WsSourceControlCloneRepositoryRpc, WsSourceControlPublishRepositoryRpc, WsProjectsListEntriesRpc, WsProjectsReadFileRpc, WsProjectsSearchEntriesRpc, WsProjectsWriteFileRpc, WsShellOpenInEditorRpc, WsFilesystemBrowseRpc, WsAssetsCreateUrlRpc, WsSubscribeVcsStatusRpc, WsVcsPullRpc, WsVcsRefreshStatusRpc, WsGitRunStackedActionRpc, WsGitResolvePullRequestRpc, WsGitPreparePullRequestThreadRpc, WsVcsListRefsRpc, WsVcsCreateWorktreeRpc, WsVcsRemoveWorktreeRpc, WsVcsCreateRefRpc, WsVcsSwitchRefRpc, WsVcsInitRpc, WsReviewGetDiffPreviewRpc, WsTerminalOpenRpc, WsTerminalAttachRpc, WsTerminalWriteRpc, WsTerminalResizeRpc, WsTerminalClearRpc, WsTerminalRestartRpc, WsTerminalCloseRpc, WsSubscribeTerminalEventsRpc, WsSubscribeTerminalMetadataRpc, WsPreviewOpenRpc, WsPreviewNavigateRpc, WsPreviewResizeRpc, WsPreviewRefreshRpc, WsPreviewCloseRpc, WsPreviewListRpc, WsPreviewReportStatusRpc, WsPreviewAutomationConnectRpc, WsPreviewAutomationRespondRpc, WsPreviewAutomationFocusHostRpc, WsSubscribePreviewEventsRpc, WsSubscribeDiscoveredLocalServersRpc, WsSubscribeServerConfigRpc, WsSubscribeServerLifecycleRpc, WsSubscribeAuthAccessRpc, WsTasksListRpc, WsTasksGetRpc, WsTasksCreateRpc, WsTasksUpdateRpc, WsTasksDeleteRpc, WsTasksStartThreadRpc, WsSubscribeTasksRpc, WsHubGetSyncStatusRpc, WsHubConnectRpc, WsHubDisconnectRpc, WsHubSetSyncModeRpc, WsHubSetShareModeRpc, WsHubMintTokenRpc, WsSkillsSyncRpc, WsSkillsPublishRpc, WsSkillsPublishAllRpc, WsSkillsUnpublishRpc, WsAssetsReadRpc, WsAssetsSaveRpc, WsAssetsDeleteRpc, WsAssetsCreateLocalRpc, WsAssetsRemoveLocalRpc, WsSkillRegistrySearchRpc, WsSkillRegistryFetchRpc, WsMcpListRpc, WsMcpSaveRpc, WsMcpRemoveRpc, WsMcpSetSecretRpc, WsMcpOAuthBeginRpc, WsMcpOAuthDisconnectRpc, WsOrchestrationDispatchCommandRpc, WsOrchestrationGetTurnDiffRpc, WsOrchestrationGetFullThreadDiffRpc, WsOrchestrationGetArchivedShellSnapshotRpc, WsOrchestrationSubscribeShellRpc, WsOrchestrationSubscribeThreadRpc);
9692
+ const WsRpcGroup = RpcGroup.make(WsServerProbeRpc, WsServerGetConfigRpc, WsServerRefreshProvidersRpc, WsServerUpdateProviderRpc, WsServerUpdateServerRpc, WsServerUpsertKeybindingRpc, WsServerRemoveKeybindingRpc, WsServerGetSettingsRpc, WsServerUpdateSettingsRpc, WsServerDiscoverSourceControlRpc, WsServerGetProviderUsageRpc, WsServerGetTraceDiagnosticsRpc, WsServerGetProcessDiagnosticsRpc, WsServerGetProcessResourceHistoryRpc, WsServerSignalProcessRpc, WsSourceControlLookupRepositoryRpc, WsSourceControlCloneRepositoryRpc, WsSourceControlPublishRepositoryRpc, WsProjectsListEntriesRpc, WsProjectsReadFileRpc, WsProjectsSearchEntriesRpc, WsProjectsWriteFileRpc, WsShellOpenInEditorRpc, WsFilesystemBrowseRpc, WsAssetsCreateUrlRpc, WsSubscribeVcsStatusRpc, WsVcsPullRpc, WsVcsRefreshStatusRpc, WsGitRunStackedActionRpc, WsGitResolvePullRequestRpc, WsGitPreparePullRequestThreadRpc, WsVcsListRefsRpc, WsVcsCreateWorktreeRpc, WsVcsRemoveWorktreeRpc, WsVcsCreateRefRpc, WsVcsSwitchRefRpc, WsVcsInitRpc, WsReviewGetDiffPreviewRpc, WsTerminalOpenRpc, WsTerminalAttachRpc, WsTerminalWriteRpc, WsTerminalResizeRpc, WsTerminalClearRpc, WsTerminalRestartRpc, WsTerminalCloseRpc, WsSubscribeTerminalEventsRpc, WsSubscribeTerminalMetadataRpc, WsPreviewOpenRpc, WsPreviewNavigateRpc, WsPreviewResizeRpc, WsPreviewRefreshRpc, WsPreviewCloseRpc, WsPreviewListRpc, WsPreviewReportStatusRpc, WsPreviewAutomationConnectRpc, WsPreviewAutomationRespondRpc, WsPreviewAutomationFocusHostRpc, WsSubscribePreviewEventsRpc, WsSubscribeDiscoveredLocalServersRpc, WsSubscribeServerConfigRpc, WsSubscribeServerLifecycleRpc, WsSubscribeAuthAccessRpc, WsTasksListRpc, WsTasksGetRpc, WsTasksCreateRpc, WsTasksUpdateRpc, WsTasksDeleteRpc, WsTasksStartThreadRpc, WsSubscribeTasksRpc, WsHubGetSyncStatusRpc, WsHubConnectRpc, WsHubDisconnectRpc, WsHubSetSyncModeRpc, WsHubSetShareModeRpc, WsHubMintTokenRpc, WsSkillsSyncRpc, WsSkillsPublishRpc, WsSkillsPublishAllRpc, WsSkillsUnpublishRpc, WsAssetsReadRpc, WsAssetsSaveRpc, WsAssetsDeleteRpc, WsAssetsCreateLocalRpc, WsAssetsRemoveLocalRpc, WsSkillRegistrySearchRpc, WsSkillRegistryFetchRpc, WsMcpListRpc, WsMcpSaveRpc, WsMcpRemoveRpc, WsMcpSetSecretRpc, WsMcpOAuthBeginRpc, WsMcpOAuthDisconnectRpc, WsTicketsResolveRpc, WsOrchestrationDispatchCommandRpc, WsOrchestrationGetTurnDiffRpc, WsOrchestrationGetFullThreadDiffRpc, WsOrchestrationGetArchivedShellSnapshotRpc, WsOrchestrationSubscribeShellRpc, WsOrchestrationSubscribeThreadRpc);
9414
9693
  //#endregion
9415
9694
  //#region ../../packages/shared/src/oauthScope.ts
9416
9695
  const OAUTH_SCOPE_TOKEN = /^[\u0021\u0023-\u005b\u005d-\u007e]+$/u;
@@ -9556,7 +9835,7 @@ function deriveAuthClientMetadata(input) {
9556
9835
  //#endregion
9557
9836
  //#region src/auth/EnvironmentAuthPolicy.ts
9558
9837
  var EnvironmentAuthPolicy = class extends Context.Service()("@p4code/cli/auth/EnvironmentAuthPolicy") {};
9559
- const make$73 = Effect.gen(function* () {
9838
+ const make$74 = Effect.gen(function* () {
9560
9839
  const config = yield* ServerConfig$1;
9561
9840
  const isRemoteReachable = isRemoteReachableHost(config.host);
9562
9841
  const policy = config.mode === "desktop" ? isRemoteReachable ? "remote-reachable" : "desktop-managed-local" : isRemoteReachable ? "remote-reachable" : "loopback-browser";
@@ -9574,7 +9853,7 @@ const make$73 = Effect.gen(function* () {
9574
9853
  };
9575
9854
  return EnvironmentAuthPolicy.of({ getDescriptor: () => Effect.succeed(descriptor).pipe(Effect.withSpan("EnvironmentAuthPolicy.getDescriptor")) });
9576
9855
  });
9577
- const layer$69 = Layer.effect(EnvironmentAuthPolicy, make$73);
9856
+ const layer$70 = Layer.effect(EnvironmentAuthPolicy, make$74);
9578
9857
  //#endregion
9579
9858
  //#region src/persistence/Errors.ts
9580
9859
  function summarizeSchemaIssue(issue) {
@@ -9755,7 +10034,7 @@ function toPersistenceSqlOrDecodeError$6(sqlOperation, decodeOperation, correlat
9755
10034
  cause
9756
10035
  });
9757
10036
  }
9758
- const make$72 = Effect.gen(function* () {
10037
+ const make$73 = Effect.gen(function* () {
9759
10038
  const sql = yield* SqlClient.SqlClient;
9760
10039
  const createSessionRow = SqlSchema.void({
9761
10040
  Request: CreateAuthSessionInput,
@@ -9889,7 +10168,7 @@ const make$72 = Effect.gen(function* () {
9889
10168
  setLastConnectedAt
9890
10169
  };
9891
10170
  });
9892
- const layer$68 = Layer.effect(AuthSessionRepository, make$72);
10171
+ const layer$69 = Layer.effect(AuthSessionRepository, make$73);
9893
10172
  //#endregion
9894
10173
  //#region src/auth/ServerSecretStore.ts
9895
10174
  const secretStoreErrorContext = {
@@ -9956,7 +10235,7 @@ const isSecretStoreError = Schema$1.is(SecretStoreError);
9956
10235
  const isPlatformError = (value) => Predicate.isTagged(value, "PlatformError");
9957
10236
  const isSecretAlreadyExistsError = (error) => "cause" in error && isPlatformError(error.cause) && error.cause.reason._tag === "AlreadyExists";
9958
10237
  var ServerSecretStore = class extends Context.Service()("@p4code/cli/auth/ServerSecretStore") {};
9959
- const make$71 = Effect.gen(function* () {
10238
+ const make$72 = Effect.gen(function* () {
9960
10239
  const crypto = yield* Crypto.Crypto;
9961
10240
  const fileSystem = yield* FileSystem.FileSystem;
9962
10241
  const path = yield* Path.Path;
@@ -10026,7 +10305,7 @@ const make$71 = Effect.gen(function* () {
10026
10305
  remove
10027
10306
  });
10028
10307
  });
10029
- const layer$67 = Layer.effect(ServerSecretStore, make$71);
10308
+ const layer$68 = Layer.effect(ServerSecretStore, make$72);
10030
10309
  //#endregion
10031
10310
  //#region src/auth/SessionStore.ts
10032
10311
  var MalformedSessionTokenError = class extends Schema$1.TaggedErrorClass()("MalformedSessionTokenError", {}) {
@@ -10264,7 +10543,7 @@ function toAuthClientSession(input) {
10264
10543
  current: false
10265
10544
  };
10266
10545
  }
10267
- const make$70 = Effect.gen(function* () {
10546
+ const make$71 = Effect.gen(function* () {
10268
10547
  const crypto = yield* Crypto.Crypto;
10269
10548
  const serverConfig = yield* ServerConfig$1;
10270
10549
  const secretStore = yield* ServerSecretStore;
@@ -10578,7 +10857,7 @@ const make$70 = Effect.gen(function* () {
10578
10857
  markDisconnected
10579
10858
  });
10580
10859
  });
10581
- const layer$66 = Layer.effect(SessionStore, make$70).pipe(Layer.provideMerge(layer$68));
10860
+ const layer$67 = Layer.effect(SessionStore, make$71).pipe(Layer.provideMerge(layer$69));
10582
10861
  //#endregion
10583
10862
  //#region src/persistence/AuthPairingLinks.ts
10584
10863
  const AuthPairingLinkRecord = Schema$1.Struct({
@@ -10639,7 +10918,7 @@ function toPersistenceSqlOrDecodeError$5(sqlOperation, decodeOperation, correlat
10639
10918
  cause
10640
10919
  });
10641
10920
  }
10642
- const make$69 = Effect.gen(function* () {
10921
+ const make$70 = Effect.gen(function* () {
10643
10922
  const sql = yield* SqlClient.SqlClient;
10644
10923
  const createPairingLinkRow = SqlSchema.void({
10645
10924
  Request: CreateAuthPairingLinkInput,
@@ -10774,7 +11053,7 @@ const make$69 = Effect.gen(function* () {
10774
11053
  getByCredential
10775
11054
  };
10776
11055
  });
10777
- const layer$65 = Layer.effect(AuthPairingLinkRepository, make$69);
11056
+ const layer$66 = Layer.effect(AuthPairingLinkRepository, make$70);
10778
11057
  //#endregion
10779
11058
  //#region src/auth/PairingGrantStore.ts
10780
11059
  var UnknownBootstrapCredentialError = class extends Schema$1.TaggedErrorClass()("UnknownBootstrapCredentialError", {}) {
@@ -10869,7 +11148,7 @@ const DEV_STARTUP_TTL_HOURS = Duration.hours(24);
10869
11148
  const PAIRING_TOKEN_ALPHABET = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
10870
11149
  const PAIRING_TOKEN_LENGTH = 12;
10871
11150
  const PAIRING_TOKEN_REJECTION_LIMIT = Math.floor(256 / 32) * 32;
10872
- const make$68 = Effect.gen(function* () {
11151
+ const make$69 = Effect.gen(function* () {
10873
11152
  const crypto = yield* Crypto.Crypto;
10874
11153
  const config = yield* ServerConfig$1;
10875
11154
  const pairingLinks = yield* AuthPairingLinkRepository;
@@ -11067,7 +11346,7 @@ const make$68 = Effect.gen(function* () {
11067
11346
  consume
11068
11347
  });
11069
11348
  });
11070
- const layer$64 = Layer.effect(PairingGrantStore, make$68).pipe(Layer.provideMerge(layer$65));
11349
+ const layer$65 = Layer.effect(PairingGrantStore, make$69).pipe(Layer.provideMerge(layer$66));
11071
11350
  //#endregion
11072
11351
  //#region src/persistence/Migrations/001_OrchestrationEvents.ts
11073
11352
  var _001_OrchestrationEvents_default = Effect.gen(function* () {
@@ -12520,6 +12799,75 @@ var _039_ProjectionThreadsCompressMode_default = Effect.gen(function* () {
12520
12799
  `;
12521
12800
  });
12522
12801
  //#endregion
12802
+ //#region src/persistence/Migrations/040_TaskExternalRef.ts
12803
+ /**
12804
+ * The tracker issue a task mirrors, for tasks that did not start on this board.
12805
+ *
12806
+ * Stored as one JSON blob rather than three columns because nothing queries
12807
+ * inside it except the uniqueness check below, and a tracker's shape is not
12808
+ * ours to normalize - a second source will bring fields Linear does not have.
12809
+ *
12810
+ * The unique index is over the identifier alone, extracted from the JSON: it is
12811
+ * what stops a second paste of the same ticket from filing a duplicate task.
12812
+ * Partial, so the many rows with no external ref do not collide on NULL.
12813
+ */
12814
+ var _040_TaskExternalRef_default = Effect.gen(function* () {
12815
+ const sql = yield* SqlClient.SqlClient;
12816
+ yield* sql`ALTER TABLE tasks ADD COLUMN external_ref_json TEXT`;
12817
+ yield* sql`
12818
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_tasks_external_ref_identifier
12819
+ ON tasks(json_extract(external_ref_json, '$.identifier'))
12820
+ WHERE external_ref_json IS NOT NULL
12821
+ `;
12822
+ });
12823
+ //#endregion
12824
+ //#region src/persistence/Migrations/041_TaskParentTaskId.ts
12825
+ /**
12826
+ * The task a task was split out of, for the plan phase's subtasks.
12827
+ *
12828
+ * A plain column rather than a join table: a task has at most one parent, and
12829
+ * the question asked of it is always "children of this row", which one indexed
12830
+ * column answers.
12831
+ *
12832
+ * No foreign key. Tasks are synced between machines and a child can arrive
12833
+ * before its parent does, or outlive it - a constraint would reject the write
12834
+ * rather than leave a link that resolves later. The board treats a parent it
12835
+ * cannot find as no parent at all.
12836
+ *
12837
+ * The index is non-unique and partial: many children per parent, and the many
12838
+ * rows that are nobody's child stay out of it.
12839
+ */
12840
+ var _041_TaskParentTaskId_default = Effect.gen(function* () {
12841
+ const sql = yield* SqlClient.SqlClient;
12842
+ yield* sql`ALTER TABLE tasks ADD COLUMN parent_task_id TEXT`;
12843
+ yield* sql`
12844
+ CREATE INDEX IF NOT EXISTS idx_tasks_parent_task_id
12845
+ ON tasks(parent_task_id)
12846
+ WHERE parent_task_id IS NOT NULL
12847
+ `;
12848
+ });
12849
+ //#endregion
12850
+ //#region src/persistence/Migrations/042_ProjectionThreadsUnpromptedSubagents.ts
12851
+ /**
12852
+ * Whether a thread lets the model spawn subagents without being asked, moved
12853
+ * from a machine-wide setting onto the thread itself.
12854
+ *
12855
+ * Stored as an integer beside `has_actionable_proposed_plan` rather than as a
12856
+ * SQLite boolean, which does not exist; the projection reads it back as
12857
+ * `> 0`.
12858
+ *
12859
+ * The default is `1` — subagents allowed — because that is what every existing
12860
+ * thread has been doing under the old server setting. Anything else would
12861
+ * change the behaviour of live threads on upgrade, which is not a migration's
12862
+ * business.
12863
+ */
12864
+ var _042_ProjectionThreadsUnpromptedSubagents_default = Effect.gen(function* () {
12865
+ yield* (yield* SqlClient.SqlClient)`
12866
+ ALTER TABLE projection_threads
12867
+ ADD COLUMN unprompted_subagents INTEGER NOT NULL DEFAULT 1
12868
+ `;
12869
+ });
12870
+ //#endregion
12523
12871
  //#region src/persistence/Migrations.ts
12524
12872
  /**
12525
12873
  * MigrationsLive - Migration runner with inline loader
@@ -12735,6 +13083,21 @@ const migrationEntries = [
12735
13083
  39,
12736
13084
  "ProjectionThreadsCompressMode",
12737
13085
  _039_ProjectionThreadsCompressMode_default
13086
+ ],
13087
+ [
13088
+ 40,
13089
+ "TaskExternalRef",
13090
+ _040_TaskExternalRef_default
13091
+ ],
13092
+ [
13093
+ 41,
13094
+ "TaskParentTaskId",
13095
+ _041_TaskParentTaskId_default
13096
+ ],
13097
+ [
13098
+ 42,
13099
+ "ProjectionThreadsUnpromptedSubagents",
13100
+ _042_ProjectionThreadsUnpromptedSubagents_default
12738
13101
  ]
12739
13102
  ];
12740
13103
  const makeMigrationLoader = (throughId) => Migrator.fromRecord(Object.fromEntries(migrationEntries.filter(([id]) => throughId === void 0 || id <= throughId).map(([id, name, migration]) => [`${id}_${name}`, migration])));
@@ -12922,7 +13285,7 @@ function parseBearerToken(request) {
12922
13285
  const token = header.slice(7).trim();
12923
13286
  return token.length > 0 ? token : null;
12924
13287
  }
12925
- const make$67 = Effect.gen(function* () {
13288
+ const make$68 = Effect.gen(function* () {
12926
13289
  const policy = yield* EnvironmentAuthPolicy;
12927
13290
  const bootstrapCredentials = yield* PairingGrantStore;
12928
13291
  const sessions = yield* SessionStore;
@@ -13117,9 +13480,9 @@ const make$67 = Effect.gen(function* () {
13117
13480
  issueStartupPairingUrl
13118
13481
  });
13119
13482
  });
13120
- const layer$63 = Layer.effect(EnvironmentAuth, make$67).pipe(Layer.provideMerge(layer$64), Layer.provideMerge(layer$66), Layer.provideMerge(layer$69));
13121
- const storageLayer = Layer.mergeAll(layer$67, layerConfig);
13122
- const runtimeLayer = layer$63.pipe(Layer.provideMerge(storageLayer));
13483
+ const layer$64 = Layer.effect(EnvironmentAuth, make$68).pipe(Layer.provideMerge(layer$65), Layer.provideMerge(layer$67), Layer.provideMerge(layer$70));
13484
+ const storageLayer = Layer.mergeAll(layer$68, layerConfig);
13485
+ const runtimeLayer = layer$64.pipe(Layer.provideMerge(storageLayer));
13123
13486
  //#endregion
13124
13487
  //#region src/cliAuthFormat.ts
13125
13488
  const newline = "\n";
@@ -13874,7 +14237,7 @@ const runWithEnvironmentAuth = (flags, run, options) => Effect.gen(function* ()
13874
14237
  }
13875
14238
  return yield* Effect.gen(function* () {
13876
14239
  return yield* run(yield* EnvironmentAuth);
13877
- }).pipe(Effect.provide(Layer.mergeAll(runtimeLayer).pipe(Layer.provide(layer$70(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)))));
14240
+ }).pipe(Effect.provide(Layer.mergeAll(runtimeLayer).pipe(Layer.provide(layer$71(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)))));
13878
14241
  });
13879
14242
  const ttlFlag = Flag.string("ttl").pipe(Flag.withSchema(DurationFromString), Flag.withDescription("TTL, for example `5m`, `1h`, `30d`, or `15 minutes`."), Flag.optional);
13880
14243
  const jsonFlag = Flag.boolean("json").pipe(Flag.withDescription("Emit JSON instead of human-readable output."), Flag.withDefault(false));
@@ -14014,7 +14377,7 @@ const DEFAULT_LIMITS = {
14014
14377
  windowMillis: FAILURE_WINDOW_MS,
14015
14378
  blockMillis: BLOCK_DURATION_MS
14016
14379
  };
14017
- const make$66 = Effect.fn("HubAuthThrottle.make")(function* (limits = DEFAULT_LIMITS) {
14380
+ const make$67 = Effect.fn("HubAuthThrottle.make")(function* (limits = DEFAULT_LIMITS) {
14018
14381
  const state = yield* Ref.make(initialThrottleState);
14019
14382
  return HubAuthThrottle.of({
14020
14383
  shouldRefuse: Effect.gen(function* () {
@@ -14028,7 +14391,7 @@ const make$66 = Effect.fn("HubAuthThrottle.make")(function* (limits = DEFAULT_LI
14028
14391
  })
14029
14392
  });
14030
14393
  });
14031
- const layer$62 = Layer.effect(HubAuthThrottle, make$66());
14394
+ const layer$63 = Layer.effect(HubAuthThrottle, make$67());
14032
14395
  //#endregion
14033
14396
  //#region src/hub/HubAuth.ts
14034
14397
  /**
@@ -14342,7 +14705,10 @@ const DeleteTaskInput = Schema$1.Struct({ taskId: TaskId });
14342
14705
  var TaskRepository = class extends Context.Service()("@p4code/cli/persistence/Services/Tasks/TaskRepository") {};
14343
14706
  //#endregion
14344
14707
  //#region src/persistence/Layers/Tasks.ts
14345
- const TaskDbRow = Task.mapFields(Struct.assign({ labels: Schema$1.fromJsonString(Schema$1.Array(TaskLabel)) }));
14708
+ const TaskDbRow = Task.mapFields(Struct.assign({
14709
+ labels: Schema$1.fromJsonString(Schema$1.Array(TaskLabel)),
14710
+ externalRef: Schema$1.NullOr(Schema$1.fromJsonString(TaskExternalRef))
14711
+ }));
14346
14712
  /**
14347
14713
  * The `list` filter with every field normalized to a bound `null` rather than
14348
14714
  * an absent key. Each predicate is then `(:param IS NULL OR column = :param)`,
@@ -14362,6 +14728,8 @@ const TASK_COLUMNS = `
14362
14728
  project_id AS "projectId",
14363
14729
  repository_key AS "repositoryKey",
14364
14730
  thread_id AS "threadId",
14731
+ external_ref_json AS "externalRef",
14732
+ parent_task_id AS "parentTaskId",
14365
14733
  title,
14366
14734
  body,
14367
14735
  status,
@@ -14385,6 +14753,8 @@ const makeTaskRepository = Effect.gen(function* () {
14385
14753
  project_id,
14386
14754
  repository_key,
14387
14755
  thread_id,
14756
+ external_ref_json,
14757
+ parent_task_id,
14388
14758
  title,
14389
14759
  body,
14390
14760
  status,
@@ -14401,6 +14771,8 @@ const makeTaskRepository = Effect.gen(function* () {
14401
14771
  ${row.projectId},
14402
14772
  ${row.repositoryKey},
14403
14773
  ${row.threadId},
14774
+ ${row.externalRef === null ? null : JSON.stringify(row.externalRef)},
14775
+ ${row.parentTaskId},
14404
14776
  ${row.title},
14405
14777
  ${row.body},
14406
14778
  ${row.status},
@@ -14417,6 +14789,8 @@ const makeTaskRepository = Effect.gen(function* () {
14417
14789
  project_id = excluded.project_id,
14418
14790
  repository_key = excluded.repository_key,
14419
14791
  thread_id = excluded.thread_id,
14792
+ external_ref_json = excluded.external_ref_json,
14793
+ parent_task_id = excluded.parent_task_id,
14420
14794
  title = excluded.title,
14421
14795
  body = excluded.body,
14422
14796
  status = excluded.status,
@@ -14515,6 +14889,8 @@ const makeTaskRepository = Effect.gen(function* () {
14515
14889
  ...input.projectId !== void 0 ? { projectId: input.projectId } : {},
14516
14890
  ...input.repositoryKey !== void 0 ? { repositoryKey: input.repositoryKey } : {},
14517
14891
  ...input.threadId !== void 0 ? { threadId: input.threadId } : {},
14892
+ ...input.externalRef !== void 0 ? { externalRef: input.externalRef } : {},
14893
+ ...input.parentTaskId !== void 0 ? { parentTaskId: input.parentTaskId } : {},
14518
14894
  ...input.title !== void 0 ? { title: input.title } : {},
14519
14895
  ...input.body !== void 0 ? { body: input.body } : {},
14520
14896
  ...input.status !== void 0 ? { status: input.status } : {},
@@ -14553,6 +14929,7 @@ const makeTaskRepository = Effect.gen(function* () {
14553
14929
  ...input.patch.projectId !== void 0 ? { projectId: input.patch.projectId } : {},
14554
14930
  ...input.patch.repositoryKey !== void 0 ? { repositoryKey: input.patch.repositoryKey } : {},
14555
14931
  ...input.patch.threadId !== void 0 ? { threadId: input.patch.threadId } : {},
14932
+ ...input.patch.parentTaskId !== void 0 ? { parentTaskId: input.patch.parentTaskId } : {},
14556
14933
  ...input.patch.title !== void 0 ? { title: input.patch.title } : {},
14557
14934
  ...input.patch.body !== void 0 ? { body: input.patch.body } : {},
14558
14935
  ...input.patch.status !== void 0 ? { status: input.patch.status } : {},
@@ -14686,6 +15063,8 @@ const createTaskRoute = HttpRouter.add("POST", "/tasks", respondToHubFailures(Ef
14686
15063
  projectId: input.projectId ?? null,
14687
15064
  repositoryKey: input.repositoryKey ?? null,
14688
15065
  threadId: input.threadId ?? null,
15066
+ externalRef: input.externalRef ?? null,
15067
+ parentTaskId: input.parentTaskId ?? null,
14689
15068
  title: input.title,
14690
15069
  body: input.body ?? "",
14691
15070
  status: input.status ?? "backlog",
@@ -14827,7 +15206,7 @@ const deleteAssetRoute = HttpRouter.add("DELETE", "/assets/:kind/:name", respond
14827
15206
  const outcome = yield* (yield* AgentAssetRepository).deleteByName(requested);
14828
15207
  return outcome._tag === "conflict" ? assetConflict(Option.getOrNull(outcome.current)) : HttpServerResponse.empty({ status: 204 });
14829
15208
  })));
14830
- const layer$61 = Layer.mergeAll(healthRoute, listTasksRoute, getTaskRoute, createTaskRoute, updateTaskRoute, deleteTaskRoute, listAssetsRoute, getAssetRoute, putAssetRoute, deleteAssetRoute);
15209
+ const layer$62 = Layer.mergeAll(healthRoute, listTasksRoute, getTaskRoute, createTaskRoute, updateTaskRoute, deleteTaskRoute, listAssetsRoute, getAssetRoute, putAssetRoute, deleteAssetRoute);
14831
15210
  //#endregion
14832
15211
  //#region src/hub/Migrations/001_Tasks.ts
14833
15212
  /**
@@ -14989,6 +15368,38 @@ var _005_TaskOrderKeys_default = Effect.gen(function* () {
14989
15368
  yield* (yield* SqlClient.SqlClient)`ALTER TABLE tasks ADD COLUMN order_key TEXT`;
14990
15369
  });
14991
15370
  //#endregion
15371
+ //#region src/hub/Migrations/006_TaskExternalRef.ts
15372
+ /**
15373
+ * The tracker issue a task mirrors, mirroring the environment server's
15374
+ * migration 040. The hub carries the column so a task filed against a Linear
15375
+ * ticket on one machine still reads as that ticket on another.
15376
+ */
15377
+ var _006_TaskExternalRef_default = Effect.gen(function* () {
15378
+ const sql = yield* SqlClient.SqlClient;
15379
+ yield* sql`ALTER TABLE tasks ADD COLUMN external_ref_json TEXT`;
15380
+ yield* sql`
15381
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_tasks_external_ref_identifier
15382
+ ON tasks(json_extract(external_ref_json, '$.identifier'))
15383
+ WHERE external_ref_json IS NOT NULL
15384
+ `;
15385
+ });
15386
+ //#endregion
15387
+ //#region src/hub/Migrations/007_TaskParentTaskId.ts
15388
+ /**
15389
+ * The task a task was split out of, mirroring the environment server's
15390
+ * migration 041. The hub carries the column so a plan filed on one machine
15391
+ * still reads as a plan on another, rather than as a handful of loose tasks.
15392
+ */
15393
+ var _007_TaskParentTaskId_default = Effect.gen(function* () {
15394
+ const sql = yield* SqlClient.SqlClient;
15395
+ yield* sql`ALTER TABLE tasks ADD COLUMN parent_task_id TEXT`;
15396
+ yield* sql`
15397
+ CREATE INDEX IF NOT EXISTS idx_tasks_parent_task_id
15398
+ ON tasks(parent_task_id)
15399
+ WHERE parent_task_id IS NOT NULL
15400
+ `;
15401
+ });
15402
+ //#endregion
14992
15403
  //#region src/hub/Migrations.ts
14993
15404
  /**
14994
15405
  * Hub migrations.
@@ -15024,6 +15435,16 @@ const hubMigrationEntries = [
15024
15435
  5,
15025
15436
  "TaskOrderKeys",
15026
15437
  _005_TaskOrderKeys_default
15438
+ ],
15439
+ [
15440
+ 6,
15441
+ "TaskExternalRef",
15442
+ _006_TaskExternalRef_default
15443
+ ],
15444
+ [
15445
+ 7,
15446
+ "TaskParentTaskId",
15447
+ _007_TaskParentTaskId_default
15027
15448
  ]
15028
15449
  ];
15029
15450
  const hubMigrationLoader = Migrator.fromRecord(Object.fromEntries(hubMigrationEntries.map(([id, name, migration]) => [`${id}_${name}`, migration])));
@@ -15106,7 +15527,7 @@ const announce = Layer.effectDiscard(Effect.gen(function* () {
15106
15527
  });
15107
15528
  if (isRemoteReachableHost(config.host)) yield* Effect.logWarning("Hub is reachable beyond loopback. The bearer token is the only control — repeated auth failures are not rate limited.", { host: config.host });
15108
15529
  }));
15109
- const makeHubLayer = HttpRouter.serve(layer$61).pipe(Layer.provideMerge(announce), Layer.provideMerge(HubHttpServerLive), Layer.provideMerge(layer$62), Layer.provideMerge(TaskRepositoryLive), Layer.provideMerge(AgentAssetRepositoryLive), Layer.provideMerge(HubPersistenceLive));
15530
+ const makeHubLayer = HttpRouter.serve(layer$62).pipe(Layer.provideMerge(announce), Layer.provideMerge(HubHttpServerLive), Layer.provideMerge(layer$63), Layer.provideMerge(TaskRepositoryLive), Layer.provideMerge(AgentAssetRepositoryLive), Layer.provideMerge(HubPersistenceLive));
15110
15531
  const runHub = Layer.launch(makeHubLayer);
15111
15532
  //#endregion
15112
15533
  //#region src/atomicWrite.ts
@@ -15230,7 +15651,7 @@ function stripDefaultServerSettings(current, defaults) {
15230
15651
  }
15231
15652
  return Object.is(current, defaults) ? void 0 : current;
15232
15653
  }
15233
- const make$65 = Effect.gen(function* () {
15654
+ const make$66 = Effect.gen(function* () {
15234
15655
  const { settingsPath } = yield* ServerConfig$1;
15235
15656
  const fs = yield* FileSystem.FileSystem;
15236
15657
  const pathService = yield* Path.Path;
@@ -15451,7 +15872,7 @@ const make$65 = Effect.gen(function* () {
15451
15872
  }
15452
15873
  };
15453
15874
  });
15454
- const layer$60 = Layer.effect(ServerSettingsService, make$65);
15875
+ const layer$61 = Layer.effect(ServerSettingsService, make$66);
15455
15876
  //#endregion
15456
15877
  //#region src/pathExpansion.ts
15457
15878
  /**
@@ -15793,7 +16214,7 @@ function claudeEntryFromRegistration(registration) {
15793
16214
  };
15794
16215
  }
15795
16216
  var ClaudeMcpFiles = class extends Context.Service()("@p4code/cli/mcp/ClaudeMcpFiles") {};
15796
- const make$64 = Effect.gen(function* () {
16217
+ const make$65 = Effect.gen(function* () {
15797
16218
  const fileSystem = yield* FileSystem.FileSystem;
15798
16219
  const path = yield* Path.Path;
15799
16220
  const services = yield* Effect.context();
@@ -15858,7 +16279,7 @@ const make$64 = Effect.gen(function* () {
15858
16279
  removeProject: (projectDir, name) => removeAt(Effect.succeed(projectFile(projectDir)))(name)
15859
16280
  };
15860
16281
  });
15861
- const layer$59 = Layer.effect(ClaudeMcpFiles, make$64);
16282
+ const layer$60 = Layer.effect(ClaudeMcpFiles, make$65);
15862
16283
  Layer.succeed(ClaudeMcpFiles, {
15863
16284
  readUser: Effect.succeed([]),
15864
16285
  upsertUser: () => Effect.fail(new McpRegistryError({ detail: "No Claude config in tests." })),
@@ -15994,7 +16415,7 @@ const decodeClientRegistration = Schema$1.decodeUnknownExit(ClientRegistrationRe
15994
16415
  const decodeTokenResponse = Schema$1.decodeUnknownExit(TokenResponse);
15995
16416
  var McpOAuth = class extends Context.Service()("@p4code/cli/mcp/McpOAuth") {};
15996
16417
  const registryError = (detail) => new McpRegistryError({ detail });
15997
- const make$63 = Effect.gen(function* () {
16418
+ const make$64 = Effect.gen(function* () {
15998
16419
  const config = yield* ServerConfig$1;
15999
16420
  const secrets = yield* ServerSecretStore;
16000
16421
  const http = yield* HttpClient.HttpClient;
@@ -16314,7 +16735,7 @@ const make$63 = Effect.gen(function* () {
16314
16735
  accessTokenFor
16315
16736
  };
16316
16737
  });
16317
- const layer$58 = Layer.effect(McpOAuth, make$63);
16738
+ const layer$59 = Layer.effect(McpOAuth, make$64);
16318
16739
  Layer.succeed(McpOAuth, {
16319
16740
  statusFor: () => Effect.succeed(Option.none()),
16320
16741
  begin: () => Effect.fail(new McpRegistryError({ detail: "OAuth sign-in is not available." })),
@@ -16332,7 +16753,7 @@ const decodeRegistration$1 = Schema$1.decodeUnknownExit(RegistrationFromJson$1);
16332
16753
  const encodeRegistration = Schema$1.encodeSync(RegistrationFromJson$1);
16333
16754
  var McpRegistry = class extends Context.Service()("@p4code/cli/mcp/McpRegistry") {};
16334
16755
  const slotsOf = (registration) => registration.secrets ?? [];
16335
- const make$62 = Effect.gen(function* () {
16756
+ const make$63 = Effect.gen(function* () {
16336
16757
  const config = yield* ServerConfig$1;
16337
16758
  const secrets = yield* ServerSecretStore;
16338
16759
  const oauth = yield* McpOAuth;
@@ -16483,7 +16904,7 @@ const make$62 = Effect.gen(function* () {
16483
16904
  }).pipe(Effect.provide(services), Effect.catchCause((cause) => Effect.logWarning("mcp registry resolve failed", { cause }).pipe(Effect.as({}))))
16484
16905
  };
16485
16906
  });
16486
- const layer$57 = Layer.effect(McpRegistry, make$62);
16907
+ const layer$58 = Layer.effect(McpRegistry, make$63);
16487
16908
  //#endregion
16488
16909
  //#region src/sync/skillDirectory.ts
16489
16910
  /**
@@ -16539,7 +16960,7 @@ const isIgnoredEntry = (entry) => IGNORED_ENTRIES.has(entry) || entry.startsWith
16539
16960
  * Exported so the single-file kinds ask it the same way this one does, rather
16540
16961
  * than each keeping a copy that could drift into a different answer.
16541
16962
  */
16542
- const looksBinary$1 = (content) => content.includes("\0");
16963
+ const looksBinary$2 = (content) => content.includes("\0");
16543
16964
  /**
16544
16965
  * Read one directory as an asset's files, or say why it cannot be one.
16545
16966
  *
@@ -16571,7 +16992,7 @@ const readSkillFiles = Effect.fn("skillDirectory.readSkillFiles")(function* (roo
16571
16992
  if (files.length >= 512) return "too-many-files";
16572
16993
  const content = yield* fileSystem.readFileString(absolute).pipe(Effect.orElseSucceed(() => void 0));
16573
16994
  if (content === void 0) return "unreadable";
16574
- if (looksBinary$1(content)) return "binary-file";
16995
+ if (looksBinary$2(content)) return "binary-file";
16575
16996
  if (content.length > 524288) return "file-too-large";
16576
16997
  files.push({
16577
16998
  path: relative,
@@ -16695,7 +17116,7 @@ const readAgentDefinitions = Effect.fn("agentDefinitionFiles.read")(function* (a
16695
17116
  });
16696
17117
  continue;
16697
17118
  }
16698
- if (looksBinary$1(content)) {
17119
+ if (looksBinary$2(content)) {
16699
17120
  refused.push({
16700
17121
  name: entry,
16701
17122
  reason: "binary-file"
@@ -16862,7 +17283,7 @@ const formatHubLink = (input) => encodeStoredHubLink({
16862
17283
  shareMode: input.shareMode
16863
17284
  });
16864
17285
  const fromEnvironment = (environment) => validateHubLink(environment.P4CODE_HUB_URL ?? "", environment.P4CODE_HUB_TOKEN ?? "");
16865
- const make$61 = Effect.fn("HubLink.make")(function* (environment) {
17286
+ const make$62 = Effect.fn("HubLink.make")(function* (environment) {
16866
17287
  const secrets = yield* ServerSecretStore;
16867
17288
  const env = environment ?? process.env;
16868
17289
  const fromEnv = fromEnvironment(env);
@@ -16928,7 +17349,7 @@ const make$61 = Effect.fn("HubLink.make")(function* (environment) {
16928
17349
  })
16929
17350
  };
16930
17351
  });
16931
- const layer$56 = Layer.effect(HubLink, make$61());
17352
+ const layer$57 = Layer.effect(HubLink, make$62());
16932
17353
  //#endregion
16933
17354
  //#region src/sync/HubAssetClient.ts
16934
17355
  /**
@@ -16961,7 +17382,7 @@ const decodeAssetListPage = Schema$1.decodeUnknownEffect(AssetListPage);
16961
17382
  const decodeConflictBody$1 = Schema$1.decodeUnknownEffect(ConflictBody$1);
16962
17383
  const decodeAsset = Schema$1.decodeUnknownEffect(AgentAsset);
16963
17384
  var HubAssetClient = class extends Context.Service()("@p4code/cli/sync/HubAssetClient") {};
16964
- const make$60 = Effect.gen(function* () {
17385
+ const make$61 = Effect.gen(function* () {
16965
17386
  const http = yield* HttpClient.HttpClient;
16966
17387
  const link = yield* HubLink;
16967
17388
  const requireSettings = Effect.gen(function* () {
@@ -17043,7 +17464,7 @@ const make$60 = Effect.gen(function* () {
17043
17464
  remove
17044
17465
  };
17045
17466
  });
17046
- const layer$55 = Layer.effect(HubAssetClient, make$60);
17467
+ const layer$56 = Layer.effect(HubAssetClient, make$61);
17047
17468
  //#endregion
17048
17469
  //#region src/sync/mcpRegistrationFiles.ts
17049
17470
  /**
@@ -17217,7 +17638,7 @@ const isMemoryDirectoryName = (name) => MEMORY_DIRECTORY_NAMES.includes(name);
17217
17638
  */
17218
17639
  const isMemoryAssetName = (name) => isMemoryFileName(name) || isMemoryDirectoryName(name);
17219
17640
  /** The same test the skill reader uses: a NUL is what breaks a JSON text column. */
17220
- const looksBinary = (content) => content.includes("\0");
17641
+ const looksBinary$1 = (content) => content.includes("\0");
17221
17642
  /**
17222
17643
  * Read whichever memory files exist in this machine's config directory.
17223
17644
  *
@@ -17241,7 +17662,7 @@ const readMemoryFiles = Effect.fn("memoryFile.readMemoryFiles")(function* (memor
17241
17662
  });
17242
17663
  continue;
17243
17664
  }
17244
- if (looksBinary(content)) {
17665
+ if (looksBinary$1(content)) {
17245
17666
  refused.push({
17246
17667
  name,
17247
17668
  reason: "binary-file"
@@ -17618,7 +18039,7 @@ const EMPTY_REPORT = {
17618
18039
  unavailable: null
17619
18040
  };
17620
18041
  var AssetSync = class extends Context.Service()("@p4code/cli/sync/AssetSync") {};
17621
- const make$59 = Effect.gen(function* () {
18042
+ const make$60 = Effect.gen(function* () {
17622
18043
  const client = yield* HubAssetClient;
17623
18044
  const link = yield* HubLink;
17624
18045
  const settingsStore = yield* ServerSettingsService;
@@ -18331,7 +18752,7 @@ const make$59 = Effect.gen(function* () {
18331
18752
  removeLocal
18332
18753
  };
18333
18754
  });
18334
- const layer$54 = Layer.effect(AssetSync, make$59);
18755
+ const layer$55 = Layer.effect(AssetSync, make$60);
18335
18756
  //#endregion
18336
18757
  //#region src/provider/CompressPrompts.ts
18337
18758
  /**
@@ -18354,6 +18775,8 @@ Never drop not/never/no/only/except: flipping the meaning costs more than any to
18354
18775
 
18355
18776
  Tool calls: fire direct. No plan, no progress note between calls, never announce the next call. If the harness asks for a short lead-in before tool calls, write that one line and nothing more. Otherwise text before a call only to clarify, warn about a security or irreversible action, or resolve ambiguity.
18356
18777
 
18778
+ Subagent and tool output is raw material, not the answer. Never relay a subagent report, a file listing, or command output verbatim - state the conclusion it supports and cite the file and line that back it. Its length is its own and is never a licence to answer at that length. Quote from it only where the exact wording is the point.
18779
+
18357
18780
  Preserve the user's dominant language: reply in the language the user writes, whatever language the examples or the surrounding context use. Compress the style, not the language. Every emitted line in that language, lead-in lines included, not just the final reply. Always keep technical terms, code, API names, CLI commands, commit-type keywords (feat/fix/...), and exact error strings verbatim unless the user explicitly asks for translation.
18358
18781
 
18359
18782
  Dropping articles applies to article languages only. Where small markers carry case or role (particles, postpositions), keep them: grammar, not filler. Compress politeness and filler instead.
@@ -18590,7 +19013,7 @@ const findAgentPreset = (requested) => {
18590
19013
  };
18591
19014
  //#endregion
18592
19015
  //#region src/cli/assets.ts
18593
- const SyncServicesLive = layer$54.pipe(Layer.provideMerge(layer$55), Layer.provideMerge(layer$56), Layer.provideMerge(layer$60.pipe(Layer.provide(layer$67))), Layer.provideMerge(layer$67), Layer.provideMerge(FetchHttpClient.layer));
19016
+ const SyncServicesLive = layer$55.pipe(Layer.provideMerge(layer$56), Layer.provideMerge(layer$57), Layer.provideMerge(layer$61.pipe(Layer.provide(layer$68))), Layer.provideMerge(layer$68), Layer.provideMerge(FetchHttpClient.layer));
18594
19017
  const runWithSync = (flags, run) => Effect.gen(function* () {
18595
19018
  const config = yield* resolveCliAuthConfig(flags, yield* GlobalFlag.LogLevel);
18596
19019
  return yield* Effect.gen(function* () {
@@ -18598,7 +19021,7 @@ const runWithSync = (flags, run) => Effect.gen(function* () {
18598
19021
  sync: yield* AssetSync,
18599
19022
  link: yield* HubLink
18600
19023
  });
18601
- }).pipe(Effect.provide(SyncServicesLive.pipe(Layer.provide(layer$70(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, "Error")))));
19024
+ }).pipe(Effect.provide(SyncServicesLive.pipe(Layer.provide(layer$71(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, "Error")))));
18602
19025
  });
18603
19026
  const STATE_LABEL = {
18604
19027
  synced: "synced",
@@ -19613,6 +20036,7 @@ const ThreadMetaUpdatedPayload = ThreadMetaUpdatedPayload$1;
19613
20036
  const ThreadRuntimeModeSetPayload = ThreadRuntimeModeSetPayload$1;
19614
20037
  const ThreadInteractionModeSetPayload = ThreadInteractionModeSetPayload$1;
19615
20038
  const ThreadCompressModeSetPayload = ThreadCompressModeSetPayload$1;
20039
+ const ThreadUnpromptedSubagentsSetPayload = ThreadUnpromptedSubagentsSetPayload$1;
19616
20040
  const ThreadDeletedPayload = ThreadDeletedPayload$1;
19617
20041
  const ThreadUnarchivedPayload = ThreadUnarchivedPayload$1;
19618
20042
  const ThreadUnsettledPayload = ThreadUnsettledPayload$1;
@@ -19753,6 +20177,7 @@ function projectEvent(model, event) {
19753
20177
  runtimeMode: payload.runtimeMode,
19754
20178
  interactionMode: payload.interactionMode,
19755
20179
  compressMode: payload.compressMode,
20180
+ unpromptedSubagents: payload.unpromptedSubagents,
19756
20181
  branch: payload.branch,
19757
20182
  worktreePath: payload.worktreePath,
19758
20183
  latestTurn: null,
@@ -19859,6 +20284,13 @@ function projectEvent(model, event) {
19859
20284
  updatedAt: payload.updatedAt
19860
20285
  })
19861
20286
  })));
20287
+ case "thread.unprompted-subagents-set": return decodeForEvent(ThreadUnpromptedSubagentsSetPayload, event.payload, event.type, "payload").pipe(Effect.map((payload) => ({
20288
+ ...nextBase,
20289
+ threads: updateThread(nextBase.threads, payload.threadId, {
20290
+ unpromptedSubagents: payload.unpromptedSubagents,
20291
+ updatedAt: payload.updatedAt
20292
+ })
20293
+ })));
19862
20294
  case "thread.message-sent": return Effect.gen(function* () {
19863
20295
  const payload = yield* decodeForEvent(MessageSentPayloadSchema, event.payload, event.type, "payload");
19864
20296
  const thread = nextBase.threads.find((entry) => entry.id === payload.threadId);
@@ -20225,6 +20657,7 @@ const decideOrchestrationCommand = Effect.fn("decideOrchestrationCommand")(funct
20225
20657
  runtimeMode: command.runtimeMode,
20226
20658
  interactionMode: command.interactionMode,
20227
20659
  compressMode: command.compressMode,
20660
+ unpromptedSubagents: command.unpromptedSubagents,
20228
20661
  branch: command.branch,
20229
20662
  worktreePath: command.worktreePath,
20230
20663
  createdAt: command.createdAt,
@@ -20504,6 +20937,28 @@ const decideOrchestrationCommand = Effect.fn("decideOrchestrationCommand")(funct
20504
20937
  }
20505
20938
  };
20506
20939
  }
20940
+ case "thread.unprompted-subagents.set": {
20941
+ yield* requireThread({
20942
+ readModel,
20943
+ command,
20944
+ threadId: command.threadId
20945
+ });
20946
+ const occurredAt = yield* nowIso$8;
20947
+ return {
20948
+ ...yield* withEventBase({
20949
+ aggregateKind: "thread",
20950
+ aggregateId: command.threadId,
20951
+ occurredAt,
20952
+ commandId: command.commandId
20953
+ }),
20954
+ type: "thread.unprompted-subagents-set",
20955
+ payload: {
20956
+ threadId: command.threadId,
20957
+ unpromptedSubagents: command.unpromptedSubagents,
20958
+ updatedAt: occurredAt
20959
+ }
20960
+ };
20961
+ }
20507
20962
  case "thread.turn.start": {
20508
20963
  const targetThread = yield* requireThread({
20509
20964
  readModel,
@@ -20562,6 +21017,7 @@ const decideOrchestrationCommand = Effect.fn("decideOrchestrationCommand")(funct
20562
21017
  runtimeMode: targetThread.runtimeMode,
20563
21018
  interactionMode: targetThread.interactionMode,
20564
21019
  compressMode: targetThread.compressMode,
21020
+ unpromptedSubagents: targetThread.unpromptedSubagents,
20565
21021
  ...sourceProposedPlan !== void 0 ? { sourceProposedPlan } : {},
20566
21022
  createdAt: command.createdAt
20567
21023
  }
@@ -21328,6 +21784,8 @@ const ProjectionThread = Schema$1.Struct({
21328
21784
  runtimeMode: RuntimeMode,
21329
21785
  interactionMode: ProviderInteractionMode,
21330
21786
  compressMode: CompressMode,
21787
+ /** 0 or 1: SQLite has no boolean, matching `hasActionableProposedPlan`. */
21788
+ unpromptedSubagents: NonNegativeInt,
21331
21789
  branch: Schema$1.NullOr(Schema$1.String),
21332
21790
  worktreePath: Schema$1.NullOr(Schema$1.String),
21333
21791
  latestTurnId: Schema$1.NullOr(TurnId),
@@ -22216,6 +22674,7 @@ const makeProjectionThreadRepository = Effect.gen(function* () {
22216
22674
  runtime_mode,
22217
22675
  interaction_mode,
22218
22676
  compress_mode,
22677
+ unprompted_subagents,
22219
22678
  branch,
22220
22679
  worktree_path,
22221
22680
  latest_turn_id,
@@ -22240,6 +22699,7 @@ const makeProjectionThreadRepository = Effect.gen(function* () {
22240
22699
  ${row.runtimeMode},
22241
22700
  ${row.interactionMode},
22242
22701
  ${row.compressMode},
22702
+ ${row.unpromptedSubagents},
22243
22703
  ${row.branch},
22244
22704
  ${row.worktreePath},
22245
22705
  ${row.latestTurnId},
@@ -22264,6 +22724,7 @@ const makeProjectionThreadRepository = Effect.gen(function* () {
22264
22724
  runtime_mode = excluded.runtime_mode,
22265
22725
  interaction_mode = excluded.interaction_mode,
22266
22726
  compress_mode = excluded.compress_mode,
22727
+ unprompted_subagents = excluded.unprompted_subagents,
22267
22728
  branch = excluded.branch,
22268
22729
  worktree_path = excluded.worktree_path,
22269
22730
  latest_turn_id = excluded.latest_turn_id,
@@ -22293,6 +22754,7 @@ const makeProjectionThreadRepository = Effect.gen(function* () {
22293
22754
  runtime_mode AS "runtimeMode",
22294
22755
  interaction_mode AS "interactionMode",
22295
22756
  compress_mode AS "compressMode",
22757
+ unprompted_subagents AS "unpromptedSubagents",
22296
22758
  branch,
22297
22759
  worktree_path AS "worktreePath",
22298
22760
  latest_turn_id AS "latestTurnId",
@@ -22324,6 +22786,7 @@ const makeProjectionThreadRepository = Effect.gen(function* () {
22324
22786
  runtime_mode AS "runtimeMode",
22325
22787
  interaction_mode AS "interactionMode",
22326
22788
  compress_mode AS "compressMode",
22789
+ unprompted_subagents AS "unpromptedSubagents",
22327
22790
  branch,
22328
22791
  worktree_path AS "worktreePath",
22329
22792
  latest_turn_id AS "latestTurnId",
@@ -22791,6 +23254,7 @@ const makeOrchestrationProjectionPipeline = Effect.fn("makeOrchestrationProjecti
22791
23254
  runtimeMode: event.payload.runtimeMode,
22792
23255
  interactionMode: event.payload.interactionMode,
22793
23256
  compressMode: event.payload.compressMode,
23257
+ unpromptedSubagents: event.payload.unpromptedSubagents ? 1 : 0,
22794
23258
  branch: event.payload.branch,
22795
23259
  worktreePath: event.payload.worktreePath,
22796
23260
  latestTurnId: null,
@@ -22915,6 +23379,16 @@ const makeOrchestrationProjectionPipeline = Effect.fn("makeOrchestrationProjecti
22915
23379
  });
22916
23380
  return;
22917
23381
  }
23382
+ case "thread.unprompted-subagents-set": {
23383
+ const existingRow = yield* projectionThreadRepository.getById({ threadId: event.payload.threadId });
23384
+ if (Option.isNone(existingRow)) return;
23385
+ yield* projectionThreadRepository.upsert({
23386
+ ...existingRow.value,
23387
+ unpromptedSubagents: event.payload.unpromptedSubagents ? 1 : 0,
23388
+ updatedAt: event.payload.updatedAt
23389
+ });
23390
+ return;
23391
+ }
22918
23392
  case "thread.deleted": {
22919
23393
  attachmentSideEffects.deletedThreadIds.add(event.payload.threadId);
22920
23394
  const existingRow = yield* projectionThreadRepository.getById({ threadId: event.payload.threadId });
@@ -23950,12 +24424,12 @@ const runProcessCore = Effect.fn("processRunner.runProcessCore")(function* (spaw
23950
24424
  stderrTruncated: stderr.truncated
23951
24425
  };
23952
24426
  });
23953
- const make$58 = Effect.fn("ProcessRunner.make")(function* () {
24427
+ const make$59 = Effect.fn("ProcessRunner.make")(function* () {
23954
24428
  const spawner = yield* ChildProcessSpawner$1.ChildProcessSpawner;
23955
24429
  const run = (input) => finalizeRunProcess(runProcessCore(spawner, input), input);
23956
24430
  return ProcessRunner.of({ run });
23957
24431
  });
23958
- const layer$53 = Layer.effect(ProcessRunner, make$58());
24432
+ const layer$54 = Layer.effect(ProcessRunner, make$59());
23959
24433
  //#endregion
23960
24434
  //#region src/project/RepositoryIdentityResolver.ts
23961
24435
  const DEFAULT_REPOSITORY_IDENTITY_CACHE_CAPACITY = 512;
@@ -24046,7 +24520,7 @@ const resolveRepositoryIdentityFromCacheKey = Effect.fn("RepositoryIdentityResol
24046
24520
  rootPath: cacheKey
24047
24521
  }) : null;
24048
24522
  });
24049
- const make$57 = Effect.fn("RepositoryIdentityResolver.make")(function* (options = {}) {
24523
+ const make$58 = Effect.fn("RepositoryIdentityResolver.make")(function* (options = {}) {
24050
24524
  const processRunner = yield* ProcessRunner;
24051
24525
  const repositoryIdentityCache = yield* Cache.makeWith((cacheKey) => resolveRepositoryIdentityFromCacheKey(cacheKey).pipe(Effect.provideService(ProcessRunner, processRunner)), {
24052
24526
  capacity: options.cacheCapacity ?? DEFAULT_REPOSITORY_IDENTITY_CACHE_CAPACITY,
@@ -24061,7 +24535,7 @@ const make$57 = Effect.fn("RepositoryIdentityResolver.make")(function* (options
24061
24535
  });
24062
24536
  return RepositoryIdentityResolver.of({ resolve });
24063
24537
  });
24064
- const layer$52 = Layer.effect(RepositoryIdentityResolver, make$57()).pipe(Layer.provide(layer$53));
24538
+ const layer$53 = Layer.effect(RepositoryIdentityResolver, make$58()).pipe(Layer.provide(layer$54));
24065
24539
  //#endregion
24066
24540
  //#region src/orchestration/Layers/ProjectionSnapshotQuery.ts
24067
24541
  const decodeReadModel = Schema$1.decodeUnknownEffect(OrchestrationReadModel);
@@ -24237,6 +24711,7 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
24237
24711
  runtime_mode AS "runtimeMode",
24238
24712
  interaction_mode AS "interactionMode",
24239
24713
  compress_mode AS "compressMode",
24714
+ unprompted_subagents AS "unpromptedSubagents",
24240
24715
  branch,
24241
24716
  worktree_path AS "worktreePath",
24242
24717
  latest_turn_id AS "latestTurnId",
@@ -24268,6 +24743,7 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
24268
24743
  runtime_mode AS "runtimeMode",
24269
24744
  interaction_mode AS "interactionMode",
24270
24745
  compress_mode AS "compressMode",
24746
+ unprompted_subagents AS "unpromptedSubagents",
24271
24747
  branch,
24272
24748
  worktree_path AS "worktreePath",
24273
24749
  latest_turn_id AS "latestTurnId",
@@ -24301,6 +24777,7 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
24301
24777
  runtime_mode AS "runtimeMode",
24302
24778
  interaction_mode AS "interactionMode",
24303
24779
  compress_mode AS "compressMode",
24780
+ unprompted_subagents AS "unpromptedSubagents",
24304
24781
  branch,
24305
24782
  worktree_path AS "worktreePath",
24306
24783
  latest_turn_id AS "latestTurnId",
@@ -24634,6 +25111,7 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
24634
25111
  runtime_mode AS "runtimeMode",
24635
25112
  interaction_mode AS "interactionMode",
24636
25113
  compress_mode AS "compressMode",
25114
+ unprompted_subagents AS "unpromptedSubagents",
24637
25115
  branch,
24638
25116
  worktree_path AS "worktreePath",
24639
25117
  latest_turn_id AS "latestTurnId",
@@ -24936,6 +25414,7 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
24936
25414
  runtimeMode: row.runtimeMode,
24937
25415
  interactionMode: row.interactionMode,
24938
25416
  compressMode: row.compressMode,
25417
+ unpromptedSubagents: row.unpromptedSubagents > 0,
24939
25418
  branch: row.branch,
24940
25419
  worktreePath: row.worktreePath,
24941
25420
  latestTurn: latestTurnByThread.get(row.threadId) ?? null,
@@ -25048,6 +25527,7 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
25048
25527
  runtimeMode: row.runtimeMode,
25049
25528
  interactionMode: row.interactionMode,
25050
25529
  compressMode: row.compressMode,
25530
+ unpromptedSubagents: row.unpromptedSubagents > 0,
25051
25531
  branch: row.branch,
25052
25532
  worktreePath: row.worktreePath,
25053
25533
  latestTurn: latestTurnByThread.get(row.threadId) ?? null,
@@ -25107,6 +25587,7 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
25107
25587
  runtimeMode: row.runtimeMode,
25108
25588
  interactionMode: row.interactionMode,
25109
25589
  compressMode: row.compressMode,
25590
+ unpromptedSubagents: row.unpromptedSubagents > 0,
25110
25591
  branch: row.branch,
25111
25592
  worktreePath: row.worktreePath,
25112
25593
  latestTurn: latestTurnByThread.get(row.threadId) ?? null,
@@ -25162,6 +25643,7 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
25162
25643
  runtimeMode: row.runtimeMode,
25163
25644
  interactionMode: row.interactionMode,
25164
25645
  compressMode: row.compressMode,
25646
+ unpromptedSubagents: row.unpromptedSubagents > 0,
25165
25647
  branch: row.branch,
25166
25648
  worktreePath: row.worktreePath,
25167
25649
  latestTurn: latestTurnByThread.get(row.threadId) ?? null,
@@ -25253,6 +25735,7 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
25253
25735
  runtimeMode: threadRow.value.runtimeMode,
25254
25736
  interactionMode: threadRow.value.interactionMode,
25255
25737
  compressMode: threadRow.value.compressMode,
25738
+ unpromptedSubagents: threadRow.value.unpromptedSubagents > 0,
25256
25739
  branch: threadRow.value.branch,
25257
25740
  worktreePath: threadRow.value.worktreePath,
25258
25741
  latestTurn: Option.isSome(latestTurnRow) ? mapLatestTurn(latestTurnRow.value) : null,
@@ -25289,6 +25772,7 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
25289
25772
  runtimeMode: threadRow.value.runtimeMode,
25290
25773
  interactionMode: threadRow.value.interactionMode,
25291
25774
  compressMode: threadRow.value.compressMode,
25775
+ unpromptedSubagents: threadRow.value.unpromptedSubagents > 0,
25292
25776
  branch: threadRow.value.branch,
25293
25777
  worktreePath: threadRow.value.worktreePath,
25294
25778
  latestTurn: Option.isSome(latestTurnRow) ? mapLatestTurn(latestTurnRow.value) : null,
@@ -25881,7 +26365,7 @@ function mergeWithDefaultKeybindings(custom) {
25881
26365
  * Keybindings - Service tag for keybinding configuration operations.
25882
26366
  */
25883
26367
  var Keybindings = class extends Context.Service()("@p4code/cli/keybindings") {};
25884
- const make$56 = Effect.gen(function* () {
26368
+ const make$57 = Effect.gen(function* () {
25885
26369
  const { keybindingsConfigPath } = yield* ServerConfig$1;
25886
26370
  const fs = yield* FileSystem.FileSystem;
25887
26371
  const path = yield* Path.Path;
@@ -26142,7 +26626,7 @@ const make$56 = Effect.gen(function* () {
26142
26626
  }))
26143
26627
  };
26144
26628
  });
26145
- const layer$51 = Layer.effect(Keybindings, make$56);
26629
+ const layer$52 = Layer.effect(Keybindings, make$57);
26146
26630
  //#endregion
26147
26631
  //#region src/process/externalLauncher.ts
26148
26632
  /**
@@ -26369,7 +26853,7 @@ const launchEditorProcess = Effect.fn("externalLauncher.launchEditorProcess")(fu
26369
26853
  cause
26370
26854
  }));
26371
26855
  });
26372
- const make$55 = Effect.gen(function* () {
26856
+ const make$56 = Effect.gen(function* () {
26373
26857
  const spawner = yield* ChildProcessSpawner$1.ChildProcessSpawner;
26374
26858
  const fileSystem = yield* FileSystem.FileSystem;
26375
26859
  const path = yield* Path.Path;
@@ -26380,7 +26864,7 @@ const make$55 = Effect.gen(function* () {
26380
26864
  launchEditor: (input) => provideCommandResolutionServices(Effect.flatMap(resolveEditorLaunch(input), (launch) => launchEditorProcess(launch).pipe(Effect.provideService(ChildProcessSpawner$1.ChildProcessSpawner, spawner))))
26381
26865
  });
26382
26866
  });
26383
- const layer$50 = Layer.effect(ExternalLauncher, make$55);
26867
+ const layer$51 = Layer.effect(ExternalLauncher, make$56);
26384
26868
  //#endregion
26385
26869
  //#region src/orchestration/Services/OrchestrationReactor.ts
26386
26870
  /**
@@ -26398,7 +26882,7 @@ var OrchestrationReactor = class extends Context.Service()("@p4code/cli/orchestr
26398
26882
  //#endregion
26399
26883
  //#region src/serverLifecycleEvents.ts
26400
26884
  var ServerLifecycleEvents = class extends Context.Service()("@p4code/cli/serverLifecycleEvents") {};
26401
- const make$54 = Effect.gen(function* () {
26885
+ const make$55 = Effect.gen(function* () {
26402
26886
  const pubsub = yield* PubSub.unbounded();
26403
26887
  const state = yield* Ref.make({
26404
26888
  sequence: 0,
@@ -26422,7 +26906,7 @@ const make$54 = Effect.gen(function* () {
26422
26906
  }
26423
26907
  };
26424
26908
  });
26425
- const layer$49 = Layer.effect(ServerLifecycleEvents, make$54);
26909
+ const layer$50 = Layer.effect(ServerLifecycleEvents, make$55);
26426
26910
  //#endregion
26427
26911
  //#region src/telemetry/Identify.ts
26428
26912
  const CodexAuthJsonSchema = Schema$1.Struct({ tokens: Schema$1.Struct({ account_id: Schema$1.String }) });
@@ -26595,7 +27079,7 @@ var AnalyticsService = class AnalyticsService extends Context.Service()("@p4code
26595
27079
  /** No-op layer for callers that intentionally disable telemetry. */
26596
27080
  static layerTest = Layer.succeed(AnalyticsService, inert);
26597
27081
  };
26598
- const make$53 = Effect.gen(function* () {
27082
+ const make$54 = Effect.gen(function* () {
26599
27083
  const telemetryConfig = yield* TelemetryEnvConfig;
26600
27084
  const posthogKey = telemetryConfig.posthogKey.trim();
26601
27085
  if (!telemetryConfig.enabled || posthogKey === "") return inert;
@@ -26665,7 +27149,7 @@ const make$53 = Effect.gen(function* () {
26665
27149
  flush
26666
27150
  });
26667
27151
  });
26668
- const layer$48 = Layer.effect(AnalyticsService, make$53);
27152
+ const layer$49 = Layer.effect(AnalyticsService, make$54);
26669
27153
  AnalyticsService.layerTest;
26670
27154
  //#endregion
26671
27155
  //#region src/service/pinnedRuntime.ts
@@ -27012,7 +27496,7 @@ var BootServiceInstallError = class extends Schema$1.TaggedErrorClass()("BootSer
27012
27496
  }
27013
27497
  };
27014
27498
  var BootService = class extends Context.Service()("@p4code/cli/service/bootService") {};
27015
- const make$52 = Effect.fn("cloud.boot_service.make")(function* (input) {
27499
+ const make$53 = Effect.fn("cloud.boot_service.make")(function* (input) {
27016
27500
  const hostExecPath = yield* HostProcessExecutablePath;
27017
27501
  const hostArguments = yield* HostProcessArguments;
27018
27502
  const host = input.host ?? {
@@ -27231,7 +27715,7 @@ const make$52 = Effect.fn("cloud.boot_service.make")(function* (input) {
27231
27715
  logPath
27232
27716
  });
27233
27717
  });
27234
- const layer$47 = (input) => Layer.effect(BootService, make$52(input));
27718
+ const layer$48 = (input) => Layer.effect(BootService, make$53(input));
27235
27719
  //#endregion
27236
27720
  //#region src/service/selfUpdate.ts
27237
27721
  /**
@@ -27306,7 +27790,7 @@ const resolveServerSelfUpdateCapability = Effect.fn("cloud.server_self_update.re
27306
27790
  return null;
27307
27791
  });
27308
27792
  var ServerSelfUpdate = class extends Context.Service()("@p4code/cli/service/selfUpdate/ServerSelfUpdate") {};
27309
- const make$51 = Effect.fn("cloud.server_self_update.make")(function* (options) {
27793
+ const make$52 = Effect.fn("cloud.server_self_update.make")(function* (options) {
27310
27794
  const serverConfig = yield* ServerConfig$1;
27311
27795
  const fs = yield* FileSystem.FileSystem;
27312
27796
  const path = yield* Path.Path;
@@ -27456,7 +27940,7 @@ const make$51 = Effect.fn("cloud.server_self_update.make")(function* (options) {
27456
27940
  });
27457
27941
  return ServerSelfUpdate.of({ update });
27458
27942
  });
27459
- const layer$46 = Layer.effect(ServerSelfUpdate, make$51()).pipe(Layer.provide(layer$53));
27943
+ const layer$47 = Layer.effect(ServerSelfUpdate, make$52()).pipe(Layer.provide(layer$54));
27460
27944
  //#endregion
27461
27945
  //#region src/environment/ServerEnvironmentLabel.ts
27462
27946
  const ServerEnvironmentLabelCommandProbe = Schema$1.Literals(["macos-computer-name", "linux-pretty-hostname"]);
@@ -27588,7 +28072,7 @@ function platformArch(architecture) {
27588
28072
  default: return "other";
27589
28073
  }
27590
28074
  }
27591
- const make$50 = Effect.gen(function* () {
28075
+ const make$51 = Effect.gen(function* () {
27592
28076
  const fileSystem = yield* FileSystem.FileSystem;
27593
28077
  const path = yield* Path.Path;
27594
28078
  const serverConfig = yield* ServerConfig$1;
@@ -27651,7 +28135,7 @@ const make$50 = Effect.gen(function* () {
27651
28135
  * state. It intentionally has no fallback Layer.succeed value: callers must
27652
28136
  * provide the external platform services and a ServerConfig.
27653
28137
  */
27654
- const layer$45 = Layer.effect(ServerEnvironment, make$50).pipe(Layer.provide(layer$53));
28138
+ const layer$46 = Layer.effect(ServerEnvironment, make$51).pipe(Layer.provide(layer$54));
27655
28139
  //#endregion
27656
28140
  //#region src/provider/Services/ProviderSessionReaper.ts
27657
28141
  var ProviderSessionReaper = class extends Context.Service()("@p4code/cli/provider/Services/ProviderSessionReaper") {};
@@ -28698,6 +29182,7 @@ const resolveAutoBootstrapWelcomeTargets = Effect.gen(function* () {
28698
29182
  modelSelection: nextProjectDefaultModelSelection,
28699
29183
  interactionMode: DEFAULT_PROVIDER_INTERACTION_MODE,
28700
29184
  compressMode: DEFAULT_COMPRESS_MODE,
29185
+ unpromptedSubagents: true,
28701
29186
  runtimeMode: "full-access",
28702
29187
  branch: null,
28703
29188
  worktreePath: null,
@@ -28728,7 +29213,7 @@ const maybeOpenBrowser = (target) => Effect.gen(function* () {
28728
29213
  yield* (yield* ExternalLauncher).launchBrowser(target).pipe(Effect.catch(() => Effect.logInfo("browser auto-open unavailable", { hint: `Open ${target} in your browser.` })));
28729
29214
  });
28730
29215
  const runStartupPhase = (phase, effect) => effect.pipe(Effect.annotateSpans({ "startup.phase": phase }), Effect.withSpan(`server.startup.${phase}`));
28731
- const make$49 = Effect.gen(function* () {
29216
+ const make$50 = Effect.gen(function* () {
28732
29217
  const serverConfig = yield* ServerConfig$1;
28733
29218
  const keybindings = yield* Keybindings;
28734
29219
  const orchestrationReactor = yield* OrchestrationReactor;
@@ -28869,7 +29354,7 @@ const make$49 = Effect.gen(function* () {
28869
29354
  enqueueCommand: commandGate.enqueueCommand
28870
29355
  };
28871
29356
  });
28872
- const layer$44 = Layer.effect(ServerRuntimeStartup, make$49);
29357
+ const layer$45 = Layer.effect(ServerRuntimeStartup, make$50);
28873
29358
  //#endregion
28874
29359
  //#region src/serverRuntimeState.ts
28875
29360
  const PersistedServerRuntimeState = Schema$1.Struct({
@@ -29017,7 +29502,7 @@ function expandHomePath$2(input, path) {
29017
29502
  if (input.startsWith("~/") || input.startsWith("~\\")) return path.join(NodeOS.homedir(), input.slice(2));
29018
29503
  return input;
29019
29504
  }
29020
- const make$48 = Effect.gen(function* () {
29505
+ const make$49 = Effect.gen(function* () {
29021
29506
  const fileSystem = yield* FileSystem.FileSystem;
29022
29507
  const path = yield* Path.Path;
29023
29508
  const statWorkspaceRoot = Effect.fn("WorkspacePaths.statWorkspaceRoot")(function* (workspaceRoot, normalizedWorkspaceRoot, phase) {
@@ -29074,7 +29559,7 @@ const make$48 = Effect.gen(function* () {
29074
29559
  resolveRelativePathWithinRoot
29075
29560
  });
29076
29561
  });
29077
- const layer$43 = Layer.effect(WorkspacePaths, make$48);
29562
+ const layer$44 = Layer.effect(WorkspacePaths, make$49);
29078
29563
  //#endregion
29079
29564
  //#region src/cli/project.ts
29080
29565
  const isEnvironmentHttpCommonError = Schema$1.is(EnvironmentHttpCommonError);
@@ -29180,7 +29665,7 @@ const projectCommandUuid = Crypto.Crypto.pipe(Effect.flatMap((crypto) => crypto.
29180
29665
  operation: "generateProjectCommandId",
29181
29666
  cause
29182
29667
  })));
29183
- const ProjectCliRuntimeLive = Layer.mergeAll(layer$43, OrchestrationLayerLive.pipe(Layer.provideMerge(layer$52), Layer.provideMerge(layerConfig)));
29668
+ const ProjectCliRuntimeLive = Layer.mergeAll(layer$44, OrchestrationLayerLive.pipe(Layer.provideMerge(layer$53), Layer.provideMerge(layerConfig)));
29184
29669
  const PROJECT_CLI_LIVE_SERVER_TIMEOUT = Duration.seconds(1);
29185
29670
  const withProjectCliSessionToken = (environmentAuth, run) => Effect.acquireUseRelease(environmentAuth.issueSession({
29186
29671
  scopes: AuthAdministrativeScopes,
@@ -29271,7 +29756,7 @@ const runProjectMutation = Effect.fn("runProjectMutation")(function* (flags, run
29271
29756
  });
29272
29757
  yield* Console.log(output);
29273
29758
  }));
29274
- const offlineRuntimeLayer = ProjectCliRuntimeLive.pipe(Layer.provide(layer$70(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)));
29759
+ const offlineRuntimeLayer = ProjectCliRuntimeLive.pipe(Layer.provide(layer$71(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)));
29275
29760
  return yield* Effect.gen(function* () {
29276
29761
  const snapshot = yield* getOfflineSnapshot();
29277
29762
  const orchestrationEngine = yield* OrchestrationEngineService;
@@ -29282,7 +29767,7 @@ const runProjectMutation = Effect.fn("runProjectMutation")(function* (flags, run
29282
29767
  });
29283
29768
  yield* Console.log(output);
29284
29769
  }).pipe(Effect.provide(offlineRuntimeLayer));
29285
- }).pipe(Effect.provide(Layer.mergeAll(runtimeLayer, layer$43).pipe(Layer.provideMerge(FetchHttpClient.layer), Layer.provide(layer$70(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)))));
29770
+ }).pipe(Effect.provide(Layer.mergeAll(runtimeLayer, layer$44).pipe(Layer.provideMerge(FetchHttpClient.layer), Layer.provide(layer$71(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)))));
29286
29771
  });
29287
29772
  const projectAddCommand = Command.make("add", {
29288
29773
  ...projectLocationFlags,
@@ -29889,7 +30374,7 @@ const logP4ProjectFileLoadError = (error) => Effect.logWarning(error).pipe(Effec
29889
30374
  filePath: error.filePath,
29890
30375
  errorTag: error._tag
29891
30376
  }));
29892
- const make$47 = Effect.gen(function* () {
30377
+ const make$48 = Effect.gen(function* () {
29893
30378
  const fileSystem = yield* FileSystem.FileSystem;
29894
30379
  const path = yield* Path.Path;
29895
30380
  const load = Effect.fn("P4ProjectFileLoader.load")(function* (workspaceRoot) {
@@ -29910,7 +30395,7 @@ const make$47 = Effect.gen(function* () {
29910
30395
  });
29911
30396
  return P4ProjectFileLoader.of({ load });
29912
30397
  });
29913
- const layer$42 = Layer.effect(P4ProjectFileLoader, make$47);
30398
+ const layer$43 = Layer.effect(P4ProjectFileLoader, make$48);
29914
30399
  //#endregion
29915
30400
  //#region src/project/ProjectFaviconResolver.ts
29916
30401
  /**
@@ -29981,7 +30466,7 @@ function extractIconHref(source) {
29981
30466
  return null;
29982
30467
  }
29983
30468
  const optionOnNotFound$1 = (effect) => effect.pipe(Effect.map(Option.some), Effect.catchTags({ PlatformError: (error) => error.reason._tag === "NotFound" ? Effect.succeed(Option.none()) : Effect.fail(error) }));
29984
- const make$46 = Effect.gen(function* () {
30469
+ const make$47 = Effect.gen(function* () {
29985
30470
  const fileSystem = yield* FileSystem.FileSystem;
29986
30471
  const path = yield* Path.Path;
29987
30472
  const workspacePaths = yield* WorkspacePaths;
@@ -30050,7 +30535,7 @@ const make$46 = Effect.gen(function* () {
30050
30535
  });
30051
30536
  return ProjectFaviconResolver.of({ resolvePath });
30052
30537
  });
30053
- const layer$41 = Layer.effect(ProjectFaviconResolver, make$46);
30538
+ const layer$42 = Layer.effect(ProjectFaviconResolver, make$47);
30054
30539
  //#endregion
30055
30540
  //#region src/assets/AssetAccess.ts
30056
30541
  const ASSET_ROUTE_PREFIX = "/api/assets";
@@ -30303,10 +30788,10 @@ const resolveAsset = Effect.fn("AssetAccess.resolveAsset")(function* (token, rel
30303
30788
  //#endregion
30304
30789
  //#region src/observability/BrowserTraceCollector.ts
30305
30790
  var BrowserTraceCollector = class extends Context.Service()("@p4code/cli/observability/BrowserTraceCollector") {};
30306
- const make$45 = (sink) => BrowserTraceCollector.of({ record: (records) => Effect.sync(() => {
30791
+ const make$46 = (sink) => BrowserTraceCollector.of({ record: (records) => Effect.sync(() => {
30307
30792
  for (const record of records) sink.push(record);
30308
30793
  }) });
30309
- const layer$40 = (sink) => Layer.succeed(BrowserTraceCollector, make$45(sink));
30794
+ const layer$41 = (sink) => Layer.succeed(BrowserTraceCollector, make$46(sink));
30310
30795
  //#endregion
30311
30796
  //#region src/auth/http.ts
30312
30797
  const CREDENTIAL_RESPONSE_HEADERS = {
@@ -31269,7 +31754,7 @@ const NESTED_PAYLOAD_KEYS = [
31269
31754
  "operations"
31270
31755
  ];
31271
31756
  const MAX_COLLECT_DEPTH = 4;
31272
- function asRecord$3(value) {
31757
+ function asRecord$4(value) {
31273
31758
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
31274
31759
  }
31275
31760
  function pushChangedFilePath(target, value) {
@@ -31284,7 +31769,7 @@ function collectChangedFilePaths(value, target, depth) {
31284
31769
  for (const entry of value) collectChangedFilePaths(entry, target, depth + 1);
31285
31770
  return;
31286
31771
  }
31287
- const record = asRecord$3(value);
31772
+ const record = asRecord$4(value);
31288
31773
  if (!record) return;
31289
31774
  for (const field of CHANGED_FILE_FIELDS) pushChangedFilePath(target, record[field]);
31290
31775
  for (const nestedKey of NESTED_PAYLOAD_KEYS) if (nestedKey in record) collectChangedFilePaths(record[nestedKey], target, depth + 1);
@@ -31295,7 +31780,7 @@ function collectChangedFilePaths(value, target, depth) {
31295
31780
  */
31296
31781
  function collectActivityChangedFilePaths(payload) {
31297
31782
  const target = /* @__PURE__ */ new Set();
31298
- collectChangedFilePaths(asRecord$3(asRecord$3(payload)?.data), target, 0);
31783
+ collectChangedFilePaths(asRecord$4(asRecord$4(payload)?.data), target, 0);
31299
31784
  return target;
31300
31785
  }
31301
31786
  /**
@@ -33200,7 +33685,7 @@ const classifyNonZeroExit = (command, stderr) => {
33200
33685
  if (command === "gh" && (normalized.includes("could not resolve to a pullrequest") || normalized.includes("repository.pullrequest") || normalized.includes("no pull requests found for branch") || normalized.includes("pull request not found")) || command === "glab" && (normalized.includes("merge request not found") || normalized.includes("not found") || normalized.includes("404")) || command === "az" && normalized.includes("pull request") && (normalized.includes("not found") || normalized.includes("does not exist"))) return "not-found";
33201
33686
  return "command-failed";
33202
33687
  };
33203
- const make$44 = Effect.gen(function* () {
33688
+ const make$45 = Effect.gen(function* () {
33204
33689
  const processRunner = yield* ProcessRunner;
33205
33690
  const run = Effect.fn("VcsProcess.run")(function* (input) {
33206
33691
  const baseError = {
@@ -33257,7 +33742,7 @@ const make$44 = Effect.gen(function* () {
33257
33742
  });
33258
33743
  return VcsProcess.of({ run });
33259
33744
  });
33260
- const layer$39 = Layer.effect(VcsProcess, make$44).pipe(Layer.provide(layer$53));
33745
+ const layer$40 = Layer.effect(VcsProcess, make$45).pipe(Layer.provide(layer$54));
33261
33746
  //#endregion
33262
33747
  //#region src/vcs/VcsDriver.ts
33263
33748
  var VcsDriver = class extends Context.Service()("@p4code/cli/vcs/VcsDriver") {};
@@ -33708,12 +34193,12 @@ const makeVcsDriver = Effect.gen(function* () {
33708
34193
  const driver = yield* makeVcsDriverShape();
33709
34194
  return VcsDriver.of(driver);
33710
34195
  });
33711
- const make$43 = Effect.gen(function* () {
34196
+ const make$44 = Effect.gen(function* () {
33712
34197
  const git = yield* makeGitVcsDriverCore();
33713
34198
  return GitVcsDriver.of(git);
33714
34199
  });
33715
34200
  Layer.effect(VcsDriver, makeVcsDriver);
33716
- const layer$38 = Layer.effect(GitVcsDriver, make$43);
34201
+ const layer$39 = Layer.effect(GitVcsDriver, make$44);
33717
34202
  //#endregion
33718
34203
  //#region src/vcs/VcsProjectConfig.ts
33719
34204
  const ProjectVcsConfigJson = fromLenientJson(Schema$1.Struct({
@@ -33745,7 +34230,7 @@ const logVcsProjectConfigError = (error) => Effect.logWarning(error).pipe(Effect
33745
34230
  configPath: error.configPath,
33746
34231
  errorTag: error._tag
33747
34232
  }));
33748
- const make$42 = Effect.gen(function* () {
34233
+ const make$43 = Effect.gen(function* () {
33749
34234
  const fileSystem = yield* FileSystem.FileSystem;
33750
34235
  const path = yield* Path.Path;
33751
34236
  const findConfigPath = Effect.fn("VcsProjectConfig.findConfigPath")(function* (cwd) {
@@ -33786,7 +34271,7 @@ const make$42 = Effect.gen(function* () {
33786
34271
  });
33787
34272
  return VcsProjectConfig.of({ resolveKind });
33788
34273
  });
33789
- const layer$37 = Layer.effect(VcsProjectConfig, make$42);
34274
+ const layer$38 = Layer.effect(VcsProjectConfig, make$43);
33790
34275
  //#endregion
33791
34276
  //#region src/vcs/VcsDriverRegistry.ts
33792
34277
  const DETECTION_CACHE_CAPACITY = 2048;
@@ -33806,7 +34291,7 @@ function parseDetectionCacheKey(key) {
33806
34291
  cwd: key.slice(separatorIndex + 1)
33807
34292
  };
33808
34293
  }
33809
- const make$41 = Effect.gen(function* () {
34294
+ const make$42 = Effect.gen(function* () {
33810
34295
  const projectConfig = yield* VcsProjectConfig;
33811
34296
  const git = yield* makeVcsDriver;
33812
34297
  const drivers = { git };
@@ -33863,7 +34348,7 @@ const make$41 = Effect.gen(function* () {
33863
34348
  resolve
33864
34349
  });
33865
34350
  });
33866
- const layer$36 = Layer.effect(VcsDriverRegistry, make$41).pipe(Layer.provide(layer$37));
34351
+ const layer$37 = Layer.effect(VcsDriverRegistry, make$42).pipe(Layer.provide(layer$38));
33867
34352
  //#endregion
33868
34353
  //#region src/checkpointing/CheckpointStore.ts
33869
34354
  /**
@@ -33883,7 +34368,7 @@ const layer$36 = Layer.effect(VcsDriverRegistry, make$41).pipe(Layer.provide(lay
33883
34368
  */
33884
34369
  /** Service tag for checkpoint persistence and restore operations. */
33885
34370
  var CheckpointStore = class extends Context.Service()("@p4code/cli/checkpointing/CheckpointStore") {};
33886
- const make$40 = Effect.gen(function* () {
34371
+ const make$41 = Effect.gen(function* () {
33887
34372
  const vcsRegistry = yield* VcsDriverRegistry;
33888
34373
  const resolveCheckpoints = Effect.fn("CheckpointStore.resolveCheckpoints")(function* (operation, cwd) {
33889
34374
  const handle = yield* vcsRegistry.resolve({ cwd });
@@ -33922,7 +34407,7 @@ const make$40 = Effect.gen(function* () {
33922
34407
  deleteCheckpointRefs
33923
34408
  });
33924
34409
  });
33925
- const layer$35 = Layer.effect(CheckpointStore, make$40);
34410
+ const layer$36 = Layer.effect(CheckpointStore, make$41);
33926
34411
  //#endregion
33927
34412
  //#region src/checkpointing/CheckpointDiffQuery.ts
33928
34413
  /**
@@ -33944,7 +34429,7 @@ function buildTurnDiffResult(input, diff) {
33944
34429
  diff
33945
34430
  };
33946
34431
  }
33947
- const make$39 = Effect.gen(function* () {
34432
+ const make$40 = Effect.gen(function* () {
33948
34433
  const projectionSnapshotQuery = yield* ProjectionSnapshotQuery;
33949
34434
  const checkpointStore = yield* CheckpointStore;
33950
34435
  const threadActivities = yield* ProjectionThreadActivityRepository;
@@ -34110,7 +34595,7 @@ const make$39 = Effect.gen(function* () {
34110
34595
  getFullThreadDiff
34111
34596
  });
34112
34597
  });
34113
- const layer$34 = Layer.effect(CheckpointDiffQuery, make$39);
34598
+ const layer$35 = Layer.effect(CheckpointDiffQuery, make$40);
34114
34599
  //#endregion
34115
34600
  //#region ../../packages/shared/src/toolCategory.ts
34116
34601
  const TOOL_CATEGORY_TITLES = {
@@ -34379,22 +34864,22 @@ function classifyToolCategory(input) {
34379
34864
  if (normalized.includes("image")) return "image_view";
34380
34865
  return "tool";
34381
34866
  }
34382
- function asRecord$2(value) {
34867
+ function asRecord$3(value) {
34383
34868
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
34384
34869
  }
34385
34870
  /** Classify from a runtime item payload's `data` (`{ toolName, input }`). */
34386
34871
  function classifyToolCategoryFromToolData(data) {
34387
- const record = asRecord$2(data);
34872
+ const record = asRecord$3(data);
34388
34873
  const toolName = record?.toolName;
34389
34874
  if (typeof toolName !== "string" || toolName.trim().length === 0) return;
34390
34875
  return classifyToolCategory({
34391
34876
  toolName,
34392
- toolInput: asRecord$2(record?.input)
34877
+ toolInput: asRecord$3(record?.input)
34393
34878
  });
34394
34879
  }
34395
34880
  //#endregion
34396
34881
  //#region src/orchestration/ActivityPayloadProjection.ts
34397
- function asRecord$1(value) {
34882
+ function asRecord$2(value) {
34398
34883
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
34399
34884
  }
34400
34885
  function asTrimmedString$1(value) {
@@ -34417,7 +34902,7 @@ function collectChangedFiles(value, target, seen, depth) {
34417
34902
  }
34418
34903
  return;
34419
34904
  }
34420
- const record = asRecord$1(value);
34905
+ const record = asRecord$2(value);
34421
34906
  if (!record) return;
34422
34907
  pushChangedFile(target, seen, record.path);
34423
34908
  pushChangedFile(target, seen, record.filePath);
@@ -34443,13 +34928,13 @@ function collectChangedFiles(value, target, seen, depth) {
34443
34928
  }
34444
34929
  }
34445
34930
  function projectCommandData(data) {
34446
- const item = asRecord$1(data.item);
34931
+ const item = asRecord$2(data.item);
34447
34932
  if (!item) return;
34448
34933
  const projectedItem = {};
34449
34934
  if ("command" in item) projectedItem.command = item.command;
34450
- const input = asRecord$1(item.input);
34935
+ const input = asRecord$2(item.input);
34451
34936
  if (input && "command" in input) projectedItem.input = { command: input.command };
34452
- const result = asRecord$1(item.result);
34937
+ const result = asRecord$2(item.result);
34453
34938
  if (result && "command" in result) projectedItem.result = { command: result.command };
34454
34939
  return Object.keys(projectedItem).length > 0 ? projectedItem : void 0;
34455
34940
  }
@@ -34465,7 +34950,7 @@ function summarizeToolTextOutput(value) {
34465
34950
  return null;
34466
34951
  }
34467
34952
  function projectRawOutput(value) {
34468
- const rawOutput = asRecord$1(value);
34953
+ const rawOutput = asRecord$2(value);
34469
34954
  if (!rawOutput) return;
34470
34955
  if (typeof rawOutput.totalFiles === "number" && Number.isFinite(rawOutput.totalFiles)) return {
34471
34956
  totalFiles: rawOutput.totalFiles,
@@ -34487,14 +34972,14 @@ function projectRawOutput(value) {
34487
34972
  * the full payload in persistence and the event store.
34488
34973
  */
34489
34974
  function projectActivityPayload(activity) {
34490
- const payload = asRecord$1(activity.payload);
34491
- const data = asRecord$1(payload?.data);
34975
+ const payload = asRecord$2(activity.payload);
34976
+ const data = asRecord$2(payload?.data);
34492
34977
  if (!payload || !data || payload.itemType === "mcp_tool_call") return activity;
34493
34978
  const projectedData = {};
34494
34979
  const item = projectCommandData(data);
34495
34980
  if (item) projectedData.item = item;
34496
34981
  if ("command" in data) projectedData.command = data.command;
34497
- const input = asRecord$1(data.input);
34982
+ const input = asRecord$2(data.input);
34498
34983
  if (input && "command" in input) projectedData.input = { command: input.command };
34499
34984
  const changedFiles = [];
34500
34985
  collectChangedFiles(data, changedFiles, /* @__PURE__ */ new Set(), 0);
@@ -34521,7 +35006,7 @@ function projectActivityPayload(activity) {
34521
35006
  */
34522
35007
  function isResolvableContextWindowActivity(activity) {
34523
35008
  if (activity.kind !== "context-window.updated") return false;
34524
- const usedTokens = asRecord$1(activity.payload)?.usedTokens;
35009
+ const usedTokens = asRecord$2(activity.payload)?.usedTokens;
34525
35010
  return typeof usedTokens === "number" && Number.isFinite(usedTokens) && usedTokens >= 0;
34526
35011
  }
34527
35012
  /**
@@ -34537,7 +35022,7 @@ function isResolvableContextWindowActivity(activity) {
34537
35022
  * client.
34538
35023
  */
34539
35024
  function withoutContextWindowBreakdown$1(activity) {
34540
- const payload = asRecord$1(activity.payload);
35025
+ const payload = asRecord$2(activity.payload);
34541
35026
  if (!payload || payload.breakdown === void 0) return activity;
34542
35027
  const { breakdown: _breakdown, ...rest } = payload;
34543
35028
  return {
@@ -34552,7 +35037,7 @@ function dropStaleContextWindowActivities(activities) {
34552
35037
  const retainedIndexes = new Set(latestIndexByTurn.values());
34553
35038
  let breakdownIndex = null;
34554
35039
  for (const index of retainedIndexes) {
34555
- if (asRecord$1(activities[index].payload)?.breakdown === void 0) continue;
35040
+ if (asRecord$2(activities[index].payload)?.breakdown === void 0) continue;
34556
35041
  if (breakdownIndex === null || index > breakdownIndex) breakdownIndex = index;
34557
35042
  }
34558
35043
  return activities.flatMap((activity, index) => {
@@ -34680,6 +35165,8 @@ const buildTaskRow = (input, taskId, timestamp, repositoryKey = null) => ({
34680
35165
  projectId: input.projectId ?? null,
34681
35166
  repositoryKey: repositoryKey ?? input.repositoryKey ?? null,
34682
35167
  threadId: input.threadId ?? null,
35168
+ externalRef: input.externalRef ?? null,
35169
+ parentTaskId: input.parentTaskId ?? null,
34683
35170
  title: input.title,
34684
35171
  body: input.body ?? "",
34685
35172
  status: input.status ?? "backlog",
@@ -35272,7 +35759,7 @@ function makeUpdateState(input) {
35272
35759
  output: input.output ?? null
35273
35760
  };
35274
35761
  }
35275
- const make$38 = Effect.fn("ProviderMaintenanceRunner.make")(function* () {
35762
+ const make$39 = Effect.fn("ProviderMaintenanceRunner.make")(function* () {
35276
35763
  const providerRegistry = yield* ProviderRegistry;
35277
35764
  const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
35278
35765
  const httpClient = yield* HttpClient.HttpClient;
@@ -35387,7 +35874,7 @@ const make$38 = Effect.fn("ProviderMaintenanceRunner.make")(function* () {
35387
35874
  });
35388
35875
  return ProviderMaintenanceRunner.of({ updateProvider });
35389
35876
  });
35390
- const layer$33 = Layer.effect(ProviderMaintenanceRunner, make$38());
35877
+ const layer$34 = Layer.effect(ProviderMaintenanceRunner, make$39());
35391
35878
  //#endregion
35392
35879
  //#region src/provider/Services/ProviderInstanceRegistry.ts
35393
35880
  var ProviderInstanceRegistry = class extends Context.Service()("@p4code/cli/provider/Services/ProviderInstanceRegistry") {};
@@ -35411,11 +35898,11 @@ const makeTextGenerationFromRegistry = (registry) => TextGeneration.of({
35411
35898
  detail: "This provider does not report account usage."
35412
35899
  }))))
35413
35900
  });
35414
- const make$37 = Effect.gen(function* () {
35901
+ const make$38 = Effect.gen(function* () {
35415
35902
  const registry = yield* ProviderInstanceRegistry;
35416
35903
  return makeTextGenerationFromRegistry(registry);
35417
35904
  });
35418
- const layer$32 = Layer.effect(TextGeneration, make$37);
35905
+ const layer$33 = Layer.effect(TextGeneration, make$38);
35419
35906
  const SKILL_MANIFEST_FILENAME = "SKILL.md";
35420
35907
  /**
35421
35908
  * Split a catalogue id (`owner/repo/skill-name`) into its parts.
@@ -35524,7 +36011,7 @@ function toAssetFilePath(repositoryPath, root) {
35524
36011
  const REGISTRY_SEARCH_URL = "https://www.skills.sh/api/search";
35525
36012
  const GITHUB_API_URL = "https://api.github.com";
35526
36013
  const GITHUB_RAW_URL = "https://raw.githubusercontent.com";
35527
- const REQUEST_TIMEOUT_MS = 15e3;
36014
+ const REQUEST_TIMEOUT_MS$1 = 15e3;
35528
36015
  /** More than a person reads, few enough that the panel is a list and not a feed. */
35529
36016
  const MAX_SEARCH_RESULTS = 25;
35530
36017
  /**
@@ -35561,10 +36048,10 @@ const emptyFetch = (id, unavailable) => ({
35561
36048
  skipped: [],
35562
36049
  unavailable
35563
36050
  });
35564
- const make$36 = Effect.gen(function* () {
36051
+ const make$37 = Effect.gen(function* () {
35565
36052
  const http = yield* HttpClient.HttpClient;
35566
36053
  const request = Effect.fn("SkillRegistry.request")(function* (url) {
35567
- return yield* http.execute(HttpClientRequest.get(url).pipe(HttpClientRequest.setHeader("accept", "application/json"), HttpClientRequest.setHeader("user-agent", "p4code"))).pipe(Effect.timeout(REQUEST_TIMEOUT_MS));
36054
+ return yield* http.execute(HttpClientRequest.get(url).pipe(HttpClientRequest.setHeader("accept", "application/json"), HttpClientRequest.setHeader("user-agent", "p4code"))).pipe(Effect.timeout(REQUEST_TIMEOUT_MS$1));
35568
36055
  });
35569
36056
  const search = (query) => Effect.gen(function* () {
35570
36057
  const response = yield* request(`${REGISTRY_SEARCH_URL}?q=${encodeURIComponent(query)}`).pipe(Effect.orElseSucceed(() => null));
@@ -35632,7 +36119,309 @@ const make$36 = Effect.gen(function* () {
35632
36119
  fetch
35633
36120
  };
35634
36121
  });
35635
- const layer$31 = Layer.effect(SkillRegistry, make$36);
36122
+ const layer$32 = Layer.effect(SkillRegistry, make$37);
36123
+ //#endregion
36124
+ //#region ../../packages/shared/src/ticketReference.ts
36125
+ /**
36126
+ * Recognizing a tracker reference in text somebody pasted.
36127
+ *
36128
+ * Shared because both ends need the same answer: the composer decides whether a
36129
+ * paste is worth a round trip, and the server decides what to ask the tracker.
36130
+ * Two copies of this rule would differ, and the difference would read as "it
36131
+ * works on my machine but the paste does nothing".
36132
+ *
36133
+ * @module ticketReference
36134
+ */
36135
+ /**
36136
+ * A Linear issue URL, e.g.
36137
+ * `https://linear.app/omnicasa/issue/MOBILE-12262/haptic-feedback`.
36138
+ *
36139
+ * The workspace slug is what makes the URL unguessable from the identifier
36140
+ * alone, which is why a resolved ticket keeps the tracker's own URL rather than
36141
+ * rebuilding one.
36142
+ */
36143
+ const LINEAR_ISSUE_URL_PATTERN = /https?:\/\/(?:www\.)?linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]*-\d+)/;
36144
+ /**
36145
+ * A bare identifier, e.g. `MOBILE-12262`.
36146
+ *
36147
+ * Anchored: only a paste that is *nothing but* an identifier counts. Scanning
36148
+ * prose for this shape would claim things that are not tickets - a date range,
36149
+ * a version, a p4code readable id like `P4-40` - and each false positive costs
36150
+ * a request and, worse, a wrong link.
36151
+ */
36152
+ const BARE_IDENTIFIER_PATTERN = /^([A-Za-z][A-Za-z0-9]*-\d+)$/;
36153
+ /**
36154
+ * The issue identifier a paste refers to, or `null` when it refers to none.
36155
+ *
36156
+ * Case is normalized up, the way every tracker prints it, so the same ticket
36157
+ * pasted two ways compares equal.
36158
+ */
36159
+ function parseTicketReference(text) {
36160
+ const trimmed = text.trim();
36161
+ if (trimmed.length === 0) return null;
36162
+ const url = LINEAR_ISSUE_URL_PATTERN.exec(trimmed);
36163
+ if (url?.[1] !== void 0) return url[1].toUpperCase();
36164
+ const bare = BARE_IDENTIFIER_PATTERN.exec(trimmed);
36165
+ if (bare?.[1] !== void 0) return bare[1].toUpperCase();
36166
+ return null;
36167
+ }
36168
+ //#endregion
36169
+ //#region src/mcp/McpToolClient.ts
36170
+ /**
36171
+ * Calling a tool on somebody else's MCP server.
36172
+ *
36173
+ * The rest of `apps/server/src/mcp` points the other way: it *exposes* p4code's
36174
+ * toolkit to an agent. This is the only place p4code is the client, and it
36175
+ * exists because one feature - recognizing a pasted ticket - needs an answer
36176
+ * before any agent turn runs, from a server the user has already registered.
36177
+ *
36178
+ * Deliberately not a general MCP client. There is no session reuse, no tool
36179
+ * listing, no notifications, no SSE stream held open: one request/response pair
36180
+ * per call, which is all streamable HTTP requires for a plain `tools/call`. A
36181
+ * real client belongs here only once something needs one.
36182
+ *
36183
+ * @module mcp/McpToolClient
36184
+ */
36185
+ /**
36186
+ * The version p4code speaks. Sent on `initialize` and echoed back as a header
36187
+ * on the follow-up calls, which is what the spec asks of a client that has
36188
+ * already negotiated.
36189
+ */
36190
+ const MCP_PROTOCOL_VERSION = "2025-06-18";
36191
+ const REQUEST_TIMEOUT_MS = 15e3;
36192
+ var McpToolCallError = class extends Schema$1.TaggedErrorClass()("McpToolCallError", {
36193
+ detail: Schema$1.String,
36194
+ status: Schema$1.NullOr(Schema$1.Number)
36195
+ }) {
36196
+ get message() {
36197
+ return this.detail;
36198
+ }
36199
+ };
36200
+ const unauthorized$1 = (status) => status === 401 || status === 403;
36201
+ /**
36202
+ * Read a JSON-RPC envelope out of a response that may be either JSON or SSE.
36203
+ *
36204
+ * A streamable-http server picks the encoding, and both are legal answers to a
36205
+ * single call. The SSE branch keeps the last `data:` payload that parses: a
36206
+ * server is free to emit progress notifications ahead of the result, and the
36207
+ * result is what a caller of this asked for.
36208
+ */
36209
+ const readEnvelope = (body) => {
36210
+ const direct = parseJsonObject(body);
36211
+ if (direct !== void 0) return direct;
36212
+ let latest;
36213
+ for (const line of body.split("\n")) {
36214
+ const trimmed = line.trim();
36215
+ if (!trimmed.startsWith("data:")) continue;
36216
+ const parsed = parseJsonObject(trimmed.slice(5).trim());
36217
+ if (parsed !== void 0) latest = parsed;
36218
+ }
36219
+ return latest;
36220
+ };
36221
+ const parseJsonObject = (text) => {
36222
+ if (text.length === 0) return void 0;
36223
+ try {
36224
+ const parsed = JSON.parse(text);
36225
+ return typeof parsed === "object" && parsed !== null ? parsed : void 0;
36226
+ } catch {
36227
+ return;
36228
+ }
36229
+ };
36230
+ const textOf = (content) => {
36231
+ if (!Array.isArray(content)) return "";
36232
+ const parts = [];
36233
+ for (const block of content) {
36234
+ if (typeof block !== "object" || block === null) continue;
36235
+ const record = block;
36236
+ if (record["type"] === "text" && typeof record["text"] === "string") parts.push(record["text"]);
36237
+ }
36238
+ return parts.join("\n");
36239
+ };
36240
+ /**
36241
+ * One `tools/call`, preceded by the `initialize` handshake the transport
36242
+ * requires.
36243
+ *
36244
+ * The handshake is not optional and not cacheable here: a server may hand out a
36245
+ * session id, and this holds no session state between calls. Two round trips
36246
+ * for one answer is the price of not keeping a connection open for a feature
36247
+ * that fires when somebody pastes something.
36248
+ */
36249
+ const callMcpTool = Effect.fn("McpToolClient.callMcpTool")(function* (input) {
36250
+ const http = yield* HttpClient$1.HttpClient;
36251
+ const post = (body, extraHeaders) => http.execute(HttpClientRequest$1.bodyJsonUnsafe(HttpClientRequest$1.setHeaders(HttpClientRequest$1.post(input.url), {
36252
+ ...input.headers,
36253
+ ...extraHeaders,
36254
+ accept: "application/json, text/event-stream"
36255
+ }), body)).pipe(Effect.timeout(REQUEST_TIMEOUT_MS), Effect.catchCause((cause) => Effect.fail(new McpToolCallError({
36256
+ detail: `Could not reach ${input.url}: ${String(cause)}`,
36257
+ status: null
36258
+ }))));
36259
+ const initialize = yield* post({
36260
+ jsonrpc: "2.0",
36261
+ id: 1,
36262
+ method: "initialize",
36263
+ params: {
36264
+ protocolVersion: MCP_PROTOCOL_VERSION,
36265
+ capabilities: {},
36266
+ clientInfo: {
36267
+ name: "p4code",
36268
+ version: "0.0.0"
36269
+ }
36270
+ }
36271
+ }, {});
36272
+ if (initialize.status >= 400) return yield* new McpToolCallError({
36273
+ detail: unauthorized$1(initialize.status) ? "The server rejected p4code's credentials." : `The server answered ${initialize.status} to initialize.`,
36274
+ status: initialize.status
36275
+ });
36276
+ const sessionId = initialize.headers["mcp-session-id"];
36277
+ const sessionHeaders = {
36278
+ "mcp-protocol-version": MCP_PROTOCOL_VERSION,
36279
+ ...sessionId === void 0 ? {} : { "mcp-session-id": sessionId }
36280
+ };
36281
+ yield* post({
36282
+ jsonrpc: "2.0",
36283
+ method: "notifications/initialized"
36284
+ }, sessionHeaders).pipe(Effect.ignore);
36285
+ const response = yield* post({
36286
+ jsonrpc: "2.0",
36287
+ id: 2,
36288
+ method: "tools/call",
36289
+ params: {
36290
+ name: input.toolName,
36291
+ arguments: input.arguments
36292
+ }
36293
+ }, sessionHeaders);
36294
+ const body = yield* response.text.pipe(Effect.catchCause(() => Effect.succeed("")));
36295
+ if (response.status >= 400) return yield* new McpToolCallError({
36296
+ detail: unauthorized$1(response.status) ? "The server rejected p4code's credentials." : `The server answered ${response.status} to ${input.toolName}.`,
36297
+ status: response.status
36298
+ });
36299
+ const envelope = readEnvelope(body);
36300
+ if (envelope === void 0) return yield* new McpToolCallError({
36301
+ detail: `The server's answer to ${input.toolName} was not JSON-RPC.`,
36302
+ status: response.status
36303
+ });
36304
+ const error = envelope["error"];
36305
+ if (typeof error === "object" && error !== null) {
36306
+ const detail = error["message"];
36307
+ return yield* new McpToolCallError({
36308
+ detail: typeof detail === "string" ? detail : `${input.toolName} failed.`,
36309
+ status: response.status
36310
+ });
36311
+ }
36312
+ const result = envelope["result"];
36313
+ if (typeof result !== "object" || result === null) return yield* new McpToolCallError({
36314
+ detail: `${input.toolName} returned no result.`,
36315
+ status: response.status
36316
+ });
36317
+ const resultRecord = result;
36318
+ const text = textOf(resultRecord["content"]);
36319
+ if (resultRecord["isError"] === true) return yield* new McpToolCallError({
36320
+ detail: text.length > 0 ? text : `${input.toolName} reported an error.`,
36321
+ status: response.status
36322
+ });
36323
+ return {
36324
+ structuredContent: resultRecord["structuredContent"],
36325
+ text
36326
+ };
36327
+ });
36328
+ //#endregion
36329
+ //#region src/mcp/TicketResolver.ts
36330
+ /**
36331
+ * The registered server name a Linear ticket is resolved through.
36332
+ *
36333
+ * Matched by name rather than by URL: the name is what the user typed in
36334
+ * Settings and what every other surface calls it, and a workspace may reach
36335
+ * Linear through a proxy URL that no pattern here would recognize.
36336
+ */
36337
+ const LINEAR_SERVER_NAME = "linear";
36338
+ /** Linear's own tool for reading one issue. */
36339
+ const LINEAR_GET_ISSUE_TOOL = "get_issue";
36340
+ var TicketResolver = class extends Context.Service()("@p4code/cli/mcp/TicketResolver") {};
36341
+ const stringField$1 = (record, key) => {
36342
+ const value = record[key];
36343
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
36344
+ };
36345
+ const asRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
36346
+ /**
36347
+ * Find the issue inside whatever the tool returned.
36348
+ *
36349
+ * MCP leaves the shape of a tool result to the tool, so this accepts the three
36350
+ * arrangements in the wild - the structured payload itself, that payload with
36351
+ * the issue nested one level down, or a text block that happens to be JSON -
36352
+ * rather than assuming the one Linear happens to send today.
36353
+ */
36354
+ const readIssue = (result) => {
36355
+ const structured = asRecord$1(result.structuredContent);
36356
+ if (structured !== void 0) {
36357
+ const nested = asRecord$1(structured["issue"]);
36358
+ if (nested !== void 0) return nested;
36359
+ if (stringField$1(structured, "url") !== void 0) return structured;
36360
+ }
36361
+ try {
36362
+ const parsed = JSON.parse(result.text);
36363
+ const record = asRecord$1(parsed);
36364
+ if (record === void 0) return structured;
36365
+ return asRecord$1(record["issue"]) ?? record;
36366
+ } catch {
36367
+ return structured;
36368
+ }
36369
+ };
36370
+ const make$36 = Effect.gen(function* () {
36371
+ const registry = yield* McpRegistry;
36372
+ const oauth = yield* McpOAuth;
36373
+ const http = yield* HttpClient$1.HttpClient;
36374
+ return { resolve: Effect.fn("TicketResolver.resolve")(function* (reference) {
36375
+ const identifier = parseTicketReference(reference);
36376
+ if (identifier === null) return yield* new TicketResolveError({
36377
+ reason: "not_found",
36378
+ detail: `"${reference}" does not look like a ticket reference.`
36379
+ });
36380
+ const server = (yield* registry.list).find((candidate) => candidate.registration.name.toLowerCase() === LINEAR_SERVER_NAME && candidate.registration.enabled);
36381
+ if (server === void 0 || server.registration.transport === "stdio") return yield* new TicketResolveError({
36382
+ reason: "not_configured",
36383
+ detail: "No Linear MCP server is registered on this machine. Add it in Settings to link tickets."
36384
+ });
36385
+ const registration = server.registration;
36386
+ const token = yield* oauth.accessTokenFor(registration);
36387
+ const headers = {
36388
+ ...registration.headers,
36389
+ ...Option.isSome(token) ? { authorization: `Bearer ${token.value}` } : {}
36390
+ };
36391
+ if (Option.isNone(token) && Object.keys(registration.headers ?? {}).length === 0) return yield* new TicketResolveError({
36392
+ reason: "not_authorized",
36393
+ detail: "Linear is registered but not signed in on this machine. Sign in from Settings."
36394
+ });
36395
+ const result = yield* callMcpTool({
36396
+ url: registration.url,
36397
+ headers,
36398
+ toolName: LINEAR_GET_ISSUE_TOOL,
36399
+ arguments: { id: identifier }
36400
+ }).pipe(Effect.provideService(HttpClient$1.HttpClient, http), Effect.mapError((error) => error.status === 401 || error.status === 403 ? new TicketResolveError({
36401
+ reason: "not_authorized",
36402
+ detail: "Linear rejected p4code's sign-in. Sign in again from Settings."
36403
+ }) : new TicketResolveError({
36404
+ reason: "unavailable",
36405
+ detail: error.detail
36406
+ })));
36407
+ const issue = readIssue(result);
36408
+ const url = issue === void 0 ? void 0 : stringField$1(issue, "url");
36409
+ const title = issue === void 0 ? void 0 : stringField$1(issue, "title");
36410
+ if (issue === void 0 || url === void 0 || title === void 0) return yield* new TicketResolveError({
36411
+ reason: "not_found",
36412
+ detail: `Linear returned nothing usable for ${identifier}.`
36413
+ });
36414
+ return {
36415
+ externalRef: {
36416
+ source: "linear",
36417
+ identifier: stringField$1(issue, "identifier") ?? identifier,
36418
+ url
36419
+ },
36420
+ title
36421
+ };
36422
+ }) };
36423
+ });
36424
+ const layer$31 = Layer.effect(TicketResolver, make$36);
35636
36425
  //#endregion
35637
36426
  //#region ../../packages/shared/src/KeyedCoalescingWorker.ts
35638
36427
  const makeKeyedCoalescingWorker = (options) => Effect.gen(function* () {
@@ -37722,7 +38511,7 @@ const makeWithOptions$1 = Effect.fn("TerminalManager.makeWithOptions")(function*
37722
38511
  subscribeMetadata
37723
38512
  });
37724
38513
  });
37725
- const layer$29 = Layer.effect(TerminalManager, make$34()).pipe(Layer.provide(layer$53));
38514
+ const layer$29 = Layer.effect(TerminalManager, make$34()).pipe(Layer.provide(layer$54));
37726
38515
  //#endregion
37727
38516
  //#region src/mcp/McpInvocationContext.ts
37728
38517
  var McpInvocationContext = class extends Context.Service()("@p4code/cli/mcp/McpInvocationContext") {};
@@ -44802,6 +45591,7 @@ const RPC_REQUIRED_SCOPE = /* @__PURE__ */ new Map([
44802
45591
  [WS_METHODS.tasksUpdate, AuthOrchestrationOperateScope],
44803
45592
  [WS_METHODS.tasksDelete, AuthOrchestrationOperateScope],
44804
45593
  [WS_METHODS.tasksStartThread, AuthOrchestrationOperateScope],
45594
+ [WS_METHODS.ticketsResolve, AuthOrchestrationReadScope],
44805
45595
  [WS_METHODS.subscribeTasks, AuthOrchestrationReadScope],
44806
45596
  [WS_METHODS.hubGetSyncStatus, AuthOrchestrationReadScope],
44807
45597
  [WS_METHODS.hubConnect, AuthOrchestrationOperateScope],
@@ -44956,6 +45746,7 @@ const makeWsRpcLayer = (currentSession, previewAutomationBroker) => WsRpcGroup.t
44956
45746
  const mcpOAuth = yield* McpOAuth;
44957
45747
  const claudeMcpFiles = yield* ClaudeMcpFiles;
44958
45748
  const skillRegistry = yield* SkillRegistry;
45749
+ const ticketResolver = yield* TicketResolver;
44959
45750
  const listMcpServersEverywhere = Effect.gen(function* () {
44960
45751
  const servers = [...yield* mcpRegistry.list];
44961
45752
  const projectRows = yield* projectionProjects.listAll().pipe(Effect.map((rows) => rows.filter((row) => row.deletedAt === null)), Effect.orElseSucceed(() => []));
@@ -45213,6 +46004,7 @@ const makeWsRpcLayer = (currentSession, previewAutomationBroker) => WsRpcGroup.t
45213
46004
  runtimeMode: bootstrap.createThread.runtimeMode,
45214
46005
  interactionMode: bootstrap.createThread.interactionMode,
45215
46006
  compressMode: bootstrap.createThread.compressMode,
46007
+ unpromptedSubagents: bootstrap.createThread.unpromptedSubagents,
45216
46008
  branch: bootstrap.createThread.branch,
45217
46009
  worktreePath: bootstrap.createThread.worktreePath,
45218
46010
  createdAt: bootstrap.createThread.createdAt
@@ -45477,6 +46269,7 @@ const makeWsRpcLayer = (currentSession, previewAutomationBroker) => WsRpcGroup.t
45477
46269
  runtimeMode: DEFAULT_RUNTIME_MODE$2,
45478
46270
  interactionMode: DEFAULT_PROVIDER_INTERACTION_MODE,
45479
46271
  compressMode: DEFAULT_COMPRESS_MODE,
46272
+ unpromptedSubagents: true,
45480
46273
  bootstrap: { createThread: {
45481
46274
  projectId: plan.projectId,
45482
46275
  title: threadTitle,
@@ -45484,6 +46277,7 @@ const makeWsRpcLayer = (currentSession, previewAutomationBroker) => WsRpcGroup.t
45484
46277
  runtimeMode: DEFAULT_RUNTIME_MODE$2,
45485
46278
  interactionMode: DEFAULT_PROVIDER_INTERACTION_MODE,
45486
46279
  compressMode: DEFAULT_COMPRESS_MODE,
46280
+ unpromptedSubagents: true,
45487
46281
  branch: null,
45488
46282
  worktreePath: null,
45489
46283
  createdAt
@@ -45502,6 +46296,13 @@ const makeWsRpcLayer = (currentSession, previewAutomationBroker) => WsRpcGroup.t
45502
46296
  threadId
45503
46297
  };
45504
46298
  }), { "rpc.aggregate": "tasks" }),
46299
+ /**
46300
+ * What a pasted reference is, straight from the tracker.
46301
+ *
46302
+ * Nothing is written here: the client decides whether the answer
46303
+ * becomes a task, so an accidental paste costs one request and no row.
46304
+ */
46305
+ [WS_METHODS.ticketsResolve]: ({ reference }) => observeRpcEffect$1(WS_METHODS.ticketsResolve, ticketResolver.resolve(reference), { "rpc.aggregate": "tickets" }),
45505
46306
  [WS_METHODS.subscribeTasks]: (_input) => observeRpcStreamEffect$1(WS_METHODS.subscribeTasks, Effect.succeed(taskRepository.streamWithSnapshot.pipe(Stream.catch((cause) => Stream.fromEffect(toTaskStoreError("tasks.subscribe")(cause))))), { "rpc.aggregate": "tasks" }),
45506
46307
  [WS_METHODS.hubGetSyncStatus]: (_input) => observeRpcEffect$1(WS_METHODS.hubGetSyncStatus, assetSync.status, { "rpc.aggregate": "hub" }),
45507
46308
  [WS_METHODS.hubConnect]: (input) => observeRpcEffect$1(WS_METHODS.hubConnect, hubLink.connect(input).pipe(Effect.mapError((cause) => new HubLinkError({ detail: cause.message })), Effect.andThen(assetSync.status)), { "rpc.aggregate": "hub" }),
@@ -45769,7 +46570,7 @@ const websocketRpcRouteLayer = Layer.unwrap(Effect.gen(function* () {
45769
46570
  const serverAuth = yield* EnvironmentAuth;
45770
46571
  const sessions = yield* SessionStore;
45771
46572
  const session = yield* serverAuth.authenticateWebSocketUpgrade(request).pipe(Effect.catchIf(isServerAuthCredentialError, (error) => failEnvironmentAuthInvalid(serverAuthCredentialReason(error))), Effect.catchIf(isServerAuthInternalError, (error) => failEnvironmentInternal("internal_error", error)));
45772
- const rpcWebSocketHttpEffect = yield* RpcServer.toHttpEffectWebsocket(WsRpcGroup, { disableTracing: true }).pipe(Effect.provide(makeWsRpcLayer(session, previewAutomationBroker).pipe(Layer.provideMerge(RpcSerialization.layerJson), Layer.provide(layer$33), Layer.provide(Layer.succeed(ServerSelfUpdate, serverSelfUpdate)), Layer.provide(Layer.succeed(TaskRepository, taskRepository)), Layer.provide(Layer.succeed(ProjectionProjectRepository, projectionProjects)), Layer.provide(Layer.succeed(P4ProjectFileLoader, p4ProjectFileLoader)), Layer.provide(layer$5.pipe(Layer.provide(layer$14.pipe(Layer.provide(Layer.mergeAll(layer$22, layer$20, layer$18, layer$16)), Layer.provideMerge(layer$38), Layer.provide(layer$36.pipe(Layer.provide(layer$37))))), Layer.provide(layer$39))))));
46573
+ const rpcWebSocketHttpEffect = yield* RpcServer.toHttpEffectWebsocket(WsRpcGroup, { disableTracing: true }).pipe(Effect.provide(makeWsRpcLayer(session, previewAutomationBroker).pipe(Layer.provideMerge(RpcSerialization.layerJson), Layer.provide(layer$34), Layer.provide(Layer.succeed(ServerSelfUpdate, serverSelfUpdate)), Layer.provide(Layer.succeed(TaskRepository, taskRepository)), Layer.provide(Layer.succeed(ProjectionProjectRepository, projectionProjects)), Layer.provide(Layer.succeed(P4ProjectFileLoader, p4ProjectFileLoader)), Layer.provide(layer$5.pipe(Layer.provide(layer$14.pipe(Layer.provide(Layer.mergeAll(layer$22, layer$20, layer$18, layer$16)), Layer.provideMerge(layer$39), Layer.provide(layer$37.pipe(Layer.provide(layer$38))))), Layer.provide(layer$40))))));
45773
46574
  return yield* Effect.acquireUseRelease(sessions.markConnected(session.sessionId), () => rpcWebSocketHttpEffect, () => sessions.markDisconnected(session.sessionId));
45774
46575
  }).pipe(Effect.catchTags({
45775
46576
  EnvironmentAuthInvalidError: HttpServerRespondable.toResponse,
@@ -45887,6 +46688,7 @@ const makeRemoteTaskRepository = Effect.fn("makeRemoteTaskRepository")(function*
45887
46688
  ...row.projectId === null ? {} : { projectId: row.projectId },
45888
46689
  ...row.repositoryKey === null ? {} : { repositoryKey: row.repositoryKey },
45889
46690
  ...row.threadId === null ? {} : { threadId: row.threadId },
46691
+ ...row.parentTaskId === null ? {} : { parentTaskId: row.parentTaskId },
45890
46692
  title: row.title,
45891
46693
  body: row.body,
45892
46694
  status: row.status,
@@ -48093,7 +48895,7 @@ const makeOpenCodeRuntime = Effect.gen(function* () {
48093
48895
  };
48094
48896
  });
48095
48897
  var OpenCodeRuntime = class extends Context.Service()("@p4code/cli/provider/opencodeRuntime") {};
48096
- const OpenCodeRuntimeLive = Layer.effect(OpenCodeRuntime, makeOpenCodeRuntime).pipe(Layer.provide(layer$71));
48898
+ const OpenCodeRuntimeLive = Layer.effect(OpenCodeRuntime, makeOpenCodeRuntime).pipe(Layer.provide(layer$72));
48097
48899
  //#endregion
48098
48900
  //#region src/textGeneration/TextGenerationUtils.ts
48099
48901
  const isTextGenerationError$3 = Schema$1.is(TextGenerationError);
@@ -52015,7 +52817,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
52015
52817
  const mcpSession = readMcpProviderSession(input.threadId);
52016
52818
  const externalMcpServers = options?.resolveMcpServers === void 0 ? {} : yield* options.resolveMcpServers;
52017
52819
  const narrateBeforeTools = options?.resolveToolCallNarration === void 0 ? DEFAULT_SERVER_SETTINGS.enableToolCallNarration : yield* options.resolveToolCallNarration;
52018
- const unpromptedSubagents = options?.resolveUnpromptedSubagents === void 0 ? DEFAULT_SERVER_SETTINGS.enableUnpromptedSubagents : yield* options.resolveUnpromptedSubagents;
52820
+ const unpromptedSubagents = input.unpromptedSubagents !== void 0 ? input.unpromptedSubagents : options?.resolveUnpromptedSubagents === void 0 ? DEFAULT_SERVER_SETTINGS.enableUnpromptedSubagents : yield* options.resolveUnpromptedSubagents;
52019
52821
  const compressRuleset = compressRulesetFor(input.compressMode ?? "off");
52020
52822
  const systemPromptAppend = [
52021
52823
  ...narrateBeforeTools ? [NARRATE_BEFORE_TOOLS_PROMPT] : [],
@@ -52114,6 +52916,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
52114
52916
  startedAt,
52115
52917
  basePermissionMode: permissionMode,
52116
52918
  currentApiModelId: apiModelId,
52919
+ currentUnpromptedSubagents: unpromptedSubagents,
52117
52920
  resumeSessionId: sessionId,
52118
52921
  pendingApprovals,
52119
52922
  pendingUserInputs,
@@ -52185,7 +52988,13 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
52185
52988
  const context = yield* requireSession(input.threadId);
52186
52989
  const modelSelection = input.modelSelection !== void 0 && input.modelSelection.instanceId === boundInstanceId ? input.modelSelection : void 0;
52187
52990
  if (context.turnState?.synthetic === true) yield* completeTurn(context, "completed");
52188
- const message = yield* buildUserMessageEffect(input, {
52991
+ const turnSubagentsPrompt = input.unpromptedSubagents !== void 0 && input.unpromptedSubagents !== context.currentUnpromptedSubagents ? input.unpromptedSubagents ? SUBAGENTS_ALLOWED_PROMPT : SUBAGENTS_ON_REQUEST_PROMPT : void 0;
52992
+ if (input.unpromptedSubagents !== void 0) context.currentUnpromptedSubagents = input.unpromptedSubagents;
52993
+ const turnInput = turnSubagentsPrompt === void 0 ? input : {
52994
+ ...input,
52995
+ input: input.input === void 0 ? turnSubagentsPrompt : `${turnSubagentsPrompt}\n\n${input.input}`
52996
+ };
52997
+ const message = yield* buildUserMessageEffect(turnInput, {
52189
52998
  fileSystem,
52190
52999
  attachmentsDir: serverConfig.attachmentsDir,
52191
53000
  boundInstanceId
@@ -85458,6 +86267,28 @@ const findTask = (taskId) => Effect.gen(function* () {
85458
86267
  const tasks = yield* TaskRepository;
85459
86268
  return isReadableTaskId(taskId) ? yield* tasks.getByReadableId(taskId) : yield* tasks.getById({ taskId });
85460
86269
  });
86270
+ /**
86271
+ * Resolve a parent reference to the UUID the column stores.
86272
+ *
86273
+ * A planning agent works in readable ids - it has just read `P4-12` off the
86274
+ * board - but the link is stored as the row key, because readable ids are
86275
+ * allocated per store and the one it quotes may have been minted on another
86276
+ * machine. Absent stays absent and an explicit null stays null: those mean
86277
+ * "leave it" and "clear it", neither of which is a reference to resolve.
86278
+ *
86279
+ * A readable id that names no task fails rather than being stored raw. The
86280
+ * opposite would file a subtask whose parent link can never resolve, which
86281
+ * reads on the board as a task that is part of nothing. A UUID is taken on
86282
+ * trust, matching the column's lack of a foreign key: a parent may legitimately
86283
+ * arrive after its child on a board synced from elsewhere.
86284
+ */
86285
+ const resolveParentTaskId = (parentTaskId, operation) => Effect.gen(function* () {
86286
+ if (parentTaskId === void 0 || parentTaskId === null) return parentTaskId;
86287
+ if (!isReadableTaskId(parentTaskId)) return parentTaskId;
86288
+ const parent = yield* findTask(parentTaskId).pipe(Effect.catch(toTaskStoreError(operation)));
86289
+ if (Option.isNone(parent)) return yield* new TaskNotFoundError({ taskId: parentTaskId });
86290
+ return parent.value.taskId;
86291
+ });
85461
86292
  const TaskToolkitHandlersLive = TaskToolkit.toLayer({
85462
86293
  task_list: (input) => Effect.gen(function* () {
85463
86294
  yield* requireTaskCapability();
@@ -85485,7 +86316,11 @@ const TaskToolkitHandlersLive = TaskToolkit.toLayer({
85485
86316
  const timestamp = yield* nowIso$1;
85486
86317
  const repositoryKey = yield* resolveTaskRepositoryKey(input.projectId);
85487
86318
  const readableIdPrefix = yield* resolveTaskReadableIdPrefix(input.projectId);
85488
- const row = buildTaskRow(input, taskId, timestamp, repositoryKey);
86319
+ const parentTaskId = yield* resolveParentTaskId(input.parentTaskId, "task_create");
86320
+ const row = buildTaskRow(parentTaskId === void 0 || parentTaskId === null ? input : {
86321
+ ...input,
86322
+ parentTaskId
86323
+ }, taskId, timestamp, repositoryKey);
85489
86324
  return yield* tasks.create(row, readableIdPrefix).pipe(Effect.catch(toTaskStoreError("task_create")));
85490
86325
  }),
85491
86326
  task_update: (input) => Effect.gen(function* () {
@@ -85494,13 +86329,12 @@ const TaskToolkitHandlersLive = TaskToolkit.toLayer({
85494
86329
  const timestamp = yield* nowIso$1;
85495
86330
  const taskId = isReadableTaskId(input.taskId) ? Option.getOrNull(yield* findTask(input.taskId).pipe(Effect.catch(toTaskStoreError("task_update"))))?.taskId : input.taskId;
85496
86331
  if (taskId === void 0) return yield* new TaskNotFoundError({ taskId: input.taskId });
85497
- const patch = input.projectId === void 0 ? {
85498
- ...input,
85499
- taskId
85500
- } : {
86332
+ const parentTaskId = yield* resolveParentTaskId(input.parentTaskId, "task_update");
86333
+ const patch = {
85501
86334
  ...input,
85502
86335
  taskId,
85503
- repositoryKey: yield* resolveTaskRepositoryKey(input.projectId)
86336
+ ...parentTaskId === void 0 ? {} : { parentTaskId },
86337
+ ...input.projectId === void 0 ? {} : { repositoryKey: yield* resolveTaskRepositoryKey(input.projectId) }
85504
86338
  };
85505
86339
  const patched = yield* tasks.patch(patch, timestamp).pipe(Effect.catch(toTaskStoreError("task_update")));
85506
86340
  if (Option.isNone(patched)) return yield* new TaskNotFoundError({ taskId: input.taskId });
@@ -85512,6 +86346,242 @@ const TaskToolkitHandlersLive = TaskToolkit.toLayer({
85512
86346
  })
85513
86347
  });
85514
86348
  //#endregion
86349
+ //#region src/sync/assetCompression.ts
86350
+ /**
86351
+ * Checking that a compressed asset still says what the original said.
86352
+ *
86353
+ * Compression here is a rewrite of prose an agent reads before it does anything
86354
+ * else: a skill, a memory file, a subagent definition. Rewriting that prose is
86355
+ * cheap to get almost right and expensive to get slightly wrong, because the
86356
+ * damage is silent. A dropped URL, a mangled command, a heading that no longer
86357
+ * matches the sentence pointing at it - none of that fails loudly. It just makes
86358
+ * the agent behave a little differently on every machine that syncs the file.
86359
+ *
86360
+ * So the write is gated on a comparison rather than on the model's word for it.
86361
+ * The rules are ported from caveman's `validate.py`, with the ones this task
86362
+ * calls out - headings, code blocks, URLs, paths, bullet counts - promoted to
86363
+ * refusals rather than warnings. A refusal costs one retry; a bad write costs
86364
+ * whatever the agent does next, on every machine, until someone notices.
86365
+ *
86366
+ * Pure on purpose. Everything here is a string comparison, so every rule can be
86367
+ * tested without a filesystem, a provider or a thread, and the tool that calls
86368
+ * it is left with only the parts that genuinely touch disk.
86369
+ *
86370
+ * @module sync/assetCompression
86371
+ */
86372
+ /**
86373
+ * How far the bullet count may move before the rewrite stops being compression.
86374
+ *
86375
+ * Not zero. Compression legitimately folds a two-line bullet into one, and a
86376
+ * rule that forbade that would forbid the feature. Caveman's own threshold,
86377
+ * kept because it is the one their receipts were measured against.
86378
+ */
86379
+ const BULLET_COUNT_TOLERANCE = .15;
86380
+ /**
86381
+ * Characters per token, for the before/after figures.
86382
+ *
86383
+ * An estimate, and labelled as one everywhere it surfaces. The exact count
86384
+ * belongs to whichever provider tokenizer will read the file, and asking one
86385
+ * would mean a network call inside a validation path that is otherwise pure.
86386
+ * Four is the usual English-prose approximation and is good enough to answer
86387
+ * the only question anyone asks here: did this get meaningfully smaller.
86388
+ */
86389
+ const CHARS_PER_TOKEN_ESTIMATE = 4;
86390
+ const URL_REGEX = /https?:\/\/[^\s)]+/gu;
86391
+ /**
86392
+ * Sentence punctuation swept up by the URL match.
86393
+ *
86394
+ * `See https://svgl.app for the catalogue.` and `Catalogue: https://svgl.app.`
86395
+ * name the same URL, and a comparison that disagreed would refuse every rewrite
86396
+ * that moved a link to the end of a sentence - which is most of them.
86397
+ */
86398
+ const URL_TRAILING_PUNCTUATION_REGEX = /[.,;:!?'"\])]+$/u;
86399
+ const FENCE_LINE_REGEX = /^(\s{0,3})(`{3,}|~{3,})(.*)$/u;
86400
+ const HEADING_REGEX = /^(#{1,6})\s+(.*)$/gmu;
86401
+ const BULLET_REGEX = /^\s*[-*+]\s+/gmu;
86402
+ const INLINE_CODE_REGEX = /`([^`]+)`/gu;
86403
+ /**
86404
+ * Crude, and deliberately so.
86405
+ *
86406
+ * Either a path prefix (`./`, `../`, `/`, a drive letter) or a separator inside
86407
+ * the match. It over-matches - `and/or` is a "path" by this rule - and the
86408
+ * over-match is the safe direction: a false positive refuses a rewrite that
86409
+ * dropped the phrase, and a false negative lets a real path go missing.
86410
+ */
86411
+ const PATH_REGEX = /(?:\.\/|\.\.\/|\/|[A-Za-z]:\\)[\w\-/\\.]+|[\w\-.]+[/\\][\w\-/\\.]+/gu;
86412
+ /** YAML frontmatter: `---` on the first line, `---` on a line of its own after. */
86413
+ const FRONTMATTER_REGEX = /^---\r?\n[\s\S]*?\r?\n---\r?\n/u;
86414
+ /**
86415
+ * Filenames that almost certainly hold secrets, and the directories that do.
86416
+ *
86417
+ * Compressing a file means handing its bytes to a model, so this is checked
86418
+ * before the read rather than after it. The asset roots should never contain
86419
+ * any of these, which is exactly why the check is cheap to keep: it costs
86420
+ * nothing in the ordinary case and is the only thing standing between a
86421
+ * misplaced `credentials.md` and a provider's API.
86422
+ */
86423
+ const SENSITIVE_BASENAME_REGEX = /^(\.env(\..+)?|\.netrc|credentials(\..+)?|secrets?(\..+)?|passwords?(\..+)?|id_(rsa|dsa|ecdsa|ed25519)(\.pub)?|authorized_keys|known_hosts|.*\.(pem|key|p12|pfx|crt|cer|jks|keystore|asc|gpg))$/iu;
86424
+ const SENSITIVE_PATH_SEGMENTS = /* @__PURE__ */ new Set([
86425
+ ".ssh",
86426
+ ".aws",
86427
+ ".gnupg",
86428
+ ".kube",
86429
+ ".docker"
86430
+ ]);
86431
+ const SENSITIVE_NAME_TOKENS = [
86432
+ "secret",
86433
+ "credential",
86434
+ "password",
86435
+ "passwd",
86436
+ "apikey",
86437
+ "accesskey",
86438
+ "token",
86439
+ "privatekey"
86440
+ ];
86441
+ const segmentsOf = (filePath) => filePath.split(/[/\\]/u).filter((segment) => segment.length > 0);
86442
+ /**
86443
+ * Whether this path looks like something that must not be sent to a model.
86444
+ *
86445
+ * Name-based, because that is all that is knowable before the read. Separators
86446
+ * inside the name are normalized away first so `api-key.md` and `api_key.md`
86447
+ * are the same answer.
86448
+ */
86449
+ const isSensitiveAssetPath = (filePath) => {
86450
+ const segments = segmentsOf(filePath);
86451
+ const name = segments.at(-1) ?? "";
86452
+ if (SENSITIVE_BASENAME_REGEX.test(name)) return true;
86453
+ if (segments.some((segment) => SENSITIVE_PATH_SEGMENTS.has(segment.toLowerCase()))) return true;
86454
+ const flattened = name.toLowerCase().replace(/[_\-\s.]/gu, "");
86455
+ return SENSITIVE_NAME_TOKENS.some((token) => flattened.includes(token));
86456
+ };
86457
+ /** The frontmatter block including its delimiters, or the empty string. */
86458
+ const frontmatterOf = (text) => FRONTMATTER_REGEX.exec(text)?.[0] ?? "";
86459
+ const headingsOf = (text) => [...text.matchAll(HEADING_REGEX)].map((match) => `${match[1] ?? ""} ${(match[2] ?? "").trim()}`);
86460
+ /**
86461
+ * Fenced code blocks, as whole strings, fences included.
86462
+ *
86463
+ * Line-based rather than one regex, so a closing fence is only a closing fence
86464
+ * when CommonMark says it is: same character, at least as long as the opener,
86465
+ * nothing after it. That is what lets a four-backtick block wrap three-backtick
86466
+ * content without the extractor stopping in the middle of it.
86467
+ *
86468
+ * An unclosed fence is skipped rather than reported. It means the markdown was
86469
+ * already malformed, and including it would fail every compression of a file
86470
+ * that was broken before anyone touched it.
86471
+ */
86472
+ const codeBlocksOf = (text) => {
86473
+ const lines = text.split("\n");
86474
+ const blocks = [];
86475
+ let index = 0;
86476
+ while (index < lines.length) {
86477
+ const opening = FENCE_LINE_REGEX.exec(lines[index] ?? "");
86478
+ if (opening === null) {
86479
+ index += 1;
86480
+ continue;
86481
+ }
86482
+ const fence = opening[2] ?? "";
86483
+ const collected = [lines[index] ?? ""];
86484
+ index += 1;
86485
+ let closed = false;
86486
+ while (index < lines.length) {
86487
+ const line = lines[index] ?? "";
86488
+ const closing = FENCE_LINE_REGEX.exec(line);
86489
+ const closingFence = closing?.[2] ?? "";
86490
+ if (closing !== null && closingFence[0] === fence[0] && closingFence.length >= fence.length && (closing[3] ?? "").trim().length === 0) {
86491
+ collected.push(line);
86492
+ closed = true;
86493
+ index += 1;
86494
+ break;
86495
+ }
86496
+ collected.push(line);
86497
+ index += 1;
86498
+ }
86499
+ if (closed) blocks.push(collected.join("\n"));
86500
+ }
86501
+ return blocks;
86502
+ };
86503
+ const withoutCodeBlocks = (text) => codeBlocksOf(text).reduce((rest, block) => rest.replace(block, ""), text);
86504
+ const inlineCodeCounts = (text) => {
86505
+ const counts = /* @__PURE__ */ new Map();
86506
+ for (const match of withoutCodeBlocks(text).matchAll(INLINE_CODE_REGEX)) {
86507
+ const span = match[1] ?? "";
86508
+ counts.set(span, (counts.get(span) ?? 0) + 1);
86509
+ }
86510
+ return counts;
86511
+ };
86512
+ const urlsOf = (text) => new Set([...text.matchAll(URL_REGEX)].map((match) => match[0].replace(URL_TRAILING_PUNCTUATION_REGEX, "")));
86513
+ /**
86514
+ * Paths, with URLs taken out of the text first.
86515
+ *
86516
+ * `//svgl.app` inside `https://svgl.app` is a path by the pattern's reckoning,
86517
+ * so leaving the URLs in would have every link contribute a phantom path whose
86518
+ * spelling changes with the punctuation after it.
86519
+ */
86520
+ const pathsOf = (text) => new Set([...text.replace(URL_REGEX, " ").matchAll(PATH_REGEX)].map((match) => match[0]));
86521
+ const countOf = (text, pattern) => [...text.matchAll(pattern)].length;
86522
+ const missingFrom = (original, compressed) => [...original].filter((value) => !compressed.has(value));
86523
+ const addedTo = (original, compressed) => [...compressed].filter((value) => !original.has(value));
86524
+ const quoteList = (values) => values.map((value) => `'${value}'`).join(", ");
86525
+ /**
86526
+ * Compare a compressed rewrite against the original it came from.
86527
+ *
86528
+ * Errors are the things whose loss changes what the file instructs: a code
86529
+ * block, a URL, a path, an inline span, a heading, the frontmatter. Additions
86530
+ * are warnings rather than errors throughout, because a rewrite that names one
86531
+ * more path than the original is doing something odd but is not losing
86532
+ * anything, and a refusal there would be a refusal nobody can act on.
86533
+ */
86534
+ const validateCompression = (original, compressed) => {
86535
+ const errors = [];
86536
+ const warnings = [];
86537
+ if (frontmatterOf(original) !== frontmatterOf(compressed)) errors.push("The YAML frontmatter changed. It is metadata the provider parses, not prose; return it byte-identical.");
86538
+ const originalHeadings = headingsOf(original);
86539
+ const compressedHeadings = headingsOf(compressed);
86540
+ if (originalHeadings.length !== compressedHeadings.length || originalHeadings.some((heading, at) => heading !== compressedHeadings[at])) errors.push(`Headings changed: ${originalHeadings.length} in the original, ${compressedHeadings.length} in the rewrite. Keep every heading, in order, with its exact text.`);
86541
+ const originalBlocks = codeBlocksOf(original);
86542
+ const compressedBlocks = codeBlocksOf(compressed);
86543
+ if (originalBlocks.length !== compressedBlocks.length || originalBlocks.some((block, at) => block !== compressedBlocks[at])) errors.push(`Code blocks are not byte-identical: ${originalBlocks.length} in the original, ${compressedBlocks.length} in the rewrite. Copy them through unchanged.`);
86544
+ const originalUrls = urlsOf(original);
86545
+ const compressedUrls = urlsOf(compressed);
86546
+ const lostUrls = missingFrom(originalUrls, compressedUrls);
86547
+ if (lostUrls.length > 0) errors.push(`URLs missing from the rewrite: ${quoteList(lostUrls)}.`);
86548
+ const addedUrls = addedTo(originalUrls, compressedUrls);
86549
+ if (addedUrls.length > 0) warnings.push(`URLs the original did not have: ${quoteList(addedUrls)}.`);
86550
+ const originalPaths = pathsOf(original);
86551
+ const compressedPaths = pathsOf(compressed);
86552
+ const lostPaths = missingFrom(originalPaths, compressedPaths);
86553
+ if (lostPaths.length > 0) errors.push(`Paths missing from the rewrite: ${quoteList(lostPaths)}.`);
86554
+ const addedPaths = addedTo(originalPaths, compressedPaths);
86555
+ if (addedPaths.length > 0) warnings.push(`Paths the original did not have: ${quoteList(addedPaths)}.`);
86556
+ const originalInline = inlineCodeCounts(original);
86557
+ const compressedInline = inlineCodeCounts(compressed);
86558
+ const lostInline = [...originalInline].filter(([span, count]) => (compressedInline.get(span) ?? 0) < count);
86559
+ if (lostInline.length > 0) errors.push(`Inline code spans missing from the rewrite: ${quoteList(lostInline.map(([span]) => span))}.`);
86560
+ const originalBullets = countOf(original, BULLET_REGEX);
86561
+ const compressedBullets = countOf(compressed, BULLET_REGEX);
86562
+ if (originalBullets > 0) {
86563
+ if (Math.abs(originalBullets - compressedBullets) / originalBullets > BULLET_COUNT_TOLERANCE) errors.push(`Bullet count moved from ${originalBullets} to ${compressedBullets}, past the ${Math.round(BULLET_COUNT_TOLERANCE * 100)}% tolerance. Compress the wording of each bullet rather than merging or dropping bullets.`);
86564
+ }
86565
+ return {
86566
+ errors,
86567
+ warnings
86568
+ };
86569
+ };
86570
+ /** Roughly how many tokens this text costs to read. Estimated, never measured. */
86571
+ const estimateTokens = (text) => Math.ceil(text.length / CHARS_PER_TOKEN_ESTIMATE);
86572
+ /**
86573
+ * The name a backup of this file is kept under.
86574
+ *
86575
+ * Timestamped rather than fixed. Caveman refuses to compress when a backup
86576
+ * already exists, which protects the first original at the cost of blocking
86577
+ * every later compression; keeping one backup per write protects every version
86578
+ * instead, including the edits someone made between two compressions.
86579
+ *
86580
+ * The parent directory's name is carried in the path rather than the file name
86581
+ * so that `global-memory/feedback_svgl.md` and `svgl/SKILL.md` cannot collide.
86582
+ */
86583
+ const backupFileName = (input) => `${input.fileName}.${input.atIso.replace(/[:.]/gu, "-")}.original`;
86584
+ //#endregion
85515
86585
  //#region src/mcp/toolkits/threads/tools.ts
85516
86586
  /**
85517
86587
  * Self-scoped on purpose: neither tool takes a thread id. The thread acted on
@@ -85543,10 +86613,46 @@ const MemoryAppendTool = Tool.make("memory_append", {
85543
86613
  Path.Path
85544
86614
  ]
85545
86615
  }).annotate(Tool.Title, "Append to memory").annotate(Tool.Readonly, false).annotate(Tool.Destructive, false).annotate(Tool.Idempotent, false);
85546
- const ThreadToolkit = Toolkit.make(ThreadRenameTool, MemoryAppendTool);
86616
+ /**
86617
+ * Overwrite an asset with a compressed version of itself, if it survives the
86618
+ * comparison.
86619
+ *
86620
+ * A tool rather than an instruction to use `Edit` because the guarantee is the
86621
+ * point: the original is copied out of every synced directory first, the
86622
+ * rewrite is compared against it, and a rewrite that lost a URL or a code block
86623
+ * is refused with the list of what it lost. An agent editing the file directly
86624
+ * can be asked to preserve those things but cannot be prevented from not.
86625
+ */
86626
+ const AssetCompressTool = Tool.make("asset_compress", {
86627
+ description: "Overwrite one agent asset file - a skill, a memory file, a subagent definition - with a compressed rewrite of it, after checking the rewrite kept everything that carries meaning. Headings, fenced code blocks, inline code, URLs, paths and YAML frontmatter must come back unchanged; only the prose may be rewritten. The original is backed up outside every synced directory first. If the check fails nothing is written and the failed checks are returned, so a corrected rewrite can be sent as a second call.",
86628
+ parameters: AssetCompressInput,
86629
+ success: AssetCompressResult,
86630
+ failure: ThreadControlToolError,
86631
+ dependencies: [
86632
+ McpInvocationContext,
86633
+ ServerConfig$1,
86634
+ ServerSettingsService,
86635
+ FileSystem.FileSystem,
86636
+ Path.Path
86637
+ ]
86638
+ }).annotate(Tool.Title, "Compress an asset").annotate(Tool.Readonly, false).annotate(Tool.Destructive, true).annotate(Tool.Idempotent, false);
86639
+ const ThreadToolkit = Toolkit.make(ThreadRenameTool, MemoryAppendTool, AssetCompressTool);
85547
86640
  //#endregion
85548
86641
  //#region src/mcp/toolkits/threads/handlers.ts
85549
86642
  const DEFAULT_MEMORY_APPEND_TARGET = "CLAUDE.md";
86643
+ /**
86644
+ * Where originals are kept.
86645
+ *
86646
+ * Under the server's state directory, which is the one place on this machine
86647
+ * that is neither an asset root nor inside one. A backup next to the file would
86648
+ * be published by the next sync and re-read by the next agent turn, which is
86649
+ * the opposite of what compressing the file was for.
86650
+ */
86651
+ const COMPRESS_BACKUP_DIRNAME = "asset-compress-backups";
86652
+ /** Prose only. A skill may ship scripts, and a rewritten script is a broken one. */
86653
+ const COMPRESSIBLE_EXTENSION = ".md";
86654
+ /** The same test the asset readers use: a NUL is what breaks a JSON text column. */
86655
+ const looksBinary = (content) => content.includes("\0");
85550
86656
  const ThreadToolkitHandlersLive = ThreadToolkit.toLayer({
85551
86657
  thread_rename: (input) => Effect.gen(function* () {
85552
86658
  const invocation = yield* requireThreadCapability();
@@ -85596,6 +86702,68 @@ const ThreadToolkitHandlersLive = ThreadToolkit.toLayer({
85596
86702
  file,
85597
86703
  path: filePath
85598
86704
  };
86705
+ }),
86706
+ asset_compress: (input) => Effect.gen(function* () {
86707
+ yield* requireThreadCapability();
86708
+ const path = yield* Path.Path;
86709
+ const fileSystem = yield* FileSystem.FileSystem;
86710
+ const config = yield* ServerConfig$1;
86711
+ const claudeHome = { homePath: (yield* (yield* ServerSettingsService).getSettings.pipe(Effect.orElseSucceed(() => void 0)))?.providers.claudeAgent.homePath ?? "" };
86712
+ const refuse = (target, detail) => new AssetCompressRefusedError({
86713
+ path: target,
86714
+ detail
86715
+ });
86716
+ if (!path.isAbsolute(input.path)) return yield* refuse(input.path, "The path must be absolute.");
86717
+ const filePath = path.resolve(input.path);
86718
+ const fileName = path.basename(filePath);
86719
+ if (path.extname(fileName).toLowerCase() !== COMPRESSIBLE_EXTENSION) return yield* refuse(filePath, `Only ${COMPRESSIBLE_EXTENSION} files are compressed. Scripts and data files an asset ships alongside its prose are left alone.`);
86720
+ if (isSensitiveAssetPath(filePath)) return yield* refuse(filePath, "The name looks like credentials, keys or secrets, and compressing sends the file's contents to a model.");
86721
+ const configDir = yield* resolveClaudeConfigDirPath(claudeHome, process.env);
86722
+ const skillsDir = yield* resolveClaudeUserSkillsDir(claudeHome);
86723
+ const agentsDir = yield* resolveClaudeUserAgentsDir(claudeHome);
86724
+ const within = (root) => {
86725
+ const relative = path.relative(root, filePath);
86726
+ return relative.length > 0 && !relative.startsWith("..") && !path.isAbsolute(relative);
86727
+ };
86728
+ const memoryDirectoryName = path.relative(configDir, filePath).split(path.sep)[0] ?? "";
86729
+ if (!(within(skillsDir) || within(agentsDir) || path.dirname(filePath) === configDir && isMemoryFileName(fileName) || within(configDir) && isMemoryDirectoryName(memoryDirectoryName))) return yield* refuse(filePath, `Not an agent asset. Compressible files live under '${skillsDir}', under '${agentsDir}', or in '${configDir}' as a memory file or inside ${MEMORY_DIRECTORY_NAMES.join(" or ")}.`);
86730
+ const original = yield* fileSystem.readFileString(filePath).pipe(Effect.orElseSucceed(() => void 0));
86731
+ if (original === void 0) return yield* refuse(filePath, "The file could not be read.");
86732
+ if (looksBinary(original)) return yield* refuse(filePath, "The file is not text.");
86733
+ if (original.trim().length === 0) return yield* refuse(filePath, "The file is empty.");
86734
+ const compressed = input.content;
86735
+ if (compressed.trim().length === 0) return yield* refuse(filePath, "The rewrite is blank.");
86736
+ if (compressed.trim() === original.trim()) return yield* refuse(filePath, "The rewrite is the file that is already there. Nothing was written.");
86737
+ if (compressed.length >= original.length) return yield* refuse(filePath, `The rewrite is not smaller: ${original.length} characters became ${compressed.length}. Send the file's contents alone, with no commentary or wrapping fence.`);
86738
+ const validation = validateCompression(original, compressed);
86739
+ if (validation.errors.length > 0) return yield* new AssetCompressValidationFailedError({
86740
+ path: filePath,
86741
+ errors: validation.errors
86742
+ });
86743
+ const backupPath = path.join(config.stateDir, COMPRESS_BACKUP_DIRNAME, path.basename(path.dirname(filePath)), backupFileName({
86744
+ fileName,
86745
+ atIso: DateTime.formatIso(yield* DateTime.now)
86746
+ }));
86747
+ if (((yield* writeFileStringAtomically({
86748
+ filePath: backupPath,
86749
+ contents: original
86750
+ }).pipe(Effect.as(true), Effect.orElseSucceed(() => false))) ? yield* fileSystem.readFileString(backupPath).pipe(Effect.orElseSucceed(() => void 0)) : void 0) !== original) {
86751
+ yield* fileSystem.remove(backupPath, { force: true }).pipe(Effect.ignore);
86752
+ return yield* refuse(filePath, `The original could not be backed up to '${backupPath}', so it was left as it is.`);
86753
+ }
86754
+ yield* writeFileStringAtomically({
86755
+ filePath,
86756
+ contents: compressed
86757
+ }).pipe(Effect.mapError((cause) => refuse(filePath, `The write failed: ${cause.message}. The original is at '${backupPath}'.`)));
86758
+ return {
86759
+ path: filePath,
86760
+ backupPath,
86761
+ charsBefore: original.length,
86762
+ charsAfter: compressed.length,
86763
+ estimatedTokensBefore: estimateTokens(original),
86764
+ estimatedTokensAfter: estimateTokens(compressed),
86765
+ warnings: validation.warnings
86766
+ };
85599
86767
  })
85600
86768
  });
85601
86769
  //#endregion
@@ -87179,7 +88347,8 @@ const make$2 = Effect.gen(function* () {
87179
88347
  modelSelection: desiredModelSelection,
87180
88348
  ...input?.resumeCursor !== void 0 ? { resumeCursor: input.resumeCursor } : {},
87181
88349
  runtimeMode: desiredRuntimeMode,
87182
- compressMode: thread.compressMode
88350
+ compressMode: thread.compressMode,
88351
+ unpromptedSubagents: thread.unpromptedSubagents
87183
88352
  });
87184
88353
  const bindSessionToThread = (session) => Effect.gen(function* () {
87185
88354
  if (session.providerInstanceId === void 0) return yield* new ProviderAdapterRequestError({
@@ -87281,7 +88450,8 @@ const make$2 = Effect.gen(function* () {
87281
88450
  ...normalizedAttachments.length > 0 ? { attachments: normalizedAttachments } : {},
87282
88451
  ...modelForTurn !== void 0 ? { modelSelection: modelForTurn } : {},
87283
88452
  ...input.interactionMode !== void 0 ? { interactionMode: input.interactionMode } : {},
87284
- compressMode
88453
+ compressMode,
88454
+ unpromptedSubagents: thread.unpromptedSubagents
87285
88455
  };
87286
88456
  });
87287
88457
  const maybeGenerateAndRenameWorktreeBranchForFirstTurn = Effect.fn("maybeGenerateAndRenameWorktreeBranchForFirstTurn")(function* (input) {
@@ -88537,7 +89707,7 @@ const ObservabilityLive = Layer.unwrap(Effect.gen(function* () {
88537
89707
  sink,
88538
89708
  ...delegate ? { delegate } : {}
88539
89709
  });
88540
- return Layer.mergeAll(Layer.succeed(Tracer.Tracer, tracer), layer$40(sink));
89710
+ return Layer.mergeAll(Layer.succeed(Tracer.Tracer, tracer), layer$41(sink));
88541
89711
  })).pipe(Layer.provide(OtlpExporter.layerFlusher), Layer.provideMerge(otlpSerializationLayer));
88542
89712
  const metricsLayer = config.otlpMetricsUrl === void 0 ? Layer.empty : OtlpMetrics.layer({
88543
89713
  url: config.otlpMetricsUrl,
@@ -88816,25 +89986,25 @@ const ReactorLayerLive = Layer.empty.pipe(Layer.provideMerge(OrchestrationReacto
88816
89986
  const ProviderSessionDirectoryLayerLive = ProviderSessionDirectoryLive.pipe(Layer.provide(layer$3));
88817
89987
  const ProviderLayerLive = ProviderServiceLive.pipe(Layer.provide(ProviderAdapterRegistryLive), Layer.provideMerge(ProviderSessionDirectoryLayerLive));
88818
89988
  const PersistenceLayerLive = Layer.empty.pipe(Layer.provideMerge(layerConfig));
88819
- const VcsDriverRegistryLayerLive = layer$36.pipe(Layer.provide(layer$37));
88820
- const SourceControlProviderRegistryLayerLive = layer$14.pipe(Layer.provide(Layer.mergeAll(layer$22, layer$20, layer$18, layer$16)), Layer.provideMerge(layer$38), Layer.provideMerge(VcsDriverRegistryLayerLive));
88821
- const GitManagerLayerLive = layer$13.pipe(Layer.provideMerge(layer$23), Layer.provideMerge(layer$38), Layer.provideMerge(SourceControlProviderRegistryLayerLive), Layer.provideMerge(layer$32));
88822
- const GitLayerLive = Layer.empty.pipe(Layer.provideMerge(GitManagerLayerLive), Layer.provideMerge(layer$38));
89989
+ const VcsDriverRegistryLayerLive = layer$37.pipe(Layer.provide(layer$38));
89990
+ const SourceControlProviderRegistryLayerLive = layer$14.pipe(Layer.provide(Layer.mergeAll(layer$22, layer$20, layer$18, layer$16)), Layer.provideMerge(layer$39), Layer.provideMerge(VcsDriverRegistryLayerLive));
89991
+ const GitManagerLayerLive = layer$13.pipe(Layer.provideMerge(layer$23), Layer.provideMerge(layer$39), Layer.provideMerge(SourceControlProviderRegistryLayerLive), Layer.provideMerge(layer$33));
89992
+ const GitLayerLive = Layer.empty.pipe(Layer.provideMerge(GitManagerLayerLive), Layer.provideMerge(layer$39));
88823
89993
  const GitWorkflowLayerLive = layer$12.pipe(Layer.provideMerge(VcsDriverRegistryLayerLive), Layer.provideMerge(GitLayerLive));
88824
- const SourceControlRepositoryServiceLayerLive = layer$4.pipe(Layer.provideMerge(layer$38), Layer.provideMerge(SourceControlProviderRegistryLayerLive));
88825
- const ReviewLayerLive = layer$9.pipe(Layer.provideMerge(layer$38), Layer.provideMerge(VcsDriverRegistryLayerLive));
88826
- const VcsLayerLive = Layer.empty.pipe(Layer.provideMerge(layer$37), Layer.provideMerge(VcsDriverRegistryLayerLive), Layer.provideMerge(layer$10.pipe(Layer.provide(VcsDriverRegistryLayerLive))), Layer.provideMerge(GitWorkflowLayerLive), Layer.provideMerge(ReviewLayerLive), Layer.provideMerge(SourceControlRepositoryServiceLayerLive), Layer.provideMerge(layer$11.pipe(Layer.provide(GitWorkflowLayerLive))));
88827
- const CheckpointingLayerLive = Layer.empty.pipe(Layer.provideMerge(layer$34), Layer.provideMerge(layer$35.pipe(Layer.provide(VcsDriverRegistryLayerLive))));
88828
- const PortScannerLayerLive = layer$30.pipe(Layer.provide(layer$53));
89994
+ const SourceControlRepositoryServiceLayerLive = layer$4.pipe(Layer.provideMerge(layer$39), Layer.provideMerge(SourceControlProviderRegistryLayerLive));
89995
+ const ReviewLayerLive = layer$9.pipe(Layer.provideMerge(layer$39), Layer.provideMerge(VcsDriverRegistryLayerLive));
89996
+ const VcsLayerLive = Layer.empty.pipe(Layer.provideMerge(layer$38), Layer.provideMerge(VcsDriverRegistryLayerLive), Layer.provideMerge(layer$10.pipe(Layer.provide(VcsDriverRegistryLayerLive))), Layer.provideMerge(GitWorkflowLayerLive), Layer.provideMerge(ReviewLayerLive), Layer.provideMerge(SourceControlRepositoryServiceLayerLive), Layer.provideMerge(layer$11.pipe(Layer.provide(GitWorkflowLayerLive))));
89997
+ const CheckpointingLayerLive = Layer.empty.pipe(Layer.provideMerge(layer$35), Layer.provideMerge(layer$36.pipe(Layer.provide(VcsDriverRegistryLayerLive))));
89998
+ const PortScannerLayerLive = layer$30.pipe(Layer.provide(layer$54));
88829
89999
  const TerminalLayerLive = layer$29.pipe(Layer.provide(PtyAdapterLive), Layer.provide(PortScannerLayerLive));
88830
90000
  const PreviewLayerLive = Layer.empty.pipe(Layer.provideMerge(layer$27), Layer.provideMerge(PortScannerLayerLive));
88831
- const WorkspaceEntriesLayerLive = layer$25.pipe(Layer.provide(layer$43));
88832
- const WorkspaceFileSystemLayerLive = layer$24.pipe(Layer.provide(layer$43), Layer.provide(WorkspaceEntriesLayerLive));
88833
- const WorkspaceLayerLive = Layer.mergeAll(layer$43, WorkspaceEntriesLayerLive, WorkspaceFileSystemLayerLive);
88834
- const ProjectFaviconResolverLayerLive = layer$41.pipe(Layer.provide(layer$43), Layer.provide(layer$42));
88835
- const AuthLayerLive = layer$63.pipe(Layer.provideMerge(PersistenceLayerLive), Layer.provide(layer$67));
90001
+ const WorkspaceEntriesLayerLive = layer$25.pipe(Layer.provide(layer$44));
90002
+ const WorkspaceFileSystemLayerLive = layer$24.pipe(Layer.provide(layer$44), Layer.provide(WorkspaceEntriesLayerLive));
90003
+ const WorkspaceLayerLive = Layer.mergeAll(layer$44, WorkspaceEntriesLayerLive, WorkspaceFileSystemLayerLive);
90004
+ const ProjectFaviconResolverLayerLive = layer$42.pipe(Layer.provide(layer$44), Layer.provide(layer$43));
90005
+ const AuthLayerLive = layer$64.pipe(Layer.provideMerge(PersistenceLayerLive), Layer.provide(layer$68));
88836
90006
  const ProviderRuntimeLayerLive = ProviderSessionReaperLive.pipe(Layer.provideMerge(ProviderLayerLive), Layer.provideMerge(OrchestrationLayerLive));
88837
- const RuntimeDependenciesLive = ReactorLayerLive.pipe(Layer.provideMerge(CheckpointingLayerLive), Layer.provideMerge(SourceControlProviderRegistryLayerLive), Layer.provideMerge(GitLayerLive), Layer.provideMerge(VcsLayerLive), Layer.provideMerge(ProviderRuntimeLayerLive), Layer.provideMerge(Layer.mergeAll(TerminalLayerLive, PreviewLayerLive)), Layer.provideMerge(PersistenceLayerLive), Layer.provideMerge(layer$51), Layer.provideMerge(ProviderRegistryLive), Layer.provideMerge(ProviderInstanceRegistryHydrationLive), Layer.provideMerge(ProviderEventLoggersLive), Layer.provideMerge(OpenCodeRuntimeLive), Layer.provideMerge(layer$60.pipe(Layer.provide(layer$67))), Layer.provideMerge(WorkspaceLayerLive), Layer.provideMerge(ProjectFaviconResolverLayerLive), Layer.provideMerge(layer$52), Layer.provideMerge(layer$45), Layer.provideMerge(AuthLayerLive), Layer.provideMerge(Layer.mergeAll(layer$57, layer$31).pipe(Layer.provideMerge(layer$58), Layer.provideMerge(layer$59))), Layer.provideMerge(layer$67)).pipe(Layer.provideMerge(layer$8), Layer.provideMerge(layer$7), Layer.provideMerge(layer$6), Layer.provideMerge(layer$48), Layer.provideMerge(layer$50), Layer.provideMerge(layer$49), Layer.provide(layer$71));
90007
+ const RuntimeDependenciesLive = ReactorLayerLive.pipe(Layer.provideMerge(CheckpointingLayerLive), Layer.provideMerge(SourceControlProviderRegistryLayerLive), Layer.provideMerge(GitLayerLive), Layer.provideMerge(VcsLayerLive), Layer.provideMerge(ProviderRuntimeLayerLive), Layer.provideMerge(Layer.mergeAll(TerminalLayerLive, PreviewLayerLive)), Layer.provideMerge(PersistenceLayerLive), Layer.provideMerge(layer$52), Layer.provideMerge(ProviderRegistryLive), Layer.provideMerge(ProviderInstanceRegistryHydrationLive), Layer.provideMerge(ProviderEventLoggersLive), Layer.provideMerge(OpenCodeRuntimeLive), Layer.provideMerge(layer$61.pipe(Layer.provide(layer$68))), Layer.provideMerge(WorkspaceLayerLive), Layer.provideMerge(ProjectFaviconResolverLayerLive), Layer.provideMerge(layer$53), Layer.provideMerge(layer$46), Layer.provideMerge(AuthLayerLive), Layer.provideMerge(layer$31.pipe(Layer.provideMerge(Layer.mergeAll(layer$58, layer$32).pipe(Layer.provideMerge(layer$59), Layer.provideMerge(layer$60))))), Layer.provideMerge(layer$68)).pipe(Layer.provideMerge(layer$8), Layer.provideMerge(layer$7), Layer.provideMerge(layer$6), Layer.provideMerge(layer$49), Layer.provideMerge(layer$51), Layer.provideMerge(layer$50), Layer.provide(layer$72));
88838
90008
  /**
88839
90009
  * Hub asset sync.
88840
90010
  *
@@ -88842,9 +90012,9 @@ const RuntimeDependenciesLive = ReactorLayerLive.pipe(Layer.provideMerge(Checkpo
88842
90012
  * not-configured shape, and every call reports that rather than failing — so
88843
90013
  * this costs a server with no hub nothing but a layer.
88844
90014
  */
88845
- const AssetSyncLive = layer$54.pipe(Layer.provideMerge(layer$55), Layer.provideMerge(layer$56));
88846
- const RuntimeServicesLive = layer$44.pipe(Layer.provideMerge(AssetSyncLive), Layer.provideMerge(RuntimeDependenciesLive));
88847
- const makeRoutesLayer = Layer.mergeAll(Layer.mergeAll(HttpApiBuilder.layer(EnvironmentHttpApi).pipe(Layer.provide(authHttpApiLayer), Layer.provide(orchestrationHttpApiLayer), Layer.provide(serverEnvironmentHttpApiLayer), Layer.provide(environmentAuthenticatedAuthLayer)), otlpTracesProxyRouteLayer, assetRouteLayer, mcpOAuthCallbackRouteLayer, staticAndDevRouteLayer, websocketRpcRouteLayer), layer.pipe(Layer.provide(layer$2))).pipe(Layer.provide(RoutedTaskRepositoryLive.pipe(Layer.provide(PersistenceLayerLive))), Layer.provide(ProjectionProjectRepositoryLive.pipe(Layer.provide(PersistenceLayerLive))), Layer.provide(layer$42), Layer.provide(layer$28), Layer.provide(layer$46), Layer.provide(browserApiCorsLayer), Layer.provide(httpCompressionLayer));
90015
+ const AssetSyncLive = layer$55.pipe(Layer.provideMerge(layer$56), Layer.provideMerge(layer$57));
90016
+ const RuntimeServicesLive = layer$45.pipe(Layer.provideMerge(AssetSyncLive), Layer.provideMerge(RuntimeDependenciesLive));
90017
+ const makeRoutesLayer = Layer.mergeAll(Layer.mergeAll(HttpApiBuilder.layer(EnvironmentHttpApi).pipe(Layer.provide(authHttpApiLayer), Layer.provide(orchestrationHttpApiLayer), Layer.provide(serverEnvironmentHttpApiLayer), Layer.provide(environmentAuthenticatedAuthLayer)), otlpTracesProxyRouteLayer, assetRouteLayer, mcpOAuthCallbackRouteLayer, staticAndDevRouteLayer, websocketRpcRouteLayer), layer.pipe(Layer.provide(layer$2))).pipe(Layer.provide(RoutedTaskRepositoryLive.pipe(Layer.provide(PersistenceLayerLive))), Layer.provide(ProjectionProjectRepositoryLive.pipe(Layer.provide(PersistenceLayerLive))), Layer.provide(layer$43), Layer.provide(layer$28), Layer.provide(layer$47), Layer.provide(browserApiCorsLayer), Layer.provide(httpCompressionLayer));
88848
90018
  const makeServerLayer = Layer.unwrap(Effect.gen(function* () {
88849
90019
  const config = yield* ServerConfig$1;
88850
90020
  yield* fixPath();
@@ -88887,7 +90057,7 @@ const makeServerLayer = Layer.unwrap(Effect.gen(function* () {
88887
90057
  cause,
88888
90058
  servePort: configured.servePort
88889
90059
  }))) : Effect.void)) : Layer.empty;
88890
- return Layer.mergeAll(HttpRouter.serve(makeRoutesLayer, { disableLogger: !config.logWebSocketEvents }), httpListeningLayer, runtimeStateLayer, tailscaleServeLayer).pipe(Layer.provideMerge(RuntimeServicesLive), Layer.provideMerge(HttpResponseCompressionLive), Layer.provideMerge(HttpServerLive), Layer.provide(ObservabilityLive), Layer.provideMerge(FetchHttpClient.layer), Layer.provideMerge(layer$39), Layer.provideMerge(PlatformServicesLive));
90060
+ return Layer.mergeAll(HttpRouter.serve(makeRoutesLayer, { disableLogger: !config.logWebSocketEvents }), httpListeningLayer, runtimeStateLayer, tailscaleServeLayer).pipe(Layer.provideMerge(RuntimeServicesLive), Layer.provideMerge(HttpResponseCompressionLive), Layer.provideMerge(HttpServerLive), Layer.provide(ObservabilityLive), Layer.provideMerge(FetchHttpClient.layer), Layer.provideMerge(layer$40), Layer.provideMerge(PlatformServicesLive));
88891
90061
  }));
88892
90062
  const runServer = Layer.launch(makeServerLayer);
88893
90063
  //#endregion
@@ -89049,11 +90219,11 @@ const reconcileWithNewBinary = Effect.fn("cli.selfUpdate.reconcileWithNewBinary"
89049
90219
  //#endregion
89050
90220
  //#region src/cli/service.ts
89051
90221
  const isBootServiceCommandError = Schema$1.is(BootServiceCommandError);
89052
- const bootServiceLayer = (config) => layer$47({
90222
+ const bootServiceLayer = (config) => layer$48({
89053
90223
  baseDir: config.baseDir,
89054
90224
  logsDir: config.logsDir,
89055
90225
  cliVersion: version
89056
- }).pipe(Layer.provide(layer$53));
90226
+ }).pipe(Layer.provide(layer$54));
89057
90227
  /** Install, update, or repair the service using the CLI version running this command. */
89058
90228
  const reconcileService = Effect.fn("cli.service.reconcile")(function* () {
89059
90229
  const service = yield* BootService;
@@ -89100,7 +90270,7 @@ const runServiceCommand = Effect.fn("cli.service.run")(function* (flags, run, pr
89100
90270
  const baseDirNotice = yield* describeNonDefaultBaseDir(config.baseDir);
89101
90271
  if (baseDirNotice !== null) yield* Console.error(baseDirNotice);
89102
90272
  if (prepare !== void 0) yield* prepare(config);
89103
- return yield* run.pipe(Effect.tapError((error) => isBootServiceCommandError(error) ? BootService.pipe(Effect.flatMap((service) => Console.error(`Details: ${service.logPath}`))) : Effect.void), Effect.provide(Layer.mergeAll(bootServiceLayer(config), layer$53)));
90273
+ return yield* run.pipe(Effect.tapError((error) => isBootServiceCommandError(error) ? BootService.pipe(Effect.flatMap((service) => Console.error(`Details: ${service.logPath}`))) : Effect.void), Effect.provide(Layer.mergeAll(bootServiceLayer(config), layer$54)));
89104
90274
  });
89105
90275
  /**
89106
90276
  * Options the installed unit carries, rather than ones this command consumes.
@@ -89254,7 +90424,7 @@ const serviceCommand = Command.make("service").pipe(Command.withDescription("Man
89254
90424
  ]));
89255
90425
  //#endregion
89256
90426
  //#region src/bin.ts
89257
- const CliRuntimeLayer = Layer.mergeAll(NodeServices.layer, layer$71, FetchHttpClient.layer);
90427
+ const CliRuntimeLayer = Layer.mergeAll(NodeServices.layer, layer$72, FetchHttpClient.layer);
89258
90428
  /**
89259
90429
  * Prints an update reminder before dispatching, at most once a day.
89260
90430
  *