@gitroll/tacitt-cli 0.6.1 → 0.6.2

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/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { _ as readStateDump, a as readAgentPresence, b as traceStartup, d as helpText, f as parseOptions, g as startupFailure, h as permissionDiagnostic, i as invoke, l as awaitingPassport, m as longCommands, n as readOperation, o as createRuntime, p as businessCommands, r as waitOperation, s as executorEventSchema, t as cancelOperation, u as createRenderActivity, v as createExecutionPort, y as startHost$1 } from "./query-BlisHJtP.mjs";
2
+ import { _ as readStateDump, a as readAgentPresence, b as traceStartup, d as helpText, f as parseOptions, g as startupFailure, h as permissionDiagnostic, i as invoke, l as awaitingPassport, m as longCommands, n as readOperation, o as createRuntime, p as businessCommands, r as waitOperation, s as executorEventSchema, t as cancelOperation, u as createRenderActivity, v as createExecutionPort, y as startHost$1 } from "./query-CQ6Tr5zC.mjs";
3
3
  import { nt as makePrivateDirectory, rt as openPrivateFile } from "./src-JoT3L94j.mjs";
4
4
  import { $t as safeField, Qt as safeCode, Xt as ApplicationError } from "./locate-JYtFmKu-.mjs";
5
5
  import { Zn as reviewTransportBytes, et as readWorkspaceOutcomes, oi as stateSchema, st as resolveFailureLocation } from "./src--Ln5GHdz.mjs";
@@ -1,4 +1,4 @@
1
- import { c as executorRequestSchema, f as parseOptions, g as startupFailure, i as invoke, m as longCommands, n as readOperation, o as createRuntime, r as waitOperation } from "./query-BlisHJtP.mjs";
1
+ import { c as executorRequestSchema, f as parseOptions, g as startupFailure, i as invoke, m as longCommands, n as readOperation, o as createRuntime, r as waitOperation } from "./query-CQ6Tr5zC.mjs";
2
2
  import { $t as safeField, Qt as safeCode, Xt as ApplicationError } from "./locate-JYtFmKu-.mjs";
3
3
  import "./src--Ln5GHdz.mjs";
4
4
  import { z } from "zod";
@@ -13,7 +13,8 @@ import { execFile, spawn } from "node:child_process";
13
13
  import { basename, dirname, isAbsolute, join, posix, relative, resolve, sep } from "node:path";
14
14
  import { createInterface } from "node:readline";
15
15
  import { Writable } from "node:stream";
16
- import { parseArgs, promisify, types } from "node:util";
16
+ import { isDeepStrictEqual, parseArgs, promisify, types } from "node:util";
17
+ import { pathToFileURL } from "node:url";
17
18
  import { createHash, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
18
19
  import { setTimeout as setTimeout$1 } from "node:timers/promises";
19
20
  import { fastify } from "fastify";
@@ -10275,6 +10276,27 @@ async function* sessionArtifacts(storage, find, journeyId, artifact, signal) {
10275
10276
  }
10276
10277
  };
10277
10278
  }
10279
+ /** Own one live upload iterator; advancing it releases the preceding session ZIP. */
10280
+ async function prepareSessionArtifacts(storage, find, journeyId, artifact, signal) {
10281
+ const source = sessionArtifacts(storage, find, journeyId, artifact, signal);
10282
+ let closed = false;
10283
+ async function* iterate() {
10284
+ try {
10285
+ yield* source;
10286
+ } finally {
10287
+ closed = true;
10288
+ }
10289
+ }
10290
+ const items = iterate();
10291
+ return {
10292
+ items,
10293
+ async close() {
10294
+ if (closed) return;
10295
+ closed = true;
10296
+ await items.return(void 0);
10297
+ }
10298
+ };
10299
+ }
10278
10300
  //#endregion
10279
10301
  //#region apps/cli/src/journey/send-artifacts.ts
