@p4code/cli 0.2.22 → 0.2.23

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
@@ -126,7 +126,7 @@ const closeServer = (server) => {
126
126
  * NetService - Service tag for startup networking helpers.
127
127
  */
128
128
  var NetService = class extends Context.Service()("@p4code/shared/Net/NetService") {};
129
- const make$89 = () => {
129
+ const make$90 = () => {
130
130
  /**
131
131
  * Returns true when a TCP server can bind to {host, port}.
132
132
  * `EADDRNOTAVAIL` is treated as available so IPv6-absent hosts don't fail
@@ -235,10 +235,10 @@ const make$89 = () => {
235
235
  })
236
236
  };
237
237
  };
238
- const layer$80 = Layer.sync(NetService, make$89);
238
+ const layer$81 = Layer.sync(NetService, make$90);
239
239
  //#endregion
240
240
  //#region package.json
241
- var version = "0.2.22";
241
+ var version = "0.2.23";
242
242
  //#endregion
243
243
  //#region src/config.ts
244
244
  /**
@@ -259,8 +259,8 @@ var ServerConfig$1 = class extends Context.Service()("@p4code/cli/config/ServerC
259
259
  /** @deprecated Import and use `layerTest` from this module. */
260
260
  static layerTest = (cwd, baseDirOrPrefix) => layerTest$3(cwd, baseDirOrPrefix);
261
261
  };
262
- const make$88 = (config) => ServerConfig$1.of(config);
263
- const layer$79 = (config) => Layer.succeed(ServerConfig$1, make$88(config));
262
+ const make$89 = (config) => ServerConfig$1.of(config);
263
+ const layer$80 = (config) => Layer.succeed(ServerConfig$1, make$89(config));
264
264
  const deriveServerPaths = Effect.fn(function* (baseDir, devUrl, options = {}) {
265
265
  const { join } = yield* Path.Path;
266
266
  const stateDir = join(baseDir, devUrl !== void 0 && !options.baseDirIsExplicit ? "dev" : "userdata");
@@ -1329,6 +1329,9 @@ const ExecutionEnvironmentCapabilities = Schema$1.Struct({
1329
1329
  /** Server understands thread.snooze / thread.unsnooze commands. Same
1330
1330
  version-skew contract as threadSettlement. */
1331
1331
  threadSnooze: Schema$1.optionalKey(Schema$1.Boolean),
1332
+ /** Server understands thread.pin / thread.unpin / thread.pin.reorder
1333
+ commands. Same version-skew contract as threadSettlement. */
1334
+ threadPinning: Schema$1.optionalKey(Schema$1.Boolean),
1332
1335
  /** Server exposes the pull-request list, detail, activity, diff, and mutation APIs. Absent on
1333
1336
  servers from before the pull-request workspace shipped, so clients must not probe them. */
1334
1337
  pullRequests: Schema$1.optionalKey(Schema$1.Boolean),
@@ -1961,6 +1964,8 @@ const OrchestrationThread = Schema$1.Struct({
1961
1964
  settledAt: Schema$1.NullOr(IsoDateTime).pipe(Schema$1.withDecodingDefault(Effect.succeed(null))),
1962
1965
  snoozedUntil: Schema$1.optional(Schema$1.NullOr(IsoDateTime)),
1963
1966
  snoozedAt: Schema$1.optional(Schema$1.NullOr(IsoDateTime)),
1967
+ pinnedAt: Schema$1.optional(Schema$1.NullOr(IsoDateTime)),
1968
+ pinOrderKey: Schema$1.optional(Schema$1.NullOr(Schema$1.String)),
1964
1969
  deletedAt: Schema$1.NullOr(IsoDateTime),
1965
1970
  messages: Schema$1.Array(OrchestrationMessage),
1966
1971
  proposedPlans: Schema$1.Array(OrchestrationProposedPlan).pipe(Schema$1.withDecodingDefault(Effect.succeed([]))),
@@ -2058,11 +2063,19 @@ const OrchestrationThreadShell = Schema$1.Struct({
2058
2063
  settledAt: Schema$1.NullOr(IsoDateTime).pipe(Schema$1.withDecodingDefault(Effect.succeed(null))),
2059
2064
  snoozedUntil: Schema$1.optional(Schema$1.NullOr(IsoDateTime)),
2060
2065
  snoozedAt: Schema$1.optional(Schema$1.NullOr(IsoDateTime)),
2066
+ pinnedAt: Schema$1.optional(Schema$1.NullOr(IsoDateTime)),
2067
+ pinOrderKey: Schema$1.optional(Schema$1.NullOr(Schema$1.String)),
2061
2068
  session: Schema$1.NullOr(OrchestrationSession),
2062
2069
  latestUserMessageAt: Schema$1.NullOr(IsoDateTime),
2063
2070
  hasPendingApprovals: Schema$1.Boolean,
2064
2071
  hasPendingUserInput: Schema$1.Boolean,
2065
2072
  hasActionableProposedPlan: Schema$1.Boolean,
2073
+ /**
2074
+ * Native background work alive after the turn settles: "working" while
2075
+ * subagents/workflows run, "monitoring" when watch loops are the only
2076
+ * live work. Optional so old servers/clients interop; absent = none.
2077
+ */
2078
+ backgroundLiveness: Schema$1.optional(Schema$1.NullOr(Schema$1.Literals(["working", "monitoring"]))),
2066
2079
  hasBackgroundTasks: Schema$1.optional(Schema$1.Boolean),
2067
2080
  scheduledWakeAt: Schema$1.optional(Schema$1.NullOr(IsoDateTime))
2068
2081
  });
@@ -2227,6 +2240,23 @@ const ThreadUnsnoozeCommand = Schema$1.Struct({
2227
2240
  threadId: ThreadId,
2228
2241
  reason: Schema$1.Literal("user")
2229
2242
  });
2243
+ const ThreadPinCommand = Schema$1.Struct({
2244
+ type: Schema$1.Literal("thread.pin"),
2245
+ commandId: CommandId,
2246
+ threadId: ThreadId
2247
+ });
2248
+ const ThreadUnpinCommand = Schema$1.Struct({
2249
+ type: Schema$1.Literal("thread.unpin"),
2250
+ commandId: CommandId,
2251
+ threadId: ThreadId
2252
+ });
2253
+ /** Writes one key to one thread, so neighbours on other servers stay untouched. */
2254
+ const ThreadPinReorderCommand = Schema$1.Struct({
2255
+ type: Schema$1.Literal("thread.pin.reorder"),
2256
+ commandId: CommandId,
2257
+ threadId: ThreadId,
2258
+ pinOrderKey: Schema$1.NullOr(Schema$1.String)
2259
+ });
2230
2260
  const ThreadMetaUpdateCommand = Schema$1.Struct({
2231
2261
  type: Schema$1.Literal("thread.meta.update"),
2232
2262
  commandId: CommandId,
@@ -2411,6 +2441,9 @@ const DispatchableClientOrchestrationCommand = Schema$1.Union([
2411
2441
  ThreadUnsettleCommand,
2412
2442
  ThreadSnoozeCommand,
2413
2443
  ThreadUnsnoozeCommand,
2444
+ ThreadPinCommand,
2445
+ ThreadUnpinCommand,
2446
+ ThreadPinReorderCommand,
2414
2447
  ThreadMetaUpdateCommand,
2415
2448
  ThreadRuntimeModeSetCommand,
2416
2449
  ThreadInteractionModeSetCommand,
@@ -2438,6 +2471,9 @@ const ClientOrchestrationCommand = Schema$1.Union([
2438
2471
  ThreadUnsettleCommand,
2439
2472
  ThreadSnoozeCommand,
2440
2473
  ThreadUnsnoozeCommand,
2474
+ ThreadPinCommand,
2475
+ ThreadUnpinCommand,
2476
+ ThreadPinReorderCommand,
2441
2477
  ThreadMetaUpdateCommand,
2442
2478
  ThreadRuntimeModeSetCommand,
2443
2479
  ThreadInteractionModeSetCommand,
@@ -2582,6 +2618,9 @@ const OrchestrationEventType = Schema$1.Literals([
2582
2618
  "thread.unsettled",
2583
2619
  "thread.snoozed",
2584
2620
  "thread.unsnoozed",
2621
+ "thread.pinned",
2622
+ "thread.unpinned",
2623
+ "thread.pin-reordered",
2585
2624
  "thread.meta-updated",
2586
2625
  "thread.runtime-mode-set",
2587
2626
  "thread.interaction-mode-set",
@@ -2688,6 +2727,21 @@ const ThreadUnsnoozedPayload$1 = Schema$1.Struct({
2688
2727
  reason: Schema$1.Literals(["user", "activity"]),
2689
2728
  updatedAt: IsoDateTime
2690
2729
  });
2730
+ const ThreadPinnedPayload$1 = Schema$1.Struct({
2731
+ threadId: ThreadId,
2732
+ pinnedAt: IsoDateTime,
2733
+ pinOrderKey: Schema$1.optional(Schema$1.NullOr(Schema$1.String)),
2734
+ updatedAt: IsoDateTime
2735
+ });
2736
+ const ThreadUnpinnedPayload$1 = Schema$1.Struct({
2737
+ threadId: ThreadId,
2738
+ updatedAt: IsoDateTime
2739
+ });
2740
+ const ThreadPinReorderedPayload$1 = Schema$1.Struct({
2741
+ threadId: ThreadId,
2742
+ pinOrderKey: Schema$1.NullOr(Schema$1.String),
2743
+ updatedAt: IsoDateTime
2744
+ });
2691
2745
  const ThreadMetaUpdatedPayload$1 = Schema$1.Struct({
2692
2746
  threadId: ThreadId,
2693
2747
  title: Schema$1.optional(TrimmedNonEmptyString),
@@ -2933,6 +2987,21 @@ const OrchestrationEvent = Schema$1.Union([
2933
2987
  type: Schema$1.Literal("thread.unsnoozed"),
2934
2988
  payload: ThreadUnsnoozedPayload$1
2935
2989
  }),
2990
+ Schema$1.Struct({
2991
+ ...EventBaseFields,
2992
+ type: Schema$1.Literal("thread.pinned"),
2993
+ payload: ThreadPinnedPayload$1
2994
+ }),
2995
+ Schema$1.Struct({
2996
+ ...EventBaseFields,
2997
+ type: Schema$1.Literal("thread.unpinned"),
2998
+ payload: ThreadUnpinnedPayload$1
2999
+ }),
3000
+ Schema$1.Struct({
3001
+ ...EventBaseFields,
3002
+ type: Schema$1.Literal("thread.pin-reordered"),
3003
+ payload: ThreadPinReorderedPayload$1
3004
+ }),
2936
3005
  Schema$1.Struct({
2937
3006
  ...EventBaseFields,
2938
3007
  type: Schema$1.Literal("thread.meta-updated"),
@@ -6787,6 +6856,19 @@ const UserInputQuestion = Schema$1.Struct({
6787
6856
  });
6788
6857
  const UserInputRequestedPayload = Schema$1.Struct({ questions: Schema$1.Array(UserInputQuestion) });
6789
6858
  const UserInputResolvedPayload = Schema$1.Struct({ answers: UnknownRecordSchema });
6859
+ /**
6860
+ * Watch-loop task types: Monitor-tool tasks plus background shells (a shell
6861
+ * that outlives its turn is in practice a watch loop). Canonical single copy,
6862
+ * so the server liveness registry and any client fold classify identically.
6863
+ */
6864
+ const MONITOR_TASK_TYPES = /* @__PURE__ */ new Set([
6865
+ "monitor",
6866
+ "monitor_mcp",
6867
+ "local_bash",
6868
+ "shell"
6869
+ ]);
6870
+ /** Task types that are neither agents nor watch loops (plan-mode bookkeeping). */
6871
+ const INERT_TASK_TYPES = /* @__PURE__ */ new Set(["plan", "dream"]);
6790
6872
  const TaskStartedPayload = Schema$1.Struct({
6791
6873
  taskId: RuntimeTaskId,
6792
6874
  description: Schema$1.optional(TrimmedNonEmptyStringSchema$1),
@@ -7852,6 +7934,8 @@ const SidebarProjectSortOrder = Schema$1.Literals([
7852
7934
  const DEFAULT_SIDEBAR_PROJECT_SORT_ORDER = "updated_at";
7853
7935
  const SidebarThreadSortOrder = Schema$1.Literals(["updated_at", "created_at"]);
7854
7936
  const DEFAULT_SIDEBAR_THREAD_SORT_ORDER = "updated_at";
7937
+ const SidebarDisplayStyle = Schema$1.Literals(["project", "thread"]);
7938
+ const DEFAULT_SIDEBAR_DISPLAY_STYLE = "project";
7855
7939
  const SidebarProjectGroupingMode = Schema$1.Literals([
7856
7940
  "repository",
7857
7941
  "repository_path",
@@ -7894,6 +7978,7 @@ const ClientSettingsSchema = Schema$1.Struct({
7894
7978
  })).pipe(Schema$1.withDecodingDefault(Effect.succeed({}))),
7895
7979
  showBuildModeToggle: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(false))),
7896
7980
  sidebarAutoSettleAfterDays: Schema$1.NullOr(SidebarAutoSettleAfterDays).pipe(Schema$1.withDecodingDefault(Effect.succeed(3))),
7981
+ sidebarDisplayStyle: SidebarDisplayStyle.pipe(Schema$1.withDecodingDefault(Effect.succeed(DEFAULT_SIDEBAR_DISPLAY_STYLE))),
7897
7982
  sidebarProjectGroupingMode: SidebarProjectGroupingMode.pipe(Schema$1.withDecodingDefault(Effect.succeed(DEFAULT_SIDEBAR_PROJECT_GROUPING_MODE))),
7898
7983
  sidebarProjectGroupingOverrides: Schema$1.Record(TrimmedNonEmptyString, SidebarProjectGroupingMode).pipe(Schema$1.withDecodingDefault(Effect.succeed({}))),
7899
7984
  sidebarProjectSortOrder: SidebarProjectSortOrder.pipe(Schema$1.withDecodingDefault(Effect.succeed(DEFAULT_SIDEBAR_PROJECT_SORT_ORDER))),
@@ -8151,6 +8236,12 @@ const ServerSettings = Schema$1.Struct({
8151
8236
  instanceId: ProviderInstanceId.make("codex"),
8152
8237
  model: DEFAULT_TEXT_GENERATION_MODEL
8153
8238
  }))),
8239
+ /**
8240
+ * The supervisor model a Fusion pair starts with. Null until the user picks
8241
+ * one; kept in settings rather than per-draft state so the choice survives
8242
+ * the next thread instead of resetting to none each time.
8243
+ */
8244
+ fusionWatcherModelSelection: Schema$1.NullOr(ModelSelection).pipe(Schema$1.withDecodingDefault(Effect.succeed(null))),
8154
8245
  sourceControlWritingStyle: SourceControlWritingStyleSettings.pipe(Schema$1.withDecodingDefault(Effect.succeed({}))),
8155
8246
  sourceControlWriterModelSelection: Schema$1.NullOr(ModelSelection).pipe(Schema$1.withDecodingDefault(Effect.succeed(null))),
8156
8247
  /** Prefix used for tasks without a project or project-specific override. */
@@ -8288,6 +8379,7 @@ const ServerSettingsPatch = Schema$1.Struct({
8288
8379
  newWorktreesStartFromOrigin: Schema$1.optionalKey(Schema$1.Boolean),
8289
8380
  addProjectBaseDirectory: Schema$1.optionalKey(TrimmedString),
8290
8381
  textGenerationModelSelection: Schema$1.optionalKey(ModelSelectionPatch),
8382
+ fusionWatcherModelSelection: Schema$1.optionalKey(Schema$1.NullOr(ModelSelection)),
8291
8383
  sourceControlWritingStyle: Schema$1.optionalKey(Schema$1.Struct({
8292
8384
  mode: Schema$1.optionalKey(SourceControlWritingStyleMode),
8293
8385
  customInstructions: Schema$1.optionalKey(TrimmedString),
@@ -8331,6 +8423,7 @@ Schema$1.Struct({
8331
8423
  }))),
8332
8424
  showBuildModeToggle: Schema$1.optionalKey(Schema$1.Boolean),
8333
8425
  sidebarAutoSettleAfterDays: Schema$1.optionalKey(Schema$1.NullOr(SidebarAutoSettleAfterDays)),
8426
+ sidebarDisplayStyle: Schema$1.optionalKey(SidebarDisplayStyle),
8334
8427
  sidebarProjectGroupingMode: Schema$1.optionalKey(SidebarProjectGroupingMode),
8335
8428
  sidebarProjectGroupingOverrides: Schema$1.optionalKey(Schema$1.Record(TrimmedNonEmptyString, SidebarProjectGroupingMode)),
8336
8429
  sidebarProjectSortOrder: Schema$1.optionalKey(SidebarProjectSortOrder),
@@ -9404,7 +9497,17 @@ const TrackerStatus = Schema$1.Struct({
9404
9497
  * Null in `mcp` and `none` modes, where p4code holds no credential to ask
9405
9498
  * with.
9406
9499
  */
9407
- workspace: Schema$1.NullOr(Schema$1.String)
9500
+ workspace: Schema$1.NullOr(Schema$1.String),
9501
+ /**
9502
+ * The stored key with its middle replaced by bullets, for the settings row
9503
+ * to display. Null in `mcp` and `none` modes.
9504
+ *
9505
+ * The full key is never sent back out. This exists so the panel can show
9506
+ * that a key is present, and which one, without the reader being able to
9507
+ * reconstruct it: the mask is a fixed width, so it does not disclose the
9508
+ * key's length either.
9509
+ */
9510
+ maskedKey: Schema$1.NullOr(Schema$1.String)
9408
9511
  });
9409
9512
  const TrackerProviderInput = Schema$1.Struct({ provider: TrackerProviderName });
9410
9513
  const TrackerApiKeyInput = Schema$1.Struct({
@@ -12098,7 +12201,7 @@ function deriveAuthClientMetadata(input) {
12098
12201
  //#endregion
12099
12202
  //#region src/auth/EnvironmentAuthPolicy.ts
12100
12203
  var EnvironmentAuthPolicy = class extends Context.Service()("@p4code/cli/auth/EnvironmentAuthPolicy") {};
12101
- const make$87 = Effect.gen(function* () {
12204
+ const make$88 = Effect.gen(function* () {
12102
12205
  const config = yield* ServerConfig$1;
12103
12206
  const isRemoteReachable = isRemoteReachableHost(config.host);
12104
12207
  const policy = config.mode === "desktop" ? isRemoteReachable ? "remote-reachable" : "desktop-managed-local" : isRemoteReachable ? "remote-reachable" : "loopback-browser";
@@ -12116,7 +12219,7 @@ const make$87 = Effect.gen(function* () {
12116
12219
  };
12117
12220
  return EnvironmentAuthPolicy.of({ getDescriptor: () => Effect.succeed(descriptor).pipe(Effect.withSpan("EnvironmentAuthPolicy.getDescriptor")) });
12118
12221
  });
12119
- const layer$78 = Layer.effect(EnvironmentAuthPolicy, make$87);
12222
+ const layer$79 = Layer.effect(EnvironmentAuthPolicy, make$88);
12120
12223
  //#endregion
12121
12224
  //#region src/persistence/Errors.ts
12122
12225
  function summarizeSchemaIssue(issue) {
@@ -12297,7 +12400,7 @@ function toPersistenceSqlOrDecodeError$6(sqlOperation, decodeOperation, correlat
12297
12400
  cause
12298
12401
  });
12299
12402
  }
12300
- const make$86 = Effect.gen(function* () {
12403
+ const make$87 = Effect.gen(function* () {
12301
12404
  const sql = yield* SqlClient.SqlClient;
12302
12405
  const createSessionRow = SqlSchema.void({
12303
12406
  Request: CreateAuthSessionInput,
@@ -12431,7 +12534,7 @@ const make$86 = Effect.gen(function* () {
12431
12534
  setLastConnectedAt
12432
12535
  };
12433
12536
  });
12434
- const layer$77 = Layer.effect(AuthSessionRepository, make$86);
12537
+ const layer$78 = Layer.effect(AuthSessionRepository, make$87);
12435
12538
  //#endregion
12436
12539
  //#region src/auth/ServerSecretStore.ts
12437
12540
  const secretStoreErrorContext = {
@@ -12498,7 +12601,7 @@ const isSecretStoreError = Schema$1.is(SecretStoreError);
12498
12601
  const isPlatformError = (value) => Predicate.isTagged(value, "PlatformError");
12499
12602
  const isSecretAlreadyExistsError = (error) => "cause" in error && isPlatformError(error.cause) && error.cause.reason._tag === "AlreadyExists";
12500
12603
  var ServerSecretStore = class extends Context.Service()("@p4code/cli/auth/ServerSecretStore") {};
12501
- const make$85 = Effect.gen(function* () {
12604
+ const make$86 = Effect.gen(function* () {
12502
12605
  const crypto = yield* Crypto.Crypto;
12503
12606
  const fileSystem = yield* FileSystem.FileSystem;
12504
12607
  const path = yield* Path.Path;
@@ -12568,7 +12671,7 @@ const make$85 = Effect.gen(function* () {
12568
12671
  remove
12569
12672
  });
12570
12673
  });
12571
- const layer$76 = Layer.effect(ServerSecretStore, make$85);
12674
+ const layer$77 = Layer.effect(ServerSecretStore, make$86);
12572
12675
  //#endregion
12573
12676
  //#region src/auth/SessionStore.ts
12574
12677
  var MalformedSessionTokenError = class extends Schema$1.TaggedErrorClass()("MalformedSessionTokenError", {}) {
@@ -12806,7 +12909,7 @@ function toAuthClientSession(input) {
12806
12909
  current: false
12807
12910
  };
12808
12911
  }
12809
- const make$84 = Effect.gen(function* () {
12912
+ const make$85 = Effect.gen(function* () {
12810
12913
  const crypto = yield* Crypto.Crypto;
12811
12914
  const serverConfig = yield* ServerConfig$1;
12812
12915
  const secretStore = yield* ServerSecretStore;
@@ -13120,7 +13223,7 @@ const make$84 = Effect.gen(function* () {
13120
13223
  markDisconnected
13121
13224
  });
13122
13225
  });
13123
- const layer$75 = Layer.effect(SessionStore, make$84).pipe(Layer.provideMerge(layer$77));
13226
+ const layer$76 = Layer.effect(SessionStore, make$85).pipe(Layer.provideMerge(layer$78));
13124
13227
  //#endregion
13125
13228
  //#region src/persistence/AuthPairingLinks.ts
13126
13229
  const AuthPairingLinkRecord = Schema$1.Struct({
@@ -13181,7 +13284,7 @@ function toPersistenceSqlOrDecodeError$5(sqlOperation, decodeOperation, correlat
13181
13284
  cause
13182
13285
  });
13183
13286
  }
13184
- const make$83 = Effect.gen(function* () {
13287
+ const make$84 = Effect.gen(function* () {
13185
13288
  const sql = yield* SqlClient.SqlClient;
13186
13289
  const createPairingLinkRow = SqlSchema.void({
13187
13290
  Request: CreateAuthPairingLinkInput,
@@ -13316,7 +13419,7 @@ const make$83 = Effect.gen(function* () {
13316
13419
  getByCredential
13317
13420
  };
13318
13421
  });
13319
- const layer$74 = Layer.effect(AuthPairingLinkRepository, make$83);
13422
+ const layer$75 = Layer.effect(AuthPairingLinkRepository, make$84);
13320
13423
  //#endregion
13321
13424
  //#region src/auth/PairingGrantStore.ts
13322
13425
  var UnknownBootstrapCredentialError = class extends Schema$1.TaggedErrorClass()("UnknownBootstrapCredentialError", {}) {
@@ -13411,7 +13514,7 @@ const DEV_STARTUP_TTL_HOURS = Duration.hours(24);
13411
13514
  const PAIRING_TOKEN_ALPHABET = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
13412
13515
  const PAIRING_TOKEN_LENGTH = 12;
13413
13516
  const PAIRING_TOKEN_REJECTION_LIMIT = Math.floor(256 / 32) * 32;
13414
- const make$82 = Effect.gen(function* () {
13517
+ const make$83 = Effect.gen(function* () {
13415
13518
  const crypto = yield* Crypto.Crypto;
13416
13519
  const config = yield* ServerConfig$1;
13417
13520
  const pairingLinks = yield* AuthPairingLinkRepository;
@@ -13609,7 +13712,7 @@ const make$82 = Effect.gen(function* () {
13609
13712
  consume
13610
13713
  });
13611
13714
  });
13612
- const layer$73 = Layer.effect(PairingGrantStore, make$82).pipe(Layer.provideMerge(layer$74));
13715
+ const layer$74 = Layer.effect(PairingGrantStore, make$83).pipe(Layer.provideMerge(layer$75));
13613
13716
  //#endregion
13614
13717
  //#region src/persistence/DatabaseSnapshot.ts
13615
13718
  /**
@@ -15450,6 +15553,27 @@ var _047_ThreadPairGates_default = Effect.gen(function* () {
15450
15553
  `;
15451
15554
  });
15452
15555
  //#endregion
15556
+ //#region src/persistence/Migrations/048_ProjectionThreadsPinned.ts
15557
+ /**
15558
+ * Server-side thread pinning. `pinned_at` holds the pin itself; `pin_order_key`
15559
+ * is the fractional index that orders the pinned block, written one key at a
15560
+ * time so a reorder never touches threads living on other servers.
15561
+ *
15562
+ * Both are nullable with no default: an unpinned thread is the absence of a
15563
+ * pin, and every existing thread starts unpinned.
15564
+ */
15565
+ var _048_ProjectionThreadsPinned_default = Effect.gen(function* () {
15566
+ const sql = yield* SqlClient.SqlClient;
15567
+ yield* sql`
15568
+ ALTER TABLE projection_threads
15569
+ ADD COLUMN pinned_at TEXT
15570
+ `;
15571
+ yield* sql`
15572
+ ALTER TABLE projection_threads
15573
+ ADD COLUMN pin_order_key TEXT
15574
+ `;
15575
+ });
15576
+ //#endregion
15453
15577
  //#region src/persistence/Migrations.ts
15454
15578
  /**
15455
15579
  * MigrationsLive - Migration runner with inline loader
@@ -15705,6 +15829,11 @@ const migrationEntries = [
15705
15829
  47,
15706
15830
  "ThreadPairGates",
15707
15831
  _047_ThreadPairGates_default
15832
+ ],
15833
+ [
15834
+ 48,
15835
+ "ProjectionThreadsPinned",
15836
+ _048_ProjectionThreadsPinned_default
15708
15837
  ]
15709
15838
  ];
15710
15839
  const makeMigrationLoader = (throughId) => Migrator.fromRecord(Object.fromEntries(migrationEntries.filter(([id]) => throughId === void 0 || id <= throughId).map(([id, name, migration]) => [`${id}_${name}`, migration])));
@@ -15955,7 +16084,7 @@ function parseBearerToken(request) {
15955
16084
  const token = header.slice(7).trim();
15956
16085
  return token.length > 0 ? token : null;
15957
16086
  }
15958
- const make$81 = Effect.gen(function* () {
16087
+ const make$82 = Effect.gen(function* () {
15959
16088
  const policy = yield* EnvironmentAuthPolicy;
15960
16089
  const bootstrapCredentials = yield* PairingGrantStore;
15961
16090
  const sessions = yield* SessionStore;
@@ -16150,9 +16279,9 @@ const make$81 = Effect.gen(function* () {
16150
16279
  issueStartupPairingUrl
16151
16280
  });
16152
16281
  });
16153
- const layer$72 = Layer.effect(EnvironmentAuth, make$81).pipe(Layer.provideMerge(layer$73), Layer.provideMerge(layer$75), Layer.provideMerge(layer$78));
16154
- const storageLayer = Layer.mergeAll(layer$76, layerConfig);
16155
- const runtimeLayer = layer$72.pipe(Layer.provideMerge(storageLayer));
16282
+ const layer$73 = Layer.effect(EnvironmentAuth, make$82).pipe(Layer.provideMerge(layer$74), Layer.provideMerge(layer$76), Layer.provideMerge(layer$79));
16283
+ const storageLayer = Layer.mergeAll(layer$77, layerConfig);
16284
+ const runtimeLayer = layer$73.pipe(Layer.provideMerge(storageLayer));
16156
16285
  //#endregion
16157
16286
  //#region src/cliAuthFormat.ts
16158
16287
  const newline = "\n";
@@ -16907,7 +17036,7 @@ const runWithEnvironmentAuth = (flags, run, options) => Effect.gen(function* ()
16907
17036
  }
16908
17037
  return yield* Effect.gen(function* () {
16909
17038
  return yield* run(yield* EnvironmentAuth);
16910
- }).pipe(Effect.provide(Layer.mergeAll(runtimeLayer).pipe(Layer.provide(layer$79(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)))));
17039
+ }).pipe(Effect.provide(Layer.mergeAll(runtimeLayer).pipe(Layer.provide(layer$80(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)))));
16911
17040
  });
16912
17041
  const ttlFlag = Flag.string("ttl").pipe(Flag.withSchema(DurationFromString), Flag.withDescription("TTL, for example `5m`, `1h`, `30d`, or `15 minutes`."), Flag.optional);
16913
17042
  const jsonFlag = Flag.boolean("json").pipe(Flag.withDescription("Emit JSON instead of human-readable output."), Flag.withDefault(false));
@@ -17047,7 +17176,7 @@ const DEFAULT_LIMITS = {
17047
17176
  windowMillis: FAILURE_WINDOW_MS,
17048
17177
  blockMillis: BLOCK_DURATION_MS
17049
17178
  };
17050
- const make$80 = Effect.fn("HubAuthThrottle.make")(function* (limits = DEFAULT_LIMITS) {
17179
+ const make$81 = Effect.fn("HubAuthThrottle.make")(function* (limits = DEFAULT_LIMITS) {
17051
17180
  const state = yield* Ref.make(initialThrottleState);
17052
17181
  return HubAuthThrottle.of({
17053
17182
  shouldRefuse: Effect.gen(function* () {
@@ -17061,7 +17190,7 @@ const make$80 = Effect.fn("HubAuthThrottle.make")(function* (limits = DEFAULT_LI
17061
17190
  })
17062
17191
  });
17063
17192
  });
17064
- const layer$71 = Layer.effect(HubAuthThrottle, make$80());
17193
+ const layer$72 = Layer.effect(HubAuthThrottle, make$81());
17065
17194
  //#endregion
17066
17195
  //#region src/hub/HubAuth.ts
17067
17196
  /**
@@ -18120,7 +18249,7 @@ const releaseFeedLeaseRoute = HttpRouter.add("DELETE", "/feed/lease", respondToH
18120
18249
  yield* (yield* SqlClient.SqlClient)`DELETE FROM feed_refresh_lease WHERE lease_key='refresh' AND owner=${body.owner}`;
18121
18250
  return HttpServerResponse.empty({ status: 204 });
18122
18251
  })));
18123
- const layer$70 = Layer.mergeAll(healthRoute, listTasksRoute, getTaskRoute, createTaskRoute, putTaskRoute, updateTaskRoute, deleteTaskRoute, listAssetsRoute, getAssetRoute, putAssetRoute, deleteAssetRoute, listFeedSourcesRoute, upsertFeedSourceRoute, deleteFeedSourceRoute, listFeedRoute, putFeedArticleRoute, putFeedEngagementRoute, markFeedReadRoute, cleanupFeedRoute, setFeedRefreshedAtRoute, acquireFeedLeaseRoute, renewFeedLeaseRoute, releaseFeedLeaseRoute);
18252
+ const layer$71 = Layer.mergeAll(healthRoute, listTasksRoute, getTaskRoute, createTaskRoute, putTaskRoute, updateTaskRoute, deleteTaskRoute, listAssetsRoute, getAssetRoute, putAssetRoute, deleteAssetRoute, listFeedSourcesRoute, upsertFeedSourceRoute, deleteFeedSourceRoute, listFeedRoute, putFeedArticleRoute, putFeedEngagementRoute, markFeedReadRoute, cleanupFeedRoute, setFeedRefreshedAtRoute, acquireFeedLeaseRoute, renewFeedLeaseRoute, releaseFeedLeaseRoute);
18124
18253
  //#endregion
18125
18254
  //#region src/hub/Migrations/001_Tasks.ts
18126
18255
  /**
@@ -18635,7 +18764,7 @@ const announce = Layer.effectDiscard(Effect.gen(function* () {
18635
18764
  });
18636
18765
  if (isRemoteReachableHost(config.host)) yield* Effect.logWarning("Hub is reachable beyond loopback. The bearer token is the only control — repeated auth failures are not rate limited.", { host: config.host });
18637
18766
  }));
18638
- const makeHubLayer = HttpRouter.serve(layer$70).pipe(Layer.provideMerge(announce), Layer.provideMerge(HubHttpServerLive), Layer.provideMerge(layer$71), Layer.provideMerge(TaskRepositoryLive), Layer.provideMerge(AgentAssetRepositoryLive), Layer.provideMerge(HubPersistenceLive));
18767
+ const makeHubLayer = HttpRouter.serve(layer$71).pipe(Layer.provideMerge(announce), Layer.provideMerge(HubHttpServerLive), Layer.provideMerge(layer$72), Layer.provideMerge(TaskRepositoryLive), Layer.provideMerge(AgentAssetRepositoryLive), Layer.provideMerge(HubPersistenceLive));
18639
18768
  const runHub = Layer.launch(makeHubLayer);
18640
18769
  //#endregion
18641
18770
  //#region src/atomicWrite.ts
@@ -18759,7 +18888,7 @@ function stripDefaultServerSettings(current, defaults) {
18759
18888
  }
18760
18889
  return Object.is(current, defaults) ? void 0 : current;
18761
18890
  }
18762
- const make$79 = Effect.gen(function* () {
18891
+ const make$80 = Effect.gen(function* () {
18763
18892
  const { settingsPath } = yield* ServerConfig$1;
18764
18893
  const fs = yield* FileSystem.FileSystem;
18765
18894
  const pathService = yield* Path.Path;
@@ -18980,7 +19109,7 @@ const make$79 = Effect.gen(function* () {
18980
19109
  }
18981
19110
  };
18982
19111
  });
18983
- const layer$69 = Layer.effect(ServerSettingsService, make$79);
19112
+ const layer$70 = Layer.effect(ServerSettingsService, make$80);
18984
19113
  //#endregion
18985
19114
  //#region src/pathExpansion.ts
18986
19115
  /**
@@ -19354,7 +19483,7 @@ function claudeEntryFromRegistration(registration) {
19354
19483
  };
19355
19484
  }
19356
19485
  var ClaudeMcpFiles = class extends Context.Service()("@p4code/cli/mcp/ClaudeMcpFiles") {};
19357
- const make$78 = Effect.gen(function* () {
19486
+ const make$79 = Effect.gen(function* () {
19358
19487
  const fileSystem = yield* FileSystem.FileSystem;
19359
19488
  const path = yield* Path.Path;
19360
19489
  const services = yield* Effect.context();
@@ -19419,7 +19548,7 @@ const make$78 = Effect.gen(function* () {
19419
19548
  removeProject: (projectDir, name) => removeAt(Effect.succeed(projectFile(projectDir)))(name)
19420
19549
  };
19421
19550
  });
19422
- const layer$68 = Layer.effect(ClaudeMcpFiles, make$78);
19551
+ const layer$69 = Layer.effect(ClaudeMcpFiles, make$79);
19423
19552
  Layer.succeed(ClaudeMcpFiles, {
19424
19553
  readUser: Effect.succeed([]),
19425
19554
  upsertUser: () => Effect.fail(new McpRegistryError({ detail: "No Claude config in tests." })),
@@ -19555,7 +19684,7 @@ const decodeClientRegistration = Schema$1.decodeUnknownExit(ClientRegistrationRe
19555
19684
  const decodeTokenResponse = Schema$1.decodeUnknownExit(TokenResponse);
19556
19685
  var McpOAuth = class extends Context.Service()("@p4code/cli/mcp/McpOAuth") {};
19557
19686
  const registryError = (detail) => new McpRegistryError({ detail });
19558
- const make$77 = Effect.gen(function* () {
19687
+ const make$78 = Effect.gen(function* () {
19559
19688
  const config = yield* ServerConfig$1;
19560
19689
  const secrets = yield* ServerSecretStore;
19561
19690
  const http = yield* HttpClient.HttpClient;
@@ -19875,7 +20004,7 @@ const make$77 = Effect.gen(function* () {
19875
20004
  accessTokenFor
19876
20005
  };
19877
20006
  });
19878
- const layer$67 = Layer.effect(McpOAuth, make$77);
20007
+ const layer$68 = Layer.effect(McpOAuth, make$78);
19879
20008
  Layer.succeed(McpOAuth, {
19880
20009
  statusFor: () => Effect.succeed(Option.none()),
19881
20010
  begin: () => Effect.fail(new McpRegistryError({ detail: "OAuth sign-in is not available." })),
@@ -19893,7 +20022,7 @@ const decodeRegistration$1 = Schema$1.decodeUnknownExit(RegistrationFromJson$1);
19893
20022
  const encodeRegistration = Schema$1.encodeSync(RegistrationFromJson$1);
19894
20023
  var McpRegistry = class extends Context.Service()("@p4code/cli/mcp/McpRegistry") {};
19895
20024
  const slotsOf = (registration) => registration.secrets ?? [];
19896
- const make$76 = Effect.gen(function* () {
20025
+ const make$77 = Effect.gen(function* () {
19897
20026
  const config = yield* ServerConfig$1;
19898
20027
  const secrets = yield* ServerSecretStore;
19899
20028
  const oauth = yield* McpOAuth;
@@ -20044,7 +20173,7 @@ const make$76 = Effect.gen(function* () {
20044
20173
  }).pipe(Effect.provide(services), Effect.catchCause((cause) => Effect.logWarning("mcp registry resolve failed", { cause }).pipe(Effect.as({}))))
20045
20174
  };
20046
20175
  });
20047
- const layer$66 = Layer.effect(McpRegistry, make$76);
20176
+ const layer$67 = Layer.effect(McpRegistry, make$77);
20048
20177
  //#endregion
20049
20178
  //#region src/sync/skillDirectory.ts
20050
20179
  /**
@@ -20423,7 +20552,7 @@ const formatHubLink = (input) => encodeStoredHubLink({
20423
20552
  shareMode: input.shareMode
20424
20553
  });
20425
20554
  const fromEnvironment = (environment) => validateHubLink(environment.P4CODE_HUB_URL ?? "", environment.P4CODE_HUB_TOKEN ?? "");
20426
- const make$75 = Effect.fn("HubLink.make")(function* (environment) {
20555
+ const make$76 = Effect.fn("HubLink.make")(function* (environment) {
20427
20556
  const secrets = yield* ServerSecretStore;
20428
20557
  const env = environment ?? process.env;
20429
20558
  const fromEnv = fromEnvironment(env);
@@ -20489,7 +20618,7 @@ const make$75 = Effect.fn("HubLink.make")(function* (environment) {
20489
20618
  })
20490
20619
  };
20491
20620
  });
20492
- const layer$65 = Layer.effect(HubLink, make$75());
20621
+ const layer$66 = Layer.effect(HubLink, make$76());
20493
20622
  //#endregion
20494
20623
  //#region src/sync/HubAssetClient.ts
20495
20624
  /**
@@ -20522,7 +20651,7 @@ const decodeAssetListPage = Schema$1.decodeUnknownEffect(AssetListPage);
20522
20651
  const decodeConflictBody$1 = Schema$1.decodeUnknownEffect(ConflictBody$1);
20523
20652
  const decodeAsset = Schema$1.decodeUnknownEffect(AgentAsset);
20524
20653
  var HubAssetClient = class extends Context.Service()("@p4code/cli/sync/HubAssetClient") {};
20525
- const make$74 = Effect.gen(function* () {
20654
+ const make$75 = Effect.gen(function* () {
20526
20655
  const http = yield* HttpClient.HttpClient;
20527
20656
  const link = yield* HubLink;
20528
20657
  const requireSettings = Effect.gen(function* () {
@@ -20604,7 +20733,7 @@ const make$74 = Effect.gen(function* () {
20604
20733
  remove
20605
20734
  };
20606
20735
  });
20607
- const layer$64 = Layer.effect(HubAssetClient, make$74);
20736
+ const layer$65 = Layer.effect(HubAssetClient, make$75);
20608
20737
  //#endregion
20609
20738
  //#region src/sync/mcpRegistrationFiles.ts
20610
20739
  /**
@@ -21179,7 +21308,7 @@ const EMPTY_REPORT = {
21179
21308
  unavailable: null
21180
21309
  };
21181
21310
  var AssetSync = class extends Context.Service()("@p4code/cli/sync/AssetSync") {};
21182
- const make$73 = Effect.gen(function* () {
21311
+ const make$74 = Effect.gen(function* () {
21183
21312
  const client = yield* HubAssetClient;
21184
21313
  const link = yield* HubLink;
21185
21314
  const settingsStore = yield* ServerSettingsService;
@@ -21892,7 +22021,7 @@ const make$73 = Effect.gen(function* () {
21892
22021
  removeLocal
21893
22022
  };
21894
22023
  });
21895
- const layer$63 = Layer.effect(AssetSync, make$73);
22024
+ const layer$64 = Layer.effect(AssetSync, make$74);
21896
22025
  //#endregion
21897
22026
  //#region src/provider/CompressPrompts.ts
21898
22027
  /**
@@ -22266,7 +22395,7 @@ const findAgentPreset = (requested) => {
22266
22395
  };
22267
22396
  //#endregion
22268
22397
  //#region src/cli/assets.ts
22269
- const SyncServicesLive = layer$63.pipe(Layer.provideMerge(layer$64), Layer.provideMerge(layer$65), Layer.provideMerge(layer$69.pipe(Layer.provide(layer$76))), Layer.provideMerge(layer$76), Layer.provideMerge(FetchHttpClient.layer));
22398
+ const SyncServicesLive = layer$64.pipe(Layer.provideMerge(layer$65), Layer.provideMerge(layer$66), Layer.provideMerge(layer$70.pipe(Layer.provide(layer$77))), Layer.provideMerge(layer$77), Layer.provideMerge(FetchHttpClient.layer));
22270
22399
  const runWithSync = (flags, run) => Effect.gen(function* () {
22271
22400
  const config = yield* resolveCliAuthConfig(flags, yield* GlobalFlag.LogLevel);
22272
22401
  return yield* Effect.gen(function* () {
@@ -22274,7 +22403,7 @@ const runWithSync = (flags, run) => Effect.gen(function* () {
22274
22403
  sync: yield* AssetSync,
22275
22404
  link: yield* HubLink
22276
22405
  });
22277
- }).pipe(Effect.provide(SyncServicesLive.pipe(Layer.provide(layer$79(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, "Error")))));
22406
+ }).pipe(Effect.provide(SyncServicesLive.pipe(Layer.provide(layer$80(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, "Error")))));
22278
22407
  });
22279
22408
  const STATE_LABEL = {
22280
22409
  synced: "synced",
@@ -23305,6 +23434,9 @@ const ThreadUnpromptedSubagentsSetPayload = ThreadUnpromptedSubagentsSetPayload$
23305
23434
  const ThreadDeletedPayload = ThreadDeletedPayload$1;
23306
23435
  const ThreadUnarchivedPayload = ThreadUnarchivedPayload$1;
23307
23436
  const ThreadUnsettledPayload = ThreadUnsettledPayload$1;
23437
+ const ThreadPinnedPayload = ThreadPinnedPayload$1;
23438
+ const ThreadPinReorderedPayload = ThreadPinReorderedPayload$1;
23439
+ const ThreadUnpinnedPayload = ThreadUnpinnedPayload$1;
23308
23440
  const ThreadSnoozedPayload = ThreadSnoozedPayload$1;
23309
23441
  const ThreadUnsnoozedPayload = ThreadUnsnoozedPayload$1;
23310
23442
  const MessageSentPayloadSchema = ThreadMessageSentPayload;
@@ -23574,6 +23706,29 @@ function projectEvent(model, event) {
23574
23706
  updatedAt: payload.updatedAt
23575
23707
  })
23576
23708
  })));
23709
+ case "thread.pinned": return decodeForEvent(ThreadPinnedPayload, event.payload, event.type, "payload").pipe(Effect.map((payload) => ({
23710
+ ...nextBase,
23711
+ threads: updateThread(nextBase.threads, payload.threadId, {
23712
+ pinnedAt: payload.pinnedAt,
23713
+ ...payload.pinOrderKey === void 0 ? {} : { pinOrderKey: payload.pinOrderKey },
23714
+ updatedAt: payload.updatedAt
23715
+ })
23716
+ })));
23717
+ case "thread.unpinned": return decodeForEvent(ThreadUnpinnedPayload, event.payload, event.type, "payload").pipe(Effect.map((payload) => ({
23718
+ ...nextBase,
23719
+ threads: updateThread(nextBase.threads, payload.threadId, {
23720
+ pinnedAt: null,
23721
+ pinOrderKey: null,
23722
+ updatedAt: payload.updatedAt
23723
+ })
23724
+ })));
23725
+ case "thread.pin-reordered": return decodeForEvent(ThreadPinReorderedPayload, event.payload, event.type, "payload").pipe(Effect.map((payload) => ({
23726
+ ...nextBase,
23727
+ threads: updateThread(nextBase.threads, payload.threadId, {
23728
+ pinOrderKey: payload.pinOrderKey,
23729
+ updatedAt: payload.updatedAt
23730
+ })
23731
+ })));
23577
23732
  case "thread.meta-updated": return decodeForEvent(ThreadMetaUpdatedPayload, event.payload, event.type, "payload").pipe(Effect.map((payload) => ({
23578
23733
  ...nextBase,
23579
23734
  threads: updateThread(nextBase.threads, payload.threadId, {
@@ -24305,7 +24460,7 @@ const decideOrchestrationCommand = Effect.fn("decideOrchestrationCommand")(funct
24305
24460
  detail: `thread ${command.threadId} has a queued turn start and cannot be settled`
24306
24461
  }));
24307
24462
  const alreadySettled = thread.settledOverride === "settled" && thread.settledAt !== null;
24308
- return {
24463
+ const settledEvent = {
24309
24464
  ...yield* withEventBase({
24310
24465
  aggregateKind: "thread",
24311
24466
  aggregateId: command.threadId,
@@ -24319,6 +24474,20 @@ const decideOrchestrationCommand = Effect.fn("decideOrchestrationCommand")(funct
24319
24474
  updatedAt: alreadySettled ? thread.updatedAt : occurredAt
24320
24475
  }
24321
24476
  };
24477
+ if (thread.pinnedAt == null) return settledEvent;
24478
+ return [settledEvent, {
24479
+ ...yield* withEventBase({
24480
+ aggregateKind: "thread",
24481
+ aggregateId: command.threadId,
24482
+ occurredAt,
24483
+ commandId: command.commandId
24484
+ }),
24485
+ type: "thread.unpinned",
24486
+ payload: {
24487
+ threadId: command.threadId,
24488
+ updatedAt: occurredAt
24489
+ }
24490
+ }];
24322
24491
  }
24323
24492
  case "thread.unsettle": {
24324
24493
  const thread = yield* requireThreadNotArchived({
@@ -24402,6 +24571,105 @@ const decideOrchestrationCommand = Effect.fn("decideOrchestrationCommand")(funct
24402
24571
  }
24403
24572
  };
24404
24573
  }
24574
+ case "thread.pin": {
24575
+ const thread = yield* requireThreadNotArchived({
24576
+ readModel,
24577
+ command,
24578
+ threadId: command.threadId
24579
+ });
24580
+ const occurredAt = yield* nowIso$8;
24581
+ const existingPinnedAt = thread.pinnedAt ?? null;
24582
+ const pinnedEvent = {
24583
+ ...yield* withEventBase({
24584
+ aggregateKind: "thread",
24585
+ aggregateId: command.threadId,
24586
+ occurredAt,
24587
+ commandId: command.commandId
24588
+ }),
24589
+ type: "thread.pinned",
24590
+ payload: {
24591
+ threadId: command.threadId,
24592
+ pinnedAt: existingPinnedAt ?? occurredAt,
24593
+ updatedAt: existingPinnedAt !== null ? thread.updatedAt : occurredAt
24594
+ }
24595
+ };
24596
+ const promotionEvents = [];
24597
+ if (thread.settledOverride === "settled") promotionEvents.push({
24598
+ ...yield* withEventBase({
24599
+ aggregateKind: "thread",
24600
+ aggregateId: command.threadId,
24601
+ occurredAt,
24602
+ commandId: command.commandId
24603
+ }),
24604
+ type: "thread.unsettled",
24605
+ payload: {
24606
+ threadId: command.threadId,
24607
+ reason: "user",
24608
+ updatedAt: occurredAt
24609
+ }
24610
+ });
24611
+ if (thread.snoozedUntil != null) promotionEvents.push({
24612
+ ...yield* withEventBase({
24613
+ aggregateKind: "thread",
24614
+ aggregateId: command.threadId,
24615
+ occurredAt,
24616
+ commandId: command.commandId
24617
+ }),
24618
+ type: "thread.unsnoozed",
24619
+ payload: {
24620
+ threadId: command.threadId,
24621
+ reason: "user",
24622
+ updatedAt: occurredAt
24623
+ }
24624
+ });
24625
+ return promotionEvents.length > 0 ? [pinnedEvent, ...promotionEvents] : pinnedEvent;
24626
+ }
24627
+ case "thread.unpin": {
24628
+ const thread = yield* requireThreadNotArchived({
24629
+ readModel,
24630
+ command,
24631
+ threadId: command.threadId
24632
+ });
24633
+ const alreadyUnpinned = thread.pinnedAt == null;
24634
+ const occurredAt = yield* nowIso$8;
24635
+ return {
24636
+ ...yield* withEventBase({
24637
+ aggregateKind: "thread",
24638
+ aggregateId: command.threadId,
24639
+ occurredAt,
24640
+ commandId: command.commandId
24641
+ }),
24642
+ type: "thread.unpinned",
24643
+ payload: {
24644
+ threadId: command.threadId,
24645
+ updatedAt: alreadyUnpinned ? thread.updatedAt : occurredAt
24646
+ }
24647
+ };
24648
+ }
24649
+ case "thread.pin.reorder": {
24650
+ const thread = yield* requireThreadNotArchived({
24651
+ readModel,
24652
+ command,
24653
+ threadId: command.threadId
24654
+ });
24655
+ const occurredAt = yield* nowIso$8;
24656
+ const nextKey = thread.pinnedAt == null ? null : command.pinOrderKey;
24657
+ const unchanged = (thread.pinOrderKey ?? null) === nextKey;
24658
+ return {
24659
+ ...yield* withEventBase({
24660
+ aggregateKind: "thread",
24661
+ aggregateId: command.threadId,
24662
+ occurredAt,
24663
+ commandId: command.commandId
24664
+ }),
24665
+ type: "thread.pin-reordered",
24666
+ payload: {
24667
+ threadId: command.threadId,
24668
+ pinOrderKey: nextKey,
24669
+ updatedAt: unchanged ? thread.updatedAt : occurredAt
24670
+ }
24671
+ };
24672
+ }
24405
24673
  case "thread.meta.update": {
24406
24674
  const thread = yield* requireThread({
24407
24675
  readModel,
@@ -25362,6 +25630,8 @@ const ProjectionThread = Schema$1.Struct({
25362
25630
  settledAt: Schema$1.NullOr(IsoDateTime),
25363
25631
  snoozedUntil: Schema$1.NullOr(IsoDateTime),
25364
25632
  snoozedAt: Schema$1.NullOr(IsoDateTime),
25633
+ pinnedAt: Schema$1.NullOr(IsoDateTime),
25634
+ pinOrderKey: Schema$1.NullOr(Schema$1.String),
25365
25635
  latestUserMessageAt: Schema$1.NullOr(IsoDateTime),
25366
25636
  pendingApprovalCount: NonNegativeInt,
25367
25637
  pendingUserInputCount: NonNegativeInt,
@@ -26255,6 +26525,8 @@ const makeProjectionThreadRepository = Effect.gen(function* () {
26255
26525
  settled_at,
26256
26526
  snoozed_until,
26257
26527
  snoozed_at,
26528
+ pinned_at,
26529
+ pin_order_key,
26258
26530
  latest_user_message_at,
26259
26531
  pending_approval_count,
26260
26532
  pending_user_input_count,
@@ -26282,6 +26554,8 @@ const makeProjectionThreadRepository = Effect.gen(function* () {
26282
26554
  ${row.settledAt},
26283
26555
  ${row.snoozedUntil},
26284
26556
  ${row.snoozedAt},
26557
+ ${row.pinnedAt},
26558
+ ${row.pinOrderKey},
26285
26559
  ${row.latestUserMessageAt},
26286
26560
  ${row.pendingApprovalCount},
26287
26561
  ${row.pendingUserInputCount},
@@ -26309,6 +26583,8 @@ const makeProjectionThreadRepository = Effect.gen(function* () {
26309
26583
  settled_at = excluded.settled_at,
26310
26584
  snoozed_until = excluded.snoozed_until,
26311
26585
  snoozed_at = excluded.snoozed_at,
26586
+ pinned_at = excluded.pinned_at,
26587
+ pin_order_key = excluded.pin_order_key,
26312
26588
  latest_user_message_at = excluded.latest_user_message_at,
26313
26589
  pending_approval_count = excluded.pending_approval_count,
26314
26590
  pending_user_input_count = excluded.pending_user_input_count,
@@ -26341,6 +26617,8 @@ const makeProjectionThreadRepository = Effect.gen(function* () {
26341
26617
  settled_at AS "settledAt",
26342
26618
  snoozed_until AS "snoozedUntil",
26343
26619
  snoozed_at AS "snoozedAt",
26620
+ pinned_at AS "pinnedAt",
26621
+ pin_order_key AS "pinOrderKey",
26344
26622
  latest_user_message_at AS "latestUserMessageAt",
26345
26623
  pending_approval_count AS "pendingApprovalCount",
26346
26624
  pending_user_input_count AS "pendingUserInputCount",
@@ -26375,6 +26653,8 @@ const makeProjectionThreadRepository = Effect.gen(function* () {
26375
26653
  settled_at AS "settledAt",
26376
26654
  snoozed_until AS "snoozedUntil",
26377
26655
  snoozed_at AS "snoozedAt",
26656
+ pinned_at AS "pinnedAt",
26657
+ pin_order_key AS "pinOrderKey",
26378
26658
  latest_user_message_at AS "latestUserMessageAt",
26379
26659
  pending_approval_count AS "pendingApprovalCount",
26380
26660
  pending_user_input_count AS "pendingUserInputCount",
@@ -26969,6 +27249,8 @@ const makeOrchestrationProjectionPipeline = Effect.fn("makeOrchestrationProjecti
26969
27249
  settledAt: null,
26970
27250
  snoozedUntil: null,
26971
27251
  snoozedAt: null,
27252
+ pinnedAt: null,
27253
+ pinOrderKey: null,
26972
27254
  latestUserMessageAt: null,
26973
27255
  pendingApprovalCount: 0,
26974
27256
  pendingUserInputCount: 0,
@@ -27042,6 +27324,38 @@ const makeOrchestrationProjectionPipeline = Effect.fn("makeOrchestrationProjecti
27042
27324
  });
27043
27325
  return;
27044
27326
  }
27327
+ case "thread.pinned": {
27328
+ const existingRow = yield* projectionThreadRepository.getById({ threadId: event.payload.threadId });
27329
+ if (Option.isNone(existingRow)) return;
27330
+ yield* projectionThreadRepository.upsert({
27331
+ ...existingRow.value,
27332
+ pinnedAt: event.payload.pinnedAt,
27333
+ ...event.payload.pinOrderKey === void 0 ? {} : { pinOrderKey: event.payload.pinOrderKey },
27334
+ updatedAt: event.payload.updatedAt
27335
+ });
27336
+ return;
27337
+ }
27338
+ case "thread.unpinned": {
27339
+ const existingRow = yield* projectionThreadRepository.getById({ threadId: event.payload.threadId });
27340
+ if (Option.isNone(existingRow)) return;
27341
+ yield* projectionThreadRepository.upsert({
27342
+ ...existingRow.value,
27343
+ pinnedAt: null,
27344
+ pinOrderKey: null,
27345
+ updatedAt: event.payload.updatedAt
27346
+ });
27347
+ return;
27348
+ }
27349
+ case "thread.pin-reordered": {
27350
+ const existingRow = yield* projectionThreadRepository.getById({ threadId: event.payload.threadId });
27351
+ if (Option.isNone(existingRow)) return;
27352
+ yield* projectionThreadRepository.upsert({
27353
+ ...existingRow.value,
27354
+ pinOrderKey: event.payload.pinOrderKey,
27355
+ updatedAt: event.payload.updatedAt
27356
+ });
27357
+ return;
27358
+ }
27045
27359
  case "thread.meta-updated": {
27046
27360
  const existingRow = yield* projectionThreadRepository.getById({ threadId: event.payload.threadId });
27047
27361
  if (Option.isNone(existingRow)) return;
@@ -27616,6 +27930,84 @@ const makeOrchestrationProjectionPipeline = Effect.fn("makeOrchestrationProjecti
27616
27930
  });
27617
27931
  const OrchestrationProjectionPipelineLive = Layer.effect(OrchestrationProjectionPipeline, makeOrchestrationProjectionPipeline()).pipe(Layer.provideMerge(ProjectionProjectRepositoryLive), Layer.provideMerge(ProjectionThreadRepositoryLive), Layer.provideMerge(ProjectionThreadMessageRepositoryLive), Layer.provideMerge(ProjectionThreadProposedPlanRepositoryLive), Layer.provideMerge(ProjectionThreadActivityRepositoryLive), Layer.provideMerge(ProjectionThreadSessionRepositoryLive), Layer.provideMerge(ProjectionTurnRepositoryLive), Layer.provideMerge(ProjectionPendingApprovalRepositoryLive), Layer.provideMerge(ProjectionStateRepositoryLive));
27618
27932
  //#endregion
27933
+ //#region src/orchestration/ThreadBackgroundLiveness.ts
27934
+ /**
27935
+ * ThreadBackgroundLivenessService - in-memory per-thread background liveness
27936
+ * for the sidebar status pill.
27937
+ *
27938
+ * The turn can settle while native background work runs on (subagent fleets,
27939
+ * workflow runs, Monitor watch loops); the shell previously showed nothing.
27940
+ * Ingestion records task lifecycle transitions and the shell query reads the
27941
+ * derived state at mapping time — no persistence, no migration. After a
27942
+ * server restart the registry is empty until new task events arrive, which
27943
+ * matches reality: orphaned background work is not live.
27944
+ *
27945
+ * "monitoring" is reserved for watch loops (monitor tasks and background
27946
+ * shells) when they are the ONLY live work; any agent work presents as
27947
+ * "working".
27948
+ *
27949
+ * @module ThreadBackgroundLivenessService
27950
+ */
27951
+ const TERMINAL_STATUSES = /* @__PURE__ */ new Set([
27952
+ "completed",
27953
+ "failed",
27954
+ "stopped",
27955
+ "cancelled",
27956
+ "interrupted"
27957
+ ]);
27958
+ var ThreadBackgroundLivenessService = class extends Context.Service()("p4code/orchestration/ThreadBackgroundLiveness/ThreadBackgroundLivenessService") {};
27959
+ function make$73() {
27960
+ const stateByThreadId = /* @__PURE__ */ new Map();
27961
+ const stateFor = (threadId) => {
27962
+ const existing = stateByThreadId.get(threadId);
27963
+ if (existing) return existing;
27964
+ const created = {
27965
+ agents: /* @__PURE__ */ new Set(),
27966
+ monitors: /* @__PURE__ */ new Set()
27967
+ };
27968
+ stateByThreadId.set(threadId, created);
27969
+ return created;
27970
+ };
27971
+ const drop = (threadId, taskId) => {
27972
+ const state = stateByThreadId.get(threadId);
27973
+ if (!state) return;
27974
+ state.agents.delete(taskId);
27975
+ state.monitors.delete(taskId);
27976
+ if (state.agents.size === 0 && state.monitors.size === 0) stateByThreadId.delete(threadId);
27977
+ };
27978
+ return {
27979
+ recordTaskLiveness: (input) => {
27980
+ const taskType = input.taskType;
27981
+ if (taskType !== void 0 && INERT_TASK_TYPES.has(taskType)) {
27982
+ drop(input.threadId, input.taskId);
27983
+ return;
27984
+ }
27985
+ if (input.agentId !== void 0 && (taskType === void 0 || MONITOR_TASK_TYPES.has(taskType))) {
27986
+ drop(input.threadId, input.taskId);
27987
+ return;
27988
+ }
27989
+ if (input.kind === "completed" || input.status === "idle" || input.status !== void 0 && TERMINAL_STATUSES.has(input.status)) {
27990
+ drop(input.threadId, input.taskId);
27991
+ return;
27992
+ }
27993
+ drop(input.threadId, input.taskId);
27994
+ const state = stateFor(input.threadId);
27995
+ (taskType !== void 0 && MONITOR_TASK_TYPES.has(taskType) ? state.monitors : state.agents).add(input.taskId);
27996
+ },
27997
+ clearThreadLiveness: (threadId) => {
27998
+ stateByThreadId.delete(threadId);
27999
+ },
28000
+ getThreadBackgroundLiveness: (threadId) => {
28001
+ const state = stateByThreadId.get(threadId);
28002
+ if (!state) return null;
28003
+ if (state.agents.size > 0) return "working";
28004
+ if (state.monitors.size > 0) return "monitoring";
28005
+ return null;
28006
+ }
28007
+ };
28008
+ }
28009
+ const layer$63 = Layer.effect(ThreadBackgroundLivenessService, Effect.sync(make$73));
28010
+ //#endregion
27619
28011
  //#region src/persistence/Services/ProjectionCheckpoints.ts
27620
28012
  /**
27621
28013
  * ProjectionCheckpointRepository - Projection repository interface for checkpoints.
@@ -28463,6 +28855,7 @@ function toPersistenceSqlOrDecodeError$1(sqlOperation, decodeOperation) {
28463
28855
  const makeProjectionSnapshotQuery = Effect.gen(function* () {
28464
28856
  const sql = yield* SqlClient.SqlClient;
28465
28857
  const repositoryIdentityResolver = yield* RepositoryIdentityResolver;
28858
+ const threadBackgroundLiveness = yield* ThreadBackgroundLivenessService;
28466
28859
  const repositoryIdentityResolutionConcurrency = 4;
28467
28860
  const resolveRepositoryIdentitiesForProjects = Effect.fn("ProjectionSnapshotQuery.resolveRepositoryIdentitiesForProjects")(function* (projectRows, options) {
28468
28861
  const filteredProjectRows = options?.includeDeleted === true ? projectRows : projectRows.filter((row) => row.deletedAt === null);
@@ -28546,6 +28939,8 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
28546
28939
  settled_at AS "settledAt",
28547
28940
  snoozed_until AS "snoozedUntil",
28548
28941
  snoozed_at AS "snoozedAt",
28942
+ pinned_at AS "pinnedAt",
28943
+ pin_order_key AS "pinOrderKey",
28549
28944
  latest_user_message_at AS "latestUserMessageAt",
28550
28945
  pending_approval_count AS "pendingApprovalCount",
28551
28946
  pending_user_input_count AS "pendingUserInputCount",
@@ -28580,6 +28975,8 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
28580
28975
  settled_at AS "settledAt",
28581
28976
  snoozed_until AS "snoozedUntil",
28582
28977
  snoozed_at AS "snoozedAt",
28978
+ pinned_at AS "pinnedAt",
28979
+ pin_order_key AS "pinOrderKey",
28583
28980
  latest_user_message_at AS "latestUserMessageAt",
28584
28981
  pending_approval_count AS "pendingApprovalCount",
28585
28982
  pending_user_input_count AS "pendingUserInputCount",
@@ -28616,6 +29013,8 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
28616
29013
  settled_at AS "settledAt",
28617
29014
  snoozed_until AS "snoozedUntil",
28618
29015
  snoozed_at AS "snoozedAt",
29016
+ pinned_at AS "pinnedAt",
29017
+ pin_order_key AS "pinOrderKey",
28619
29018
  latest_user_message_at AS "latestUserMessageAt",
28620
29019
  pending_approval_count AS "pendingApprovalCount",
28621
29020
  pending_user_input_count AS "pendingUserInputCount",
@@ -29036,6 +29435,8 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
29036
29435
  settled_at AS "settledAt",
29037
29436
  snoozed_until AS "snoozedUntil",
29038
29437
  snoozed_at AS "snoozedAt",
29438
+ pinned_at AS "pinnedAt",
29439
+ pin_order_key AS "pinOrderKey",
29039
29440
  latest_user_message_at AS "latestUserMessageAt",
29040
29441
  pending_approval_count AS "pendingApprovalCount",
29041
29442
  pending_user_input_count AS "pendingUserInputCount",
@@ -29356,6 +29757,8 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
29356
29757
  settledAt: row.settledAt,
29357
29758
  snoozedUntil: row.snoozedUntil,
29358
29759
  snoozedAt: row.snoozedAt,
29760
+ pinnedAt: row.pinnedAt,
29761
+ pinOrderKey: row.pinOrderKey ?? null,
29359
29762
  deletedAt: row.deletedAt,
29360
29763
  messages: messagesByThread.get(row.threadId) ?? [],
29361
29764
  proposedPlans: proposedPlansByThread.get(row.threadId) ?? [],
@@ -29472,6 +29875,8 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
29472
29875
  settledAt: row.settledAt,
29473
29876
  snoozedUntil: row.snoozedUntil,
29474
29877
  snoozedAt: row.snoozedAt,
29878
+ pinnedAt: row.pinnedAt,
29879
+ pinOrderKey: row.pinOrderKey ?? null,
29475
29880
  deletedAt: row.deletedAt,
29476
29881
  messages: [],
29477
29882
  proposedPlans: proposedPlansByThread.get(row.threadId) ?? [],
@@ -29534,11 +29939,14 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
29534
29939
  settledAt: row.settledAt,
29535
29940
  snoozedUntil: row.snoozedUntil,
29536
29941
  snoozedAt: row.snoozedAt,
29942
+ pinnedAt: row.pinnedAt,
29943
+ pinOrderKey: row.pinOrderKey ?? null,
29537
29944
  session: sessionByThread.get(row.threadId) ?? null,
29538
29945
  latestUserMessageAt: row.latestUserMessageAt,
29539
29946
  hasPendingApprovals: row.pendingApprovalCount > 0,
29540
29947
  hasPendingUserInput: row.pendingUserInputCount > 0,
29541
29948
  hasActionableProposedPlan: row.hasActionableProposedPlan > 0,
29949
+ backgroundLiveness: threadBackgroundLiveness.getThreadBackgroundLiveness(row.threadId),
29542
29950
  hasBackgroundTasks: row.backgroundTaskCount > 0,
29543
29951
  scheduledWakeAt: row.scheduledWakeAt
29544
29952
  }) : Result.failVoid),
@@ -29593,11 +30001,14 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
29593
30001
  settledAt: row.settledAt,
29594
30002
  snoozedUntil: row.snoozedUntil,
29595
30003
  snoozedAt: row.snoozedAt,
30004
+ pinnedAt: row.pinnedAt,
30005
+ pinOrderKey: row.pinOrderKey ?? null,
29596
30006
  session: sessionByThread.get(row.threadId) ?? null,
29597
30007
  latestUserMessageAt: row.latestUserMessageAt,
29598
30008
  hasPendingApprovals: row.pendingApprovalCount > 0,
29599
30009
  hasPendingUserInput: row.pendingUserInputCount > 0,
29600
30010
  hasActionableProposedPlan: row.hasActionableProposedPlan > 0,
30011
+ backgroundLiveness: threadBackgroundLiveness.getThreadBackgroundLiveness(row.threadId),
29601
30012
  hasBackgroundTasks: row.backgroundTaskCount > 0,
29602
30013
  scheduledWakeAt: row.scheduledWakeAt
29603
30014
  })),
@@ -29702,11 +30113,14 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
29702
30113
  settledAt: threadRow.value.settledAt,
29703
30114
  snoozedUntil: threadRow.value.snoozedUntil,
29704
30115
  snoozedAt: threadRow.value.snoozedAt,
30116
+ pinnedAt: threadRow.value.pinnedAt,
30117
+ pinOrderKey: threadRow.value.pinOrderKey ?? null,
29705
30118
  session: Option.isSome(sessionRow) ? mapSessionRow(sessionRow.value) : null,
29706
30119
  latestUserMessageAt: threadRow.value.latestUserMessageAt,
29707
30120
  hasPendingApprovals: threadRow.value.pendingApprovalCount > 0,
29708
30121
  hasPendingUserInput: threadRow.value.pendingUserInputCount > 0,
29709
30122
  hasActionableProposedPlan: threadRow.value.hasActionableProposedPlan > 0,
30123
+ backgroundLiveness: threadBackgroundLiveness.getThreadBackgroundLiveness(threadRow.value.threadId),
29710
30124
  hasBackgroundTasks: threadRow.value.backgroundTaskCount > 0,
29711
30125
  scheduledWakeAt: threadRow.value.scheduledWakeAt
29712
30126
  });
@@ -29743,6 +30157,8 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
29743
30157
  settledAt: threadRow.value.settledAt,
29744
30158
  snoozedUntil: threadRow.value.snoozedUntil,
29745
30159
  snoozedAt: threadRow.value.snoozedAt,
30160
+ pinnedAt: threadRow.value.pinnedAt,
30161
+ pinOrderKey: threadRow.value.pinOrderKey ?? null,
29746
30162
  deletedAt: null,
29747
30163
  messages: messageRows.map((row) => {
29748
30164
  const message = {
@@ -29812,7 +30228,7 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
29812
30228
  getThreadDetailSnapshot
29813
30229
  };
29814
30230
  });
29815
- const OrchestrationProjectionSnapshotQueryLive = Layer.effect(ProjectionSnapshotQuery, makeProjectionSnapshotQuery);
30231
+ const OrchestrationProjectionSnapshotQueryLive = Layer.effect(ProjectionSnapshotQuery, makeProjectionSnapshotQuery).pipe(Layer.provide(layer$63));
29816
30232
  //#endregion
29817
30233
  //#region src/orchestration/Services/ThreadEventStream.ts
29818
30234
  var ThreadEventStreamService = class extends Context.Service()("@p4code/cli/orchestration/Services/ThreadEventStream/ThreadEventStreamService") {};
@@ -29871,7 +30287,14 @@ const ThreadEventStreamLive = Layer.effect(ThreadEventStreamService, makeThreadE
29871
30287
  //#region src/orchestration/runtimeLayer.ts
29872
30288
  const OrchestrationEventInfrastructureLayerLive = Layer.mergeAll(OrchestrationEventStoreLive, OrchestrationCommandReceiptRepositoryLive);
29873
30289
  const OrchestrationProjectionPipelineLayerLive = OrchestrationProjectionPipelineLive.pipe(Layer.provide(OrchestrationEventStoreLive));
29874
- const OrchestrationInfrastructureLayerLive = Layer.mergeAll(OrchestrationProjectionSnapshotQueryLive, OrchestrationEventInfrastructureLayerLive, OrchestrationProjectionPipelineLayerLive);
30290
+ /**
30291
+ * In-memory registry of background work that outlives a turn. Exported as one
30292
+ * reference on purpose: the snapshot query reads liveness while provider
30293
+ * ingestion writes it, and layers memoize by reference, so both must receive
30294
+ * THIS layer or they would each get their own empty registry.
30295
+ */
30296
+ const ThreadBackgroundLivenessLayerLive = layer$63;
30297
+ const OrchestrationInfrastructureLayerLive = Layer.mergeAll(OrchestrationProjectionSnapshotQueryLive, OrchestrationEventInfrastructureLayerLive, OrchestrationProjectionPipelineLayerLive, ThreadBackgroundLivenessLayerLive);
29875
30298
  const OrchestrationEngineLayerLive = OrchestrationEngineLive.pipe(Layer.provide(OrchestrationInfrastructureLayerLive));
29876
30299
  const OrchestrationLayerLive = Layer.mergeAll(OrchestrationInfrastructureLayerLive, OrchestrationEngineLayerLive, ThreadEventStreamLive.pipe(Layer.provide(OrchestrationEngineLayerLive)));
29877
30300
  //#endregion
@@ -32091,6 +32514,7 @@ const make$64 = Effect.gen(function* () {
32091
32514
  pullRequests: true,
32092
32515
  threadSettlement: true,
32093
32516
  threadSnooze: true,
32517
+ threadPinning: true,
32094
32518
  ...serverSelfUpdate === null ? {} : { serverSelfUpdate },
32095
32519
  ...serverServiceSupervised ? { serverServiceSupervised } : {}
32096
32520
  }
@@ -33735,7 +34159,7 @@ const runProjectMutation = Effect.fn("runProjectMutation")(function* (flags, run
33735
34159
  });
33736
34160
  yield* Console.log(output);
33737
34161
  }));
33738
- const offlineRuntimeLayer = ProjectCliRuntimeLive.pipe(Layer.provide(layer$79(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)));
34162
+ const offlineRuntimeLayer = ProjectCliRuntimeLive.pipe(Layer.provide(layer$80(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)));
33739
34163
  return yield* Effect.gen(function* () {
33740
34164
  const snapshot = yield* getOfflineSnapshot();
33741
34165
  const orchestrationEngine = yield* OrchestrationEngineService;
@@ -33746,7 +34170,7 @@ const runProjectMutation = Effect.fn("runProjectMutation")(function* (flags, run
33746
34170
  });
33747
34171
  yield* Console.log(output);
33748
34172
  }).pipe(Effect.provide(offlineRuntimeLayer));
33749
- }).pipe(Effect.provide(Layer.mergeAll(runtimeLayer, layer$52).pipe(Layer.provideMerge(FetchHttpClient.layer), Layer.provide(layer$79(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)))));
34173
+ }).pipe(Effect.provide(Layer.mergeAll(runtimeLayer, layer$52).pipe(Layer.provideMerge(FetchHttpClient.layer), Layer.provide(layer$80(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)))));
33750
34174
  });
33751
34175
  const projectAddCommand = Command.make("add", {
33752
34176
  ...projectLocationFlags,
@@ -40506,6 +40930,24 @@ const makeLinearApiTransport = Effect.gen(function* () {
40506
40930
  * @module tracker/LinearClient
40507
40931
  */
40508
40932
  var LinearClient = class extends Context.Service()("@p4code/cli/tracker/LinearClient") {};
40933
+ /**
40934
+ * How much of a stored key the settings row may show.
40935
+ *
40936
+ * The prefix identifies the provider (`lin_api_`) and the suffix is enough to
40937
+ * tell two keys apart when one is being replaced. The bullet run is a fixed
40938
+ * width rather than the number of characters hidden, so the mask says nothing
40939
+ * about how long the key is.
40940
+ */
40941
+ const MASK_PREFIX_CHARS = 8;
40942
+ const MASK_BULLETS = "•".repeat(8);
40943
+ /**
40944
+ * A stored key rendered for display, never for use.
40945
+ *
40946
+ * A key too short to reveal both ends without meeting in the middle is shown
40947
+ * as bullets alone: a mask that gives away most of a short key is worse than
40948
+ * one that gives away nothing.
40949
+ */
40950
+ const maskApiKey = (apiKey) => apiKey.length > 12 ? `${apiKey.slice(0, MASK_PREFIX_CHARS)}${MASK_BULLETS}${apiKey.slice(-4)}` : MASK_BULLETS;
40509
40951
  const makeLinearClient = Effect.gen(function* () {
40510
40952
  const secrets = yield* TrackerSecrets;
40511
40953
  const api = yield* makeLinearApiTransport;
@@ -40524,12 +40966,14 @@ const makeLinearClient = Effect.gen(function* () {
40524
40966
  if (Option.isSome(credential)) return {
40525
40967
  provider: "linear",
40526
40968
  mode: "api",
40527
- workspace: credential.value.workspace
40969
+ workspace: credential.value.workspace,
40970
+ maskedKey: maskApiKey(credential.value.apiKey)
40528
40971
  };
40529
40972
  return {
40530
40973
  provider: "linear",
40531
40974
  mode: (yield* mcp.isConfigured) ? "mcp" : "none",
40532
- workspace: null
40975
+ workspace: null,
40976
+ maskedKey: null
40533
40977
  };
40534
40978
  });
40535
40979
  const setApiKey = (apiKey) => Effect.gen(function* () {
@@ -64555,7 +64999,7 @@ const makeOpenCodeRuntime = Effect.gen(function* () {
64555
64999
  };
64556
65000
  });
64557
65001
  var OpenCodeRuntime = class extends Context.Service()("@p4code/cli/provider/opencodeRuntime") {};
64558
- const OpenCodeRuntimeLive = Layer.effect(OpenCodeRuntime, makeOpenCodeRuntime).pipe(Layer.provide(layer$80));
65002
+ const OpenCodeRuntimeLive = Layer.effect(OpenCodeRuntime, makeOpenCodeRuntime).pipe(Layer.provide(layer$81));
64559
65003
  //#endregion
64560
65004
  //#region src/textGeneration/TextGenerationUtils.ts
64561
65005
  const isTextGenerationError$3 = Schema$1.is(TextGenerationError);
@@ -103874,6 +104318,7 @@ const make$4 = Effect.gen(function* () {
103874
104318
  const projectionSnapshotQuery = yield* ProjectionSnapshotQuery;
103875
104319
  const providerService = yield* ProviderService;
103876
104320
  const projectionTurnRepository = yield* ProjectionTurnRepository;
104321
+ const threadBackgroundLiveness = yield* ThreadBackgroundLivenessService;
103877
104322
  const serverSettingsService = yield* ServerSettingsService;
103878
104323
  const taskRepositories = yield* TaskRepositoryRegistry;
103879
104324
  const providerCommandId = (event, tag) => crypto.randomUUIDv4.pipe(Effect.map((uuid) => CommandId.make(`provider:${event.eventId}:${tag}:${uuid}`)));
@@ -104160,6 +104605,26 @@ const make$4 = Effect.gen(function* () {
104160
104605
  const processRuntimeEvent = (event) => Effect.gen(function* () {
104161
104606
  const thread = yield* resolveThreadShell(event.threadId);
104162
104607
  if (!thread) return;
104608
+ switch (event.type) {
104609
+ case "task.started":
104610
+ case "task.progress":
104611
+ case "task.completed": {
104612
+ const payload = event.payload;
104613
+ threadBackgroundLiveness.recordTaskLiveness({
104614
+ threadId: thread.id,
104615
+ taskId: payload.taskId,
104616
+ taskType: payload.taskType,
104617
+ status: payload.status,
104618
+ agentId: payload.agentId,
104619
+ kind: event.type === "task.started" ? "started" : event.type === "task.progress" ? "progress" : "completed"
104620
+ });
104621
+ break;
104622
+ }
104623
+ case "session.exited":
104624
+ threadBackgroundLiveness.clearThreadLiveness(thread.id);
104625
+ break;
104626
+ default: break;
104627
+ }
104163
104628
  let loadedThreadDetail;
104164
104629
  const getLoadedThreadDetail = () => Effect.gen(function* () {
104165
104630
  if (loadedThreadDetail !== void 0) return loadedThreadDetail;
@@ -104477,7 +104942,7 @@ const make$4 = Effect.gen(function* () {
104477
104942
  drain: worker.drain
104478
104943
  };
104479
104944
  });
104480
- const ProviderRuntimeIngestionLive = Layer.effect(ProviderRuntimeIngestionService, make$4).pipe(Layer.provide(ProjectionTurnRepositoryLive));
104945
+ const ProviderRuntimeIngestionLive = Layer.effect(ProviderRuntimeIngestionService, make$4).pipe(Layer.provide(ProjectionTurnRepositoryLive), Layer.provide(layer$63));
104481
104946
  //#endregion
104482
104947
  //#region src/provider/userInvokedSkills.ts
104483
104948
  /**
@@ -107229,11 +107694,11 @@ const WorkspaceEntriesLayerLive = layer$32.pipe(Layer.provide(layer$52));
107229
107694
  const WorkspaceFileSystemLayerLive = layer$31.pipe(Layer.provide(layer$52), Layer.provide(WorkspaceEntriesLayerLive));
107230
107695
  const WorkspaceLayerLive = Layer.mergeAll(layer$52, WorkspaceEntriesLayerLive, WorkspaceFileSystemLayerLive);
107231
107696
  const ProjectFaviconResolverLayerLive = layer$50.pipe(Layer.provide(layer$52), Layer.provide(layer$51));
107232
- const AuthLayerLive = layer$72.pipe(Layer.provideMerge(PersistenceLayerLive), Layer.provide(layer$76));
107697
+ const AuthLayerLive = layer$73.pipe(Layer.provideMerge(PersistenceLayerLive), Layer.provide(layer$77));
107233
107698
  const ProviderRuntimeLayerLive = ProviderSessionReaperLive.pipe(Layer.provideMerge(ProviderLayerLive), Layer.provideMerge(OrchestrationLayerLive));
107234
- const RuntimeCoreDependenciesLive = ReactorLayerLive.pipe(Layer.provideMerge(CheckpointingLayerLive), Layer.provideMerge(SourceControlProviderRegistryLayerLive), Layer.provideMerge(GitLayerLive), Layer.provideMerge(VcsLayerLive), Layer.provideMerge(ProviderRuntimeLayerLive), Layer.provideMerge(Layer.mergeAll(TerminalLayerLive, PreviewLayerLive, RoutedTaskRepositoryLive)), Layer.provideMerge(Layer.mergeAll(PersistenceLayerLive, layer$65)), Layer.provideMerge(layer$60), Layer.provideMerge(ProviderRegistryLive), Layer.provideMerge(ProviderInstanceRegistryHydrationLive), Layer.provideMerge(ProviderEventLoggersLive), Layer.provideMerge(OpenCodeRuntimeLive), Layer.provideMerge(layer$69.pipe(Layer.provide(layer$76))), Layer.provideMerge(WorkspaceLayerLive), Layer.provideMerge(ProjectFaviconResolverLayerLive), Layer.provideMerge(layer$61), Layer.provideMerge(layer$54), Layer.provideMerge(AuthLayerLive), Layer.provideMerge(layer$1.pipe(Layer.provideMerge(LinearClientLive), Layer.provideMerge(Layer.mergeAll(LinearMcpClientLive, TrackerSecretsLive)), Layer.provideMerge(Layer.mergeAll(layer$66, layer$38).pipe(Layer.provideMerge(layer$67), Layer.provideMerge(layer$68))))), Layer.provideMerge(layer$76));
107235
- const UsageLayerLive = layer$40.pipe(Layer.provide(layer$69.pipe(Layer.provide(layer$76))));
107236
- const RuntimeDependenciesLive = layer$39.pipe(Layer.provideMerge(RuntimeCoreDependenciesLive)).pipe(Layer.provideMerge(UsageLayerLive), Layer.provideMerge(layer$15), Layer.provideMerge(layer$14), Layer.provideMerge(layer$13), Layer.provideMerge(layer$57), Layer.provideMerge(layer$59), Layer.provideMerge(layer$58), Layer.provide(layer$80));
107699
+ const RuntimeCoreDependenciesLive = ReactorLayerLive.pipe(Layer.provideMerge(CheckpointingLayerLive), Layer.provideMerge(SourceControlProviderRegistryLayerLive), Layer.provideMerge(GitLayerLive), Layer.provideMerge(VcsLayerLive), Layer.provideMerge(ProviderRuntimeLayerLive), Layer.provideMerge(Layer.mergeAll(TerminalLayerLive, PreviewLayerLive, RoutedTaskRepositoryLive)), Layer.provideMerge(Layer.mergeAll(PersistenceLayerLive, layer$66)), Layer.provideMerge(layer$60), Layer.provideMerge(ProviderRegistryLive), Layer.provideMerge(ProviderInstanceRegistryHydrationLive), Layer.provideMerge(ProviderEventLoggersLive), Layer.provideMerge(OpenCodeRuntimeLive), Layer.provideMerge(layer$70.pipe(Layer.provide(layer$77))), Layer.provideMerge(WorkspaceLayerLive), Layer.provideMerge(ProjectFaviconResolverLayerLive), Layer.provideMerge(layer$61), Layer.provideMerge(layer$54), Layer.provideMerge(AuthLayerLive), Layer.provideMerge(layer$1.pipe(Layer.provideMerge(LinearClientLive), Layer.provideMerge(Layer.mergeAll(LinearMcpClientLive, TrackerSecretsLive)), Layer.provideMerge(Layer.mergeAll(layer$67, layer$38).pipe(Layer.provideMerge(layer$68), Layer.provideMerge(layer$69))))), Layer.provideMerge(layer$77));
107700
+ const UsageLayerLive = layer$40.pipe(Layer.provide(layer$70.pipe(Layer.provide(layer$77))));
107701
+ const RuntimeDependenciesLive = layer$39.pipe(Layer.provideMerge(RuntimeCoreDependenciesLive)).pipe(Layer.provideMerge(UsageLayerLive), Layer.provideMerge(layer$15), Layer.provideMerge(layer$14), Layer.provideMerge(layer$13), Layer.provideMerge(layer$57), Layer.provideMerge(layer$59), Layer.provideMerge(layer$58), Layer.provide(layer$81));
107237
107702
  /**
107238
107703
  * Hub asset sync.
107239
107704
  *
@@ -107241,7 +107706,7 @@ const RuntimeDependenciesLive = layer$39.pipe(Layer.provideMerge(RuntimeCoreDepe
107241
107706
  * not-configured shape, and every call reports that rather than failing — so
107242
107707
  * this costs a server with no hub nothing but a layer.
107243
107708
  */
107244
- const AssetSyncLive = layer$63.pipe(Layer.provideMerge(layer$64), Layer.provideMerge(layer$65));
107709
+ const AssetSyncLive = layer$64.pipe(Layer.provideMerge(layer$65), Layer.provideMerge(layer$66));
107245
107710
  const RuntimeServicesLive = layer$53.pipe(Layer.provideMerge(AssetSyncLive), Layer.provideMerge(RuntimeDependenciesLive));
107246
107711
  const PullRequestServiceLive = layer$7.pipe(Layer.provide(layer$8), Layer.provide(SourceControlProviderRegistryLayerLive), Layer.provide(layer$48));
107247
107712
  const makeRoutesLayer = Layer.mergeAll(Layer.mergeAll(HttpApiBuilder.layer(EnvironmentHttpApi).pipe(Layer.provide(authHttpApiLayer), Layer.provide(orchestrationHttpApiLayer), Layer.provide(pullRequestHttpApiLayer), Layer.provide(serverEnvironmentHttpApiLayer), Layer.provide(environmentAuthenticatedAuthLayer)), otlpTracesProxyRouteLayer, assetRouteLayer, mcpOAuthCallbackRouteLayer, staticAndDevRouteLayer, websocketRpcRouteLayer), layer.pipe(Layer.provide(layer$3))).pipe(Layer.provide(ProjectionProjectRepositoryLive.pipe(Layer.provide(PersistenceLayerLive))), Layer.provide(layer$51), Layer.provide(PullRequestServiceLive), Layer.provide(layer$35), Layer.provide(layer$55), Layer.provide(browserApiCorsLayer), Layer.provide(httpCompressionLayer));
@@ -107882,7 +108347,7 @@ const serviceCommand = Command.make("service").pipe(Command.withDescription("Man
107882
108347
  ]));
107883
108348
  //#endregion
107884
108349
  //#region src/bin.ts
107885
- const CliRuntimeLayer = Layer.mergeAll(NodeServices.layer, layer$80, FetchHttpClient.layer);
108350
+ const CliRuntimeLayer = Layer.mergeAll(NodeServices.layer, layer$81, FetchHttpClient.layer);
107886
108351
  /**
107887
108352
  * Prints an update reminder before dispatching, at most once a day.
107888
108353
  *