@p4code/cli 0.3.24 → 0.3.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.mjs CHANGED
@@ -127,7 +127,7 @@ const closeServer = (server) => {
127
127
  * NetService - Service tag for startup networking helpers.
128
128
  */
129
129
  var NetService = class extends Context.Service()("@p4code/shared/Net/NetService") {};
130
- const make$91 = () => {
130
+ const make$92 = () => {
131
131
  /**
132
132
  * Returns true when a TCP server can bind to {host, port}.
133
133
  * `EADDRNOTAVAIL` is treated as available so IPv6-absent hosts don't fail
@@ -236,10 +236,10 @@ const make$91 = () => {
236
236
  })
237
237
  };
238
238
  };
239
- const layer$82 = Layer.sync(NetService, make$91);
239
+ const layer$82 = Layer.sync(NetService, make$92);
240
240
  //#endregion
241
241
  //#region package.json
242
- var version = "0.3.24";
242
+ var version = "0.3.25";
243
243
  //#endregion
244
244
  //#region src/config.ts
245
245
  /**
@@ -260,8 +260,8 @@ var ServerConfig$1 = class extends Context.Service()("@p4code/cli/config/ServerC
260
260
  /** @deprecated Import and use `layerTest` from this module. */
261
261
  static layerTest = (cwd, baseDirOrPrefix) => layerTest$3(cwd, baseDirOrPrefix);
262
262
  };
263
- const make$90 = (config) => ServerConfig$1.of(config);
264
- const layer$81 = (config) => Layer.succeed(ServerConfig$1, make$90(config));
263
+ const make$91 = (config) => ServerConfig$1.of(config);
264
+ const layer$81 = (config) => Layer.succeed(ServerConfig$1, make$91(config));
265
265
  const deriveServerPaths = Effect.fn(function* (baseDir, devUrl, options = {}) {
266
266
  const { join } = yield* Path.Path;
267
267
  const stateDir = join(baseDir, devUrl !== void 0 && !options.baseDirIsExplicit ? "dev" : "userdata");
@@ -1333,6 +1333,12 @@ const ExecutionEnvironmentCapabilities = Schema$1.Struct({
1333
1333
  /** Server understands thread.pin / thread.unpin / thread.pin.reorder
1334
1334
  commands. Same version-skew contract as threadSettlement. */
1335
1335
  threadPinning: Schema$1.optionalKey(Schema$1.Boolean),
1336
+ /** Server understands the thread.fork command. Same version-skew contract
1337
+ as threadSettlement. */
1338
+ threadFork: Schema$1.optionalKey(Schema$1.Boolean),
1339
+ /** Server understands thread.scheduled-task.create / cancel and fires
1340
+ pending tasks. Same version-skew contract as threadSettlement. */
1341
+ threadScheduledTasks: Schema$1.optionalKey(Schema$1.Boolean),
1336
1342
  /** Server exposes the pull-request list, detail, activity, diff, and mutation APIs. Absent on
1337
1343
  servers from before the pull-request workspace shipped, so clients must not probe them. */
1338
1344
  pullRequests: Schema$1.optionalKey(Schema$1.Boolean),
@@ -1595,7 +1601,7 @@ const PREFERRED_DEFAULT_CODEX_MODELS = ["gpt-5.6-sol", "gpt-5.6-terra"];
1595
1601
  const DEFAULT_TEXT_GENERATION_MODEL = "gpt-5.6-luna";
1596
1602
  const DEFAULT_MODEL_BY_PROVIDER = {
1597
1603
  [CODEX_DRIVER_KIND]: DEFAULT_MODEL,
1598
- [CLAUDE_DRIVER_KIND]: "claude-sonnet-5",
1604
+ [CLAUDE_DRIVER_KIND]: "claude-fable-5-1",
1599
1605
  [CURSOR_DRIVER_KIND]: "auto",
1600
1606
  [GROK_DRIVER_KIND$1]: "grok-build",
1601
1607
  [MUSE_DRIVER_KIND]: "muse-spark-1.2",
@@ -1877,6 +1883,31 @@ const OrchestrationProposedPlan = Schema$1.Struct({
1877
1883
  createdAt: IsoDateTime,
1878
1884
  updatedAt: IsoDateTime
1879
1885
  });
1886
+ const OrchestrationScheduledTaskId = TrimmedNonEmptyString;
1887
+ const OrchestrationScheduledTaskStatus = Schema$1.Literals([
1888
+ "pending",
1889
+ "fired",
1890
+ "failed",
1891
+ "cancelled"
1892
+ ]);
1893
+ /**
1894
+ * A prompt the server sends to a thread at a chosen time. Pending tasks are
1895
+ * the live ones: firing starts a turn with `prompt`, cancelling is the way
1896
+ * out before that. A task whose turn could not start after bounded retries
1897
+ * ends `failed` with `failure` explaining why; it can be dismissed or
1898
+ * scheduled again from the client.
1899
+ */
1900
+ const OrchestrationScheduledTask = Schema$1.Struct({
1901
+ id: OrchestrationScheduledTaskId,
1902
+ prompt: TrimmedNonEmptyString,
1903
+ runAt: IsoDateTime,
1904
+ status: OrchestrationScheduledTaskStatus,
1905
+ createdAt: IsoDateTime,
1906
+ updatedAt: IsoDateTime,
1907
+ firedAt: Schema$1.NullOr(IsoDateTime).pipe(Schema$1.withDecodingDefault(Effect.succeed(null))),
1908
+ cancelledAt: Schema$1.NullOr(IsoDateTime).pipe(Schema$1.withDecodingDefault(Effect.succeed(null))),
1909
+ failure: Schema$1.NullOr(Schema$1.String).pipe(Schema$1.withDecodingDefault(Effect.succeed(null)))
1910
+ });
1880
1911
  const SourceProposedPlanReference = Schema$1.Struct({
1881
1912
  threadId: ThreadId,
1882
1913
  planId: OrchestrationProposedPlanId
@@ -1996,6 +2027,7 @@ const OrchestrationThread = Schema$1.Struct({
1996
2027
  deletedAt: Schema$1.NullOr(IsoDateTime),
1997
2028
  messages: Schema$1.Array(OrchestrationMessage),
1998
2029
  proposedPlans: Schema$1.Array(OrchestrationProposedPlan).pipe(Schema$1.withDecodingDefault(Effect.succeed([]))),
2030
+ scheduledTasks: Schema$1.optional(Schema$1.Array(OrchestrationScheduledTask)),
1999
2031
  activities: Schema$1.Array(OrchestrationThreadActivity),
2000
2032
  checkpoints: Schema$1.Array(OrchestrationCheckpointSummary),
2001
2033
  session: Schema$1.NullOr(OrchestrationSession)
@@ -2266,6 +2298,44 @@ const ThreadCreateCommand = Schema$1.Struct({
2266
2298
  worktreePath: Schema$1.NullOr(TrimmedNonEmptyString),
2267
2299
  createdAt: IsoDateTime
2268
2300
  });
2301
+ /**
2302
+ * Creates a new thread from an existing one. The decider copies the source
2303
+ * thread's configuration and the projections copy its conversation, so the
2304
+ * fork opens with the same context while the source stays untouched.
2305
+ */
2306
+ const ThreadForkCommand = Schema$1.Struct({
2307
+ type: Schema$1.Literal("thread.fork"),
2308
+ commandId: CommandId,
2309
+ sourceThreadId: ThreadId,
2310
+ threadId: ThreadId,
2311
+ title: Schema$1.optional(TrimmedNonEmptyString),
2312
+ createdAt: IsoDateTime
2313
+ });
2314
+ const ThreadScheduledTaskCreateCommand = Schema$1.Struct({
2315
+ type: Schema$1.Literal("thread.scheduled-task.create"),
2316
+ commandId: CommandId,
2317
+ threadId: ThreadId,
2318
+ taskId: OrchestrationScheduledTaskId,
2319
+ prompt: TrimmedNonEmptyString,
2320
+ runAt: IsoDateTime,
2321
+ createdAt: IsoDateTime
2322
+ });
2323
+ const ThreadScheduledTaskCancelCommand = Schema$1.Struct({
2324
+ type: Schema$1.Literal("thread.scheduled-task.cancel"),
2325
+ commandId: CommandId,
2326
+ threadId: ThreadId,
2327
+ taskId: OrchestrationScheduledTaskId,
2328
+ cancelledAt: IsoDateTime
2329
+ });
2330
+ /** Server-only: records that the scheduler fired (or failed to fire) a task. */
2331
+ const ThreadScheduledTaskFireCommand = Schema$1.Struct({
2332
+ type: Schema$1.Literal("thread.scheduled-task.fire"),
2333
+ commandId: CommandId,
2334
+ threadId: ThreadId,
2335
+ taskId: OrchestrationScheduledTaskId,
2336
+ firedAt: IsoDateTime,
2337
+ failure: Schema$1.optional(Schema$1.String)
2338
+ });
2269
2339
  const ThreadDeleteCommand = Schema$1.Struct({
2270
2340
  type: Schema$1.Literal("thread.delete"),
2271
2341
  commandId: CommandId,
@@ -2513,6 +2583,9 @@ const DispatchableClientOrchestrationCommand = Schema$1.Union([
2513
2583
  ProjectMetaUpdateCommand,
2514
2584
  ProjectDeleteCommand,
2515
2585
  ThreadCreateCommand,
2586
+ ThreadForkCommand,
2587
+ ThreadScheduledTaskCreateCommand,
2588
+ ThreadScheduledTaskCancelCommand,
2516
2589
  ThreadDeleteCommand,
2517
2590
  ThreadArchiveCommand,
2518
2591
  ThreadUnarchiveCommand,
@@ -2544,6 +2617,9 @@ const ClientOrchestrationCommand = Schema$1.Union([
2544
2617
  ProjectMetaUpdateCommand,
2545
2618
  ProjectDeleteCommand,
2546
2619
  ThreadCreateCommand,
2620
+ ThreadForkCommand,
2621
+ ThreadScheduledTaskCreateCommand,
2622
+ ThreadScheduledTaskCancelCommand,
2547
2623
  ThreadDeleteCommand,
2548
2624
  ThreadArchiveCommand,
2549
2625
  ThreadUnarchiveCommand,
@@ -2698,7 +2774,8 @@ const InternalOrchestrationCommand = Schema$1.Union([
2698
2774
  ThreadProposedPlanUpsertCommand,
2699
2775
  ThreadTurnDiffCompleteCommand,
2700
2776
  ThreadActivityAppendCommand,
2701
- ThreadRevertCompleteCommand
2777
+ ThreadRevertCompleteCommand,
2778
+ ThreadScheduledTaskFireCommand
2702
2779
  ]);
2703
2780
  Schema$1.Union([DispatchableClientOrchestrationCommand, InternalOrchestrationCommand]);
2704
2781
  const OrchestrationEventType = Schema$1.Literals([
@@ -2732,6 +2809,9 @@ const OrchestrationEventType = Schema$1.Literals([
2732
2809
  "thread.session-force-stop-requested",
2733
2810
  "thread.session-set",
2734
2811
  "thread.proposed-plan-upserted",
2812
+ "thread.scheduled-task.created",
2813
+ "thread.scheduled-task.cancelled",
2814
+ "thread.scheduled-task.fired",
2735
2815
  "thread.turn-diff-completed",
2736
2816
  "thread.activity-appended",
2737
2817
  "thread.turn-completed",
@@ -2786,6 +2866,11 @@ const ThreadCreatedPayload$1 = Schema$1.Struct({
2786
2866
  unpromptedSubagents: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true))),
2787
2867
  branch: Schema$1.NullOr(TrimmedNonEmptyString),
2788
2868
  worktreePath: Schema$1.NullOr(TrimmedNonEmptyString),
2869
+ /**
2870
+ * Set when the thread was forked: projections copy the source thread's
2871
+ * messages into this one. Optional so events from pre-fork servers decode.
2872
+ */
2873
+ forkedFromThreadId: Schema$1.optional(ThreadId),
2789
2874
  createdAt: IsoDateTime,
2790
2875
  updatedAt: IsoDateTime
2791
2876
  });
@@ -2998,6 +3083,21 @@ const ThreadProposedPlanUpsertedPayload$1 = Schema$1.Struct({
2998
3083
  threadId: ThreadId,
2999
3084
  proposedPlan: OrchestrationProposedPlan
3000
3085
  });
3086
+ const ThreadScheduledTaskCreatedPayload$1 = Schema$1.Struct({
3087
+ threadId: ThreadId,
3088
+ task: OrchestrationScheduledTask
3089
+ });
3090
+ const ThreadScheduledTaskCancelledPayload$1 = Schema$1.Struct({
3091
+ threadId: ThreadId,
3092
+ taskId: OrchestrationScheduledTaskId,
3093
+ cancelledAt: IsoDateTime
3094
+ });
3095
+ const ThreadScheduledTaskFiredPayload$1 = Schema$1.Struct({
3096
+ threadId: ThreadId,
3097
+ taskId: OrchestrationScheduledTaskId,
3098
+ firedAt: IsoDateTime,
3099
+ failure: Schema$1.NullOr(Schema$1.String).pipe(Schema$1.withDecodingDefault(Effect.succeed(null)))
3100
+ });
3001
3101
  const ThreadTurnDiffCompletedPayload$1 = Schema$1.Struct({
3002
3102
  threadId: ThreadId,
3003
3103
  turnId: TurnId,
@@ -3185,6 +3285,21 @@ const OrchestrationEvent = Schema$1.Union([
3185
3285
  type: Schema$1.Literal("thread.proposed-plan-upserted"),
3186
3286
  payload: ThreadProposedPlanUpsertedPayload$1
3187
3287
  }),
3288
+ Schema$1.Struct({
3289
+ ...EventBaseFields,
3290
+ type: Schema$1.Literal("thread.scheduled-task.created"),
3291
+ payload: ThreadScheduledTaskCreatedPayload$1
3292
+ }),
3293
+ Schema$1.Struct({
3294
+ ...EventBaseFields,
3295
+ type: Schema$1.Literal("thread.scheduled-task.cancelled"),
3296
+ payload: ThreadScheduledTaskCancelledPayload$1
3297
+ }),
3298
+ Schema$1.Struct({
3299
+ ...EventBaseFields,
3300
+ type: Schema$1.Literal("thread.scheduled-task.fired"),
3301
+ payload: ThreadScheduledTaskFiredPayload$1
3302
+ }),
3188
3303
  Schema$1.Struct({
3189
3304
  ...EventBaseFields,
3190
3305
  type: Schema$1.Literal("thread.turn-diff-completed"),
@@ -5163,6 +5278,11 @@ const PREVIEW_AUTOMATION_OPERATIONS = [
5163
5278
  const PreviewAutomationOperation = Schema.Literals(PREVIEW_AUTOMATION_OPERATIONS);
5164
5279
  const PreviewAutomationTabTargetFields = { tabId: Schema.optional(PreviewTabId.annotate({ description: "Exact collaborative browser tab to target. Omit to use this agent session's current tab." })).annotate({ description: "Exact collaborative browser tab to target. Omit to use this agent session's current tab." }) };
5165
5280
  const PreviewAutomationTabTargetInput = Schema.Struct(PreviewAutomationTabTargetFields);
5281
+ const INCLUDE_SCREENSHOT_DESCRIPTION = "Attach a PNG screenshot to the result. Omit it for the text-only page state, which already carries locators, visible text, and diagnostics; every attached image stays in the agent's context for the rest of the session.";
5282
+ const PreviewAutomationSnapshotInput = Schema.Struct({
5283
+ ...PreviewAutomationTabTargetFields,
5284
+ includeScreenshot: Schema.optional(Schema.Boolean.annotate({ description: INCLUDE_SCREENSHOT_DESCRIPTION })).annotate({ description: INCLUDE_SCREENSHOT_DESCRIPTION })
5285
+ });
5166
5286
  const PreviewAutomationStatus = Schema.Struct({
5167
5287
  available: Schema.Boolean,
5168
5288
  visible: Schema.Boolean,
@@ -5834,6 +5954,7 @@ const ContextMenuItemSchema = Schema$1.Struct({
5834
5954
  header: Schema$1.optionalKey(Schema$1.Boolean),
5835
5955
  separator: Schema$1.optionalKey(Schema$1.Boolean),
5836
5956
  icon: Schema$1.optionalKey(Schema$1.String),
5957
+ shortcut: Schema$1.optionalKey(Schema$1.String),
5837
5958
  children: Schema$1.optionalKey(Schema$1.Array(Schema$1.suspend(() => ContextMenuItemSchema)))
5838
5959
  });
5839
5960
  const DesktopUpdateStatusSchema = Schema$1.Literals([
@@ -8156,7 +8277,9 @@ const ClientSettingsSchema = Schema$1.Struct({
8156
8277
  })).pipe(Schema$1.withDecodingDefault(Effect.succeed([]))),
8157
8278
  providerModelPreferences: Schema$1.Record(ProviderInstanceId, Schema$1.Struct({
8158
8279
  hiddenModels: Schema$1.Array(Schema$1.String).pipe(Schema$1.withDecodingDefault(Effect.succeed([]))),
8159
- modelOrder: Schema$1.Array(Schema$1.String).pipe(Schema$1.withDecodingDefault(Effect.succeed([])))
8280
+ modelOrder: Schema$1.Array(Schema$1.String).pipe(Schema$1.withDecodingDefault(Effect.succeed([]))),
8281
+ /** Option values a model starts with in this instance until the user picks others. */
8282
+ modelOptionDefaults: Schema$1.Record(TrimmedNonEmptyString, ProviderOptionSelections).pipe(Schema$1.withDecodingDefault(Effect.succeed({})))
8160
8283
  })).pipe(Schema$1.withDecodingDefault(Effect.succeed({}))),
8161
8284
  showBuildModeToggle: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(false))),
8162
8285
  sidebarAutoSettleAfterDays: Schema$1.NullOr(SidebarAutoSettleAfterDays).pipe(Schema$1.withDecodingDefault(Effect.succeed(3))),
@@ -8614,7 +8737,8 @@ Schema$1.Struct({
8614
8737
  }))),
8615
8738
  providerModelPreferences: Schema$1.optionalKey(Schema$1.Record(ProviderInstanceId, Schema$1.Struct({
8616
8739
  hiddenModels: Schema$1.Array(Schema$1.String).pipe(Schema$1.withDecodingDefault(Effect.succeed([]))),
8617
- modelOrder: Schema$1.Array(Schema$1.String).pipe(Schema$1.withDecodingDefault(Effect.succeed([])))
8740
+ modelOrder: Schema$1.Array(Schema$1.String).pipe(Schema$1.withDecodingDefault(Effect.succeed([]))),
8741
+ modelOptionDefaults: Schema$1.Record(TrimmedNonEmptyString, ProviderOptionSelections).pipe(Schema$1.withDecodingDefault(Effect.succeed({})))
8618
8742
  }))),
8619
8743
  showBuildModeToggle: Schema$1.optionalKey(Schema$1.Boolean),
8620
8744
  sidebarAutoSettleAfterDays: Schema$1.optionalKey(Schema$1.NullOr(SidebarAutoSettleAfterDays)),
@@ -12493,7 +12617,7 @@ function deriveAuthClientMetadata(input) {
12493
12617
  //#endregion
12494
12618
  //#region src/auth/EnvironmentAuthPolicy.ts
12495
12619
  var EnvironmentAuthPolicy = class extends Context.Service()("@p4code/cli/auth/EnvironmentAuthPolicy") {};
12496
- const make$89 = Effect.gen(function* () {
12620
+ const make$90 = Effect.gen(function* () {
12497
12621
  const config = yield* ServerConfig$1;
12498
12622
  const isRemoteReachable = isRemoteReachableHost(config.host);
12499
12623
  const policy = config.mode === "desktop" ? isRemoteReachable ? "remote-reachable" : "desktop-managed-local" : isRemoteReachable ? "remote-reachable" : "loopback-browser";
@@ -12511,7 +12635,7 @@ const make$89 = Effect.gen(function* () {
12511
12635
  };
12512
12636
  return EnvironmentAuthPolicy.of({ getDescriptor: () => Effect.succeed(descriptor).pipe(Effect.withSpan("EnvironmentAuthPolicy.getDescriptor")) });
12513
12637
  });
12514
- const layer$80 = Layer.effect(EnvironmentAuthPolicy, make$89);
12638
+ const layer$80 = Layer.effect(EnvironmentAuthPolicy, make$90);
12515
12639
  //#endregion
12516
12640
  //#region src/persistence/Errors.ts
12517
12641
  function summarizeSchemaIssue(issue) {
@@ -12692,7 +12816,7 @@ function toPersistenceSqlOrDecodeError$6(sqlOperation, decodeOperation, correlat
12692
12816
  cause
12693
12817
  });
12694
12818
  }
12695
- const make$88 = Effect.gen(function* () {
12819
+ const make$89 = Effect.gen(function* () {
12696
12820
  const sql = yield* SqlClient.SqlClient;
12697
12821
  const createSessionRow = SqlSchema.void({
12698
12822
  Request: CreateAuthSessionInput,
@@ -12826,7 +12950,7 @@ const make$88 = Effect.gen(function* () {
12826
12950
  setLastConnectedAt
12827
12951
  };
12828
12952
  });
12829
- const layer$79 = Layer.effect(AuthSessionRepository, make$88);
12953
+ const layer$79 = Layer.effect(AuthSessionRepository, make$89);
12830
12954
  //#endregion
12831
12955
  //#region src/auth/ServerSecretStore.ts
12832
12956
  const secretStoreErrorContext = {
@@ -12893,7 +13017,7 @@ const isSecretStoreError = Schema$1.is(SecretStoreError);
12893
13017
  const isPlatformError = (value) => Predicate.isTagged(value, "PlatformError");
12894
13018
  const isSecretAlreadyExistsError = (error) => "cause" in error && isPlatformError(error.cause) && error.cause.reason._tag === "AlreadyExists";
12895
13019
  var ServerSecretStore = class extends Context.Service()("@p4code/cli/auth/ServerSecretStore") {};
12896
- const make$87 = Effect.gen(function* () {
13020
+ const make$88 = Effect.gen(function* () {
12897
13021
  const crypto = yield* Crypto.Crypto;
12898
13022
  const fileSystem = yield* FileSystem.FileSystem;
12899
13023
  const path = yield* Path.Path;
@@ -12969,7 +13093,7 @@ const make$87 = Effect.gen(function* () {
12969
13093
  remove
12970
13094
  });
12971
13095
  });
12972
- const layer$78 = Layer.effect(ServerSecretStore, make$87);
13096
+ const layer$78 = Layer.effect(ServerSecretStore, make$88);
12973
13097
  //#endregion
12974
13098
  //#region src/auth/SessionStore.ts
12975
13099
  var MalformedSessionTokenError = class extends Schema$1.TaggedErrorClass()("MalformedSessionTokenError", {}) {
@@ -13207,7 +13331,7 @@ function toAuthClientSession(input) {
13207
13331
  current: false
13208
13332
  };
13209
13333
  }
13210
- const make$86 = Effect.gen(function* () {
13334
+ const make$87 = Effect.gen(function* () {
13211
13335
  const crypto = yield* Crypto.Crypto;
13212
13336
  const serverConfig = yield* ServerConfig$1;
13213
13337
  const secretStore = yield* ServerSecretStore;
@@ -13521,7 +13645,7 @@ const make$86 = Effect.gen(function* () {
13521
13645
  markDisconnected
13522
13646
  });
13523
13647
  });
13524
- const layer$77 = Layer.effect(SessionStore, make$86).pipe(Layer.provideMerge(layer$79));
13648
+ const layer$77 = Layer.effect(SessionStore, make$87).pipe(Layer.provideMerge(layer$79));
13525
13649
  //#endregion
13526
13650
  //#region src/persistence/AuthPairingLinks.ts
13527
13651
  const AuthPairingLinkRecord = Schema$1.Struct({
@@ -13582,7 +13706,7 @@ function toPersistenceSqlOrDecodeError$5(sqlOperation, decodeOperation, correlat
13582
13706
  cause
13583
13707
  });
13584
13708
  }
13585
- const make$85 = Effect.gen(function* () {
13709
+ const make$86 = Effect.gen(function* () {
13586
13710
  const sql = yield* SqlClient.SqlClient;
13587
13711
  const createPairingLinkRow = SqlSchema.void({
13588
13712
  Request: CreateAuthPairingLinkInput,
@@ -13717,7 +13841,7 @@ const make$85 = Effect.gen(function* () {
13717
13841
  getByCredential
13718
13842
  };
13719
13843
  });
13720
- const layer$76 = Layer.effect(AuthPairingLinkRepository, make$85);
13844
+ const layer$76 = Layer.effect(AuthPairingLinkRepository, make$86);
13721
13845
  //#endregion
13722
13846
  //#region src/auth/PairingGrantStore.ts
13723
13847
  var UnknownBootstrapCredentialError = class extends Schema$1.TaggedErrorClass()("UnknownBootstrapCredentialError", {}) {
@@ -13812,7 +13936,7 @@ const DEV_STARTUP_TTL_HOURS = Duration.hours(24);
13812
13936
  const PAIRING_TOKEN_ALPHABET = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
13813
13937
  const PAIRING_TOKEN_LENGTH = 12;
13814
13938
  const PAIRING_TOKEN_REJECTION_LIMIT = Math.floor(256 / 32) * 32;
13815
- const make$84 = Effect.gen(function* () {
13939
+ const make$85 = Effect.gen(function* () {
13816
13940
  const crypto = yield* Crypto.Crypto;
13817
13941
  const config = yield* ServerConfig$1;
13818
13942
  const pairingLinks = yield* AuthPairingLinkRepository;
@@ -14010,7 +14134,7 @@ const make$84 = Effect.gen(function* () {
14010
14134
  consume
14011
14135
  });
14012
14136
  });
14013
- const layer$75 = Layer.effect(PairingGrantStore, make$84).pipe(Layer.provideMerge(layer$76));
14137
+ const layer$75 = Layer.effect(PairingGrantStore, make$85).pipe(Layer.provideMerge(layer$76));
14014
14138
  //#endregion
14015
14139
  //#region src/persistence/DatabaseSnapshot.ts
14016
14140
  /**
@@ -15929,6 +16053,33 @@ var _053_ProjectionTurnsKeysetIndex_default = Effect.gen(function* () {
15929
16053
  `;
15930
16054
  });
15931
16055
  //#endregion
16056
+ //#region src/persistence/Migrations/054_ProjectionThreadScheduledTasks.ts
16057
+ /**
16058
+ * User-created scheduled tasks: a prompt the server sends to a thread at a
16059
+ * chosen time. Pending rows are what the scheduler re-arms after a restart.
16060
+ */
16061
+ var _054_ProjectionThreadScheduledTasks_default = Effect.gen(function* () {
16062
+ const sql = yield* SqlClient.SqlClient;
16063
+ yield* sql`
16064
+ CREATE TABLE IF NOT EXISTS projection_thread_scheduled_tasks (
16065
+ task_id TEXT PRIMARY KEY,
16066
+ thread_id TEXT NOT NULL,
16067
+ prompt TEXT NOT NULL,
16068
+ run_at TEXT NOT NULL,
16069
+ status TEXT NOT NULL,
16070
+ created_at TEXT NOT NULL,
16071
+ updated_at TEXT NOT NULL,
16072
+ fired_at TEXT,
16073
+ cancelled_at TEXT,
16074
+ failure TEXT
16075
+ )
16076
+ `;
16077
+ yield* sql`
16078
+ CREATE INDEX IF NOT EXISTS idx_projection_thread_scheduled_tasks_thread_run
16079
+ ON projection_thread_scheduled_tasks(thread_id, run_at)
16080
+ `;
16081
+ });
16082
+ //#endregion
15932
16083
  //#region src/persistence/Migrations.ts
15933
16084
  /**
15934
16085
  * MigrationsLive - Migration runner with inline loader
@@ -16214,6 +16365,11 @@ const migrationEntries = [
16214
16365
  53,
16215
16366
  "ProjectionTurnsKeysetIndex",
16216
16367
  _053_ProjectionTurnsKeysetIndex_default
16368
+ ],
16369
+ [
16370
+ 54,
16371
+ "ProjectionThreadScheduledTasks",
16372
+ _054_ProjectionThreadScheduledTasks_default
16217
16373
  ]
16218
16374
  ];
16219
16375
  const makeMigrationLoader = (throughId) => Migrator.fromRecord(Object.fromEntries(migrationEntries.filter(([id]) => throughId === void 0 || id <= throughId).map(([id, name, migration]) => [`${id}_${name}`, migration])));
@@ -17204,7 +17360,7 @@ function parseBearerToken(request) {
17204
17360
  const token = header.slice(7).trim();
17205
17361
  return token.length > 0 ? token : null;
17206
17362
  }
17207
- const make$83 = Effect.gen(function* () {
17363
+ const make$84 = Effect.gen(function* () {
17208
17364
  const policy = yield* EnvironmentAuthPolicy;
17209
17365
  const bootstrapCredentials = yield* PairingGrantStore;
17210
17366
  const sessions = yield* SessionStore;
@@ -17399,7 +17555,7 @@ const make$83 = Effect.gen(function* () {
17399
17555
  issueStartupPairingUrl
17400
17556
  });
17401
17557
  });
17402
- const layer$74 = Layer.effect(EnvironmentAuth, make$83).pipe(Layer.provideMerge(layer$75), Layer.provideMerge(layer$77), Layer.provideMerge(layer$80));
17558
+ const layer$74 = Layer.effect(EnvironmentAuth, make$84).pipe(Layer.provideMerge(layer$75), Layer.provideMerge(layer$77), Layer.provideMerge(layer$80));
17403
17559
  const storageLayer = Layer.mergeAll(layer$78, layerConfig);
17404
17560
  const runtimeLayer = layer$74.pipe(Layer.provideMerge(storageLayer));
17405
17561
  //#endregion
@@ -19237,7 +19393,7 @@ const DEFAULT_LIMITS = {
19237
19393
  windowMillis: FAILURE_WINDOW_MS,
19238
19394
  blockMillis: BLOCK_DURATION_MS
19239
19395
  };
19240
- const make$82 = Effect.fn("HubAuthThrottle.make")(function* (limits = DEFAULT_LIMITS) {
19396
+ const make$83 = Effect.fn("HubAuthThrottle.make")(function* (limits = DEFAULT_LIMITS) {
19241
19397
  const state = yield* Ref.make(initialThrottleState);
19242
19398
  return HubAuthThrottle.of({
19243
19399
  shouldRefuse: Effect.gen(function* () {
@@ -19251,7 +19407,7 @@ const make$82 = Effect.fn("HubAuthThrottle.make")(function* (limits = DEFAULT_LI
19251
19407
  })
19252
19408
  });
19253
19409
  });
19254
- const layer$73 = Layer.effect(HubAuthThrottle, make$82());
19410
+ const layer$73 = Layer.effect(HubAuthThrottle, make$83());
19255
19411
  //#endregion
19256
19412
  //#region src/hub/HubAuth.ts
19257
19413
  /**
@@ -20971,7 +21127,7 @@ function stripDefaultServerSettings(current, defaults) {
20971
21127
  }
20972
21128
  return Object.is(current, defaults) ? void 0 : current;
20973
21129
  }
20974
- const make$81 = Effect.gen(function* () {
21130
+ const make$82 = Effect.gen(function* () {
20975
21131
  const { settingsPath } = yield* ServerConfig$1;
20976
21132
  const fs = yield* FileSystem.FileSystem;
20977
21133
  const pathService = yield* Path.Path;
@@ -21192,7 +21348,7 @@ const make$81 = Effect.gen(function* () {
21192
21348
  }
21193
21349
  };
21194
21350
  });
21195
- const layer$71 = Layer.effect(ServerSettingsService, make$81);
21351
+ const layer$71 = Layer.effect(ServerSettingsService, make$82);
21196
21352
  //#endregion
21197
21353
  //#region src/pathExpansion.ts
21198
21354
  /**
@@ -21569,7 +21725,7 @@ function claudeEntryFromRegistration(registration) {
21569
21725
  };
21570
21726
  }
21571
21727
  var ClaudeMcpFiles = class extends Context.Service()("@p4code/cli/mcp/ClaudeMcpFiles") {};
21572
- const make$80 = Effect.gen(function* () {
21728
+ const make$81 = Effect.gen(function* () {
21573
21729
  const fileSystem = yield* FileSystem.FileSystem;
21574
21730
  const path = yield* Path.Path;
21575
21731
  const services = yield* Effect.context();
@@ -21636,7 +21792,7 @@ const make$80 = Effect.gen(function* () {
21636
21792
  removeProject: (projectDir, name) => removeAt(Effect.succeed(projectFile(projectDir)))(name)
21637
21793
  };
21638
21794
  });
21639
- const layer$70 = Layer.effect(ClaudeMcpFiles, make$80);
21795
+ const layer$70 = Layer.effect(ClaudeMcpFiles, make$81);
21640
21796
  Layer.succeed(ClaudeMcpFiles, {
21641
21797
  readUser: Effect.succeed([]),
21642
21798
  readUserAt: () => Effect.succeed([]),
@@ -21774,7 +21930,7 @@ const decodeClientRegistration = Schema$1.decodeUnknownExit(ClientRegistrationRe
21774
21930
  const decodeTokenResponse = Schema$1.decodeUnknownExit(TokenResponse);
21775
21931
  var McpOAuth = class extends Context.Service()("@p4code/cli/mcp/McpOAuth") {};
21776
21932
  const registryError = (detail) => new McpRegistryError({ detail });
21777
- const make$79 = Effect.gen(function* () {
21933
+ const make$80 = Effect.gen(function* () {
21778
21934
  const config = yield* ServerConfig$1;
21779
21935
  const secrets = yield* ServerSecretStore;
21780
21936
  const http = yield* HttpClient.HttpClient;
@@ -22094,7 +22250,7 @@ const make$79 = Effect.gen(function* () {
22094
22250
  accessTokenFor
22095
22251
  };
22096
22252
  });
22097
- const layer$69 = Layer.effect(McpOAuth, make$79);
22253
+ const layer$69 = Layer.effect(McpOAuth, make$80);
22098
22254
  Layer.succeed(McpOAuth, {
22099
22255
  statusFor: () => Effect.succeed(Option.none()),
22100
22256
  begin: () => Effect.fail(new McpRegistryError({ detail: "OAuth sign-in is not available." })),
@@ -22112,7 +22268,7 @@ const decodeRegistration$1 = Schema$1.decodeUnknownExit(RegistrationFromJson$1);
22112
22268
  const encodeRegistration = Schema$1.encodeSync(RegistrationFromJson$1);
22113
22269
  var McpRegistry = class extends Context.Service()("@p4code/cli/mcp/McpRegistry") {};
22114
22270
  const slotsOf = (registration) => registration.secrets ?? [];
22115
- const make$78 = Effect.gen(function* () {
22271
+ const make$79 = Effect.gen(function* () {
22116
22272
  const config = yield* ServerConfig$1;
22117
22273
  const secrets = yield* ServerSecretStore;
22118
22274
  const oauth = yield* McpOAuth;
@@ -22286,7 +22442,7 @@ const make$78 = Effect.gen(function* () {
22286
22442
  resolveForSessionAtClaudeUserConfigPath
22287
22443
  };
22288
22444
  });
22289
- const layer$68 = Layer.effect(McpRegistry, make$78);
22445
+ const layer$68 = Layer.effect(McpRegistry, make$79);
22290
22446
  //#endregion
22291
22447
  //#region src/sync/skillDirectory.ts
22292
22448
  /**
@@ -22665,7 +22821,7 @@ const formatHubLink = (input) => encodeStoredHubLink({
22665
22821
  shareMode: input.shareMode
22666
22822
  });
22667
22823
  const fromEnvironment = (environment) => validateHubLink(environment.P4CODE_HUB_URL ?? "", environment.P4CODE_HUB_TOKEN ?? "");
22668
- const make$77 = Effect.fn("HubLink.make")(function* (environment) {
22824
+ const make$78 = Effect.fn("HubLink.make")(function* (environment) {
22669
22825
  const secrets = yield* ServerSecretStore;
22670
22826
  const env = environment ?? process.env;
22671
22827
  const fromEnv = fromEnvironment(env);
@@ -22731,7 +22887,7 @@ const make$77 = Effect.fn("HubLink.make")(function* (environment) {
22731
22887
  })
22732
22888
  };
22733
22889
  });
22734
- const layer$67 = Layer.effect(HubLink, make$77());
22890
+ const layer$67 = Layer.effect(HubLink, make$78());
22735
22891
  //#endregion
22736
22892
  //#region src/sync/HubAssetClient.ts
22737
22893
  /**
@@ -22764,7 +22920,7 @@ const decodeAssetListPage = Schema$1.decodeUnknownEffect(AssetListPage);
22764
22920
  const decodeConflictBody$1 = Schema$1.decodeUnknownEffect(ConflictBody$1);
22765
22921
  const decodeAsset = Schema$1.decodeUnknownEffect(AgentAsset);
22766
22922
  var HubAssetClient = class extends Context.Service()("@p4code/cli/sync/HubAssetClient") {};
22767
- const make$76 = Effect.gen(function* () {
22923
+ const make$77 = Effect.gen(function* () {
22768
22924
  const http = yield* HttpClient.HttpClient;
22769
22925
  const link = yield* HubLink;
22770
22926
  const requireSettings = Effect.gen(function* () {
@@ -22846,7 +23002,7 @@ const make$76 = Effect.gen(function* () {
22846
23002
  remove
22847
23003
  };
22848
23004
  });
22849
- const layer$66 = Layer.effect(HubAssetClient, make$76);
23005
+ const layer$66 = Layer.effect(HubAssetClient, make$77);
22850
23006
  //#endregion
22851
23007
  //#region src/sync/mcpRegistrationFiles.ts
22852
23008
  /**
@@ -23452,7 +23608,7 @@ const EMPTY_REPORT = {
23452
23608
  unavailable: null
23453
23609
  };
23454
23610
  var AssetSync = class extends Context.Service()("@p4code/cli/sync/AssetSync") {};
23455
- const make$75 = Effect.gen(function* () {
23611
+ const make$76 = Effect.gen(function* () {
23456
23612
  const client = yield* HubAssetClient;
23457
23613
  const link = yield* HubLink;
23458
23614
  const settingsStore = yield* ServerSettingsService;
@@ -24162,7 +24318,7 @@ const make$75 = Effect.gen(function* () {
24162
24318
  removeLocal
24163
24319
  };
24164
24320
  });
24165
- const layer$65 = Layer.effect(AssetSync, make$75);
24321
+ const layer$65 = Layer.effect(AssetSync, make$76);
24166
24322
  //#endregion
24167
24323
  //#region src/provider/CompressPrompts.ts
24168
24324
  /**
@@ -25610,6 +25766,306 @@ function requireThreadAbsent(input) {
25610
25766
  return Effect.fail(invariantError(input.command.type, `Thread '${input.threadId}' already exists and cannot be created twice.`));
25611
25767
  }
25612
25768
  //#endregion
25769
+ //#region src/attachmentPaths.ts
25770
+ function normalizeAttachmentRelativePath(rawRelativePath) {
25771
+ const normalized = NodePath.normalize(rawRelativePath).replace(/^[/\\]+/, "");
25772
+ if (normalized.length === 0 || normalized.startsWith("..") || normalized.includes("\0")) return null;
25773
+ return normalized.replace(/\\/g, "/");
25774
+ }
25775
+ function resolveAttachmentRelativePath(input) {
25776
+ const normalizedRelativePath = normalizeAttachmentRelativePath(input.relativePath);
25777
+ if (!normalizedRelativePath) return null;
25778
+ const attachmentsRoot = NodePath.resolve(input.attachmentsDir);
25779
+ const filePath = NodePath.resolve(NodePath.join(attachmentsRoot, normalizedRelativePath));
25780
+ if (!filePath.startsWith(`${attachmentsRoot}${NodePath.sep}`)) return null;
25781
+ return filePath;
25782
+ }
25783
+ //#endregion
25784
+ //#region src/imageMime.ts
25785
+ const IMAGE_EXTENSION_BY_MIME_TYPE = {
25786
+ "image/avif": ".avif",
25787
+ "image/bmp": ".bmp",
25788
+ "image/gif": ".gif",
25789
+ "image/heic": ".heic",
25790
+ "image/heif": ".heif",
25791
+ "image/jpeg": ".jpg",
25792
+ "image/jpg": ".jpg",
25793
+ "image/png": ".png",
25794
+ "image/svg+xml": ".svg",
25795
+ "image/tiff": ".tiff",
25796
+ "image/webp": ".webp"
25797
+ };
25798
+ const SAFE_IMAGE_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
25799
+ ".avif",
25800
+ ".bmp",
25801
+ ".gif",
25802
+ ".heic",
25803
+ ".heif",
25804
+ ".ico",
25805
+ ".jpeg",
25806
+ ".jpg",
25807
+ ".png",
25808
+ ".svg",
25809
+ ".tiff",
25810
+ ".webp"
25811
+ ]);
25812
+ function isBase64Char(code) {
25813
+ return code >= 97 && code <= 122 || code >= 65 && code <= 90 || code >= 48 && code <= 57 || code === 43 || code === 47 || code === 61;
25814
+ }
25815
+ function isBase64Whitespace(code) {
25816
+ return code === 13 || code === 10 || code === 32;
25817
+ }
25818
+ function parseBase64DataUrl(dataUrl) {
25819
+ const trimmed = dataUrl.trim();
25820
+ if (trimmed.slice(0, 5).toLowerCase() !== "data:") return null;
25821
+ const commaIndex = trimmed.indexOf(",");
25822
+ if (commaIndex === -1) return null;
25823
+ const header = trimmed.slice(5, commaIndex);
25824
+ if (header.length === 0) return null;
25825
+ const headerParts = [];
25826
+ for (const part of header.split(";")) {
25827
+ const partTrimmed = part.trim();
25828
+ if (partTrimmed.length > 0) headerParts.push(partTrimmed);
25829
+ }
25830
+ if (headerParts.length < 2) return null;
25831
+ if (headerParts.at(-1)?.toLowerCase() !== "base64") return null;
25832
+ const mimeType = headerParts[0]?.toLowerCase();
25833
+ if (!mimeType) return null;
25834
+ const payload = trimmed.slice(commaIndex + 1);
25835
+ const runs = [];
25836
+ let runStart = -1;
25837
+ for (let index = 0; index < payload.length; index += 1) {
25838
+ const code = payload.charCodeAt(index);
25839
+ if (isBase64Char(code)) {
25840
+ if (runStart === -1) runStart = index;
25841
+ continue;
25842
+ }
25843
+ if (!isBase64Whitespace(code)) return null;
25844
+ if (runStart !== -1) {
25845
+ runs.push(payload.slice(runStart, index));
25846
+ runStart = -1;
25847
+ }
25848
+ }
25849
+ if (runStart !== -1) runs.push(payload.slice(runStart));
25850
+ const base64 = runs.length === 1 ? runs[0] : runs.join("");
25851
+ if (base64.length === 0 || base64.length % 4 !== 0) return null;
25852
+ const firstPad = base64.indexOf("=");
25853
+ if (firstPad !== -1) {
25854
+ if (base64.length - firstPad > 2) return null;
25855
+ for (let index = firstPad; index < base64.length; index += 1) if (base64.charCodeAt(index) !== 61) return null;
25856
+ }
25857
+ return {
25858
+ mimeType,
25859
+ base64
25860
+ };
25861
+ }
25862
+ function inferImageExtension(input) {
25863
+ const key = input.mimeType.toLowerCase();
25864
+ const fromMime = Object.hasOwn(IMAGE_EXTENSION_BY_MIME_TYPE, key) ? IMAGE_EXTENSION_BY_MIME_TYPE[key] : void 0;
25865
+ if (fromMime) return fromMime;
25866
+ const fromMimeExtension = Mime.getExtension(input.mimeType);
25867
+ if (fromMimeExtension && SAFE_IMAGE_FILE_EXTENSIONS.has(fromMimeExtension)) return fromMimeExtension;
25868
+ const fileName = input.fileName?.trim() ?? "";
25869
+ const extensionMatch = /\.([a-z0-9]{1,8})$/i.exec(fileName);
25870
+ const fileNameExtension = extensionMatch ? `.${extensionMatch[1].toLowerCase()}` : "";
25871
+ if (SAFE_IMAGE_FILE_EXTENSIONS.has(fileNameExtension)) return fileNameExtension;
25872
+ return ".bin";
25873
+ }
25874
+ //#endregion
25875
+ //#region src/attachmentStore.ts
25876
+ const ATTACHMENT_FILENAME_EXTENSIONS = [
25877
+ ...SAFE_IMAGE_FILE_EXTENSIONS,
25878
+ ".pdf",
25879
+ ".bin"
25880
+ ];
25881
+ const ATTACHMENT_ID_THREAD_SEGMENT_MAX_CHARS = 80;
25882
+ const ATTACHMENT_ID_PATTERN = new RegExp(`^([a-z0-9_]+(?:-[a-z0-9_]+)*)-([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$`, "i");
25883
+ function toSafeThreadAttachmentSegment(threadId) {
25884
+ const segment = threadId.trim().toLowerCase().replace(/[^a-z0-9_-]+/gi, "-").replace(/-+/g, "-").replace(/^[-_]+|[-_]+$/g, "").slice(0, ATTACHMENT_ID_THREAD_SEGMENT_MAX_CHARS).replace(/[-_]+$/g, "");
25885
+ if (segment.length === 0) return null;
25886
+ return segment;
25887
+ }
25888
+ function createAttachmentId(threadId) {
25889
+ const threadSegment = toSafeThreadAttachmentSegment(threadId);
25890
+ if (!threadSegment) return null;
25891
+ return `${threadSegment}-${NodeCrypto.randomUUID()}`;
25892
+ }
25893
+ function parseThreadSegmentFromAttachmentId(attachmentId) {
25894
+ const normalizedId = normalizeAttachmentRelativePath(attachmentId);
25895
+ if (!normalizedId || normalizedId.includes("/") || normalizedId.includes(".")) return null;
25896
+ const match = normalizedId.match(ATTACHMENT_ID_PATTERN);
25897
+ if (!match) return null;
25898
+ return match[1]?.toLowerCase() ?? null;
25899
+ }
25900
+ function attachmentRelativePath(attachment) {
25901
+ switch (attachment.type) {
25902
+ case "image": {
25903
+ const extension = inferImageExtension({
25904
+ mimeType: attachment.mimeType,
25905
+ fileName: attachment.name
25906
+ });
25907
+ return `${attachment.id}${extension}`;
25908
+ }
25909
+ case "document": return `${attachment.id}.pdf`;
25910
+ }
25911
+ }
25912
+ function resolveAttachmentPath(input) {
25913
+ return resolveAttachmentRelativePath({
25914
+ attachmentsDir: input.attachmentsDir,
25915
+ relativePath: attachmentRelativePath(input.attachment)
25916
+ });
25917
+ }
25918
+ function resolveAttachmentPathById(input) {
25919
+ const normalizedId = normalizeAttachmentRelativePath(input.attachmentId);
25920
+ if (!normalizedId || normalizedId.includes("/") || normalizedId.includes(".")) return null;
25921
+ for (const extension of ATTACHMENT_FILENAME_EXTENSIONS) {
25922
+ const maybePath = resolveAttachmentRelativePath({
25923
+ attachmentsDir: input.attachmentsDir,
25924
+ relativePath: `${normalizedId}${extension}`
25925
+ });
25926
+ if (maybePath && NodeFS.existsSync(maybePath)) return maybePath;
25927
+ }
25928
+ return null;
25929
+ }
25930
+ function parseAttachmentIdFromRelativePath(relativePath) {
25931
+ const normalized = normalizeAttachmentRelativePath(relativePath);
25932
+ if (!normalized || normalized.includes("/")) return null;
25933
+ const extensionIndex = normalized.lastIndexOf(".");
25934
+ if (extensionIndex <= 0) return null;
25935
+ const id = normalized.slice(0, extensionIndex);
25936
+ return id.length > 0 && !id.includes(".") ? id : null;
25937
+ }
25938
+ //#endregion
25939
+ //#region src/orchestration/threadFork.ts
25940
+ /** Providers whose adapter can open a new session on top of a saved one. */
25941
+ const FORKABLE_PROVIDERS = /* @__PURE__ */ new Set(["claudeAgent"]);
25942
+ /**
25943
+ * Message ids are unique across threads, so a copied message needs an id
25944
+ * that ties it to the fork while staying derivable from the original.
25945
+ */
25946
+ function forkedMessageId(threadId, messageId) {
25947
+ return `${threadId}:fork:${messageId}`;
25948
+ }
25949
+ /**
25950
+ * Attachment ids must look like `<thread-segment>-<uuid>` and the file lives
25951
+ * beside the id, so a copy gets a fresh id under the fork's segment. The uuid
25952
+ * is derived from the source id: the pure read-model projector and the SQLite
25953
+ * projection must agree on it without sharing state.
25954
+ */
25955
+ function forkedAttachmentId(threadId, attachmentId) {
25956
+ const segment = toSafeThreadAttachmentSegment(threadId);
25957
+ if (!segment) return null;
25958
+ const digest = NodeCrypto.createHash("sha256").update(`${threadId} ${attachmentId}`).digest("hex");
25959
+ return `${segment}-${[
25960
+ digest.slice(0, 8),
25961
+ digest.slice(8, 12),
25962
+ digest.slice(12, 16),
25963
+ digest.slice(16, 20),
25964
+ digest.slice(20, 32)
25965
+ ].join("-")}`;
25966
+ }
25967
+ function copyForkedAttachments(threadId, attachments) {
25968
+ if (attachments === void 0) return void 0;
25969
+ const copies = [];
25970
+ for (const attachment of attachments) {
25971
+ const id = forkedAttachmentId(threadId, attachment.id);
25972
+ if (id === null) continue;
25973
+ copies.push({
25974
+ ...attachment,
25975
+ id
25976
+ });
25977
+ }
25978
+ return copies;
25979
+ }
25980
+ /**
25981
+ * Copies a source thread's conversation for the fork read model. Turn ids
25982
+ * belong to the source thread's turns, so copies carry none; attachments are
25983
+ * re-keyed to the fork so their files can be copied beside them; a streaming
25984
+ * message is frozen as-is.
25985
+ */
25986
+ function copyForkedMessages(threadId, messages) {
25987
+ return messages.map((message) => {
25988
+ const attachments = copyForkedAttachments(threadId, message.attachments);
25989
+ return {
25990
+ id: forkedMessageId(threadId, message.id),
25991
+ role: message.role,
25992
+ text: message.text,
25993
+ ...attachments !== void 0 ? { attachments } : {},
25994
+ turnId: null,
25995
+ streaming: false,
25996
+ createdAt: message.createdAt,
25997
+ updatedAt: message.updatedAt
25998
+ };
25999
+ });
26000
+ }
26001
+ /** Same copy as {@link copyForkedMessages}, for the SQLite message projection. */
26002
+ function copyForkedProjectionMessages(threadId, rows) {
26003
+ return rows.map((row) => {
26004
+ const attachments = copyForkedAttachments(threadId, row.attachments);
26005
+ return {
26006
+ messageId: forkedMessageId(threadId, row.messageId),
26007
+ threadId,
26008
+ turnId: null,
26009
+ role: row.role,
26010
+ text: row.text,
26011
+ ...attachments !== void 0 ? { attachments } : {},
26012
+ isStreaming: false,
26013
+ createdAt: row.createdAt,
26014
+ updatedAt: row.updatedAt
26015
+ };
26016
+ });
26017
+ }
26018
+ /** File copies that make the re-keyed attachment references resolve. */
26019
+ function collectForkedAttachmentCopies(threadId, rows) {
26020
+ const copies = [];
26021
+ for (const row of rows) for (const attachment of row.attachments ?? []) {
26022
+ const id = forkedAttachmentId(threadId, attachment.id);
26023
+ if (id === null) continue;
26024
+ copies.push({
26025
+ fromRelativePath: attachmentRelativePath(attachment),
26026
+ toRelativePath: attachmentRelativePath({
26027
+ ...attachment,
26028
+ id
26029
+ })
26030
+ });
26031
+ }
26032
+ return copies;
26033
+ }
26034
+ function isRecord$6(value) {
26035
+ return value !== null && typeof value === "object" && !Array.isArray(value);
26036
+ }
26037
+ /**
26038
+ * Provider binding for a fork, or the reason there is none. The resume cursor
26039
+ * is re-keyed to the fork and flagged so the adapter opens a new provider
26040
+ * session from the saved one instead of continuing the source's session.
26041
+ */
26042
+ function buildForkedProviderBinding(threadId, source) {
26043
+ if (source === void 0) return { rejection: "no-binding" };
26044
+ if (!FORKABLE_PROVIDERS.has(source.provider)) return { rejection: "provider-cannot-fork" };
26045
+ if (!isRecord$6(source.resumeCursor) || typeof source.resumeCursor.resume !== "string") return { rejection: "no-resume-cursor" };
26046
+ return { binding: {
26047
+ threadId,
26048
+ provider: source.provider,
26049
+ ...source.providerInstanceId ? { providerInstanceId: source.providerInstanceId } : {},
26050
+ ...source.adapterKey ? { adapterKey: source.adapterKey } : {},
26051
+ ...source.runtimeMode ? { runtimeMode: source.runtimeMode } : {},
26052
+ status: "stopped",
26053
+ resumeCursor: {
26054
+ ...source.resumeCursor,
26055
+ threadId,
26056
+ forkSession: true
26057
+ },
26058
+ runtimePayload: source.runtimePayload ?? null
26059
+ } };
26060
+ }
26061
+ function describeThreadForkRejection(rejection) {
26062
+ switch (rejection) {
26063
+ case "no-binding": return "This thread has no provider session to fork yet. Send a message first.";
26064
+ case "provider-cannot-fork": return "This thread's provider cannot fork a session. Fork is available for Claude threads.";
26065
+ case "no-resume-cursor": return "This thread's provider session cannot be resumed, so it cannot be forked.";
26066
+ }
26067
+ }
26068
+ //#endregion
25613
26069
  //#region src/orchestration/Schemas.ts
25614
26070
  const ProjectCreatedPayload = ProjectCreatedPayload$1;
25615
26071
  const ProjectMetaUpdatedPayload = ProjectMetaUpdatedPayload$1;
@@ -25632,6 +26088,9 @@ const ThreadSnoozedPayload = ThreadSnoozedPayload$1;
25632
26088
  const ThreadUnsnoozedPayload = ThreadUnsnoozedPayload$1;
25633
26089
  const MessageSentPayloadSchema = ThreadMessageSentPayload;
25634
26090
  const ThreadProposedPlanUpsertedPayload = ThreadProposedPlanUpsertedPayload$1;
26091
+ const ThreadScheduledTaskCreatedPayload = ThreadScheduledTaskCreatedPayload$1;
26092
+ const ThreadScheduledTaskCancelledPayload = ThreadScheduledTaskCancelledPayload$1;
26093
+ const ThreadScheduledTaskFiredPayload = ThreadScheduledTaskFiredPayload$1;
25635
26094
  const ThreadSessionSetPayload = ThreadSessionSetPayload$1;
25636
26095
  const ThreadTurnDiffCompletedPayload = ThreadTurnDiffCompletedPayload$1;
25637
26096
  const ThreadRevertedPayload = ThreadRevertedPayload$1;
@@ -25814,6 +26273,7 @@ function projectEvent(model, event) {
25814
26273
  })));
25815
26274
  case "thread.created": return Effect.gen(function* () {
25816
26275
  const payload = yield* decodeForEvent(ThreadCreatedPayload, event.payload, event.type, "payload");
26276
+ const forkSource = payload.forkedFromThreadId === void 0 ? void 0 : nextBase.threads.find((entry) => entry.id === payload.forkedFromThreadId);
25817
26277
  const thread = yield* decodeForEvent(OrchestrationThread, {
25818
26278
  id: payload.threadId,
25819
26279
  projectId: payload.projectId,
@@ -25834,7 +26294,7 @@ function projectEvent(model, event) {
25834
26294
  snoozedUntil: null,
25835
26295
  snoozedAt: null,
25836
26296
  deletedAt: null,
25837
- messages: [],
26297
+ messages: forkSource ? copyForkedMessages(payload.threadId, forkSource.messages) : [],
25838
26298
  activities: [],
25839
26299
  checkpoints: [],
25840
26300
  session: null
@@ -26016,6 +26476,42 @@ function projectEvent(model, event) {
26016
26476
  })
26017
26477
  };
26018
26478
  });
26479
+ case "thread.scheduled-task.created": return Effect.gen(function* () {
26480
+ const payload = yield* decodeForEvent(ThreadScheduledTaskCreatedPayload, event.payload, event.type, "payload");
26481
+ const thread = nextBase.threads.find((entry) => entry.id === payload.threadId);
26482
+ if (!thread) return nextBase;
26483
+ return {
26484
+ ...nextBase,
26485
+ threads: updateThread(nextBase.threads, payload.threadId, {
26486
+ scheduledTasks: [...(thread.scheduledTasks ?? []).filter((entry) => entry.id !== payload.task.id), payload.task],
26487
+ updatedAt: event.occurredAt
26488
+ })
26489
+ };
26490
+ });
26491
+ case "thread.scheduled-task.cancelled":
26492
+ case "thread.scheduled-task.fired": return Effect.gen(function* () {
26493
+ const payload = event.type === "thread.scheduled-task.cancelled" ? yield* decodeForEvent(ThreadScheduledTaskCancelledPayload, event.payload, event.type, "payload") : yield* decodeForEvent(ThreadScheduledTaskFiredPayload, event.payload, event.type, "payload");
26494
+ const thread = nextBase.threads.find((entry) => entry.id === payload.threadId);
26495
+ if (!thread) return nextBase;
26496
+ return {
26497
+ ...nextBase,
26498
+ threads: updateThread(nextBase.threads, payload.threadId, {
26499
+ scheduledTasks: (thread.scheduledTasks ?? []).map((task) => task.id !== payload.taskId ? task : "cancelledAt" in payload ? {
26500
+ ...task,
26501
+ status: "cancelled",
26502
+ cancelledAt: payload.cancelledAt,
26503
+ updatedAt: event.occurredAt
26504
+ } : {
26505
+ ...task,
26506
+ status: payload.failure === null ? "fired" : "failed",
26507
+ firedAt: payload.firedAt,
26508
+ failure: payload.failure,
26509
+ updatedAt: event.occurredAt
26510
+ }),
26511
+ updatedAt: event.occurredAt
26512
+ })
26513
+ };
26514
+ });
26019
26515
  case "thread.proposed-plan-upserted": return Effect.gen(function* () {
26020
26516
  const payload = yield* decodeForEvent(ThreadProposedPlanUpsertedPayload, event.payload, event.type, "payload");
26021
26517
  const thread = nextBase.threads.find((entry) => entry.id === payload.threadId);
@@ -26336,6 +26832,113 @@ const decideOrchestrationCommand = Effect.fn("decideOrchestrationCommand")(funct
26336
26832
  updatedAt: command.createdAt
26337
26833
  }
26338
26834
  };
26835
+ case "thread.fork": {
26836
+ const source = yield* requireThread({
26837
+ readModel,
26838
+ command,
26839
+ threadId: command.sourceThreadId
26840
+ });
26841
+ yield* requireThreadAbsent({
26842
+ readModel,
26843
+ command,
26844
+ threadId: command.threadId
26845
+ });
26846
+ return {
26847
+ ...yield* withEventBase({
26848
+ aggregateKind: "thread",
26849
+ aggregateId: command.threadId,
26850
+ occurredAt: command.createdAt,
26851
+ commandId: command.commandId
26852
+ }),
26853
+ type: "thread.created",
26854
+ payload: {
26855
+ threadId: command.threadId,
26856
+ projectId: source.projectId,
26857
+ title: command.title ?? `${source.title} (fork)`,
26858
+ modelSelection: source.modelSelection,
26859
+ runtimeMode: source.runtimeMode,
26860
+ interactionMode: source.interactionMode,
26861
+ compressMode: source.compressMode,
26862
+ unpromptedSubagents: source.unpromptedSubagents,
26863
+ branch: source.branch,
26864
+ worktreePath: source.worktreePath,
26865
+ forkedFromThreadId: source.id,
26866
+ createdAt: command.createdAt,
26867
+ updatedAt: command.createdAt
26868
+ }
26869
+ };
26870
+ }
26871
+ case "thread.scheduled-task.create":
26872
+ if (((yield* requireThread({
26873
+ readModel,
26874
+ command,
26875
+ threadId: command.threadId
26876
+ })).scheduledTasks ?? []).some((task) => task.id === command.taskId)) return yield* new OrchestrationCommandInvariantError({
26877
+ commandType: command.type,
26878
+ detail: `Scheduled task '${command.taskId}' already exists on thread '${command.threadId}'.`
26879
+ });
26880
+ return {
26881
+ ...yield* withEventBase({
26882
+ aggregateKind: "thread",
26883
+ aggregateId: command.threadId,
26884
+ occurredAt: command.createdAt,
26885
+ commandId: command.commandId
26886
+ }),
26887
+ type: "thread.scheduled-task.created",
26888
+ payload: {
26889
+ threadId: command.threadId,
26890
+ task: {
26891
+ id: command.taskId,
26892
+ prompt: command.prompt,
26893
+ runAt: command.runAt,
26894
+ status: "pending",
26895
+ createdAt: command.createdAt,
26896
+ updatedAt: command.createdAt,
26897
+ firedAt: null,
26898
+ cancelledAt: null,
26899
+ failure: null
26900
+ }
26901
+ }
26902
+ };
26903
+ case "thread.scheduled-task.cancel":
26904
+ case "thread.scheduled-task.fire": {
26905
+ const task = ((yield* requireThread({
26906
+ readModel,
26907
+ command,
26908
+ threadId: command.threadId
26909
+ })).scheduledTasks ?? []).find((entry) => entry.id === command.taskId);
26910
+ const cancellable = task?.status === "pending" || task?.status === "failed";
26911
+ if (task === void 0 || (command.type === "thread.scheduled-task.fire" ? task.status !== "pending" : !cancellable)) return yield* new OrchestrationCommandInvariantError({
26912
+ commandType: command.type,
26913
+ detail: `Scheduled task '${command.taskId}' is not ${command.type === "thread.scheduled-task.fire" ? "pending" : "pending or failed"} on thread '${command.threadId}'.`
26914
+ });
26915
+ const occurredAt = command.type === "thread.scheduled-task.cancel" ? command.cancelledAt : command.firedAt;
26916
+ const base = yield* withEventBase({
26917
+ aggregateKind: "thread",
26918
+ aggregateId: command.threadId,
26919
+ occurredAt,
26920
+ commandId: command.commandId
26921
+ });
26922
+ if (command.type === "thread.scheduled-task.cancel") return {
26923
+ ...base,
26924
+ type: "thread.scheduled-task.cancelled",
26925
+ payload: {
26926
+ threadId: command.threadId,
26927
+ taskId: command.taskId,
26928
+ cancelledAt: command.cancelledAt
26929
+ }
26930
+ };
26931
+ return {
26932
+ ...base,
26933
+ type: "thread.scheduled-task.fired",
26934
+ payload: {
26935
+ threadId: command.threadId,
26936
+ taskId: command.taskId,
26937
+ firedAt: command.firedAt,
26938
+ failure: command.failure ?? null
26939
+ }
26940
+ };
26941
+ }
26339
26942
  case "thread.delete": {
26340
26943
  yield* requireThread({
26341
26944
  readModel,
@@ -27903,6 +28506,23 @@ const ListProjectionThreadProposedPlansInput = Schema$1.Struct({ threadId: Threa
27903
28506
  const DeleteProjectionThreadProposedPlansInput = Schema$1.Struct({ threadId: ThreadId });
27904
28507
  var ProjectionThreadProposedPlanRepository = class extends Context.Service()("@p4code/cli/persistence/Services/ProjectionThreadProposedPlans/ProjectionThreadProposedPlanRepository") {};
27905
28508
  //#endregion
28509
+ //#region src/persistence/Services/ProjectionThreadScheduledTasks.ts
28510
+ const ProjectionThreadScheduledTask = Schema$1.Struct({
28511
+ taskId: OrchestrationScheduledTaskId,
28512
+ threadId: ThreadId,
28513
+ prompt: TrimmedNonEmptyString,
28514
+ runAt: IsoDateTime,
28515
+ status: OrchestrationScheduledTaskStatus,
28516
+ createdAt: IsoDateTime,
28517
+ updatedAt: IsoDateTime,
28518
+ firedAt: Schema$1.NullOr(IsoDateTime),
28519
+ cancelledAt: Schema$1.NullOr(IsoDateTime),
28520
+ failure: Schema$1.NullOr(Schema$1.String)
28521
+ });
28522
+ const GetProjectionThreadScheduledTaskInput = Schema$1.Struct({ taskId: OrchestrationScheduledTaskId });
28523
+ const ListProjectionThreadScheduledTasksInput = Schema$1.Struct({ threadId: ThreadId });
28524
+ var ProjectionThreadScheduledTaskRepository = class extends Context.Service()("@p4code/cli/persistence/Services/ProjectionThreadScheduledTasks/ProjectionThreadScheduledTaskRepository") {};
28525
+ //#endregion
27906
28526
  //#region src/persistence/Services/ProjectionThreadSessions.ts
27907
28527
  /**
27908
28528
  * ProjectionThreadSessionRepository - Repository interface for thread sessions.
@@ -28598,6 +29218,91 @@ const makeProjectionThreadProposedPlanRepository = Effect.gen(function* () {
28598
29218
  });
28599
29219
  const ProjectionThreadProposedPlanRepositoryLive = Layer.effect(ProjectionThreadProposedPlanRepository, makeProjectionThreadProposedPlanRepository);
28600
29220
  //#endregion
29221
+ //#region src/persistence/Layers/ProjectionThreadScheduledTasks.ts
29222
+ const makeProjectionThreadScheduledTaskRepository = Effect.gen(function* () {
29223
+ const sql = yield* SqlClient.SqlClient;
29224
+ const upsertRow = SqlSchema.void({
29225
+ Request: ProjectionThreadScheduledTask,
29226
+ execute: (row) => sql`
29227
+ INSERT INTO projection_thread_scheduled_tasks (
29228
+ task_id,
29229
+ thread_id,
29230
+ prompt,
29231
+ run_at,
29232
+ status,
29233
+ created_at,
29234
+ updated_at,
29235
+ fired_at,
29236
+ cancelled_at,
29237
+ failure
29238
+ )
29239
+ VALUES (
29240
+ ${row.taskId},
29241
+ ${row.threadId},
29242
+ ${row.prompt},
29243
+ ${row.runAt},
29244
+ ${row.status},
29245
+ ${row.createdAt},
29246
+ ${row.updatedAt},
29247
+ ${row.firedAt},
29248
+ ${row.cancelledAt},
29249
+ ${row.failure}
29250
+ )
29251
+ ON CONFLICT (task_id)
29252
+ DO UPDATE SET
29253
+ thread_id = excluded.thread_id,
29254
+ prompt = excluded.prompt,
29255
+ run_at = excluded.run_at,
29256
+ status = excluded.status,
29257
+ created_at = excluded.created_at,
29258
+ updated_at = excluded.updated_at,
29259
+ fired_at = excluded.fired_at,
29260
+ cancelled_at = excluded.cancelled_at,
29261
+ failure = excluded.failure
29262
+ `
29263
+ });
29264
+ const selectColumns = sql`
29265
+ task_id AS "taskId",
29266
+ thread_id AS "threadId",
29267
+ prompt,
29268
+ run_at AS "runAt",
29269
+ status,
29270
+ created_at AS "createdAt",
29271
+ updated_at AS "updatedAt",
29272
+ fired_at AS "firedAt",
29273
+ cancelled_at AS "cancelledAt",
29274
+ failure
29275
+ `;
29276
+ const getRow = SqlSchema.findOneOption({
29277
+ Request: GetProjectionThreadScheduledTaskInput,
29278
+ Result: ProjectionThreadScheduledTask,
29279
+ execute: ({ taskId }) => sql`
29280
+ SELECT ${selectColumns}
29281
+ FROM projection_thread_scheduled_tasks
29282
+ WHERE task_id = ${taskId}
29283
+ `
29284
+ });
29285
+ const listRows = SqlSchema.findAll({
29286
+ Request: ListProjectionThreadScheduledTasksInput,
29287
+ Result: ProjectionThreadScheduledTask,
29288
+ execute: ({ threadId }) => sql`
29289
+ SELECT ${selectColumns}
29290
+ FROM projection_thread_scheduled_tasks
29291
+ WHERE thread_id = ${threadId}
29292
+ ORDER BY run_at ASC, task_id ASC
29293
+ `
29294
+ });
29295
+ const upsert = (row) => upsertRow(row).pipe(Effect.mapError(toPersistenceSqlError("ProjectionThreadScheduledTaskRepository.upsert:query")));
29296
+ const getByTaskId = (input) => getRow(input).pipe(Effect.map(Option.getOrNull), Effect.mapError(toPersistenceSqlError("ProjectionThreadScheduledTaskRepository.getByTaskId:query")));
29297
+ const listByThreadId = (input) => listRows(input).pipe(Effect.mapError(toPersistenceSqlError("ProjectionThreadScheduledTaskRepository.listByThreadId:query")));
29298
+ return {
29299
+ upsert,
29300
+ getByTaskId,
29301
+ listByThreadId
29302
+ };
29303
+ });
29304
+ const ProjectionThreadScheduledTaskRepositoryLive = Layer.effect(ProjectionThreadScheduledTaskRepository, makeProjectionThreadScheduledTaskRepository);
29305
+ //#endregion
28601
29306
  //#region src/persistence/Layers/ProjectionThreadSessions.ts
28602
29307
  const makeProjectionThreadSessionRepository = Effect.gen(function* () {
28603
29308
  const sql = yield* SqlClient.SqlClient;
@@ -29093,182 +29798,13 @@ const makeProjectionThreadRepository = Effect.gen(function* () {
29093
29798
  });
29094
29799
  const ProjectionThreadRepositoryLive = Layer.effect(ProjectionThreadRepository, makeProjectionThreadRepository);
29095
29800
  //#endregion
29096
- //#region src/attachmentPaths.ts
29097
- function normalizeAttachmentRelativePath(rawRelativePath) {
29098
- const normalized = NodePath.normalize(rawRelativePath).replace(/^[/\\]+/, "");
29099
- if (normalized.length === 0 || normalized.startsWith("..") || normalized.includes("\0")) return null;
29100
- return normalized.replace(/\\/g, "/");
29101
- }
29102
- function resolveAttachmentRelativePath(input) {
29103
- const normalizedRelativePath = normalizeAttachmentRelativePath(input.relativePath);
29104
- if (!normalizedRelativePath) return null;
29105
- const attachmentsRoot = NodePath.resolve(input.attachmentsDir);
29106
- const filePath = NodePath.resolve(NodePath.join(attachmentsRoot, normalizedRelativePath));
29107
- if (!filePath.startsWith(`${attachmentsRoot}${NodePath.sep}`)) return null;
29108
- return filePath;
29109
- }
29110
- //#endregion
29111
- //#region src/imageMime.ts
29112
- const IMAGE_EXTENSION_BY_MIME_TYPE = {
29113
- "image/avif": ".avif",
29114
- "image/bmp": ".bmp",
29115
- "image/gif": ".gif",
29116
- "image/heic": ".heic",
29117
- "image/heif": ".heif",
29118
- "image/jpeg": ".jpg",
29119
- "image/jpg": ".jpg",
29120
- "image/png": ".png",
29121
- "image/svg+xml": ".svg",
29122
- "image/tiff": ".tiff",
29123
- "image/webp": ".webp"
29124
- };
29125
- const SAFE_IMAGE_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
29126
- ".avif",
29127
- ".bmp",
29128
- ".gif",
29129
- ".heic",
29130
- ".heif",
29131
- ".ico",
29132
- ".jpeg",
29133
- ".jpg",
29134
- ".png",
29135
- ".svg",
29136
- ".tiff",
29137
- ".webp"
29138
- ]);
29139
- function isBase64Char(code) {
29140
- return code >= 97 && code <= 122 || code >= 65 && code <= 90 || code >= 48 && code <= 57 || code === 43 || code === 47 || code === 61;
29141
- }
29142
- function isBase64Whitespace(code) {
29143
- return code === 13 || code === 10 || code === 32;
29144
- }
29145
- function parseBase64DataUrl(dataUrl) {
29146
- const trimmed = dataUrl.trim();
29147
- if (trimmed.slice(0, 5).toLowerCase() !== "data:") return null;
29148
- const commaIndex = trimmed.indexOf(",");
29149
- if (commaIndex === -1) return null;
29150
- const header = trimmed.slice(5, commaIndex);
29151
- if (header.length === 0) return null;
29152
- const headerParts = [];
29153
- for (const part of header.split(";")) {
29154
- const partTrimmed = part.trim();
29155
- if (partTrimmed.length > 0) headerParts.push(partTrimmed);
29156
- }
29157
- if (headerParts.length < 2) return null;
29158
- if (headerParts.at(-1)?.toLowerCase() !== "base64") return null;
29159
- const mimeType = headerParts[0]?.toLowerCase();
29160
- if (!mimeType) return null;
29161
- const payload = trimmed.slice(commaIndex + 1);
29162
- const runs = [];
29163
- let runStart = -1;
29164
- for (let index = 0; index < payload.length; index += 1) {
29165
- const code = payload.charCodeAt(index);
29166
- if (isBase64Char(code)) {
29167
- if (runStart === -1) runStart = index;
29168
- continue;
29169
- }
29170
- if (!isBase64Whitespace(code)) return null;
29171
- if (runStart !== -1) {
29172
- runs.push(payload.slice(runStart, index));
29173
- runStart = -1;
29174
- }
29175
- }
29176
- if (runStart !== -1) runs.push(payload.slice(runStart));
29177
- const base64 = runs.length === 1 ? runs[0] : runs.join("");
29178
- if (base64.length === 0 || base64.length % 4 !== 0) return null;
29179
- const firstPad = base64.indexOf("=");
29180
- if (firstPad !== -1) {
29181
- if (base64.length - firstPad > 2) return null;
29182
- for (let index = firstPad; index < base64.length; index += 1) if (base64.charCodeAt(index) !== 61) return null;
29183
- }
29184
- return {
29185
- mimeType,
29186
- base64
29187
- };
29188
- }
29189
- function inferImageExtension(input) {
29190
- const key = input.mimeType.toLowerCase();
29191
- const fromMime = Object.hasOwn(IMAGE_EXTENSION_BY_MIME_TYPE, key) ? IMAGE_EXTENSION_BY_MIME_TYPE[key] : void 0;
29192
- if (fromMime) return fromMime;
29193
- const fromMimeExtension = Mime.getExtension(input.mimeType);
29194
- if (fromMimeExtension && SAFE_IMAGE_FILE_EXTENSIONS.has(fromMimeExtension)) return fromMimeExtension;
29195
- const fileName = input.fileName?.trim() ?? "";
29196
- const extensionMatch = /\.([a-z0-9]{1,8})$/i.exec(fileName);
29197
- const fileNameExtension = extensionMatch ? `.${extensionMatch[1].toLowerCase()}` : "";
29198
- if (SAFE_IMAGE_FILE_EXTENSIONS.has(fileNameExtension)) return fileNameExtension;
29199
- return ".bin";
29200
- }
29201
- //#endregion
29202
- //#region src/attachmentStore.ts
29203
- const ATTACHMENT_FILENAME_EXTENSIONS = [
29204
- ...SAFE_IMAGE_FILE_EXTENSIONS,
29205
- ".pdf",
29206
- ".bin"
29207
- ];
29208
- const ATTACHMENT_ID_THREAD_SEGMENT_MAX_CHARS = 80;
29209
- const ATTACHMENT_ID_PATTERN = new RegExp(`^([a-z0-9_]+(?:-[a-z0-9_]+)*)-([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$`, "i");
29210
- function toSafeThreadAttachmentSegment(threadId) {
29211
- const segment = threadId.trim().toLowerCase().replace(/[^a-z0-9_-]+/gi, "-").replace(/-+/g, "-").replace(/^[-_]+|[-_]+$/g, "").slice(0, ATTACHMENT_ID_THREAD_SEGMENT_MAX_CHARS).replace(/[-_]+$/g, "");
29212
- if (segment.length === 0) return null;
29213
- return segment;
29214
- }
29215
- function createAttachmentId(threadId) {
29216
- const threadSegment = toSafeThreadAttachmentSegment(threadId);
29217
- if (!threadSegment) return null;
29218
- return `${threadSegment}-${NodeCrypto.randomUUID()}`;
29219
- }
29220
- function parseThreadSegmentFromAttachmentId(attachmentId) {
29221
- const normalizedId = normalizeAttachmentRelativePath(attachmentId);
29222
- if (!normalizedId || normalizedId.includes("/") || normalizedId.includes(".")) return null;
29223
- const match = normalizedId.match(ATTACHMENT_ID_PATTERN);
29224
- if (!match) return null;
29225
- return match[1]?.toLowerCase() ?? null;
29226
- }
29227
- function attachmentRelativePath(attachment) {
29228
- switch (attachment.type) {
29229
- case "image": {
29230
- const extension = inferImageExtension({
29231
- mimeType: attachment.mimeType,
29232
- fileName: attachment.name
29233
- });
29234
- return `${attachment.id}${extension}`;
29235
- }
29236
- case "document": return `${attachment.id}.pdf`;
29237
- }
29238
- }
29239
- function resolveAttachmentPath(input) {
29240
- return resolveAttachmentRelativePath({
29241
- attachmentsDir: input.attachmentsDir,
29242
- relativePath: attachmentRelativePath(input.attachment)
29243
- });
29244
- }
29245
- function resolveAttachmentPathById(input) {
29246
- const normalizedId = normalizeAttachmentRelativePath(input.attachmentId);
29247
- if (!normalizedId || normalizedId.includes("/") || normalizedId.includes(".")) return null;
29248
- for (const extension of ATTACHMENT_FILENAME_EXTENSIONS) {
29249
- const maybePath = resolveAttachmentRelativePath({
29250
- attachmentsDir: input.attachmentsDir,
29251
- relativePath: `${normalizedId}${extension}`
29252
- });
29253
- if (maybePath && NodeFS.existsSync(maybePath)) return maybePath;
29254
- }
29255
- return null;
29256
- }
29257
- function parseAttachmentIdFromRelativePath(relativePath) {
29258
- const normalized = normalizeAttachmentRelativePath(relativePath);
29259
- if (!normalized || normalized.includes("/")) return null;
29260
- const extensionIndex = normalized.lastIndexOf(".");
29261
- if (extensionIndex <= 0) return null;
29262
- const id = normalized.slice(0, extensionIndex);
29263
- return id.length > 0 && !id.includes(".") ? id : null;
29264
- }
29265
- //#endregion
29266
29801
  //#region src/orchestration/Layers/ProjectionPipeline.ts
29267
29802
  const ORCHESTRATION_PROJECTOR_NAMES = {
29268
29803
  projects: "projection.projects",
29269
29804
  threads: "projection.threads",
29270
29805
  threadMessages: "projection.thread-messages",
29271
29806
  threadProposedPlans: "projection.thread-proposed-plans",
29807
+ threadScheduledTasks: "projection.thread-scheduled-tasks",
29272
29808
  threadActivities: "projection.thread-activities",
29273
29809
  threadSessions: "projection.thread-sessions",
29274
29810
  threadTurns: "projection.thread-turns",
@@ -29487,6 +30023,7 @@ const makeOrchestrationProjectionPipeline = Effect.fn("makeOrchestrationProjecti
29487
30023
  const projectionThreadRepository = yield* ProjectionThreadRepository;
29488
30024
  const projectionThreadMessageRepository = yield* ProjectionThreadMessageRepository;
29489
30025
  const projectionThreadProposedPlanRepository = yield* ProjectionThreadProposedPlanRepository;
30026
+ const projectionThreadScheduledTaskRepository = yield* ProjectionThreadScheduledTaskRepository;
29490
30027
  const projectionThreadActivityRepository = yield* ProjectionThreadActivityRepository;
29491
30028
  const projectionThreadSessionRepository = yield* ProjectionThreadSessionRepository;
29492
30029
  const projectionTurnRepository = yield* ProjectionTurnRepository;
@@ -29494,6 +30031,7 @@ const makeOrchestrationProjectionPipeline = Effect.fn("makeOrchestrationProjecti
29494
30031
  const fileSystem = yield* FileSystem.FileSystem;
29495
30032
  const path = yield* Path.Path;
29496
30033
  const serverConfig = yield* ServerConfig$1;
30034
+ const attachmentsRootDir = serverConfig.attachmentsDir;
29497
30035
  const applyProjectsProjection = Effect.fn("applyProjectsProjection")(function* (event, _attachmentSideEffects) {
29498
30036
  switch (event.type) {
29499
30037
  case "project.created":
@@ -29893,8 +30431,34 @@ const makeOrchestrationProjectionPipeline = Effect.fn("makeOrchestrationProjecti
29893
30431
  default: return;
29894
30432
  }
29895
30433
  });
30434
+ const copyForkedAttachmentFile = Effect.fn("copyForkedAttachmentFile")(function* (copy) {
30435
+ const fromPath = resolveAttachmentRelativePath({
30436
+ attachmentsDir: attachmentsRootDir,
30437
+ relativePath: copy.fromRelativePath
30438
+ });
30439
+ const toPath = resolveAttachmentRelativePath({
30440
+ attachmentsDir: attachmentsRootDir,
30441
+ relativePath: copy.toRelativePath
30442
+ });
30443
+ if (!fromPath || !toPath) return yield* new PersistenceSqlError({
30444
+ operation: "ProjectionPipeline.copyForkedAttachmentFile",
30445
+ detail: `Unsafe attachment path while forking: ${copy.fromRelativePath}`
30446
+ });
30447
+ yield* fileSystem.copyFile(fromPath, toPath).pipe(Effect.catch((error) => error.reason._tag === "NotFound" ? Effect.logWarning("thread fork skipped a missing source attachment", { from: copy.fromRelativePath }) : Effect.fail(new PersistenceSqlError({
30448
+ operation: "ProjectionPipeline.copyForkedAttachmentFile",
30449
+ detail: `Could not copy attachment ${copy.fromRelativePath}: ${error.message}`,
30450
+ cause: error
30451
+ }))));
30452
+ });
29896
30453
  const applyThreadMessagesProjection = Effect.fn("applyThreadMessagesProjection")(function* (event, attachmentSideEffects) {
29897
30454
  switch (event.type) {
30455
+ case "thread.created": {
30456
+ if (event.payload.forkedFromThreadId === void 0) return;
30457
+ const sourceRows = yield* projectionThreadMessageRepository.listByThreadId({ threadId: event.payload.forkedFromThreadId });
30458
+ yield* Effect.forEach(collectForkedAttachmentCopies(event.payload.threadId, sourceRows), copyForkedAttachmentFile, { concurrency: 1 });
30459
+ yield* Effect.forEach(copyForkedProjectionMessages(event.payload.threadId, sourceRows), projectionThreadMessageRepository.upsert, { concurrency: 1 }).pipe(Effect.asVoid);
30460
+ return;
30461
+ }
29898
30462
  case "thread.message-sent": {
29899
30463
  const existingMessage = yield* projectionThreadMessageRepository.getByMessageId({ messageId: event.payload.messageId });
29900
30464
  const previousMessage = Option.getOrUndefined(existingMessage);
@@ -29933,6 +30497,43 @@ const makeOrchestrationProjectionPipeline = Effect.fn("makeOrchestrationProjecti
29933
30497
  default: return;
29934
30498
  }
29935
30499
  });
30500
+ const applyThreadScheduledTasksProjection = Effect.fn("applyThreadScheduledTasksProjection")(function* (event) {
30501
+ switch (event.type) {
30502
+ case "thread.scheduled-task.created":
30503
+ yield* projectionThreadScheduledTaskRepository.upsert({
30504
+ taskId: event.payload.task.id,
30505
+ threadId: event.payload.threadId,
30506
+ prompt: event.payload.task.prompt,
30507
+ runAt: event.payload.task.runAt,
30508
+ status: event.payload.task.status,
30509
+ createdAt: event.payload.task.createdAt,
30510
+ updatedAt: event.payload.task.updatedAt,
30511
+ firedAt: event.payload.task.firedAt,
30512
+ cancelledAt: event.payload.task.cancelledAt,
30513
+ failure: event.payload.task.failure
30514
+ });
30515
+ return;
30516
+ case "thread.scheduled-task.cancelled":
30517
+ case "thread.scheduled-task.fired": {
30518
+ const existing = yield* projectionThreadScheduledTaskRepository.getByTaskId({ taskId: event.payload.taskId });
30519
+ if (existing === null) return;
30520
+ yield* projectionThreadScheduledTaskRepository.upsert(event.type === "thread.scheduled-task.cancelled" ? {
30521
+ ...existing,
30522
+ status: "cancelled",
30523
+ cancelledAt: event.payload.cancelledAt,
30524
+ updatedAt: event.occurredAt
30525
+ } : {
30526
+ ...existing,
30527
+ status: event.payload.failure === null ? "fired" : "failed",
30528
+ firedAt: event.payload.firedAt,
30529
+ failure: event.payload.failure,
30530
+ updatedAt: event.occurredAt
30531
+ });
30532
+ return;
30533
+ }
30534
+ default: return;
30535
+ }
30536
+ });
29936
30537
  const applyThreadProposedPlansProjection = Effect.fn("applyThreadProposedPlansProjection")(function* (event, _attachmentSideEffects) {
29937
30538
  switch (event.type) {
29938
30539
  case "thread.proposed-plan-upserted":
@@ -30292,6 +30893,10 @@ const makeOrchestrationProjectionPipeline = Effect.fn("makeOrchestrationProjecti
30292
30893
  name: ORCHESTRATION_PROJECTOR_NAMES.threadProposedPlans,
30293
30894
  apply: applyThreadProposedPlansProjection
30294
30895
  },
30896
+ {
30897
+ name: ORCHESTRATION_PROJECTOR_NAMES.threadScheduledTasks,
30898
+ apply: applyThreadScheduledTasksProjection
30899
+ },
30295
30900
  {
30296
30901
  name: ORCHESTRATION_PROJECTOR_NAMES.threadActivities,
30297
30902
  apply: applyThreadActivitiesProjection
@@ -30341,7 +30946,7 @@ const makeOrchestrationProjectionPipeline = Effect.fn("makeOrchestrationProjecti
30341
30946
  projectEvent
30342
30947
  };
30343
30948
  });
30344
- const OrchestrationProjectionPipelineLive = Layer.effect(OrchestrationProjectionPipeline, makeOrchestrationProjectionPipeline()).pipe(Layer.provideMerge(ProjectionProjectRepositoryLive), Layer.provideMerge(ProjectionThreadRepositoryLive), Layer.provideMerge(ProjectionThreadMessageRepositoryLive), Layer.provideMerge(ProjectionThreadProposedPlanRepositoryLive), Layer.provideMerge(ProjectionThreadActivityRepositoryLive), Layer.provideMerge(ProjectionThreadSessionRepositoryLive), Layer.provideMerge(ProjectionTurnRepositoryLive), Layer.provideMerge(ProjectionPendingApprovalRepositoryLive), Layer.provideMerge(ProjectionStateRepositoryLive));
30949
+ const OrchestrationProjectionPipelineLive = Layer.effect(OrchestrationProjectionPipeline, makeOrchestrationProjectionPipeline()).pipe(Layer.provideMerge(ProjectionProjectRepositoryLive), Layer.provideMerge(ProjectionThreadRepositoryLive), Layer.provideMerge(ProjectionThreadMessageRepositoryLive), Layer.provideMerge(ProjectionThreadProposedPlanRepositoryLive), Layer.provideMerge(ProjectionThreadScheduledTaskRepositoryLive), Layer.provideMerge(ProjectionThreadActivityRepositoryLive), Layer.provideMerge(ProjectionThreadSessionRepositoryLive), Layer.provideMerge(ProjectionTurnRepositoryLive), Layer.provideMerge(ProjectionPendingApprovalRepositoryLive), Layer.provideMerge(ProjectionStateRepositoryLive));
30345
30950
  //#endregion
30346
30951
  //#region src/orchestration/ThreadBackgroundLiveness.ts
30347
30952
  /**
@@ -30369,7 +30974,7 @@ const TERMINAL_STATUSES = /* @__PURE__ */ new Set([
30369
30974
  "interrupted"
30370
30975
  ]);
30371
30976
  var ThreadBackgroundLivenessService = class extends Context.Service()("@p4code/cli/orchestration/ThreadBackgroundLiveness/ThreadBackgroundLivenessService") {};
30372
- function make$74() {
30977
+ function make$75() {
30373
30978
  const stateByThreadId = /* @__PURE__ */ new Map();
30374
30979
  const stateFor = (threadId) => {
30375
30980
  const existing = stateByThreadId.get(threadId);
@@ -30419,7 +31024,7 @@ function make$74() {
30419
31024
  }
30420
31025
  };
30421
31026
  }
30422
- const layer$64 = Layer.effect(ThreadBackgroundLivenessService, Effect.sync(make$74));
31027
+ const layer$64 = Layer.effect(ThreadBackgroundLivenessService, Effect.sync(make$75));
30423
31028
  //#endregion
30424
31029
  //#region src/persistence/Services/ProjectionCheckpoints.ts
30425
31030
  /**
@@ -30988,12 +31593,12 @@ const runProcessCore = Effect.fn("processRunner.runProcessCore")(function* (spaw
30988
31593
  stderrInvalidUtf8: stderr.invalidUtf8
30989
31594
  };
30990
31595
  });
30991
- const make$73 = Effect.fn("ProcessRunner.make")(function* () {
31596
+ const make$74 = Effect.fn("ProcessRunner.make")(function* () {
30992
31597
  const spawner = yield* ChildProcessSpawner$1.ChildProcessSpawner;
30993
31598
  const run = (input) => finalizeRunProcess(runProcessCore(spawner, input), input);
30994
31599
  return ProcessRunner.of({ run });
30995
31600
  });
30996
- const layer$63 = Layer.effect(ProcessRunner, make$73());
31601
+ const layer$63 = Layer.effect(ProcessRunner, make$74());
30997
31602
  //#endregion
30998
31603
  //#region src/project/RepositoryIdentityResolver.ts
30999
31604
  const DEFAULT_REPOSITORY_IDENTITY_CACHE_CAPACITY = 512;
@@ -31084,7 +31689,7 @@ const resolveRepositoryIdentityFromCacheKey = Effect.fn("RepositoryIdentityResol
31084
31689
  rootPath: cacheKey
31085
31690
  }) : null;
31086
31691
  });
31087
- const make$72 = Effect.fn("RepositoryIdentityResolver.make")(function* (options = {}) {
31692
+ const make$73 = Effect.fn("RepositoryIdentityResolver.make")(function* (options = {}) {
31088
31693
  const processRunner = yield* ProcessRunner;
31089
31694
  const repositoryIdentityCache = yield* Cache.makeWith((cacheKey) => resolveRepositoryIdentityFromCacheKey(cacheKey).pipe(Effect.provideService(ProcessRunner, processRunner)), {
31090
31695
  capacity: options.cacheCapacity ?? DEFAULT_REPOSITORY_IDENTITY_CACHE_CAPACITY,
@@ -31099,7 +31704,7 @@ const make$72 = Effect.fn("RepositoryIdentityResolver.make")(function* (options
31099
31704
  });
31100
31705
  return RepositoryIdentityResolver.of({ resolve });
31101
31706
  });
31102
- const layer$62 = Layer.effect(RepositoryIdentityResolver, make$72()).pipe(Layer.provide(layer$63));
31707
+ const layer$62 = Layer.effect(RepositoryIdentityResolver, make$73()).pipe(Layer.provide(layer$63));
31103
31708
  //#endregion
31104
31709
  //#region src/orchestration/Layers/ProjectionSnapshotQuery.ts
31105
31710
  const decodeReadModel = Schema$1.decodeUnknownEffect(OrchestrationReadModel);
@@ -31114,6 +31719,7 @@ const ProjectionThreadMessageDbRowSchema = ProjectionThreadMessage.mapFields(Str
31114
31719
  attachments: Schema$1.NullOr(Schema$1.fromJsonString(Schema$1.Array(ChatAttachment)))
31115
31720
  }));
31116
31721
  const ProjectionThreadProposedPlanDbRowSchema = ProjectionThreadProposedPlan;
31722
+ const ProjectionThreadScheduledTaskDbRowSchema = ProjectionThreadScheduledTask;
31117
31723
  const ProjectionThreadDbRowSchema = ProjectionThread.mapFields(Struct.assign({
31118
31724
  modelSelection: Schema$1.fromJsonString(ModelSelection),
31119
31725
  workspaceLifecycle: Schema$1.NullOr(Schema$1.fromJsonString(ThreadWorkspaceLifecycle))
@@ -31314,6 +31920,28 @@ function mapProjectShellRow(row, repositoryIdentity) {
31314
31920
  updatedAt: row.updatedAt
31315
31921
  };
31316
31922
  }
31923
+ function mapScheduledTaskRow(row) {
31924
+ return {
31925
+ id: row.taskId,
31926
+ prompt: row.prompt,
31927
+ runAt: row.runAt,
31928
+ status: row.status,
31929
+ createdAt: row.createdAt,
31930
+ updatedAt: row.updatedAt,
31931
+ firedAt: row.firedAt,
31932
+ cancelledAt: row.cancelledAt,
31933
+ failure: row.failure
31934
+ };
31935
+ }
31936
+ function groupScheduledTasksByThread(rows) {
31937
+ const byThread = /* @__PURE__ */ new Map();
31938
+ for (const row of rows) {
31939
+ const tasks = byThread.get(row.threadId) ?? [];
31940
+ tasks.push(mapScheduledTaskRow(row));
31941
+ byThread.set(row.threadId, tasks);
31942
+ }
31943
+ return byThread;
31944
+ }
31317
31945
  function mapProposedPlanRow(row) {
31318
31946
  return {
31319
31947
  id: row.planId,
@@ -31542,6 +32170,45 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
31542
32170
  updated_at AS "updatedAt"
31543
32171
  FROM projection_thread_proposed_plans
31544
32172
  ORDER BY thread_id ASC, created_at ASC, plan_id ASC
32173
+ `
32174
+ });
32175
+ const listThreadScheduledTaskRows = SqlSchema.findAll({
32176
+ Request: Schema$1.Void,
32177
+ Result: ProjectionThreadScheduledTaskDbRowSchema,
32178
+ execute: () => sql`
32179
+ SELECT
32180
+ task_id AS "taskId",
32181
+ thread_id AS "threadId",
32182
+ prompt,
32183
+ run_at AS "runAt",
32184
+ status,
32185
+ created_at AS "createdAt",
32186
+ updated_at AS "updatedAt",
32187
+ fired_at AS "firedAt",
32188
+ cancelled_at AS "cancelledAt",
32189
+ failure
32190
+ FROM projection_thread_scheduled_tasks
32191
+ ORDER BY thread_id ASC, run_at ASC, task_id ASC
32192
+ `
32193
+ });
32194
+ const listThreadScheduledTaskRowsByThread = SqlSchema.findAll({
32195
+ Request: ThreadIdLookupInput,
32196
+ Result: ProjectionThreadScheduledTaskDbRowSchema,
32197
+ execute: ({ threadId }) => sql`
32198
+ SELECT
32199
+ task_id AS "taskId",
32200
+ thread_id AS "threadId",
32201
+ prompt,
32202
+ run_at AS "runAt",
32203
+ status,
32204
+ created_at AS "createdAt",
32205
+ updated_at AS "updatedAt",
32206
+ fired_at AS "firedAt",
32207
+ cancelled_at AS "cancelledAt",
32208
+ failure
32209
+ FROM projection_thread_scheduled_tasks
32210
+ WHERE thread_id = ${threadId}
32211
+ ORDER BY run_at ASC, task_id ASC
31545
32212
  `
31546
32213
  });
31547
32214
  const listThreadActivityRows = SqlSchema.findAll({
@@ -32330,8 +32997,10 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
32330
32997
  listTurnSummaryRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getSnapshot:listTurnSummaries:query", "ProjectionSnapshotQuery.getSnapshot:listTurnSummaries:decodeRows"))),
32331
32998
  listLatestTurnRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getSnapshot:listLatestTurns:query", "ProjectionSnapshotQuery.getSnapshot:listLatestTurns:decodeRows"))),
32332
32999
  listProjectionStateRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getSnapshot:listProjectionState:query", "ProjectionSnapshotQuery.getSnapshot:listProjectionState:decodeRows"))),
32333
- listThreadPairRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getSnapshot:listThreadPairs:query", "ProjectionSnapshotQuery.getSnapshot:listThreadPairs:decodeRows")))
32334
- ])).pipe(Effect.flatMap(([projectRows, threadRows, messageRows, proposedPlanRows, activityRows, sessionRows, checkpointRows, turnRows, latestTurnRows, stateRows, threadPairRows]) => Effect.gen(function* () {
33000
+ listThreadPairRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getSnapshot:listThreadPairs:query", "ProjectionSnapshotQuery.getSnapshot:listThreadPairs:decodeRows"))),
33001
+ listThreadScheduledTaskRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getSnapshot:listThreadScheduledTasks:query", "ProjectionSnapshotQuery.getSnapshot:listThreadScheduledTasks:decodeRows")))
33002
+ ])).pipe(Effect.flatMap(([projectRows, threadRows, messageRows, proposedPlanRows, activityRows, sessionRows, checkpointRows, turnRows, latestTurnRows, stateRows, threadPairRows, scheduledTaskRows]) => Effect.gen(function* () {
33003
+ const scheduledTasksByThread = groupScheduledTasksByThread(scheduledTaskRows);
32335
33004
  const messagesByThread = /* @__PURE__ */ new Map();
32336
33005
  const proposedPlansByThread = /* @__PURE__ */ new Map();
32337
33006
  const activitiesByThread = /* @__PURE__ */ new Map();
@@ -32465,6 +33134,7 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
32465
33134
  deletedAt: row.deletedAt,
32466
33135
  messages: messagesByThread.get(row.threadId) ?? [],
32467
33136
  proposedPlans: proposedPlansByThread.get(row.threadId) ?? [],
33137
+ scheduledTasks: scheduledTasksByThread.get(row.threadId) ?? [],
32468
33138
  activities: activitiesByThread.get(row.threadId) ?? [],
32469
33139
  checkpoints: checkpointsByThread.get(row.threadId) ?? [],
32470
33140
  session: sessionsByThread.get(row.threadId) ?? null
@@ -32509,8 +33179,10 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
32509
33179
  listThreadSessionRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getCommandReadModel:listThreadSessions:query", "ProjectionSnapshotQuery.getCommandReadModel:listThreadSessions:decodeRows"))),
32510
33180
  listLatestTurnRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getCommandReadModel:listLatestTurns:query", "ProjectionSnapshotQuery.getCommandReadModel:listLatestTurns:decodeRows"))),
32511
33181
  listProjectionStateRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getCommandReadModel:listProjectionState:query", "ProjectionSnapshotQuery.getCommandReadModel:listProjectionState:decodeRows"))),
32512
- listThreadPairRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getCommandReadModel:listThreadPairs:query", "ProjectionSnapshotQuery.getCommandReadModel:listThreadPairs:decodeRows")))
32513
- ])).pipe(Effect.flatMap(([projectRows, threadRows, proposedPlanRows, sessionRows, latestTurnRows, stateRows, threadPairRows]) => Effect.sync(() => {
33182
+ listThreadPairRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getCommandReadModel:listThreadPairs:query", "ProjectionSnapshotQuery.getCommandReadModel:listThreadPairs:decodeRows"))),
33183
+ listThreadScheduledTaskRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getCommandReadModel:listThreadScheduledTasks:query", "ProjectionSnapshotQuery.getCommandReadModel:listThreadScheduledTasks:decodeRows")))
33184
+ ])).pipe(Effect.flatMap(([projectRows, threadRows, proposedPlanRows, sessionRows, latestTurnRows, stateRows, threadPairRows, scheduledTaskRows]) => Effect.sync(() => {
33185
+ const scheduledTasksByThread = groupScheduledTasksByThread(scheduledTaskRows);
32514
33186
  let updatedAt = null;
32515
33187
  const projects = [];
32516
33188
  const threads = [];
@@ -32605,6 +33277,7 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
32605
33277
  deletedAt: row.deletedAt,
32606
33278
  messages: [],
32607
33279
  proposedPlans: proposedPlansByThread.get(row.threadId) ?? [],
33280
+ scheduledTasks: scheduledTasksByThread.get(row.threadId) ?? [],
32608
33281
  activities: [],
32609
33282
  checkpoints: [],
32610
33283
  session: sessionByThread.get(row.threadId) ?? null
@@ -32857,7 +33530,7 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
32857
33530
  });
32858
33531
  });
32859
33532
  const getThreadDetailByIdBounded = (threadId, bounds) => Effect.gen(function* () {
32860
- const [threadRow, messageRows, proposedPlanRows, activityRows, checkpointRows, turnRows, latestTurnRow, sessionRow] = yield* Effect.all([
33533
+ const [threadRow, messageRows, proposedPlanRows, activityRows, checkpointRows, turnRows, latestTurnRow, sessionRow, scheduledTaskRows] = yield* Effect.all([
32861
33534
  getActiveThreadRowById({ threadId }).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getThreadDetailById:getThread:query", "ProjectionSnapshotQuery.getThreadDetailById:getThread:decodeRow"))),
32862
33535
  (bounds === void 0 ? listThreadMessageRowsByThread({ threadId }) : listThreadMessageRowsByThreadWindow({
32863
33536
  threadId,
@@ -32871,7 +33544,8 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
32871
33544
  listCheckpointRowsByThread({ threadId }).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getThreadDetailById:listCheckpoints:query", "ProjectionSnapshotQuery.getThreadDetailById:listCheckpoints:decodeRows"))),
32872
33545
  listTurnSummaryRowsByThread({ threadId }).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getThreadDetailById:listTurnSummaries:query", "ProjectionSnapshotQuery.getThreadDetailById:listTurnSummaries:decodeRows"))),
32873
33546
  getLatestTurnRowByThread({ threadId }).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getThreadDetailById:getLatestTurn:query", "ProjectionSnapshotQuery.getThreadDetailById:getLatestTurn:decodeRow"))),
32874
- getThreadSessionRowByThread({ threadId }).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getThreadDetailById:getSession:query", "ProjectionSnapshotQuery.getThreadDetailById:getSession:decodeRow")))
33547
+ getThreadSessionRowByThread({ threadId }).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getThreadDetailById:getSession:query", "ProjectionSnapshotQuery.getThreadDetailById:getSession:decodeRow"))),
33548
+ listThreadScheduledTaskRowsByThread({ threadId }).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getThreadDetailById:listScheduledTasks:query", "ProjectionSnapshotQuery.getThreadDetailById:listScheduledTasks:decodeRows")))
32875
33549
  ]);
32876
33550
  if (Option.isNone(threadRow)) return Option.none();
32877
33551
  const thread = {
@@ -32912,6 +33586,7 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
32912
33586
  return message;
32913
33587
  }),
32914
33588
  proposedPlans: proposedPlanRows.map(mapProposedPlanRow),
33589
+ scheduledTasks: scheduledTaskRows.map(mapScheduledTaskRow),
32915
33590
  activities: activityRows.map((row) => {
32916
33591
  const activity = {
32917
33592
  id: row.activityId,
@@ -33564,7 +34239,7 @@ function mergeWithDefaultKeybindings(custom) {
33564
34239
  * Keybindings - Service tag for keybinding configuration operations.
33565
34240
  */
33566
34241
  var Keybindings = class extends Context.Service()("@p4code/cli/keybindings") {};
33567
- const make$71 = Effect.gen(function* () {
34242
+ const make$72 = Effect.gen(function* () {
33568
34243
  const { keybindingsConfigPath } = yield* ServerConfig$1;
33569
34244
  const fs = yield* FileSystem.FileSystem;
33570
34245
  const path = yield* Path.Path;
@@ -33825,7 +34500,7 @@ const make$71 = Effect.gen(function* () {
33825
34500
  }))
33826
34501
  };
33827
34502
  });
33828
- const layer$61 = Layer.effect(Keybindings, make$71);
34503
+ const layer$61 = Layer.effect(Keybindings, make$72);
33829
34504
  //#endregion
33830
34505
  //#region src/process/externalLauncher.ts
33831
34506
  /**
@@ -34052,7 +34727,7 @@ const launchEditorProcess = Effect.fn("externalLauncher.launchEditorProcess")(fu
34052
34727
  cause
34053
34728
  }));
34054
34729
  });
34055
- const make$70 = Effect.gen(function* () {
34730
+ const make$71 = Effect.gen(function* () {
34056
34731
  const spawner = yield* ChildProcessSpawner$1.ChildProcessSpawner;
34057
34732
  const fileSystem = yield* FileSystem.FileSystem;
34058
34733
  const path = yield* Path.Path;
@@ -34063,7 +34738,7 @@ const make$70 = Effect.gen(function* () {
34063
34738
  launchEditor: (input) => provideCommandResolutionServices(Effect.flatMap(resolveEditorLaunch(input), (launch) => launchEditorProcess(launch).pipe(Effect.provideService(ChildProcessSpawner$1.ChildProcessSpawner, spawner))))
34064
34739
  });
34065
34740
  });
34066
- const layer$60 = Layer.effect(ExternalLauncher, make$70);
34741
+ const layer$60 = Layer.effect(ExternalLauncher, make$71);
34067
34742
  //#endregion
34068
34743
  //#region src/orchestration/Services/OrchestrationReactor.ts
34069
34744
  /**
@@ -34081,7 +34756,7 @@ var OrchestrationReactor = class extends Context.Service()("@p4code/cli/orchestr
34081
34756
  //#endregion
34082
34757
  //#region src/serverLifecycleEvents.ts
34083
34758
  var ServerLifecycleEvents = class extends Context.Service()("@p4code/cli/serverLifecycleEvents") {};
34084
- const make$69 = Effect.gen(function* () {
34759
+ const make$70 = Effect.gen(function* () {
34085
34760
  const pubsub = yield* PubSub.unbounded();
34086
34761
  const state = yield* Ref.make({
34087
34762
  sequence: 0,
@@ -34105,7 +34780,7 @@ const make$69 = Effect.gen(function* () {
34105
34780
  }
34106
34781
  };
34107
34782
  });
34108
- const layer$59 = Layer.effect(ServerLifecycleEvents, make$69);
34783
+ const layer$59 = Layer.effect(ServerLifecycleEvents, make$70);
34109
34784
  //#endregion
34110
34785
  //#region src/telemetry/Identify.ts
34111
34786
  const CodexAuthJsonSchema = Schema$1.Struct({ tokens: Schema$1.Struct({ account_id: Schema$1.String }) });
@@ -34278,7 +34953,7 @@ var AnalyticsService = class AnalyticsService extends Context.Service()("@p4code
34278
34953
  /** No-op layer for callers that intentionally disable telemetry. */
34279
34954
  static layerTest = Layer.succeed(AnalyticsService, inert);
34280
34955
  };
34281
- const make$68 = Effect.gen(function* () {
34956
+ const make$69 = Effect.gen(function* () {
34282
34957
  const telemetryConfig = yield* TelemetryEnvConfig;
34283
34958
  const posthogKey = telemetryConfig.posthogKey.trim();
34284
34959
  if (!telemetryConfig.enabled || posthogKey === "") return inert;
@@ -34348,7 +35023,7 @@ const make$68 = Effect.gen(function* () {
34348
35023
  flush
34349
35024
  });
34350
35025
  });
34351
- const layer$58 = Layer.effect(AnalyticsService, make$68);
35026
+ const layer$58 = Layer.effect(AnalyticsService, make$69);
34352
35027
  AnalyticsService.layerTest;
34353
35028
  //#endregion
34354
35029
  //#region src/service/pinnedRuntime.ts
@@ -34695,7 +35370,7 @@ var BootServiceInstallError = class extends Schema$1.TaggedErrorClass()("BootSer
34695
35370
  }
34696
35371
  };
34697
35372
  var BootService = class extends Context.Service()("@p4code/cli/service/bootService") {};
34698
- const make$67 = Effect.fn("cloud.boot_service.make")(function* (input) {
35373
+ const make$68 = Effect.fn("cloud.boot_service.make")(function* (input) {
34699
35374
  const hostExecPath = yield* HostProcessExecutablePath;
34700
35375
  const hostArguments = yield* HostProcessArguments;
34701
35376
  const host = input.host ?? {
@@ -34917,7 +35592,7 @@ const make$67 = Effect.fn("cloud.boot_service.make")(function* (input) {
34917
35592
  logPath
34918
35593
  });
34919
35594
  });
34920
- const layer$57 = (input) => Layer.effect(BootService, make$67(input));
35595
+ const layer$57 = (input) => Layer.effect(BootService, make$68(input));
34921
35596
  //#endregion
34922
35597
  //#region src/service/selfUpdate.ts
34923
35598
  /**
@@ -34992,7 +35667,7 @@ const resolveServerSelfUpdateCapability = Effect.fn("cloud.server_self_update.re
34992
35667
  return null;
34993
35668
  });
34994
35669
  var ServerSelfUpdate = class extends Context.Service()("@p4code/cli/service/selfUpdate/ServerSelfUpdate") {};
34995
- const make$66 = Effect.fn("cloud.server_self_update.make")(function* (options) {
35670
+ const make$67 = Effect.fn("cloud.server_self_update.make")(function* (options) {
34996
35671
  const serverConfig = yield* ServerConfig$1;
34997
35672
  const fs = yield* FileSystem.FileSystem;
34998
35673
  const path = yield* Path.Path;
@@ -35142,7 +35817,7 @@ const make$66 = Effect.fn("cloud.server_self_update.make")(function* (options) {
35142
35817
  });
35143
35818
  return ServerSelfUpdate.of({ update });
35144
35819
  });
35145
- const layer$56 = Layer.effect(ServerSelfUpdate, make$66()).pipe(Layer.provide(layer$63));
35820
+ const layer$56 = Layer.effect(ServerSelfUpdate, make$67()).pipe(Layer.provide(layer$63));
35146
35821
  //#endregion
35147
35822
  //#region src/environment/ServerEnvironmentLabel.ts
35148
35823
  const ServerEnvironmentLabelCommandProbe = Schema$1.Literals(["macos-computer-name", "linux-pretty-hostname"]);
@@ -35274,7 +35949,7 @@ function platformArch(architecture) {
35274
35949
  default: return "other";
35275
35950
  }
35276
35951
  }
35277
- const make$65 = Effect.gen(function* () {
35952
+ const make$66 = Effect.gen(function* () {
35278
35953
  const fileSystem = yield* FileSystem.FileSystem;
35279
35954
  const path = yield* Path.Path;
35280
35955
  const serverConfig = yield* ServerConfig$1;
@@ -35325,6 +36000,8 @@ const make$65 = Effect.gen(function* () {
35325
36000
  threadSettlement: true,
35326
36001
  threadSnooze: true,
35327
36002
  threadPinning: true,
36003
+ threadFork: true,
36004
+ threadScheduledTasks: true,
35328
36005
  ...serverSelfUpdate === null ? {} : { serverSelfUpdate },
35329
36006
  ...serverServiceSupervised ? { serverServiceSupervised } : {}
35330
36007
  }
@@ -35339,7 +36016,7 @@ const make$65 = Effect.gen(function* () {
35339
36016
  * state. It intentionally has no fallback Layer.succeed value: callers must
35340
36017
  * provide the external platform services and a ServerConfig.
35341
36018
  */
35342
- const layer$55 = Layer.effect(ServerEnvironment, make$65).pipe(Layer.provide(layer$63));
36019
+ const layer$55 = Layer.effect(ServerEnvironment, make$66).pipe(Layer.provide(layer$63));
35343
36020
  //#endregion
35344
36021
  //#region src/provider/Services/ProviderSessionReaper.ts
35345
36022
  var ProviderSessionReaper = class extends Context.Service()("@p4code/cli/provider/Services/ProviderSessionReaper") {};
@@ -35481,7 +36158,7 @@ const maybeOpenBrowser = (target) => Effect.gen(function* () {
35481
36158
  yield* (yield* ExternalLauncher).launchBrowser(target).pipe(Effect.catch(() => Effect.logInfo("browser auto-open unavailable", { hint: `Open ${target} in your browser.` })));
35482
36159
  });
35483
36160
  const runStartupPhase = (phase, effect) => effect.pipe(Effect.annotateSpans({ "startup.phase": phase }), Effect.withSpan(`server.startup.${phase}`));
35484
- const make$64 = Effect.gen(function* () {
36161
+ const make$65 = Effect.gen(function* () {
35485
36162
  const serverConfig = yield* ServerConfig$1;
35486
36163
  const keybindings = yield* Keybindings;
35487
36164
  const orchestrationReactor = yield* OrchestrationReactor;
@@ -35622,7 +36299,7 @@ const make$64 = Effect.gen(function* () {
35622
36299
  enqueueCommand: commandGate.enqueueCommand
35623
36300
  };
35624
36301
  });
35625
- const layer$54 = Layer.effect(ServerRuntimeStartup, make$64);
36302
+ const layer$54 = Layer.effect(ServerRuntimeStartup, make$65);
35626
36303
  //#endregion
35627
36304
  //#region src/serverRuntimeState.ts
35628
36305
  const PersistedServerRuntimeState = Schema$1.Struct({
@@ -35779,7 +36456,7 @@ function expandHomePath$2(input, path) {
35779
36456
  if (input.startsWith("~/") || input.startsWith("~\\")) return path.join(NodeOS.homedir(), input.slice(2));
35780
36457
  return input;
35781
36458
  }
35782
- const make$63 = Effect.gen(function* () {
36459
+ const make$64 = Effect.gen(function* () {
35783
36460
  const fileSystem = yield* FileSystem.FileSystem;
35784
36461
  const path = yield* Path.Path;
35785
36462
  const statWorkspaceRoot = Effect.fn("WorkspacePaths.statWorkspaceRoot")(function* (workspaceRoot, normalizedWorkspaceRoot, phase) {
@@ -35836,7 +36513,7 @@ const make$63 = Effect.gen(function* () {
35836
36513
  resolveRelativePathWithinRoot
35837
36514
  });
35838
36515
  });
35839
- const layer$53 = Layer.effect(WorkspacePaths, make$63);
36516
+ const layer$53 = Layer.effect(WorkspacePaths, make$64);
35840
36517
  //#endregion
35841
36518
  //#region src/cli/project.ts
35842
36519
  const isEnvironmentHttpCommonError = Schema$1.is(EnvironmentHttpCommonError);
@@ -37019,7 +37696,7 @@ const logP4ProjectFileLoadError = (error) => Effect.logWarning(error).pipe(Effec
37019
37696
  filePath: error.filePath,
37020
37697
  errorTag: error._tag
37021
37698
  }));
37022
- const make$62 = Effect.gen(function* () {
37699
+ const make$63 = Effect.gen(function* () {
37023
37700
  const fileSystem = yield* FileSystem.FileSystem;
37024
37701
  const path = yield* Path.Path;
37025
37702
  const load = Effect.fn("P4ProjectFileLoader.load")(function* (workspaceRoot) {
@@ -37040,7 +37717,7 @@ const make$62 = Effect.gen(function* () {
37040
37717
  });
37041
37718
  return P4ProjectFileLoader.of({ load });
37042
37719
  });
37043
- const layer$52 = Layer.effect(P4ProjectFileLoader, make$62);
37720
+ const layer$52 = Layer.effect(P4ProjectFileLoader, make$63);
37044
37721
  //#endregion
37045
37722
  //#region src/project/ProjectFaviconResolver.ts
37046
37723
  /**
@@ -37115,7 +37792,7 @@ function extractIconHref(source) {
37115
37792
  return null;
37116
37793
  }
37117
37794
  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) }));
37118
- const make$61 = Effect.gen(function* () {
37795
+ const make$62 = Effect.gen(function* () {
37119
37796
  const fileSystem = yield* FileSystem.FileSystem;
37120
37797
  const path = yield* Path.Path;
37121
37798
  const workspacePaths = yield* WorkspacePaths;
@@ -37184,7 +37861,7 @@ const make$61 = Effect.gen(function* () {
37184
37861
  });
37185
37862
  return ProjectFaviconResolver.of({ resolvePath });
37186
37863
  });
37187
- const layer$51 = Layer.effect(ProjectFaviconResolver, make$61);
37864
+ const layer$51 = Layer.effect(ProjectFaviconResolver, make$62);
37188
37865
  //#endregion
37189
37866
  //#region src/assets/AssetAccess.ts
37190
37867
  const ASSET_ROUTE_PREFIX = "/api/assets";
@@ -37595,10 +38272,10 @@ const resolveAsset = Effect.fn("AssetAccess.resolveAsset")(function* (token, rel
37595
38272
  //#endregion
37596
38273
  //#region src/observability/BrowserTraceCollector.ts
37597
38274
  var BrowserTraceCollector = class extends Context.Service()("@p4code/cli/observability/BrowserTraceCollector") {};
37598
- const make$60 = (sink) => BrowserTraceCollector.of({ record: (records) => Effect.sync(() => {
38275
+ const make$61 = (sink) => BrowserTraceCollector.of({ record: (records) => Effect.sync(() => {
37599
38276
  for (const record of records) sink.push(record);
37600
38277
  }) });
37601
- const layer$50 = (sink) => Layer.succeed(BrowserTraceCollector, make$60(sink));
38278
+ const layer$50 = (sink) => Layer.succeed(BrowserTraceCollector, make$61(sink));
37602
38279
  //#endregion
37603
38280
  //#region src/auth/http.ts
37604
38281
  const CREDENTIAL_RESPONSE_HEADERS = {
@@ -40651,7 +41328,7 @@ const classifyNonZeroExit = (command, stderr) => {
40651
41328
  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";
40652
41329
  return "command-failed";
40653
41330
  };
40654
- const make$59 = Effect.gen(function* () {
41331
+ const make$60 = Effect.gen(function* () {
40655
41332
  const processRunner = yield* ProcessRunner;
40656
41333
  const run = Effect.fn("VcsProcess.run")(function* (input) {
40657
41334
  const baseError = {
@@ -40710,7 +41387,7 @@ const make$59 = Effect.gen(function* () {
40710
41387
  });
40711
41388
  return VcsProcess.of({ run });
40712
41389
  });
40713
- const layer$49 = Layer.effect(VcsProcess, make$59).pipe(Layer.provide(layer$63));
41390
+ const layer$49 = Layer.effect(VcsProcess, make$60).pipe(Layer.provide(layer$63));
40714
41391
  //#endregion
40715
41392
  //#region src/vcs/VcsDriver.ts
40716
41393
  var VcsDriver = class extends Context.Service()("@p4code/cli/vcs/VcsDriver") {};
@@ -41161,12 +41838,12 @@ const makeVcsDriver = Effect.gen(function* () {
41161
41838
  const driver = yield* makeVcsDriverShape();
41162
41839
  return VcsDriver.of(driver);
41163
41840
  });
41164
- const make$58 = Effect.gen(function* () {
41841
+ const make$59 = Effect.gen(function* () {
41165
41842
  const git = yield* makeGitVcsDriverCore();
41166
41843
  return GitVcsDriver.of(git);
41167
41844
  });
41168
41845
  Layer.effect(VcsDriver, makeVcsDriver);
41169
- const layer$48 = Layer.effect(GitVcsDriver, make$58);
41846
+ const layer$48 = Layer.effect(GitVcsDriver, make$59);
41170
41847
  //#endregion
41171
41848
  //#region src/vcs/VcsProjectConfig.ts
41172
41849
  const ProjectVcsConfigJson = fromLenientJson(Schema$1.Struct({
@@ -41198,7 +41875,7 @@ const logVcsProjectConfigError = (error) => Effect.logWarning(error).pipe(Effect
41198
41875
  configPath: error.configPath,
41199
41876
  errorTag: error._tag
41200
41877
  }));
41201
- const make$57 = Effect.gen(function* () {
41878
+ const make$58 = Effect.gen(function* () {
41202
41879
  const fileSystem = yield* FileSystem.FileSystem;
41203
41880
  const path = yield* Path.Path;
41204
41881
  const findConfigPath = Effect.fn("VcsProjectConfig.findConfigPath")(function* (cwd) {
@@ -41239,7 +41916,7 @@ const make$57 = Effect.gen(function* () {
41239
41916
  });
41240
41917
  return VcsProjectConfig.of({ resolveKind });
41241
41918
  });
41242
- const layer$47 = Layer.effect(VcsProjectConfig, make$57);
41919
+ const layer$47 = Layer.effect(VcsProjectConfig, make$58);
41243
41920
  //#endregion
41244
41921
  //#region src/vcs/VcsDriverRegistry.ts
41245
41922
  const DETECTION_CACHE_CAPACITY = 2048;
@@ -41259,7 +41936,7 @@ function parseDetectionCacheKey(key) {
41259
41936
  cwd: key.slice(separatorIndex + 1)
41260
41937
  };
41261
41938
  }
41262
- const make$56 = Effect.gen(function* () {
41939
+ const make$57 = Effect.gen(function* () {
41263
41940
  const projectConfig = yield* VcsProjectConfig;
41264
41941
  const git = yield* makeVcsDriver;
41265
41942
  const drivers = { git };
@@ -41316,7 +41993,7 @@ const make$56 = Effect.gen(function* () {
41316
41993
  resolve
41317
41994
  });
41318
41995
  });
41319
- const layer$46 = Layer.effect(VcsDriverRegistry, make$56).pipe(Layer.provide(layer$47));
41996
+ const layer$46 = Layer.effect(VcsDriverRegistry, make$57).pipe(Layer.provide(layer$47));
41320
41997
  //#endregion
41321
41998
  //#region src/checkpointing/CheckpointStore.ts
41322
41999
  /**
@@ -41336,7 +42013,7 @@ const layer$46 = Layer.effect(VcsDriverRegistry, make$56).pipe(Layer.provide(lay
41336
42013
  */
41337
42014
  /** Service tag for checkpoint persistence and restore operations. */
41338
42015
  var CheckpointStore = class extends Context.Service()("@p4code/cli/checkpointing/CheckpointStore") {};
41339
- const make$55 = Effect.gen(function* () {
42016
+ const make$56 = Effect.gen(function* () {
41340
42017
  const vcsRegistry = yield* VcsDriverRegistry;
41341
42018
  const resolveCheckpoints = Effect.fn("CheckpointStore.resolveCheckpoints")(function* (operation, cwd) {
41342
42019
  const handle = yield* vcsRegistry.resolve({ cwd });
@@ -41375,7 +42052,7 @@ const make$55 = Effect.gen(function* () {
41375
42052
  deleteCheckpointRefs
41376
42053
  });
41377
42054
  });
41378
- const layer$45 = Layer.effect(CheckpointStore, make$55);
42055
+ const layer$45 = Layer.effect(CheckpointStore, make$56);
41379
42056
  //#endregion
41380
42057
  //#region src/checkpointing/CheckpointDiffQuery.ts
41381
42058
  /**
@@ -41397,7 +42074,7 @@ function buildTurnDiffResult(input, diff) {
41397
42074
  diff
41398
42075
  };
41399
42076
  }
41400
- const make$54 = Effect.gen(function* () {
42077
+ const make$55 = Effect.gen(function* () {
41401
42078
  const projectionSnapshotQuery = yield* ProjectionSnapshotQuery;
41402
42079
  const checkpointStore = yield* CheckpointStore;
41403
42080
  const threadActivities = yield* ProjectionThreadActivityRepository;
@@ -41568,7 +42245,7 @@ const make$54 = Effect.gen(function* () {
41568
42245
  getFullThreadDiff
41569
42246
  });
41570
42247
  });
41571
- const layer$44 = Layer.effect(CheckpointDiffQuery, make$54);
42248
+ const layer$44 = Layer.effect(CheckpointDiffQuery, make$55);
41572
42249
  //#endregion
41573
42250
  //#region src/orchestration/Normalizer.ts
41574
42251
  const canonicalizeClientCommandTimestamps = (command, receivedAt) => {
@@ -41683,11 +42360,11 @@ const makeTextGenerationFromRegistry = (registry) => TextGeneration.of({
41683
42360
  detail: "This provider does not report account usage."
41684
42361
  }))))
41685
42362
  });
41686
- const make$53 = Effect.gen(function* () {
42363
+ const make$54 = Effect.gen(function* () {
41687
42364
  const registry = yield* ProviderInstanceRegistry;
41688
42365
  return makeTextGenerationFromRegistry(registry);
41689
42366
  });
41690
- const layer$43 = Layer.effect(TextGeneration, make$53);
42367
+ const layer$43 = Layer.effect(TextGeneration, make$54);
41691
42368
  //#endregion
41692
42369
  //#region src/textGeneration/TextGenerationPresets.ts
41693
42370
  const conventionalCommitsTextGenerationPolicy = {
@@ -42019,7 +42696,7 @@ const serversEqual = (left, right) => {
42019
42696
  }
42020
42697
  return true;
42021
42698
  };
42022
- const make$52 = Effect.gen(function* PortDiscoveryMake() {
42699
+ const make$53 = Effect.gen(function* PortDiscoveryMake() {
42023
42700
  const net = yield* NetService;
42024
42701
  const processRunner = yield* ProcessRunner;
42025
42702
  const hostPlatform = yield* HostProcessPlatform;
@@ -42170,7 +42847,7 @@ const make$52 = Effect.gen(function* PortDiscoveryMake() {
42170
42847
  unregisterTerminal
42171
42848
  });
42172
42849
  }).pipe(Effect.withSpan("PortDiscovery.make"));
42173
- const layer$42 = Layer.effect(PortDiscovery, make$52);
42850
+ const layer$42 = Layer.effect(PortDiscovery, make$53);
42174
42851
  //#endregion
42175
42852
  //#region src/terminal/Manager.ts
42176
42853
  /**
@@ -42848,7 +43525,7 @@ function normalizedRuntimeEnv(env) {
42848
43525
  if (entries.length === 0) return null;
42849
43526
  return Object.fromEntries(entries.toSorted(([left], [right]) => left.localeCompare(right)));
42850
43527
  }
42851
- const make$51 = Effect.fn("TerminalManager.make")(function* () {
43528
+ const make$52 = Effect.fn("TerminalManager.make")(function* () {
42852
43529
  const { terminalLogsDir } = yield* ServerConfig$1;
42853
43530
  const ptyAdapter = yield* PtyAdapter;
42854
43531
  const portDiscovery = yield* PortDiscovery;
@@ -43810,7 +44487,7 @@ const makeWithOptions$1 = Effect.fn("TerminalManager.makeWithOptions")(function*
43810
44487
  subscribeMetadata
43811
44488
  });
43812
44489
  });
43813
- const layer$41 = Layer.effect(TerminalManager, make$51()).pipe(Layer.provide(layer$63));
44490
+ const layer$41 = Layer.effect(TerminalManager, make$52()).pipe(Layer.provide(layer$63));
43814
44491
  //#endregion
43815
44492
  //#region src/project/ProjectSetupScriptRunner.ts
43816
44493
  var ProjectSetupScriptOperationError = class extends Schema$1.TaggedErrorClass()("ProjectSetupScriptOperationError", {
@@ -43841,7 +44518,7 @@ var ProjectSetupScriptProjectNotFoundError = class extends Schema$1.TaggedErrorC
43841
44518
  };
43842
44519
  Schema$1.Union([ProjectSetupScriptOperationError, ProjectSetupScriptProjectNotFoundError]);
43843
44520
  var ProjectSetupScriptRunner = class extends Context.Service()("@p4code/cli/project/ProjectSetupScriptRunner") {};
43844
- const make$50 = Effect.gen(function* () {
44521
+ const make$51 = Effect.gen(function* () {
43845
44522
  const projectionSnapshotQuery = yield* ProjectionSnapshotQuery;
43846
44523
  const terminalManager = yield* TerminalManager;
43847
44524
  const runForThread = Effect.fn("ProjectSetupScriptRunner.runForThread")(function* (input) {
@@ -43899,7 +44576,7 @@ const make$50 = Effect.gen(function* () {
43899
44576
  });
43900
44577
  return ProjectSetupScriptRunner.of({ runForThread });
43901
44578
  });
43902
- const layer$40 = Layer.effect(ProjectSetupScriptRunner, make$50);
44579
+ const layer$40 = Layer.effect(ProjectSetupScriptRunner, make$51);
43903
44580
  //#endregion
43904
44581
  //#region src/provider/Services/ProviderRegistry.ts
43905
44582
  var ProviderRegistry = class extends Context.Service()("@p4code/cli/provider/Services/ProviderRegistry") {};
@@ -44226,7 +44903,7 @@ function decodeAzureDevOpsJson(raw, schema, operation, cwd) {
44226
44903
  cause
44227
44904
  })));
44228
44905
  }
44229
- const make$49 = Effect.gen(function* () {
44906
+ const make$50 = Effect.gen(function* () {
44230
44907
  const process = yield* VcsProcess;
44231
44908
  const execute = (input) => process.run({
44232
44909
  operation: "AzureDevOpsCli.execute",
@@ -44368,7 +45045,7 @@ const make$49 = Effect.gen(function* () {
44368
45045
  }).pipe(Effect.asVoid)
44369
45046
  });
44370
45047
  });
44371
- const layer$39 = Layer.effect(AzureDevOpsCli, make$49);
45048
+ const layer$39 = Layer.effect(AzureDevOpsCli, make$50);
44372
45049
  //#endregion
44373
45050
  //#region src/sourceControl/SourceControlProviderDiscovery.ts
44374
45051
  function firstNonEmptyLine(text) {
@@ -44571,7 +45248,7 @@ function toChangeRequest$5(summary) {
44571
45248
  isCrossRepository: false
44572
45249
  };
44573
45250
  }
44574
- const make$48 = Effect.gen(function* () {
45251
+ const make$49 = Effect.gen(function* () {
44575
45252
  const azure = yield* AzureDevOpsCli;
44576
45253
  return SourceControlProvider.of({
44577
45254
  kind: "azure-devops",
@@ -44663,7 +45340,7 @@ const make$48 = Effect.gen(function* () {
44663
45340
  })))
44664
45341
  });
44665
45342
  });
44666
- Layer.effect(SourceControlProvider, make$48);
45343
+ Layer.effect(SourceControlProvider, make$49);
44667
45344
  //#endregion
44668
45345
  //#region src/sourceControl/bitbucketPullRequests.ts
44669
45346
  const BitbucketRepositoryRefSchema = Schema$1.Struct({
@@ -45040,7 +45717,7 @@ function responseError(operation, response) {
45040
45717
  responseBodyLength: collected.text.length
45041
45718
  }))));
45042
45719
  }
45043
- const make$47 = Effect.gen(function* () {
45720
+ const make$48 = Effect.gen(function* () {
45044
45721
  const config = yield* BitbucketApiEnvConfig;
45045
45722
  const httpClient = yield* HttpClient.HttpClient;
45046
45723
  const fileSystem = yield* FileSystem.FileSystem;
@@ -45256,7 +45933,7 @@ const make$47 = Effect.gen(function* () {
45256
45933
  })))
45257
45934
  });
45258
45935
  });
45259
- const layer$37 = Layer.effect(BitbucketApi, make$47);
45936
+ const layer$37 = Layer.effect(BitbucketApi, make$48);
45260
45937
  //#endregion
45261
45938
  //#region src/sourceControl/BitbucketSourceControlProvider.ts
45262
45939
  function toChangeRequest$4(summary) {
@@ -45274,7 +45951,7 @@ function toChangeRequest$4(summary) {
45274
45951
  ...summary.headRepositoryOwnerLogin !== void 0 ? { headRepositoryOwnerLogin: summary.headRepositoryOwnerLogin } : {}
45275
45952
  };
45276
45953
  }
45277
- const make$46 = Effect.gen(function* () {
45954
+ const make$47 = Effect.gen(function* () {
45278
45955
  const bitbucket = yield* BitbucketApi;
45279
45956
  return SourceControlProvider.of({
45280
45957
  kind: "bitbucket",
@@ -45365,7 +46042,7 @@ const make$46 = Effect.gen(function* () {
45365
46042
  })))
45366
46043
  });
45367
46044
  });
45368
- Layer.effect(SourceControlProvider, make$46);
46045
+ Layer.effect(SourceControlProvider, make$47);
45369
46046
  const makeDiscovery = Effect.gen(function* () {
45370
46047
  return {
45371
46048
  type: "api",
@@ -45607,7 +46284,7 @@ function deriveRepositoryCloneUrlsFromCreateOutput(stdout, repository) {
45607
46284
  sshUrl: `git@${fallbackHost}:${repository}.git`
45608
46285
  };
45609
46286
  }
45610
- const make$45 = Effect.gen(function* () {
46287
+ const make$46 = Effect.gen(function* () {
45611
46288
  const process = yield* VcsProcess;
45612
46289
  const execute = (input) => process.run({
45613
46290
  operation: "GitHubCli.execute",
@@ -45725,7 +46402,7 @@ const make$45 = Effect.gen(function* () {
45725
46402
  }).pipe(Effect.asVoid)
45726
46403
  });
45727
46404
  });
45728
- const layer$35 = Layer.effect(GitHubCli, make$45);
46405
+ const layer$35 = Layer.effect(GitHubCli, make$46);
45729
46406
  //#endregion
45730
46407
  //#region src/sourceControl/gitHubAuthStatus.ts
45731
46408
  const GitHubAuthStatusAccountSchema = Schema$1.Struct({
@@ -45826,7 +46503,7 @@ const discovery$1 = {
45826
46503
  parseAuth: parseGitHubAuth,
45827
46504
  installHint: "Install the GitHub command-line tool (`gh`) via https://cli.github.com/ or your package manager (for example `brew install gh`)."
45828
46505
  };
45829
- const make$44 = Effect.gen(function* () {
46506
+ const make$45 = Effect.gen(function* () {
45830
46507
  const github = yield* GitHubCli;
45831
46508
  const listChangeRequests = (input) => {
45832
46509
  if (input.state === "open") return github.listOpenPullRequests({
@@ -45942,7 +46619,7 @@ const make$44 = Effect.gen(function* () {
45942
46619
  })))
45943
46620
  });
45944
46621
  });
45945
- Layer.effect(SourceControlProvider, make$44);
46622
+ Layer.effect(SourceControlProvider, make$45);
45946
46623
  //#endregion
45947
46624
  //#region src/sourceControl/gitLabMergeRequests.ts
45948
46625
  const GitLabProjectReferenceSchema = Schema$1.Struct({
@@ -46254,7 +46931,7 @@ function parseRepositoryPath(repository) {
46254
46931
  projectPath
46255
46932
  };
46256
46933
  }
46257
- const make$43 = Effect.gen(function* () {
46934
+ const make$44 = Effect.gen(function* () {
46258
46935
  const process = yield* VcsProcess;
46259
46936
  const run = (input, mapError) => process.run({
46260
46937
  operation: "GitLabCli.execute",
@@ -46405,7 +47082,7 @@ const make$43 = Effect.gen(function* () {
46405
47082
  }).pipe(Effect.asVoid)
46406
47083
  });
46407
47084
  });
46408
- const layer$33 = Layer.effect(GitLabCli, make$43);
47085
+ const layer$33 = Layer.effect(GitLabCli, make$44);
46409
47086
  //#endregion
46410
47087
  //#region src/sourceControl/gitLabAuthStatus.ts
46411
47088
  const HOST_LINE_PATTERN = /^(?:[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?|\[[a-f0-9:.]+\])(?::\d+)?$/iu;
@@ -46502,7 +47179,7 @@ const discovery = {
46502
47179
  refineUnknownRemote: refineUnknownGitLabRemote,
46503
47180
  installHint: "Install the GitLab command-line tool (`glab`) from https://gitlab.com/gitlab-org/cli or your package manager (for example `brew install glab`)."
46504
47181
  };
46505
- const make$42 = Effect.gen(function* () {
47182
+ const make$43 = Effect.gen(function* () {
46506
47183
  const gitlab = yield* GitLabCli;
46507
47184
  return SourceControlProvider.of({
46508
47185
  kind: "gitlab",
@@ -46590,7 +47267,7 @@ const make$42 = Effect.gen(function* () {
46590
47267
  })))
46591
47268
  });
46592
47269
  });
46593
- Layer.effect(SourceControlProvider, make$42);
47270
+ Layer.effect(SourceControlProvider, make$43);
46594
47271
  //#endregion
46595
47272
  //#region src/sourceControl/SourceControlProviderRegistry.ts
46596
47273
  const PROVIDER_DETECTION_CACHE_CAPACITY = 2048;
@@ -46746,12 +47423,12 @@ const makeWithProviders = Effect.fn("makeSourceControlProviderRegistryWithProvid
46746
47423
  })), { concurrency: "unbounded" })
46747
47424
  });
46748
47425
  });
46749
- const make$41 = Effect.gen(function* () {
46750
- const github = yield* make$44;
46751
- const gitlab = yield* make$42;
46752
- const bitbucket = yield* make$46;
47426
+ const make$42 = Effect.gen(function* () {
47427
+ const github = yield* make$45;
47428
+ const gitlab = yield* make$43;
47429
+ const bitbucket = yield* make$47;
46753
47430
  const bitbucketDiscovery = yield* makeDiscovery;
46754
- const azureDevOps = yield* make$48;
47431
+ const azureDevOps = yield* make$49;
46755
47432
  return yield* makeWithProviders([
46756
47433
  {
46757
47434
  kind: "github",
@@ -46775,7 +47452,7 @@ const make$41 = Effect.gen(function* () {
46775
47452
  }
46776
47453
  ]);
46777
47454
  });
46778
- const layer$31 = Layer.effect(SourceControlProviderRegistry, make$41);
47455
+ const layer$31 = Layer.effect(SourceControlProviderRegistry, make$42);
46779
47456
  //#endregion
46780
47457
  //#region src/sourceControl/PrTemplateDetection.ts
46781
47458
  const TEMPLATE_MAX_BYTES = 8e3;
@@ -47145,7 +47822,7 @@ function toPullRequestHeadRemoteInfo(pr) {
47145
47822
  ...pr.headRepositoryOwnerLogin !== void 0 ? { headRepositoryOwnerLogin: pr.headRepositoryOwnerLogin } : {}
47146
47823
  };
47147
47824
  }
47148
- const make$40 = Effect.gen(function* () {
47825
+ const make$41 = Effect.gen(function* () {
47149
47826
  const gitCore = yield* GitVcsDriver;
47150
47827
  const sourceControlProviders = yield* SourceControlProviderRegistry;
47151
47828
  const textGeneration = yield* TextGeneration;
@@ -48067,7 +48744,7 @@ const make$40 = Effect.gen(function* () {
48067
48744
  runStackedAction
48068
48745
  });
48069
48746
  });
48070
- const layer$30 = Layer.effect(GitManager, make$40);
48747
+ const layer$30 = Layer.effect(GitManager, make$41);
48071
48748
  //#endregion
48072
48749
  //#region src/git/GitWorkflowService.ts
48073
48750
  var GitWorkflowService = class extends Context.Service()("@p4code/cli/git/GitWorkflowService") {};
@@ -48104,7 +48781,7 @@ function nonRepositoryListRefs() {
48104
48781
  totalCount: 0
48105
48782
  };
48106
48783
  }
48107
- const make$39 = Effect.gen(function* () {
48784
+ const make$40 = Effect.gen(function* () {
48108
48785
  const registry = yield* VcsDriverRegistry;
48109
48786
  const git = yield* GitVcsDriver;
48110
48787
  const gitManager = yield* GitManager;
@@ -48190,7 +48867,7 @@ const make$39 = Effect.gen(function* () {
48190
48867
  renameBranch: (input) => ensureGit("GitWorkflowService.renameBranch", input.cwd).pipe(Effect.andThen(git.renameBranch(input)))
48191
48868
  });
48192
48869
  });
48193
- const layer$29 = Layer.effect(GitWorkflowService, make$39);
48870
+ const layer$29 = Layer.effect(GitWorkflowService, make$40);
48194
48871
  //#endregion
48195
48872
  //#region src/pullRequest/PullRequestProvider.ts
48196
48873
  /**
@@ -48551,7 +49228,7 @@ function isReviewerName(value) {
48551
49228
  const name = value.trim();
48552
49229
  return name.length > 0 && !name.startsWith("-");
48553
49230
  }
48554
- const make$38 = Effect.gen(function* () {
49231
+ const make$39 = Effect.gen(function* () {
48555
49232
  const azure = yield* AzureDevOpsCli;
48556
49233
  const detectArgs = ["--detect", "true"];
48557
49234
  const executeJson = (input) => azure.execute({
@@ -48751,7 +49428,7 @@ const make$38 = Effect.gen(function* () {
48751
49428
  }).pipe(Effect.asVoid)
48752
49429
  });
48753
49430
  });
48754
- const layer$28 = Layer.effect(AzureDevOpsPullRequestCli, make$38);
49431
+ const layer$28 = Layer.effect(AzureDevOpsPullRequestCli, make$39);
48755
49432
  //#endregion
48756
49433
  //#region src/pullRequest/AzureDevOpsPullRequestProvider.ts
48757
49434
  const CAPABILITIES$3 = {
@@ -48826,7 +49503,7 @@ function toChangeRequest$1(pullRequest) {
48826
49503
  labels: []
48827
49504
  };
48828
49505
  }
48829
- const make$37 = Effect.gen(function* () {
49506
+ const make$38 = Effect.gen(function* () {
48830
49507
  const cli = yield* AzureDevOpsPullRequestCli;
48831
49508
  const fail = (operation) => (error) => new PullRequestProviderError({
48832
49509
  provider: "azure-devops",
@@ -49566,7 +50243,7 @@ function mergeStrategy(method) {
49566
50243
  default: return "merge_commit";
49567
50244
  }
49568
50245
  }
49569
- const make$36 = Effect.gen(function* () {
50246
+ const make$37 = Effect.gen(function* () {
49570
50247
  const bitbucket = yield* BitbucketApi;
49571
50248
  /**
49572
50249
  * The repository's own path, and the workspace above it — which the people who may review are
@@ -49846,7 +50523,7 @@ const make$36 = Effect.gen(function* () {
49846
50523
  }).pipe(Effect.asVoid))
49847
50524
  });
49848
50525
  });
49849
- const layer$27 = Layer.effect(BitbucketPullRequestApi, make$36);
50526
+ const layer$27 = Layer.effect(BitbucketPullRequestApi, make$37);
49850
50527
  //#endregion
49851
50528
  //#region src/pullRequest/BitbucketPullRequestProvider.ts
49852
50529
  const CAPABILITIES$2 = {
@@ -49926,7 +50603,7 @@ function toChangeRequest(pullRequest) {
49926
50603
  labels: []
49927
50604
  };
49928
50605
  }
49929
- const make$35 = Effect.gen(function* () {
50606
+ const make$36 = Effect.gen(function* () {
49930
50607
  const api = yield* BitbucketPullRequestApi;
49931
50608
  const fail = (operation) => (error) => new PullRequestProviderError({
49932
50609
  provider: "bitbucket",
@@ -51813,7 +52490,7 @@ function actionArgs$1(action, mergeMethod, updateMethod) {
51813
52490
  case "reopen": return ["reopen"];
51814
52491
  }
51815
52492
  }
51816
- const make$34 = Effect.gen(function* () {
52493
+ const make$35 = Effect.gen(function* () {
51817
52494
  const github = yield* GitHubCli;
51818
52495
  /**
51819
52496
  * The pull request's own node id, which is what a mutation against the pull request itself is
@@ -52533,7 +53210,7 @@ const make$34 = Effect.gen(function* () {
52533
53210
  })))
52534
53211
  });
52535
53212
  });
52536
- const layer$26 = Layer.effect(GitHubPullRequestCli, make$34);
53213
+ const layer$26 = Layer.effect(GitHubPullRequestCli, make$35);
52537
53214
  //#endregion
52538
53215
  //#region src/pullRequest/GitHubPullRequestProvider.ts
52539
53216
  const CAPABILITIES$1 = {
@@ -52648,7 +53325,7 @@ function loginAvatarUrl(login, host) {
52648
53325
  }
52649
53326
  /** True where markdown would render nothing: whitespace, or only HTML comments. */
52650
53327
  const rendersEmpty = (body) => body.replace(/<!--[\s\S]*?-->/g, "").trim().length === 0;
52651
- const make$33 = Effect.gen(function* () {
53328
+ const make$34 = Effect.gen(function* () {
52652
53329
  const cli = yield* GitHubPullRequestCli;
52653
53330
  const fail = (operation) => (error) => new PullRequestProviderError({
52654
53331
  provider: "github",
@@ -53667,7 +54344,7 @@ function actionArgs(action, mergeMethod) {
53667
54344
  case "reopen": return ["reopen"];
53668
54345
  }
53669
54346
  }
53670
- const make$32 = Effect.gen(function* () {
54347
+ const make$33 = Effect.gen(function* () {
53671
54348
  const gitlab = yield* GitLabCli;
53672
54349
  const api = (input) => gitlab.execute({
53673
54350
  cwd: input.cwd,
@@ -54238,7 +54915,7 @@ const make$32 = Effect.gen(function* () {
54238
54915
  }).pipe(Effect.asVoid)
54239
54916
  });
54240
54917
  });
54241
- const layer$25 = Layer.effect(GitLabPullRequestCli, make$32);
54918
+ const layer$25 = Layer.effect(GitLabPullRequestCli, make$33);
54242
54919
  //#endregion
54243
54920
  //#region src/pullRequest/GitLabPullRequestProvider.ts
54244
54921
  const CAPABILITIES = {
@@ -54318,7 +54995,7 @@ function reasonFor(error) {
54318
54995
  if (error._tag === "GitLabCliAuthenticationError") return "unauthenticated";
54319
54996
  return "failed";
54320
54997
  }
54321
- const make$31 = Effect.gen(function* () {
54998
+ const make$32 = Effect.gen(function* () {
54322
54999
  const cli = yield* GitLabPullRequestCli;
54323
55000
  const fail = (operation) => (error) => new PullRequestProviderError({
54324
55001
  provider: "gitlab",
@@ -54461,13 +55138,13 @@ function fromProviders(providers) {
54461
55138
  * The hosts this build can read change requests from. A host with no entry here still shows up
54462
55139
  * in the provider list as unimplemented, so its projects are explained rather than missing.
54463
55140
  */
54464
- const make$30 = Effect.map(Effect.all([
54465
- make$33,
54466
- make$31,
54467
- make$35,
54468
- make$37
55141
+ const make$31 = Effect.map(Effect.all([
55142
+ make$34,
55143
+ make$32,
55144
+ make$36,
55145
+ make$38
54469
55146
  ]), fromProviders);
54470
- const layer$24 = Layer.effect(PullRequestProviderRegistry, make$30).pipe(Layer.provide(layer$26.pipe(Layer.provide(layer$35))), Layer.provide(layer$25.pipe(Layer.provide(layer$33))), Layer.provide(layer$27.pipe(Layer.provide(layer$37))), Layer.provide(layer$28.pipe(Layer.provide(layer$39))));
55147
+ const layer$24 = Layer.effect(PullRequestProviderRegistry, make$31).pipe(Layer.provide(layer$26.pipe(Layer.provide(layer$35))), Layer.provide(layer$25.pipe(Layer.provide(layer$33))), Layer.provide(layer$27.pipe(Layer.provide(layer$37))), Layer.provide(layer$28.pipe(Layer.provide(layer$39))));
54471
55148
  //#endregion
54472
55149
  //#region src/pullRequest/PullRequestService.ts
54473
55150
  /**
@@ -54655,7 +55332,7 @@ function repositoryIdentityOf(project) {
54655
55332
  if (identity.displayName) return identity.displayName;
54656
55333
  return identity.owner && identity.name ? `${identity.owner}/${identity.name}` : null;
54657
55334
  }
54658
- const make$29 = Effect.gen(function* () {
55335
+ const make$30 = Effect.gen(function* () {
54659
55336
  const registry = yield* PullRequestProviderRegistry;
54660
55337
  const projections = yield* ProjectionSnapshotQuery;
54661
55338
  const sourceControlProviders = yield* SourceControlProviderRegistry;
@@ -55625,7 +56302,7 @@ const make$29 = Effect.gen(function* () {
55625
56302
  invalidate
55626
56303
  });
55627
56304
  });
55628
- const layer$23 = Layer.effect(PullRequestService, make$29);
56305
+ const layer$23 = Layer.effect(PullRequestService, make$30);
55629
56306
  //#endregion
55630
56307
  //#region src/orchestration/ThreadWorkspaceLifecycle.ts
55631
56308
  var ThreadWorkspaceLifecycleError = class extends Data.TaggedError("ThreadWorkspaceLifecycleError") {};
@@ -55659,7 +56336,7 @@ const mapLifecycleError = Effect.mapError((cause) => cause instanceof ThreadWork
55659
56336
  detail: "Thread workspace lifecycle operation failed.",
55660
56337
  cause
55661
56338
  }));
55662
- const make$28 = Effect.gen(function* () {
56339
+ const make$29 = Effect.gen(function* () {
55663
56340
  const snapshots = yield* ProjectionSnapshotQuery;
55664
56341
  const engine = yield* OrchestrationEngineService;
55665
56342
  const gitWorkflow = yield* GitWorkflowService;
@@ -55840,7 +56517,7 @@ const make$28 = Effect.gen(function* () {
55840
56517
  record
55841
56518
  };
55842
56519
  });
55843
- const layer$22 = Layer.effect(ThreadWorkspaceLifecycleService, make$28);
56520
+ const layer$22 = Layer.effect(ThreadWorkspaceLifecycleService, make$29);
55844
56521
  //#endregion
55845
56522
  //#region src/textGeneration/BtwRequestCoordinator.ts
55846
56523
  const MAX_PENDING_BTW_CANCELLATIONS = 256;
@@ -57994,6 +58671,9 @@ const observeRpcStreamEffect = (method, effect, traceAttributes) => {
57994
58671
  return withRpcStreamTracing(method, instrumented, traceAttributes);
57995
58672
  };
57996
58673
  //#endregion
58674
+ //#region src/provider/Services/ProviderSessionDirectory.ts
58675
+ var ProviderSessionDirectory = class extends Context.Service()("@p4code/cli/provider/Services/ProviderSessionDirectory") {};
58676
+ //#endregion
57997
58677
  //#region src/provider/providerMaintenanceCommandCoordinator.ts
57998
58678
  const makeProviderMaintenanceCommandCoordinator = Effect.fn("makeProviderMaintenanceCommandCoordinator")(function* (input) {
57999
58679
  const runningTargetsRef = yield* Ref.make(/* @__PURE__ */ new Set());
@@ -58381,7 +59061,7 @@ function makeUpdateState(input) {
58381
59061
  output: input.output ?? null
58382
59062
  };
58383
59063
  }
58384
- const make$27 = Effect.fn("ProviderMaintenanceRunner.make")(function* () {
59064
+ const make$28 = Effect.fn("ProviderMaintenanceRunner.make")(function* () {
58385
59065
  const providerRegistry = yield* ProviderRegistry;
58386
59066
  const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
58387
59067
  const httpClient = yield* HttpClient.HttpClient;
@@ -58496,7 +59176,7 @@ const make$27 = Effect.fn("ProviderMaintenanceRunner.make")(function* () {
58496
59176
  });
58497
59177
  return ProviderMaintenanceRunner.of({ updateProvider });
58498
59178
  });
58499
- const layer$21 = Layer.effect(ProviderMaintenanceRunner, make$27());
59179
+ const layer$21 = Layer.effect(ProviderMaintenanceRunner, make$28());
58500
59180
  //#endregion
58501
59181
  //#region src/provider/Drivers/ClaudeHome.ts
58502
59182
  const resolveClaudeHomePath = Effect.fn("resolveClaudeHomePath")(function* (config) {
@@ -59512,7 +60192,7 @@ Layer.succeed(UsageService, UsageService.of({ readSummary: (input) => Effect.suc
59512
60192
  },
59513
60193
  scanDurationMs: 0
59514
60194
  }) }));
59515
- const make$26 = Effect.gen(function* () {
60195
+ const make$27 = Effect.gen(function* () {
59516
60196
  const fileSystem = yield* FileSystem.FileSystem;
59517
60197
  const path = yield* Path.Path;
59518
60198
  const config = yield* ServerConfig$1;
@@ -59744,7 +60424,7 @@ const make$26 = Effect.gen(function* () {
59744
60424
  };
59745
60425
  }) };
59746
60426
  });
59747
- const layer$20 = Layer.effect(UsageService, make$26);
60427
+ const layer$20 = Layer.effect(UsageService, make$27);
59748
60428
  //#endregion
59749
60429
  //#region src/feed/FeedStore.ts
59750
60430
  const storageFailure = (message) => new FeedError({
@@ -60108,7 +60788,7 @@ const jsonRequest = Effect.fn("FeedService.jsonRequest")(function* (url, token,
60108
60788
  catch: () => fail("hub_unavailable", "Hub returned invalid JSON.")
60109
60789
  });
60110
60790
  });
60111
- const make$25 = Effect.gen(function* () {
60791
+ const make$26 = Effect.gen(function* () {
60112
60792
  const hubLink = yield* HubLink;
60113
60793
  const providers = yield* ProviderInstanceRegistry;
60114
60794
  const config = yield* ServerConfig$1;
@@ -60346,7 +61026,7 @@ var FeedService = class extends Context.Reference("@p4code/cli/feed/FeedService"
60346
61026
  markRead: unavailable,
60347
61027
  cleanup: unavailable
60348
61028
  }) }) {};
60349
- const layer$19 = Layer.effect(FeedService, make$25);
61029
+ const layer$19 = Layer.effect(FeedService, make$26);
60350
61030
  const SKILL_MANIFEST_FILENAME = "SKILL.md";
60351
61031
  /**
60352
61032
  * Split a catalogue id (`owner/repo/skill-name`) into its parts.
@@ -60492,7 +61172,7 @@ const emptyFetch = (id, unavailable) => ({
60492
61172
  skipped: [],
60493
61173
  unavailable
60494
61174
  });
60495
- const make$24 = Effect.gen(function* () {
61175
+ const make$25 = Effect.gen(function* () {
60496
61176
  const http = yield* HttpClient.HttpClient;
60497
61177
  const request = Effect.fn("SkillRegistry.request")(function* (url) {
60498
61178
  return yield* http.execute(HttpClientRequest.get(url).pipe(HttpClientRequest.setHeader("accept", "application/json"), HttpClientRequest.setHeader("user-agent", "p4code"))).pipe(Effect.timeout(REQUEST_TIMEOUT_MS));
@@ -60563,7 +61243,7 @@ const make$24 = Effect.gen(function* () {
60563
61243
  fetch
60564
61244
  };
60565
61245
  });
60566
- const layer$18 = Layer.effect(SkillRegistry, make$24);
61246
+ const layer$18 = Layer.effect(SkillRegistry, make$25);
60567
61247
  //#endregion
60568
61248
  //#region src/mcp/McpInvocationContext.ts
60569
61249
  var McpInvocationContext = class extends Context.Service()("@p4code/cli/mcp/McpInvocationContext") {};
@@ -60791,7 +61471,7 @@ const classifyResponseError = (context, error) => {
60791
61471
  });
60792
61472
  }
60793
61473
  };
60794
- const make$23 = Effect.gen(function* PreviewAutomationBrokerMake() {
61474
+ const make$24 = Effect.gen(function* PreviewAutomationBrokerMake() {
60795
61475
  const crypto = yield* Crypto.Crypto;
60796
61476
  const state = yield* SynchronizedRef.make({
60797
61477
  clients: /* @__PURE__ */ new Map(),
@@ -61025,7 +61705,7 @@ const make$23 = Effect.gen(function* PreviewAutomationBrokerMake() {
61025
61705
  invoke
61026
61706
  });
61027
61707
  }).pipe(Effect.withSpan("PreviewAutomationBroker.make"));
61028
- const layer$17 = Layer.effect(PreviewAutomationBroker, make$23);
61708
+ const layer$17 = Layer.effect(PreviewAutomationBroker, make$24);
61029
61709
  //#endregion
61030
61710
  //#region src/preview/Manager.ts
61031
61711
  /**
@@ -61089,7 +61769,7 @@ const buildIdleSnapshot = (input) => ({
61089
61769
  viewport: FILL_PREVIEW_VIEWPORT,
61090
61770
  updatedAt: input.updatedAt
61091
61771
  });
61092
- const make$22 = Effect.gen(function* PreviewManagerMake() {
61772
+ const make$23 = Effect.gen(function* PreviewManagerMake() {
61093
61773
  const serverEpoch = NodeCrypto.randomUUID();
61094
61774
  const stateRef = yield* SynchronizedRef.make(initialState);
61095
61775
  const eventsPubSub = yield* PubSub.unbounded();
@@ -61320,7 +62000,7 @@ const make$22 = Effect.gen(function* PreviewManagerMake() {
61320
62000
  subscribeEvents: PubSub.subscribe(eventsPubSub)
61321
62001
  });
61322
62002
  }).pipe(Effect.withSpan("PreviewManager.make"));
61323
- const layer$16 = Layer.effect(PreviewManager, make$22);
62003
+ const layer$16 = Layer.effect(PreviewManager, make$23);
61324
62004
  //#endregion
61325
62005
  //#region src/workspace/WorkspaceSearchIndex.ts
61326
62006
  const WORKSPACE_INDEX_MAX_ENTRIES = 25e3;
@@ -61454,7 +62134,7 @@ const waitForScan = (cwd, finder, onFailure) => Effect.try({
61454
62134
  timeout: WORKSPACE_INDEX_SCAN_TIMEOUT
61455
62135
  })
61456
62136
  }), Effect.withSpan("WorkspaceSearchIndex.waitForScan"));
61457
- const make$21 = Effect.fn("WorkspaceSearchIndex.make")(function* (cwd) {
62137
+ const make$22 = Effect.fn("WorkspaceSearchIndex.make")(function* (cwd) {
61458
62138
  const finder = yield* Effect.acquireRelease(createFinder(cwd), (finder) => Effect.try({
61459
62139
  try: () => finder.destroy(),
61460
62140
  catch: (cause) => new WorkspaceSearchIndexDestroyFailed({
@@ -61528,7 +62208,7 @@ const make$21 = Effect.fn("WorkspaceSearchIndex.make")(function* (cwd) {
61528
62208
  * workspace root. WorkspaceSearchIndexMap owns memoization and idle cleanup;
61529
62209
  * using a default cwd here would mix resources from different workspaces.
61530
62210
  */
61531
- const layer$15 = (cwd) => Layer.effect(WorkspaceSearchIndex, make$21(cwd));
62211
+ const layer$15 = (cwd) => Layer.effect(WorkspaceSearchIndex, make$22(cwd));
61532
62212
  var WorkspaceSearchIndexMap = class extends LayerMap.Service()("@p4code/cli/workspace/WorkspaceSearchIndexMap", {
61533
62213
  lookup: layer$15,
61534
62214
  idleTimeToLive: WORKSPACE_INDEX_IDLE_TTL
@@ -61592,7 +62272,7 @@ const resolveBrowseTarget = Effect.fn("WorkspaceEntries.resolveBrowseTarget")(fu
61592
62272
  if (!input.cwd) return yield* new WorkspaceEntriesCurrentProjectRequiredError({ partialPath: input.partialPath });
61593
62273
  return path.resolve(expandHomePath$1(input.cwd, path), input.partialPath);
61594
62274
  });
61595
- const make$20 = Effect.gen(function* () {
62275
+ const make$21 = Effect.gen(function* () {
61596
62276
  const path = yield* Path.Path;
61597
62277
  const workspacePaths = yield* WorkspacePaths;
61598
62278
  const workspaceSearchIndexes = yield* WorkspaceSearchIndexMap;
@@ -61666,7 +62346,7 @@ const make$20 = Effect.gen(function* () {
61666
62346
  search
61667
62347
  });
61668
62348
  });
61669
- const layer$14 = Layer.effect(WorkspaceEntries, make$20).pipe(Layer.provide(WorkspaceSearchIndexMap.layer));
62349
+ const layer$14 = Layer.effect(WorkspaceEntries, make$21).pipe(Layer.provide(WorkspaceSearchIndexMap.layer));
61670
62350
  //#endregion
61671
62351
  //#region src/workspace/WorkspaceFileSystem.ts
61672
62352
  /**
@@ -61725,7 +62405,7 @@ Schema$1.Union([
61725
62405
  ]);
61726
62406
  /** Service tag for workspace file operations. */
61727
62407
  var WorkspaceFileSystem = class extends Context.Service()("@p4code/cli/workspace/WorkspaceFileSystem") {};
61728
- const make$19 = Effect.gen(function* () {
62408
+ const make$20 = Effect.gen(function* () {
61729
62409
  const fileSystem = yield* FileSystem.FileSystem;
61730
62410
  const path = yield* Path.Path;
61731
62411
  const workspacePaths = yield* WorkspacePaths;
@@ -61869,7 +62549,7 @@ const make$19 = Effect.gen(function* () {
61869
62549
  writeFile
61870
62550
  });
61871
62551
  });
61872
- const layer$13 = Layer.effect(WorkspaceFileSystem, make$19);
62552
+ const layer$13 = Layer.effect(WorkspaceFileSystem, make$20);
61873
62553
  //#endregion
61874
62554
  //#region src/vcs/VcsStatusBroadcaster.ts
61875
62555
  const DEFAULT_VCS_STATUS_REFRESH_INTERVAL = Duration.seconds(30);
@@ -61941,7 +62621,7 @@ function fingerprintStatusPart(status) {
61941
62621
  return JSON.stringify(status);
61942
62622
  }
61943
62623
  const normalizeCwd = (cwd) => Effect.service(FileSystem.FileSystem).pipe(Effect.flatMap((fs) => fs.realPath(cwd)), Effect.orElseSucceed(() => cwd));
61944
- const make$18 = Effect.gen(function* () {
62624
+ const make$19 = Effect.gen(function* () {
61945
62625
  const workflow = yield* GitWorkflowService;
61946
62626
  const fs = yield* FileSystem.FileSystem;
61947
62627
  const changesPubSub = yield* Effect.acquireRelease(PubSub.unbounded(), (pubsub) => PubSub.shutdown(pubsub));
@@ -62165,7 +62845,7 @@ const make$18 = Effect.gen(function* () {
62165
62845
  streamStatus
62166
62846
  });
62167
62847
  });
62168
- const layer$12 = Layer.effect(VcsStatusBroadcaster, make$18);
62848
+ const layer$12 = Layer.effect(VcsStatusBroadcaster, make$19);
62169
62849
  //#endregion
62170
62850
  //#region src/vcs/VcsProvisioningService.ts
62171
62851
  var VcsProvisioningService = class extends Context.Service()("@p4code/cli/vcs/VcsProvisioningService") {};
@@ -62178,7 +62858,7 @@ function resolveRequestedKind(kind) {
62178
62858
  }));
62179
62859
  return Effect.succeed(kind);
62180
62860
  }
62181
- const make$17 = Effect.gen(function* () {
62861
+ const make$18 = Effect.gen(function* () {
62182
62862
  const registry = yield* VcsDriverRegistry;
62183
62863
  const initRepository = Effect.fn("VcsProvisioningService.initRepository")(function* (input) {
62184
62864
  const kind = yield* resolveRequestedKind(input.kind);
@@ -62186,11 +62866,11 @@ const make$17 = Effect.gen(function* () {
62186
62866
  });
62187
62867
  return VcsProvisioningService.of({ initRepository });
62188
62868
  });
62189
- const layer$11 = Layer.effect(VcsProvisioningService, make$17);
62869
+ const layer$11 = Layer.effect(VcsProvisioningService, make$18);
62190
62870
  //#endregion
62191
62871
  //#region src/review/ReviewService.ts
62192
62872
  var ReviewService = class extends Context.Service()("@p4code/cli/review/ReviewService") {};
62193
- const make$16 = Effect.gen(function* () {
62873
+ const make$17 = Effect.gen(function* () {
62194
62874
  const config = yield* ServerConfig$1;
62195
62875
  const fileSystem = yield* FileSystem.FileSystem;
62196
62876
  const path = yield* Path.Path;
@@ -62246,7 +62926,7 @@ const make$16 = Effect.gen(function* () {
62246
62926
  });
62247
62927
  return ReviewService.of({ getDiffPreview });
62248
62928
  });
62249
- const layer$10 = Layer.effect(ReviewService, make$16);
62929
+ const layer$10 = Layer.effect(ReviewService, make$17);
62250
62930
  //#endregion
62251
62931
  //#region src/diagnostics/ProcessDiagnostics.ts
62252
62932
  const PROCESS_QUERY_TIMEOUT_MS = 1e3;
@@ -62541,7 +63221,7 @@ function assertDescendantPid(pid) {
62541
63221
  }));
62542
63222
  }));
62543
63223
  }
62544
- const make$15 = Effect.gen(function* () {
63224
+ const make$16 = Effect.gen(function* () {
62545
63225
  const spawner = yield* ChildProcessSpawner$1.ChildProcessSpawner;
62546
63226
  const read = Effect.gen(function* () {
62547
63227
  const readAt = yield* DateTime.now;
@@ -62585,7 +63265,7 @@ const make$15 = Effect.gen(function* () {
62585
63265
  signal
62586
63266
  });
62587
63267
  });
62588
- const layer$9 = Layer.effect(ProcessDiagnostics, make$15);
63268
+ const layer$9 = Layer.effect(ProcessDiagnostics, make$16);
62589
63269
  //#endregion
62590
63270
  //#region src/diagnostics/ProcessResourceMonitor.ts
62591
63271
  const SAMPLE_INTERVAL_MS = 5e3;
@@ -62736,7 +63416,7 @@ function aggregateProcessResourceHistory(input) {
62736
63416
  }) : Option.none()
62737
63417
  };
62738
63418
  }
62739
- const make$14 = Effect.gen(function* () {
63419
+ const make$15 = Effect.gen(function* () {
62740
63420
  const spawner = yield* ChildProcessSpawner$1.ChildProcessSpawner;
62741
63421
  const state = yield* Ref.make({
62742
63422
  samples: [],
@@ -62785,7 +63465,7 @@ const make$14 = Effect.gen(function* () {
62785
63465
  });
62786
63466
  return ProcessResourceMonitor.of({ readHistory });
62787
63467
  });
62788
- const layer$8 = Layer.effect(ProcessResourceMonitor, make$14);
63468
+ const layer$8 = Layer.effect(ProcessResourceMonitor, make$15);
62789
63469
  //#endregion
62790
63470
  //#region src/diagnostics/TraceDiagnostics.ts
62791
63471
  var TraceFileReadError = class extends Schema$1.TaggedErrorClass()("TraceFileReadError", {
@@ -63033,7 +63713,7 @@ function readTraceFile(fileSystem, path) {
63033
63713
  cause
63034
63714
  })) }));
63035
63715
  }
63036
- const make$13 = Effect.gen(function* () {
63716
+ const make$14 = Effect.gen(function* () {
63037
63717
  const fileSystem = yield* FileSystem.FileSystem;
63038
63718
  const read = Effect.fn("TraceDiagnostics.read")(function* (options) {
63039
63719
  const readAt = options.readAt ?? (yield* DateTime.now);
@@ -63077,7 +63757,7 @@ const make$13 = Effect.gen(function* () {
63077
63757
  });
63078
63758
  return TraceDiagnostics.of({ read });
63079
63759
  });
63080
- const layer$7 = Layer.effect(TraceDiagnostics, make$13);
63760
+ const layer$7 = Layer.effect(TraceDiagnostics, make$14);
63081
63761
  function readTraceDiagnostics(options) {
63082
63762
  return Effect.gen(function* () {
63083
63763
  return yield* (yield* TraceDiagnostics).read(options);
@@ -63101,7 +63781,7 @@ const VCS_PROBES = [{
63101
63781
  installHint: "Install Jujutsu with `brew install jj` or from https://github.com/jj-vcs/jj."
63102
63782
  }];
63103
63783
  var SourceControlDiscovery = class extends Context.Service()("@p4code/cli/sourceControl/SourceControlDiscovery") {};
63104
- const make$12 = Effect.gen(function* () {
63784
+ const make$13 = Effect.gen(function* () {
63105
63785
  const config = yield* ServerConfig$1;
63106
63786
  const process = yield* VcsProcess;
63107
63787
  const sourceControlProviders = yield* SourceControlProviderRegistry;
@@ -63150,7 +63830,7 @@ const make$12 = Effect.gen(function* () {
63150
63830
  sourceControlProviders: sourceControlProviders.discover
63151
63831
  }) });
63152
63832
  });
63153
- const layer$6 = Layer.effect(SourceControlDiscovery, make$12);
63833
+ const layer$6 = Layer.effect(SourceControlDiscovery, make$13);
63154
63834
  //#endregion
63155
63835
  //#region src/sourceControl/SourceControlRepositoryService.ts
63156
63836
  const isSourceControlRepositoryError = Schema$1.is(SourceControlRepositoryError);
@@ -63183,7 +63863,7 @@ function expandHomePath(input, path) {
63183
63863
  if (input.startsWith("~/") || input.startsWith("~\\")) return path.join(NodeOS.homedir(), input.slice(2));
63184
63864
  return input;
63185
63865
  }
63186
- const make$11 = Effect.gen(function* () {
63866
+ const make$12 = Effect.gen(function* () {
63187
63867
  const config = yield* ServerConfig$1;
63188
63868
  const fileSystem = yield* FileSystem.FileSystem;
63189
63869
  const git = yield* GitVcsDriver;
@@ -63322,7 +64002,7 @@ const make$11 = Effect.gen(function* () {
63322
64002
  publishRepository: (input) => publishRepository(input).pipe(mapRepositoryError("publishRepository", input.provider))
63323
64003
  });
63324
64004
  });
63325
- const layer$5 = Layer.effect(SourceControlRepositoryService, make$11);
64005
+ const layer$5 = Layer.effect(SourceControlRepositoryService, make$12);
63326
64006
  //#endregion
63327
64007
  //#region src/ws.ts
63328
64008
  /** Matches `p4c hub token add`, so a token minted here and one minted there are the same thing. */
@@ -63424,7 +64104,7 @@ function projectSetupScriptCompatibilityDetail(error) {
63424
64104
  }
63425
64105
  }
63426
64106
  function isThreadDetailEvent(event) {
63427
- return event.type === "thread.message-sent" || event.type === "thread.proposed-plan-upserted" || event.type === "thread.activity-appended" || event.type === "thread.turn-diff-completed" || event.type === "thread.reverted" || event.type === "thread.session-set";
64107
+ return event.type === "thread.message-sent" || event.type === "thread.proposed-plan-upserted" || event.type === "thread.scheduled-task.created" || event.type === "thread.scheduled-task.cancelled" || event.type === "thread.scheduled-task.fired" || event.type === "thread.activity-appended" || event.type === "thread.turn-diff-completed" || event.type === "thread.reverted" || event.type === "thread.session-set";
63428
64108
  }
63429
64109
  const PROVIDER_STATUS_DEBOUNCE_MS = 200;
63430
64110
  const SHELL_RESUME_MAX_GAP = 1e3;
@@ -63622,6 +64302,7 @@ const makeWsRpcLayer = (currentSession, previewAutomationBroker) => WsRpcGroup.t
63622
64302
  const previewManager = yield* PreviewManager;
63623
64303
  const portDiscovery = yield* PortDiscovery;
63624
64304
  const providerRegistry = yield* ProviderRegistry;
64305
+ const providerSessionDirectory = yield* ProviderSessionDirectory;
63625
64306
  const providerMaintenanceRunner = yield* ProviderMaintenanceRunner;
63626
64307
  const serverSelfUpdate = yield* ServerSelfUpdate;
63627
64308
  const textGeneration = yield* TextGeneration;
@@ -64018,7 +64699,22 @@ const makeWsRpcLayer = (currentSession, previewAutomationBroker) => WsRpcGroup.t
64018
64699
  onNone: () => false,
64019
64700
  onSome: (thread) => thread.session !== null && thread.session.status !== "stopped"
64020
64701
  })), Effect.orElseSucceed(() => false)) : false;
64021
- const result = yield* dispatchNormalizedCommand(normalizedCommand);
64702
+ const result = normalizedCommand.type === "thread.fork" ? yield* Effect.uninterruptible(Effect.gen(function* () {
64703
+ if (yield* projectionSnapshotQuery.getThreadShellById(normalizedCommand.threadId).pipe(Effect.map(Option.isSome), Effect.orElseSucceed(() => false))) return yield* dispatchNormalizedCommand(normalizedCommand);
64704
+ const forkBinding = yield* providerSessionDirectory.getBinding(normalizedCommand.sourceThreadId).pipe(Effect.map((source) => buildForkedProviderBinding(normalizedCommand.threadId, Option.getOrUndefined(source))), Effect.mapError((error) => new OrchestrationDispatchCommandError({
64705
+ message: `Could not read the source thread's provider session: ${error.message}`,
64706
+ cause: error
64707
+ })));
64708
+ if (forkBinding.rejection !== void 0) return yield* new OrchestrationDispatchCommandError({ message: describeThreadForkRejection(forkBinding.rejection) });
64709
+ yield* providerSessionDirectory.upsert(forkBinding.binding).pipe(Effect.mapError((error) => new OrchestrationDispatchCommandError({
64710
+ message: `Could not save the fork's provider session: ${error.message}`,
64711
+ cause: error
64712
+ })));
64713
+ return yield* dispatchNormalizedCommand(normalizedCommand).pipe(Effect.tapError(() => providerSessionDirectory.remove(normalizedCommand.threadId).pipe(Effect.catchCause((cause) => Effect.logWarning("failed to remove fork binding after dispatch failure", {
64714
+ threadId: normalizedCommand.threadId,
64715
+ cause
64716
+ })))));
64717
+ })) : yield* dispatchNormalizedCommand(normalizedCommand);
64022
64718
  if (normalizedCommand.type === "thread.archive") {
64023
64719
  const archivedThreadIds = result.events?.filter((event) => event.type === "thread.archived").map((event) => event.payload.threadId);
64024
64720
  if (archivedThreadIds === void 0 || archivedThreadIds.length === 0) return yield* new OrchestrationDispatchCommandError({ message: "Archive command completed without authoritative archive events." });
@@ -64609,7 +65305,7 @@ function toPersistenceSqlOrDecodeError(sqlOperation, decodeOperation, correlatio
64609
65305
  cause
64610
65306
  });
64611
65307
  }
64612
- const make$10 = Effect.gen(function* () {
65308
+ const make$11 = Effect.gen(function* () {
64613
65309
  const sql = yield* SqlClient.SqlClient;
64614
65310
  const upsertRuntimeRow = SqlSchema.void({
64615
65311
  Request: ProviderSessionRuntimeDbRowSchema,
@@ -64712,7 +65408,7 @@ const make$10 = Effect.gen(function* () {
64712
65408
  deleteByThreadId
64713
65409
  };
64714
65410
  });
64715
- const layer$4 = Layer.effect(ProviderSessionRuntimeRepository, make$10);
65411
+ const layer$4 = Layer.effect(ProviderSessionRuntimeRepository, make$11);
64716
65412
  //#endregion
64717
65413
  //#region src/provider/Errors.ts
64718
65414
  /**
@@ -64837,9 +65533,6 @@ var ProviderSessionDirectoryPersistenceError = class extends Schema$1.TaggedErro
64837
65533
  }
64838
65534
  };
64839
65535
  //#endregion
64840
- //#region src/provider/Services/ProviderSessionDirectory.ts
64841
- var ProviderSessionDirectory = class extends Context.Service()("@p4code/cli/provider/Services/ProviderSessionDirectory") {};
64842
- //#endregion
64843
65536
  //#region src/provider/Layers/ProviderSessionDirectory.ts
64844
65537
  const decodeProviderDriverKindValue = Schema$1.decodeUnknownEffect(ProviderDriverKind);
64845
65538
  function toPersistenceError(operation) {
@@ -64920,12 +65613,14 @@ const makeProviderSessionDirectory = Effect.gen(function* () {
64920
65613
  detail: `No persisted provider binding found for thread '${threadId}'.`
64921
65614
  }))
64922
65615
  })));
65616
+ const remove = (threadId) => repository.deleteByThreadId({ threadId }).pipe(Effect.mapError(toPersistenceError("ProviderSessionDirectory.remove:deleteByThreadId")));
64923
65617
  const listThreadIds = () => repository.list().pipe(Effect.mapError(toPersistenceError("ProviderSessionDirectory.listThreadIds:list")), Effect.map((rows) => rows.map((row) => row.threadId)));
64924
65618
  const listBindings = () => repository.list().pipe(Effect.mapError(toPersistenceError("ProviderSessionDirectory.listBindings:list")), Effect.flatMap((rows) => Effect.forEach(rows, (row) => toRuntimeBinding(row, "ProviderSessionDirectory.listBindings"), { concurrency: "unbounded" })));
64925
65619
  return {
64926
65620
  upsert,
64927
65621
  getProvider,
64928
65622
  getBinding,
65623
+ remove,
64929
65624
  listThreadIds,
64930
65625
  listBindings
64931
65626
  };
@@ -65600,12 +66295,12 @@ const makeWithOptions = Effect.fn("McpSessionRegistry.make")(function* (options
65600
66295
  });
65601
66296
  });
65602
66297
  let activeMcpSessionRegistry;
65603
- const make$9 = Effect.acquireRelease(makeWithOptions().pipe(Effect.tap((registry) => Effect.sync(() => {
66298
+ const make$10 = Effect.acquireRelease(makeWithOptions().pipe(Effect.tap((registry) => Effect.sync(() => {
65604
66299
  activeMcpSessionRegistry = registry;
65605
66300
  }))), (registry) => Effect.sync(() => {
65606
66301
  if (activeMcpSessionRegistry === registry) activeMcpSessionRegistry = void 0;
65607
66302
  }));
65608
- const layer$3 = Layer.effect(McpSessionRegistry, make$9);
66303
+ const layer$3 = Layer.effect(McpSessionRegistry, make$10);
65609
66304
  const issueActiveMcpCredential = (request) => activeMcpSessionRegistry ? activeMcpSessionRegistry.revokeThread(request.threadId).pipe(Effect.andThen(activeMcpSessionRegistry.issue(request))) : Effect.sync(() => void 0);
65610
66305
  /**
65611
66306
  * Refreshes the liveness of a thread's MCP credential. Called on every provider
@@ -67229,53 +67924,84 @@ const MINIMUM_CLAUDE_OPUS_5_VERSION = "2.1.219";
67229
67924
  const MINIMUM_CLAUDE_FABLE_5_VERSION = "2.1.169";
67230
67925
  const MINIMUM_CLAUDE_OPUS_4_8_VERSION = "2.1.154";
67231
67926
  const MINIMUM_CLAUDE_OPUS_4_7_VERSION = "2.1.111";
67232
- const CLAUDE_FABLE_CAPABILITIES = createModelCapabilities({ optionDescriptors: [buildSelectOptionDescriptor({
67233
- id: "effort",
67234
- label: "Reasoning",
67235
- options: [
67236
- {
67237
- value: "low",
67238
- label: "Low"
67239
- },
67240
- {
67241
- value: "medium",
67242
- label: "Medium"
67243
- },
67244
- {
67245
- value: "high",
67246
- label: "High",
67927
+ const AUTO_COMPACT_WINDOW_OPTION_ID = "autoCompactWindow";
67928
+ const AUTO_COMPACT_WINDOW_AUTO = "auto";
67929
+ /** Token counts behind each explicit auto-compact choice. */
67930
+ const AUTO_COMPACT_WINDOW_TOKENS = {
67931
+ "200k": 2e5,
67932
+ "400k": 4e5,
67933
+ "600k": 6e5
67934
+ };
67935
+ /**
67936
+ * Where Claude Code starts summarising the conversation. "Auto" keeps Claude
67937
+ * Code's own per-model threshold, which on 1M-context models lets a session
67938
+ * grow close to 1M tokens and re-read all of it on every call.
67939
+ */
67940
+ function buildAutoCompactWindowDescriptor() {
67941
+ return buildSelectOptionDescriptor({
67942
+ id: AUTO_COMPACT_WINDOW_OPTION_ID,
67943
+ label: "Auto-compact At",
67944
+ options: [{
67945
+ value: AUTO_COMPACT_WINDOW_AUTO,
67946
+ label: "Auto",
67247
67947
  isDefault: true
67248
- },
67249
- {
67250
- value: "xhigh",
67251
- label: "Extra High"
67252
- },
67253
- {
67254
- value: "max",
67255
- label: "Max"
67256
- },
67257
- {
67258
- value: "ultracode",
67259
- label: "Ultracode"
67260
- },
67261
- {
67262
- value: "ultrathink",
67263
- label: "Ultrathink"
67264
- }
67265
- ],
67266
- promptInjectedValues: ["ultrathink"]
67267
- }), buildSelectOptionDescriptor({
67268
- id: "contextWindow",
67269
- label: "Context Window",
67270
- options: [{
67271
- value: "200k",
67272
- label: "200k"
67273
- }, {
67274
- value: "1m",
67275
- label: "1M",
67276
- isDefault: true
67277
- }]
67278
- })] });
67948
+ }, ...Object.keys(AUTO_COMPACT_WINDOW_TOKENS).map((value) => ({
67949
+ value,
67950
+ label: value
67951
+ }))]
67952
+ });
67953
+ }
67954
+ const CLAUDE_FABLE_CAPABILITIES = createModelCapabilities({ optionDescriptors: [
67955
+ buildSelectOptionDescriptor({
67956
+ id: "effort",
67957
+ label: "Reasoning",
67958
+ options: [
67959
+ {
67960
+ value: "low",
67961
+ label: "Low"
67962
+ },
67963
+ {
67964
+ value: "medium",
67965
+ label: "Medium"
67966
+ },
67967
+ {
67968
+ value: "high",
67969
+ label: "High",
67970
+ isDefault: true
67971
+ },
67972
+ {
67973
+ value: "xhigh",
67974
+ label: "Extra High"
67975
+ },
67976
+ {
67977
+ value: "max",
67978
+ label: "Max"
67979
+ },
67980
+ {
67981
+ value: "ultracode",
67982
+ label: "Ultracode"
67983
+ },
67984
+ {
67985
+ value: "ultrathink",
67986
+ label: "Ultrathink"
67987
+ }
67988
+ ],
67989
+ promptInjectedValues: ["ultrathink"]
67990
+ }),
67991
+ buildSelectOptionDescriptor({
67992
+ id: "contextWindow",
67993
+ label: "Context Window",
67994
+ options: [{
67995
+ value: "200k",
67996
+ label: "200k"
67997
+ }, {
67998
+ value: "1m",
67999
+ label: "1M",
68000
+ isDefault: true
68001
+ }]
68002
+ }),
68003
+ buildAutoCompactWindowDescriptor()
68004
+ ] });
67279
68005
  const BUILT_IN_MODELS = [
67280
68006
  {
67281
68007
  slug: "claude-fable-5-1",
@@ -67345,92 +68071,101 @@ const BUILT_IN_MODELS = [
67345
68071
  label: "1M",
67346
68072
  isDefault: true
67347
68073
  }]
67348
- })
68074
+ }),
68075
+ buildAutoCompactWindowDescriptor()
67349
68076
  ] })
67350
68077
  },
67351
68078
  {
67352
68079
  slug: "claude-opus-4-8",
67353
68080
  name: "Claude Opus 4.8",
67354
68081
  isCustom: false,
67355
- capabilities: createModelCapabilities({ optionDescriptors: [buildSelectOptionDescriptor({
67356
- id: "effort",
67357
- label: "Reasoning",
67358
- options: [
67359
- {
67360
- value: "low",
67361
- label: "Low"
67362
- },
67363
- {
67364
- value: "medium",
67365
- label: "Medium"
67366
- },
67367
- {
67368
- value: "high",
67369
- label: "High",
67370
- isDefault: true
67371
- },
67372
- {
67373
- value: "xhigh",
67374
- label: "Extra High"
67375
- },
67376
- {
67377
- value: "max",
67378
- label: "Max"
67379
- },
67380
- {
67381
- value: "ultracode",
67382
- label: "Ultracode"
67383
- },
67384
- {
67385
- value: "ultrathink",
67386
- label: "Ultrathink"
67387
- }
67388
- ],
67389
- promptInjectedValues: ["ultrathink"]
67390
- }), buildBooleanOptionDescriptor({
67391
- id: "fastMode",
67392
- label: "Fast Mode"
67393
- })] })
68082
+ capabilities: createModelCapabilities({ optionDescriptors: [
68083
+ buildSelectOptionDescriptor({
68084
+ id: "effort",
68085
+ label: "Reasoning",
68086
+ options: [
68087
+ {
68088
+ value: "low",
68089
+ label: "Low"
68090
+ },
68091
+ {
68092
+ value: "medium",
68093
+ label: "Medium"
68094
+ },
68095
+ {
68096
+ value: "high",
68097
+ label: "High",
68098
+ isDefault: true
68099
+ },
68100
+ {
68101
+ value: "xhigh",
68102
+ label: "Extra High"
68103
+ },
68104
+ {
68105
+ value: "max",
68106
+ label: "Max"
68107
+ },
68108
+ {
68109
+ value: "ultracode",
68110
+ label: "Ultracode"
68111
+ },
68112
+ {
68113
+ value: "ultrathink",
68114
+ label: "Ultrathink"
68115
+ }
68116
+ ],
68117
+ promptInjectedValues: ["ultrathink"]
68118
+ }),
68119
+ buildBooleanOptionDescriptor({
68120
+ id: "fastMode",
68121
+ label: "Fast Mode"
68122
+ }),
68123
+ buildAutoCompactWindowDescriptor()
68124
+ ] })
67394
68125
  },
67395
68126
  {
67396
68127
  slug: "claude-opus-4-7",
67397
68128
  name: "Claude Opus 4.7",
67398
68129
  isCustom: false,
67399
- capabilities: createModelCapabilities({ optionDescriptors: [buildSelectOptionDescriptor({
67400
- id: "effort",
67401
- label: "Reasoning",
67402
- options: [
67403
- {
67404
- value: "low",
67405
- label: "Low"
67406
- },
67407
- {
67408
- value: "medium",
67409
- label: "Medium"
67410
- },
67411
- {
67412
- value: "high",
67413
- label: "High"
67414
- },
67415
- {
67416
- value: "xhigh",
67417
- label: "Extra High",
67418
- isDefault: true
67419
- },
67420
- {
67421
- value: "max",
67422
- label: "Max"
67423
- },
67424
- {
67425
- value: "ultrathink",
67426
- label: "Ultrathink"
67427
- }
67428
- ],
67429
- promptInjectedValues: ["ultrathink"]
67430
- }), buildBooleanOptionDescriptor({
67431
- id: "fastMode",
67432
- label: "Fast Mode"
67433
- })] })
68130
+ capabilities: createModelCapabilities({ optionDescriptors: [
68131
+ buildSelectOptionDescriptor({
68132
+ id: "effort",
68133
+ label: "Reasoning",
68134
+ options: [
68135
+ {
68136
+ value: "low",
68137
+ label: "Low"
68138
+ },
68139
+ {
68140
+ value: "medium",
68141
+ label: "Medium"
68142
+ },
68143
+ {
68144
+ value: "high",
68145
+ label: "High"
68146
+ },
68147
+ {
68148
+ value: "xhigh",
68149
+ label: "Extra High",
68150
+ isDefault: true
68151
+ },
68152
+ {
68153
+ value: "max",
68154
+ label: "Max"
68155
+ },
68156
+ {
68157
+ value: "ultrathink",
68158
+ label: "Ultrathink"
68159
+ }
68160
+ ],
68161
+ promptInjectedValues: ["ultrathink"]
68162
+ }),
68163
+ buildBooleanOptionDescriptor({
68164
+ id: "fastMode",
68165
+ label: "Fast Mode"
68166
+ }),
68167
+ buildAutoCompactWindowDescriptor()
68168
+ ] })
67434
68169
  },
67435
68170
  {
67436
68171
  slug: "claude-opus-4-6",
@@ -67480,7 +68215,8 @@ const BUILT_IN_MODELS = [
67480
68215
  label: "1M",
67481
68216
  isDefault: true
67482
68217
  }]
67483
- })
68218
+ }),
68219
+ buildAutoCompactWindowDescriptor()
67484
68220
  ] })
67485
68221
  },
67486
68222
  {
@@ -67518,93 +68254,101 @@ const BUILT_IN_MODELS = [
67518
68254
  slug: "claude-sonnet-5",
67519
68255
  name: "Claude Sonnet 5",
67520
68256
  isCustom: false,
67521
- capabilities: createModelCapabilities({ optionDescriptors: [buildSelectOptionDescriptor({
67522
- id: "effort",
67523
- label: "Reasoning",
67524
- options: [
67525
- {
67526
- value: "low",
67527
- label: "Low"
67528
- },
67529
- {
67530
- value: "medium",
67531
- label: "Medium"
67532
- },
67533
- {
67534
- value: "high",
67535
- label: "High",
68257
+ capabilities: createModelCapabilities({ optionDescriptors: [
68258
+ buildSelectOptionDescriptor({
68259
+ id: "effort",
68260
+ label: "Reasoning",
68261
+ options: [
68262
+ {
68263
+ value: "low",
68264
+ label: "Low"
68265
+ },
68266
+ {
68267
+ value: "medium",
68268
+ label: "Medium"
68269
+ },
68270
+ {
68271
+ value: "high",
68272
+ label: "High",
68273
+ isDefault: true
68274
+ },
68275
+ {
68276
+ value: "xhigh",
68277
+ label: "Extra High"
68278
+ },
68279
+ {
68280
+ value: "max",
68281
+ label: "Max"
68282
+ },
68283
+ {
68284
+ value: "ultrathink",
68285
+ label: "Ultrathink"
68286
+ }
68287
+ ],
68288
+ promptInjectedValues: ["ultrathink"]
68289
+ }),
68290
+ buildSelectOptionDescriptor({
68291
+ id: "contextWindow",
68292
+ label: "Context Window",
68293
+ options: [{
68294
+ value: "200k",
68295
+ label: "200k",
67536
68296
  isDefault: true
67537
- },
67538
- {
67539
- value: "xhigh",
67540
- label: "Extra High"
67541
- },
67542
- {
67543
- value: "max",
67544
- label: "Max"
67545
- },
67546
- {
67547
- value: "ultrathink",
67548
- label: "Ultrathink"
67549
- }
67550
- ],
67551
- promptInjectedValues: ["ultrathink"]
67552
- }), buildSelectOptionDescriptor({
67553
- id: "contextWindow",
67554
- label: "Context Window",
67555
- options: [{
67556
- value: "200k",
67557
- label: "200k",
67558
- isDefault: true
67559
- }, {
67560
- value: "1m",
67561
- label: "1M"
67562
- }]
67563
- })] })
68297
+ }, {
68298
+ value: "1m",
68299
+ label: "1M"
68300
+ }]
68301
+ }),
68302
+ buildAutoCompactWindowDescriptor()
68303
+ ] })
67564
68304
  },
67565
68305
  {
67566
68306
  slug: "claude-sonnet-4-6",
67567
68307
  name: "Claude Sonnet 4.6",
67568
68308
  isCustom: false,
67569
- capabilities: createModelCapabilities({ optionDescriptors: [buildSelectOptionDescriptor({
67570
- id: "effort",
67571
- label: "Reasoning",
67572
- options: [
67573
- {
67574
- value: "low",
67575
- label: "Low"
67576
- },
67577
- {
67578
- value: "medium",
67579
- label: "Medium"
67580
- },
67581
- {
67582
- value: "high",
67583
- label: "High",
68309
+ capabilities: createModelCapabilities({ optionDescriptors: [
68310
+ buildSelectOptionDescriptor({
68311
+ id: "effort",
68312
+ label: "Reasoning",
68313
+ options: [
68314
+ {
68315
+ value: "low",
68316
+ label: "Low"
68317
+ },
68318
+ {
68319
+ value: "medium",
68320
+ label: "Medium"
68321
+ },
68322
+ {
68323
+ value: "high",
68324
+ label: "High",
68325
+ isDefault: true
68326
+ },
68327
+ {
68328
+ value: "max",
68329
+ label: "Max"
68330
+ },
68331
+ {
68332
+ value: "ultrathink",
68333
+ label: "Ultrathink"
68334
+ }
68335
+ ],
68336
+ promptInjectedValues: ["ultrathink"]
68337
+ }),
68338
+ buildSelectOptionDescriptor({
68339
+ id: "contextWindow",
68340
+ label: "Context Window",
68341
+ options: [{
68342
+ value: "200k",
68343
+ label: "200k",
67584
68344
  isDefault: true
67585
- },
67586
- {
67587
- value: "max",
67588
- label: "Max"
67589
- },
67590
- {
67591
- value: "ultrathink",
67592
- label: "Ultrathink"
67593
- }
67594
- ],
67595
- promptInjectedValues: ["ultrathink"]
67596
- }), buildSelectOptionDescriptor({
67597
- id: "contextWindow",
67598
- label: "Context Window",
67599
- options: [{
67600
- value: "200k",
67601
- label: "200k",
67602
- isDefault: true
67603
- }, {
67604
- value: "1m",
67605
- label: "1M"
67606
- }]
67607
- })] })
68345
+ }, {
68346
+ value: "1m",
68347
+ label: "1M"
68348
+ }]
68349
+ }),
68350
+ buildAutoCompactWindowDescriptor()
68351
+ ] })
67608
68352
  },
67609
68353
  {
67610
68354
  slug: "claude-haiku-4-5",
@@ -67702,6 +68446,11 @@ function resolveClaudeContextWindow(modelSelection) {
67702
68446
  }).find((candidate) => candidate.id === "contextWindow"));
67703
68447
  return typeof value === "string" ? value : void 0;
67704
68448
  }
68449
+ /** Tokens at which Claude Code should auto-compact, or undefined for its default. */
68450
+ function resolveClaudeAutoCompactWindow(modelSelection) {
68451
+ const raw = getModelSelectionStringOptionValue(modelSelection, AUTO_COMPACT_WINDOW_OPTION_ID);
68452
+ return raw === void 0 ? void 0 : AUTO_COMPACT_WINDOW_TOKENS[raw];
68453
+ }
67705
68454
  function resolveClaudeApiModelId(modelSelection) {
67706
68455
  switch (resolveClaudeContextWindow(modelSelection)) {
67707
68456
  case "1m": return `${modelSelection.model}[1m]`;
@@ -68433,7 +69182,7 @@ function formatAskUserQuestionAnswers(answers) {
68433
69182
  /** Fresh-evidence gate adapted from superpowers' verification skill. */
68434
69183
  const VERIFY_BEFORE_COMPLETION_PROMPT = "NO COMPLETION CLAIMS WITHOUT FRESH VERIFICATION EVIDENCE. Before claiming complete, fixed, or passing: 1) identify proving command; 2) run it fresh and fully; 3) read full output, exit code, failure count; 4) confirm evidence matches claim; 5) state claim with evidence. Missing or failed proof: report actual status.";
68435
69184
  /** Visual-proof gate for user-visible frontend work. */
68436
- const SCREENSHOTS_AFTER_UI_WORK_PROMPT = "AFTER USER-VISIBLE FRONTEND WORK, FRESH VISUAL PROOF IS MANDATORY. Before completing: 1) run the relevant real client; 2) inspect the full changed surface; 3) when P4Code preview is available, use preview_snapshot for inspection, then call preview_save_screenshot once after verification passes so P4Code saves the final state under Settings > Screenshots; 4) otherwise capture the verified final state with the relevant approved browser, simulator, or computer tool; 5) verify the screenshot shows the requested result without visible errors; 6) include it in the final response. Ask before launching browser or computer use when approval is required. Skip only work with no user-visible frontend change.";
69185
+ const SCREENSHOTS_AFTER_UI_WORK_PROMPT = "AFTER USER-VISIBLE FRONTEND WORK, FRESH VISUAL PROOF IS MANDATORY. Before completing: 1) run the relevant real client; 2) inspect the full changed surface; 3) when P4Code preview is available, inspect with preview_snapshot (text state by default; pass includeScreenshot: true only for the final visual check), then call preview_save_screenshot once after verification passes so P4Code saves the final state under Settings > Screenshots; 4) otherwise capture the verified final state with the relevant approved browser, simulator, or computer tool; 5) verify the screenshot shows the requested result without visible errors; 6) include it in the final response. Keep screenshots out of your own context where possible: delegate repeated visual inspection to a read-only visual review subagent when one is available and only pull the final proof yourself. Ask before launching browser or computer use when approval is required. Skip only work with no user-visible frontend change.";
68437
69186
  /** Root-cause gate adapted from superpowers' systematic-debugging skill. */
68438
69187
  const ROOT_CAUSE_BEFORE_FIX_PROMPT = "NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST. For bugs or unexpected behavior: 1) read errors, reproduce, inspect recent changes, trace data to source; 2) compare working patterns; 3) state one hypothesis and test smallest change; 4) add failing regression test, implement one fix, verify. After 3 failed fixes, question architecture.";
68439
69188
  function guardrailPromptsFor(settings) {
@@ -68889,7 +69638,8 @@ function readClaudeResumeState(resumeCursor) {
68889
69638
  ...threadId ? { threadId } : {},
68890
69639
  ...resume ? { resume } : {},
68891
69640
  ...resumeSessionAt ? { resumeSessionAt } : {},
68892
- ...turnCountValue !== void 0 && Number.isInteger(turnCountValue) && turnCountValue >= 0 ? { turnCount: turnCountValue } : {}
69641
+ ...turnCountValue !== void 0 && Number.isInteger(turnCountValue) && turnCountValue >= 0 ? { turnCount: turnCountValue } : {},
69642
+ ...cursor.forkSession === true && resume ? { forkSession: true } : {}
68893
69643
  };
68894
69644
  }
68895
69645
  function classifyToolItemType(toolName) {
@@ -70853,8 +71603,9 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
70853
71603
  const resumeState = readClaudeResumeState(input.resumeCursor);
70854
71604
  const threadId = input.threadId;
70855
71605
  const existingResumeSessionId = resumeState?.resume;
70856
- const newSessionId = existingResumeSessionId === void 0 ? yield* randomUUIDv4 : void 0;
70857
- const sessionId = existingResumeSessionId ?? newSessionId;
71606
+ const forkSession = resumeState?.forkSession === true && existingResumeSessionId !== void 0;
71607
+ const newSessionId = existingResumeSessionId === void 0 || forkSession ? yield* randomUUIDv4 : void 0;
71608
+ const sessionId = forkSession ? newSessionId : existingResumeSessionId ?? newSessionId;
70858
71609
  const runtimeContext = yield* Effect.context();
70859
71610
  const runFork = Effect.runForkWith(runtimeContext);
70860
71611
  const runPromise = Effect.runPromiseWith(runtimeContext);
@@ -71059,6 +71810,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
71059
71810
  const descriptors = getProviderOptionDescriptors({ caps });
71060
71811
  const apiModelId = modelSelection ? resolveClaudeApiModelId(modelSelection) : void 0;
71061
71812
  const initialContextWindow = selectedClaudeContextWindow(modelSelection);
71813
+ const autoCompactWindow = resolveClaudeAutoCompactWindow(modelSelection);
71062
71814
  const effort = resolveClaudeEffort(caps, getModelSelectionStringOptionValue(modelSelection, "effort")) ?? null;
71063
71815
  const fastModeSupported = descriptors.some((descriptor) => descriptor.type === "boolean" && descriptor.id === "fastMode");
71064
71816
  const thinkingSupported = descriptors.some((descriptor) => descriptor.type === "boolean" && descriptor.id === "thinking");
@@ -71118,11 +71870,16 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
71118
71870
  ...permissionMode === "bypassPermissions" ? { allowDangerouslySkipPermissions: true } : {},
71119
71871
  ...Object.keys(settings).length > 0 ? { settings } : {},
71120
71872
  ...existingResumeSessionId ? { resume: existingResumeSessionId } : {},
71873
+ ...forkSession ? { forkSession: true } : {},
71121
71874
  ...newSessionId ? { sessionId: newSessionId } : {},
71122
71875
  includePartialMessages: true,
71123
71876
  canUseTool,
71124
71877
  hooks: { SubagentStart: [{ hooks: [compressionSubagentHook] }] },
71125
- env: claudeEnvironment,
71878
+ env: {
71879
+ ...claudeEnvironment,
71880
+ CLAUDE_CODE_ENABLE_TODO_TOOLS: claudeEnvironment.CLAUDE_CODE_ENABLE_TODO_TOOLS ?? "1",
71881
+ ...autoCompactWindow === void 0 ? {} : { CLAUDE_CODE_AUTO_COMPACT_WINDOW: String(autoCompactWindow) }
71882
+ },
71126
71883
  ...input.cwd ? { additionalDirectories: [input.cwd] } : {},
71127
71884
  ...Object.keys(extraArgs).length > 0 ? { extraArgs } : {},
71128
71885
  ...mcpSession || Object.keys(externalMcpServers).length > 0 ? { mcpServers: {
@@ -71138,7 +71895,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
71138
71895
  "provider.kind": PROVIDER$6,
71139
71896
  "provider.thread_id": threadId,
71140
71897
  "provider.runtime_mode": input.runtimeMode,
71141
- "claude.resume.source": existingResumeSessionId !== void 0 ? "resume-session" : "generated-session",
71898
+ "claude.resume.source": forkSession ? "fork-session" : existingResumeSessionId !== void 0 ? "resume-session" : "generated-session",
71142
71899
  "claude.resume.thread_id": resumeState?.threadId ?? "",
71143
71900
  "claude.resume.session_id": existingResumeSessionId ?? "",
71144
71901
  "claude.resume.session_at": resumeState?.resumeSessionAt ?? "",
@@ -89646,7 +90403,7 @@ const makeTerminationError$1 = (handle) => Effect.match(handle.exitCode, {
89646
90403
  //#endregion
89647
90404
  //#region ../../packages/effect-codex-app-server/src/client.ts
89648
90405
  var CodexAppServerClient = class extends Context.Service()("effect-codex-app-server/client/CodexAppServerClient") {};
89649
- const make$8 = Effect.fn("effect-codex-app-server/CodexAppServerClient.make")(function* (stdio, options = {}, terminationError) {
90406
+ const make$9 = Effect.fn("effect-codex-app-server/CodexAppServerClient.make")(function* (stdio, options = {}, terminationError) {
89650
90407
  const requestHandlers = /* @__PURE__ */ new Map();
89651
90408
  const notificationHandlers = /* @__PURE__ */ new Map();
89652
90409
  let unknownRequestHandler;
@@ -89713,7 +90470,7 @@ const make$8 = Effect.fn("effect-codex-app-server/CodexAppServerClient.make")(fu
89713
90470
  const layerChildProcess$1 = (handle, options = {}) => Layer.effect(CodexAppServerClient, makeChildProcessClient(handle, options));
89714
90471
  const makeChildProcessClient = Effect.fn("effect-codex-app-server/CodexAppServerClient.makeChildProcessClient")(function* (handle, options) {
89715
90472
  yield* Stream.runDrain(handle.stderr).pipe(Effect.ignore, Effect.forkScoped);
89716
- return yield* make$8(makeChildStdio$1(handle), options, makeTerminationError$1(handle));
90473
+ return yield* make$9(makeChildStdio$1(handle), options, makeTerminationError$1(handle));
89717
90474
  });
89718
90475
  const resolveCodexLaunchArgs = (launchArgs, environment = process.env) => environment["P4CODE_CODEX_LAUNCH_ARGS"]?.trim() || launchArgs?.trim() || "";
89719
90476
  const codexLaunchArgv = (launchArgs) => tokenizeCliArgs(launchArgs);
@@ -96499,7 +97256,7 @@ const makeTerminationError = (handle) => Effect.match(handle.exitCode, {
96499
97256
  //#endregion
96500
97257
  //#region ../../packages/effect-acp/src/client.ts
96501
97258
  var AcpClient = class extends Context.Service()("effect-acp/client/AcpClient") {};
96502
- const make$7 = Effect.fn("effect-acp/AcpClient.make")(function* (stdio, options = {}, terminationError) {
97259
+ const make$8 = Effect.fn("effect-acp/AcpClient.make")(function* (stdio, options = {}, terminationError) {
96503
97260
  const coreHandlers = {};
96504
97261
  const notificationHandlers = {
96505
97262
  sessionUpdate: {
@@ -96657,7 +97414,7 @@ const make$7 = Effect.fn("effect-acp/AcpClient.make")(function* (stdio, options
96657
97414
  const layerChildProcess = (handle, options = {}) => {
96658
97415
  const stdio = makeChildStdio(handle);
96659
97416
  const terminationError = makeTerminationError(handle);
96660
- return Layer.effect(AcpClient, make$7(stdio, options, terminationError));
97417
+ return Layer.effect(AcpClient, make$8(stdio, options, terminationError));
96661
97418
  };
96662
97419
  //#endregion
96663
97420
  //#region ../../packages/shared/src/toolActivity.ts
@@ -97117,7 +97874,7 @@ function formatConfigOptionValue(value) {
97117
97874
  const defaultSessionLoadTimeout = Duration.seconds(90);
97118
97875
  const defaultSessionLoadReplayIdleGap = Duration.seconds(2);
97119
97876
  var AcpSessionRuntime = class extends Context.Service()("@p4code/cli/provider/acp/AcpSessionRuntime") {};
97120
- const make$6 = (options) => Effect.gen(function* () {
97877
+ const make$7 = (options) => Effect.gen(function* () {
97121
97878
  const crypto = yield* Crypto.Crypto;
97122
97879
  const spawner = yield* ChildProcessSpawner$1.ChildProcessSpawner;
97123
97880
  const runtimeScope = yield* Scope.Scope;
@@ -97433,7 +98190,7 @@ const make$6 = (options) => Effect.gen(function* () {
97433
98190
  notify: acp.raw.notify
97434
98191
  };
97435
98192
  });
97436
- const layer$2 = (options) => Layer.effect(AcpSessionRuntime, make$6(options));
98193
+ const layer$2 = (options) => Layer.effect(AcpSessionRuntime, make$7(options));
97437
98194
  function sessionConfigOptionsFromSetup(response) {
97438
98195
  return response?.configOptions ?? [];
97439
98196
  }
@@ -104754,8 +105511,8 @@ const PreviewSetAppearanceTool = safeBrowserTool(Tool.make("preview_set_appearan
104754
105511
  dependencies: dependencies$1
104755
105512
  }).annotate(Tool.Title, "Set preview appearance").annotate(Tool.Idempotent, true));
104756
105513
  const PreviewSnapshotTool = readonlyBrowserTool(Tool.make("preview_snapshot", {
104757
- description: "Inspect a page before interacting. Pass tabId to inspect a specific tab; omit it to use this agent session's current tab. Returns page state, semantic elements, diagnostics, action history, and a PNG screenshot.",
104758
- parameters: PreviewAutomationTabTargetInput,
105514
+ description: "Inspect a page before interacting. Pass tabId to inspect a specific tab; omit it to use this agent session's current tab. Returns page state, semantic elements, diagnostics, and action history as text. Pass includeScreenshot: true only when you need to see the rendering; the text state is enough for locators and assertions.",
105515
+ parameters: PreviewAutomationSnapshotInput,
104759
105516
  success: PreviewAutomationSnapshot,
104760
105517
  failure: PreviewAutomationError,
104761
105518
  dependencies: dependencies$1
@@ -104857,7 +105614,7 @@ const handlers$4 = {
104857
105614
  preview_navigate: (input) => invokeTargeted("navigate", input, input.timeoutMs),
104858
105615
  preview_resize: (input) => invokeTargeted("resize", input, input.timeoutMs),
104859
105616
  preview_set_appearance: (input) => invokeTargeted("setColorScheme", input),
104860
- preview_snapshot: (input) => invokeTargeted("snapshot", input ?? {}),
105617
+ preview_snapshot: (input) => invokeTargeted("snapshot", input?.tabId ? { tabId: input.tabId } : {}),
104861
105618
  preview_save_screenshot: (input) => invokeTargeted("saveScreenshot", input ?? {}),
104862
105619
  preview_click: (input) => invokeTargeted("click", input, input.timeoutMs).pipe(Effect.as(null)),
104863
105620
  preview_type: (input) => invokeTargeted("type", input, input.timeoutMs).pipe(Effect.as(null)),
@@ -104879,7 +105636,7 @@ const stringField = (record, key) => {
104879
105636
  const value = record[key];
104880
105637
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
104881
105638
  };
104882
- const make$5 = Effect.gen(function* () {
105639
+ const make$6 = Effect.gen(function* () {
104883
105640
  const linear = yield* LinearClient;
104884
105641
  return { resolve: Effect.fn("TicketResolver.resolve")(function* (reference) {
104885
105642
  const identifier = parseTicketReference(reference);
@@ -104910,7 +105667,7 @@ const make$5 = Effect.gen(function* () {
104910
105667
  };
104911
105668
  }) };
104912
105669
  });
104913
- const layer$1 = Layer.effect(TicketResolver, make$5);
105670
+ const layer$1 = Layer.effect(TicketResolver, make$6);
104914
105671
  //#endregion
104915
105672
  //#region src/mcp/toolkits/tasks/tools.ts
104916
105673
  const dependencies = [McpInvocationContext, TaskRepository];
@@ -106419,6 +107176,7 @@ const registerPreviewSnapshot = Effect.fn("McpHttpServer.registerPreviewSnapshot
106419
107176
  return built.handle("preview_snapshot", payload).pipe(Stream.unwrap, Stream.run(Sink.last()), Effect.flatMap(Effect.fromOption), Effect.provideService(PreviewAutomationBroker, broker), Effect.provideService(McpInvocationContext, invocation), Effect.matchCauseEffect({
106420
107177
  onFailure: previewSnapshotFailure,
106421
107178
  onSuccess: ({ encodedResult }) => {
107179
+ const includeScreenshot = payload?.includeScreenshot === true;
106422
107180
  const { screenshot, ...page } = encodedResult;
106423
107181
  const metadata = {
106424
107182
  ...page,
@@ -106434,11 +107192,11 @@ const registerPreviewSnapshot = Effect.fn("McpHttpServer.registerPreviewSnapshot
106434
107192
  content: [{
106435
107193
  type: "text",
106436
107194
  text: JSON.stringify(metadata)
106437
- }, {
107195
+ }, ...includeScreenshot ? [{
106438
107196
  type: "image",
106439
107197
  data: new Uint8Array(Buffer.from(screenshot.data, "base64")),
106440
107198
  mimeType: screenshot.mimeType
106441
- }]
107199
+ }] : []]
106442
107200
  }));
106443
107201
  }
106444
107202
  }));
@@ -106518,6 +107276,18 @@ var ThreadDeletionReactor = class extends Context.Service()("@p4code/cli/orchest
106518
107276
  //#region src/orchestration/Services/FusionWatcherReactor.ts
106519
107277
  var FusionWatcherReactor = class extends Context.Service()("@p4code/cli/orchestration/Services/FusionWatcherReactor") {};
106520
107278
  //#endregion
107279
+ //#region src/orchestration/Services/ScheduledTaskReactor.ts
107280
+ /**
107281
+ * ScheduledTaskReactor - fires user-created scheduled tasks.
107282
+ *
107283
+ * Arms one timer per pending task (from the read model at start and from
107284
+ * `thread.scheduled-task.created` events afterwards), starts the thread turn
107285
+ * when the time comes, and records the outcome on the task.
107286
+ *
107287
+ * @module ScheduledTaskReactor
107288
+ */
107289
+ var ScheduledTaskReactor = class extends Context.Service()("@p4code/cli/orchestration/Services/ScheduledTaskReactor") {};
107290
+ //#endregion
106521
107291
  //#region src/orchestration/Layers/OrchestrationReactor.ts
106522
107292
  const makeOrchestrationReactor = Effect.gen(function* () {
106523
107293
  const providerRuntimeIngestion = yield* ProviderRuntimeIngestionService;
@@ -106525,12 +107295,14 @@ const makeOrchestrationReactor = Effect.gen(function* () {
106525
107295
  const checkpointReactor = yield* CheckpointReactor;
106526
107296
  const threadDeletionReactor = yield* ThreadDeletionReactor;
106527
107297
  const fusionWatcherReactor = yield* FusionWatcherReactor;
107298
+ const scheduledTaskReactor = yield* ScheduledTaskReactor;
106528
107299
  return { start: Effect.fn("start")(function* () {
106529
107300
  yield* providerRuntimeIngestion.start();
106530
107301
  yield* providerCommandReactor.start();
106531
107302
  yield* checkpointReactor.start();
106532
107303
  yield* threadDeletionReactor.start();
106533
107304
  yield* fusionWatcherReactor.start();
107305
+ yield* scheduledTaskReactor.start();
106534
107306
  }) };
106535
107307
  });
106536
107308
  const OrchestrationReactorLive = Layer.effect(OrchestrationReactor, makeOrchestrationReactor);
@@ -107116,7 +107888,7 @@ function runtimeEventToActivities(event, taskTitle, compressMode) {
107116
107888
  }
107117
107889
  return [];
107118
107890
  }
107119
- const make$4 = Effect.gen(function* () {
107891
+ const make$5 = Effect.gen(function* () {
107120
107892
  const crypto = yield* Crypto.Crypto;
107121
107893
  const orchestrationEngine = yield* OrchestrationEngineService;
107122
107894
  const projectionSnapshotQuery = yield* ProjectionSnapshotQuery;
@@ -107746,7 +108518,7 @@ const make$4 = Effect.gen(function* () {
107746
108518
  drain: worker.drain
107747
108519
  };
107748
108520
  });
107749
- const ProviderRuntimeIngestionLive = Layer.effect(ProviderRuntimeIngestionService, make$4).pipe(Layer.provide(ProjectionTurnRepositoryLive), Layer.provide(layer$64));
108521
+ const ProviderRuntimeIngestionLive = Layer.effect(ProviderRuntimeIngestionService, make$5).pipe(Layer.provide(ProjectionTurnRepositoryLive), Layer.provide(layer$64));
107750
108522
  //#endregion
107751
108523
  //#region src/provider/userInvokedSkills.ts
107752
108524
  /**
@@ -107922,8 +108694,8 @@ const DEFAULT_RUNTIME_MODE = "full-access";
107922
108694
  const DEFAULT_THREAD_TITLE = "New thread";
107923
108695
  const NON_SYSTEM_PROVIDER_STRUCTURED_USER_QUESTIONS = structuredUserQuestionPrompt("your provider's structured user-input question tool");
107924
108696
  const FUSION_PROMOTION_INSTRUCTIONS = `Work independently in this normal thread. Fusion is a silent escalation path, not a startup procedure. Do not inspect Fusion tools/skill, mention Fusion status, or announce that Fusion was not invoked. First analyze the task normally. Only if that analysis reveals a concrete unresolved tradeoff, correctness risk, or design decision materially needing a second opinion, stop before implementation, propose Fusion, and ask the user for explicit approval. The user may approve with ordinary affirmative text such as "approved"; /fusion or $fusion also authorizes Fusion directly without a prior proposal. Do not activate, spawn, or promote until one of those authorizations arrives. UI work, complex logic, task size, unfamiliarity, or duration alone never qualifies.`;
107925
- const FUSION_BUILDER_INSTRUCTIONS = `You are Fusion Builder in an already-created native server pair. Server owns pairing and coordination. Do not inspect or invoke the Fusion skill, create/pair/rename threads, or announce/setup Fusion. Start the user's task directly. Before editing, create and maintain the phase list in the same plan/todo tool you use for ordinary step tracking, never the MCP task board tools - one entry per phase in order, exactly one in progress at a time, marked completed at each phase end - so phases render in the task banner. That list holds phase entries only for the whole task; keep step-level or per-file todos out of it. Prose alone leaves the banner empty. Split it into the fewest substantial phases the task genuinely needs plus a final integration/whole-task phase; most tasks need one to three work phases. Each phase is a complete reviewable slice of behavior. Never split per file, per function, or per trivial step: over-splitting spends review turns instead of finishing the job. Add a phase only when a real review boundary, risky decision, or independent behavior separates the work. Complete exactly one phase per turn, and finish the whole phase in that turn rather than stopping early. Do not run tests, typecheck, lint, or builds per phase; write the tests the change needs, then run verification once in the final phase over the whole task. Exception: a phase whose own correctness is unclear may run the single narrowest check that resolves it. End every phase turn with phase completed, todo status, changed behavior/files, and remaining phases; do not start the next phase in the same turn. Server then wakes the paired Supervisor, which resumes you through ${FUSION_ADVICE_PROMPT_PREFIX}; a user message may also revise or resume the work. Final phase verifies the entire task against the original request and labels it ready for whole-task review. Supervisor is unreachable during your turn. Never spawn/use another Supervisor thread/subagent or attribute Supervisor decisions without ${FUSION_ADVICE_PROMPT_PREFIX}. Within the current phase, continue when straightforward or evidence is clear. For a concrete unresolved tradeoff, correctness risk, or design decision materially needing judgment, stop safely before the risky choice; final response states the exact question and why review is needed. Evaluate/follow Supervisor advice unless conflicting with user request or verified repo state.`;
107926
- const FUSION_WATCHER_INSTRUCTIONS = `You are Fusion Supervisor (watcher) in an already-created native server pair. Server owns pairing and coordination and wakes you with ${FUSION_REVIEW_PROMPT_PREFIX} or ${FUSION_GATE_PROMPT_PREFIX} prompts at builder turn boundaries; this message arrived outside such a wake, so your conversational memory of the pair may be gone. The pair metadata below is authoritative: the builder thread exists and is the counterpart thread id. Never report that no builder thread exists. To resume supervision, read builder events with thread_watch_events from lastReviewedImplementerSequence with limit 50, paging forward with the last returned sequence rather than requesting a whole range at once, derive phase from artifacts (git log/status, PR, builder events), steer with thread_advise, and answer an open gate with thread_gate_respond. When a review or gate wake prompt specifies an explicit event range, that range wins over this metadata. Never poll or wait for the builder; deliver review or advice, then end the turn.`;
108697
+ const FUSION_BUILDER_INSTRUCTIONS = `You are Fusion Builder in an already-created native server pair. Server owns pairing and coordination. Do not inspect or invoke the Fusion skill, create/pair/rename threads, or announce/setup Fusion. Start the user's task directly. Before editing, create and maintain the phase list with your provider's step-tracking tool (Claude Code: TaskCreate for each phase, then TaskUpdate for status, or TodoWrite when that is the tool offered; Codex: update_plan), never the MCP task board tools - one entry per phase in order, exactly one in progress at a time, marked completed at each phase end - so phases render in the task banner. That list holds phase entries only for the whole task; keep step-level or per-file todos out of it. Prose alone leaves the banner empty. Split it into the fewest substantial phases the task genuinely needs plus a final integration/whole-task phase; most tasks need one to three work phases. Each phase is a complete reviewable slice of behavior. Never split per file, per function, or per trivial step: over-splitting spends review turns instead of finishing the job. Add a phase only when a real review boundary, risky decision, or independent behavior separates the work. Complete exactly one phase per turn, and finish the whole phase in that turn rather than stopping early. Do not run tests, typecheck, lint, or builds per phase; write the tests the change needs, then run verification once in the final phase over the whole task. Exception: a phase whose own correctness is unclear may run the single narrowest check that resolves it. End every phase turn with phase completed, todo status, changed behavior/files, and remaining phases; do not start the next phase in the same turn. Server then wakes the paired Supervisor, which resumes you through ${FUSION_ADVICE_PROMPT_PREFIX}; a user message may also revise or resume the work. Final phase verifies the entire task against the original request and labels it ready for whole-task review. Supervisor is unreachable during your turn. Never spawn/use another Supervisor thread/subagent or attribute Supervisor decisions without ${FUSION_ADVICE_PROMPT_PREFIX}. Within the current phase, continue when straightforward or evidence is clear. For a concrete unresolved tradeoff, correctness risk, or design decision materially needing judgment, stop safely before the risky choice; final response states the exact question and why review is needed. Evaluate/follow Supervisor advice unless conflicting with user request or verified repo state.`;
108698
+ const FUSION_WATCHER_INSTRUCTIONS = `You are Fusion Supervisor (watcher) in an already-created native server pair. Server owns pairing and coordination and wakes you with ${FUSION_REVIEW_PROMPT_PREFIX} or ${FUSION_GATE_PROMPT_PREFIX} prompts at builder turn boundaries; this message arrived outside such a wake, so your conversational memory of the pair may be gone. The pair metadata below is authoritative: the builder thread exists and is the counterpart thread id. Never report that no builder thread exists. To resume supervision, read builder events with thread_watch_events from lastReviewedImplementerSequence with limit 50, paging forward with the last returned sequence rather than requesting a whole range at once, derive phase from artifacts (git log/status, PR, builder events, including its turn.plan.updated phase list), steer with thread_advise, and answer an open gate with thread_gate_respond. When a review or gate wake prompt specifies an explicit event range, that range wins over this metadata. Never poll or wait for the builder; deliver review or advice, then end the turn.`;
107927
108699
  const isFusionWatcherWakeMessageId = (messageId) => messageId.startsWith("fusion-review:") || messageId.startsWith("fusion-gate:");
107928
108700
  const fusionPairContext = (pair, role) => {
107929
108701
  const counterpartThreadId = role === "implementer" ? pair.watcherThreadId : pair.implementerThreadId;
@@ -108011,7 +108783,7 @@ function resolvePendingWorkspaceCleanupGroups(input) {
108011
108783
  }
108012
108784
  return groups;
108013
108785
  }
108014
- const make$3 = Effect.gen(function* () {
108786
+ const make$4 = Effect.gen(function* () {
108015
108787
  const crypto = yield* Crypto.Crypto;
108016
108788
  const orchestrationEngine = yield* OrchestrationEngineService;
108017
108789
  const projectionSnapshotQuery = yield* ProjectionSnapshotQuery;
@@ -108890,7 +109662,7 @@ const make$3 = Effect.gen(function* () {
108890
109662
  drain: Effect.all([worker.drain, forceStopDrain], { discard: true }).pipe(Effect.asVoid)
108891
109663
  };
108892
109664
  });
108893
- const ProviderCommandReactorLive = Layer.effect(ProviderCommandReactor, make$3);
109665
+ const ProviderCommandReactorLive = Layer.effect(ProviderCommandReactor, make$4);
108894
109666
  //#endregion
108895
109667
  //#region src/checkpointing/Diffs.ts
108896
109668
  function parseTurnDiffFilesFromUnifiedDiff(diff) {
@@ -108920,7 +109692,7 @@ function checkpointStatusFromRuntime(status) {
108920
109692
  default: return "ready";
108921
109693
  }
108922
109694
  }
108923
- const make$2 = Effect.gen(function* () {
109695
+ const make$3 = Effect.gen(function* () {
108924
109696
  const randomUUID = (yield* Crypto.Crypto).randomUUIDv4;
108925
109697
  const serverEventId = randomUUID.pipe(Effect.map(EventId.make));
108926
109698
  const serverCommandId = (tag) => randomUUID.pipe(Effect.map((uuid) => CommandId.make(`server:${tag}:${uuid}`)));
@@ -109402,7 +110174,7 @@ const make$2 = Effect.gen(function* () {
109402
110174
  drain: worker.drain
109403
110175
  };
109404
110176
  });
109405
- const CheckpointReactorLive = Layer.effect(CheckpointReactor, make$2);
110177
+ const CheckpointReactorLive = Layer.effect(CheckpointReactor, make$3);
109406
110178
  //#endregion
109407
110179
  //#region src/orchestration/Layers/FusionWatcherReactor.ts
109408
110180
  const GATE_TIMEOUT_SWEEP_INTERVAL = "10 seconds";
@@ -109492,7 +110264,7 @@ Then thread_gate_respond, threadId ${input.implementerThreadId}, gateId ${input.
109492
110264
  - "object" plus message: send objection, spend round. After ${input.roundCap} objections, escalate to user.
109493
110265
 
109494
110266
  No answer within ${Math.round(input.gateTimeoutMs / 1e3)} seconds: fail open, record unwatched. Answer, briefly explain to user, end turn; never wait for builder.`;
109495
- const make$1 = Effect.gen(function* () {
110267
+ const make$2 = Effect.gen(function* () {
109496
110268
  const orchestrationEngine = yield* OrchestrationEngineService;
109497
110269
  const projectionSnapshotQuery = yield* ProjectionSnapshotQuery;
109498
110270
  /**
@@ -110092,7 +110864,7 @@ const make$1 = Effect.gen(function* () {
110092
110864
  sweepGates: sweepGateTimeouts.pipe(Effect.catchCause((cause) => Effect.logWarning("fusion gate timeout sweep failed", { cause: Cause.pretty(cause) })))
110093
110865
  };
110094
110866
  });
110095
- const FusionWatcherReactorLive = Layer.effect(FusionWatcherReactor, make$1);
110867
+ const FusionWatcherReactorLive = Layer.effect(FusionWatcherReactor, make$2);
110096
110868
  //#endregion
110097
110869
  //#region src/orchestration/Layers/ThreadDeletionReactor.ts
110098
110870
  const logCleanupCauseUnlessInterrupted = ({ effect, message, threadId }) => effect.pipe(Effect.catchCause((cause) => {
@@ -110102,7 +110874,7 @@ const logCleanupCauseUnlessInterrupted = ({ effect, message, threadId }) => effe
110102
110874
  cause: Cause.pretty(cause)
110103
110875
  });
110104
110876
  }));
110105
- const make = Effect.gen(function* () {
110877
+ const make$1 = Effect.gen(function* () {
110106
110878
  const orchestrationEngine = yield* OrchestrationEngineService;
110107
110879
  const providerService = yield* ProviderService;
110108
110880
  const terminalManager = yield* TerminalManager;
@@ -110143,7 +110915,213 @@ const make = Effect.gen(function* () {
110143
110915
  drain: worker.drain
110144
110916
  };
110145
110917
  });
110146
- const ThreadDeletionReactorLive = Layer.effect(ThreadDeletionReactor, make);
110918
+ const ThreadDeletionReactorLive = Layer.effect(ThreadDeletionReactor, make$1);
110919
+ //#endregion
110920
+ //#region src/orchestration/scheduledTasks.ts
110921
+ /** Milliseconds until a task is due; 0 for anything already overdue. */
110922
+ function scheduledTaskDelayMs(runAt, nowMs) {
110923
+ const runAtMs = Date.parse(runAt);
110924
+ if (Number.isNaN(runAtMs)) return 0;
110925
+ return Math.max(0, runAtMs - nowMs);
110926
+ }
110927
+ function pendingScheduledTasks(threads) {
110928
+ return threads.flatMap((thread) => thread.deletedAt !== null ? [] : (thread.scheduledTasks ?? []).filter((task) => task.status === "pending").map((task) => ({
110929
+ threadId: thread.id,
110930
+ task
110931
+ })));
110932
+ }
110933
+ /**
110934
+ * Task ids are unique per thread only, so every key derived from a task is
110935
+ * scoped by its thread.
110936
+ */
110937
+ function scheduledTaskKey(threadId, taskId) {
110938
+ return `${threadId}:${taskId}`;
110939
+ }
110940
+ /**
110941
+ * Every turn-start attempt gets its own command id: the engine keeps a
110942
+ * rejected receipt per command id, so reusing one would replay the first
110943
+ * rejection (a busy thread) on every retry. Crash idempotence comes from the
110944
+ * deterministic message id instead: a turn that already sent
110945
+ * {@link scheduledTaskMessageId} is never started again.
110946
+ */
110947
+ function scheduledTaskTurnCommandId(threadId, taskId, attemptToken) {
110948
+ return CommandId.make(`scheduled-task:${threadId}:${taskId}:turn:${attemptToken}`);
110949
+ }
110950
+ function scheduledTaskFireCommandId(threadId, taskId) {
110951
+ return CommandId.make(`scheduled-task:${threadId}:${taskId}:fire`);
110952
+ }
110953
+ function scheduledTaskMessageId(threadId, taskId) {
110954
+ return MessageId.make(`scheduled-task:${threadId}:${taskId}`);
110955
+ }
110956
+ //#endregion
110957
+ //#region src/orchestration/Layers/ScheduledTaskReactor.ts
110958
+ /**
110959
+ * A turn that cannot start (thread busy, provider down) is retried on this
110960
+ * cadence before the task is marked failed; the count resets with the process,
110961
+ * and the periodic reconcile re-arms anything still pending.
110962
+ */
110963
+ const SCHEDULED_TASK_TURN_RETRY_DELAY = Duration.minutes(2);
110964
+ /** Re-arms pending tasks that lost their timer (fire failure, missed event). */
110965
+ const SCHEDULED_TASK_RECONCILE_INTERVAL = Duration.minutes(5);
110966
+ /** Engine-level failures while firing (e.g. persistence) back off briefly and retry. */
110967
+ const FIRE_RETRY = {
110968
+ schedule: Schedule.exponential(Duration.seconds(10)),
110969
+ times: 3
110970
+ };
110971
+ const make = Effect.gen(function* () {
110972
+ const orchestrationEngine = yield* OrchestrationEngineService;
110973
+ const projectionSnapshotQuery = yield* ProjectionSnapshotQuery;
110974
+ const threadMessages = yield* ProjectionThreadMessageRepository;
110975
+ const timers = /* @__PURE__ */ new Map();
110976
+ const turnAttempts = /* @__PURE__ */ new Map();
110977
+ const nowIso = Effect.map(DateTime.now, DateTime.formatIso);
110978
+ const disarm = (threadId, taskId) => Effect.gen(function* () {
110979
+ const key = scheduledTaskKey(threadId, taskId);
110980
+ turnAttempts.delete(key);
110981
+ const fiber = timers.get(key);
110982
+ if (fiber === void 0) return;
110983
+ timers.delete(key);
110984
+ yield* Fiber.interrupt(fiber);
110985
+ });
110986
+ const describeFailure = (failure) => failure instanceof Error ? failure.message : String(failure);
110987
+ /**
110988
+ * Starts the turn, then marks the task fired. The turn's user message has a
110989
+ * deterministic id, so a crash between the two steps re-fires on the next
110990
+ * boot without a second turn: an existing message means the turn already
110991
+ * started. Each attempt uses a fresh command id because the engine replays
110992
+ * a rejected receipt for a reused one. A turn that cannot start re-arms the
110993
+ * task for a bounded number of retries and only then records the failure.
110994
+ */
110995
+ const fire = Effect.fn("ScheduledTaskReactor.fire")(function* (threadId, task) {
110996
+ const key = scheduledTaskKey(threadId, task.id);
110997
+ timers.delete(key);
110998
+ const thread = (yield* projectionSnapshotQuery.getCommandReadModel()).threads.find((entry) => entry.id === threadId);
110999
+ const current = thread?.scheduledTasks?.find((entry) => entry.id === task.id);
111000
+ if (thread === void 0 || thread.deletedAt !== null || current?.status !== "pending") {
111001
+ turnAttempts.delete(key);
111002
+ return;
111003
+ }
111004
+ const messageId = scheduledTaskMessageId(threadId, task.id);
111005
+ const turnResult = Option.isSome(yield* threadMessages.getByMessageId({ messageId })) ? { _tag: "Success" } : yield* orchestrationEngine.dispatch({
111006
+ type: "thread.turn.start",
111007
+ commandId: scheduledTaskTurnCommandId(threadId, task.id, String(yield* Clock.currentTimeMillis)),
111008
+ threadId,
111009
+ message: {
111010
+ messageId,
111011
+ role: "user",
111012
+ text: task.prompt,
111013
+ attachments: []
111014
+ },
111015
+ runtimeMode: thread.runtimeMode,
111016
+ interactionMode: thread.interactionMode,
111017
+ compressMode: thread.compressMode,
111018
+ unpromptedSubagents: thread.unpromptedSubagents,
111019
+ createdAt: yield* nowIso
111020
+ }).pipe(Effect.result);
111021
+ if (turnResult._tag === "Failure") {
111022
+ const attempts = (turnAttempts.get(key) ?? 0) + 1;
111023
+ if (attempts < 10) {
111024
+ turnAttempts.set(key, attempts);
111025
+ yield* Effect.logInfo("scheduled task turn could not start; retrying", {
111026
+ threadId,
111027
+ taskId: task.id,
111028
+ attempt: attempts,
111029
+ failure: describeFailure(turnResult.failure)
111030
+ });
111031
+ yield* armAfter(threadId, task, SCHEDULED_TASK_TURN_RETRY_DELAY);
111032
+ return;
111033
+ }
111034
+ turnAttempts.delete(key);
111035
+ yield* orchestrationEngine.dispatch({
111036
+ type: "thread.scheduled-task.fire",
111037
+ commandId: scheduledTaskFireCommandId(threadId, task.id),
111038
+ threadId,
111039
+ taskId: task.id,
111040
+ firedAt: yield* nowIso,
111041
+ failure: `Could not start the turn after ${attempts} attempts: ${describeFailure(turnResult.failure)}`
111042
+ });
111043
+ return;
111044
+ }
111045
+ turnAttempts.delete(key);
111046
+ yield* orchestrationEngine.dispatch({
111047
+ type: "thread.scheduled-task.fire",
111048
+ commandId: scheduledTaskFireCommandId(threadId, task.id),
111049
+ threadId,
111050
+ taskId: task.id,
111051
+ firedAt: yield* nowIso
111052
+ });
111053
+ });
111054
+ const armAfter = (threadId, task, delay) => Effect.gen(function* () {
111055
+ const key = scheduledTaskKey(threadId, task.id);
111056
+ const existing = timers.get(key);
111057
+ if (existing !== void 0) {
111058
+ timers.delete(key);
111059
+ yield* Fiber.interrupt(existing);
111060
+ }
111061
+ const fiber = yield* Effect.forkDetach(Effect.sleep(delay).pipe(Effect.flatMap(() => fire(threadId, task).pipe(Effect.retry(FIRE_RETRY))), Effect.catchCause((cause) => Cause.hasInterruptsOnly(cause) ? Effect.void : Effect.logWarning("scheduled task failed to fire; it stays pending until reconcile re-arms it", {
111062
+ threadId,
111063
+ taskId: task.id,
111064
+ cause: Cause.pretty(cause)
111065
+ }))));
111066
+ timers.set(key, fiber);
111067
+ });
111068
+ const arm = (threadId, task) => Effect.gen(function* () {
111069
+ const delayMs = scheduledTaskDelayMs(task.runAt, yield* Clock.currentTimeMillis);
111070
+ yield* armAfter(threadId, task, Duration.millis(delayMs));
111071
+ });
111072
+ /** Arms every pending task that has no live timer; the read model is authoritative. */
111073
+ const reconcile = Effect.fn("ScheduledTaskReactor.reconcile")(function* () {
111074
+ const readModel = yield* projectionSnapshotQuery.getCommandReadModel();
111075
+ for (const pending of pendingScheduledTasks(readModel.threads)) {
111076
+ if (timers.has(scheduledTaskKey(pending.threadId, pending.task.id))) continue;
111077
+ yield* arm(pending.threadId, pending.task);
111078
+ }
111079
+ });
111080
+ const processEvent = Effect.fn("ScheduledTaskReactor.processEvent")(function* (event) {
111081
+ switch (event.type) {
111082
+ case "thread.scheduled-task.created":
111083
+ yield* arm(event.payload.threadId, event.payload.task);
111084
+ return;
111085
+ case "thread.scheduled-task.cancelled":
111086
+ case "thread.scheduled-task.fired":
111087
+ yield* disarm(event.payload.threadId, event.payload.taskId);
111088
+ return;
111089
+ case "thread.deleted":
111090
+ for (const key of Array.from(timers.keys())) {
111091
+ if (!key.startsWith(`${event.payload.threadId}:`)) continue;
111092
+ const fiber = timers.get(key);
111093
+ timers.delete(key);
111094
+ turnAttempts.delete(key);
111095
+ if (fiber !== void 0) yield* Fiber.interrupt(fiber);
111096
+ }
111097
+ return;
111098
+ }
111099
+ });
111100
+ const processEventSafely = (event) => processEvent(event).pipe(Effect.catchCause((cause) => Cause.hasInterruptsOnly(cause) ? Effect.failCause(cause) : Effect.logWarning("scheduled task reactor failed to process event", {
111101
+ eventType: event.type,
111102
+ cause: Cause.pretty(cause)
111103
+ })));
111104
+ const worker = yield* makeDrainableWorker(processEventSafely);
111105
+ const logReconcileFailure = (cause) => Effect.logWarning("scheduled task reconcile failed", { cause: Cause.pretty(cause) });
111106
+ return {
111107
+ start: Effect.fn("start")(function* () {
111108
+ yield* Effect.forkScoped(Stream.runForEach(orchestrationEngine.streamDomainEvents, (event) => {
111109
+ switch (event.type) {
111110
+ case "thread.scheduled-task.created":
111111
+ case "thread.scheduled-task.cancelled":
111112
+ case "thread.scheduled-task.fired":
111113
+ case "thread.deleted": return worker.enqueue(event);
111114
+ default: return Effect.void;
111115
+ }
111116
+ }));
111117
+ yield* reconcile().pipe(Effect.catchCause(logReconcileFailure));
111118
+ yield* Effect.forkScoped(Effect.repeat(reconcile().pipe(Effect.catchCause(logReconcileFailure)), Schedule.spaced(SCHEDULED_TASK_RECONCILE_INTERVAL)).pipe(Effect.delay(SCHEDULED_TASK_RECONCILE_INTERVAL)));
111119
+ yield* Effect.addFinalizer(() => Effect.forEach([...timers.values()], (fiber) => Fiber.interrupt(fiber), { discard: true }).pipe(Effect.tap(() => Effect.sync(() => timers.clear()))));
111120
+ }),
111121
+ drain: worker.drain
111122
+ };
111123
+ });
111124
+ const ScheduledTaskReactorLive = Layer.effect(ScheduledTaskReactor, make);
110147
111125
  //#endregion
110148
111126
  //#region src/provider/providerStatusCache.ts
110149
111127
  const decodeProviderStatusCache = Schema$1.decodeUnknownEffect(Schema$1.fromJsonString(ServerProvider));
@@ -110924,7 +111902,7 @@ const PlatformServicesLive = Layer.unwrap(Effect.gen(function* () {
110924
111902
  return layer;
110925
111903
  }
110926
111904
  }));
110927
- const ReactorLayerLive = Layer.empty.pipe(Layer.provideMerge(OrchestrationReactorLive), Layer.provideMerge(ProviderRuntimeIngestionLive), Layer.provideMerge(ProviderCommandReactorLive), Layer.provideMerge(CheckpointReactorLive), Layer.provideMerge(ThreadDeletionReactorLive), Layer.provideMerge(FusionWatcherReactorLive), Layer.provideMerge(RuntimeReceiptBusLive));
111905
+ const ReactorLayerLive = Layer.empty.pipe(Layer.provideMerge(OrchestrationReactorLive), Layer.provideMerge(ProviderRuntimeIngestionLive), Layer.provideMerge(ProviderCommandReactorLive), Layer.provideMerge(CheckpointReactorLive), Layer.provideMerge(ThreadDeletionReactorLive), Layer.provideMerge(FusionWatcherReactorLive), Layer.provideMerge(ScheduledTaskReactorLive), Layer.provideMerge(RuntimeReceiptBusLive));
110928
111906
  const ProviderSessionDirectoryLayerLive = ProviderSessionDirectoryLive.pipe(Layer.provide(layer$4));
110929
111907
  const ProviderLayerLive = ProviderServiceLive.pipe(Layer.provide(ProviderAdapterRegistryLive), Layer.provideMerge(ProviderSessionDirectoryLayerLive));
110930
111908
  const PersistenceLayerLive = Layer.empty.pipe(Layer.provideMerge(layerConfig));