@gitroll/tacitt-cli 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/README.md +84 -2
  2. package/bin/INDEX.md +9 -0
  3. package/bin/tacitt.cjs +16 -0
  4. package/dist/agent-setup-B8DAiQ6Z.mjs +71 -0
  5. package/dist/agent-setup-worker.mjs +56 -0
  6. package/dist/cli.mjs +1442 -0
  7. package/dist/codex-title-worker.mjs +215 -0
  8. package/dist/configured-preview-worker.mjs +133 -0
  9. package/dist/configured-session-worker.mjs +117 -0
  10. package/dist/frame-waS5FnN5.mjs +84 -0
  11. package/dist/heap-1FYhGds4.mjs +75 -0
  12. package/dist/heap-BHcvxWCw.mjs +7265 -0
  13. package/dist/inspection-worker.mjs +57 -0
  14. package/dist/job-J3NFw3MH.mjs +194 -0
  15. package/dist/locate-U3-nkird.mjs +10670 -0
  16. package/dist/messages-D3m3bhva.mjs +40 -0
  17. package/dist/model-BJdTLNjT.mjs +41 -0
  18. package/dist/operation-executor.mjs +181 -0
  19. package/dist/preview-worker.mjs +22 -0
  20. package/dist/privacy-validator.mjs +46 -0
  21. package/dist/query-BA0XCwnR.mjs +14869 -0
  22. package/dist/run-BeLHsZWb.mjs +916 -0
  23. package/dist/session-process.mjs +90 -0
  24. package/dist/src-B5SArpVY.mjs +6601 -0
  25. package/dist/src-CC95LYYp.mjs +12985 -0
  26. package/dist/src-k0Ya1Bri.mjs +16422 -0
  27. package/dist/store-database-worker.mjs +14 -0
  28. package/dist/web/THIRD_PARTY_LICENSES.md +388 -0
  29. package/dist/web/assets/ibm-plex-mono-cyrillic-400-normal-BSMlKf0J.woff2 +0 -0
  30. package/dist/web/assets/ibm-plex-mono-cyrillic-400-normal-CEL4l2ZJ.woff +0 -0
  31. package/dist/web/assets/ibm-plex-mono-cyrillic-500-normal-Ael50iVv.woff +0 -0
  32. package/dist/web/assets/ibm-plex-mono-cyrillic-500-normal-Bq9vWWag.woff2 +0 -0
  33. package/dist/web/assets/ibm-plex-mono-cyrillic-ext-400-normal-DMdlQ8Kv.woff +0 -0
  34. package/dist/web/assets/ibm-plex-mono-cyrillic-ext-400-normal-xuaO2J-f.woff2 +0 -0
  35. package/dist/web/assets/ibm-plex-mono-cyrillic-ext-500-normal-BIfNGwUT.woff +0 -0
  36. package/dist/web/assets/ibm-plex-mono-cyrillic-ext-500-normal-BqneJy0T.woff2 +0 -0
  37. package/dist/web/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
  38. package/dist/web/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
  39. package/dist/web/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
  40. package/dist/web/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
  41. package/dist/web/assets/ibm-plex-mono-latin-ext-400-normal-BmRBH3aV.woff2 +0 -0
  42. package/dist/web/assets/ibm-plex-mono-latin-ext-400-normal-D3D2R8hC.woff +0 -0
  43. package/dist/web/assets/ibm-plex-mono-latin-ext-500-normal-CAhNIIs5.woff2 +0 -0
  44. package/dist/web/assets/ibm-plex-mono-latin-ext-500-normal-CZ70TYgx.woff +0 -0
  45. package/dist/web/assets/ibm-plex-mono-vietnamese-400-normal-BulugwFq.woff2 +0 -0
  46. package/dist/web/assets/ibm-plex-mono-vietnamese-400-normal-DDuiU_S-.woff +0 -0
  47. package/dist/web/assets/ibm-plex-mono-vietnamese-500-normal-C8zxqsMH.woff +0 -0
  48. package/dist/web/assets/ibm-plex-mono-vietnamese-500-normal-DZ4AoWbu.woff2 +0 -0
  49. package/dist/web/assets/ibm-plex-sans-cyrillic-400-normal-BTotfTJu.woff +0 -0
  50. package/dist/web/assets/ibm-plex-sans-cyrillic-400-normal-DZqxrq2p.woff2 +0 -0
  51. package/dist/web/assets/ibm-plex-sans-cyrillic-500-normal-ByOcLdNv.woff +0 -0
  52. package/dist/web/assets/ibm-plex-sans-cyrillic-500-normal-CocWQlwt.woff2 +0 -0
  53. package/dist/web/assets/ibm-plex-sans-cyrillic-600-normal-71GNu3SW.woff2 +0 -0
  54. package/dist/web/assets/ibm-plex-sans-cyrillic-600-normal-BGq0mW3O.woff +0 -0
  55. package/dist/web/assets/ibm-plex-sans-cyrillic-700-normal-D_uavGvn.woff2 +0 -0
  56. package/dist/web/assets/ibm-plex-sans-cyrillic-700-normal-RFb1v-CK.woff +0 -0
  57. package/dist/web/assets/ibm-plex-sans-cyrillic-ext-400-normal-Dsrv2Tcn.woff +0 -0
  58. package/dist/web/assets/ibm-plex-sans-cyrillic-ext-400-normal-g30qAdWV.woff2 +0 -0
  59. package/dist/web/assets/ibm-plex-sans-cyrillic-ext-500-normal-Cs5J6C77.woff2 +0 -0
  60. package/dist/web/assets/ibm-plex-sans-cyrillic-ext-500-normal-DB5PtV2g.woff +0 -0
  61. package/dist/web/assets/ibm-plex-sans-cyrillic-ext-600-normal-Bz0x94Yp.woff +0 -0
  62. package/dist/web/assets/ibm-plex-sans-cyrillic-ext-600-normal-DUMzJB7m.woff2 +0 -0
  63. package/dist/web/assets/ibm-plex-sans-cyrillic-ext-700-normal-BmRLXWpJ.woff2 +0 -0
  64. package/dist/web/assets/ibm-plex-sans-cyrillic-ext-700-normal-DCQwxkRk.woff +0 -0
  65. package/dist/web/assets/ibm-plex-sans-greek-400-normal-D9ESIMu3.woff +0 -0
  66. package/dist/web/assets/ibm-plex-sans-greek-400-normal-_efipK4i.woff2 +0 -0
  67. package/dist/web/assets/ibm-plex-sans-greek-500-normal-CuWXN6rf.woff +0 -0
  68. package/dist/web/assets/ibm-plex-sans-greek-500-normal-JMMifIXV.woff2 +0 -0
  69. package/dist/web/assets/ibm-plex-sans-greek-600-normal-D-CqTdkO.woff +0 -0
  70. package/dist/web/assets/ibm-plex-sans-greek-600-normal-DzTrcv_p.woff2 +0 -0
  71. package/dist/web/assets/ibm-plex-sans-greek-700-normal-CkZN0sRy.woff +0 -0
  72. package/dist/web/assets/ibm-plex-sans-greek-700-normal-wyJ6jzdA.woff2 +0 -0
  73. package/dist/web/assets/ibm-plex-sans-latin-400-normal-CDDApCn2.woff2 +0 -0
  74. package/dist/web/assets/ibm-plex-sans-latin-400-normal-CYLoc0-x.woff +0 -0
  75. package/dist/web/assets/ibm-plex-sans-latin-500-normal-6ng42L7E.woff2 +0 -0
  76. package/dist/web/assets/ibm-plex-sans-latin-500-normal-BgVn5rGT.woff +0 -0
  77. package/dist/web/assets/ibm-plex-sans-latin-600-normal-Cu4Hd6ag.woff +0 -0
  78. package/dist/web/assets/ibm-plex-sans-latin-600-normal-CuJfVYMP.woff2 +0 -0
  79. package/dist/web/assets/ibm-plex-sans-latin-700-normal-Bth3BMcD.woff +0 -0
  80. package/dist/web/assets/ibm-plex-sans-latin-700-normal-Bxkt5Cjx.woff2 +0 -0
  81. package/dist/web/assets/ibm-plex-sans-latin-ext-400-normal-C5H60-Va.woff2 +0 -0
  82. package/dist/web/assets/ibm-plex-sans-latin-ext-400-normal-RBey6euL.woff +0 -0
  83. package/dist/web/assets/ibm-plex-sans-latin-ext-500-normal-D0aIdm-b.woff +0 -0
  84. package/dist/web/assets/ibm-plex-sans-latin-ext-500-normal-DakdToA3.woff2 +0 -0
  85. package/dist/web/assets/ibm-plex-sans-latin-ext-600-normal-DIrixKbi.woff +0 -0
  86. package/dist/web/assets/ibm-plex-sans-latin-ext-600-normal-DOrvGEcy.woff2 +0 -0
  87. package/dist/web/assets/ibm-plex-sans-latin-ext-700-normal-DZH6Xh-4.woff +0 -0
  88. package/dist/web/assets/ibm-plex-sans-latin-ext-700-normal-aMgXseD4.woff2 +0 -0
  89. package/dist/web/assets/ibm-plex-sans-vietnamese-400-normal-DG4YqDda.woff2 +0 -0
  90. package/dist/web/assets/ibm-plex-sans-vietnamese-400-normal-fK1oJ5dG.woff +0 -0
  91. package/dist/web/assets/ibm-plex-sans-vietnamese-500-normal-BEb3_waV.woff +0 -0
  92. package/dist/web/assets/ibm-plex-sans-vietnamese-500-normal-e4dixQRQ.woff2 +0 -0
  93. package/dist/web/assets/ibm-plex-sans-vietnamese-600-normal-DgdngZtN.woff +0 -0
  94. package/dist/web/assets/ibm-plex-sans-vietnamese-600-normal-DpPYBSTl.woff2 +0 -0
  95. package/dist/web/assets/ibm-plex-sans-vietnamese-700-normal-BV5B8Are.woff +0 -0
  96. package/dist/web/assets/ibm-plex-sans-vietnamese-700-normal-CkEo6LDy.woff2 +0 -0
  97. package/dist/web/assets/index-Bjos73wK.js +67 -0
  98. package/dist/web/assets/index-Bl4C2b2_.css +1 -0
  99. package/dist/web/index.html +14 -0
  100. package/package.json +56 -3
  101. package/skills/INDEX.md +9 -0
  102. package/skills/tacitt/INDEX.md +11 -0
  103. package/skills/tacitt/SKILL.md +44 -0
  104. package/skills/tacitt/references/INDEX.md +15 -0
  105. package/skills/tacitt/references/b-method.md +22 -0
  106. package/skills/tacitt/references/bootstrap.md +109 -0
  107. package/skills/tacitt/references/classification.md +83 -0
  108. package/skills/tacitt/references/commands.md +147 -0
  109. package/skills/tacitt/references/journey.md +54 -0
  110. package/skills/tacitt/references/parallel-review.md +64 -0
  111. package/skills/tacitt/references/send.md +30 -0
  112. package/skills/tacitt/scripts/INDEX.md +43 -0
  113. package/skills/tacitt/scripts/answer.mjs +278 -0
  114. package/skills/tacitt/scripts/describe.mjs +55 -0
  115. package/skills/tacitt/scripts/journey-card-consolidation.d.mts +17 -0
  116. package/skills/tacitt/scripts/journey-card-consolidation.mjs +209 -0
  117. package/skills/tacitt/scripts/journey-classification-metadata.d.mts +16 -0
  118. package/skills/tacitt/scripts/journey-classification-metadata.mjs +111 -0
  119. package/skills/tacitt/scripts/journey-classification-progress.mjs +195 -0
  120. package/skills/tacitt/scripts/journey-classification-tasks.d.mts +18 -0
  121. package/skills/tacitt/scripts/journey-classification-tasks.mjs +345 -0
  122. package/skills/tacitt/scripts/journey-classification-validation.d.mts +20 -0
  123. package/skills/tacitt/scripts/journey-classification-validation.mjs +240 -0
  124. package/skills/tacitt/scripts/journey-classification.mjs +145 -0
  125. package/skills/tacitt/scripts/journey-consolidation-submit.mjs +227 -0
  126. package/skills/tacitt/scripts/journey-consolidation.d.mts +14 -0
  127. package/skills/tacitt/scripts/journey-consolidation.mjs +227 -0
  128. package/skills/tacitt/scripts/journey-scan-timing.mjs +143 -0
  129. package/skills/tacitt/scripts/journey-state.d.mts +43 -0
  130. package/skills/tacitt/scripts/journey-state.mjs +294 -0
  131. package/skills/tacitt/scripts/journey-tasks.d.mts +26 -0
  132. package/skills/tacitt/scripts/journey-tasks.mjs +185 -0
  133. package/skills/tacitt/scripts/journey.d.mts +40 -0
  134. package/skills/tacitt/scripts/journey.mjs +592 -0
  135. package/skills/tacitt/scripts/local-cli.d.mts +18 -0
  136. package/skills/tacitt/scripts/local-cli.mjs +116 -0
  137. package/skills/tacitt/scripts/privacy-validator.mjs +10369 -0
  138. package/skills/tacitt/scripts/review-stream-client.d.mts +22 -0
  139. package/skills/tacitt/scripts/review-stream-client.mjs +194 -0
  140. package/skills/tacitt/scripts/review.d.mts +87 -0
  141. package/skills/tacitt/scripts/review.mjs +1610 -0
  142. package/skills/tacitt/scripts/verify-cli.mjs +10 -0