10280
10302
  function createJourneySendArtifacts(storage) {
@@ -10293,6 +10315,7 @@ function createJourneySendArtifacts(storage) {
10293
10315
  return output;
10294
10316
  }
10295
10317
  return {
10318
+ prepareSessions: (journeyId, artifact, signal) => prepareSessionArtifacts(storage, find, journeyId, artifact, signal),
10296
10319
  sessions: (journeyId, artifact, signal) => sessionArtifacts(storage, find, journeyId, artifact, signal),
10297
10320
  async describe(journey, work, signal) {
10298
10321
  const output = await find(journey.journeyId, work.id, work.revision);
@@ -10377,6 +10400,74 @@ async function putPrimary(primary, byteLength, upload, signal) {
10377
10400
  }
10378
10401
  }
10379
10402
  //#endregion
10403
+ //#region apps/cli/src/account/journey/session-preparation.ts
10404
+ /** Phase-local facts retain no bytes; only upload steps acquire live iterators. */
10405
+ function createSessionPreparation(dependencies) {
10406
+ const prepare = dependencies.artifacts.prepareSessions;
10407
+ const prepared = [];
10408
+ const facts = /* @__PURE__ */ new Map();
10409
+ const sessions = async function* (journeyId, artifact, signal) {
10410
+ if (!prepare) {
10411
+ const original = dependencies.artifacts.sessions;
10412
+ if (original) yield* original(journeyId, artifact, signal);
10413
+ return;
10414
+ }
10415
+ const result = await prepare(journeyId, artifact, signal);
10416
+ prepared.push(result);
10417
+ for await (const item of result.items) {
10418
+ signal.throwIfAborted();
10419
+ yield item;
10420
+ }
10421
+ };
10422
+ const sessionFacts = async function* (journeyId, artifact, signal) {
10423
+ const key = JSON.stringify([
10424
+ journeyId,
10425
+ artifact.workId,
10426
+ artifact.revision,
10427
+ artifact.sha256
10428
+ ]);
10429
+ let pending = facts.get(key);
10430
+ if (!pending) {
10431
+ pending = (async () => {
10432
+ const original = dependencies.artifacts.sessionFacts ?? dependencies.artifacts.sessions;
10433
+ const items = [];
10434
+ if (original) for await (const item of original(journeyId, artifact, signal)) {
10435
+ const { nativeSessionId, relativePath, harness, lastAt, metadata, sha256, byteLength, crc32 } = item;
10436
+ items.push({
10437
+ nativeSessionId,
10438
+ relativePath,
10439
+ harness,
10440
+ lastAt,
10441
+ metadata,
10442
+ sha256,
10443
+ byteLength,
10444
+ crc32
10445
+ });
10446
+ }
10447
+ return items;
10448
+ })();
10449
+ facts.set(key, pending);
10450
+ }
10451
+ for (const item of await pending) {
10452
+ signal.throwIfAborted();
10453
+ yield item;
10454
+ }
10455
+ };
10456
+ return {
10457
+ dependencies: {
10458
+ ...dependencies,
10459
+ artifacts: {
10460
+ ...dependencies.artifacts,
10461
+ sessions,
10462
+ sessionFacts
10463
+ }
10464
+ },
10465
+ async close() {
10466
+ await createCleanup().run(...prepared.map((result) => () => result.close()));
10467
+ }
10468
+ };
10469
+ }
10470
+ //#endregion
10380
10471
  //#region apps/cli/src/account/journey/session-key.ts
10381
10472
  /** Engine native identity, or harness-root-relative path identity when native identity is absent. */
10382
10473
  async function sessionKeyFor(input) {
@@ -10395,12 +10486,25 @@ async function sessionKeyFor(input) {
10395
10486
  //#region apps/cli/src/account/journey/session-access.ts
10396
10487
  /** Check the entire pending Send before admitting any session mutation. */
10397
10488
  async function sessionAccessBlocked(dependencies, journeyId, items, inventory, signal) {
10398
- const sessions = dependencies.artifacts.sessions;
10489
+ const sessions = dependencies.artifacts.sessionFacts ?? dependencies.artifacts.sessions;
10399
10490
  if (!sessions) return false;
10400
10491
  const requestedAccess = /* @__PURE__ */ new Map();
10401
10492
  const requestedDigests = /* @__PURE__ */ new Map();
10402
10493
  for (const item of items) {
10403
- if (item.sessionAssembly || item.status === "completed") continue;
10494
+ if (item.status === "blocked" || item.sessionAssembly || !item.sessionMembership) continue;
10495
+ const destination = inventory.works.find((work) => work.logicalWorkId === item.workId);
10496
+ if (destination?.access === "removed") throw new ApplicationError("WORK_REMOVED");
10497
+ for (const key of item.sessionMembership.keys) {
10498
+ const access = requestedAccess.get(key);
10499
+ if (access !== void 0 && access !== item.access) return true;
10500
+ requestedAccess.set(key, item.access);
10501
+ const source = inventory.works.find((work) => work.sessions.some((entry) => entry.key === key));
10502
+ if (source && source.logicalWorkId !== item.workId && source.access !== item.access) return true;
10503
+ if (destination && destination.access !== item.access && !destination.sessions.some((entry) => entry.key === key)) return true;
10504
+ }
10505
+ }
10506
+ for (const item of items) {
10507
+ if (item.sessionAssembly || item.sessionMembership || ["completed", "blocked"].includes(item.status)) continue;
10404
10508
  const destination = inventory.works.find((work) => work.logicalWorkId === item.workId);
10405
10509
  if (destination?.access === "removed") throw new ApplicationError("WORK_REMOVED");
10406
10510
  for await (const session of sessions(journeyId, item.artifact, signal)) {
@@ -10420,14 +10524,14 @@ async function sessionAccessBlocked(dependencies, journeyId, items, inventory, s
10420
10524
  }
10421
10525
  /** Keep overlapping session ownership serial through membership, including retained members. */
10422
10526
  async function sessionIndependentItems(dependencies, journeyId, items, inventory, signal) {
10423
- const sessions = dependencies.artifacts.sessions;
10527
+ const sessions = dependencies.artifacts.sessionFacts ?? dependencies.artifacts.sessions;
10424
10528
  if (!sessions) return [];
10425
10529
  const pending = items.filter((item) => item.status === "uploading" || item.status === "ready").toSorted((left, right) => Number(right.status === "uploading") - Number(left.status === "uploading") || right.artifact.byteLength - left.artifact.byteLength);
10426
10530
  const claimed = /* @__PURE__ */ new Set();
10427
10531
  const independent = [];
10428
10532
  for (const item of pending) {
10429
10533
  const keys = /* @__PURE__ */ new Set([...inventory.works.find((work) => work.logicalWorkId === item.workId)?.sessions.map((session) => session.key) ?? [], ...item.sessionMembership?.keys ?? []]);
10430
- for await (const session of sessions(journeyId, item.artifact, signal)) keys.add(await sessionKeyFor(session));
10534
+ if (!item.sessionMembership && !item.sessionAssembly) for await (const session of sessions(journeyId, item.artifact, signal)) keys.add(await sessionKeyFor(session));
10431
10535
  if (![...keys].some((key) => claimed.has(key))) independent.push(item);
10432
10536
  for (const key of keys) claimed.add(key);
10433
10537
  }
@@ -11188,7 +11292,7 @@ function renewed(item, error) {
11188
11292
  //#endregion
11189
11293
  //#region apps/cli/src/account/journey/session-step.ts
11190
11294
  /** Reconcile against completed server facts on every retry, including after process loss. */
11191
- async function stepSessionWork(dependencies, journeyId, context, item, connection, signal) {
11295
+ async function stepSessionWork(dependencies, journeyId, context, item, connection, signal, checkpoint) {
11192
11296
  const remote = connection.sessions;
11193
11297
  const sessions = dependencies.artifacts.sessions;
11194
11298
  if (!remote || !sessions) throw new ApplicationError("INVALID_STATE");
@@ -11267,7 +11371,6 @@ async function stepSessionWork(dependencies, journeyId, context, item, connectio
11267
11371
  throw error;
11268
11372
  }
11269
11373
  }
11270
- const assembly = await remote.assembly(item.workId, signal);
11271
11374
  const inventory = await remote.inventory(signal);
11272
11375
  const stored = new Map([...inventory.unassigned, ...inventory.works.flatMap((work) => work.sessions)].map((session) => [session.key, session]));
11273
11376
  const keys = [];
@@ -11283,13 +11386,17 @@ async function stepSessionWork(dependencies, journeyId, context, item, connectio
11283
11386
  lastAt: session.lastAt,
11284
11387
  harness: session.harness
11285
11388
  };
11286
- if (item.sessionUpload?.key !== key || item.sessionUpload.sha256 !== session.sha256) return {
11287
- ...item,
11288
- sessionUpload: {
11289
- key,
11290
- sha256: session.sha256
11291
- }
11292
- };
11389
+ if (item.sessionUpload?.key !== key || item.sessionUpload.sha256 !== session.sha256) {
11390
+ await guard();
11391
+ item = {
11392
+ ...item,
11393
+ sessionUpload: {
11394
+ key,
11395
+ sha256: session.sha256
11396
+ }
11397
+ };
11398
+ await checkpoint(item);
11399
+ }
11293
11400
  await guard();
11294
11401
  try {
11295
11402
  await remote.complete({
@@ -11311,13 +11418,14 @@ async function stepSessionWork(dependencies, journeyId, context, item, connectio
11311
11418
  }
11312
11419
  }
11313
11420
  await guard();
11314
- return {
11421
+ item = {
11315
11422
  ...item,
11316
11423
  sessionUpload: null
11317
11424
  };
11318
11425
  }
11319
11426
  await guard();
11320
- const current = inventory.works.find((work) => work.logicalWorkId === item.workId);
11427
+ const assembly = await remote.assembly(item.workId, signal);
11428
+ const current = (await remote.inventory(signal)).works.find((work) => work.logicalWorkId === item.workId);
11321
11429
  if (current && current.access !== item.access) {
11322
11430
  const resolution = await connection.resolve(resolveInput(item, context.bindingId), signal);
11323
11431
  const [candidate] = resolution.candidates;
@@ -11558,9 +11666,12 @@ async function captureProfile(connection, guard, signal, failure) {
11558
11666
  //#region apps/cli/src/account/journey/advance.ts
11559
11667
  /** A Work Passport is done with: assessed, or stopped unless Resume retries it. */
11560
11668
  const settled = (item) => ["completed", "blocked"].includes(item.status);
11669
+ /** Stored state equality: key order and explicit undefined fields do not count. */
11670
+ const sameJson = (left, right) => isDeepStrictEqual(JSON.parse(JSON.stringify(left)), JSON.parse(JSON.stringify(right)));
11561
11671
  /** The stored Send still holds exactly this Work state, whatever else changed meanwhile. */
11562
11672
  function sameItem(items, item) {
11563
- return JSON.stringify(items.find((value) => value.workId === item.workId)) === JSON.stringify(item);
11673
+ const current = items.find((value) => value.workId === item.workId);
11674
+ return current !== void 0 && sameJson(current, item);
11564
11675
  }
11565
11676
  /** Shared sign-in, account and per-item rules for native Send and Manual upload. */
11566
11677
  async function advanceItems(host, signal) {
@@ -11679,7 +11790,7 @@ async function advanceItems(host, signal) {
11679
11790
  status: "sign-in",
11680
11791
  error: null
11681
11792
  });
11682
- if (JSON.stringify(items) === JSON.stringify(send.items) && send.error === error) return host.applied();
11793
+ if (sameJson(items, send.items) && send.error === error) return host.applied();
11683
11794
  return host.save(aggregate({
11684
11795
  ...send,
11685
11796
  error,
@@ -11737,7 +11848,7 @@ function completeAtUpload(send, sessions) {
11737
11848
  };
11738
11849
  }
11739
11850
  async function advanceSend(dependencies, address, signal, chain) {
11740
- const snapshot = requireSend(await dependencies.storage.loadSend(address.journeyId, address.sendId));
11851
+ let snapshot = requireSend(await dependencies.storage.loadSend(address.journeyId, address.sendId));
11741
11852
  if (chain) chain.revision = snapshot.send.revision;
11742
11853
  if ((await dependencies.storage.loadSend(address.journeyId))?.send?.id !== snapshot.send.id) return {
11743
11854
  ...snapshot,
@@ -11748,13 +11859,36 @@ async function advanceSend(dependencies, address, signal, chain) {
11748
11859
  ...snapshot,
11749
11860
  status: "applied"
11750
11861
  };
11751
- const save = (next) => !completed ? saveSend(dependencies, snapshot, completeAtUpload(next, usesSessionSend(dependencies, next))) : JSON.stringify(next.items) === JSON.stringify(snapshot.send.items) ? Promise.resolve({
11752
- ...snapshot,
11753
- status: "applied"
11754
- }) : saveSend(dependencies, snapshot, {
11755
- ...snapshot.send,
11756
- items: next.items
11757
- });
11862
+ const checkpoints = /* @__PURE__ */ new Map();
11863
+ const fulfilled = /* @__PURE__ */ new Set();
11864
+ let saving = Promise.resolve();
11865
+ async function active(item) {
11866
+ const current = requireSend(await dependencies.storage.loadSend(address.journeyId));
11867
+ if (current.send.id !== snapshot.send.id || !sameItem(current.send.items, checkpoints.get(item.workId) ?? item) || current.send.status === "paused" || current.journey.phase === "paused") throw new ApplicationError("REVISION_CONFLICT");
11868
+ assertScope(current.journey, current.send);
11869
+ return current;
11870
+ }
11871
+ const save = (proposed) => {
11872
+ const next = {
11873
+ ...proposed,
11874
+ items: proposed.items.map((item) => {
11875
+ const checkpoint = checkpoints.get(item.workId);
11876
+ return checkpoint && !fulfilled.has(item.workId) ? {
11877
+ ...checkpoint,
11878
+ ...item,
11879
+ sessionUpload: checkpoint.sessionUpload
11880
+ } : item;
11881
+ })
11882
+ };
11883
+ return !completed ? saveSend(dependencies, snapshot, completeAtUpload(next, usesSessionSend(dependencies, next))) : sameJson(next.items, snapshot.send.items) ? Promise.resolve({
11884
+ ...snapshot,
11885
+ status: "applied"
11886
+ }) : saveSend(dependencies, snapshot, {
11887
+ ...snapshot.send,
11888
+ items: next.items
11889
+ });
11890
+ };
11891
+ const send = withoutQuestions(snapshot.send);
11758
11892
  try {
11759
11893
  assertScope(snapshot.journey, snapshot.send);
11760
11894
  } catch (error) {
@@ -11776,67 +11910,94 @@ async function advanceSend(dependencies, address, signal, chain) {
11776
11910
  ...snapshot,
11777
11911
  status: "applied"
11778
11912
  };
11779
- const send = withoutQuestions(snapshot.send);
11780
11913
  if (send !== snapshot.send) return save(send);
11781
11914
  let independentItems = [];
11782
- return advanceItems({
11783
- send,
11784
- failure: dependencies.failure,
11785
- ...usesSessionSend(dependencies, send) ? {
11786
- batch: () => {
11787
- const limit = send.parallelCompletes ? 3 : 1;
11788
- return independentItems.length ? pickBatch(independentItems, Date.now(), limit) : pickBatch(send.items, Date.now(), limit);
11789
- },
11790
- preflight: async (connection, preflightSignal) => {
11791
- if (!connection.sessions) throw new ApplicationError("INVALID_STATE");
11792
- const inventory = await connection.sessions.inventory(preflightSignal);
11793
- if (!await sessionAccessBlocked(dependencies, snapshot.journey.journeyId, send.items, inventory, preflightSignal)) {
11794
- independentItems = await sessionIndependentItems(dependencies, snapshot.journey.journeyId, send.items, inventory, preflightSignal);
11795
- return null;
11796
- }
11797
- await connection.assertCurrent(preflightSignal);
11798
- return save({
11799
- ...send,
11800
- status: "blocked",
11801
- items: send.items.map((item) => item.sessionAssembly || item.status === "completed" ? item : {
11802
- ...item,
11915
+ const preparation = createSessionPreparation(dependencies);
11916
+ const cleanup = createCleanup();
11917
+ try {
11918
+ return await advanceItems({
11919
+ send,
11920
+ failure: dependencies.failure,
11921
+ ...usesSessionSend(dependencies, send) ? {
11922
+ batch: () => {
11923
+ const limit = send.parallelCompletes ? 3 : 1;
11924
+ return independentItems.length ? pickBatch(independentItems, Date.now(), limit) : pickBatch(send.items, Date.now(), limit);
11925
+ },
11926
+ preflight: async (connection, preflightSignal) => {
11927
+ if (!connection.sessions) throw new ApplicationError("INVALID_STATE");
11928
+ if (!send.items.some((item) => item.status === "uploading" && !item.sessionAssembly && !item.receipt && !item.reservation)) return null;
11929
+ const inventory = await connection.sessions.inventory(preflightSignal);
11930
+ if (!await sessionAccessBlocked(preparation.dependencies, snapshot.journey.journeyId, send.items, inventory, preflightSignal)) {
11931
+ independentItems = await sessionIndependentItems(preparation.dependencies, snapshot.journey.journeyId, send.items, inventory, preflightSignal);
11932
+ return null;
11933
+ }
11934
+ await connection.assertCurrent(preflightSignal);
11935
+ return save({
11936
+ ...send,
11803
11937
  status: "blocked",
11804
- error: "SESSION_MOVE_ACCESS_REQUIRED"
11805
- })
11806
- });
11807
- },
11808
- step: (context, item, connection, stepSignal) => stepSessionWork(dependencies, snapshot.journey.journeyId, context, item, connection, stepSignal)
11809
- } : {},
11810
- connect: chain?.connect ?? dependencies.auth.connect,
11811
- profile: (connection, profileSignal) => prepareProfile(dependencies, snapshot, connection, profileSignal),
11812
- context: (item) => {
11813
- const binding = snapshot.journey.works.find((value) => value.id === item.workId)?.binding;
11814
- return {
11815
- snapshot: true,
11816
- ...chain ? { fresh: chain.fresh } : {},
11817
- active: async () => {
11818
- const current = requireSend(await dependencies.storage.loadSend(snapshot.journey.journeyId));
11819
- if (current.send.id !== snapshot.send.id || !sameItem(current.send.items, item) || current.send.status === "paused" || current.journey.phase === "paused") throw new ApplicationError("REVISION_CONFLICT");
11820
- assertScope(current.journey, current.send);
11938
+ items: send.items.map((item) => item.sessionAssembly || item.status === "completed" ? item : {
11939
+ ...item,
11940
+ status: "blocked",
11941
+ error: "SESSION_MOVE_ACCESS_REQUIRED"
11942
+ })
11943
+ });
11821
11944
  },
11822
- bindingId: binding?.subject === send.subject ? binding.bundleRecordId : void 0,
11823
- upload: async (artifact, upload, uploadSignal) => {
11824
- const started = performance.now();
11825
- await dependencies.artifacts.upload(snapshot.journey.journeyId, artifact, upload, uploadSignal);
11826
- dependencies.uploaded?.(artifact.workId, artifact.byteLength, Math.round(performance.now() - started));
11827
- }
11828
- };
11829
- },
11830
- save,
11831
- applied: () => ({
11832
- ...snapshot,
11833
- status: "applied"
11834
- }),
11835
- stale: async () => ({
11836
- ...requireSend(await dependencies.storage.loadSend(address.journeyId, address.sendId)),
11837
- status: "stale"
11838
- })
11839
- }, signal);
11945
+ step: (context, item, connection, stepSignal) => stepSessionWork(preparation.dependencies, snapshot.journey.journeyId, context, item, connection, stepSignal, (next) => {
11946
+ const pending = saving.then(async () => {
11947
+ const current = await active(item);
11948
+ if (current.send.revision !== snapshot.send.revision || current.journey.revision !== snapshot.journey.revision) throw new ApplicationError("REVISION_CONFLICT");
11949
+ stepSignal.throwIfAborted();
11950
+ const result = await saveSend(dependencies, current, {
11951
+ ...current.send,
11952
+ items: current.send.items.map((value) => value.workId === next.workId ? next : value)
11953
+ });
11954
+ if (result.status === "stale") throw new ApplicationError("REVISION_CONFLICT");
11955
+ snapshot = requireSend(result);
11956
+ const saved = snapshot.send.items.find((value) => value.workId === next.workId);
11957
+ if (!saved) throw new ApplicationError("INVALID_STATE");
11958
+ checkpoints.set(next.workId, saved);
11959
+ });
11960
+ saving = pending.catch(() => void 0);
11961
+ return pending;
11962
+ }).then((result) => {
11963
+ fulfilled.add(item.workId);
11964
+ return result;
11965
+ })
11966
+ } : {},
11967
+ connect: chain?.connect ?? dependencies.auth.connect,
11968
+ profile: (connection, profileSignal) => prepareProfile(dependencies, snapshot, connection, profileSignal),
11969
+ context: (item) => {
11970
+ const binding = snapshot.journey.works.find((value) => value.id === item.workId)?.binding;
11971
+ return {
11972
+ snapshot: true,
11973
+ ...chain ? { fresh: chain.fresh } : {},
11974
+ active: async () => {
11975
+ await active(item);
11976
+ },
11977
+ bindingId: binding?.subject === send.subject ? binding.bundleRecordId : void 0,
11978
+ upload: async (artifact, upload, uploadSignal) => {
11979
+ const started = performance.now();
11980
+ await dependencies.artifacts.upload(snapshot.journey.journeyId, artifact, upload, uploadSignal);
11981
+ dependencies.uploaded?.(artifact.workId, artifact.byteLength, Math.round(performance.now() - started));
11982
+ }
11983
+ };
11984
+ },
11985
+ save,
11986
+ applied: () => ({
11987
+ ...snapshot,
11988
+ status: "applied"
11989
+ }),
11990
+ stale: async () => ({
11991
+ ...requireSend(await dependencies.storage.loadSend(address.journeyId, address.sendId)),
11992
+ status: "stale"
11993
+ })
11994
+ }, signal);
11995
+ } catch (error) {
11996
+ cleanup.failed(error);
11997
+ throw error;
11998
+ } finally {
11999
+ await cleanup.run(() => preparation.close());
12000
+ }
11840
12001
  }
11841
12002
  /** The first wait after a bare refusal of staged bytes, doubled per refusal up to the cap. */
11842
12003
  const busyBaseMs = 2e3;
@@ -15210,7 +15371,13 @@ async function createRuntime(options, callbacks = {}) {
15210
15371
  let bundles;
15211
15372
  if (import.meta.url.endsWith(".ts")) {
15212
15373
  traceStartup("worker-build-start");
15213
- bundles = await (await import(new URL("../../../scripts/workers/build.ts", import.meta.url).href)).buildWorkerBundles();
15374
+ const shared = process.env["SESS_PILOT_TEST_WORKER_BUNDLES"];
15375
+ if (shared) bundles = {
15376
+ directory: shared,
15377
+ url: (name) => pathToFileURL(join(shared, `${name}.mjs`)),
15378
+ dispose: async () => {}
15379
+ };
15380
+ else bundles = await (await import(new URL("../../../scripts/workers/build.ts", import.meta.url).href)).buildWorkerBundles();
15214
15381
  traceStartup("worker-build-end");
15215
15382
  }
15216
15383
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitroll/tacitt-cli",
3
- "version": "0.6.1",
3
+ "version": "0.6.2",
4
4
  "description": "Private local session review and export for coding agents",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {