@p4code/cli 0.2.6 → 0.2.8

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
@@ -125,7 +125,7 @@ const closeServer = (server) => {
125
125
  * NetService - Service tag for startup networking helpers.
126
126
  */
127
127
  var NetService = class extends Context.Service()("@p4code/shared/Net/NetService") {};
128
- const make$87 = () => {
128
+ const make$88 = () => {
129
129
  /**
130
130
  * Returns true when a TCP server can bind to {host, port}.
131
131
  * `EADDRNOTAVAIL` is treated as available so IPv6-absent hosts don't fail
@@ -234,10 +234,10 @@ const make$87 = () => {
234
234
  })
235
235
  };
236
236
  };
237
- const layer$79 = Layer.sync(NetService, make$87);
237
+ const layer$79 = Layer.sync(NetService, make$88);
238
238
  //#endregion
239
239
  //#region package.json
240
- var version = "0.2.6";
240
+ var version = "0.2.8";
241
241
  //#endregion
242
242
  //#region src/config.ts
243
243
  /**
@@ -258,8 +258,8 @@ var ServerConfig$1 = class extends Context.Service()("@p4code/cli/config/ServerC
258
258
  /** @deprecated Import and use `layerTest` from this module. */
259
259
  static layerTest = (cwd, baseDirOrPrefix) => layerTest$3(cwd, baseDirOrPrefix);
260
260
  };
261
- const make$86 = (config) => ServerConfig$1.of(config);
262
- const layer$78 = (config) => Layer.succeed(ServerConfig$1, make$86(config));
261
+ const make$87 = (config) => ServerConfig$1.of(config);
262
+ const layer$78 = (config) => Layer.succeed(ServerConfig$1, make$87(config));
263
263
  const deriveServerPaths = Effect.fn(function* (baseDir, devUrl, options = {}) {
264
264
  const { join } = yield* Path.Path;
265
265
  const stateDir = join(baseDir, devUrl !== void 0 && !options.baseDirIsExplicit ? "dev" : "userdata");
@@ -1005,6 +1005,7 @@ const makeEntityId = (brand) => {
1005
1005
  return TrimmedNonEmptyString.pipe(Schema$1.brand(brand));
1006
1006
  };
1007
1007
  const ThreadId = makeEntityId("ThreadId");
1008
+ const ThreadPairId = makeEntityId("ThreadPairId");
1008
1009
  const ProjectId = makeEntityId("ProjectId");
1009
1010
  /**
1010
1011
  * Identifies a task on the agent task board. Unrelated to {@link RuntimeTaskId},
@@ -1830,6 +1831,8 @@ const OrchestrationMessage = Schema$1.Struct({
1830
1831
  createdAt: IsoDateTime,
1831
1832
  updatedAt: IsoDateTime
1832
1833
  });
1834
+ const FUSION_REVIEW_PROMPT_PREFIX = "[fusion-review]";
1835
+ const FUSION_NO_OBJECTION_TEXT = "[fusion-no-objection]";
1833
1836
  const OrchestrationProposedPlanId = TrimmedNonEmptyString;
1834
1837
  const OrchestrationProposedPlan = Schema$1.Struct({
1835
1838
  id: OrchestrationProposedPlanId,
@@ -1914,6 +1917,12 @@ const OrchestrationLatestTurn = Schema$1.Struct({
1914
1917
  assistantMessageId: Schema$1.NullOr(MessageId),
1915
1918
  sourceProposedPlan: Schema$1.optional(SourceProposedPlanReference)
1916
1919
  });
1920
+ const OrchestrationTurnSummary = Schema$1.Struct({
1921
+ turnId: TurnId,
1922
+ state: OrchestrationLatestTurnState,
1923
+ startedAt: Schema$1.NullOr(IsoDateTime),
1924
+ completedAt: Schema$1.NullOr(IsoDateTime)
1925
+ });
1917
1926
  const OrchestrationThread = Schema$1.Struct({
1918
1927
  id: ThreadId,
1919
1928
  projectId: ProjectId,
@@ -1926,6 +1935,7 @@ const OrchestrationThread = Schema$1.Struct({
1926
1935
  branch: Schema$1.NullOr(TrimmedNonEmptyString),
1927
1936
  worktreePath: Schema$1.NullOr(TrimmedNonEmptyString),
1928
1937
  latestTurn: Schema$1.NullOr(OrchestrationLatestTurn),
1938
+ turns: Schema$1.optional(Schema$1.Array(OrchestrationTurnSummary)),
1929
1939
  createdAt: IsoDateTime,
1930
1940
  updatedAt: IsoDateTime,
1931
1941
  archivedAt: Schema$1.NullOr(IsoDateTime).pipe(Schema$1.withDecodingDefault(Effect.succeed(null))),
@@ -1940,10 +1950,20 @@ const OrchestrationThread = Schema$1.Struct({
1940
1950
  checkpoints: Schema$1.Array(OrchestrationCheckpointSummary),
1941
1951
  session: Schema$1.NullOr(OrchestrationSession)
1942
1952
  });
1953
+ /** Persisted relationship between two otherwise ordinary threads. */
1954
+ const OrchestrationThreadPair = Schema$1.Struct({
1955
+ id: ThreadPairId,
1956
+ implementerThreadId: ThreadId,
1957
+ watcherThreadId: ThreadId,
1958
+ lastReviewedImplementerSequence: NonNegativeInt,
1959
+ createdAt: IsoDateTime,
1960
+ detachedAt: Schema$1.NullOr(IsoDateTime)
1961
+ });
1943
1962
  const OrchestrationReadModel = Schema$1.Struct({
1944
1963
  snapshotSequence: NonNegativeInt,
1945
1964
  projects: Schema$1.Array(OrchestrationProject),
1946
1965
  threads: Schema$1.Array(OrchestrationThread),
1966
+ threadPairs: Schema$1.optional(Schema$1.Array(OrchestrationThreadPair)),
1947
1967
  updatedAt: IsoDateTime
1948
1968
  });
1949
1969
  const OrchestrationProjectShell = Schema$1.Struct({
@@ -1987,6 +2007,7 @@ const OrchestrationShellSnapshot = Schema$1.Struct({
1987
2007
  snapshotSequence: NonNegativeInt,
1988
2008
  projects: Schema$1.Array(OrchestrationProjectShell),
1989
2009
  threads: Schema$1.Array(OrchestrationThreadShell),
2010
+ threadPairs: Schema$1.optional(Schema$1.Array(OrchestrationThreadPair)),
1990
2011
  updatedAt: IsoDateTime
1991
2012
  });
1992
2013
  const OrchestrationShellStreamEvent = Schema$1.Union([
@@ -2009,6 +2030,16 @@ const OrchestrationShellStreamEvent = Schema$1.Union([
2009
2030
  kind: Schema$1.Literal("thread-removed"),
2010
2031
  sequence: NonNegativeInt,
2011
2032
  threadId: ThreadId
2033
+ }),
2034
+ Schema$1.Struct({
2035
+ kind: Schema$1.Literal("thread-pair-upserted"),
2036
+ sequence: NonNegativeInt,
2037
+ pair: OrchestrationThreadPair
2038
+ }),
2039
+ Schema$1.Struct({
2040
+ kind: Schema$1.Literal("thread-pair-removed"),
2041
+ sequence: NonNegativeInt,
2042
+ pairId: ThreadPairId
2012
2043
  })
2013
2044
  ]);
2014
2045
  const OrchestrationShellStreamItem = Schema$1.Union([
@@ -2275,6 +2306,20 @@ const ThreadSessionStopCommand = Schema$1.Struct({
2275
2306
  threadId: ThreadId,
2276
2307
  createdAt: IsoDateTime
2277
2308
  });
2309
+ const ThreadPairCreateCommand = Schema$1.Struct({
2310
+ type: Schema$1.Literal("thread-pair.create"),
2311
+ commandId: CommandId,
2312
+ pairId: ThreadPairId,
2313
+ implementerThreadId: ThreadId,
2314
+ watcherThreadId: ThreadId,
2315
+ createdAt: IsoDateTime
2316
+ });
2317
+ const ThreadPairDetachCommand = Schema$1.Struct({
2318
+ type: Schema$1.Literal("thread-pair.detach"),
2319
+ commandId: CommandId,
2320
+ pairId: ThreadPairId,
2321
+ createdAt: IsoDateTime
2322
+ });
2278
2323
  const DispatchableClientOrchestrationCommand = Schema$1.Union([
2279
2324
  ProjectCreateCommand,
2280
2325
  ProjectMetaUpdateCommand,
@@ -2297,7 +2342,9 @@ const DispatchableClientOrchestrationCommand = Schema$1.Union([
2297
2342
  ThreadApprovalRespondCommand,
2298
2343
  ThreadUserInputRespondCommand,
2299
2344
  ThreadCheckpointRevertCommand,
2300
- ThreadSessionStopCommand
2345
+ ThreadSessionStopCommand,
2346
+ ThreadPairCreateCommand,
2347
+ ThreadPairDetachCommand
2301
2348
  ]);
2302
2349
  const ClientOrchestrationCommand = Schema$1.Union([
2303
2350
  ProjectCreateCommand,
@@ -2321,7 +2368,9 @@ const ClientOrchestrationCommand = Schema$1.Union([
2321
2368
  ThreadApprovalRespondCommand,
2322
2369
  ThreadUserInputRespondCommand,
2323
2370
  ThreadCheckpointRevertCommand,
2324
- ThreadSessionStopCommand
2371
+ ThreadSessionStopCommand,
2372
+ ThreadPairCreateCommand,
2373
+ ThreadPairDetachCommand
2325
2374
  ]);
2326
2375
  const ThreadSessionSetCommand = Schema$1.Struct({
2327
2376
  type: Schema$1.Literal("thread.session.set"),
@@ -2330,6 +2379,26 @@ const ThreadSessionSetCommand = Schema$1.Struct({
2330
2379
  session: OrchestrationSession,
2331
2380
  createdAt: IsoDateTime
2332
2381
  });
2382
+ const ThreadTurnCompleteCommand = Schema$1.Struct({
2383
+ type: Schema$1.Literal("thread.turn.complete"),
2384
+ commandId: CommandId,
2385
+ threadId: ThreadId,
2386
+ turnId: Schema$1.optional(TurnId),
2387
+ state: Schema$1.Literals([
2388
+ "completed",
2389
+ "failed",
2390
+ "interrupted",
2391
+ "cancelled"
2392
+ ]),
2393
+ completedAt: IsoDateTime
2394
+ });
2395
+ const ThreadPairCursorAdvanceCommand = Schema$1.Struct({
2396
+ type: Schema$1.Literal("thread-pair.cursor.advance"),
2397
+ commandId: CommandId,
2398
+ pairId: ThreadPairId,
2399
+ implementerSequence: NonNegativeInt,
2400
+ advancedAt: IsoDateTime
2401
+ });
2333
2402
  const ThreadMessageAssistantDeltaCommand = Schema$1.Struct({
2334
2403
  type: Schema$1.Literal("thread.message.assistant.delta"),
2335
2404
  commandId: CommandId,
@@ -2383,6 +2452,8 @@ const ThreadRevertCompleteCommand = Schema$1.Struct({
2383
2452
  });
2384
2453
  const InternalOrchestrationCommand = Schema$1.Union([
2385
2454
  ThreadSessionSetCommand,
2455
+ ThreadTurnCompleteCommand,
2456
+ ThreadPairCursorAdvanceCommand,
2386
2457
  ThreadMessageAssistantDeltaCommand,
2387
2458
  ThreadMessageAssistantCompleteCommand,
2388
2459
  ThreadProposedPlanUpsertCommand,
@@ -2419,9 +2490,17 @@ const OrchestrationEventType = Schema$1.Literals([
2419
2490
  "thread.session-set",
2420
2491
  "thread.proposed-plan-upserted",
2421
2492
  "thread.turn-diff-completed",
2422
- "thread.activity-appended"
2493
+ "thread.activity-appended",
2494
+ "thread.turn-completed",
2495
+ "thread-pair.created",
2496
+ "thread-pair.detached",
2497
+ "thread-pair.cursor-advanced"
2498
+ ]);
2499
+ const OrchestrationAggregateKind = Schema$1.Literals([
2500
+ "project",
2501
+ "thread",
2502
+ "thread-pair"
2423
2503
  ]);
2424
- const OrchestrationAggregateKind = Schema$1.Literals(["project", "thread"]);
2425
2504
  const OrchestrationActorKind = Schema$1.Literals([
2426
2505
  "client",
2427
2506
  "server",
@@ -2588,6 +2667,33 @@ const ThreadSessionSetPayload$1 = Schema$1.Struct({
2588
2667
  threadId: ThreadId,
2589
2668
  session: OrchestrationSession
2590
2669
  });
2670
+ const ThreadTurnCompletedPayload = Schema$1.Struct({
2671
+ threadId: ThreadId,
2672
+ turnId: Schema$1.NullOr(TurnId),
2673
+ state: Schema$1.Literals([
2674
+ "completed",
2675
+ "failed",
2676
+ "interrupted",
2677
+ "cancelled"
2678
+ ]),
2679
+ completedAt: IsoDateTime
2680
+ });
2681
+ const ThreadPairCreatedPayload$1 = Schema$1.Struct({
2682
+ pairId: ThreadPairId,
2683
+ implementerThreadId: ThreadId,
2684
+ watcherThreadId: ThreadId,
2685
+ lastReviewedImplementerSequence: NonNegativeInt,
2686
+ createdAt: IsoDateTime
2687
+ });
2688
+ const ThreadPairDetachedPayload$1 = Schema$1.Struct({
2689
+ pairId: ThreadPairId,
2690
+ detachedAt: IsoDateTime
2691
+ });
2692
+ const ThreadPairCursorAdvancedPayload$1 = Schema$1.Struct({
2693
+ pairId: ThreadPairId,
2694
+ implementerSequence: NonNegativeInt,
2695
+ advancedAt: IsoDateTime
2696
+ });
2591
2697
  const ThreadProposedPlanUpsertedPayload$1 = Schema$1.Struct({
2592
2698
  threadId: ThreadId,
2593
2699
  proposedPlan: OrchestrationProposedPlan
@@ -2617,7 +2723,11 @@ const EventBaseFields = {
2617
2723
  sequence: NonNegativeInt,
2618
2724
  eventId: EventId,
2619
2725
  aggregateKind: OrchestrationAggregateKind,
2620
- aggregateId: Schema$1.Union([ProjectId, ThreadId]),
2726
+ aggregateId: Schema$1.Union([
2727
+ ProjectId,
2728
+ ThreadId,
2729
+ ThreadPairId
2730
+ ]),
2621
2731
  occurredAt: IsoDateTime,
2622
2732
  commandId: Schema$1.NullOr(CommandId),
2623
2733
  causationEventId: Schema$1.NullOr(EventId),
@@ -2764,6 +2874,26 @@ const OrchestrationEvent = Schema$1.Union([
2764
2874
  ...EventBaseFields,
2765
2875
  type: Schema$1.Literal("thread.activity-appended"),
2766
2876
  payload: ThreadActivityAppendedPayload$1
2877
+ }),
2878
+ Schema$1.Struct({
2879
+ ...EventBaseFields,
2880
+ type: Schema$1.Literal("thread.turn-completed"),
2881
+ payload: ThreadTurnCompletedPayload
2882
+ }),
2883
+ Schema$1.Struct({
2884
+ ...EventBaseFields,
2885
+ type: Schema$1.Literal("thread-pair.created"),
2886
+ payload: ThreadPairCreatedPayload$1
2887
+ }),
2888
+ Schema$1.Struct({
2889
+ ...EventBaseFields,
2890
+ type: Schema$1.Literal("thread-pair.detached"),
2891
+ payload: ThreadPairDetachedPayload$1
2892
+ }),
2893
+ Schema$1.Struct({
2894
+ ...EventBaseFields,
2895
+ type: Schema$1.Literal("thread-pair.cursor-advanced"),
2896
+ payload: ThreadPairCursorAdvancedPayload$1
2767
2897
  })
2768
2898
  ]);
2769
2899
  const OrchestrationThreadStreamItem = Schema$1.Union([
@@ -11558,7 +11688,7 @@ function deriveAuthClientMetadata(input) {
11558
11688
  //#endregion
11559
11689
  //#region src/auth/EnvironmentAuthPolicy.ts
11560
11690
  var EnvironmentAuthPolicy = class extends Context.Service()("@p4code/cli/auth/EnvironmentAuthPolicy") {};
11561
- const make$85 = Effect.gen(function* () {
11691
+ const make$86 = Effect.gen(function* () {
11562
11692
  const config = yield* ServerConfig$1;
11563
11693
  const isRemoteReachable = isRemoteReachableHost(config.host);
11564
11694
  const policy = config.mode === "desktop" ? isRemoteReachable ? "remote-reachable" : "desktop-managed-local" : isRemoteReachable ? "remote-reachable" : "loopback-browser";
@@ -11576,7 +11706,7 @@ const make$85 = Effect.gen(function* () {
11576
11706
  };
11577
11707
  return EnvironmentAuthPolicy.of({ getDescriptor: () => Effect.succeed(descriptor).pipe(Effect.withSpan("EnvironmentAuthPolicy.getDescriptor")) });
11578
11708
  });
11579
- const layer$77 = Layer.effect(EnvironmentAuthPolicy, make$85);
11709
+ const layer$77 = Layer.effect(EnvironmentAuthPolicy, make$86);
11580
11710
  //#endregion
11581
11711
  //#region src/persistence/Errors.ts
11582
11712
  function summarizeSchemaIssue(issue) {
@@ -11757,7 +11887,7 @@ function toPersistenceSqlOrDecodeError$6(sqlOperation, decodeOperation, correlat
11757
11887
  cause
11758
11888
  });
11759
11889
  }
11760
- const make$84 = Effect.gen(function* () {
11890
+ const make$85 = Effect.gen(function* () {
11761
11891
  const sql = yield* SqlClient.SqlClient;
11762
11892
  const createSessionRow = SqlSchema.void({
11763
11893
  Request: CreateAuthSessionInput,
@@ -11891,7 +12021,7 @@ const make$84 = Effect.gen(function* () {
11891
12021
  setLastConnectedAt
11892
12022
  };
11893
12023
  });
11894
- const layer$76 = Layer.effect(AuthSessionRepository, make$84);
12024
+ const layer$76 = Layer.effect(AuthSessionRepository, make$85);
11895
12025
  //#endregion
11896
12026
  //#region src/auth/ServerSecretStore.ts
11897
12027
  const secretStoreErrorContext = {
@@ -11958,7 +12088,7 @@ const isSecretStoreError = Schema$1.is(SecretStoreError);
11958
12088
  const isPlatformError = (value) => Predicate.isTagged(value, "PlatformError");
11959
12089
  const isSecretAlreadyExistsError = (error) => "cause" in error && isPlatformError(error.cause) && error.cause.reason._tag === "AlreadyExists";
11960
12090
  var ServerSecretStore = class extends Context.Service()("@p4code/cli/auth/ServerSecretStore") {};
11961
- const make$83 = Effect.gen(function* () {
12091
+ const make$84 = Effect.gen(function* () {
11962
12092
  const crypto = yield* Crypto.Crypto;
11963
12093
  const fileSystem = yield* FileSystem.FileSystem;
11964
12094
  const path = yield* Path.Path;
@@ -12028,7 +12158,7 @@ const make$83 = Effect.gen(function* () {
12028
12158
  remove
12029
12159
  });
12030
12160
  });
12031
- const layer$75 = Layer.effect(ServerSecretStore, make$83);
12161
+ const layer$75 = Layer.effect(ServerSecretStore, make$84);
12032
12162
  //#endregion
12033
12163
  //#region src/auth/SessionStore.ts
12034
12164
  var MalformedSessionTokenError = class extends Schema$1.TaggedErrorClass()("MalformedSessionTokenError", {}) {
@@ -12266,7 +12396,7 @@ function toAuthClientSession(input) {
12266
12396
  current: false
12267
12397
  };
12268
12398
  }
12269
- const make$82 = Effect.gen(function* () {
12399
+ const make$83 = Effect.gen(function* () {
12270
12400
  const crypto = yield* Crypto.Crypto;
12271
12401
  const serverConfig = yield* ServerConfig$1;
12272
12402
  const secretStore = yield* ServerSecretStore;
@@ -12580,7 +12710,7 @@ const make$82 = Effect.gen(function* () {
12580
12710
  markDisconnected
12581
12711
  });
12582
12712
  });
12583
- const layer$74 = Layer.effect(SessionStore, make$82).pipe(Layer.provideMerge(layer$76));
12713
+ const layer$74 = Layer.effect(SessionStore, make$83).pipe(Layer.provideMerge(layer$76));
12584
12714
  //#endregion
12585
12715
  //#region src/persistence/AuthPairingLinks.ts
12586
12716
  const AuthPairingLinkRecord = Schema$1.Struct({
@@ -12641,7 +12771,7 @@ function toPersistenceSqlOrDecodeError$5(sqlOperation, decodeOperation, correlat
12641
12771
  cause
12642
12772
  });
12643
12773
  }
12644
- const make$81 = Effect.gen(function* () {
12774
+ const make$82 = Effect.gen(function* () {
12645
12775
  const sql = yield* SqlClient.SqlClient;
12646
12776
  const createPairingLinkRow = SqlSchema.void({
12647
12777
  Request: CreateAuthPairingLinkInput,
@@ -12776,7 +12906,7 @@ const make$81 = Effect.gen(function* () {
12776
12906
  getByCredential
12777
12907
  };
12778
12908
  });
12779
- const layer$73 = Layer.effect(AuthPairingLinkRepository, make$81);
12909
+ const layer$73 = Layer.effect(AuthPairingLinkRepository, make$82);
12780
12910
  //#endregion
12781
12911
  //#region src/auth/PairingGrantStore.ts
12782
12912
  var UnknownBootstrapCredentialError = class extends Schema$1.TaggedErrorClass()("UnknownBootstrapCredentialError", {}) {
@@ -12871,7 +13001,7 @@ const DEV_STARTUP_TTL_HOURS = Duration.hours(24);
12871
13001
  const PAIRING_TOKEN_ALPHABET = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
12872
13002
  const PAIRING_TOKEN_LENGTH = 12;
12873
13003
  const PAIRING_TOKEN_REJECTION_LIMIT = Math.floor(256 / 32) * 32;
12874
- const make$80 = Effect.gen(function* () {
13004
+ const make$81 = Effect.gen(function* () {
12875
13005
  const crypto = yield* Crypto.Crypto;
12876
13006
  const config = yield* ServerConfig$1;
12877
13007
  const pairingLinks = yield* AuthPairingLinkRepository;
@@ -13069,7 +13199,7 @@ const make$80 = Effect.gen(function* () {
13069
13199
  consume
13070
13200
  });
13071
13201
  });
13072
- const layer$72 = Layer.effect(PairingGrantStore, make$80).pipe(Layer.provideMerge(layer$73));
13202
+ const layer$72 = Layer.effect(PairingGrantStore, make$81).pipe(Layer.provideMerge(layer$73));
13073
13203
  //#endregion
13074
13204
  //#region src/persistence/DatabaseSnapshot.ts
13075
13205
  /**
@@ -14824,6 +14954,69 @@ var _045_ProjectionThreadsBackgroundWork_default = Effect.gen(function* () {
14824
14954
  `;
14825
14955
  });
14826
14956
  //#endregion
14957
+ //#region src/persistence/Migrations/046_ThreadPairs.ts
14958
+ /** Persisted Fusion relationship and durable watcher cursor. */
14959
+ var _046_ThreadPairs_default = Effect.gen(function* () {
14960
+ const sql = yield* SqlClient.SqlClient;
14961
+ yield* sql`
14962
+ CREATE TABLE thread_pairs (
14963
+ pair_id TEXT PRIMARY KEY,
14964
+ implementer_thread_id TEXT NOT NULL,
14965
+ watcher_thread_id TEXT NOT NULL,
14966
+ last_reviewed_implementer_sequence INTEGER NOT NULL DEFAULT 0,
14967
+ created_at TEXT NOT NULL,
14968
+ detached_at TEXT,
14969
+ CHECK (implementer_thread_id <> watcher_thread_id),
14970
+ CHECK (last_reviewed_implementer_sequence >= 0),
14971
+ FOREIGN KEY (implementer_thread_id) REFERENCES projection_threads(thread_id),
14972
+ FOREIGN KEY (watcher_thread_id) REFERENCES projection_threads(thread_id)
14973
+ )
14974
+ `;
14975
+ yield* sql`
14976
+ CREATE UNIQUE INDEX thread_pairs_active_implementer_idx
14977
+ ON thread_pairs(implementer_thread_id)
14978
+ WHERE detached_at IS NULL
14979
+ `;
14980
+ yield* sql`
14981
+ CREATE UNIQUE INDEX thread_pairs_active_watcher_idx
14982
+ ON thread_pairs(watcher_thread_id)
14983
+ WHERE detached_at IS NULL
14984
+ `;
14985
+ yield* sql`
14986
+ CREATE TRIGGER thread_pairs_active_cross_role_insert
14987
+ BEFORE INSERT ON thread_pairs
14988
+ WHEN NEW.detached_at IS NULL AND EXISTS (
14989
+ SELECT 1
14990
+ FROM thread_pairs
14991
+ WHERE detached_at IS NULL
14992
+ AND (
14993
+ implementer_thread_id IN (NEW.implementer_thread_id, NEW.watcher_thread_id)
14994
+ OR watcher_thread_id IN (NEW.implementer_thread_id, NEW.watcher_thread_id)
14995
+ )
14996
+ )
14997
+ BEGIN
14998
+ SELECT RAISE(ABORT, 'thread already belongs to an active pair');
14999
+ END
15000
+ `;
15001
+ yield* sql`
15002
+ CREATE TRIGGER thread_pairs_active_cross_role_update
15003
+ BEFORE UPDATE OF implementer_thread_id, watcher_thread_id, detached_at ON thread_pairs
15004
+ WHEN NEW.detached_at IS NULL AND EXISTS (
15005
+ SELECT 1
15006
+ FROM thread_pairs
15007
+ WHERE pair_id <> NEW.pair_id
15008
+ AND detached_at IS NULL
15009
+ AND (
15010
+ implementer_thread_id IN (NEW.implementer_thread_id, NEW.watcher_thread_id)
15011
+ OR watcher_thread_id IN (NEW.implementer_thread_id, NEW.watcher_thread_id)
15012
+ )
15013
+ )
15014
+ BEGIN
15015
+ SELECT RAISE(ABORT, 'thread already belongs to an active pair');
15016
+ END
15017
+ `;
15018
+ });
15019
+ //#endregion
14827
15020
  //#region src/persistence/Migrations.ts
14828
15021
  /**
14829
15022
  * MigrationsLive - Migration runner with inline loader
@@ -15069,6 +15262,11 @@ const migrationEntries = [
15069
15262
  45,
15070
15263
  "ProjectionThreadsBackgroundWork",
15071
15264
  _045_ProjectionThreadsBackgroundWork_default
15265
+ ],
15266
+ [
15267
+ 46,
15268
+ "ThreadPairs",
15269
+ _046_ThreadPairs_default
15072
15270
  ]
15073
15271
  ];
15074
15272
  const makeMigrationLoader = (throughId) => Migrator.fromRecord(Object.fromEntries(migrationEntries.filter(([id]) => throughId === void 0 || id <= throughId).map(([id, name, migration]) => [`${id}_${name}`, migration])));
@@ -15319,7 +15517,7 @@ function parseBearerToken(request) {
15319
15517
  const token = header.slice(7).trim();
15320
15518
  return token.length > 0 ? token : null;
15321
15519
  }
15322
- const make$79 = Effect.gen(function* () {
15520
+ const make$80 = Effect.gen(function* () {
15323
15521
  const policy = yield* EnvironmentAuthPolicy;
15324
15522
  const bootstrapCredentials = yield* PairingGrantStore;
15325
15523
  const sessions = yield* SessionStore;
@@ -15514,7 +15712,7 @@ const make$79 = Effect.gen(function* () {
15514
15712
  issueStartupPairingUrl
15515
15713
  });
15516
15714
  });
15517
- const layer$71 = Layer.effect(EnvironmentAuth, make$79).pipe(Layer.provideMerge(layer$72), Layer.provideMerge(layer$74), Layer.provideMerge(layer$77));
15715
+ const layer$71 = Layer.effect(EnvironmentAuth, make$80).pipe(Layer.provideMerge(layer$72), Layer.provideMerge(layer$74), Layer.provideMerge(layer$77));
15518
15716
  const storageLayer = Layer.mergeAll(layer$75, layerConfig);
15519
15717
  const runtimeLayer = layer$71.pipe(Layer.provideMerge(storageLayer));
15520
15718
  //#endregion
@@ -16411,7 +16609,7 @@ const DEFAULT_LIMITS = {
16411
16609
  windowMillis: FAILURE_WINDOW_MS,
16412
16610
  blockMillis: BLOCK_DURATION_MS
16413
16611
  };
16414
- const make$78 = Effect.fn("HubAuthThrottle.make")(function* (limits = DEFAULT_LIMITS) {
16612
+ const make$79 = Effect.fn("HubAuthThrottle.make")(function* (limits = DEFAULT_LIMITS) {
16415
16613
  const state = yield* Ref.make(initialThrottleState);
16416
16614
  return HubAuthThrottle.of({
16417
16615
  shouldRefuse: Effect.gen(function* () {
@@ -16425,7 +16623,7 @@ const make$78 = Effect.fn("HubAuthThrottle.make")(function* (limits = DEFAULT_LI
16425
16623
  })
16426
16624
  });
16427
16625
  });
16428
- const layer$70 = Layer.effect(HubAuthThrottle, make$78());
16626
+ const layer$70 = Layer.effect(HubAuthThrottle, make$79());
16429
16627
  //#endregion
16430
16628
  //#region src/hub/HubAuth.ts
16431
16629
  /**
@@ -17748,7 +17946,7 @@ function stripDefaultServerSettings(current, defaults) {
17748
17946
  }
17749
17947
  return Object.is(current, defaults) ? void 0 : current;
17750
17948
  }
17751
- const make$77 = Effect.gen(function* () {
17949
+ const make$78 = Effect.gen(function* () {
17752
17950
  const { settingsPath } = yield* ServerConfig$1;
17753
17951
  const fs = yield* FileSystem.FileSystem;
17754
17952
  const pathService = yield* Path.Path;
@@ -17969,7 +18167,7 @@ const make$77 = Effect.gen(function* () {
17969
18167
  }
17970
18168
  };
17971
18169
  });
17972
- const layer$68 = Layer.effect(ServerSettingsService, make$77);
18170
+ const layer$68 = Layer.effect(ServerSettingsService, make$78);
17973
18171
  //#endregion
17974
18172
  //#region src/pathExpansion.ts
17975
18173
  /**
@@ -18343,7 +18541,7 @@ function claudeEntryFromRegistration(registration) {
18343
18541
  };
18344
18542
  }
18345
18543
  var ClaudeMcpFiles = class extends Context.Service()("@p4code/cli/mcp/ClaudeMcpFiles") {};
18346
- const make$76 = Effect.gen(function* () {
18544
+ const make$77 = Effect.gen(function* () {
18347
18545
  const fileSystem = yield* FileSystem.FileSystem;
18348
18546
  const path = yield* Path.Path;
18349
18547
  const services = yield* Effect.context();
@@ -18408,7 +18606,7 @@ const make$76 = Effect.gen(function* () {
18408
18606
  removeProject: (projectDir, name) => removeAt(Effect.succeed(projectFile(projectDir)))(name)
18409
18607
  };
18410
18608
  });
18411
- const layer$67 = Layer.effect(ClaudeMcpFiles, make$76);
18609
+ const layer$67 = Layer.effect(ClaudeMcpFiles, make$77);
18412
18610
  Layer.succeed(ClaudeMcpFiles, {
18413
18611
  readUser: Effect.succeed([]),
18414
18612
  upsertUser: () => Effect.fail(new McpRegistryError({ detail: "No Claude config in tests." })),
@@ -18544,7 +18742,7 @@ const decodeClientRegistration = Schema$1.decodeUnknownExit(ClientRegistrationRe
18544
18742
  const decodeTokenResponse = Schema$1.decodeUnknownExit(TokenResponse);
18545
18743
  var McpOAuth = class extends Context.Service()("@p4code/cli/mcp/McpOAuth") {};
18546
18744
  const registryError = (detail) => new McpRegistryError({ detail });
18547
- const make$75 = Effect.gen(function* () {
18745
+ const make$76 = Effect.gen(function* () {
18548
18746
  const config = yield* ServerConfig$1;
18549
18747
  const secrets = yield* ServerSecretStore;
18550
18748
  const http = yield* HttpClient.HttpClient;
@@ -18864,7 +19062,7 @@ const make$75 = Effect.gen(function* () {
18864
19062
  accessTokenFor
18865
19063
  };
18866
19064
  });
18867
- const layer$66 = Layer.effect(McpOAuth, make$75);
19065
+ const layer$66 = Layer.effect(McpOAuth, make$76);
18868
19066
  Layer.succeed(McpOAuth, {
18869
19067
  statusFor: () => Effect.succeed(Option.none()),
18870
19068
  begin: () => Effect.fail(new McpRegistryError({ detail: "OAuth sign-in is not available." })),
@@ -18882,7 +19080,7 @@ const decodeRegistration$1 = Schema$1.decodeUnknownExit(RegistrationFromJson$1);
18882
19080
  const encodeRegistration = Schema$1.encodeSync(RegistrationFromJson$1);
18883
19081
  var McpRegistry = class extends Context.Service()("@p4code/cli/mcp/McpRegistry") {};
18884
19082
  const slotsOf = (registration) => registration.secrets ?? [];
18885
- const make$74 = Effect.gen(function* () {
19083
+ const make$75 = Effect.gen(function* () {
18886
19084
  const config = yield* ServerConfig$1;
18887
19085
  const secrets = yield* ServerSecretStore;
18888
19086
  const oauth = yield* McpOAuth;
@@ -19033,7 +19231,7 @@ const make$74 = Effect.gen(function* () {
19033
19231
  }).pipe(Effect.provide(services), Effect.catchCause((cause) => Effect.logWarning("mcp registry resolve failed", { cause }).pipe(Effect.as({}))))
19034
19232
  };
19035
19233
  });
19036
- const layer$65 = Layer.effect(McpRegistry, make$74);
19234
+ const layer$65 = Layer.effect(McpRegistry, make$75);
19037
19235
  //#endregion
19038
19236
  //#region src/sync/skillDirectory.ts
19039
19237
  /**
@@ -19412,7 +19610,7 @@ const formatHubLink = (input) => encodeStoredHubLink({
19412
19610
  shareMode: input.shareMode
19413
19611
  });
19414
19612
  const fromEnvironment = (environment) => validateHubLink(environment.P4CODE_HUB_URL ?? "", environment.P4CODE_HUB_TOKEN ?? "");
19415
- const make$73 = Effect.fn("HubLink.make")(function* (environment) {
19613
+ const make$74 = Effect.fn("HubLink.make")(function* (environment) {
19416
19614
  const secrets = yield* ServerSecretStore;
19417
19615
  const env = environment ?? process.env;
19418
19616
  const fromEnv = fromEnvironment(env);
@@ -19478,7 +19676,7 @@ const make$73 = Effect.fn("HubLink.make")(function* (environment) {
19478
19676
  })
19479
19677
  };
19480
19678
  });
19481
- const layer$64 = Layer.effect(HubLink, make$73());
19679
+ const layer$64 = Layer.effect(HubLink, make$74());
19482
19680
  //#endregion
19483
19681
  //#region src/sync/HubAssetClient.ts
19484
19682
  /**
@@ -19511,7 +19709,7 @@ const decodeAssetListPage = Schema$1.decodeUnknownEffect(AssetListPage);
19511
19709
  const decodeConflictBody$1 = Schema$1.decodeUnknownEffect(ConflictBody$1);
19512
19710
  const decodeAsset = Schema$1.decodeUnknownEffect(AgentAsset);
19513
19711
  var HubAssetClient = class extends Context.Service()("@p4code/cli/sync/HubAssetClient") {};
19514
- const make$72 = Effect.gen(function* () {
19712
+ const make$73 = Effect.gen(function* () {
19515
19713
  const http = yield* HttpClient.HttpClient;
19516
19714
  const link = yield* HubLink;
19517
19715
  const requireSettings = Effect.gen(function* () {
@@ -19593,7 +19791,7 @@ const make$72 = Effect.gen(function* () {
19593
19791
  remove
19594
19792
  };
19595
19793
  });
19596
- const layer$63 = Layer.effect(HubAssetClient, make$72);
19794
+ const layer$63 = Layer.effect(HubAssetClient, make$73);
19597
19795
  //#endregion
19598
19796
  //#region src/sync/mcpRegistrationFiles.ts
19599
19797
  /**
@@ -20168,7 +20366,7 @@ const EMPTY_REPORT = {
20168
20366
  unavailable: null
20169
20367
  };
20170
20368
  var AssetSync = class extends Context.Service()("@p4code/cli/sync/AssetSync") {};
20171
- const make$71 = Effect.gen(function* () {
20369
+ const make$72 = Effect.gen(function* () {
20172
20370
  const client = yield* HubAssetClient;
20173
20371
  const link = yield* HubLink;
20174
20372
  const settingsStore = yield* ServerSettingsService;
@@ -20881,7 +21079,7 @@ const make$71 = Effect.gen(function* () {
20881
21079
  removeLocal
20882
21080
  };
20883
21081
  });
20884
- const layer$62 = Layer.effect(AssetSync, make$71);
21082
+ const layer$62 = Layer.effect(AssetSync, make$72);
20885
21083
  //#endregion
20886
21084
  //#region src/provider/CompressPrompts.ts
20887
21085
  /**
@@ -21801,7 +21999,11 @@ var ProjectionSnapshotQuery = class extends Context.Service()("@p4code/cli/orche
21801
21999
  const OrchestrationCommandReceipt = Schema$1.Struct({
21802
22000
  commandId: CommandId,
21803
22001
  aggregateKind: OrchestrationAggregateKind,
21804
- aggregateId: Schema$1.Union([ProjectId, ThreadId]),
22002
+ aggregateId: Schema$1.Union([
22003
+ ProjectId,
22004
+ ThreadId,
22005
+ ThreadPairId
22006
+ ]),
21805
22007
  acceptedAt: IsoDateTime,
21806
22008
  resultSequence: NonNegativeInt,
21807
22009
  status: OrchestrationCommandReceiptStatus,
@@ -21893,7 +22095,11 @@ const EventMetadataFromJsonString = Schema$1.fromJsonString(OrchestrationEventMe
21893
22095
  const AppendEventRequestSchema = Schema$1.Struct({
21894
22096
  eventId: EventId,
21895
22097
  aggregateKind: OrchestrationAggregateKind,
21896
- streamId: Schema$1.Union([ProjectId, ThreadId]),
22098
+ streamId: Schema$1.Union([
22099
+ ProjectId,
22100
+ ThreadId,
22101
+ ThreadPairId
22102
+ ]),
21897
22103
  type: OrchestrationEventType,
21898
22104
  causationEventId: Schema$1.NullOr(EventId),
21899
22105
  correlationId: Schema$1.NullOr(CommandId),
@@ -21908,7 +22114,11 @@ const OrchestrationEventPersistedRowSchema = Schema$1.Struct({
21908
22114
  eventId: EventId,
21909
22115
  type: OrchestrationEventType,
21910
22116
  aggregateKind: OrchestrationAggregateKind,
21911
- aggregateId: Schema$1.Union([ProjectId, ThreadId]),
22117
+ aggregateId: Schema$1.Union([
22118
+ ProjectId,
22119
+ ThreadId,
22120
+ ThreadPairId
22121
+ ]),
21912
22122
  occurredAt: IsoDateTime,
21913
22123
  commandId: Schema$1.NullOr(CommandId),
21914
22124
  causationEventId: Schema$1.NullOr(EventId),
@@ -22267,6 +22477,9 @@ const ThreadSessionSetPayload = ThreadSessionSetPayload$1;
22267
22477
  const ThreadTurnDiffCompletedPayload = ThreadTurnDiffCompletedPayload$1;
22268
22478
  const ThreadRevertedPayload = ThreadRevertedPayload$1;
22269
22479
  const ThreadActivityAppendedPayload = ThreadActivityAppendedPayload$1;
22480
+ const ThreadPairCreatedPayload = ThreadPairCreatedPayload$1;
22481
+ const ThreadPairDetachedPayload = ThreadPairDetachedPayload$1;
22482
+ const ThreadPairCursorAdvancedPayload = ThreadPairCursorAdvancedPayload$1;
22270
22483
  //#endregion
22271
22484
  //#region src/orchestration/projector.ts
22272
22485
  function checkpointStatusToLatestTurnState(status) {
@@ -22340,6 +22553,7 @@ function createEmptyReadModel(nowIso) {
22340
22553
  snapshotSequence: 0,
22341
22554
  projects: [],
22342
22555
  threads: [],
22556
+ threadPairs: [],
22343
22557
  updatedAt: nowIso
22344
22558
  };
22345
22559
  }
@@ -22350,6 +22564,31 @@ function projectEvent(model, event) {
22350
22564
  updatedAt: event.occurredAt
22351
22565
  };
22352
22566
  switch (event.type) {
22567
+ case "thread-pair.created": return decodeForEvent(ThreadPairCreatedPayload, event.payload, event.type, "payload").pipe(Effect.map((payload) => ({
22568
+ ...nextBase,
22569
+ threadPairs: [...(nextBase.threadPairs ?? []).filter((pair) => pair.id !== payload.pairId), {
22570
+ id: payload.pairId,
22571
+ implementerThreadId: payload.implementerThreadId,
22572
+ watcherThreadId: payload.watcherThreadId,
22573
+ lastReviewedImplementerSequence: payload.lastReviewedImplementerSequence,
22574
+ createdAt: payload.createdAt,
22575
+ detachedAt: null
22576
+ }]
22577
+ })));
22578
+ case "thread-pair.detached": return decodeForEvent(ThreadPairDetachedPayload, event.payload, event.type, "payload").pipe(Effect.map((payload) => ({
22579
+ ...nextBase,
22580
+ threadPairs: (nextBase.threadPairs ?? []).map((pair) => pair.id === payload.pairId ? {
22581
+ ...pair,
22582
+ detachedAt: payload.detachedAt
22583
+ } : pair)
22584
+ })));
22585
+ case "thread-pair.cursor-advanced": return decodeForEvent(ThreadPairCursorAdvancedPayload, event.payload, event.type, "payload").pipe(Effect.map((payload) => ({
22586
+ ...nextBase,
22587
+ threadPairs: (nextBase.threadPairs ?? []).map((pair) => pair.id === payload.pairId ? {
22588
+ ...pair,
22589
+ lastReviewedImplementerSequence: payload.implementerSequence
22590
+ } : pair)
22591
+ })));
22353
22592
  case "project.created": return decodeForEvent(ProjectCreatedPayload, event.payload, event.type, "payload").pipe(Effect.map((payload) => {
22354
22593
  const existing = nextBase.projects.find((entry) => entry.id === payload.projectId);
22355
22594
  const nextProject = {
@@ -22889,6 +23128,16 @@ const decideOrchestrationCommand = Effect.fn("decideOrchestrationCommand")(funct
22889
23128
  command,
22890
23129
  threadId: command.threadId
22891
23130
  });
23131
+ const activePair = (readModel.threadPairs ?? []).find((pair) => pair.detachedAt === null && (pair.implementerThreadId === command.threadId || pair.watcherThreadId === command.threadId));
23132
+ if (activePair !== void 0) return yield* decideCommandSequence({
23133
+ readModel,
23134
+ commands: [{
23135
+ type: "thread-pair.detach",
23136
+ commandId: command.commandId,
23137
+ pairId: activePair.id,
23138
+ createdAt: yield* nowIso$8
23139
+ }, command]
23140
+ });
22892
23141
  const occurredAt = yield* nowIso$8;
22893
23142
  return {
22894
23143
  ...yield* withEventBase({
@@ -22904,6 +23153,118 @@ const decideOrchestrationCommand = Effect.fn("decideOrchestrationCommand")(funct
22904
23153
  }
22905
23154
  };
22906
23155
  }
23156
+ case "thread-pair.create": {
23157
+ if (command.implementerThreadId === command.watcherThreadId) return yield* new OrchestrationCommandInvariantError({
23158
+ commandType: command.type,
23159
+ detail: "Fusion implementer and watcher must be distinct threads."
23160
+ });
23161
+ const implementer = yield* requireThread({
23162
+ readModel,
23163
+ command,
23164
+ threadId: command.implementerThreadId
23165
+ });
23166
+ const watcher = yield* requireThread({
23167
+ readModel,
23168
+ command,
23169
+ threadId: command.watcherThreadId
23170
+ });
23171
+ if (implementer.projectId !== watcher.projectId) return yield* new OrchestrationCommandInvariantError({
23172
+ commandType: command.type,
23173
+ detail: "Fusion implementer and watcher must belong to the same project."
23174
+ });
23175
+ const activePairs = readModel.threadPairs ?? [];
23176
+ if (activePairs.some((pair) => pair.id === command.pairId)) return yield* new OrchestrationCommandInvariantError({
23177
+ commandType: command.type,
23178
+ detail: `Thread pair '${command.pairId}' already exists.`
23179
+ });
23180
+ const occupiedThreadIds = new Set(activePairs.filter((pair) => pair.detachedAt === null).flatMap((pair) => [pair.implementerThreadId, pair.watcherThreadId]));
23181
+ if (occupiedThreadIds.has(command.implementerThreadId) || occupiedThreadIds.has(command.watcherThreadId)) return yield* new OrchestrationCommandInvariantError({
23182
+ commandType: command.type,
23183
+ detail: "Each thread may belong to only one active Fusion pair."
23184
+ });
23185
+ return {
23186
+ ...yield* withEventBase({
23187
+ aggregateKind: "thread-pair",
23188
+ aggregateId: command.pairId,
23189
+ occurredAt: command.createdAt,
23190
+ commandId: command.commandId
23191
+ }),
23192
+ type: "thread-pair.created",
23193
+ payload: {
23194
+ pairId: command.pairId,
23195
+ implementerThreadId: command.implementerThreadId,
23196
+ watcherThreadId: command.watcherThreadId,
23197
+ lastReviewedImplementerSequence: readModel.snapshotSequence,
23198
+ createdAt: command.createdAt
23199
+ }
23200
+ };
23201
+ }
23202
+ case "thread.turn.complete":
23203
+ yield* requireThread({
23204
+ readModel,
23205
+ command,
23206
+ threadId: command.threadId
23207
+ });
23208
+ return {
23209
+ ...yield* withEventBase({
23210
+ aggregateKind: "thread",
23211
+ aggregateId: command.threadId,
23212
+ occurredAt: command.completedAt,
23213
+ commandId: command.commandId
23214
+ }),
23215
+ type: "thread.turn-completed",
23216
+ payload: {
23217
+ threadId: command.threadId,
23218
+ turnId: command.turnId ?? null,
23219
+ state: command.state,
23220
+ completedAt: command.completedAt
23221
+ }
23222
+ };
23223
+ case "thread-pair.detach": {
23224
+ const pair = (readModel.threadPairs ?? []).find((candidate) => candidate.id === command.pairId);
23225
+ if (pair === void 0 || pair.detachedAt !== null) return yield* new OrchestrationCommandInvariantError({
23226
+ commandType: command.type,
23227
+ detail: `Active thread pair '${command.pairId}' does not exist.`
23228
+ });
23229
+ return {
23230
+ ...yield* withEventBase({
23231
+ aggregateKind: "thread-pair",
23232
+ aggregateId: command.pairId,
23233
+ occurredAt: command.createdAt,
23234
+ commandId: command.commandId
23235
+ }),
23236
+ type: "thread-pair.detached",
23237
+ payload: {
23238
+ pairId: command.pairId,
23239
+ detachedAt: command.createdAt
23240
+ }
23241
+ };
23242
+ }
23243
+ case "thread-pair.cursor.advance": {
23244
+ const pair = (readModel.threadPairs ?? []).find((candidate) => candidate.id === command.pairId);
23245
+ if (pair === void 0 || pair.detachedAt !== null) return yield* new OrchestrationCommandInvariantError({
23246
+ commandType: command.type,
23247
+ detail: `Active thread pair '${command.pairId}' does not exist.`
23248
+ });
23249
+ if (command.implementerSequence < pair.lastReviewedImplementerSequence) return yield* new OrchestrationCommandInvariantError({
23250
+ commandType: command.type,
23251
+ detail: `Thread pair '${command.pairId}' cursor cannot move backwards.`
23252
+ });
23253
+ return {
23254
+ ...yield* withEventBase({
23255
+ aggregateKind: "thread-pair",
23256
+ aggregateId: command.pairId,
23257
+ occurredAt: command.advancedAt,
23258
+ commandId: command.commandId
23259
+ }),
23260
+ type: "thread-pair.cursor-advanced",
23261
+ payload: {
23262
+ pairId: command.pairId,
23263
+ implementerSequence: command.implementerSequence,
23264
+ advancedAt: command.advancedAt
23265
+ }
23266
+ };
23267
+ }
22907
23268
  case "thread.archive": {
22908
23269
  yield* requireThreadNotArchived({
22909
23270
  readModel,
@@ -23594,6 +23955,12 @@ function commandToAggregateRef(command) {
23594
23955
  aggregateKind: "project",
23595
23956
  aggregateId: command.projectId
23596
23957
  };
23958
+ case "thread-pair.create":
23959
+ case "thread-pair.detach":
23960
+ case "thread-pair.cursor.advance": return {
23961
+ aggregateKind: "thread-pair",
23962
+ aggregateId: command.pairId
23963
+ };
23597
23964
  default: return {
23598
23965
  aggregateKind: "thread",
23599
23966
  aggregateId: command.threadId
@@ -25240,7 +25607,8 @@ const ORCHESTRATION_PROJECTOR_NAMES = {
25240
25607
  threadSessions: "projection.thread-sessions",
25241
25608
  threadTurns: "projection.thread-turns",
25242
25609
  checkpoints: "projection.checkpoints",
25243
- pendingApprovals: "projection.pending-approvals"
25610
+ pendingApprovals: "projection.pending-approvals",
25611
+ threadPairs: "projection.thread-pairs"
25244
25612
  };
25245
25613
  /**
25246
25614
  * Turn state to settle still-running turns with when their session leaves the
@@ -25499,6 +25867,50 @@ const makeOrchestrationProjectionPipeline = Effect.fn("makeOrchestrationProjecti
25499
25867
  default: return;
25500
25868
  }
25501
25869
  });
25870
+ const applyThreadPairsProjection = Effect.fn("applyThreadPairsProjection")(function* (event, _attachmentSideEffects) {
25871
+ switch (event.type) {
25872
+ case "thread-pair.created":
25873
+ yield* sql`
25874
+ INSERT INTO thread_pairs (
25875
+ pair_id,
25876
+ implementer_thread_id,
25877
+ watcher_thread_id,
25878
+ last_reviewed_implementer_sequence,
25879
+ created_at,
25880
+ detached_at
25881
+ ) VALUES (
25882
+ ${event.payload.pairId},
25883
+ ${event.payload.implementerThreadId},
25884
+ ${event.payload.watcherThreadId},
25885
+ ${event.payload.lastReviewedImplementerSequence},
25886
+ ${event.payload.createdAt},
25887
+ NULL
25888
+ )
25889
+ ON CONFLICT(pair_id) DO UPDATE SET
25890
+ implementer_thread_id = excluded.implementer_thread_id,
25891
+ watcher_thread_id = excluded.watcher_thread_id,
25892
+ last_reviewed_implementer_sequence = excluded.last_reviewed_implementer_sequence,
25893
+ created_at = excluded.created_at,
25894
+ detached_at = NULL
25895
+ `.pipe(Effect.mapError(toPersistenceSqlError("ProjectionPipeline.threadPairs:create")));
25896
+ return;
25897
+ case "thread-pair.detached":
25898
+ yield* sql`
25899
+ UPDATE thread_pairs
25900
+ SET detached_at = ${event.payload.detachedAt}
25901
+ WHERE pair_id = ${event.payload.pairId}
25902
+ `.pipe(Effect.mapError(toPersistenceSqlError("ProjectionPipeline.threadPairs:detach")));
25903
+ return;
25904
+ case "thread-pair.cursor-advanced":
25905
+ yield* sql`
25906
+ UPDATE thread_pairs
25907
+ SET last_reviewed_implementer_sequence = ${event.payload.implementerSequence}
25908
+ WHERE pair_id = ${event.payload.pairId}
25909
+ `.pipe(Effect.mapError(toPersistenceSqlError("ProjectionPipeline.threadPairs:cursor")));
25910
+ return;
25911
+ default: return;
25912
+ }
25913
+ });
25502
25914
  const refreshThreadShellSummary = Effect.fn("refreshThreadShellSummary")(function* (threadId) {
25503
25915
  const existingRow = yield* projectionThreadRepository.getById({ threadId });
25504
25916
  if (Option.isNone(existingRow)) return;
@@ -26137,6 +26549,10 @@ const makeOrchestrationProjectionPipeline = Effect.fn("makeOrchestrationProjecti
26137
26549
  name: ORCHESTRATION_PROJECTOR_NAMES.projects,
26138
26550
  apply: applyProjectsProjection
26139
26551
  },
26552
+ {
26553
+ name: ORCHESTRATION_PROJECTOR_NAMES.threadPairs,
26554
+ apply: applyThreadPairsProjection
26555
+ },
26140
26556
  {
26141
26557
  name: ORCHESTRATION_PROJECTOR_NAMES.threadMessages,
26142
26558
  apply: applyThreadMessagesProjection
@@ -26732,12 +27148,12 @@ const runProcessCore = Effect.fn("processRunner.runProcessCore")(function* (spaw
26732
27148
  stderrInvalidUtf8: stderr.invalidUtf8
26733
27149
  };
26734
27150
  });
26735
- const make$70 = Effect.fn("ProcessRunner.make")(function* () {
27151
+ const make$71 = Effect.fn("ProcessRunner.make")(function* () {
26736
27152
  const spawner = yield* ChildProcessSpawner$1.ChildProcessSpawner;
26737
27153
  const run = (input) => finalizeRunProcess(runProcessCore(spawner, input), input);
26738
27154
  return ProcessRunner.of({ run });
26739
27155
  });
26740
- const layer$61 = Layer.effect(ProcessRunner, make$70());
27156
+ const layer$61 = Layer.effect(ProcessRunner, make$71());
26741
27157
  //#endregion
26742
27158
  //#region src/project/RepositoryIdentityResolver.ts
26743
27159
  const DEFAULT_REPOSITORY_IDENTITY_CACHE_CAPACITY = 512;
@@ -26828,7 +27244,7 @@ const resolveRepositoryIdentityFromCacheKey = Effect.fn("RepositoryIdentityResol
26828
27244
  rootPath: cacheKey
26829
27245
  }) : null;
26830
27246
  });
26831
- const make$69 = Effect.fn("RepositoryIdentityResolver.make")(function* (options = {}) {
27247
+ const make$70 = Effect.fn("RepositoryIdentityResolver.make")(function* (options = {}) {
26832
27248
  const processRunner = yield* ProcessRunner;
26833
27249
  const repositoryIdentityCache = yield* Cache.makeWith((cacheKey) => resolveRepositoryIdentityFromCacheKey(cacheKey).pipe(Effect.provideService(ProcessRunner, processRunner)), {
26834
27250
  capacity: options.cacheCapacity ?? DEFAULT_REPOSITORY_IDENTITY_CACHE_CAPACITY,
@@ -26843,7 +27259,7 @@ const make$69 = Effect.fn("RepositoryIdentityResolver.make")(function* (options
26843
27259
  });
26844
27260
  return RepositoryIdentityResolver.of({ resolve });
26845
27261
  });
26846
- const layer$60 = Layer.effect(RepositoryIdentityResolver, make$69()).pipe(Layer.provide(layer$61));
27262
+ const layer$60 = Layer.effect(RepositoryIdentityResolver, make$70()).pipe(Layer.provide(layer$61));
26847
27263
  //#endregion
26848
27264
  //#region src/orchestration/Layers/ProjectionSnapshotQuery.ts
26849
27265
  const decodeReadModel = Schema$1.decodeUnknownEffect(OrchestrationReadModel);
@@ -26876,7 +27292,22 @@ const ProjectionLatestTurnDbRowSchema = Schema$1.Struct({
26876
27292
  sourceProposedPlanThreadId: Schema$1.NullOr(ThreadId),
26877
27293
  sourceProposedPlanId: Schema$1.NullOr(OrchestrationProposedPlanId)
26878
27294
  });
27295
+ const ProjectionTurnSummaryDbRowSchema = Schema$1.Struct({
27296
+ threadId: ProjectionThread.fields.threadId,
27297
+ turnId: TurnId,
27298
+ state: Schema$1.String,
27299
+ startedAt: Schema$1.NullOr(IsoDateTime),
27300
+ completedAt: Schema$1.NullOr(IsoDateTime)
27301
+ });
26879
27302
  const ProjectionStateDbRowSchema = ProjectionState;
27303
+ const ProjectionThreadPairDbRowSchema = Schema$1.Struct({
27304
+ id: ThreadPairId,
27305
+ implementerThreadId: ThreadId,
27306
+ watcherThreadId: ThreadId,
27307
+ lastReviewedImplementerSequence: NonNegativeInt,
27308
+ createdAt: IsoDateTime,
27309
+ detachedAt: Schema$1.NullOr(IsoDateTime)
27310
+ });
26880
27311
  const ProjectionCountsRowSchema = Schema$1.Struct({
26881
27312
  projectCount: Schema$1.Number,
26882
27313
  threadCount: Schema$1.Number
@@ -26924,7 +27355,8 @@ const REQUIRED_SNAPSHOT_PROJECTORS = [
26924
27355
  ORCHESTRATION_PROJECTOR_NAMES.threadProposedPlans,
26925
27356
  ORCHESTRATION_PROJECTOR_NAMES.threadActivities,
26926
27357
  ORCHESTRATION_PROJECTOR_NAMES.threadSessions,
26927
- ORCHESTRATION_PROJECTOR_NAMES.checkpoints
27358
+ ORCHESTRATION_PROJECTOR_NAMES.checkpoints,
27359
+ ORCHESTRATION_PROJECTOR_NAMES.threadPairs
26928
27360
  ];
26929
27361
  function maxIso(left, right) {
26930
27362
  if (left === null) return right;
@@ -26958,10 +27390,21 @@ function computeSnapshotSequence(stateRows) {
26958
27390
  }
26959
27391
  return Number.isFinite(minSequence) ? minSequence : 0;
26960
27392
  }
27393
+ function mapTurnState(state) {
27394
+ return state === "error" ? "error" : state === "interrupted" ? "interrupted" : state === "completed" ? "completed" : "running";
27395
+ }
27396
+ function mapTurnSummary(row) {
27397
+ return {
27398
+ turnId: row.turnId,
27399
+ state: mapTurnState(row.state),
27400
+ startedAt: row.startedAt,
27401
+ completedAt: row.completedAt
27402
+ };
27403
+ }
26961
27404
  function mapLatestTurn(row) {
26962
27405
  return {
26963
27406
  turnId: row.turnId,
26964
- state: row.state === "error" ? "error" : row.state === "interrupted" ? "interrupted" : row.state === "completed" ? "completed" : "running",
27407
+ state: mapTurnState(row.state),
26965
27408
  requestedAt: row.requestedAt,
26966
27409
  startedAt: row.startedAt,
26967
27410
  completedAt: row.completedAt,
@@ -27035,6 +27478,21 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
27035
27478
  deleted_at AS "deletedAt"
27036
27479
  FROM projection_projects
27037
27480
  ORDER BY created_at ASC, project_id ASC
27481
+ `
27482
+ });
27483
+ const listThreadPairRows = SqlSchema.findAll({
27484
+ Request: Schema$1.Void,
27485
+ Result: ProjectionThreadPairDbRowSchema,
27486
+ execute: () => sql`
27487
+ SELECT
27488
+ pair_id AS "id",
27489
+ implementer_thread_id AS "implementerThreadId",
27490
+ watcher_thread_id AS "watcherThreadId",
27491
+ last_reviewed_implementer_sequence AS "lastReviewedImplementerSequence",
27492
+ created_at AS "createdAt",
27493
+ detached_at AS "detachedAt"
27494
+ FROM thread_pairs
27495
+ ORDER BY created_at ASC, pair_id ASC
27038
27496
  `
27039
27497
  });
27040
27498
  const listThreadRows = SqlSchema.findAll({
@@ -27281,6 +27739,21 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
27281
27739
  FROM projection_turns
27282
27740
  WHERE checkpoint_turn_count IS NOT NULL
27283
27741
  ORDER BY thread_id ASC, checkpoint_turn_count ASC
27742
+ `
27743
+ });
27744
+ const listTurnSummaryRows = SqlSchema.findAll({
27745
+ Request: Schema$1.Void,
27746
+ Result: ProjectionTurnSummaryDbRowSchema,
27747
+ execute: () => sql`
27748
+ SELECT
27749
+ thread_id AS "threadId",
27750
+ turn_id AS "turnId",
27751
+ state,
27752
+ started_at AS "startedAt",
27753
+ completed_at AS "completedAt"
27754
+ FROM projection_turns
27755
+ WHERE turn_id IS NOT NULL
27756
+ ORDER BY thread_id ASC, requested_at ASC, turn_id ASC
27284
27757
  `
27285
27758
  });
27286
27759
  const listLatestTurnRows = SqlSchema.findAll({
@@ -27667,6 +28140,22 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
27667
28140
  WHERE thread_id = ${threadId}
27668
28141
  AND checkpoint_turn_count IS NOT NULL
27669
28142
  ORDER BY checkpoint_turn_count ASC
28143
+ `
28144
+ });
28145
+ const listTurnSummaryRowsByThread = SqlSchema.findAll({
28146
+ Request: ThreadIdLookupInput,
28147
+ Result: ProjectionTurnSummaryDbRowSchema,
28148
+ execute: ({ threadId }) => sql`
28149
+ SELECT
28150
+ thread_id AS "threadId",
28151
+ turn_id AS "turnId",
28152
+ state,
28153
+ started_at AS "startedAt",
28154
+ completed_at AS "completedAt"
28155
+ FROM projection_turns
28156
+ WHERE thread_id = ${threadId}
28157
+ AND turn_id IS NOT NULL
28158
+ ORDER BY requested_at ASC, turn_id ASC
27670
28159
  `
27671
28160
  });
27672
28161
  const getFullThreadDiffContextRow = SqlSchema.findOneOption({
@@ -27707,15 +28196,19 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
27707
28196
  listThreadActivityRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getSnapshot:listThreadActivities:query", "ProjectionSnapshotQuery.getSnapshot:listThreadActivities:decodeRows"))),
27708
28197
  listThreadSessionRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getSnapshot:listThreadSessions:query", "ProjectionSnapshotQuery.getSnapshot:listThreadSessions:decodeRows"))),
27709
28198
  listCheckpointRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getSnapshot:listCheckpoints:query", "ProjectionSnapshotQuery.getSnapshot:listCheckpoints:decodeRows"))),
28199
+ listTurnSummaryRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getSnapshot:listTurnSummaries:query", "ProjectionSnapshotQuery.getSnapshot:listTurnSummaries:decodeRows"))),
27710
28200
  listLatestTurnRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getSnapshot:listLatestTurns:query", "ProjectionSnapshotQuery.getSnapshot:listLatestTurns:decodeRows"))),
27711
- listProjectionStateRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getSnapshot:listProjectionState:query", "ProjectionSnapshotQuery.getSnapshot:listProjectionState:decodeRows")))
27712
- ])).pipe(Effect.flatMap(([projectRows, threadRows, messageRows, proposedPlanRows, activityRows, sessionRows, checkpointRows, latestTurnRows, stateRows]) => Effect.gen(function* () {
28201
+ listProjectionStateRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getSnapshot:listProjectionState:query", "ProjectionSnapshotQuery.getSnapshot:listProjectionState:decodeRows"))),
28202
+ listThreadPairRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getSnapshot:listThreadPairs:query", "ProjectionSnapshotQuery.getSnapshot:listThreadPairs:decodeRows")))
28203
+ ])).pipe(Effect.flatMap(([projectRows, threadRows, messageRows, proposedPlanRows, activityRows, sessionRows, checkpointRows, turnRows, latestTurnRows, stateRows, threadPairRows]) => Effect.gen(function* () {
27713
28204
  const messagesByThread = /* @__PURE__ */ new Map();
27714
28205
  const proposedPlansByThread = /* @__PURE__ */ new Map();
27715
28206
  const activitiesByThread = /* @__PURE__ */ new Map();
27716
28207
  const checkpointsByThread = /* @__PURE__ */ new Map();
28208
+ const turnsByThread = /* @__PURE__ */ new Map();
27717
28209
  const sessionsByThread = /* @__PURE__ */ new Map();
27718
28210
  const latestTurnByThread = /* @__PURE__ */ new Map();
28211
+ const threadPairs = [...threadPairRows];
27719
28212
  let updatedAt = null;
27720
28213
  for (const row of projectRows) updatedAt = maxIso(updatedAt, row.updatedAt);
27721
28214
  for (const row of threadRows) updatedAt = maxIso(updatedAt, row.updatedAt);
@@ -27778,23 +28271,17 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
27778
28271
  });
27779
28272
  checkpointsByThread.set(row.threadId, threadCheckpoints);
27780
28273
  }
28274
+ for (const row of turnRows) {
28275
+ const threadTurns = turnsByThread.get(row.threadId) ?? [];
28276
+ threadTurns.push(mapTurnSummary(row));
28277
+ turnsByThread.set(row.threadId, threadTurns);
28278
+ }
27781
28279
  for (const row of latestTurnRows) {
27782
28280
  updatedAt = maxIso(updatedAt, row.requestedAt);
27783
28281
  if (row.startedAt !== null) updatedAt = maxIso(updatedAt, row.startedAt);
27784
28282
  if (row.completedAt !== null) updatedAt = maxIso(updatedAt, row.completedAt);
27785
28283
  if (latestTurnByThread.has(row.threadId)) continue;
27786
- latestTurnByThread.set(row.threadId, {
27787
- turnId: row.turnId,
27788
- state: row.state === "error" ? "error" : row.state === "interrupted" ? "interrupted" : row.state === "completed" ? "completed" : "running",
27789
- requestedAt: row.requestedAt,
27790
- startedAt: row.startedAt,
27791
- completedAt: row.completedAt,
27792
- assistantMessageId: row.assistantMessageId,
27793
- ...row.sourceProposedPlanThreadId !== null && row.sourceProposedPlanId !== null ? { sourceProposedPlan: {
27794
- threadId: row.sourceProposedPlanThreadId,
27795
- planId: row.sourceProposedPlanId
27796
- } } : {}
27797
- });
28284
+ latestTurnByThread.set(row.threadId, mapLatestTurn(row));
27798
28285
  }
27799
28286
  for (const row of sessionRows) {
27800
28287
  updatedAt = maxIso(updatedAt, row.updatedAt);
@@ -27833,6 +28320,7 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
27833
28320
  branch: row.branch,
27834
28321
  worktreePath: row.worktreePath,
27835
28322
  latestTurn: latestTurnByThread.get(row.threadId) ?? null,
28323
+ turns: turnsByThread.get(row.threadId) ?? [],
27836
28324
  createdAt: row.createdAt,
27837
28325
  updatedAt: row.updatedAt,
27838
28326
  archivedAt: row.archivedAt,
@@ -27851,6 +28339,7 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
27851
28339
  snapshotSequence: computeSnapshotSequence(stateRows),
27852
28340
  projects,
27853
28341
  threads,
28342
+ threadPairs,
27854
28343
  updatedAt: updatedAt ?? "1970-01-01T00:00:00.000Z"
27855
28344
  };
27856
28345
  return yield* decodeReadModel(snapshot).pipe(Effect.mapError(toPersistenceDecodeError("ProjectionSnapshotQuery.getSnapshot:decodeReadModel")));
@@ -27864,11 +28353,13 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
27864
28353
  listThreadProposedPlanRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getCommandReadModel:listThreadProposedPlans:query", "ProjectionSnapshotQuery.getCommandReadModel:listThreadProposedPlans:decodeRows"))),
27865
28354
  listThreadSessionRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getCommandReadModel:listThreadSessions:query", "ProjectionSnapshotQuery.getCommandReadModel:listThreadSessions:decodeRows"))),
27866
28355
  listLatestTurnRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getCommandReadModel:listLatestTurns:query", "ProjectionSnapshotQuery.getCommandReadModel:listLatestTurns:decodeRows"))),
27867
- listProjectionStateRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getCommandReadModel:listProjectionState:query", "ProjectionSnapshotQuery.getCommandReadModel:listProjectionState:decodeRows")))
27868
- ])).pipe(Effect.flatMap(([projectRows, threadRows, proposedPlanRows, sessionRows, latestTurnRows, stateRows]) => Effect.sync(() => {
28356
+ listProjectionStateRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getCommandReadModel:listProjectionState:query", "ProjectionSnapshotQuery.getCommandReadModel:listProjectionState:decodeRows"))),
28357
+ listThreadPairRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getCommandReadModel:listThreadPairs:query", "ProjectionSnapshotQuery.getCommandReadModel:listThreadPairs:decodeRows")))
28358
+ ])).pipe(Effect.flatMap(([projectRows, threadRows, proposedPlanRows, sessionRows, latestTurnRows, stateRows, threadPairRows]) => Effect.sync(() => {
27869
28359
  let updatedAt = null;
27870
28360
  const projects = [];
27871
28361
  const threads = [];
28362
+ const threadPairs = [...threadPairRows];
27872
28363
  for (let index = 0; index < projectRows.length; index += 1) {
27873
28364
  const row = projectRows[index];
27874
28365
  if (!row) continue;
@@ -27965,6 +28456,7 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
27965
28456
  snapshotSequence: computeSnapshotSequence(stateRows),
27966
28457
  projects,
27967
28458
  threads,
28459
+ threadPairs,
27968
28460
  updatedAt: updatedAt ?? "1970-01-01T00:00:00.000Z"
27969
28461
  };
27970
28462
  })), Effect.mapError((error) => {
@@ -27976,8 +28468,9 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
27976
28468
  listActiveThreadRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getShellSnapshot:listThreads:query", "ProjectionSnapshotQuery.getShellSnapshot:listThreads:decodeRows"))),
27977
28469
  listActiveThreadSessionRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getShellSnapshot:listThreadSessions:query", "ProjectionSnapshotQuery.getShellSnapshot:listThreadSessions:decodeRows"))),
27978
28470
  listActiveLatestTurnRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getShellSnapshot:listLatestTurns:query", "ProjectionSnapshotQuery.getShellSnapshot:listLatestTurns:decodeRows"))),
27979
- listProjectionStateRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getShellSnapshot:listProjectionState:query", "ProjectionSnapshotQuery.getShellSnapshot:listProjectionState:decodeRows")))
27980
- ])).pipe(Effect.flatMap(([projectRows, threadRows, sessionRows, latestTurnRows, stateRows]) => Effect.gen(function* () {
28471
+ listProjectionStateRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getShellSnapshot:listProjectionState:query", "ProjectionSnapshotQuery.getShellSnapshot:listProjectionState:decodeRows"))),
28472
+ listThreadPairRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getShellSnapshot:listThreadPairs:query", "ProjectionSnapshotQuery.getShellSnapshot:listThreadPairs:decodeRows")))
28473
+ ])).pipe(Effect.flatMap(([projectRows, threadRows, sessionRows, latestTurnRows, stateRows, threadPairRows]) => Effect.gen(function* () {
27981
28474
  let updatedAt = null;
27982
28475
  for (const row of projectRows) updatedAt = maxIso(updatedAt, row.updatedAt);
27983
28476
  for (const row of threadRows) updatedAt = maxIso(updatedAt, row.updatedAt);
@@ -28021,6 +28514,7 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
28021
28514
  hasBackgroundTasks: row.backgroundTaskCount > 0,
28022
28515
  scheduledWakeAt: row.scheduledWakeAt
28023
28516
  }) : Result.failVoid),
28517
+ threadPairs: threadPairRows.filter((pair) => pair.detachedAt === null),
28024
28518
  updatedAt: updatedAt ?? "1970-01-01T00:00:00.000Z"
28025
28519
  };
28026
28520
  return yield* decodeShellSnapshot(snapshot).pipe(Effect.mapError(toPersistenceDecodeError("ProjectionSnapshotQuery.getShellSnapshot:decodeShellSnapshot")));
@@ -28189,12 +28683,13 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
28189
28683
  });
28190
28684
  });
28191
28685
  const getThreadDetailById = (threadId) => Effect.gen(function* () {
28192
- const [threadRow, messageRows, proposedPlanRows, activityRows, checkpointRows, latestTurnRow, sessionRow] = yield* Effect.all([
28686
+ const [threadRow, messageRows, proposedPlanRows, activityRows, checkpointRows, turnRows, latestTurnRow, sessionRow] = yield* Effect.all([
28193
28687
  getActiveThreadRowById({ threadId }).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getThreadDetailById:getThread:query", "ProjectionSnapshotQuery.getThreadDetailById:getThread:decodeRow"))),
28194
28688
  listThreadMessageRowsByThread({ threadId }).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getThreadDetailById:listMessages:query", "ProjectionSnapshotQuery.getThreadDetailById:listMessages:decodeRows"))),
28195
28689
  listThreadProposedPlanRowsByThread({ threadId }).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getThreadDetailById:listPlans:query", "ProjectionSnapshotQuery.getThreadDetailById:listPlans:decodeRows"))),
28196
28690
  listThreadActivityRowsByThread({ threadId }).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getThreadDetailById:listActivities:query", "ProjectionSnapshotQuery.getThreadDetailById:listActivities:decodeRows"))),
28197
28691
  listCheckpointRowsByThread({ threadId }).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getThreadDetailById:listCheckpoints:query", "ProjectionSnapshotQuery.getThreadDetailById:listCheckpoints:decodeRows"))),
28692
+ listTurnSummaryRowsByThread({ threadId }).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getThreadDetailById:listTurnSummaries:query", "ProjectionSnapshotQuery.getThreadDetailById:listTurnSummaries:decodeRows"))),
28198
28693
  getLatestTurnRowByThread({ threadId }).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getThreadDetailById:getLatestTurn:query", "ProjectionSnapshotQuery.getThreadDetailById:getLatestTurn:decodeRow"))),
28199
28694
  getThreadSessionRowByThread({ threadId }).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getThreadDetailById:getSession:query", "ProjectionSnapshotQuery.getThreadDetailById:getSession:decodeRow")))
28200
28695
  ]);
@@ -28211,6 +28706,7 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
28211
28706
  branch: threadRow.value.branch,
28212
28707
  worktreePath: threadRow.value.worktreePath,
28213
28708
  latestTurn: Option.isSome(latestTurnRow) ? mapLatestTurn(latestTurnRow.value) : null,
28709
+ turns: turnRows.map(mapTurnSummary),
28214
28710
  createdAt: threadRow.value.createdAt,
28215
28711
  updatedAt: threadRow.value.updatedAt,
28216
28712
  archivedAt: threadRow.value.archivedAt,
@@ -28805,7 +29301,7 @@ function mergeWithDefaultKeybindings(custom) {
28805
29301
  * Keybindings - Service tag for keybinding configuration operations.
28806
29302
  */
28807
29303
  var Keybindings = class extends Context.Service()("@p4code/cli/keybindings") {};
28808
- const make$68 = Effect.gen(function* () {
29304
+ const make$69 = Effect.gen(function* () {
28809
29305
  const { keybindingsConfigPath } = yield* ServerConfig$1;
28810
29306
  const fs = yield* FileSystem.FileSystem;
28811
29307
  const path = yield* Path.Path;
@@ -29066,7 +29562,7 @@ const make$68 = Effect.gen(function* () {
29066
29562
  }))
29067
29563
  };
29068
29564
  });
29069
- const layer$59 = Layer.effect(Keybindings, make$68);
29565
+ const layer$59 = Layer.effect(Keybindings, make$69);
29070
29566
  //#endregion
29071
29567
  //#region src/process/externalLauncher.ts
29072
29568
  /**
@@ -29293,7 +29789,7 @@ const launchEditorProcess = Effect.fn("externalLauncher.launchEditorProcess")(fu
29293
29789
  cause
29294
29790
  }));
29295
29791
  });
29296
- const make$67 = Effect.gen(function* () {
29792
+ const make$68 = Effect.gen(function* () {
29297
29793
  const spawner = yield* ChildProcessSpawner$1.ChildProcessSpawner;
29298
29794
  const fileSystem = yield* FileSystem.FileSystem;
29299
29795
  const path = yield* Path.Path;
@@ -29304,7 +29800,7 @@ const make$67 = Effect.gen(function* () {
29304
29800
  launchEditor: (input) => provideCommandResolutionServices(Effect.flatMap(resolveEditorLaunch(input), (launch) => launchEditorProcess(launch).pipe(Effect.provideService(ChildProcessSpawner$1.ChildProcessSpawner, spawner))))
29305
29801
  });
29306
29802
  });
29307
- const layer$58 = Layer.effect(ExternalLauncher, make$67);
29803
+ const layer$58 = Layer.effect(ExternalLauncher, make$68);
29308
29804
  //#endregion
29309
29805
  //#region src/orchestration/Services/OrchestrationReactor.ts
29310
29806
  /**
@@ -29322,7 +29818,7 @@ var OrchestrationReactor = class extends Context.Service()("@p4code/cli/orchestr
29322
29818
  //#endregion
29323
29819
  //#region src/serverLifecycleEvents.ts
29324
29820
  var ServerLifecycleEvents = class extends Context.Service()("@p4code/cli/serverLifecycleEvents") {};
29325
- const make$66 = Effect.gen(function* () {
29821
+ const make$67 = Effect.gen(function* () {
29326
29822
  const pubsub = yield* PubSub.unbounded();
29327
29823
  const state = yield* Ref.make({
29328
29824
  sequence: 0,
@@ -29346,7 +29842,7 @@ const make$66 = Effect.gen(function* () {
29346
29842
  }
29347
29843
  };
29348
29844
  });
29349
- const layer$57 = Layer.effect(ServerLifecycleEvents, make$66);
29845
+ const layer$57 = Layer.effect(ServerLifecycleEvents, make$67);
29350
29846
  //#endregion
29351
29847
  //#region src/telemetry/Identify.ts
29352
29848
  const CodexAuthJsonSchema = Schema$1.Struct({ tokens: Schema$1.Struct({ account_id: Schema$1.String }) });
@@ -29519,7 +30015,7 @@ var AnalyticsService = class AnalyticsService extends Context.Service()("@p4code
29519
30015
  /** No-op layer for callers that intentionally disable telemetry. */
29520
30016
  static layerTest = Layer.succeed(AnalyticsService, inert);
29521
30017
  };
29522
- const make$65 = Effect.gen(function* () {
30018
+ const make$66 = Effect.gen(function* () {
29523
30019
  const telemetryConfig = yield* TelemetryEnvConfig;
29524
30020
  const posthogKey = telemetryConfig.posthogKey.trim();
29525
30021
  if (!telemetryConfig.enabled || posthogKey === "") return inert;
@@ -29589,7 +30085,7 @@ const make$65 = Effect.gen(function* () {
29589
30085
  flush
29590
30086
  });
29591
30087
  });
29592
- const layer$56 = Layer.effect(AnalyticsService, make$65);
30088
+ const layer$56 = Layer.effect(AnalyticsService, make$66);
29593
30089
  AnalyticsService.layerTest;
29594
30090
  //#endregion
29595
30091
  //#region src/service/pinnedRuntime.ts
@@ -29936,7 +30432,7 @@ var BootServiceInstallError = class extends Schema$1.TaggedErrorClass()("BootSer
29936
30432
  }
29937
30433
  };
29938
30434
  var BootService = class extends Context.Service()("@p4code/cli/service/bootService") {};
29939
- const make$64 = Effect.fn("cloud.boot_service.make")(function* (input) {
30435
+ const make$65 = Effect.fn("cloud.boot_service.make")(function* (input) {
29940
30436
  const hostExecPath = yield* HostProcessExecutablePath;
29941
30437
  const hostArguments = yield* HostProcessArguments;
29942
30438
  const host = input.host ?? {
@@ -30158,7 +30654,7 @@ const make$64 = Effect.fn("cloud.boot_service.make")(function* (input) {
30158
30654
  logPath
30159
30655
  });
30160
30656
  });
30161
- const layer$55 = (input) => Layer.effect(BootService, make$64(input));
30657
+ const layer$55 = (input) => Layer.effect(BootService, make$65(input));
30162
30658
  //#endregion
30163
30659
  //#region src/service/selfUpdate.ts
30164
30660
  /**
@@ -30233,7 +30729,7 @@ const resolveServerSelfUpdateCapability = Effect.fn("cloud.server_self_update.re
30233
30729
  return null;
30234
30730
  });
30235
30731
  var ServerSelfUpdate = class extends Context.Service()("@p4code/cli/service/selfUpdate/ServerSelfUpdate") {};
30236
- const make$63 = Effect.fn("cloud.server_self_update.make")(function* (options) {
30732
+ const make$64 = Effect.fn("cloud.server_self_update.make")(function* (options) {
30237
30733
  const serverConfig = yield* ServerConfig$1;
30238
30734
  const fs = yield* FileSystem.FileSystem;
30239
30735
  const path = yield* Path.Path;
@@ -30383,7 +30879,7 @@ const make$63 = Effect.fn("cloud.server_self_update.make")(function* (options) {
30383
30879
  });
30384
30880
  return ServerSelfUpdate.of({ update });
30385
30881
  });
30386
- const layer$54 = Layer.effect(ServerSelfUpdate, make$63()).pipe(Layer.provide(layer$61));
30882
+ const layer$54 = Layer.effect(ServerSelfUpdate, make$64()).pipe(Layer.provide(layer$61));
30387
30883
  //#endregion
30388
30884
  //#region src/environment/ServerEnvironmentLabel.ts
30389
30885
  const ServerEnvironmentLabelCommandProbe = Schema$1.Literals(["macos-computer-name", "linux-pretty-hostname"]);
@@ -30515,7 +31011,7 @@ function platformArch(architecture) {
30515
31011
  default: return "other";
30516
31012
  }
30517
31013
  }
30518
- const make$62 = Effect.gen(function* () {
31014
+ const make$63 = Effect.gen(function* () {
30519
31015
  const fileSystem = yield* FileSystem.FileSystem;
30520
31016
  const path = yield* Path.Path;
30521
31017
  const serverConfig = yield* ServerConfig$1;
@@ -30579,7 +31075,7 @@ const make$62 = Effect.gen(function* () {
30579
31075
  * state. It intentionally has no fallback Layer.succeed value: callers must
30580
31076
  * provide the external platform services and a ServerConfig.
30581
31077
  */
30582
- const layer$53 = Layer.effect(ServerEnvironment, make$62).pipe(Layer.provide(layer$61));
31078
+ const layer$53 = Layer.effect(ServerEnvironment, make$63).pipe(Layer.provide(layer$61));
30583
31079
  //#endregion
30584
31080
  //#region src/provider/Services/ProviderSessionReaper.ts
30585
31081
  var ProviderSessionReaper = class extends Context.Service()("@p4code/cli/provider/Services/ProviderSessionReaper") {};
@@ -31657,7 +32153,7 @@ const maybeOpenBrowser = (target) => Effect.gen(function* () {
31657
32153
  yield* (yield* ExternalLauncher).launchBrowser(target).pipe(Effect.catch(() => Effect.logInfo("browser auto-open unavailable", { hint: `Open ${target} in your browser.` })));
31658
32154
  });
31659
32155
  const runStartupPhase = (phase, effect) => effect.pipe(Effect.annotateSpans({ "startup.phase": phase }), Effect.withSpan(`server.startup.${phase}`));
31660
- const make$61 = Effect.gen(function* () {
32156
+ const make$62 = Effect.gen(function* () {
31661
32157
  const serverConfig = yield* ServerConfig$1;
31662
32158
  const keybindings = yield* Keybindings;
31663
32159
  const orchestrationReactor = yield* OrchestrationReactor;
@@ -31798,7 +32294,7 @@ const make$61 = Effect.gen(function* () {
31798
32294
  enqueueCommand: commandGate.enqueueCommand
31799
32295
  };
31800
32296
  });
31801
- const layer$52 = Layer.effect(ServerRuntimeStartup, make$61);
32297
+ const layer$52 = Layer.effect(ServerRuntimeStartup, make$62);
31802
32298
  //#endregion
31803
32299
  //#region src/serverRuntimeState.ts
31804
32300
  const PersistedServerRuntimeState = Schema$1.Struct({
@@ -31955,7 +32451,7 @@ function expandHomePath$2(input, path) {
31955
32451
  if (input.startsWith("~/") || input.startsWith("~\\")) return path.join(NodeOS.homedir(), input.slice(2));
31956
32452
  return input;
31957
32453
  }
31958
- const make$60 = Effect.gen(function* () {
32454
+ const make$61 = Effect.gen(function* () {
31959
32455
  const fileSystem = yield* FileSystem.FileSystem;
31960
32456
  const path = yield* Path.Path;
31961
32457
  const statWorkspaceRoot = Effect.fn("WorkspacePaths.statWorkspaceRoot")(function* (workspaceRoot, normalizedWorkspaceRoot, phase) {
@@ -32012,7 +32508,7 @@ const make$60 = Effect.gen(function* () {
32012
32508
  resolveRelativePathWithinRoot
32013
32509
  });
32014
32510
  });
32015
- const layer$51 = Layer.effect(WorkspacePaths, make$60);
32511
+ const layer$51 = Layer.effect(WorkspacePaths, make$61);
32016
32512
  //#endregion
32017
32513
  //#region src/cli/project.ts
32018
32514
  const isEnvironmentHttpCommonError = Schema$1.is(EnvironmentHttpCommonError);
@@ -33195,7 +33691,7 @@ const logP4ProjectFileLoadError = (error) => Effect.logWarning(error).pipe(Effec
33195
33691
  filePath: error.filePath,
33196
33692
  errorTag: error._tag
33197
33693
  }));
33198
- const make$59 = Effect.gen(function* () {
33694
+ const make$60 = Effect.gen(function* () {
33199
33695
  const fileSystem = yield* FileSystem.FileSystem;
33200
33696
  const path = yield* Path.Path;
33201
33697
  const load = Effect.fn("P4ProjectFileLoader.load")(function* (workspaceRoot) {
@@ -33216,7 +33712,7 @@ const make$59 = Effect.gen(function* () {
33216
33712
  });
33217
33713
  return P4ProjectFileLoader.of({ load });
33218
33714
  });
33219
- const layer$50 = Layer.effect(P4ProjectFileLoader, make$59);
33715
+ const layer$50 = Layer.effect(P4ProjectFileLoader, make$60);
33220
33716
  //#endregion
33221
33717
  //#region src/project/ProjectFaviconResolver.ts
33222
33718
  /**
@@ -33291,7 +33787,7 @@ function extractIconHref(source) {
33291
33787
  return null;
33292
33788
  }
33293
33789
  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) }));
33294
- const make$58 = Effect.gen(function* () {
33790
+ const make$59 = Effect.gen(function* () {
33295
33791
  const fileSystem = yield* FileSystem.FileSystem;
33296
33792
  const path = yield* Path.Path;
33297
33793
  const workspacePaths = yield* WorkspacePaths;
@@ -33360,7 +33856,7 @@ const make$58 = Effect.gen(function* () {
33360
33856
  });
33361
33857
  return ProjectFaviconResolver.of({ resolvePath });
33362
33858
  });
33363
- const layer$49 = Layer.effect(ProjectFaviconResolver, make$58);
33859
+ const layer$49 = Layer.effect(ProjectFaviconResolver, make$59);
33364
33860
  //#endregion
33365
33861
  //#region src/assets/AssetAccess.ts
33366
33862
  const ASSET_ROUTE_PREFIX = "/api/assets";
@@ -33771,10 +34267,10 @@ const resolveAsset = Effect.fn("AssetAccess.resolveAsset")(function* (token, rel
33771
34267
  //#endregion
33772
34268
  //#region src/observability/BrowserTraceCollector.ts
33773
34269
  var BrowserTraceCollector = class extends Context.Service()("@p4code/cli/observability/BrowserTraceCollector") {};
33774
- const make$57 = (sink) => BrowserTraceCollector.of({ record: (records) => Effect.sync(() => {
34270
+ const make$58 = (sink) => BrowserTraceCollector.of({ record: (records) => Effect.sync(() => {
33775
34271
  for (const record of records) sink.push(record);
33776
34272
  }) });
33777
- const layer$48 = (sink) => Layer.succeed(BrowserTraceCollector, make$57(sink));
34273
+ const layer$48 = (sink) => Layer.succeed(BrowserTraceCollector, make$58(sink));
33778
34274
  //#endregion
33779
34275
  //#region src/auth/http.ts
33780
34276
  const CREDENTIAL_RESPONSE_HEADERS = {
@@ -36827,7 +37323,7 @@ const classifyNonZeroExit = (command, stderr) => {
36827
37323
  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";
36828
37324
  return "command-failed";
36829
37325
  };
36830
- const make$56 = Effect.gen(function* () {
37326
+ const make$57 = Effect.gen(function* () {
36831
37327
  const processRunner = yield* ProcessRunner;
36832
37328
  const run = Effect.fn("VcsProcess.run")(function* (input) {
36833
37329
  const baseError = {
@@ -36886,7 +37382,7 @@ const make$56 = Effect.gen(function* () {
36886
37382
  });
36887
37383
  return VcsProcess.of({ run });
36888
37384
  });
36889
- const layer$47 = Layer.effect(VcsProcess, make$56).pipe(Layer.provide(layer$61));
37385
+ const layer$47 = Layer.effect(VcsProcess, make$57).pipe(Layer.provide(layer$61));
36890
37386
  //#endregion
36891
37387
  //#region src/vcs/VcsDriver.ts
36892
37388
  var VcsDriver = class extends Context.Service()("@p4code/cli/vcs/VcsDriver") {};
@@ -37337,12 +37833,12 @@ const makeVcsDriver = Effect.gen(function* () {
37337
37833
  const driver = yield* makeVcsDriverShape();
37338
37834
  return VcsDriver.of(driver);
37339
37835
  });
37340
- const make$55 = Effect.gen(function* () {
37836
+ const make$56 = Effect.gen(function* () {
37341
37837
  const git = yield* makeGitVcsDriverCore();
37342
37838
  return GitVcsDriver.of(git);
37343
37839
  });
37344
37840
  Layer.effect(VcsDriver, makeVcsDriver);
37345
- const layer$46 = Layer.effect(GitVcsDriver, make$55);
37841
+ const layer$46 = Layer.effect(GitVcsDriver, make$56);
37346
37842
  //#endregion
37347
37843
  //#region src/vcs/VcsProjectConfig.ts
37348
37844
  const ProjectVcsConfigJson = fromLenientJson(Schema$1.Struct({
@@ -37374,7 +37870,7 @@ const logVcsProjectConfigError = (error) => Effect.logWarning(error).pipe(Effect
37374
37870
  configPath: error.configPath,
37375
37871
  errorTag: error._tag
37376
37872
  }));
37377
- const make$54 = Effect.gen(function* () {
37873
+ const make$55 = Effect.gen(function* () {
37378
37874
  const fileSystem = yield* FileSystem.FileSystem;
37379
37875
  const path = yield* Path.Path;
37380
37876
  const findConfigPath = Effect.fn("VcsProjectConfig.findConfigPath")(function* (cwd) {
@@ -37415,7 +37911,7 @@ const make$54 = Effect.gen(function* () {
37415
37911
  });
37416
37912
  return VcsProjectConfig.of({ resolveKind });
37417
37913
  });
37418
- const layer$45 = Layer.effect(VcsProjectConfig, make$54);
37914
+ const layer$45 = Layer.effect(VcsProjectConfig, make$55);
37419
37915
  //#endregion
37420
37916
  //#region src/vcs/VcsDriverRegistry.ts
37421
37917
  const DETECTION_CACHE_CAPACITY = 2048;
@@ -37435,7 +37931,7 @@ function parseDetectionCacheKey(key) {
37435
37931
  cwd: key.slice(separatorIndex + 1)
37436
37932
  };
37437
37933
  }
37438
- const make$53 = Effect.gen(function* () {
37934
+ const make$54 = Effect.gen(function* () {
37439
37935
  const projectConfig = yield* VcsProjectConfig;
37440
37936
  const git = yield* makeVcsDriver;
37441
37937
  const drivers = { git };
@@ -37492,7 +37988,7 @@ const make$53 = Effect.gen(function* () {
37492
37988
  resolve
37493
37989
  });
37494
37990
  });
37495
- const layer$44 = Layer.effect(VcsDriverRegistry, make$53).pipe(Layer.provide(layer$45));
37991
+ const layer$44 = Layer.effect(VcsDriverRegistry, make$54).pipe(Layer.provide(layer$45));
37496
37992
  //#endregion
37497
37993
  //#region src/checkpointing/CheckpointStore.ts
37498
37994
  /**
@@ -37512,7 +38008,7 @@ const layer$44 = Layer.effect(VcsDriverRegistry, make$53).pipe(Layer.provide(lay
37512
38008
  */
37513
38009
  /** Service tag for checkpoint persistence and restore operations. */
37514
38010
  var CheckpointStore = class extends Context.Service()("@p4code/cli/checkpointing/CheckpointStore") {};
37515
- const make$52 = Effect.gen(function* () {
38011
+ const make$53 = Effect.gen(function* () {
37516
38012
  const vcsRegistry = yield* VcsDriverRegistry;
37517
38013
  const resolveCheckpoints = Effect.fn("CheckpointStore.resolveCheckpoints")(function* (operation, cwd) {
37518
38014
  const handle = yield* vcsRegistry.resolve({ cwd });
@@ -37551,7 +38047,7 @@ const make$52 = Effect.gen(function* () {
37551
38047
  deleteCheckpointRefs
37552
38048
  });
37553
38049
  });
37554
- const layer$43 = Layer.effect(CheckpointStore, make$52);
38050
+ const layer$43 = Layer.effect(CheckpointStore, make$53);
37555
38051
  //#endregion
37556
38052
  //#region src/checkpointing/CheckpointDiffQuery.ts
37557
38053
  /**
@@ -37573,7 +38069,7 @@ function buildTurnDiffResult(input, diff) {
37573
38069
  diff
37574
38070
  };
37575
38071
  }
37576
- const make$51 = Effect.gen(function* () {
38072
+ const make$52 = Effect.gen(function* () {
37577
38073
  const projectionSnapshotQuery = yield* ProjectionSnapshotQuery;
37578
38074
  const checkpointStore = yield* CheckpointStore;
37579
38075
  const threadActivities = yield* ProjectionThreadActivityRepository;
@@ -37744,7 +38240,7 @@ const make$51 = Effect.gen(function* () {
37744
38240
  getFullThreadDiff
37745
38241
  });
37746
38242
  });
37747
- const layer$42 = Layer.effect(CheckpointDiffQuery, make$51);
38243
+ const layer$42 = Layer.effect(CheckpointDiffQuery, make$52);
37748
38244
  //#endregion
37749
38245
  //#region ../../packages/shared/src/toolCategory.ts
37750
38246
  const TOOL_CATEGORY_TITLES = {
@@ -40236,24 +40732,55 @@ const toTaskStoreError = (operation) => (cause) => Effect.logWarning("task store
40236
40732
  * still reading its stale snapshot. The row stays the single source of truth
40237
40733
  * and the agent reads it live over MCP.
40238
40734
  *
40239
- * Two instructions carry their weight. The agent is told its thread is already
40240
- * linked to the task, because otherwise its first useful move is to guess which
40241
- * board row it is on. And it is told to leave `status` alone: the server moved
40242
- * the task to `in_progress` when it started the thread, and where the work goes
40243
- * after that is the user's call — the same rule the scoping prompt follows, and
40244
- * the reason the board's columns still mean something.
40735
+ * The prompt owns the whole task lifecycle rather than merely pointing at the
40736
+ * row. It tells the agent how to orient safely, verify the acceptance criteria,
40737
+ * obtain a skeptical review, deliver repository changes through a pull request,
40738
+ * and leave the board in the state that matches the actual outcome.
40245
40739
  */
40246
40740
  const buildAutoPrompt = (task) => {
40247
40741
  const reference = task.readableId ?? task.taskId;
40248
- return `Picking up board task \`${reference}\`: ${task.title}
40742
+ return `Take ownership of board task \`${reference}\`: ${task.title}
40743
+
40744
+ Start with \`task_current\` (or \`task_get\` with \`${reference}\`). Read the current body, acceptance criteria, labels, priority, relationships, and existing evidence. This thread is already linked and the task is \`in_progress\`. The live row is the source of truth; re-read it before the final update.
40249
40745
 
40250
- Read the task first via \`task_current\` (or \`task_get\` with \`${reference}\`). Body, labels, project and priority live on the row, not in this message; the row may have changed since this thread started - the current row is the source of truth. Re-read it the same way whenever you need the fields again.
40746
+ Orient before acting. Read repository instructions and any skills or workflows the task names or clearly requires. Inspect the relevant code, documentation, state, recent history, and current checkout. Determine what is already complete and identify the exact remaining acceptance work.
40251
40747
 
40252
- This thread is already linked to the task, status In progress. Do the work.
40748
+ Preserve unrelated changes. If the checkout is dirty, stale, on the wrong base, or otherwise unsafe for this task, create an isolated worktree from the appropriate current base. Never discard or overwrite work you do not own.
40253
40749
 
40254
- Record findings via \`task_update\` as you go - decisions made, constraints hit, things the task turned out to be wrong about. That is what makes the row worth reading afterwards. Leave \`status\` alone: moving work across the board is the user's call.
40750
+ Execute the task end to end. Stay within its intent: no speculative features, unrelated refactors, or broad verification. Reuse valid existing evidence instead of repeating completed work without reason. Resolve routine in-scope problems autonomously.
40255
40751
 
40256
- After reading the task, read the code it concerns. If it is specified too poorly to act on, say so and stop rather than guessing.`;
40752
+ If requirements are materially ambiguous, investigate available context first. Ask only when different interpretations would materially change the result. If a real environment, permission, or dependency blocker remains after safe task-relevant recovery, stop with the exact blocker and required next action rather than guessing.
40753
+
40754
+ Verify the smallest sufficient proof against every acceptance criterion. Follow required skills exactly. Do not claim results you did not observe.
40755
+
40756
+ Keep the task row useful through \`task_update\`, not as an activity diary. Record durable decisions, corrected assumptions, concise verification evidence, and blockers. Keep status current:
40757
+
40758
+ - \`done\` only when every acceptance criterion is satisfied and no required work remains.
40759
+ - \`in_review\` when repository work is ready in a pull request or execution is complete but human review or a product decision remains.
40760
+ - \`in_progress\` when work or a blocker remains, with the exact outstanding item recorded.
40761
+ - Update related tasks only when the task requires it.
40762
+
40763
+ For repository-changing work, unless the live task or current user explicitly forbids pull-request delivery, this prompt authorizes you to create a task branch or isolated worktree, commit only your changes, push that branch, and open a pull request. Follow repository branch, commit, rebase, pull-request template, and evidence rules. Never commit directly to \`main\` or \`master\` unless the live task explicitly requires the repository's established direct-delivery workflow. Never force-push unless the current user explicitly authorizes it.
40764
+
40765
+ Before delivery or board completion, ask one bounded read-only sub-agent to review the task requirements, result, verification evidence, and complete diff when one exists. Its job is to find correctness defects, regressions, missed acceptance criteria, unsupported claims, and unnecessary scope. It must not edit files or spawn another agent. If no sub-agent mechanism is available, perform the same skeptical review yourself and record that limitation.
40766
+
40767
+ Validate every finding yourself. Fix real issues, reject false positives with a concrete reason, and rerun affected verification. If review fixes or conflict resolution materially change the diff, run one final bounded review pass.
40768
+
40769
+ Before opening the pull request:
40770
+
40771
+ - Re-read the live task and confirm every acceptance criterion.
40772
+ - Sync with the appropriate current base using the repository's required workflow.
40773
+ - Inspect the final diff and ensure it contains only task-owned changes.
40774
+ - Stage explicit paths and commit using repository conventions.
40775
+ - Push only the task branch.
40776
+ - Reuse an existing task pull request when appropriate; never create a duplicate.
40777
+ - Include the problem, solution, verification, required visual evidence, and task reference in the pull request.
40778
+
40779
+ Open a pull request only when the task produced repository changes. Verification, research, product-decision, and operational tasks with no diff should finish through evidence on the board instead. Do not delete files or mutate external systems unless the live task or current user explicitly authorizes that action.
40780
+
40781
+ After opening the pull request, append its URL and concise verification evidence to the task and move it to \`in_review\`. Leave it \`in_progress\` if implementation, verification, review findings, push, or pull-request creation remains blocked. Mark a no-pull-request task \`done\` only when all acceptance criteria are satisfied.
40782
+
40783
+ Finish by reporting the outcome, verification performed, sub-agent review outcome, pull-request URL when applicable, board status, and any remaining blocker.`;
40257
40784
  };
40258
40785
  /**
40259
40786
  * Render a task into the prompt that seeds its thread.
@@ -40727,7 +41254,7 @@ function makeUpdateState(input) {
40727
41254
  output: input.output ?? null
40728
41255
  };
40729
41256
  }
40730
- const make$50 = Effect.fn("ProviderMaintenanceRunner.make")(function* () {
41257
+ const make$51 = Effect.fn("ProviderMaintenanceRunner.make")(function* () {
40731
41258
  const providerRegistry = yield* ProviderRegistry;
40732
41259
  const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
40733
41260
  const httpClient = yield* HttpClient.HttpClient;
@@ -40842,7 +41369,7 @@ const make$50 = Effect.fn("ProviderMaintenanceRunner.make")(function* () {
40842
41369
  });
40843
41370
  return ProviderMaintenanceRunner.of({ updateProvider });
40844
41371
  });
40845
- const layer$41 = Layer.effect(ProviderMaintenanceRunner, make$50());
41372
+ const layer$41 = Layer.effect(ProviderMaintenanceRunner, make$51());
40846
41373
  //#endregion
40847
41374
  //#region src/provider/Services/ProviderInstanceRegistry.ts
40848
41375
  var ProviderInstanceRegistry = class extends Context.Service()("@p4code/cli/provider/Services/ProviderInstanceRegistry") {};
@@ -40866,11 +41393,11 @@ const makeTextGenerationFromRegistry = (registry) => TextGeneration.of({
40866
41393
  detail: "This provider does not report account usage."
40867
41394
  }))))
40868
41395
  });
40869
- const make$49 = Effect.gen(function* () {
41396
+ const make$50 = Effect.gen(function* () {
40870
41397
  const registry = yield* ProviderInstanceRegistry;
40871
41398
  return makeTextGenerationFromRegistry(registry);
40872
41399
  });
40873
- const layer$40 = Layer.effect(TextGeneration, make$49);
41400
+ const layer$40 = Layer.effect(TextGeneration, make$50);
40874
41401
  //#endregion
40875
41402
  //#region src/provider/Drivers/ClaudeHome.ts
40876
41403
  const resolveClaudeHomePath = Effect.fn("resolveClaudeHomePath")(function* (config) {
@@ -41862,7 +42389,7 @@ Layer.succeed(UsageService, UsageService.of({ readSummary: (input) => Effect.suc
41862
42389
  },
41863
42390
  scanDurationMs: 0
41864
42391
  }) }));
41865
- const make$48 = Effect.gen(function* () {
42392
+ const make$49 = Effect.gen(function* () {
41866
42393
  const fileSystem = yield* FileSystem.FileSystem;
41867
42394
  const path = yield* Path.Path;
41868
42395
  const config = yield* ServerConfig$1;
@@ -42094,7 +42621,7 @@ const make$48 = Effect.gen(function* () {
42094
42621
  };
42095
42622
  }) };
42096
42623
  });
42097
- const layer$39 = Layer.effect(UsageService, make$48);
42624
+ const layer$39 = Layer.effect(UsageService, make$49);
42098
42625
  const SKILL_MANIFEST_FILENAME = "SKILL.md";
42099
42626
  /**
42100
42627
  * Split a catalogue id (`owner/repo/skill-name`) into its parts.
@@ -42240,7 +42767,7 @@ const emptyFetch = (id, unavailable) => ({
42240
42767
  skipped: [],
42241
42768
  unavailable
42242
42769
  });
42243
- const make$47 = Effect.gen(function* () {
42770
+ const make$48 = Effect.gen(function* () {
42244
42771
  const http = yield* HttpClient.HttpClient;
42245
42772
  const request = Effect.fn("SkillRegistry.request")(function* (url) {
42246
42773
  return yield* http.execute(HttpClientRequest.get(url).pipe(HttpClientRequest.setHeader("accept", "application/json"), HttpClientRequest.setHeader("user-agent", "p4code"))).pipe(Effect.timeout(REQUEST_TIMEOUT_MS));
@@ -42311,7 +42838,7 @@ const make$47 = Effect.gen(function* () {
42311
42838
  fetch
42312
42839
  };
42313
42840
  });
42314
- const layer$38 = Layer.effect(SkillRegistry, make$47);
42841
+ const layer$38 = Layer.effect(SkillRegistry, make$48);
42315
42842
  //#endregion
42316
42843
  //#region ../../packages/shared/src/KeyedCoalescingWorker.ts
42317
42844
  const makeKeyedCoalescingWorker = (options) => Effect.gen(function* () {
@@ -42610,7 +43137,7 @@ const serversEqual = (left, right) => {
42610
43137
  }
42611
43138
  return true;
42612
43139
  };
42613
- const make$46 = Effect.gen(function* PortDiscoveryMake() {
43140
+ const make$47 = Effect.gen(function* PortDiscoveryMake() {
42614
43141
  const net = yield* NetService;
42615
43142
  const processRunner = yield* ProcessRunner;
42616
43143
  const hostPlatform = yield* HostProcessPlatform;
@@ -42761,7 +43288,7 @@ const make$46 = Effect.gen(function* PortDiscoveryMake() {
42761
43288
  unregisterTerminal
42762
43289
  });
42763
43290
  }).pipe(Effect.withSpan("PortDiscovery.make"));
42764
- const layer$37 = Layer.effect(PortDiscovery, make$46);
43291
+ const layer$37 = Layer.effect(PortDiscovery, make$47);
42765
43292
  //#endregion
42766
43293
  //#region src/terminal/Manager.ts
42767
43294
  /**
@@ -43439,7 +43966,7 @@ function normalizedRuntimeEnv(env) {
43439
43966
  if (entries.length === 0) return null;
43440
43967
  return Object.fromEntries(entries.toSorted(([left], [right]) => left.localeCompare(right)));
43441
43968
  }
43442
- const make$45 = Effect.fn("TerminalManager.make")(function* () {
43969
+ const make$46 = Effect.fn("TerminalManager.make")(function* () {
43443
43970
  const { terminalLogsDir } = yield* ServerConfig$1;
43444
43971
  const ptyAdapter = yield* PtyAdapter;
43445
43972
  const portDiscovery = yield* PortDiscovery;
@@ -44401,7 +44928,7 @@ const makeWithOptions$1 = Effect.fn("TerminalManager.makeWithOptions")(function*
44401
44928
  subscribeMetadata
44402
44929
  });
44403
44930
  });
44404
- const layer$36 = Layer.effect(TerminalManager, make$45()).pipe(Layer.provide(layer$61));
44931
+ const layer$36 = Layer.effect(TerminalManager, make$46()).pipe(Layer.provide(layer$61));
44405
44932
  //#endregion
44406
44933
  //#region src/mcp/McpInvocationContext.ts
44407
44934
  var McpInvocationContext = class extends Context.Service()("@p4code/cli/mcp/McpInvocationContext") {};
@@ -44611,7 +45138,7 @@ const classifyResponseError = (context, error) => {
44611
45138
  });
44612
45139
  }
44613
45140
  };
44614
- const make$44 = Effect.gen(function* PreviewAutomationBrokerMake() {
45141
+ const make$45 = Effect.gen(function* PreviewAutomationBrokerMake() {
44615
45142
  const crypto = yield* Crypto.Crypto;
44616
45143
  const state = yield* SynchronizedRef.make({
44617
45144
  clients: /* @__PURE__ */ new Map(),
@@ -44845,7 +45372,7 @@ const make$44 = Effect.gen(function* PreviewAutomationBrokerMake() {
44845
45372
  invoke
44846
45373
  });
44847
45374
  }).pipe(Effect.withSpan("PreviewAutomationBroker.make"));
44848
- const layer$35 = Layer.effect(PreviewAutomationBroker, make$44);
45375
+ const layer$35 = Layer.effect(PreviewAutomationBroker, make$45);
44849
45376
  //#endregion
44850
45377
  //#region src/preview/Manager.ts
44851
45378
  /**
@@ -44909,7 +45436,7 @@ const buildIdleSnapshot = (input) => ({
44909
45436
  viewport: FILL_PREVIEW_VIEWPORT,
44910
45437
  updatedAt: input.updatedAt
44911
45438
  });
44912
- const make$43 = Effect.gen(function* PreviewManagerMake() {
45439
+ const make$44 = Effect.gen(function* PreviewManagerMake() {
44913
45440
  const serverEpoch = NodeCrypto.randomUUID();
44914
45441
  const stateRef = yield* SynchronizedRef.make(initialState);
44915
45442
  const eventsPubSub = yield* PubSub.unbounded();
@@ -45140,7 +45667,7 @@ const make$43 = Effect.gen(function* PreviewManagerMake() {
45140
45667
  subscribeEvents: PubSub.subscribe(eventsPubSub)
45141
45668
  });
45142
45669
  }).pipe(Effect.withSpan("PreviewManager.make"));
45143
- const layer$34 = Layer.effect(PreviewManager, make$43);
45670
+ const layer$34 = Layer.effect(PreviewManager, make$44);
45144
45671
  //#endregion
45145
45672
  //#region src/workspace/WorkspaceSearchIndex.ts
45146
45673
  const WORKSPACE_INDEX_MAX_ENTRIES = 25e3;
@@ -45274,7 +45801,7 @@ const waitForScan = (cwd, finder, onFailure) => Effect.try({
45274
45801
  timeout: WORKSPACE_INDEX_SCAN_TIMEOUT
45275
45802
  })
45276
45803
  }), Effect.withSpan("WorkspaceSearchIndex.waitForScan"));
45277
- const make$42 = Effect.fn("WorkspaceSearchIndex.make")(function* (cwd) {
45804
+ const make$43 = Effect.fn("WorkspaceSearchIndex.make")(function* (cwd) {
45278
45805
  const finder = yield* Effect.acquireRelease(createFinder(cwd), (finder) => Effect.try({
45279
45806
  try: () => finder.destroy(),
45280
45807
  catch: (cause) => new WorkspaceSearchIndexDestroyFailed({
@@ -45348,7 +45875,7 @@ const make$42 = Effect.fn("WorkspaceSearchIndex.make")(function* (cwd) {
45348
45875
  * workspace root. WorkspaceSearchIndexMap owns memoization and idle cleanup;
45349
45876
  * using a default cwd here would mix resources from different workspaces.
45350
45877
  */
45351
- const layer$33 = (cwd) => Layer.effect(WorkspaceSearchIndex, make$42(cwd));
45878
+ const layer$33 = (cwd) => Layer.effect(WorkspaceSearchIndex, make$43(cwd));
45352
45879
  var WorkspaceSearchIndexMap = class extends LayerMap.Service()("@p4code/cli/workspace/WorkspaceSearchIndexMap", {
45353
45880
  lookup: layer$33,
45354
45881
  idleTimeToLive: WORKSPACE_INDEX_IDLE_TTL
@@ -45412,7 +45939,7 @@ const resolveBrowseTarget = Effect.fn("WorkspaceEntries.resolveBrowseTarget")(fu
45412
45939
  if (!input.cwd) return yield* new WorkspaceEntriesCurrentProjectRequiredError({ partialPath: input.partialPath });
45413
45940
  return path.resolve(expandHomePath$1(input.cwd, path), input.partialPath);
45414
45941
  });
45415
- const make$41 = Effect.gen(function* () {
45942
+ const make$42 = Effect.gen(function* () {
45416
45943
  const path = yield* Path.Path;
45417
45944
  const workspacePaths = yield* WorkspacePaths;
45418
45945
  const workspaceSearchIndexes = yield* WorkspaceSearchIndexMap;
@@ -45486,7 +46013,7 @@ const make$41 = Effect.gen(function* () {
45486
46013
  search
45487
46014
  });
45488
46015
  });
45489
- const layer$32 = Layer.effect(WorkspaceEntries, make$41).pipe(Layer.provide(WorkspaceSearchIndexMap.layer));
46016
+ const layer$32 = Layer.effect(WorkspaceEntries, make$42).pipe(Layer.provide(WorkspaceSearchIndexMap.layer));
45490
46017
  //#endregion
45491
46018
  //#region src/workspace/WorkspaceFileSystem.ts
45492
46019
  /**
@@ -45545,7 +46072,7 @@ Schema$1.Union([
45545
46072
  ]);
45546
46073
  /** Service tag for workspace file operations. */
45547
46074
  var WorkspaceFileSystem = class extends Context.Service()("@p4code/cli/workspace/WorkspaceFileSystem") {};
45548
- const make$40 = Effect.gen(function* () {
46075
+ const make$41 = Effect.gen(function* () {
45549
46076
  const fileSystem = yield* FileSystem.FileSystem;
45550
46077
  const path = yield* Path.Path;
45551
46078
  const workspacePaths = yield* WorkspacePaths;
@@ -45689,7 +46216,7 @@ const make$40 = Effect.gen(function* () {
45689
46216
  writeFile
45690
46217
  });
45691
46218
  });
45692
- const layer$31 = Layer.effect(WorkspaceFileSystem, make$40);
46219
+ const layer$31 = Layer.effect(WorkspaceFileSystem, make$41);
45693
46220
  //#endregion
45694
46221
  //#region src/textGeneration/TextGenerationPresets.ts
45695
46222
  const conventionalCommitsTextGenerationPolicy = {
@@ -45753,7 +46280,7 @@ var ProjectSetupScriptProjectNotFoundError = class extends Schema$1.TaggedErrorC
45753
46280
  };
45754
46281
  Schema$1.Union([ProjectSetupScriptOperationError, ProjectSetupScriptProjectNotFoundError]);
45755
46282
  var ProjectSetupScriptRunner = class extends Context.Service()("@p4code/cli/project/ProjectSetupScriptRunner") {};
45756
- const make$39 = Effect.gen(function* () {
46283
+ const make$40 = Effect.gen(function* () {
45757
46284
  const projectionSnapshotQuery = yield* ProjectionSnapshotQuery;
45758
46285
  const terminalManager = yield* TerminalManager;
45759
46286
  const runForThread = Effect.fn("ProjectSetupScriptRunner.runForThread")(function* (input) {
@@ -45811,7 +46338,7 @@ const make$39 = Effect.gen(function* () {
45811
46338
  });
45812
46339
  return ProjectSetupScriptRunner.of({ runForThread });
45813
46340
  });
45814
- const layer$30 = Layer.effect(ProjectSetupScriptRunner, make$39);
46341
+ const layer$30 = Layer.effect(ProjectSetupScriptRunner, make$40);
45815
46342
  //#endregion
45816
46343
  //#region src/sourceControl/azureDevOpsPullRequests.ts
45817
46344
  const AzureDevOpsPullRequestSchema = Schema$1.Struct({
@@ -46135,7 +46662,7 @@ function decodeAzureDevOpsJson(raw, schema, operation, cwd) {
46135
46662
  cause
46136
46663
  })));
46137
46664
  }
46138
- const make$38 = Effect.gen(function* () {
46665
+ const make$39 = Effect.gen(function* () {
46139
46666
  const process = yield* VcsProcess;
46140
46667
  const execute = (input) => process.run({
46141
46668
  operation: "AzureDevOpsCli.execute",
@@ -46277,7 +46804,7 @@ const make$38 = Effect.gen(function* () {
46277
46804
  }).pipe(Effect.asVoid)
46278
46805
  });
46279
46806
  });
46280
- const layer$29 = Layer.effect(AzureDevOpsCli, make$38);
46807
+ const layer$29 = Layer.effect(AzureDevOpsCli, make$39);
46281
46808
  //#endregion
46282
46809
  //#region src/sourceControl/SourceControlProviderDiscovery.ts
46283
46810
  function firstNonEmptyLine(text) {
@@ -46480,7 +47007,7 @@ function toChangeRequest$5(summary) {
46480
47007
  isCrossRepository: false
46481
47008
  };
46482
47009
  }
46483
- const make$37 = Effect.gen(function* () {
47010
+ const make$38 = Effect.gen(function* () {
46484
47011
  const azure = yield* AzureDevOpsCli;
46485
47012
  return SourceControlProvider.of({
46486
47013
  kind: "azure-devops",
@@ -46572,7 +47099,7 @@ const make$37 = Effect.gen(function* () {
46572
47099
  })))
46573
47100
  });
46574
47101
  });
46575
- Layer.effect(SourceControlProvider, make$37);
47102
+ Layer.effect(SourceControlProvider, make$38);
46576
47103
  //#endregion
46577
47104
  //#region src/sourceControl/bitbucketPullRequests.ts
46578
47105
  const BitbucketRepositoryRefSchema = Schema$1.Struct({
@@ -46949,7 +47476,7 @@ function responseError(operation, response) {
46949
47476
  responseBodyLength: collected.text.length
46950
47477
  }))));
46951
47478
  }
46952
- const make$36 = Effect.gen(function* () {
47479
+ const make$37 = Effect.gen(function* () {
46953
47480
  const config = yield* BitbucketApiEnvConfig;
46954
47481
  const httpClient = yield* HttpClient.HttpClient;
46955
47482
  const fileSystem = yield* FileSystem.FileSystem;
@@ -47165,7 +47692,7 @@ const make$36 = Effect.gen(function* () {
47165
47692
  })))
47166
47693
  });
47167
47694
  });
47168
- const layer$27 = Layer.effect(BitbucketApi, make$36);
47695
+ const layer$27 = Layer.effect(BitbucketApi, make$37);
47169
47696
  //#endregion
47170
47697
  //#region src/sourceControl/BitbucketSourceControlProvider.ts
47171
47698
  function toChangeRequest$4(summary) {
@@ -47183,7 +47710,7 @@ function toChangeRequest$4(summary) {
47183
47710
  ...summary.headRepositoryOwnerLogin !== void 0 ? { headRepositoryOwnerLogin: summary.headRepositoryOwnerLogin } : {}
47184
47711
  };
47185
47712
  }
47186
- const make$35 = Effect.gen(function* () {
47713
+ const make$36 = Effect.gen(function* () {
47187
47714
  const bitbucket = yield* BitbucketApi;
47188
47715
  return SourceControlProvider.of({
47189
47716
  kind: "bitbucket",
@@ -47274,7 +47801,7 @@ const make$35 = Effect.gen(function* () {
47274
47801
  })))
47275
47802
  });
47276
47803
  });
47277
- Layer.effect(SourceControlProvider, make$35);
47804
+ Layer.effect(SourceControlProvider, make$36);
47278
47805
  const makeDiscovery = Effect.gen(function* () {
47279
47806
  return {
47280
47807
  type: "api",
@@ -47516,7 +48043,7 @@ function deriveRepositoryCloneUrlsFromCreateOutput(stdout, repository) {
47516
48043
  sshUrl: `git@${fallbackHost}:${repository}.git`
47517
48044
  };
47518
48045
  }
47519
- const make$34 = Effect.gen(function* () {
48046
+ const make$35 = Effect.gen(function* () {
47520
48047
  const process = yield* VcsProcess;
47521
48048
  const execute = (input) => process.run({
47522
48049
  operation: "GitHubCli.execute",
@@ -47634,7 +48161,7 @@ const make$34 = Effect.gen(function* () {
47634
48161
  }).pipe(Effect.asVoid)
47635
48162
  });
47636
48163
  });
47637
- const layer$25 = Layer.effect(GitHubCli, make$34);
48164
+ const layer$25 = Layer.effect(GitHubCli, make$35);
47638
48165
  //#endregion
47639
48166
  //#region src/sourceControl/gitHubAuthStatus.ts
47640
48167
  const GitHubAuthStatusAccountSchema = Schema$1.Struct({
@@ -47735,7 +48262,7 @@ const discovery$1 = {
47735
48262
  parseAuth: parseGitHubAuth,
47736
48263
  installHint: "Install the GitHub command-line tool (`gh`) via https://cli.github.com/ or your package manager (for example `brew install gh`)."
47737
48264
  };
47738
- const make$33 = Effect.gen(function* () {
48265
+ const make$34 = Effect.gen(function* () {
47739
48266
  const github = yield* GitHubCli;
47740
48267
  const listChangeRequests = (input) => {
47741
48268
  if (input.state === "open") return github.listOpenPullRequests({
@@ -47851,7 +48378,7 @@ const make$33 = Effect.gen(function* () {
47851
48378
  })))
47852
48379
  });
47853
48380
  });
47854
- Layer.effect(SourceControlProvider, make$33);
48381
+ Layer.effect(SourceControlProvider, make$34);
47855
48382
  //#endregion
47856
48383
  //#region src/sourceControl/gitLabMergeRequests.ts
47857
48384
  const GitLabProjectReferenceSchema = Schema$1.Struct({
@@ -48163,7 +48690,7 @@ function parseRepositoryPath(repository) {
48163
48690
  projectPath
48164
48691
  };
48165
48692
  }
48166
- const make$32 = Effect.gen(function* () {
48693
+ const make$33 = Effect.gen(function* () {
48167
48694
  const process = yield* VcsProcess;
48168
48695
  const run = (input, mapError) => process.run({
48169
48696
  operation: "GitLabCli.execute",
@@ -48314,7 +48841,7 @@ const make$32 = Effect.gen(function* () {
48314
48841
  }).pipe(Effect.asVoid)
48315
48842
  });
48316
48843
  });
48317
- const layer$23 = Layer.effect(GitLabCli, make$32);
48844
+ const layer$23 = Layer.effect(GitLabCli, make$33);
48318
48845
  //#endregion
48319
48846
  //#region src/sourceControl/gitLabAuthStatus.ts
48320
48847
  const HOST_LINE_PATTERN = /^(?:[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?|\[[a-f0-9:.]+\])(?::\d+)?$/iu;
@@ -48411,7 +48938,7 @@ const discovery = {
48411
48938
  refineUnknownRemote: refineUnknownGitLabRemote,
48412
48939
  installHint: "Install the GitLab command-line tool (`glab`) from https://gitlab.com/gitlab-org/cli or your package manager (for example `brew install glab`)."
48413
48940
  };
48414
- const make$31 = Effect.gen(function* () {
48941
+ const make$32 = Effect.gen(function* () {
48415
48942
  const gitlab = yield* GitLabCli;
48416
48943
  return SourceControlProvider.of({
48417
48944
  kind: "gitlab",
@@ -48499,7 +49026,7 @@ const make$31 = Effect.gen(function* () {
48499
49026
  })))
48500
49027
  });
48501
49028
  });
48502
- Layer.effect(SourceControlProvider, make$31);
49029
+ Layer.effect(SourceControlProvider, make$32);
48503
49030
  //#endregion
48504
49031
  //#region src/sourceControl/SourceControlProviderRegistry.ts
48505
49032
  const PROVIDER_DETECTION_CACHE_CAPACITY = 2048;
@@ -48655,12 +49182,12 @@ const makeWithProviders = Effect.fn("makeSourceControlProviderRegistryWithProvid
48655
49182
  })), { concurrency: "unbounded" })
48656
49183
  });
48657
49184
  });
48658
- const make$30 = Effect.gen(function* () {
48659
- const github = yield* make$33;
48660
- const gitlab = yield* make$31;
48661
- const bitbucket = yield* make$35;
49185
+ const make$31 = Effect.gen(function* () {
49186
+ const github = yield* make$34;
49187
+ const gitlab = yield* make$32;
49188
+ const bitbucket = yield* make$36;
48662
49189
  const bitbucketDiscovery = yield* makeDiscovery;
48663
- const azureDevOps = yield* make$37;
49190
+ const azureDevOps = yield* make$38;
48664
49191
  return yield* makeWithProviders([
48665
49192
  {
48666
49193
  kind: "github",
@@ -48684,7 +49211,7 @@ const make$30 = Effect.gen(function* () {
48684
49211
  }
48685
49212
  ]);
48686
49213
  });
48687
- const layer$21 = Layer.effect(SourceControlProviderRegistry, make$30);
49214
+ const layer$21 = Layer.effect(SourceControlProviderRegistry, make$31);
48688
49215
  //#endregion
48689
49216
  //#region src/sourceControl/PrTemplateDetection.ts
48690
49217
  const TEMPLATE_MAX_BYTES = 8e3;
@@ -49054,7 +49581,7 @@ function toPullRequestHeadRemoteInfo(pr) {
49054
49581
  ...pr.headRepositoryOwnerLogin !== void 0 ? { headRepositoryOwnerLogin: pr.headRepositoryOwnerLogin } : {}
49055
49582
  };
49056
49583
  }
49057
- const make$29 = Effect.gen(function* () {
49584
+ const make$30 = Effect.gen(function* () {
49058
49585
  const gitCore = yield* GitVcsDriver;
49059
49586
  const sourceControlProviders = yield* SourceControlProviderRegistry;
49060
49587
  const textGeneration = yield* TextGeneration;
@@ -49976,7 +50503,7 @@ const make$29 = Effect.gen(function* () {
49976
50503
  runStackedAction
49977
50504
  });
49978
50505
  });
49979
- const layer$20 = Layer.effect(GitManager, make$29);
50506
+ const layer$20 = Layer.effect(GitManager, make$30);
49980
50507
  //#endregion
49981
50508
  //#region src/git/GitWorkflowService.ts
49982
50509
  var GitWorkflowService = class extends Context.Service()("@p4code/cli/git/GitWorkflowService") {};
@@ -50013,7 +50540,7 @@ function nonRepositoryListRefs() {
50013
50540
  totalCount: 0
50014
50541
  };
50015
50542
  }
50016
- const make$28 = Effect.gen(function* () {
50543
+ const make$29 = Effect.gen(function* () {
50017
50544
  const registry = yield* VcsDriverRegistry;
50018
50545
  const git = yield* GitVcsDriver;
50019
50546
  const gitManager = yield* GitManager;
@@ -50099,7 +50626,7 @@ const make$28 = Effect.gen(function* () {
50099
50626
  renameBranch: (input) => ensureGit("GitWorkflowService.renameBranch", input.cwd).pipe(Effect.andThen(git.renameBranch(input)))
50100
50627
  });
50101
50628
  });
50102
- const layer$19 = Layer.effect(GitWorkflowService, make$28);
50629
+ const layer$19 = Layer.effect(GitWorkflowService, make$29);
50103
50630
  //#endregion
50104
50631
  //#region src/vcs/VcsStatusBroadcaster.ts
50105
50632
  const DEFAULT_VCS_STATUS_REFRESH_INTERVAL = Duration.seconds(30);
@@ -50171,7 +50698,7 @@ function fingerprintStatusPart(status) {
50171
50698
  return JSON.stringify(status);
50172
50699
  }
50173
50700
  const normalizeCwd = (cwd) => Effect.service(FileSystem.FileSystem).pipe(Effect.flatMap((fs) => fs.realPath(cwd)), Effect.orElseSucceed(() => cwd));
50174
- const make$27 = Effect.gen(function* () {
50701
+ const make$28 = Effect.gen(function* () {
50175
50702
  const workflow = yield* GitWorkflowService;
50176
50703
  const fs = yield* FileSystem.FileSystem;
50177
50704
  const changesPubSub = yield* Effect.acquireRelease(PubSub.unbounded(), (pubsub) => PubSub.shutdown(pubsub));
@@ -50395,7 +50922,7 @@ const make$27 = Effect.gen(function* () {
50395
50922
  streamStatus
50396
50923
  });
50397
50924
  });
50398
- const layer$18 = Layer.effect(VcsStatusBroadcaster, make$27);
50925
+ const layer$18 = Layer.effect(VcsStatusBroadcaster, make$28);
50399
50926
  //#endregion
50400
50927
  //#region src/vcs/VcsProvisioningService.ts
50401
50928
  var VcsProvisioningService = class extends Context.Service()("@p4code/cli/vcs/VcsProvisioningService") {};
@@ -50408,7 +50935,7 @@ function resolveRequestedKind(kind) {
50408
50935
  }));
50409
50936
  return Effect.succeed(kind);
50410
50937
  }
50411
- const make$26 = Effect.gen(function* () {
50938
+ const make$27 = Effect.gen(function* () {
50412
50939
  const registry = yield* VcsDriverRegistry;
50413
50940
  const initRepository = Effect.fn("VcsProvisioningService.initRepository")(function* (input) {
50414
50941
  const kind = yield* resolveRequestedKind(input.kind);
@@ -50416,11 +50943,11 @@ const make$26 = Effect.gen(function* () {
50416
50943
  });
50417
50944
  return VcsProvisioningService.of({ initRepository });
50418
50945
  });
50419
- const layer$17 = Layer.effect(VcsProvisioningService, make$26);
50946
+ const layer$17 = Layer.effect(VcsProvisioningService, make$27);
50420
50947
  //#endregion
50421
50948
  //#region src/review/ReviewService.ts
50422
50949
  var ReviewService = class extends Context.Service()("@p4code/cli/review/ReviewService") {};
50423
- const make$25 = Effect.gen(function* () {
50950
+ const make$26 = Effect.gen(function* () {
50424
50951
  const config = yield* ServerConfig$1;
50425
50952
  const fileSystem = yield* FileSystem.FileSystem;
50426
50953
  const path = yield* Path.Path;
@@ -50476,7 +51003,7 @@ const make$25 = Effect.gen(function* () {
50476
51003
  });
50477
51004
  return ReviewService.of({ getDiffPreview });
50478
51005
  });
50479
- const layer$16 = Layer.effect(ReviewService, make$25);
51006
+ const layer$16 = Layer.effect(ReviewService, make$26);
50480
51007
  //#endregion
50481
51008
  //#region src/diagnostics/ProcessDiagnostics.ts
50482
51009
  const PROCESS_QUERY_TIMEOUT_MS = 1e3;
@@ -50771,7 +51298,7 @@ function assertDescendantPid(pid) {
50771
51298
  }));
50772
51299
  }));
50773
51300
  }
50774
- const make$24 = Effect.gen(function* () {
51301
+ const make$25 = Effect.gen(function* () {
50775
51302
  const spawner = yield* ChildProcessSpawner$1.ChildProcessSpawner;
50776
51303
  const read = Effect.gen(function* () {
50777
51304
  const readAt = yield* DateTime.now;
@@ -50815,7 +51342,7 @@ const make$24 = Effect.gen(function* () {
50815
51342
  signal
50816
51343
  });
50817
51344
  });
50818
- const layer$15 = Layer.effect(ProcessDiagnostics, make$24);
51345
+ const layer$15 = Layer.effect(ProcessDiagnostics, make$25);
50819
51346
  //#endregion
50820
51347
  //#region src/diagnostics/ProcessResourceMonitor.ts
50821
51348
  const SAMPLE_INTERVAL_MS = 5e3;
@@ -50966,7 +51493,7 @@ function aggregateProcessResourceHistory(input) {
50966
51493
  }) : Option.none()
50967
51494
  };
50968
51495
  }
50969
- const make$23 = Effect.gen(function* () {
51496
+ const make$24 = Effect.gen(function* () {
50970
51497
  const spawner = yield* ChildProcessSpawner$1.ChildProcessSpawner;
50971
51498
  const state = yield* Ref.make({
50972
51499
  samples: [],
@@ -51015,7 +51542,7 @@ const make$23 = Effect.gen(function* () {
51015
51542
  });
51016
51543
  return ProcessResourceMonitor.of({ readHistory });
51017
51544
  });
51018
- const layer$14 = Layer.effect(ProcessResourceMonitor, make$23);
51545
+ const layer$14 = Layer.effect(ProcessResourceMonitor, make$24);
51019
51546
  //#endregion
51020
51547
  //#region src/diagnostics/TraceDiagnostics.ts
51021
51548
  var TraceFileReadError = class extends Schema$1.TaggedErrorClass()("TraceFileReadError", {
@@ -51263,7 +51790,7 @@ function readTraceFile(fileSystem, path) {
51263
51790
  cause
51264
51791
  })) }));
51265
51792
  }
51266
- const make$22 = Effect.gen(function* () {
51793
+ const make$23 = Effect.gen(function* () {
51267
51794
  const fileSystem = yield* FileSystem.FileSystem;
51268
51795
  const read = Effect.fn("TraceDiagnostics.read")(function* (options) {
51269
51796
  const readAt = options.readAt ?? (yield* DateTime.now);
@@ -51307,7 +51834,7 @@ const make$22 = Effect.gen(function* () {
51307
51834
  });
51308
51835
  return TraceDiagnostics.of({ read });
51309
51836
  });
51310
- const layer$13 = Layer.effect(TraceDiagnostics, make$22);
51837
+ const layer$13 = Layer.effect(TraceDiagnostics, make$23);
51311
51838
  function readTraceDiagnostics(options) {
51312
51839
  return Effect.gen(function* () {
51313
51840
  return yield* (yield* TraceDiagnostics).read(options);
@@ -51669,7 +52196,7 @@ function isReviewerName(value) {
51669
52196
  const name = value.trim();
51670
52197
  return name.length > 0 && !name.startsWith("-");
51671
52198
  }
51672
- const make$21 = Effect.gen(function* () {
52199
+ const make$22 = Effect.gen(function* () {
51673
52200
  const azure = yield* AzureDevOpsCli;
51674
52201
  const detectArgs = ["--detect", "true"];
51675
52202
  const executeJson = (input) => azure.execute({
@@ -51869,7 +52396,7 @@ const make$21 = Effect.gen(function* () {
51869
52396
  }).pipe(Effect.asVoid)
51870
52397
  });
51871
52398
  });
51872
- const layer$12 = Layer.effect(AzureDevOpsPullRequestCli, make$21);
52399
+ const layer$12 = Layer.effect(AzureDevOpsPullRequestCli, make$22);
51873
52400
  //#endregion
51874
52401
  //#region src/pullRequest/AzureDevOpsPullRequestProvider.ts
51875
52402
  const CAPABILITIES$3 = {
@@ -51944,7 +52471,7 @@ function toChangeRequest$1(pullRequest) {
51944
52471
  labels: []
51945
52472
  };
51946
52473
  }
51947
- const make$20 = Effect.gen(function* () {
52474
+ const make$21 = Effect.gen(function* () {
51948
52475
  const cli = yield* AzureDevOpsPullRequestCli;
51949
52476
  const fail = (operation) => (error) => new PullRequestProviderError({
51950
52477
  provider: "azure-devops",
@@ -52676,7 +53203,7 @@ function mergeStrategy(method) {
52676
53203
  default: return "merge_commit";
52677
53204
  }
52678
53205
  }
52679
- const make$19 = Effect.gen(function* () {
53206
+ const make$20 = Effect.gen(function* () {
52680
53207
  const bitbucket = yield* BitbucketApi;
52681
53208
  /**
52682
53209
  * The repository's own path, and the workspace above it — which the people who may review are
@@ -52956,7 +53483,7 @@ const make$19 = Effect.gen(function* () {
52956
53483
  }).pipe(Effect.asVoid))
52957
53484
  });
52958
53485
  });
52959
- const layer$11 = Layer.effect(BitbucketPullRequestApi, make$19);
53486
+ const layer$11 = Layer.effect(BitbucketPullRequestApi, make$20);
52960
53487
  //#endregion
52961
53488
  //#region src/pullRequest/BitbucketPullRequestProvider.ts
52962
53489
  const CAPABILITIES$2 = {
@@ -53036,7 +53563,7 @@ function toChangeRequest(pullRequest) {
53036
53563
  labels: []
53037
53564
  };
53038
53565
  }
53039
- const make$18 = Effect.gen(function* () {
53566
+ const make$19 = Effect.gen(function* () {
53040
53567
  const api = yield* BitbucketPullRequestApi;
53041
53568
  const fail = (operation) => (error) => new PullRequestProviderError({
53042
53569
  provider: "bitbucket",
@@ -54917,7 +55444,7 @@ function actionArgs$1(action, mergeMethod, updateMethod) {
54917
55444
  case "reopen": return ["reopen"];
54918
55445
  }
54919
55446
  }
54920
- const make$17 = Effect.gen(function* () {
55447
+ const make$18 = Effect.gen(function* () {
54921
55448
  const github = yield* GitHubCli;
54922
55449
  /**
54923
55450
  * The pull request's own node id, which is what a mutation against the pull request itself is
@@ -55637,7 +56164,7 @@ const make$17 = Effect.gen(function* () {
55637
56164
  })))
55638
56165
  });
55639
56166
  });
55640
- const layer$10 = Layer.effect(GitHubPullRequestCli, make$17);
56167
+ const layer$10 = Layer.effect(GitHubPullRequestCli, make$18);
55641
56168
  //#endregion
55642
56169
  //#region src/pullRequest/GitHubPullRequestProvider.ts
55643
56170
  const CAPABILITIES$1 = {
@@ -55752,7 +56279,7 @@ function loginAvatarUrl(login, host) {
55752
56279
  }
55753
56280
  /** True where markdown would render nothing: whitespace, or only HTML comments. */
55754
56281
  const rendersEmpty = (body) => body.replace(/<!--[\s\S]*?-->/g, "").trim().length === 0;
55755
- const make$16 = Effect.gen(function* () {
56282
+ const make$17 = Effect.gen(function* () {
55756
56283
  const cli = yield* GitHubPullRequestCli;
55757
56284
  const fail = (operation) => (error) => new PullRequestProviderError({
55758
56285
  provider: "github",
@@ -56766,7 +57293,7 @@ function actionArgs(action, mergeMethod) {
56766
57293
  case "reopen": return ["reopen"];
56767
57294
  }
56768
57295
  }
56769
- const make$15 = Effect.gen(function* () {
57296
+ const make$16 = Effect.gen(function* () {
56770
57297
  const gitlab = yield* GitLabCli;
56771
57298
  const api = (input) => gitlab.execute({
56772
57299
  cwd: input.cwd,
@@ -57337,7 +57864,7 @@ const make$15 = Effect.gen(function* () {
57337
57864
  }).pipe(Effect.asVoid)
57338
57865
  });
57339
57866
  });
57340
- const layer$9 = Layer.effect(GitLabPullRequestCli, make$15);
57867
+ const layer$9 = Layer.effect(GitLabPullRequestCli, make$16);
57341
57868
  //#endregion
57342
57869
  //#region src/pullRequest/GitLabPullRequestProvider.ts
57343
57870
  const CAPABILITIES = {
@@ -57417,7 +57944,7 @@ function reasonFor(error) {
57417
57944
  if (error._tag === "GitLabCliAuthenticationError") return "unauthenticated";
57418
57945
  return "failed";
57419
57946
  }
57420
- const make$14 = Effect.gen(function* () {
57947
+ const make$15 = Effect.gen(function* () {
57421
57948
  const cli = yield* GitLabPullRequestCli;
57422
57949
  const fail = (operation) => (error) => new PullRequestProviderError({
57423
57950
  provider: "gitlab",
@@ -57560,13 +58087,13 @@ function fromProviders(providers) {
57560
58087
  * The hosts this build can read change requests from. A host with no entry here still shows up
57561
58088
  * in the provider list as unimplemented, so its projects are explained rather than missing.
57562
58089
  */
57563
- const make$13 = Effect.map(Effect.all([
57564
- make$16,
57565
- make$14,
57566
- make$18,
57567
- make$20
58090
+ const make$14 = Effect.map(Effect.all([
58091
+ make$17,
58092
+ make$15,
58093
+ make$19,
58094
+ make$21
57568
58095
  ]), fromProviders);
57569
- const layer$8 = Layer.effect(PullRequestProviderRegistry, make$13).pipe(Layer.provide(layer$10.pipe(Layer.provide(layer$25))), Layer.provide(layer$9.pipe(Layer.provide(layer$23))), Layer.provide(layer$11.pipe(Layer.provide(layer$27))), Layer.provide(layer$12.pipe(Layer.provide(layer$29))));
58096
+ const layer$8 = Layer.effect(PullRequestProviderRegistry, make$14).pipe(Layer.provide(layer$10.pipe(Layer.provide(layer$25))), Layer.provide(layer$9.pipe(Layer.provide(layer$23))), Layer.provide(layer$11.pipe(Layer.provide(layer$27))), Layer.provide(layer$12.pipe(Layer.provide(layer$29))));
57570
58097
  //#endregion
57571
58098
  //#region src/pullRequest/PullRequestService.ts
57572
58099
  /**
@@ -57754,7 +58281,7 @@ function repositoryIdentityOf(project) {
57754
58281
  if (identity.displayName) return identity.displayName;
57755
58282
  return identity.owner && identity.name ? `${identity.owner}/${identity.name}` : null;
57756
58283
  }
57757
- const make$12 = Effect.gen(function* () {
58284
+ const make$13 = Effect.gen(function* () {
57758
58285
  const registry = yield* PullRequestProviderRegistry;
57759
58286
  const projections = yield* ProjectionSnapshotQuery;
57760
58287
  const sourceControlProviders = yield* SourceControlProviderRegistry;
@@ -58722,7 +59249,7 @@ const make$12 = Effect.gen(function* () {
58722
59249
  invalidate
58723
59250
  });
58724
59251
  });
58725
- const layer$7 = Layer.effect(PullRequestService, make$12);
59252
+ const layer$7 = Layer.effect(PullRequestService, make$13);
58726
59253
  //#endregion
58727
59254
  //#region src/sourceControl/SourceControlDiscovery.ts
58728
59255
  const VCS_PROBES = [{
@@ -58741,7 +59268,7 @@ const VCS_PROBES = [{
58741
59268
  installHint: "Install Jujutsu with `brew install jj` or from https://github.com/jj-vcs/jj."
58742
59269
  }];
58743
59270
  var SourceControlDiscovery = class extends Context.Service()("@p4code/cli/sourceControl/SourceControlDiscovery") {};
58744
- const make$11 = Effect.gen(function* () {
59271
+ const make$12 = Effect.gen(function* () {
58745
59272
  const config = yield* ServerConfig$1;
58746
59273
  const process = yield* VcsProcess;
58747
59274
  const sourceControlProviders = yield* SourceControlProviderRegistry;
@@ -58790,7 +59317,7 @@ const make$11 = Effect.gen(function* () {
58790
59317
  sourceControlProviders: sourceControlProviders.discover
58791
59318
  }) });
58792
59319
  });
58793
- const layer$6 = Layer.effect(SourceControlDiscovery, make$11);
59320
+ const layer$6 = Layer.effect(SourceControlDiscovery, make$12);
58794
59321
  //#endregion
58795
59322
  //#region src/sourceControl/SourceControlRepositoryService.ts
58796
59323
  const isSourceControlRepositoryError = Schema$1.is(SourceControlRepositoryError);
@@ -58823,7 +59350,7 @@ function expandHomePath(input, path) {
58823
59350
  if (input.startsWith("~/") || input.startsWith("~\\")) return path.join(NodeOS.homedir(), input.slice(2));
58824
59351
  return input;
58825
59352
  }
58826
- const make$10 = Effect.gen(function* () {
59353
+ const make$11 = Effect.gen(function* () {
58827
59354
  const config = yield* ServerConfig$1;
58828
59355
  const fileSystem = yield* FileSystem.FileSystem;
58829
59356
  const git = yield* GitVcsDriver;
@@ -58962,7 +59489,7 @@ const make$10 = Effect.gen(function* () {
58962
59489
  publishRepository: (input) => publishRepository(input).pipe(mapRepositoryError("publishRepository", input.provider))
58963
59490
  });
58964
59491
  });
58965
- const layer$5 = Layer.effect(SourceControlRepositoryService, make$10);
59492
+ const layer$5 = Layer.effect(SourceControlRepositoryService, make$11);
58966
59493
  //#endregion
58967
59494
  //#region src/ws.ts
58968
59495
  /** Matches `p4c hub token add`, so a token minted here and one minted there are the same thing. */
@@ -59365,6 +59892,23 @@ const makeWsRpcLayer = (currentSession, previewAutomationBroker) => WsRpcGroup.t
59365
59892
  threadId: event.payload.threadId
59366
59893
  }));
59367
59894
  case "thread.unarchived": return threadUpsertOrRemove(event.payload.threadId, event.sequence);
59895
+ case "thread-pair.created": return Effect.succeed(Option.some({
59896
+ kind: "thread-pair-upserted",
59897
+ sequence: event.sequence,
59898
+ pair: {
59899
+ id: event.payload.pairId,
59900
+ implementerThreadId: event.payload.implementerThreadId,
59901
+ watcherThreadId: event.payload.watcherThreadId,
59902
+ lastReviewedImplementerSequence: event.payload.lastReviewedImplementerSequence,
59903
+ createdAt: event.payload.createdAt,
59904
+ detachedAt: null
59905
+ }
59906
+ }));
59907
+ case "thread-pair.detached": return Effect.succeed(Option.some({
59908
+ kind: "thread-pair-removed",
59909
+ sequence: event.sequence,
59910
+ pairId: event.payload.pairId
59911
+ }));
59368
59912
  default:
59369
59913
  if (event.aggregateKind !== "thread") return Effect.succeed(Option.none());
59370
59914
  return threadUpsertOrRemove(ThreadId.make(event.aggregateId), event.sequence);
@@ -60186,7 +60730,7 @@ function toPersistenceSqlOrDecodeError(sqlOperation, decodeOperation, correlatio
60186
60730
  cause
60187
60731
  });
60188
60732
  }
60189
- const make$9 = Effect.gen(function* () {
60733
+ const make$10 = Effect.gen(function* () {
60190
60734
  const sql = yield* SqlClient.SqlClient;
60191
60735
  const upsertRuntimeRow = SqlSchema.void({
60192
60736
  Request: ProviderSessionRuntimeDbRowSchema,
@@ -60289,7 +60833,7 @@ const make$9 = Effect.gen(function* () {
60289
60833
  deleteByThreadId
60290
60834
  };
60291
60835
  });
60292
- const layer$4 = Layer.effect(ProviderSessionRuntimeRepository, make$9);
60836
+ const layer$4 = Layer.effect(ProviderSessionRuntimeRepository, make$10);
60293
60837
  //#endregion
60294
60838
  //#region src/provider/Errors.ts
60295
60839
  /**
@@ -60943,6 +61487,54 @@ const makeWithOptions = Effect.fn("McpSessionRegistry.make")(function* (options
60943
61487
  };
60944
61488
  });
60945
61489
  });
61490
+ const grantWatchThread = Effect.fn("McpSessionRegistry.grantWatchThread")(function* ({ watcherThreadId, watchedThreadId }) {
61491
+ yield* SynchronizedRef.update(state, ({ records, spawnedThreadIds }) => {
61492
+ const next = new Map(records);
61493
+ for (const [tokenHash, record] of records) {
61494
+ if (record.scope.threadId !== watcherThreadId) continue;
61495
+ next.set(tokenHash, {
61496
+ ...record,
61497
+ scope: {
61498
+ ...record.scope,
61499
+ capabilities: /* @__PURE__ */ new Set([...record.scope.capabilities, "watch"]),
61500
+ watchThreadIds: /* @__PURE__ */ new Set([...record.scope.watchThreadIds ?? [], watchedThreadId])
61501
+ }
61502
+ });
61503
+ }
61504
+ return {
61505
+ records: next,
61506
+ spawnedThreadIds
61507
+ };
61508
+ });
61509
+ });
61510
+ const revokeWatchThread = Effect.fn("McpSessionRegistry.revokeWatchThread")(function* ({ watcherThreadId, watchedThreadId }) {
61511
+ yield* SynchronizedRef.update(state, ({ records, spawnedThreadIds }) => {
61512
+ const next = new Map(records);
61513
+ for (const [tokenHash, record] of records) {
61514
+ if (record.scope.threadId !== watcherThreadId) continue;
61515
+ const watchThreadIds = new Set(record.scope.watchThreadIds ?? []);
61516
+ watchThreadIds.delete(watchedThreadId);
61517
+ const capabilities = new Set(record.scope.capabilities);
61518
+ if (watchThreadIds.size === 0) capabilities.delete("watch");
61519
+ const { watchThreadIds: _previousWatchThreadIds, ...scopeWithoutWatch } = record.scope;
61520
+ next.set(tokenHash, {
61521
+ ...record,
61522
+ scope: watchThreadIds.size > 0 ? {
61523
+ ...scopeWithoutWatch,
61524
+ capabilities,
61525
+ watchThreadIds
61526
+ } : {
61527
+ ...scopeWithoutWatch,
61528
+ capabilities
61529
+ }
61530
+ });
61531
+ }
61532
+ return {
61533
+ records: next,
61534
+ spawnedThreadIds
61535
+ };
61536
+ });
61537
+ });
60946
61538
  const recordSpawnedThread = Effect.fn("McpSessionRegistry.recordSpawnedThread")(function* (input) {
60947
61539
  yield* SynchronizedRef.update(state, ({ records, spawnedThreadIds }) => {
60948
61540
  const next = new Map(records);
@@ -60964,6 +61556,8 @@ const makeWithOptions = Effect.fn("McpSessionRegistry.make")(function* (options
60964
61556
  issue,
60965
61557
  resolve,
60966
61558
  touch,
61559
+ grantWatchThread,
61560
+ revokeWatchThread,
60967
61561
  recordSpawnedThread,
60968
61562
  revokeProviderSession: Effect.fn("McpSessionRegistry.revokeProviderSession")(function* (providerSessionId) {
60969
61563
  yield* revokeWhere((record) => record.scope.providerSessionId === providerSessionId);
@@ -60978,18 +61572,20 @@ const makeWithOptions = Effect.fn("McpSessionRegistry.make")(function* (options
60978
61572
  });
60979
61573
  });
60980
61574
  let activeMcpSessionRegistry;
60981
- const make$8 = Effect.acquireRelease(makeWithOptions().pipe(Effect.tap((registry) => Effect.sync(() => {
61575
+ const make$9 = Effect.acquireRelease(makeWithOptions().pipe(Effect.tap((registry) => Effect.sync(() => {
60982
61576
  activeMcpSessionRegistry = registry;
60983
61577
  }))), (registry) => Effect.sync(() => {
60984
61578
  if (activeMcpSessionRegistry === registry) activeMcpSessionRegistry = void 0;
60985
61579
  }));
60986
- const layer$3 = Layer.effect(McpSessionRegistry, make$8);
61580
+ const layer$3 = Layer.effect(McpSessionRegistry, make$9);
60987
61581
  const issueActiveMcpCredential = (request) => activeMcpSessionRegistry ? activeMcpSessionRegistry.revokeThread(request.threadId).pipe(Effect.andThen(activeMcpSessionRegistry.issue(request))) : Effect.sync(() => void 0);
60988
61582
  /**
60989
61583
  * Refreshes the liveness of a thread's MCP credential. Called on every provider
60990
61584
  * turn so an active session is never mistaken for an abandoned one.
60991
61585
  */
60992
61586
  const touchActiveMcpThread = (threadId) => activeMcpSessionRegistry ? activeMcpSessionRegistry.touch(threadId) : Effect.void;
61587
+ const grantActiveMcpWatchThread = (input) => activeMcpSessionRegistry ? activeMcpSessionRegistry.grantWatchThread(input) : Effect.void;
61588
+ const revokeActiveMcpWatchThread = (input) => activeMcpSessionRegistry ? activeMcpSessionRegistry.revokeWatchThread(input) : Effect.void;
60993
61589
  const revokeActiveMcpThread = (threadId) => activeMcpSessionRegistry ? activeMcpSessionRegistry.revokeThread(threadId) : Effect.void;
60994
61590
  const revokeAllActiveMcpCredentials = () => activeMcpSessionRegistry ? activeMcpSessionRegistry.revokeAll : Effect.void;
60995
61591
  //#endregion
@@ -61075,9 +61671,10 @@ const makeProviderService = Effect.fn("makeProviderService")(function* (options)
61075
61671
  const directory = yield* ProviderSessionDirectory;
61076
61672
  const runtimeEventPubSub = yield* PubSub.unbounded();
61077
61673
  const nowIso = Effect.map(DateTime.now, DateTime.formatIso);
61078
- const prepareMcpSession = (threadId, providerInstanceId) => issueActiveMcpCredential({
61674
+ const prepareMcpSession = (threadId, providerInstanceId, watchThreadIds) => issueActiveMcpCredential({
61079
61675
  threadId,
61080
- providerInstanceId
61676
+ providerInstanceId,
61677
+ ...watchThreadIds !== void 0 ? { watchThreadIds } : {}
61081
61678
  }).pipe(Effect.tap((credential) => credential ? Effect.sync(() => setMcpProviderSession(credential.config)) : Effect.void));
61082
61679
  const clearMcpSession = (threadId) => revokeActiveMcpThread(threadId).pipe(Effect.tap(() => Effect.sync(() => clearMcpProviderSession(threadId))));
61083
61680
  const publishRuntimeEvent = (event) => Effect.succeed(event).pipe(Effect.tap((canonicalEvent) => canonicalEventLogger ? canonicalEventLogger.write(canonicalEvent, canonicalEvent.threadId) : Effect.void), Effect.flatMap((canonicalEvent) => PubSub.publish(runtimeEventPubSub, canonicalEvent)), Effect.asVoid);
@@ -61222,7 +61819,7 @@ const makeProviderService = Effect.fn("makeProviderService")(function* (options)
61222
61819
  })));
61223
61820
  }), { discard: true });
61224
61821
  });
61225
- const startSession = Effect.fn("startSession")(function* (threadId, rawInput) {
61822
+ const startSession = Effect.fn("startSession")(function* (threadId, rawInput, options) {
61226
61823
  const parsed = yield* decodeInputOrValidationError({
61227
61824
  operation: "ProviderService.startSession",
61228
61825
  schema: ProviderSessionStartInput,
@@ -61258,7 +61855,7 @@ const makeProviderService = Effect.fn("makeProviderService")(function* (options)
61258
61855
  "provider.cwd.effective": effectiveCwd ?? ""
61259
61856
  });
61260
61857
  const adapter = yield* registry.getByInstance(resolvedInstanceId);
61261
- yield* prepareMcpSession(threadId, resolvedInstanceId);
61858
+ yield* prepareMcpSession(threadId, resolvedInstanceId, options?.watchThreadIds);
61262
61859
  const session = yield* adapter.startSession({
61263
61860
  ...input,
61264
61861
  providerInstanceId: resolvedInstanceId,
@@ -63764,6 +64361,14 @@ function isClaudeInterruptedCause(cause) {
63764
64361
  function resultErrorsText(result) {
63765
64362
  return "errors" in result && Array.isArray(result.errors) ? result.errors.join(" ").toLowerCase() : "";
63766
64363
  }
64364
+ const EDE_DIAGNOSTIC_PREFIX = "[ede_diagnostic]";
64365
+ const CLAUDE_PENDING_TOOL_FAILURE_MESSAGE = "Claude stopped while a tool call was still pending.";
64366
+ function resultErrorMessage(result) {
64367
+ if (result.subtype === "success") return;
64368
+ const userFacingError = result.errors.find((error) => !error.trimStart().startsWith(EDE_DIAGNOSTIC_PREFIX));
64369
+ if (userFacingError !== void 0) return userFacingError;
64370
+ return result.stop_reason === "tool_use" && result.errors.length > 0 ? CLAUDE_PENDING_TOOL_FAILURE_MESSAGE : void 0;
64371
+ }
63767
64372
  function isInterruptedResult(result) {
63768
64373
  const errors = resultErrorsText(result);
63769
64374
  if (errors.includes("interrupt")) return true;
@@ -65525,7 +66130,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
65525
66130
  const interruptRequested = context.interruptRequested;
65526
66131
  context.interruptRequested = false;
65527
66132
  const status = turnStatusFromResult(message, interruptRequested);
65528
- const errorMessage = message.subtype === "success" ? void 0 : message.errors[0];
66133
+ const errorMessage = resultErrorMessage(message);
65529
66134
  if (status === "failed") yield* emitRuntimeError(context, errorMessage ?? "Claude turn failed.");
65530
66135
  yield* completeTurn(context, status, errorMessage, message);
65531
66136
  yield* drainPendingTurns(context);
@@ -85025,7 +85630,7 @@ const makeTerminationError$1 = (handle) => Effect.match(handle.exitCode, {
85025
85630
  //#endregion
85026
85631
  //#region ../../packages/effect-codex-app-server/src/client.ts
85027
85632
  var CodexAppServerClient = class extends Context.Service()("effect-codex-app-server/client/CodexAppServerClient") {};
85028
- const make$7 = Effect.fn("effect-codex-app-server/CodexAppServerClient.make")(function* (stdio, options = {}, terminationError) {
85633
+ const make$8 = Effect.fn("effect-codex-app-server/CodexAppServerClient.make")(function* (stdio, options = {}, terminationError) {
85029
85634
  const requestHandlers = /* @__PURE__ */ new Map();
85030
85635
  const notificationHandlers = /* @__PURE__ */ new Map();
85031
85636
  let unknownRequestHandler;
@@ -85092,7 +85697,7 @@ const make$7 = Effect.fn("effect-codex-app-server/CodexAppServerClient.make")(fu
85092
85697
  const layerChildProcess$1 = (handle, options = {}) => Layer.effect(CodexAppServerClient, makeChildProcessClient(handle, options));
85093
85698
  const makeChildProcessClient = Effect.fn("effect-codex-app-server/CodexAppServerClient.makeChildProcessClient")(function* (handle, options) {
85094
85699
  yield* Stream.runDrain(handle.stderr).pipe(Effect.ignore, Effect.forkScoped);
85095
- return yield* make$7(makeChildStdio$1(handle), options, makeTerminationError$1(handle));
85700
+ return yield* make$8(makeChildStdio$1(handle), options, makeTerminationError$1(handle));
85096
85701
  });
85097
85702
  //#endregion
85098
85703
  //#region src/provider/Layers/CodexProvider.ts
@@ -91150,7 +91755,7 @@ const makeTerminationError = (handle) => Effect.match(handle.exitCode, {
91150
91755
  //#endregion
91151
91756
  //#region ../../packages/effect-acp/src/client.ts
91152
91757
  var AcpClient = class extends Context.Service()("effect-acp/client/AcpClient") {};
91153
- const make$6 = Effect.fn("effect-acp/AcpClient.make")(function* (stdio, options = {}, terminationError) {
91758
+ const make$7 = Effect.fn("effect-acp/AcpClient.make")(function* (stdio, options = {}, terminationError) {
91154
91759
  const coreHandlers = {};
91155
91760
  const notificationHandlers = {
91156
91761
  sessionUpdate: {
@@ -91308,7 +91913,7 @@ const make$6 = Effect.fn("effect-acp/AcpClient.make")(function* (stdio, options
91308
91913
  const layerChildProcess = (handle, options = {}) => {
91309
91914
  const stdio = makeChildStdio(handle);
91310
91915
  const terminationError = makeTerminationError(handle);
91311
- return Layer.effect(AcpClient, make$6(stdio, options, terminationError));
91916
+ return Layer.effect(AcpClient, make$7(stdio, options, terminationError));
91312
91917
  };
91313
91918
  //#endregion
91314
91919
  //#region ../../packages/shared/src/toolActivity.ts
@@ -91768,7 +92373,7 @@ function formatConfigOptionValue(value) {
91768
92373
  const defaultSessionLoadTimeout = Duration.seconds(90);
91769
92374
  const defaultSessionLoadReplayIdleGap = Duration.seconds(2);
91770
92375
  var AcpSessionRuntime = class extends Context.Service()("@p4code/cli/provider/acp/AcpSessionRuntime") {};
91771
- const make$5 = (options) => Effect.gen(function* () {
92376
+ const make$6 = (options) => Effect.gen(function* () {
91772
92377
  const crypto = yield* Crypto.Crypto;
91773
92378
  const spawner = yield* ChildProcessSpawner$1.ChildProcessSpawner;
91774
92379
  const runtimeScope = yield* Scope.Scope;
@@ -92077,7 +92682,7 @@ const make$5 = (options) => Effect.gen(function* () {
92077
92682
  notify: acp.raw.notify
92078
92683
  };
92079
92684
  });
92080
- const layer$2 = (options) => Layer.effect(AcpSessionRuntime, make$5(options));
92685
+ const layer$2 = (options) => Layer.effect(AcpSessionRuntime, make$6(options));
92081
92686
  function sessionConfigOptionsFromSetup(response) {
92082
92687
  return response?.configOptions ?? [];
92083
92688
  }
@@ -99464,7 +100069,7 @@ const stringField = (record, key) => {
99464
100069
  const value = record[key];
99465
100070
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
99466
100071
  };
99467
- const make$4 = Effect.gen(function* () {
100072
+ const make$5 = Effect.gen(function* () {
99468
100073
  const linear = yield* LinearClient;
99469
100074
  return { resolve: Effect.fn("TicketResolver.resolve")(function* (reference) {
99470
100075
  const identifier = parseTicketReference(reference);
@@ -99495,7 +100100,7 @@ const make$4 = Effect.gen(function* () {
99495
100100
  };
99496
100101
  }) };
99497
100102
  });
99498
- const layer$1 = Layer.effect(TicketResolver, make$4);
100103
+ const layer$1 = Layer.effect(TicketResolver, make$5);
99499
100104
  //#endregion
99500
100105
  //#region src/mcp/toolkits/tasks/tools.ts
99501
100106
  const dependencies = [McpInvocationContext, TaskRepository];
@@ -100601,17 +101206,22 @@ var ProviderRuntimeIngestionService = class extends Context.Service()("@p4code/c
100601
101206
  */
100602
101207
  var ThreadDeletionReactor = class extends Context.Service()("@p4code/cli/orchestration/Services/ThreadDeletionReactor") {};
100603
101208
  //#endregion
101209
+ //#region src/orchestration/Services/FusionWatcherReactor.ts
101210
+ var FusionWatcherReactor = class extends Context.Service()("@p4code/cli/orchestration/Services/FusionWatcherReactor") {};
101211
+ //#endregion
100604
101212
  //#region src/orchestration/Layers/OrchestrationReactor.ts
100605
101213
  const makeOrchestrationReactor = Effect.gen(function* () {
100606
101214
  const providerRuntimeIngestion = yield* ProviderRuntimeIngestionService;
100607
101215
  const providerCommandReactor = yield* ProviderCommandReactor;
100608
101216
  const checkpointReactor = yield* CheckpointReactor;
100609
101217
  const threadDeletionReactor = yield* ThreadDeletionReactor;
101218
+ const fusionWatcherReactor = yield* FusionWatcherReactor;
100610
101219
  return { start: Effect.fn("start")(function* () {
100611
101220
  yield* providerRuntimeIngestion.start();
100612
101221
  yield* providerCommandReactor.start();
100613
101222
  yield* checkpointReactor.start();
100614
101223
  yield* threadDeletionReactor.start();
101224
+ yield* fusionWatcherReactor.start();
100615
101225
  }) };
100616
101226
  });
100617
101227
  const OrchestrationReactorLive = Layer.effect(OrchestrationReactor, makeOrchestrationReactor);
@@ -101193,7 +101803,7 @@ function runtimeEventToActivities(event, taskTitle, compressMode) {
101193
101803
  }
101194
101804
  return [];
101195
101805
  }
101196
- const make$3 = Effect.gen(function* () {
101806
+ const make$4 = Effect.gen(function* () {
101197
101807
  const crypto = yield* Crypto.Crypto;
101198
101808
  const orchestrationEngine = yield* OrchestrationEngineService;
101199
101809
  const projectionSnapshotQuery = yield* ProjectionSnapshotQuery;
@@ -101696,6 +102306,14 @@ const make$3 = Effect.gen(function* () {
101696
102306
  updatedAt: now
101697
102307
  });
101698
102308
  }
102309
+ if (shouldApplyThreadLifecycle) yield* orchestrationEngine.dispatch({
102310
+ type: "thread.turn.complete",
102311
+ commandId: yield* providerCommandId(event, "thread-turn-complete"),
102312
+ threadId: thread.id,
102313
+ ...turnId ? { turnId } : {},
102314
+ state: normalizeRuntimeTurnState(event.payload.state),
102315
+ completedAt: now
102316
+ });
101699
102317
  }
101700
102318
  if (event.type === "session.exited") yield* clearTurnStateForSession(thread.id);
101701
102319
  if (event.type === "runtime.error") {
@@ -101794,7 +102412,7 @@ const make$3 = Effect.gen(function* () {
101794
102412
  drain: worker.drain
101795
102413
  };
101796
102414
  });
101797
- const ProviderRuntimeIngestionLive = Layer.effect(ProviderRuntimeIngestionService, make$3).pipe(Layer.provide(ProjectionTurnRepositoryLive));
102415
+ const ProviderRuntimeIngestionLive = Layer.effect(ProviderRuntimeIngestionService, make$4).pipe(Layer.provide(ProjectionTurnRepositoryLive));
101798
102416
  //#endregion
101799
102417
  //#region src/provider/userInvokedSkills.ts
101800
102418
  /**
@@ -101980,7 +102598,7 @@ function buildGeneratedWorktreeBranchName(raw) {
101980
102598
  const branchFragment = (normalized.startsWith(`p4code/`) ? normalized.slice(`${WORKTREE_BRANCH_PREFIX}/`.length) : normalized).replace(/[^a-z0-9/_-]+/g, "-").replace(/\/+/g, "/").replace(/-+/g, "-").replace(/^[./_-]+|[./_-]+$/g, "").slice(0, 64).replace(/[./_-]+$/g, "");
101981
102599
  return `${WORKTREE_BRANCH_PREFIX}/${branchFragment.length > 0 ? branchFragment : "update"}`;
101982
102600
  }
101983
- const make$2 = Effect.gen(function* () {
102601
+ const make$3 = Effect.gen(function* () {
101984
102602
  const crypto = yield* Crypto.Crypto;
101985
102603
  const orchestrationEngine = yield* OrchestrationEngineService;
101986
102604
  const projectionSnapshotQuery = yield* ProjectionSnapshotQuery;
@@ -102188,6 +102806,11 @@ const make$2 = Effect.gen(function* () {
102188
102806
  if (!thread) return yield* Effect.die(/* @__PURE__ */ new Error(`Thread '${threadId}' was not found in read model.`));
102189
102807
  const desiredRuntimeMode = thread.runtimeMode;
102190
102808
  const requestedModelSelection = options?.modelSelection;
102809
+ const watchThreadIds = ((yield* projectionSnapshotQuery.getCommandReadModel()).threadPairs ?? []).filter((pair) => pair.detachedAt === null && pair.watcherThreadId === threadId).map((pair) => pair.implementerThreadId);
102810
+ yield* Effect.forEach(watchThreadIds, (watchedThreadId) => grantActiveMcpWatchThread({
102811
+ watcherThreadId: threadId,
102812
+ watchedThreadId
102813
+ }), { discard: true });
102191
102814
  const resolveActiveSession = (threadId) => providerService.listSessions().pipe(Effect.map((sessions) => sessions.find((session) => session.threadId === threadId)));
102192
102815
  const activeSession = yield* resolveActiveSession(threadId);
102193
102816
  const activeThreadSession = thread.session !== null && thread.session.status !== "stopped" && activeSession ? thread.session : null;
@@ -102277,7 +102900,7 @@ const make$2 = Effect.gen(function* () {
102277
102900
  runtimeMode: desiredRuntimeMode,
102278
102901
  compressMode: thread.compressMode,
102279
102902
  unpromptedSubagents: thread.unpromptedSubagents
102280
- });
102903
+ }, watchThreadIds.length > 0 ? { watchThreadIds } : void 0);
102281
102904
  };
102282
102905
  const bindSessionToThread = (session) => Effect.gen(function* () {
102283
102906
  if (session.providerInstanceId === void 0) return yield* new ProviderAdapterRequestError({
@@ -102386,7 +103009,7 @@ const make$2 = Effect.gen(function* () {
102386
103009
  const modelForTurn = sessionModelSwitch === "unsupported" && input.modelSelection === void 0 ? activeSession?.model !== void 0 ? {
102387
103010
  ...requestedModelSelection,
102388
103011
  model: activeSession.model
102389
- } : requestedModelSelection : input.modelSelection;
103012
+ } : requestedModelSelection : requestedModelSelection;
102390
103013
  const compressMode = thread.compressMode;
102391
103014
  const hasSessionLevelRuleset = activeSession?.provider === "claudeAgent" || activeSession?.provider === "codex";
102392
103015
  const rebuildsRulesetEachTurn = activeSession?.provider === "codex";
@@ -102695,7 +103318,7 @@ const make$2 = Effect.gen(function* () {
102695
103318
  drain: worker.drain
102696
103319
  };
102697
103320
  });
102698
- const ProviderCommandReactorLive = Layer.effect(ProviderCommandReactor, make$2);
103321
+ const ProviderCommandReactorLive = Layer.effect(ProviderCommandReactor, make$3);
102699
103322
  //#endregion
102700
103323
  //#region src/checkpointing/Diffs.ts
102701
103324
  function parseTurnDiffFilesFromUnifiedDiff(diff) {
@@ -102725,7 +103348,7 @@ function checkpointStatusFromRuntime(status) {
102725
103348
  default: return "ready";
102726
103349
  }
102727
103350
  }
102728
- const make$1 = Effect.gen(function* () {
103351
+ const make$2 = Effect.gen(function* () {
102729
103352
  const randomUUID = (yield* Crypto.Crypto).randomUUIDv4;
102730
103353
  const serverEventId = randomUUID.pipe(Effect.map(EventId.make));
102731
103354
  const serverCommandId = (tag) => randomUUID.pipe(Effect.map((uuid) => CommandId.make(`server:${tag}:${uuid}`)));
@@ -103207,7 +103830,106 @@ const make$1 = Effect.gen(function* () {
103207
103830
  drain: worker.drain
103208
103831
  };
103209
103832
  });
103210
- const CheckpointReactorLive = Layer.effect(CheckpointReactor, make$1);
103833
+ const CheckpointReactorLive = Layer.effect(CheckpointReactor, make$2);
103834
+ //#endregion
103835
+ //#region src/orchestration/Layers/FusionWatcherReactor.ts
103836
+ const reviewCommandId = (pairId, sequence) => CommandId.make(`server:fusion:${pairId}:review:${sequence}`);
103837
+ const cursorCommandId = (pairId, sequence) => CommandId.make(`server:fusion:${pairId}:cursor:${sequence}`);
103838
+ const reviewMessageId = (pairId, sequence) => MessageId.make(`fusion-review:${pairId}:${sequence}`);
103839
+ const watcherPrompt = (input) => `${FUSION_REVIEW_PROMPT_PREFIX}
103840
+ Review implementer thread ${input.implementerThreadId} after its accepted turn completion.
103841
+
103842
+ Call thread_watch_events with threadId ${input.implementerThreadId} and afterSequence ${input.afterSequence}. Continue paging through sequence ${input.throughSequence}. Inspect repository state when useful.
103843
+
103844
+ Report only a concrete objection: correctness risk, missed requirement, regression, unsafe change, or unnecessary scope. Cite evidence. If no objection exists, return exactly ${FUSION_NO_OBJECTION_TEXT} and nothing else. This marker is hidden from the transcript.`;
103845
+ const make$1 = Effect.gen(function* () {
103846
+ const orchestrationEngine = yield* OrchestrationEngineService;
103847
+ const projectionSnapshotQuery = yield* ProjectionSnapshotQuery;
103848
+ const processReview = Effect.fn("FusionWatcherReactor.processReview")(function* (pair, completion) {
103849
+ const readModel = yield* projectionSnapshotQuery.getCommandReadModel();
103850
+ const currentPair = (readModel.threadPairs ?? []).find((candidate) => candidate.id === pair.id);
103851
+ if (currentPair === void 0 || currentPair.detachedAt !== null || completion.sequence <= currentPair.lastReviewedImplementerSequence) return;
103852
+ const watcher = readModel.threads.find((thread) => thread.id === currentPair.watcherThreadId && thread.deletedAt === null);
103853
+ if (watcher === void 0) return;
103854
+ yield* orchestrationEngine.dispatch({
103855
+ type: "thread.turn.start",
103856
+ commandId: reviewCommandId(currentPair.id, completion.sequence),
103857
+ threadId: watcher.id,
103858
+ message: {
103859
+ messageId: reviewMessageId(currentPair.id, completion.sequence),
103860
+ role: "user",
103861
+ text: watcherPrompt({
103862
+ implementerThreadId: currentPair.implementerThreadId,
103863
+ afterSequence: currentPair.lastReviewedImplementerSequence,
103864
+ throughSequence: completion.sequence
103865
+ }),
103866
+ attachments: []
103867
+ },
103868
+ runtimeMode: watcher.runtimeMode,
103869
+ interactionMode: watcher.interactionMode,
103870
+ compressMode: watcher.compressMode,
103871
+ unpromptedSubagents: watcher.unpromptedSubagents,
103872
+ createdAt: completion.occurredAt
103873
+ });
103874
+ yield* orchestrationEngine.dispatch({
103875
+ type: "thread-pair.cursor.advance",
103876
+ commandId: cursorCommandId(currentPair.id, completion.sequence),
103877
+ pairId: currentPair.id,
103878
+ implementerSequence: completion.sequence,
103879
+ advancedAt: completion.occurredAt
103880
+ });
103881
+ });
103882
+ const catchUpPair = Effect.fn("FusionWatcherReactor.catchUpPair")(function* (pair, throughSequence) {
103883
+ if (throughSequence <= pair.lastReviewedImplementerSequence) return;
103884
+ const completions = (yield* orchestrationEngine.readEvents(pair.lastReviewedImplementerSequence, throughSequence - pair.lastReviewedImplementerSequence).pipe(Stream.takeWhile((event) => event.sequence <= throughSequence), Stream.runCollect)).filter((event) => event.type === "thread.turn-completed" && event.payload.threadId === pair.implementerThreadId);
103885
+ for (const completion of completions) yield* processReview(pair, completion);
103886
+ });
103887
+ const processCompletion = Effect.fn("FusionWatcherReactor.processCompletion")(function* (event) {
103888
+ const pairs = ((yield* projectionSnapshotQuery.getCommandReadModel()).threadPairs ?? []).filter((pair) => pair.detachedAt === null && pair.implementerThreadId === event.payload.threadId && event.sequence > pair.lastReviewedImplementerSequence);
103889
+ yield* Effect.forEach(pairs, (pair) => catchUpPair(pair, event.sequence), {
103890
+ concurrency: 1,
103891
+ discard: true
103892
+ });
103893
+ });
103894
+ const processEvent = Effect.fn("FusionWatcherReactor.processEvent")(function* (event) {
103895
+ if (event.type === "thread.turn-completed") {
103896
+ yield* processCompletion(event);
103897
+ return;
103898
+ }
103899
+ if (event.type === "thread-pair.created") {
103900
+ yield* grantActiveMcpWatchThread({
103901
+ watcherThreadId: event.payload.watcherThreadId,
103902
+ watchedThreadId: event.payload.implementerThreadId
103903
+ });
103904
+ return;
103905
+ }
103906
+ const pair = ((yield* projectionSnapshotQuery.getCommandReadModel()).threadPairs ?? []).find((candidate) => candidate.id === event.payload.pairId);
103907
+ if (pair === void 0) return;
103908
+ yield* revokeActiveMcpWatchThread({
103909
+ watcherThreadId: pair.watcherThreadId,
103910
+ watchedThreadId: pair.implementerThreadId
103911
+ });
103912
+ });
103913
+ const processSafely = (event) => processEvent(event).pipe(Effect.catchCause((cause) => {
103914
+ if (Cause.hasInterruptsOnly(cause)) return Effect.failCause(cause);
103915
+ return Effect.logWarning("fusion watcher reactor failed to process completion", {
103916
+ eventType: event.type,
103917
+ sequence: event.sequence,
103918
+ cause: Cause.pretty(cause)
103919
+ });
103920
+ }));
103921
+ const worker = yield* makeDrainableWorker(processSafely);
103922
+ const enqueueEvent = (event) => event.type === "thread.turn-completed" || event.type === "thread-pair.created" || event.type === "thread-pair.detached" ? worker.enqueue(event) : Effect.void;
103923
+ return {
103924
+ start: Effect.fn("FusionWatcherReactor.start")(function* () {
103925
+ yield* Effect.forkScoped(Stream.runForEach(orchestrationEngine.streamDomainEvents, enqueueEvent));
103926
+ const headSequence = yield* orchestrationEngine.latestSequence;
103927
+ yield* Stream.runForEach(orchestrationEngine.readEvents(0, Math.max(1, headSequence)), enqueueEvent).pipe(Effect.catchCause((cause) => Effect.logWarning("fusion watcher reactor failed historical replay", { cause: Cause.pretty(cause) })));
103928
+ }),
103929
+ drain: worker.drain
103930
+ };
103931
+ });
103932
+ const FusionWatcherReactorLive = Layer.effect(FusionWatcherReactor, make$1);
103211
103933
  //#endregion
103212
103934
  //#region src/orchestration/Layers/ThreadDeletionReactor.ts
103213
103935
  const logCleanupCauseUnlessInterrupted = ({ effect, message, threadId }) => effect.pipe(Effect.catchCause((cause) => {
@@ -103969,7 +104691,7 @@ const PlatformServicesLive = Layer.unwrap(Effect.gen(function* () {
103969
104691
  return layer;
103970
104692
  }
103971
104693
  }));
103972
- const ReactorLayerLive = Layer.empty.pipe(Layer.provideMerge(OrchestrationReactorLive), Layer.provideMerge(ProviderRuntimeIngestionLive), Layer.provideMerge(ProviderCommandReactorLive), Layer.provideMerge(CheckpointReactorLive), Layer.provideMerge(ThreadDeletionReactorLive), Layer.provideMerge(RuntimeReceiptBusLive));
104694
+ 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));
103973
104695
  const ProviderSessionDirectoryLayerLive = ProviderSessionDirectoryLive.pipe(Layer.provide(layer$4));
103974
104696
  const ProviderLayerLive = ProviderServiceLive.pipe(Layer.provide(ProviderAdapterRegistryLive), Layer.provideMerge(ProviderSessionDirectoryLayerLive));
103975
104697
  const PersistenceLayerLive = Layer.empty.pipe(Layer.provideMerge(layerConfig));