package/dist/cli.mjs ADDED
@@ -0,0 +1,1442 @@
1
+ #!/usr/bin/env node
2
+ import { _ as createExecutionPort, a as readAgentPresence, d as parseOptions, f as businessCommands, g as readStateDump, h as startupFailure, i as invoke, l as createRenderActivity, m as permissionDiagnostic, n as readOperation, o as createRuntime, p as longCommands, r as waitOperation, s as executorEventSchema, t as cancelOperation, u as helpText, v as startHost, y as traceStartup } from "./query-BA0XCwnR.mjs";
3
+ import { et as makePrivateDirectory, tt as openPrivateFile } from "./src-k0Ya1Bri.mjs";
4
+ import { Gt as safeField, Ht as ApplicationError, Wt as safeCode } from "./locate-U3-nkird.mjs";
5
+ import { Qr as stateSchema, er as reviewTransportBytes, rt as readWorkspaceOutcomes, ut as resolveFailureLocation } from "./src-CC95LYYp.mjs";
6
+ import { gt as isCoordinationBusy } from "./heap-BHcvxWCw.mjs";
7
+ import { constants, createReadStream, write } from "node:fs";
8
+ import { z } from "zod";
9
+ import { lstat, mkdir, mkdtemp, open, readFile, readdir, realpath, rename, rm, writeFile } from "node:fs/promises";
10
+ import { spawn } from "node:child_process";
11
+ import { dirname, isAbsolute, join, resolve } from "node:path";
12
+ import { createInterface } from "node:readline";
13
+ import { fileURLToPath } from "node:url";
14
+ import { createHash, randomUUID } from "node:crypto";
15
+ import { setTimeout as setTimeout$1 } from "node:timers/promises";
16
+ import { monitorEventLoopDelay, performance as performance$1 } from "node:perf_hooks";
17
+ import { homedir } from "node:os";
18
+ import open$1 from "open";
19
+ import { parse, patch } from "@decimalturn/toml-patch";
20
+ import { applyEdits, modify } from "jsonc-parser";
21
+ //#region apps/cli/src/host-timing.ts
22
+ function available(read) {
23
+ try {
24
+ return read();
25
+ } catch {
26
+ return null;
27
+ }
28
+ }
29
+ function sample() {
30
+ const monotonicMs = performance$1.now();
31
+ const epochMs = Date.now();
32
+ const loop = available(() => performance$1.eventLoopUtilization());
33
+ return {
34
+ monotonicMs,
35
+ epochMs,
36
+ processCpu: available(() => process.cpuUsage()),
37
+ threadCpu: available(() => process.threadCpuUsage()),
38
+ loop: loop && loop.active + loop.idle > 0 ? loop : null
39
+ };
40
+ }
41
+ function cpuDelta(current, previous) {
42
+ if (!current || !previous || current.user < previous.user || current.system < previous.system) return null;
43
+ return {
44
+ userMs: (current.user - previous.user) / 1e3,
45
+ systemMs: (current.system - previous.system) / 1e3
46
+ };
47
+ }
48
+ function loopDelta(current, previous) {
49
+ if (!current || !previous || current.active < previous.active || current.idle < previous.idle) return null;
50
+ const delta = available(() => performance$1.eventLoopUtilization(current, previous));
51
+ return delta ? {
52
+ activeMs: delta.active,
53
+ idleMs: delta.idle
54
+ } : null;
55
+ }
56
+ /** Existing-heartbeat observations only; callers keep the timer and shutdown lifetime. */
57
+ function createHostTimingSampler(write = (line) => {
58
+ process.stderr.write(line);
59
+ }) {
60
+ let previous = available(sample);
61
+ let detectedLags = 0;
62
+ let observationFailed = previous === null;
63
+ let emissionFailed = false;
64
+ return {
65
+ observe(summary) {
66
+ const current = available(sample);
67
+ const baseline = previous;
68
+ previous = current;
69
+ if (!current) observationFailed = true;
70
+ if (!current || !baseline) return;
71
+ const lagMs = current.monotonicMs - baseline.monotonicMs - 1e3;
72
+ if (lagMs <= 2e3) return;
73
+ detectedLags += 1;
74
+ const processCpu = cpuDelta(current.processCpu, baseline.processCpu);
75
+ const threadCpu = cpuDelta(current.threadCpu, baseline.threadCpu);
76
+ const loop = loopDelta(current.loop, baseline.loop);
77
+ const store = summary ? {
78
+ executor: summary.executor,
79
+ lastAccessId: summary.lastAccessId,
80
+ dequeuedAccessId: summary.dequeuedAccessId,
81
+ queueDepth: summary.queueDepth,
82
+ pending: summary.pending ? {
83
+ accessId: summary.pending.accessId,
84
+ operation: summary.pending.operation,
85
+ write: summary.pending.write,
86
+ phase: summary.pending.phase,
87
+ queueDepth: summary.pending.queueDepth,
88
+ queueMs: summary.pending.queueMs,
89
+ activeMs: summary.pending.activeMs,
90
+ phaseMs: summary.pending.phaseMs
91
+ } : null
92
+ } : null;
93
+ return {
94
+ event: "HOST_TIMER_LAG",
95
+ lagMs: Math.round(lagMs),
96
+ interval: {
97
+ monotonicStartMs: baseline.monotonicMs,
98
+ monotonicEndMs: current.monotonicMs,
99
+ epochStartMs: baseline.epochMs,
100
+ epochEndMs: current.epochMs
101
+ },
102
+ processCpu,
103
+ threadCpu,
104
+ loop,
105
+ availability: {
106
+ processCpu: processCpu !== null,
107
+ threadCpu: threadCpu !== null,
108
+ loop: loop !== null,
109
+ store: store !== null
110
+ },
111
+ store
112
+ };
113
+ },
114
+ unavailable() {
115
+ observationFailed = true;
116
+ previous = null;
117
+ },
118
+ emit(value) {
119
+ try {
120
+ write(JSON.stringify(value) + "\n");
121
+ } catch {
122
+ emissionFailed = true;
123
+ }
124
+ },
125
+ receipt: () => ({
126
+ detectedLags,
127
+ observationFailed,
128
+ emissionFailed
129
+ })
130
+ };
131
+ }
132
+ //#endregion
133
+ //#region apps/cli/src/ownership-diagnostic.ts
134
+ /** A failed diagnostic destination cannot change the inconclusive probe result. */
135
+ function reportOwnershipCheckDeferred() {
136
+ try {
137
+ write(process.stderr.fd, `${JSON.stringify({
138
+ event: "HOST_OWNERSHIP_CHECK_DEFERRED",
139
+ code: "COORDINATION_BUSY"
140
+ })}\n`, (_error) => {});
141
+ } catch {}
142
+ }
143
+ async function serve(options, journeyId) {
144
+ let initializing;
145
+ let shutdownPhase = "application-and-account";
146
+ let stopping;
147
+ let host;
148
+ let ownerTimer;
149
+ let finishTimer;
150
+ let observedStore = void 0;
151
+ const timing = process.env["CI"] ? createHostTimingSampler() : void 0;
152
+ const timingTimer = process.env["CI"] ? setInterval(() => {
153
+ try {
154
+ let summary;
155
+ try {
156
+ summary = observedStore?.accessSummary();
157
+ } catch {
158
+ summary = void 0;
159
+ }
160
+ const lag = timing?.observe(summary);
161
+ if (lag) timing?.emit(lag);
162
+ if (summary?.pending && summary.pending.activeMs >= 2e3) timing?.emit({
163
+ event: "STORE_ACCESS_PENDING",
164
+ executor: summary.executor,
165
+ ...summary.pending
166
+ });
167
+ } catch {
168
+ timing?.unavailable();
169
+ }
170
+ }, 1e3) : void 0;
171
+ timingTimer?.unref();
172
+ const loopDelay = process.env["CI"] ? monitorEventLoopDelay({ resolution: 20 }) : void 0;
173
+ loopDelay?.enable();
174
+ const stop = () => stopping ??= (async () => {
175
+ if (timingTimer) clearInterval(timingTimer);
176
+ if (loopDelay) try {
177
+ loopDelay.disable();
178
+ const ms = (nanoseconds) => Math.round(nanoseconds / 1e5) / 10;
179
+ timing?.emit({
180
+ event: "HOST_EVENT_LOOP_DELAY",
181
+ executor: runtime.store.accessSummary()?.executor,
182
+ maxMs: ms(loopDelay.max),
183
+ p99Ms: ms(loopDelay.percentile(99)),
184
+ meanMs: ms(loopDelay.mean),
185
+ timing: timing.receipt()
186
+ });
187
+ } catch {
188
+ timing?.unavailable();
189
+ }
190
+ const deadline = setTimeout(() => {
191
+ process.stderr.write(`${JSON.stringify({
192
+ event: "HOST_SHUTDOWN_FAILURE",
193
+ reason: "deadline",
194
+ phase: shutdownPhase
195
+ })}\n`);
196
+ process.exit(1);
197
+ }, 5e3);
198
+ if (ownerTimer) clearInterval(ownerTimer);
199
+ if (finishTimer) clearInterval(finishTimer);
200
+ host?.beginStop();
201
+ try {
202
+ await runtime.close();
203
+ } finally {
204
+ try {
205
+ await host?.stop();
206
+ } finally {
207
+ clearTimeout(deadline);
208
+ }
209
+ }
210
+ })();
211
+ const scheduleStop = async (code) => {
212
+ if (code) {
213
+ process.stderr.write(`${code}: this instance is stopping.\n`);
214
+ process.exitCode = 1;
215
+ }
216
+ setTimeout(() => {
217
+ stop().catch(stopFailed);
218
+ }, 100);
219
+ };
220
+ const runtime = await createRuntime(options, {
221
+ stopHost: scheduleStop,
222
+ settleStartup: async () => {
223
+ await initializing;
224
+ },
225
+ closeHost: async () => {
226
+ await host?.stop();
227
+ },
228
+ onClosePhase: (phase) => {
229
+ shutdownPhase = phase;
230
+ }
231
+ });
232
+ observedStore = runtime.store;
233
+ const stopFailed = (error) => {
234
+ process.stderr.write(`${JSON.stringify({
235
+ event: "HOST_SHUTDOWN_FAILURE",
236
+ reason: "rejection",
237
+ code: safeCode(error)
238
+ })}\n`);
239
+ process.exitCode = 1;
240
+ };
241
+ const onSignal = () => {
242
+ if (stopping) process.exit(130);
243
+ stop().catch(stopFailed);
244
+ };
245
+ try {
246
+ await runtime.application.getSensitiveTerms();
247
+ if (journeyId && !await runtime.application.journey?.get({ journeyId })) throw new ApplicationError("INVALID_TRANSITION");
248
+ traceStartup("host-start");
249
+ host = await startHost({
250
+ application: runtime.application,
251
+ account: runtime.account,
252
+ nativeInspection: runtime.nativeInspection,
253
+ agentPresence: (id) => readAgentPresence(runtime.stateDir, id),
254
+ onFatal: (code) => {
255
+ scheduleStop(code);
256
+ },
257
+ onPermissionFailure(error, stage) {
258
+ process.stderr.write(`${JSON.stringify({
259
+ event: "host.permission_check_failed",
260
+ code: safeCode(error),
261
+ stage,
262
+ cause: permissionDiagnostic(error)
263
+ })}\n`);
264
+ },
265
+ onServerFailure: (failure, error) => runtime.diagnostics.onHostFailure(failure, error),
266
+ webRoot: fileURLToPath(new URL("./web/", import.meta.url)),
267
+ ...process.env["CI"] ? { currentViewStoreSummary: () => runtime.store.accessSummary() } : {},
268
+ ...options.dev ? { devRoot: fileURLToPath(new URL("../../../", import.meta.url)) } : {}
269
+ });
270
+ traceStartup("host-ready");
271
+ let checking = false;
272
+ ownerTimer = setInterval(() => {
273
+ if (checking || stopping) return;
274
+ checking = true;
275
+ runtime.application.checkOwnership().catch((error) => {
276
+ if (isCoordinationBusy(error)) {
277
+ reportOwnershipCheckDeferred();
278
+ return;
279
+ }
280
+ return scheduleStop(safeCode(error));
281
+ }).finally(() => {
282
+ checking = false;
283
+ });
284
+ }, 1e3);
285
+ process.on("SIGINT", onSignal);
286
+ process.on("SIGTERM", onSignal);
287
+ if (journeyId) {
288
+ let endedAt;
289
+ finishTimer = setInterval(() => {
290
+ runtime.application.journey?.get({ journeyId }).then((journey) => {
291
+ endedAt = journey?.phase === "completed" || journey?.abandonedAt !== void 0 ? endedAt ?? Date.now() : void 0;
292
+ if (endedAt !== void 0 && Date.now() - endedAt >= 18e5) scheduleStop();
293
+ }).catch(() => {});
294
+ }, 6e4);
295
+ finishTimer.unref();
296
+ const url = new URL(host.url);
297
+ url.searchParams.set("view", "skill");
298
+ url.searchParams.set("journey", journeyId);
299
+ process.stdout.write(`${JSON.stringify({
300
+ ok: true,
301
+ event: "ready",
302
+ result: {
303
+ url: url.href,
304
+ journeyId
305
+ }
306
+ })}\n`);
307
+ } else {
308
+ process.stdout.write(`Open this private URL to review sessions:\n${host.url}\nBundle library: ${runtime.library.path}${runtime.library.source === "fallback" ? ` (fallback from ${runtime.library.fallbackFrom})` : ""}\nLocal diagnostic logs: ${join(runtime.stateDir, "diagnostics")}\n`);
309
+ initializing = runtime.application.initialize().catch((error) => {
310
+ if (stopping) {
311
+ process.stderr.write(`${JSON.stringify({
312
+ event: "host.initialization_interrupted",
313
+ code: safeCode(error)
314
+ })}\n`);
315
+ return;
316
+ }
317
+ return scheduleStop(safeCode(error));
318
+ });
319
+ }
320
+ if (!journeyId && !options["no-open"]) try {
321
+ await open$1(host.url, { wait: false });
322
+ } catch {
323
+ process.stderr.write("The browser could not be opened. Use the URL printed above.\n");
324
+ }
325
+ await new Promise((resolve) => {
326
+ const observe = setInterval(() => {
327
+ if (stopping) {
328
+ clearInterval(observe);
329
+ stopping.then(resolve, resolve);
330
+ }
331
+ }, 100);
332
+ });
333
+ } catch (error) {
334
+ await stop();
335
+ throw error;
336
+ } finally {
337
+ process.removeListener("SIGINT", onSignal);
338
+ process.removeListener("SIGTERM", onSignal);
339
+ }
340
+ }
341
+ //#endregion
342
+ //#region apps/cli/src/package-identity.ts
343
+ /** Release identity stays independent of producer/processing format versions. */
344
+ const packageIdentity = Object.freeze(z.object({
345
+ name: z.literal("@gitroll/tacitt-cli"),
346
+ version: z.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/)
347
+ }).parse(JSON.parse(await readFile(new URL("../package.json", import.meta.url), "utf8"))));
348
+ //#endregion
349
+ //#region apps/cli/skills/tacitt/scripts/local-cli.mjs
350
+ const fail = () => {
351
+ throw new Error("LOCAL_CLI_BINDING_INVALID");
352
+ };
353
+ const object$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
354
+ const versionPattern = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
355
+ const digest$1 = (bytes) => createHash("sha256").update(bytes).digest("hex");
356
+ async function regularFile(path, limit) {
357
+ const before = await lstat(path);
358
+ if (!before.isFile() || before.isSymbolicLink() || before.size > limit) fail();
359
+ const handle = await open(path, "r");
360
+ try {
361
+ const opened = await handle.stat();
362
+ if (opened.dev !== before.dev || opened.ino !== before.ino) fail();
363
+ const bytes = await handle.readFile();
364
+ const after = await lstat(path);
365
+ if (!after.isFile() || after.isSymbolicLink() || after.dev !== before.dev || after.ino !== before.ino || after.size !== before.size || after.mtimeMs !== before.mtimeMs || bytes.length !== before.size) fail();
366
+ return bytes;
367
+ } finally {
368
+ await handle.close();
369
+ }
370
+ }
371
+ async function packageFiles(root) {
372
+ const files = [];
373
+ let bytes = 0;
374
+ const visit = async (name, depth = 0) => {
375
+ if (depth > 16 || files.length >= 1e4) fail();
376
+ const path = join(root, name);
377
+ const info = await lstat(path);
378
+ if (info.isSymbolicLink()) fail();
379
+ if (info.isDirectory()) for (const child of (await readdir(path)).sort()) await visit(`${name}/${child}`, depth + 1);
380
+ else if (info.isFile()) {
381
+ const content = await regularFile(path, 536870912 - bytes);
382
+ bytes += content.length;
383
+ files.push([
384
+ name,
385
+ content.length,
386
+ digest$1(content)
387
+ ]);
388
+ } else fail();
389
+ };
390
+ for (const name of [
391
+ "package.json",
392
+ "bin",
393
+ "dist",
394
+ "skills"
395
+ ]) await visit(name);
396
+ return files;
397
+ }
398
+ /** Reads only the selected installed package; it never downloads or opens application state. */
399
+ async function readLocalCliBinding(cliPath, expected) {
400
+ if (typeof cliPath !== "string" || !isAbsolute(cliPath) || cliPath.split("").some((character) => character.charCodeAt(0) < 32 || character.charCodeAt(0) === 127) || !object$1(expected) || expected.name !== "@gitroll/tacitt-cli" || typeof expected.version !== "string" || !versionPattern.test(expected.version)) fail();
401
+ const requested = await lstat(cliPath);
402
+ if (!requested.isFile() || requested.isSymbolicLink()) fail();
403
+ const executable = await realpath(cliPath);
404
+ const root = dirname(dirname(executable));
405
+ if (executable !== join(root, "bin", "tacitt.cjs")) fail();
406
+ const metadataBytes = await regularFile(join(root, "package.json"), 1048576);
407
+ const metadata = JSON.parse(metadataBytes.toString("utf8"));
408
+ if (!object$1(metadata) || metadata.name !== expected.name || metadata.version !== expected.version || !object$1(metadata.bin) || metadata.bin["tacitt"] !== "bin/tacitt.cjs") fail();
409
+ const files = await packageFiles(root);
410
+ if (!files.some(([name]) => name === "dist/cli.mjs") || !files.some(([name, , sha256]) => name === "package.json" && sha256 === digest$1(metadataBytes))) fail();
411
+ return {
412
+ schemaVersion: 1,
413
+ package: expected.name,
414
+ version: expected.version,
415
+ nodePath: await realpath(process.execPath),
416
+ cliPath: executable,
417
+ artifactSha256: digest$1(JSON.stringify(files))
418
+ };
419
+ }
420
+ //#endregion
421
+ //#region apps/cli/src/skill-install/local.ts
422
+ /** Only an explicit installed package can replace the public npx invocation. */
423
+ async function localCliBinding(cliPath, identity) {
424
+ if (cliPath === void 0) return null;
425
+ if (!isAbsolute(cliPath)) throw new ApplicationError("INVALID_ARGUMENTS");
426
+ try {
427
+ return await readLocalCliBinding(cliPath, identity);
428
+ } catch (cause) {
429
+ throw new ApplicationError("ARTIFACT_INVALID_MANIFEST", void 0, { cause });
430
+ }
431
+ }
432
+ function invocation(args) {
433
+ if (process.platform === "win32") return `& ${args.map((value) => `'${value.replaceAll("'", "''")}'`).join(" ")}`;
434
+ return args.map((value) => `'${value.replaceAll("'", "'\\''")}'`).join(" ");
435
+ }
436
+ function inlineCode(value) {
437
+ const longest = Math.max(0, ...[...value.matchAll(/`+/gu)].map((match) => match[0].length));
438
+ const delimiter = "`".repeat(longest + 1);
439
+ return longest ? `${delimiter} ${value} ${delimiter}` : `${delimiter}${value}${delimiter}`;
440
+ }
441
+ function localCliInstructions(binding, directory, receiptSha256) {
442
+ const root = join(directory, "releases", binding.version);
443
+ const verifier = invocation([
444
+ binding.nodePath,
445
+ join(root, "scripts", "verify-cli.mjs"),
446
+ join(root, "cli-binding.json"),
447
+ receiptSha256
448
+ ]);
449
+ const prefix = invocation([binding.nodePath, binding.cliPath]);
450
+ const shell = process.platform === "win32" ? "PowerShell" : "POSIX shell";
451
+ const fence = "`".repeat(Math.max(3, ...[...verifier.matchAll(/`+/gu)].map((match) => match[0].length + 1)));
452
+ return `Local candidate artifact SHA-256: \`${binding.artifactSha256}\`. Before any business call, run the read-only verifier in a ${shell} exactly as printed; preserve every absolute path:\n\n${fence}sh\n${verifier}\n${fence}\n\nRequire exit zero and stdout \`LOCAL_CLI_VERIFIED\`, then run ${inlineCode(`${prefix} --version`)} from the person's workspace and require stdout \`${binding.version}\`. Every subsequent CLI call and focused host uses the exact prefix ${inlineCode(prefix)} for this entire journey. Do not fetch this unpublished candidate from npm, substitute another executable or run \`skill.install\` yourself. A failed check stops setup before source access: tell the person this local candidate is missing or changed and needs a deliberate reinstall.`;
453
+ }
454
+ //#endregion
455
+ //#region apps/cli/src/skill-install/release.ts
456
+ const versionSchema = z.string().regex(/^\d+\.\d+\.\d+(?:-[\w.-]+)?(?:\+[\w.-]+)?$/);
457
+ const digestSchema = z.string().regex(/^[a-f0-9]{64}$/);
458
+ const manifestSchema = z.object({
459
+ schemaVersion: z.literal(1),
460
+ package: z.string(),
461
+ version: versionSchema,
462
+ entrySha256: digestSchema,
463
+ files: z.record(z.string(), digestSchema)
464
+ }).strict();
465
+ function collision(cause) {
466
+ throw new ApplicationError("ARTIFACT_DESTINATION_EXISTS", void 0, { cause });
467
+ }
468
+ function digest(value) {
469
+ return createHash("sha256").update(value).digest("hex");
470
+ }
471
+ async function exists(path) {
472
+ try {
473
+ return await lstat(path);
474
+ } catch (error) {
475
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") return null;
476
+ throw error;
477
+ }
478
+ }
479
+ async function filesAt(directory, prefix = "") {
480
+ const info = await lstat(directory);
481
+ if (!info.isDirectory() || info.isSymbolicLink()) collision();
482
+ const files = /* @__PURE__ */ new Map();
483
+ for (const item of (await readdir(directory, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
484
+ const path = join(directory, item.name);
485
+ const key = `${prefix}${item.name}`;
486
+ if (item.isDirectory()) for (const [name, value] of await filesAt(path, `${key}/`)) files.set(name, value);
487
+ else if (item.isFile()) files.set(key, await readFile(path));
488
+ else collision();
489
+ }
490
+ return files;
491
+ }
492
+ function fileDigests(files) {
493
+ return Object.fromEntries([...files].map(([name, value]) => [name, digest(value)]));
494
+ }
495
+ /** Bind examples to release assets without changing the person's workspace cwd. */
496
+ function helperCommands(markdown, root, nodePath) {
497
+ const quote = (value) => `'${value.replaceAll("'", process.platform === "win32" ? "''" : "'\\''")}'`;
498
+ return markdown.replace(/(`+)([\s\S]*?)\1(?!`)/gu, (span, ticks, body) => {
499
+ const rendered = body.replace(/\bnode scripts\/([\w-]+\.mjs)\b/gu, (_match, name) => `${process.platform === "win32" ? "& " : ""}${quote(nodePath)} ${quote(join(root, "scripts", name))}`);
500
+ if (rendered === body) return span;
501
+ const delimiter = "`".repeat(Math.max(ticks.length, ...[...rendered.matchAll(/`+/gu)].map((match) => match[0].length + 1)));
502
+ return `${delimiter}${rendered}${delimiter}`;
503
+ });
504
+ }
505
+ /** Renders one entry; supporting releases deliberately contain no nested SKILL.md. */
506
+ async function createRelease(identity, source, directory, local) {
507
+ if (!isAbsolute(directory)) throw new ApplicationError("INVALID_ARGUMENTS");
508
+ versionSchema.parse(identity.version);
509
+ if (!/^@[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._-]*$/.test(identity.name)) throw new ApplicationError("INVALID_ARGUMENTS");
510
+ const template = await readFile(join(source, "SKILL.md"), "utf8");
511
+ const marker = /<!-- sess-pilot-release -->[\s\S]*?<!-- \/sess-pilot-release -->/;
512
+ if (!marker.test(template)) throw new ApplicationError("ARTIFACT_INVALID_MANIFEST");
513
+ const prefix = `npx --yes ${identity.name}@${identity.version}`;
514
+ const receipt = local === void 0 ? null : `${JSON.stringify(local.binding)}\n`;
515
+ const instructions = local !== void 0 && receipt !== null ? localCliInstructions(local.binding, local.directory, digest(receipt)) : `Before any business call, run \`${prefix} --version\` from the person's workspace and require stdout \`${identity.version}\`; this CLI check is always the first command. Every subsequent CLI call and focused host uses the exact prefix \`${prefix}\` for this entire journey. Never run \`skill.install\` yourself: if the check fails, stop before source access and give the person \`${prefix} skill.install\` to reinstall this release.`;
516
+ const root = join(directory, "releases", identity.version);
517
+ const render = (value) => helperCommands(value, root, local?.binding.nodePath ?? "node");
518
+ const entry = render(template).replaceAll("](references/", `](releases/${identity.version}/references/`).replaceAll("](scripts/", `](releases/${identity.version}/scripts/`).replace(marker, () => `<!-- sess-pilot-managed:${identity.version} -->\n\nCLI release: \`${identity.name}@${identity.version}\`. ${instructions}`);
519
+ const files = await filesAt(join(source, "references"), "references/");
520
+ for (const [name, value] of files) if (name.endsWith(".md")) files.set(name, Buffer.from(render(value.toString("utf8")).replaceAll("](../SKILL.md", "](../../../SKILL.md")));
521
+ const scripts = join(source, "scripts");
522
+ if (await exists(scripts)) for (const [name, value] of await filesAt(scripts, "scripts/")) files.set(name, value);
523
+ if (receipt !== null) files.set("cli-binding.json", Buffer.from(receipt));
524
+ return {
525
+ identity,
526
+ entry,
527
+ files,
528
+ manifest: `${JSON.stringify({
529
+ schemaVersion: 1,
530
+ package: identity.name,
531
+ version: identity.version,
532
+ entrySha256: digest(entry),
533
+ files: fileDigests(files)
534
+ })}\n`
535
+ };
536
+ }
537
+ async function writeRelease(directory, release) {
538
+ const root = join(directory, "releases", release.identity.version);
539
+ await mkdir(root, { recursive: true });
540
+ for (const [name, value] of release.files) {
541
+ const path = join(root, name);
542
+ await mkdir(dirname(path), { recursive: true });
543
+ await writeFile(path, value, { flag: "wx" });
544
+ }
545
+ await writeFile(join(root, "manifest.json"), release.manifest, { flag: "wx" });
546
+ await writeFile(join(directory, "SKILL.md"), release.entry, { flag: "wx" });
547
+ }
548
+ /** Hashes all owned bytes before replacement and refuses foreign or edited files. */
549
+ /** Earlier names of the package whose installed releases a new release may update (ADR 0209). */
550
+ const formerPackageNames = ["@gitroll/tacitt"];
551
+ async function verifyInstallation(directory, packageName) {
552
+ const info = await exists(directory);
553
+ if (info === null) return null;
554
+ if (!info.isDirectory() || info.isSymbolicLink()) collision();
555
+ if ((await readdir(directory)).sort().join("\n") !== "SKILL.md\nreleases") collision();
556
+ const entryPath = join(directory, "SKILL.md");
557
+ const entryInfo = await lstat(entryPath);
558
+ if (!entryInfo.isFile() || entryInfo.isSymbolicLink()) collision();
559
+ const entry = await readFile(entryPath, "utf8");
560
+ const match = /<!-- sess-pilot-managed:([^\s]+) -->/.exec(entry);
561
+ const currentVersion = versionSchema.safeParse(match?.[1]);
562
+ if (!currentVersion.success) collision();
563
+ const releaseRoot = join(directory, "releases");
564
+ const releaseInfo = await lstat(releaseRoot);
565
+ if (!releaseInfo.isDirectory() || releaseInfo.isSymbolicLink()) collision();
566
+ const releases = /* @__PURE__ */ new Map();
567
+ for (const version of await readdir(releaseRoot)) {
568
+ if (!versionSchema.safeParse(version).success) collision();
569
+ const files = await filesAt(join(releaseRoot, version));
570
+ const manifestBytes = files.get("manifest.json");
571
+ if (manifestBytes === void 0) collision();
572
+ files.delete("manifest.json");
573
+ let parsed;
574
+ try {
575
+ parsed = JSON.parse(manifestBytes.toString("utf8"));
576
+ } catch (error) {
577
+ collision(error);
578
+ }
579
+ const manifest = manifestSchema.safeParse(parsed);
580
+ if (!manifest.success) collision(manifest.error);
581
+ const value = manifest.data;
582
+ if (value.package !== packageName && !formerPackageNames.includes(value.package) || value.version !== version) collision();
583
+ const actual = fileDigests(files);
584
+ if (Object.keys(actual).length !== Object.keys(value.files).length || Object.entries(actual).some(([name, hash]) => value.files[name] !== hash)) collision();
585
+ if (version === currentVersion.data && value.entrySha256 !== digest(entry)) collision();
586
+ releases.set(version, manifestBytes.toString("utf8"));
587
+ }
588
+ if (!releases.has(currentVersion.data)) collision();
589
+ return {
590
+ entry,
591
+ releases
592
+ };
593
+ }
594
+ //#endregion
595
+ //#region apps/cli/src/skill-install.ts
596
+ const installSchema = z.object({
597
+ agent: z.enum([
598
+ "codex",
599
+ "claude",
600
+ "all"
601
+ ]).default("all"),
602
+ scope: z.enum(["user", "project"]).default("user"),
603
+ directory: z.string().min(1).max(4096).optional(),
604
+ cliPath: z.string().min(1).max(4096).optional(),
605
+ configureAgents: z.boolean().optional()
606
+ }).strict().refine((input) => input.directory === void 0 || input.scope === "project");
607
+ /** Installs native entries without creating or opening any Application state. */
608
+ async function installSkill(value = {}) {
609
+ const input = installSchema.parse(value);
610
+ const identity = { ...packageIdentity };
611
+ const source = fileURLToPath(new URL("../skills/tacitt/", import.meta.url));
612
+ const binding = await localCliBinding(input.cliPath, identity);
613
+ const agents = input.agent === "all" ? ["codex", "claude"] : [input.agent];
614
+ const root = input.scope === "user" ? homedir() : resolve(input.directory ?? process.cwd());
615
+ const installs = [];
616
+ for (const agent of agents) {
617
+ const directory = join(root, agent === "codex" ? ".agents" : ".claude", "skills", "tacitt");
618
+ const release = await createRelease(identity, source, directory, binding === null ? void 0 : {
619
+ binding,
620
+ directory
621
+ });
622
+ installs.push({
623
+ agent,
624
+ directory,
625
+ status: await installRelease(directory, release)
626
+ });
627
+ }
628
+ return {
629
+ skill: "tacitt",
630
+ package: identity.name,
631
+ version: identity.version,
632
+ ...binding === null ? {} : { localCli: binding },
633
+ installs
634
+ };
635
+ }
636
+ async function installRelease(directory, release) {
637
+ const parent = dirname(directory);
638
+ await mkdir(parent, { recursive: true });
639
+ const coordinationRoot = join(dirname(parent), ".sess-pilot-installer");
640
+ try {
641
+ await makePrivateDirectory(coordinationRoot);
642
+ } catch (error) {
643
+ if (!(typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST")) throw error;
644
+ }
645
+ const claim = await createExecutionPort(coordinationRoot).tryAcquire("workflow");
646
+ if (claim === null) throw new ApplicationError("COORDINATION_BUSY");
647
+ let staging;
648
+ let primary;
649
+ const failures = [];
650
+ let status = "installed";
651
+ try {
652
+ const previous = await verifyInstallation(directory, release.identity.name);
653
+ const existingRelease = previous?.releases.get(release.identity.version);
654
+ if (existingRelease !== void 0 && existingRelease !== release.manifest) throw new ApplicationError("ARTIFACT_DESTINATION_EXISTS");
655
+ if (previous?.entry === release.entry) status = "unchanged";
656
+ else {
657
+ staging = await mkdtemp(join(coordinationRoot, ".sess-pilot-install-"));
658
+ await writeRelease(staging, release);
659
+ if (previous === null) {
660
+ await rename(staging, directory);
661
+ staging = void 0;
662
+ } else {
663
+ status = "updated";
664
+ if (existingRelease === void 0) await rename(join(staging, "releases", release.identity.version), join(directory, "releases", release.identity.version));
665
+ if ((await verifyInstallation(directory, release.identity.name))?.entry !== previous.entry) throw new ApplicationError("ARTIFACT_DESTINATION_EXISTS");
666
+ await rename(join(staging, "SKILL.md"), join(directory, "SKILL.md"));
667
+ }
668
+ if (await readFile(join(directory, "SKILL.md"), "utf8") !== release.entry) throw new ApplicationError("ARTIFACT_IO_FAILED");
669
+ }
670
+ } catch (error) {
671
+ primary = error;
672
+ } finally {
673
+ const cleanup = await Promise.allSettled([...staging === void 0 ? [] : [rm(staging, {
674
+ recursive: true,
675
+ force: true
676
+ })], claim.release()]);
677
+ failures.push(...cleanup.flatMap((result) => result.status === "rejected" ? [result.reason] : []));
678
+ if (failures.length > 0) try {
679
+ process.stderr.write(`${JSON.stringify({
680
+ event: "skill.install_cleanup_failed",
681
+ failures: failures.length
682
+ })}\n`);
683
+ } catch (error) {
684
+ failures.push(error);
685
+ }
686
+ }
687
+ if (failures.length > 0) throw new AggregateError(primary === void 0 ? failures : [primary, ...failures], primary === void 0 ? "ARTIFACT_IO_FAILED" : safeCode(primary), { cause: primary ?? failures[0] });
688
+ if (primary !== void 0) throw primary;
689
+ return status;
690
+ }
691
+ //#endregion
692
+ //#region apps/cli/src/skill-install/config-file.ts
693
+ const maxBytes = 1048576;
694
+ const absent = (error) => error instanceof Error && "code" in error && error.code === "ENOENT";
695
+ /** Refuse symlink destinations and bound configuration reads, including growth while reading. */
696
+ async function readConfiguration(path) {
697
+ try {
698
+ const directory = await lstat(dirname(path));
699
+ if (!directory.isDirectory() || directory.isSymbolicLink()) throw new Error("AGENT_CONFIG_UNSAFE");
700
+ const entry = await lstat(path);
701
+ if (!entry.isFile() || entry.isSymbolicLink() || entry.nlink !== 1) throw new Error("AGENT_CONFIG_UNSAFE");
702
+ const file = await open(path, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
703
+ try {
704
+ const stat = await file.stat();
705
+ if (stat.ino !== entry.ino || stat.dev !== entry.dev || stat.size > maxBytes) throw new Error("AGENT_CONFIG_UNSAFE");
706
+ const chunks = [];
707
+ let bytes = 0;
708
+ for await (const chunk of file.createReadStream({ autoClose: false })) {
709
+ const part = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
710
+ bytes += part.length;
711
+ if (bytes > maxBytes) throw new Error("AGENT_CONFIG_UNSAFE");
712
+ chunks.push(part);
713
+ }
714
+ return new TextDecoder("utf-8", { fatal: true }).decode(Buffer.concat(chunks));
715
+ } finally {
716
+ await file.close();
717
+ }
718
+ } catch (error) {
719
+ if (absent(error)) return null;
720
+ throw error;
721
+ }
722
+ }
723
+ /** Stage privately, refuse observed concurrent edits, then publish and read back exact bytes. */
724
+ async function saveConfiguration(path, previous, next) {
725
+ if (Buffer.byteLength(next, "utf8") > maxBytes) throw new Error("AGENT_CONFIG_UNSAFE");
726
+ await mkdir(dirname(path), {
727
+ recursive: true,
728
+ mode: 448
729
+ });
730
+ if (await readConfiguration(path) !== previous) throw new Error("AGENT_CONFIG_CHANGED");
731
+ const coordination = join(dirname(path), ".sess-pilot-installer");
732
+ try {
733
+ await makePrivateDirectory(coordination);
734
+ } catch (error) {
735
+ if (!(error instanceof Error && "code" in error && error.code === "EEXIST")) throw error;
736
+ }
737
+ const claim = await createExecutionPort(coordination).tryAcquire("workflow");
738
+ if (claim === null) throw new Error("AGENT_CONFIG_BUSY");
739
+ const temporary = join(dirname(path), `.sess-pilot-config-${randomUUID()}`);
740
+ let primary;
741
+ const failures = [];
742
+ try {
743
+ const file = await openPrivateFile(temporary, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL);
744
+ try {
745
+ await file.writeFile(next);
746
+ await file.sync();
747
+ } finally {
748
+ await file.close();
749
+ }
750
+ if (await readConfiguration(path) !== previous) throw new Error("AGENT_CONFIG_CHANGED");
751
+ await rename(temporary, path);
752
+ if (await readConfiguration(path) !== next) throw new Error("AGENT_CONFIG_READBACK_FAILED");
753
+ } catch (cause) {
754
+ primary = cause;
755
+ } finally {
756
+ const cleanup = await Promise.allSettled([rm(temporary, { force: true }), claim.release()]);
757
+ failures.push(...cleanup.flatMap((result) => result.status === "rejected" ? [result.reason] : []));
758
+ }
759
+ if (failures.length) throw new AggregateError(primary === void 0 ? failures : [primary, ...failures], "AGENT_CONFIG_FAILED", { cause: primary ?? failures[0] });
760
+ if (primary !== void 0) throw primary;
761
+ }
762
+ const object = z.record(z.string(), z.unknown());
763
+ /** Edit only the count; preserve unrelated values and the existing TOML representation. */
764
+ function configureText(agent, source) {
765
+ try {
766
+ const data = object.parse(source.trim() ? agent === "codex" ? parse(source) : JSON.parse(source) : {});
767
+ const key = agent === "codex" ? "agents" : "env";
768
+ const section = data[key] === void 0 ? {} : object.parse(data[key]);
769
+ const field = agent === "claude" ? "CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS" : Object.hasOwn(section, "max_concurrent_threads_per_session") || !Object.hasOwn(section, "max_threads") ? "max_concurrent_threads_per_session" : "max_threads";
770
+ const current = section[field];
771
+ const count = current === void 0 ? void 0 : agent === "claude" ? z.string().regex(/^\d+$/).transform(Number).pipe(z.number().int().positive().safe()).parse(current) : z.number().int().positive().safe().parse(current);
772
+ if (count !== void 0 && count >= 16) return {
773
+ text: source,
774
+ count
775
+ };
776
+ const target = agent === "claude" ? String(16) : 16;
777
+ const updated = {
778
+ ...data,
779
+ [key]: {
780
+ ...section,
781
+ [field]: target
782
+ }
783
+ };
784
+ const text = agent === "codex" ? patch(source, updated) : applyEdits(source.trim() ? source : "{}\n", modify(source.trim() ? source : "{}\n", ["env", field], target, { formattingOptions: {
785
+ insertSpaces: true,
786
+ tabSize: 2
787
+ } }));
788
+ const check = object.parse(agent === "codex" ? parse(text) : JSON.parse(text));
789
+ if (object.parse(check[key])[field] !== target) throw new Error("CONFIG_READBACK_FAILED");
790
+ return {
791
+ text,
792
+ count: 16
793
+ };
794
+ } catch (cause) {
795
+ throw new Error("AGENT_CONFIG_INVALID", { cause });
796
+ }
797
+ }
798
+ //#endregion
799
+ //#region apps/cli/src/skill-install/consent.ts
800
+ /** A closed terminal or cancellation is a refusal; npm --yes is never client-setting consent. */
801
+ async function askAgentConfiguration(clients, scope) {
802
+ const names = clients.map((agent) => agent === "codex" ? "Codex" : "Claude Code").join(" and ");
803
+ const terminal = createInterface({
804
+ input: process.stdin,
805
+ output: process.stderr,
806
+ terminal: true
807
+ });
808
+ try {
809
+ process.stderr.write(`SessPilot uses up to 16 analysis subagents for faster review.\nRaise the concurrent subagent limit to 16 for ${names}? Higher limits can increase concurrent usage.
810
+ ` + (scope === "user" ? "This changes your client settings across projects.\n" : "This changes client settings for this project.\n") + "Save your work and fully restart the affected App or CLI afterward. We will not close it for you.\nAllow this change? [y/N] ");
811
+ return await new Promise((resolve) => {
812
+ terminal.once("line", (line) => resolve(/^y(?:es)?$/i.test(line.trim())));
813
+ terminal.once("close", () => resolve(false));
814
+ terminal.once("SIGINT", () => resolve(false));
815
+ });
816
+ } finally {
817
+ terminal.close();
818
+ }
819
+ }
820
+ //#endregion
821
+ //#region apps/cli/src/skill-install/agent-config.ts
822
+ const settings = z.object({
823
+ scope: z.enum(["user", "project"]).default("user"),
824
+ directory: z.string().optional(),
825
+ configureAgents: z.boolean().optional()
826
+ });
827
+ /** Installer-only configuration; no client is launched or treated as hot-reloaded. */
828
+ async function configureInstalledAgents(value, installation, options) {
829
+ const input = settings.parse(value);
830
+ const clients = installation.installs.map(({ agent }) => z.enum(["codex", "claude"]).parse(agent));
831
+ const interactive = options.input === void 0 && Boolean(process.stdin.isTTY && process.stderr.isTTY);
832
+ if (input.configureAgents === false || input.configureAgents !== true && !interactive) return clients.map((agent) => ({
833
+ agent,
834
+ status: input.configureAgents === false ? "declined" : "skipped"
835
+ }));
836
+ const results = [];
837
+ const plans = [];
838
+ for (const agent of clients) {
839
+ const root = input.scope === "project" ? join(resolve(input.directory ?? process.cwd()), agent === "codex" ? ".codex" : ".claude") : agent === "codex" ? options.codexHome : options.claudeHome;
840
+ const path = join(root, agent === "codex" ? "config.toml" : input.scope === "project" ? "settings.local.json" : "settings.json");
841
+ try {
842
+ const previous = await readConfiguration(path);
843
+ const edit = configureText(agent, previous ?? "");
844
+ if (edit.text === previous) results.push({
845
+ agent,
846
+ status: "unchanged",
847
+ count: edit.count
848
+ });
849
+ else plans.push({
850
+ agent,
851
+ path,
852
+ previous,
853
+ edit
854
+ });
855
+ } catch (cause) {
856
+ results.push(configurationFailure(agent, cause));
857
+ }
858
+ }
859
+ if (plans.length === 0) return results;
860
+ const allowed = input.configureAgents === true || await askAgentConfiguration(plans.map(({ agent }) => agent), input.scope);
861
+ for (const plan of plans) {
862
+ if (!allowed) {
863
+ results.push({
864
+ agent: plan.agent,
865
+ status: "declined"
866
+ });
867
+ continue;
868
+ }
869
+ try {
870
+ await saveConfiguration(plan.path, plan.previous, plan.edit.text);
871
+ results.push({
872
+ agent: plan.agent,
873
+ status: "updated",
874
+ count: plan.edit.count
875
+ });
876
+ } catch (cause) {
877
+ results.push(configurationFailure(plan.agent, cause));
878
+ }
879
+ }
880
+ return results;
881
+ }
882
+ function configurationFailure(agent, cause) {
883
+ const code = cause instanceof Error && [
884
+ "AGENT_CONFIG_INVALID",
885
+ "AGENT_CONFIG_UNSAFE",
886
+ "AGENT_CONFIG_CHANGED",
887
+ "AGENT_CONFIG_BUSY",
888
+ "AGENT_CONFIG_READBACK_FAILED"
889
+ ].includes(cause.message) ? cause.message : "AGENT_CONFIG_FAILED";
890
+ process.stderr.write(`${JSON.stringify({
891
+ event: "skill.agent_config_failed",
892
+ agent,
893
+ code
894
+ })}\n`);
895
+ return {
896
+ agent,
897
+ status: "failed",
898
+ code
899
+ };
900
+ }
901
+ //#endregion
902
+ //#region apps/cli/src/skill-install/notice.ts
903
+ /** Human shorthand guidance stays outside the machine-readable stdout envelope. */
904
+ function writeInstallNotice(result) {
905
+ const commands = result.installs.map(({ agent }) => agent === "codex" ? " Codex: $tacitt" : " Claude Code: /tacitt");
906
+ const message = [
907
+ "SessPilot Skill is ready.",
908
+ ...result.agentConfiguration.flatMap(({ agent, status, count }) => {
909
+ const name = agent === "codex" ? "Codex" : "Claude Code";
910
+ if (status === "updated" || status === "unchanged") return [`${name}: concurrent subagent setting ${status === "updated" ? "saved" : "already set"} at ${count}.`, ...agent === "claude" ? ["Claude Code must be version 2.1.217 or newer for this setting. Update older versions first."] : []];
911
+ if (status === "failed") return [`${name}: settings could not be updated. Skill installed; retry configuration after resolving the reported error.`];
912
+ return [`${name}: client settings were not changed. Review uses the available slots.`];
913
+ }),
914
+ ...result.agentConfiguration.some(({ status }) => status === "updated" || status === "unchanged") ? ["Save your work, fully quit and reopen the affected App, or exit and relaunch its CLI.", "Already-open chats may retain their previous limit. No App or CLI was restarted."] : [],
915
+ ...result.agentConfiguration.some(({ status }) => status === "skipped") ? ["To allow configuration in a noninteractive install, rerun skill.install with --configure-agents."] : [],
916
+ "Open a new conversation in your installed client and enter:",
917
+ ...commands,
918
+ "Your agent will guide permissions, scanning, AI privacy review, preview and Send.",
919
+ ""
920
+ ].join("\n");
921
+ return new Promise((resolve, reject) => {
922
+ process.stderr.write(message, (error) => error ? reject(error) : resolve());
923
+ });
924
+ }
925
+ //#endregion
926
+ //#region apps/cli/src/serve-detach.ts
927
+ const readySchema = z.object({
928
+ event: z.literal("ready"),
929
+ result: z.object({
930
+ url: z.string(),
931
+ journeyId: z.string()
932
+ })
933
+ });
934
+ /** The same CLI arguments without `--detach` and with the journey read from `input`. */
935
+ function hostArguments(args, input) {
936
+ const kept = [];
937
+ for (let index = 0; index < args.length; index += 1) {
938
+ const arg = args[index];
939
+ if (arg === void 0 || arg === "--detach" || arg.startsWith("--input=")) continue;
940
+ if (arg === "--input") {
941
+ index += 1;
942
+ continue;
943
+ }
944
+ kept.push(arg);
945
+ }
946
+ return [
947
+ ...kept,
948
+ "--input",
949
+ input
950
+ ];
951
+ }
952
+ /**
953
+ * Starts `journey.serve` in its own process group, outside the agent tool that ran this command,
954
+ * so an agent host's limit on background tasks does not stop the panel mid-Send (BL-210). The
955
+ * host's output goes to an owner-only log; this command prints its readiness with the pid.
956
+ */
957
+ async function serveDetached(args, stateDir, journeyId) {
958
+ const directory = join(stateDir, "serve");
959
+ await mkdir(directory, {
960
+ recursive: true,
961
+ mode: 448
962
+ });
963
+ const id = randomUUID();
964
+ const input = join(directory, `${id}.json`);
965
+ const log = join(directory, `${id}.log`);
966
+ await writeFile(input, JSON.stringify({ journeyId }), { mode: 384 });
967
+ const output = await open(log, "w", 384);
968
+ const entry = process.argv[1];
969
+ if (!entry) throw new Error("INVALID_ARGUMENTS");
970
+ const child = spawn(process.execPath, [
971
+ ...process.execArgv,
972
+ entry,
973
+ ...hostArguments(args, input)
974
+ ], {
975
+ detached: true,
976
+ stdio: [
977
+ "ignore",
978
+ output.fd,
979
+ output.fd
980
+ ]
981
+ });
982
+ let exited = null;
983
+ child.once("exit", (code) => exited = code ?? 1);
984
+ child.unref();
985
+ await output.close();
986
+ for (let waited = 0; waited < 6e4; waited += 100) {
987
+ const ready = (await readFile(log, "utf8")).split("\n").flatMap((line) => {
988
+ try {
989
+ const value = readySchema.safeParse(JSON.parse(line));
990
+ return value.success ? [value.data.result] : [];
991
+ } catch {
992
+ return [];
993
+ }
994
+ })[0];
995
+ if (ready) {
996
+ process.stdout.write(`${JSON.stringify({
997
+ ok: true,
998
+ event: "ready",
999
+ result: {
1000
+ ...ready,
1001
+ pid: child.pid,
1002
+ log
1003
+ }
1004
+ })}\n`);
1005
+ return;
1006
+ }
1007
+ if (exited !== null) break;
1008
+ await setTimeout$1(100);
1009
+ }
1010
+ if (exited === null) child.kill();
1011
+ process.stderr.write(`The panel host did not start. Its output is in ${log}.\n`);
1012
+ process.exitCode = 1;
1013
+ }
1014
+ //#endregion
1015
+ //#region apps/cli/src/business-input.ts
1016
+ const defaultBusinessInputBytes = 1048576;
1017
+ const reviewCommands = /* @__PURE__ */ new Set([
1018
+ "review.terms",
1019
+ "journey.classificationSubmit",
1020
+ "journey.classificationProgress",
1021
+ "journey.cardsSubmit",
1022
+ "journey.resolve"
1023
+ ]);
1024
+ /** Bound bytes before parsing; only evidence- and pack-bearing commands need the larger envelope. */
1025
+ async function readBusinessInput(options) {
1026
+ if (!options.input) return null;
1027
+ const limit = reviewCommands.has(options.command ?? "") ? reviewTransportBytes : defaultBusinessInputBytes;
1028
+ const chunks = [];
1029
+ let bytes = 0;
1030
+ const stream = options.input === "-" ? process.stdin : createReadStream(options.input);
1031
+ for await (const chunk of stream) {
1032
+ const part = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk));
1033
+ bytes += part.length;
1034
+ if (bytes > limit) throw new ApplicationError("INVALID_ARGUMENTS");
1035
+ chunks.push(part);
1036
+ }
1037
+ try {
1038
+ return JSON.parse(Buffer.concat(chunks).toString("utf8"));
1039
+ } catch {
1040
+ throw new ApplicationError("INVALID_ARGUMENTS");
1041
+ }
1042
+ }
1043
+ //#endregion
1044
+ //#region apps/cli/src/output.ts
1045
+ const writeJson = (value) => new Promise((resolve, reject) => {
1046
+ process.stdout.write(`${JSON.stringify(value)}\n`, (error) => error ? reject(error) : resolve());
1047
+ });
1048
+ //#endregion
1049
+ //#region apps/cli/src/review-stream.ts
1050
+ /** Procedures the review helper sends; each runs exactly as its own `--input` command would. */
1051
+ const streamProcedures = /* @__PURE__ */ new Set([
1052
+ "journey.get",
1053
+ "journey.prepare",
1054
+ "journey.catalog",
1055
+ "journey.classificationSessions",
1056
+ "journey.classificationProgress",
1057
+ "journey.classificationRead",
1058
+ "journey.classificationSubmit",
1059
+ "journey.cardsRead",
1060
+ "journey.cardsSubmit",
1061
+ "journey.resolve",
1062
+ "journey.read",
1063
+ "review.guidance",
1064
+ "review.status",
1065
+ "review.terms"
1066
+ ]);
1067
+ const request = z.object({
1068
+ id: z.number().int().nonnegative(),
1069
+ procedure: z.string(),
1070
+ input: z.unknown()
1071
+ }).strict();
1072
+ /**
1073
+ * One runtime for many review calls: each stdin line `{id, procedure, input}` gets one stdout
1074
+ * line `{id, ok, result | error}`, in order. Saves a process start per call; ends at stdin EOF.
1075
+ */
1076
+ async function reviewStream(options) {
1077
+ let runtime;
1078
+ try {
1079
+ runtime = await createRuntime(options);
1080
+ } catch (error) {
1081
+ await writeJson({
1082
+ event: "review.stream.startup-failed",
1083
+ ok: false,
1084
+ error: {
1085
+ code: safeCode(error),
1086
+ field: safeField(error)
1087
+ }
1088
+ });
1089
+ process.exitCode = 1;
1090
+ return;
1091
+ }
1092
+ const abort = new AbortController();
1093
+ const lines = createInterface({
1094
+ input: process.stdin,
1095
+ crlfDelay: Infinity
1096
+ });
1097
+ const onSignal = () => {
1098
+ abort.abort();
1099
+ lines.close();
1100
+ };
1101
+ process.once("SIGINT", onSignal);
1102
+ process.once("SIGTERM", onSignal);
1103
+ try {
1104
+ for await (const line of lines) {
1105
+ if (!line.trim()) continue;
1106
+ let id = null;
1107
+ try {
1108
+ if (Buffer.byteLength(line) > 33554432) throw new ApplicationError("INVALID_ARGUMENTS");
1109
+ const call = request.parse(JSON.parse(line));
1110
+ id = call.id;
1111
+ if (!streamProcedures.has(call.procedure)) throw new ApplicationError("INVALID_ARGUMENTS");
1112
+ const result = await invoke(runtime, call.procedure, call.input ?? null, abort.signal);
1113
+ await writeJson({
1114
+ id,
1115
+ ok: true,
1116
+ result
1117
+ });
1118
+ } catch (error) {
1119
+ runtime.diagnostics.onFailure("command", error);
1120
+ const invalid = error instanceof z.ZodError || error instanceof SyntaxError;
1121
+ await writeJson({
1122
+ id,
1123
+ ok: false,
1124
+ error: {
1125
+ code: invalid ? "INVALID_ARGUMENTS" : safeCode(error),
1126
+ field: safeField(error)
1127
+ }
1128
+ });
1129
+ }
1130
+ }
1131
+ } finally {
1132
+ process.removeListener("SIGINT", onSignal);
1133
+ process.removeListener("SIGTERM", onSignal);
1134
+ await runtime.close();
1135
+ }
1136
+ }
1137
+ //#endregion
1138
+ //#region apps/cli/src/operation/client.ts
1139
+ async function executeLong(options, args, value) {
1140
+ const source = import.meta.url.endsWith(".ts");
1141
+ const entry = fileURLToPath(new URL(source ? "./executor.ts" : "./operation-executor.mjs", import.meta.url));
1142
+ const child = spawn(process.execPath, [...source && process.platform === "win32" ? ["--liftoff-only"] : [], entry], {
1143
+ cwd: process.cwd(),
1144
+ detached: true,
1145
+ windowsHide: true,
1146
+ stdio: [
1147
+ "ignore",
1148
+ "ignore",
1149
+ "ignore",
1150
+ "ipc"
1151
+ ]
1152
+ });
1153
+ let operationId;
1154
+ let detached = false;
1155
+ let terminal = false;
1156
+ let timer;
1157
+ const { promise: done, resolve: finish, reject } = Promise.withResolvers();
1158
+ const terminate = () => {
1159
+ if (!detached && !terminal) child.kill("SIGTERM");
1160
+ };
1161
+ process.once("SIGINT", terminate);
1162
+ process.once("SIGTERM", terminate);
1163
+ const detach = () => {
1164
+ if (!child.connected || !operationId) return;
1165
+ child.send({ detach: true }, (error) => {
1166
+ if (error) reject(error);
1167
+ });
1168
+ };
1169
+ const receive = async (event) => {
1170
+ if (terminal || detached) return;
1171
+ if (event.event === "diagnostic") {
1172
+ process.stderr.write(`${JSON.stringify(event)}\n`);
1173
+ return;
1174
+ }
1175
+ if (event.event === "progress") {
1176
+ await writeJson(event);
1177
+ return;
1178
+ }
1179
+ if (event.event === "started") {
1180
+ operationId = event.operationId;
1181
+ await writeJson(event);
1182
+ if (options.async) detach();
1183
+ else timer = globalThis.setTimeout(detach, options.timeoutMs);
1184
+ } else if (event.event === "detached") {
1185
+ detached = true;
1186
+ child.unref();
1187
+ await writeJson({
1188
+ event: options.async ? "accepted" : "timeout",
1189
+ operationId: event.operationId,
1190
+ detached: true
1191
+ });
1192
+ if (!options.async) process.exitCode = 3;
1193
+ finish();
1194
+ } else {
1195
+ terminal = true;
1196
+ await writeJson(event);
1197
+ if (event.event === "error") process.exitCode = event.code === "INVALID_ARGUMENTS" ? 2 : 1;
1198
+ else if (typeof event.result === "object" && event.result !== null && "status" in event.result && event.result.status !== "completed") process.exitCode = 1;
1199
+ finish();
1200
+ }
1201
+ };
1202
+ let receiving = Promise.resolve();
1203
+ child.on("message", (message) => {
1204
+ receiving = receiving.then(async () => {
1205
+ const event = executorEventSchema.parse(message);
1206
+ await receive(event);
1207
+ }).catch(reject);
1208
+ });
1209
+ child.once("error", reject);
1210
+ child.once("exit", () => {
1211
+ receiving.then(() => {
1212
+ if (!terminal && !detached) reject(new ApplicationError("OPERATION_FAILED"));
1213
+ });
1214
+ });
1215
+ child.send({
1216
+ command: options.command,
1217
+ input: value,
1218
+ args
1219
+ }, (error) => {
1220
+ if (error) reject(error);
1221
+ });
1222
+ try {
1223
+ await done;
1224
+ } finally {
1225
+ if (timer) clearTimeout(timer);
1226
+ process.removeListener("SIGINT", terminate);
1227
+ process.removeListener("SIGTERM", terminate);
1228
+ if (!detached && child.connected) child.disconnect();
1229
+ if (!detached && !terminal) child.kill("SIGTERM");
1230
+ }
1231
+ }
1232
+ //#endregion
1233
+ //#region apps/cli/src/business.ts
1234
+ async function business(options, args) {
1235
+ try {
1236
+ if (options.command === "review.stream") {
1237
+ await reviewStream(options);
1238
+ return;
1239
+ }
1240
+ const value = await readBusinessInput(options);
1241
+ if (options.command === "journey.serve") {
1242
+ const { journeyId } = z.object({ journeyId: z.string().uuid() }).strict().parse(value);
1243
+ if (options.detach) await serveDetached(args, options.stateDir, journeyId);
1244
+ else await serve(options, journeyId);
1245
+ return;
1246
+ }
1247
+ if (options.command === "skill.install") {
1248
+ const installation = options.input === void 0 ? {
1249
+ agent: options.agent ?? "all",
1250
+ scope: options.scope ?? "user",
1251
+ ...options["cli-path"] === void 0 ? {} : { cliPath: options["cli-path"] },
1252
+ ...options["configure-agents"] === true ? { configureAgents: true } : {},
1253
+ ...options["skip-agent-config"] === true ? { configureAgents: false } : {}
1254
+ } : value;
1255
+ const result = await installSkill(installation);
1256
+ const agentConfiguration = await configureInstalledAgents(installation, result, options);
1257
+ const complete = {
1258
+ ...result,
1259
+ agentConfiguration
1260
+ };
1261
+ const ok = !agentConfiguration.some(({ status }) => status === "failed");
1262
+ if (options.input === void 0) await writeInstallNotice(complete);
1263
+ await writeJson({
1264
+ ok,
1265
+ result: complete,
1266
+ ...!ok ? { error: { code: "AGENT_CONFIG_FAILED" } } : {}
1267
+ });
1268
+ if (!ok) process.exitCode = 1;
1269
+ return;
1270
+ }
1271
+ if (options.command && longCommands.has(options.command)) {
1272
+ await executeLong(options, args, value);
1273
+ return;
1274
+ }
1275
+ const runtime = await createRuntime(options);
1276
+ const abort = new AbortController();
1277
+ const onSignal = () => abort.abort();
1278
+ process.once("SIGINT", onSignal);
1279
+ process.once("SIGTERM", onSignal);
1280
+ let response;
1281
+ let failed = false;
1282
+ let primary;
1283
+ try {
1284
+ traceStartup("command-start");
1285
+ if (options.command === "operation.get") response = {
1286
+ ok: true,
1287
+ result: await readOperation(runtime, options.operationId ?? "")
1288
+ };
1289
+ else if (options.command === "operation.cancel") response = {
1290
+ ok: true,
1291
+ result: await cancelOperation(runtime, options.operationId ?? "")
1292
+ };
1293
+ else if (options.command === "operation.wait") {
1294
+ const result = await waitOperation(runtime, options.operationId ?? "", options.timeoutMs, abort.signal);
1295
+ response = {
1296
+ ok: !result.timedOut && result.operation.status === "completed",
1297
+ ...result
1298
+ };
1299
+ process.exitCode = result.timedOut ? 3 : result.operation.status === "completed" ? 0 : 1;
1300
+ } else if (options.command === "workflow.events") {
1301
+ const deadline = performance.now() + options.timeoutMs;
1302
+ let previous = "";
1303
+ while (performance.now() < deadline && !abort.signal.aborted) {
1304
+ const current = await runtime.application.getCurrent();
1305
+ const serialized = JSON.stringify(current);
1306
+ if (serialized !== previous) {
1307
+ await writeJson({
1308
+ event: "state",
1309
+ result: current
1310
+ });
1311
+ previous = serialized;
1312
+ }
1313
+ await setTimeout$1(Math.min(200, Math.max(1, deadline - performance.now())), void 0, { signal: abort.signal });
1314
+ }
1315
+ } else response = {
1316
+ ok: true,
1317
+ result: await invoke(runtime, options.command ?? "", value, abort.signal)
1318
+ };
1319
+ } catch (error) {
1320
+ failed = true;
1321
+ primary = error;
1322
+ runtime.diagnostics.onFailure("command", error);
1323
+ } finally {
1324
+ traceStartup("command-end");
1325
+ process.removeListener("SIGINT", onSignal);
1326
+ process.removeListener("SIGTERM", onSignal);
1327
+ try {
1328
+ await runtime.close();
1329
+ } catch (error) {
1330
+ primary = failed ? new AggregateError([primary, error], safeCode(primary), { cause: primary }) : error;
1331
+ failed = true;
1332
+ }
1333
+ }
1334
+ if (failed) throw primary;
1335
+ if (response !== void 0) await writeJson(response);
1336
+ } catch (error) {
1337
+ const code = error instanceof z.ZodError ? "INVALID_ARGUMENTS" : safeCode(error);
1338
+ const detail = startupFailure(error instanceof z.ZodError ? new ApplicationError("INVALID_ARGUMENTS", void 0, { cause: error }) : error).trim();
1339
+ process.stderr.write(`${JSON.stringify({
1340
+ event: "cli.failed",
1341
+ code,
1342
+ detail
1343
+ })}\n`);
1344
+ await writeJson({
1345
+ ok: false,
1346
+ error: {
1347
+ code,
1348
+ field: safeField(error),
1349
+ detail
1350
+ }
1351
+ });
1352
+ process.exitCode = code === "INVALID_ARGUMENTS" ? 2 : 1;
1353
+ }
1354
+ }
1355
+ //#endregion
1356
+ //#region apps/cli/src/cli.ts
1357
+ const stateDumpSchema = z.object({ state: z.unknown() });
1358
+ async function main() {
1359
+ traceStartup("entry-ready");
1360
+ process.umask(63);
1361
+ const options = parseOptions(process.argv.slice(2));
1362
+ if (options.help) {
1363
+ process.stdout.write(helpText());
1364
+ return;
1365
+ }
1366
+ if (options.version) {
1367
+ process.stdout.write(`${packageIdentity.version}\n`);
1368
+ return;
1369
+ }
1370
+ if (options.status) {
1371
+ const dump = stateDumpSchema.parse(await readStateDump(options.stateDir));
1372
+ const state = stateSchema.parse(dump.state);
1373
+ const run = state.current?.operation ? state.current : state.baseline;
1374
+ for (const activity of await createRenderActivity(options.stateDir).list()) process.stdout.write(`Journey ${activity.journeyId} is packaging ${activity.workIds.length} Works since ${activity.startedAt}\n`);
1375
+ if (!run?.operation) {
1376
+ process.stdout.write("No packaging operation is available.\n");
1377
+ return;
1378
+ }
1379
+ const outcomes = readWorkspaceOutcomes(run);
1380
+ process.stdout.write(`Packaging operation ${run.operation.id} (${run.phase})\n`);
1381
+ if (outcomes.length === 0) {
1382
+ process.stdout.write("No partial workspace outcomes are saved.\n");
1383
+ return;
1384
+ }
1385
+ for (const outcome of outcomes) {
1386
+ const title = resolveFailureLocation(run.operation.approval.inventory, {
1387
+ workspaceId: outcome.workspaceId,
1388
+ ...outcome.status === "failed" && outcome.sessionId ? { sessionId: outcome.sessionId } : {},
1389
+ ...outcome.status === "failed" && outcome.sourcePartId ? { sourcePartId: outcome.sourcePartId } : {}
1390
+ })?.workspace.name ?? "Workspace";
1391
+ const status = outcome.status === "failed" ? `${outcome.status} · ${outcome.code} · ${outcome.stage}` : outcome.status;
1392
+ const details = outcome.status === "failed" ? [outcome.sessionId && `session ${outcome.sessionId}`, outcome.sourcePartId && `source ${outcome.sourcePartId}`].filter(Boolean).join(" · ") : "";
1393
+ process.stdout.write(`${title} [${outcome.workspaceId}]: ${status}${details ? ` · ${details}` : ""}\n`);
1394
+ }
1395
+ return;
1396
+ }
1397
+ if (options.stateDump) {
1398
+ const timing = process.env["CI"] === "true" && process.env["SESS_PILOT_TEST_STATE_DUMP_PHASES"] === "1";
1399
+ let phaseStarted = performance.now();
1400
+ const reportPhase = (phase) => {
1401
+ if (timing) process.stderr.write(`STATE_DUMP_PHASE_MS ${phase} ${Math.round(performance.now() - phaseStarted)}\n`);
1402
+ phaseStarted = performance.now();
1403
+ };
1404
+ const dump = JSON.stringify(await readStateDump(options.stateDir), null, 2) + "\n";
1405
+ reportPhase("read_serialize");
1406
+ process.stderr.write("Sensitive state metadata includes local paths and session references.\n");
1407
+ if (options.output) {
1408
+ const handle = await openPrivateFile(options.output, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL);
1409
+ reportPhase("private_open");
1410
+ try {
1411
+ await handle.writeFile(dump);
1412
+ await handle.sync();
1413
+ } finally {
1414
+ await handle.close();
1415
+ }
1416
+ reportPhase("write_sync_close");
1417
+ } else process.stdout.write(dump);
1418
+ return;
1419
+ }
1420
+ if (options.command) await business(options, process.argv.slice(2));
1421
+ else await serve(options);
1422
+ }
1423
+ main().catch(async (error) => {
1424
+ if (process.argv.slice(2).find((argument) => businessCommands.some((name) => name === argument))) {
1425
+ const code = safeCode(error);
1426
+ await writeJson({
1427
+ ok: false,
1428
+ error: {
1429
+ code,
1430
+ field: safeField(error),
1431
+ detail: startupFailure(error).trim()
1432
+ }
1433
+ });
1434
+ process.stderr.write(`${code}: command failed.\n`);
1435
+ process.exitCode = code === "INVALID_ARGUMENTS" ? 2 : 1;
1436
+ } else {
1437
+ process.stderr.write(startupFailure(error));
1438
+ process.exitCode = 1;
1439
+ }
1440
+ });
1441
+ //#endregion
1442
+ export {};