@nowcrew/daemon 0.6.102 → 0.6.103

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 (60) hide show
  1. package/dist/codex-home-launch.js +7 -0
  2. package/dist/codex-home-mode-controller.js +68 -0
  3. package/dist/daemon-migration-controller.js +210 -0
  4. package/dist/daemon-migration-wiring.js +22 -0
  5. package/dist/execution-runner.js +5 -2
  6. package/dist/local-executor.js +8 -1
  7. package/dist/project-skills/capability.js +109 -0
  8. package/dist/project-skills/controller-convergence.js +57 -0
  9. package/dist/project-skills/projection-set-switch.js +419 -0
  10. package/dist/project-skills/projection-state-domain.js +170 -0
  11. package/dist/project-skills/projection-state-store.js +842 -0
  12. package/dist/project-skills/projection-state-transaction.js +322 -0
  13. package/dist/project-skills/projection-state.js +3 -0
  14. package/dist/project-skills/runtime-root-bootstrap.js +47 -0
  15. package/dist/project-skills/runtime-root-domain.js +268 -0
  16. package/dist/project-skills/runtime-root-gc.js +293 -0
  17. package/dist/project-skills/runtime-root-lease-artifact.js +46 -0
  18. package/dist/project-skills/runtime-root-leases.js +487 -0
  19. package/dist/project-skills/runtime-root-source-identity.js +60 -0
  20. package/dist/project-skills/runtime-root-startup.js +49 -0
  21. package/dist/project-skills/runtime-root-state-artifact-domain.js +143 -0
  22. package/dist/project-skills/runtime-root-state-index.js +356 -0
  23. package/dist/project-skills/runtime-root-store.js +722 -0
  24. package/dist/project-skills/serve-capability.js +28 -0
  25. package/dist/project-skills/serve-startup.js +37 -0
  26. package/dist/runtime-observation.js +219 -0
  27. package/dist/runtimes/claude-observation-runner.js +55 -0
  28. package/dist/runtimes/claude-thread-runner.js +17 -3
  29. package/dist/runtimes/claude.js +5 -3
  30. package/dist/runtimes/codex-app-server-runner.js +16 -2
  31. package/dist/runtimes/codex-home-migration-cli.js +26 -0
  32. package/dist/runtimes/codex-home-migration.js +112 -0
  33. package/dist/runtimes/codex-home.js +236 -0
  34. package/dist/runtimes/deepseek-harness-runner.js +16 -0
  35. package/dist/runtimes/kimi-acp-runner.js +19 -0
  36. package/dist/runtimes/observation-context.js +18 -0
  37. package/dist/runtimes/observation-mapper.js +314 -0
  38. package/dist/runtimes/opencode-runner.js +18 -4
  39. package/dist/runtimes/pi-runner.js +17 -1
  40. package/dist/storage-control.js +212 -0
  41. package/dist/storage-copy.js +628 -0
  42. package/dist/storage-credential-guard.js +21 -0
  43. package/dist/storage-doctor.js +228 -0
  44. package/dist/storage-manifest.js +1027 -0
  45. package/dist/storage-migration-controller.js +441 -0
  46. package/dist/storage-migration-manifest.js +105 -0
  47. package/dist/storage-migration-policy.js +100 -0
  48. package/dist/storage-runtime-controller.js +361 -0
  49. package/dist/storage-runtime-readiness.js +206 -0
  50. package/dist/storage-safe-fs.js +302 -0
  51. package/dist/storage-status-delivery.js +224 -0
  52. package/dist/storage-status-replay.js +140 -0
  53. package/dist/storage-target-confirmation.js +129 -0
  54. package/dist/storage-worker-command.js +37 -0
  55. package/dist/storage-worker-executor.js +431 -0
  56. package/dist/storage-worker-handoff.js +151 -0
  57. package/dist/storage-worker-operation.js +288 -0
  58. package/dist/storage-worker-status.js +259 -0
  59. package/dist/token.js +4 -1
  60. package/package.json +1 -1
@@ -6,6 +6,8 @@ import spawn from "cross-spawn";
6
6
  import { augmentedPath } from "../runtime-path.js";
7
7
  import { prepareThreadDelivery } from "../thread-input-delivery.js";
8
8
  import { buildOpenCodeArgs, createOpenCodeEventDecoder } from "./opencode.js";
9
+ import { createRuntimeObservationCapture } from "../runtime-observation.js";
10
+ import { createObservationMapper } from "./observation-mapper.js";
9
11
  const ERROR_CAP = 2_000;
10
12
  async function readPrompt() {
11
13
  process.stdin.setEncoding("utf8");
@@ -25,7 +27,8 @@ export function isOpenCodeSessionNotFound(message) {
25
27
  return /(?:session|conversation).*(?:not found|does not exist|unknown|invalid)|(?:not found|unknown|invalid).*(?:session|conversation)/iu
26
28
  .test(message);
27
29
  }
28
- async function runAttempt(options, prompt) {
30
+ async function runAttempt(options, prompt, capture, attempt) {
31
+ const observer = createObservationMapper("opencode", capture, `attempt:${attempt}`);
29
32
  const cwd = process.cwd();
30
33
  const child = spawn(options.bin, buildOpenCodeArgs({ cwd, ...options }), {
31
34
  cwd,
@@ -37,6 +40,7 @@ async function runAttempt(options, prompt) {
37
40
  }
38
41
  child.stderr.pipe(process.stderr, { end: false });
39
42
  const childExit = once(child, "close");
43
+ observer.context({ prompt });
40
44
  child.stdin.end(prompt);
41
45
  const decoder = createOpenCodeEventDecoder();
42
46
  const lines = createInterface({ input: child.stdout });
@@ -50,6 +54,7 @@ async function runAttempt(options, prompt) {
50
54
  catch {
51
55
  continue;
52
56
  }
57
+ observer.observe(event);
53
58
  for (const normalized of decoder.push(event)) {
54
59
  const type = normalized.type;
55
60
  const semantic = type === "opencode.text_delta"
@@ -87,17 +92,26 @@ async function runAttempt(options, prompt) {
87
92
  return { code: 0, staleSession: false };
88
93
  }
89
94
  export async function runOpenCode(options) {
95
+ const capture = createRuntimeObservationCapture("opencode");
96
+ try {
97
+ return await runOpenCodeObserved(options, capture);
98
+ }
99
+ finally {
100
+ capture.finish();
101
+ }
102
+ }
103
+ async function runOpenCodeObserved(options, capture) {
90
104
  const prompt = (await prepareThreadDelivery({ env: process.env, resumed: false,
91
105
  sessionId: "unverified-cold-opencode", wakePrompt: await readPrompt() })).prompt;
92
106
  if (process.env.CREW_THREAD_ROOT) {
93
107
  const { sessionId: _unverified, ...coldOptions } = options;
94
- return (await runAttempt(coldOptions, prompt)).code;
108
+ return (await runAttempt(coldOptions, prompt, capture, 1)).code;
95
109
  }
96
- const first = await runAttempt(options, prompt);
110
+ const first = await runAttempt(options, prompt, capture, 1);
97
111
  if (!first.staleSession)
98
112
  return first.code;
99
113
  const { sessionId: _staleSessionId, ...freshOptions } = options;
100
- return (await runAttempt(freshOptions, prompt)).code;
114
+ return (await runAttempt(freshOptions, prompt, capture, 2)).code;
101
115
  }
102
116
  function optionsFromArgv(argv) {
103
117
  const { values } = parseArgs({
@@ -6,11 +6,24 @@ import spawn from "cross-spawn";
6
6
  import { augmentedPath } from "../runtime-path.js";
7
7
  import { prepareThreadDelivery } from "../thread-input-delivery.js";
8
8
  import { buildPiArgs, createPiEventDecoder } from "./pi.js";
9
+ import { createRuntimeObservationCapture } from "../runtime-observation.js";
10
+ import { createObservationMapper } from "./observation-mapper.js";
9
11
  async function readInput() { process.stdin.setEncoding("utf8"); let text = ""; for await (const chunk of process.stdin)
10
12
  text += String(chunk); return JSON.parse(text); }
11
13
  async function emit(event) { if (process.stdout.write(`${JSON.stringify(event)}\n`))
12
14
  return; await once(process.stdout, "drain"); }
13
15
  export async function runPi(bin) {
16
+ const capture = createRuntimeObservationCapture("pi");
17
+ const observer = createObservationMapper("pi", capture);
18
+ try {
19
+ return await runPiObserved(bin, observer);
20
+ }
21
+ finally {
22
+ observer.finish();
23
+ capture.finish();
24
+ }
25
+ }
26
+ async function runPiObserved(bin, observer) {
14
27
  const input = await readInput();
15
28
  // Pi 不持久化原生会话,每轮都须在启动模型前补齐当前线程快照。
16
29
  const delivery = await prepareThreadDelivery({
@@ -27,11 +40,14 @@ export async function runPi(bin) {
27
40
  let inputError;
28
41
  child.stdin.on("error", error => { inputError = error; });
29
42
  // Pi print 模式读取 stdin;完整线程历史不能塞进受长度限制的 argv。
43
+ observer.context({ systemPrompt: input.systemPrompt, prompt: delivery.prompt });
30
44
  child.stdin.end(delivery.prompt);
31
45
  const decoder = createPiEventDecoder();
32
46
  for await (const line of createInterface({ input: child.stdout })) {
33
47
  try {
34
- for (const event of decoder.push(JSON.parse(line)))
48
+ const raw = JSON.parse(line);
49
+ observer.observe(raw);
50
+ for (const event of decoder.push(raw))
35
51
  await emit(event);
36
52
  }
37
53
  catch { }
@@ -0,0 +1,212 @@
1
+ import { z } from "zod";
2
+ export const STORAGE_MIGRATION_CAPABILITY = "storage_migration_v1";
3
+ export const STORAGE_MIGRATION_STATUS_CAPABILITY = "storage_migration_status_v1";
4
+ const IdentitySchema = z.object({
5
+ migrationId: z.string().uuid(),
6
+ environment: z.string().regex(/^[a-z0-9][a-z0-9_-]{0,31}$/u),
7
+ workspaceId: z.string().uuid(),
8
+ ownerEpoch: z.number().int().positive().safe().optional(),
9
+ ownershipClaimFingerprint: z.string().regex(/^sha256:[0-9a-f]{64}$/u).optional(),
10
+ });
11
+ const DoctorRequestSchema = IdentitySchema.extend({ type: z.literal("daemon:storage-doctor") }).strict();
12
+ const MigrateRequestSchema = IdentitySchema.extend({
13
+ type: z.literal("daemon:storage-migrate"),
14
+ manifestDigest: z.string().regex(/^[0-9a-f]{64}$/u),
15
+ }).strict();
16
+ const StatusSchema = z.enum([
17
+ "preflight_running", "preflight_passed", "backing_up", "initial_sync", "quiescing", "final_sync", "verified",
18
+ "service_switched", "hello_confirmed", "completed", "failed", "rollback_pending", "rolled_back",
19
+ "rollback_failed",
20
+ ]);
21
+ const TOKEN = /(?:\bsk_(?:machine|user|agent)_[A-Za-z0-9_-]{4,}\b|\bbearer\s+[^\s]+|(?:password|secret|token)\s*[:=]\s*[^\s]+)/giu;
22
+ const safeError = (error) => (error instanceof Error ? error.message : String(error))
23
+ .replaceAll(TOKEN, "<redacted>").slice(0, 500) || "storage migration failed";
24
+ const stableErrorCode = (candidate, fallback) => typeof candidate === "string" && /^[a-z0-9._-]{1,128}$/u.test(candidate) ? candidate : fallback;
25
+ export function createStorageControlController(input) {
26
+ const environment = z.string().regex(/^[a-z0-9][a-z0-9_-]{0,31}$/u).parse(input.environment);
27
+ const workspaceId = z.string().uuid().parse(input.workspaceId);
28
+ const profile = z.string().regex(/^[a-z0-9][a-z0-9_-]{0,47}$/u).parse(input.profile);
29
+ const sourceStorageFingerprint = z.string().regex(/^sha256:[0-9a-f]{64}$/u).parse(input.sourceStorageFingerprint);
30
+ const targetStorageFingerprint = z.string().regex(/^sha256:[0-9a-f]{64}$/u).parse(input.targetStorageFingerprint);
31
+ const ownership = input.ownership === undefined ? undefined : Object.freeze({
32
+ ownerEpoch: z.number().int().positive().safe().parse(input.ownership.ownerEpoch),
33
+ ownershipClaimFingerprint: z.string().regex(/^sha256:[0-9a-f]{64}$/u)
34
+ .parse(input.ownership.ownershipClaimFingerprint),
35
+ });
36
+ const preflights = new Map();
37
+ const preflightRuns = new Map();
38
+ const appliedMigrationIds = new Set();
39
+ let runningMigrationId = null;
40
+ const preflightTimeoutMs = z.number().int().positive().max(24 * 60 * 60 * 1000)
41
+ .parse(input.preflightTimeoutMs ?? 30 * 60 * 1000);
42
+ const frame = (identity, status, extra = {}) => ({
43
+ type: "daemon:storage-migration-status",
44
+ migrationId: identity.migrationId,
45
+ status,
46
+ environment,
47
+ workspaceId,
48
+ profile,
49
+ ...(ownership === undefined ? {} : ownership),
50
+ ...extra,
51
+ });
52
+ const failure = (identity, errorCode, error, reopenAdmission = false, extra = {}) => {
53
+ reportStatus(frame(identity, "failed", {
54
+ errorCode,
55
+ ...extra,
56
+ ...(error === undefined ? {} : { errorMessage: safeError(error) }),
57
+ ...(reopenAdmission || error?.reopenAdmission === true
58
+ ? { reopenAdmission: true }
59
+ : {}),
60
+ }));
61
+ };
62
+ const reportStatus = (statusFrame) => {
63
+ try {
64
+ input.sendStatus(statusFrame);
65
+ }
66
+ catch (error) {
67
+ input.onStatusError?.(error, statusFrame);
68
+ }
69
+ };
70
+ const matchesIdentity = (identity) => identity.environment === environment && identity.workspaceId === workspaceId
71
+ && (ownership === undefined
72
+ || (identity.ownerEpoch === ownership.ownerEpoch
73
+ && identity.ownershipClaimFingerprint === ownership.ownershipClaimFingerprint));
74
+ const preflightKey = (identity) => `${identity.migrationId}\0${identity.environment}\0${identity.workspaceId}`;
75
+ const runPreflight = (identity, failureFallback = "doctor_failed") => {
76
+ const key = preflightKey(identity);
77
+ const existing = preflightRuns.get(key);
78
+ if (existing !== undefined)
79
+ return existing;
80
+ const controller = new AbortController();
81
+ let scannedEntries = 0;
82
+ let timedOut = false;
83
+ reportStatus(frame(identity, "preflight_running", {
84
+ progress: { state: "running", scannedEntries: 0 },
85
+ }));
86
+ const doctorPromise = Promise.resolve().then(() => input.runDoctor(identity.migrationId, {
87
+ signal: controller.signal,
88
+ onProgress: (progress) => {
89
+ if (timedOut)
90
+ return;
91
+ scannedEntries = progress.scannedEntries;
92
+ reportStatus(frame(identity, "preflight_running", {
93
+ progress: { state: "running", scannedEntries },
94
+ }));
95
+ },
96
+ }));
97
+ let timeoutHandle;
98
+ const timeout = new Promise((_, reject) => {
99
+ timeoutHandle = setTimeout(() => {
100
+ timedOut = true;
101
+ controller.abort();
102
+ reject(Object.assign(new Error("storage doctor preflight timed out"), {
103
+ code: "preflight_stale",
104
+ scannedEntries,
105
+ }));
106
+ }, preflightTimeoutMs);
107
+ timeoutHandle.unref?.();
108
+ });
109
+ void doctorPromise.catch(() => undefined);
110
+ let outcome;
111
+ outcome = Promise.race([doctorPromise, timeout]).then((doctor) => {
112
+ if (doctor.manifest.migrationId !== identity.migrationId
113
+ || doctor.environment !== environment || doctor.workspaceId !== workspaceId
114
+ || doctor.profile !== profile) {
115
+ failure(identity, "storage_identity_mismatch", undefined, true);
116
+ return null;
117
+ }
118
+ if (!doctor.applyAllowed) {
119
+ failure(identity, doctor.blockers[0]?.code ?? "doctor_blocked", undefined, true);
120
+ return null;
121
+ }
122
+ preflights.set(identity.migrationId, doctor);
123
+ reportStatus(frame(identity, "preflight_passed", {
124
+ sourceStorageFingerprint,
125
+ targetStorageFingerprint,
126
+ manifestDigest: doctor.manifestDigest,
127
+ }));
128
+ return doctor;
129
+ }).catch((error) => {
130
+ const code = error.code;
131
+ if (code === "preflight_stale") {
132
+ failure(identity, "preflight_stale", undefined, true, {
133
+ retryable: true,
134
+ progress: { state: "stale", scannedEntries: Number(error.scannedEntries) || scannedEntries },
135
+ });
136
+ }
137
+ else if (code === "preflight_aborted") {
138
+ failure(identity, "preflight_aborted", error, true, {
139
+ retryable: true,
140
+ progress: { state: "stale", scannedEntries },
141
+ });
142
+ }
143
+ else {
144
+ failure(identity, stableErrorCode(code, failureFallback), error, true);
145
+ }
146
+ return null;
147
+ }).finally(() => {
148
+ if (timeoutHandle !== undefined)
149
+ clearTimeout(timeoutHandle);
150
+ if (preflightRuns.get(key) === outcome)
151
+ preflightRuns.delete(key);
152
+ });
153
+ preflightRuns.set(key, outcome);
154
+ return outcome;
155
+ };
156
+ const handle = async (raw) => {
157
+ const type = typeof raw === "object" && raw !== null && "type" in raw ? raw.type : null;
158
+ if (type !== "daemon:storage-doctor" && type !== "daemon:storage-migrate")
159
+ return false;
160
+ const schema = type === "daemon:storage-doctor" ? DoctorRequestSchema : MigrateRequestSchema;
161
+ const parsed = schema.safeParse(raw);
162
+ if (!parsed.success)
163
+ return true;
164
+ const identity = parsed.data;
165
+ if (!matchesIdentity(identity)) {
166
+ failure(identity, "storage_identity_mismatch", undefined, true);
167
+ return true;
168
+ }
169
+ if (type === "daemon:storage-doctor") {
170
+ await runPreflight(identity);
171
+ return true;
172
+ }
173
+ if (runningMigrationId !== null) {
174
+ if (runningMigrationId !== identity.migrationId)
175
+ failure(identity, "migration_active");
176
+ return true;
177
+ }
178
+ const migrate = MigrateRequestSchema.parse(identity);
179
+ if (appliedMigrationIds.has(identity.migrationId))
180
+ return true;
181
+ let doctor = preflights.get(identity.migrationId);
182
+ if (doctor === undefined) {
183
+ const rebuilt = await runPreflight(identity, "preflight_rebuild_failed");
184
+ if (rebuilt === null)
185
+ return true;
186
+ doctor = rebuilt;
187
+ }
188
+ if (!doctor.applyAllowed || doctor.manifestDigest !== migrate.manifestDigest
189
+ || doctor.manifest.migrationId !== identity.migrationId
190
+ || doctor.environment !== environment || doctor.workspaceId !== workspaceId || doctor.profile !== profile) {
191
+ failure(identity, "preflight_digest_mismatch", undefined, true);
192
+ return true;
193
+ }
194
+ preflights.delete(identity.migrationId);
195
+ runningMigrationId = identity.migrationId;
196
+ try {
197
+ await input.runMigration(doctor, (status) => {
198
+ if (StatusSchema.safeParse(status).success)
199
+ reportStatus(frame(identity, status));
200
+ });
201
+ appliedMigrationIds.add(identity.migrationId);
202
+ }
203
+ catch (error) {
204
+ failure(identity, stableErrorCode(error.code, "storage_migration_failed"), error);
205
+ }
206
+ finally {
207
+ runningMigrationId = null;
208
+ }
209
+ return true;
210
+ };
211
+ return Object.freeze({ handle });
212
+ }