@nowcrew/daemon 0.6.91-beta.1 → 0.6.91-beta.10

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 (53) hide show
  1. package/dist/agent-ability/types.js +1 -1
  2. package/dist/console.js +5 -4
  3. package/dist/daemon-update-controller.js +16 -8
  4. package/dist/daemon-updater.js +8 -10
  5. package/dist/execution-concurrency-telemetry.js +1 -0
  6. package/dist/execution-journal.js +3 -1
  7. package/dist/execution-protocol.js +2 -2
  8. package/dist/execution-runner.js +6 -3
  9. package/dist/external-output.js +2 -1
  10. package/dist/host-execution-coordinator.js +12 -6
  11. package/dist/i18n.js +4 -3
  12. package/dist/local-execution-prompt.js +8 -3
  13. package/dist/local-executor.js +16 -37
  14. package/dist/local-memory/execution-workspace.js +36 -0
  15. package/dist/local-memory/full-index-search.js +47 -0
  16. package/dist/local-memory/index-budget.js +9 -3
  17. package/dist/local-memory/migration-access.js +118 -0
  18. package/dist/local-memory/migration-cli.js +54 -0
  19. package/dist/local-memory/migration-io.js +199 -0
  20. package/dist/local-memory/migration-plan.js +283 -0
  21. package/dist/local-memory/migration-schema.js +55 -0
  22. package/dist/local-memory/migration-store.js +424 -0
  23. package/dist/local-memory/migration-tool-entry.js +21 -0
  24. package/dist/local-memory/prompt.js +20 -4
  25. package/dist/local-memory/read-cli.js +36 -1
  26. package/dist/local-memory/rollout.js +53 -8
  27. package/dist/local-memory/runtime.js +5 -2
  28. package/dist/local-memory/storage-format.js +152 -0
  29. package/dist/local-memory/write.js +108 -17
  30. package/dist/machine-environment-cache.js +18 -4
  31. package/dist/machine-environment-contract.js +3 -1
  32. package/dist/machine-environment-process.js +21 -1
  33. package/dist/machine-info.js +3 -0
  34. package/dist/memory-migration-controller.js +84 -0
  35. package/dist/memory-migration-inspect.js +74 -0
  36. package/dist/memory-migration-journal.js +14 -0
  37. package/dist/memory-migration-protocol.js +14 -0
  38. package/dist/memory-migration-runtime.js +305 -0
  39. package/dist/message-file-preview.js +11 -1
  40. package/dist/normalize.js +9 -5
  41. package/dist/pre-admission-rejection.js +5 -4
  42. package/dist/prompt.js +5 -1
  43. package/dist/runner.js +1 -1
  44. package/dist/runtime-capabilities.js +2 -1
  45. package/dist/runtime-startup-gate.js +1 -0
  46. package/dist/runtimes/pi-runner.js +28 -0
  47. package/dist/runtimes/pi.js +17 -0
  48. package/dist/serve.js +37 -4
  49. package/dist/shared-execution-slots.js +2 -0
  50. package/dist/supervised-runtime.js +20 -0
  51. package/dist/token.js +1 -0
  52. package/dist/workspace-control-handler.js +16 -3
  53. package/package.json +1 -1
package/dist/runner.js CHANGED
@@ -80,7 +80,7 @@ async function runAgentWithMachineEnvironment(config, input, onActivity = defaul
80
80
  ? { scheduledExternalNotificationPolicy: input.scheduled.externalNotificationPolicy }
81
81
  : {}),
82
82
  ...(input.wakeOrigin ? { wakeOrigin: input.wakeOrigin } : {}),
83
- memory: capMemoryForInject(workspace.memory),
83
+ memory: capMemoryForInject(workspace.memory, undefined, undefined, baseWake),
84
84
  ...(resuming ? {} : { workLog: capWorkLogForInject(workspace.workLog) }),
85
85
  collaborationToolsEnabled: config.codexCollaborationTools && runtime === "codex",
86
86
  localMemoryV2Enabled: workspace.localMemoryV2Enabled ?? localMemoryV2Enabled,
@@ -1,6 +1,6 @@
1
1
  import { ATTACHMENT_NATIVE_IMAGE_MAX_BYTES } from "./attachment-limits.js";
2
2
  export const LOCAL_EXECUTION_RUNTIMES = [
3
- "claude", "codex", "kimi", "hermes", "opencode", "deepseek-harness",
3
+ "claude", "codex", "kimi", "hermes", "opencode", "deepseek-harness", "pi",
4
4
  ];
5
5
  export const LOCAL_RUNTIME_CAPABILITIES = Object.freeze({
6
6
  claude: Object.freeze({
@@ -33,6 +33,7 @@ export const LOCAL_RUNTIME_CAPABILITIES = Object.freeze({
33
33
  nativeResume: false,
34
34
  systemPromptTransport: "protocol",
35
35
  }),
36
+ pi: Object.freeze({ transport: "pi-json", nativeResume: false, systemPromptTransport: "protocol" }),
36
37
  });
37
38
  export function runtimeCapability(runtime) {
38
39
  return LOCAL_RUNTIME_CAPABILITIES[runtime];
@@ -95,6 +95,7 @@ export function createRuntimeStartupGate(limits, now = Date.now) {
95
95
  kimi: activeByRuntimeAndLane.get(activeKey("kimi", "foreground")) ?? 0,
96
96
  hermes: activeByRuntimeAndLane.get(activeKey("hermes", "foreground")) ?? 0,
97
97
  opencode: activeByRuntimeAndLane.get(activeKey("opencode", "foreground")) ?? 0,
98
+ pi: activeByRuntimeAndLane.get(activeKey("pi", "foreground")) ?? 0,
98
99
  "deepseek-harness": activeByRuntimeAndLane.get(activeKey("deepseek-harness", "foreground")) ?? 0,
99
100
  },
100
101
  }),
@@ -0,0 +1,28 @@
1
+ import { once } from "node:events";
2
+ import { createInterface } from "node:readline";
3
+ import { parseArgs } from "node:util";
4
+ import { pathToFileURL } from "node:url";
5
+ import spawn from "cross-spawn";
6
+ import { augmentedPath } from "../runtime-path.js";
7
+ import { buildPiArgs, createPiEventDecoder } from "./pi.js";
8
+ async function readInput() { process.stdin.setEncoding("utf8"); let text = ""; for await (const chunk of process.stdin)
9
+ text += String(chunk); return JSON.parse(text); }
10
+ async function emit(event) { if (process.stdout.write(`${JSON.stringify(event)}\n`))
11
+ return; await once(process.stdout, "drain"); }
12
+ export async function runPi(bin) { const input = await readInput(); const child = spawn(bin, buildPiArgs(input), { cwd: process.cwd(), env: { ...process.env, PATH: augmentedPath(), PWD: process.cwd() }, stdio: ["ignore", "pipe", "pipe"] }); if (!child.stdout || !child.stderr)
13
+ throw new Error("Pi process did not expose stdio"); child.stderr.pipe(process.stderr, { end: false }); const exit = once(child, "close"); const decoder = createPiEventDecoder(); for await (const line of createInterface({ input: child.stdout })) {
14
+ try {
15
+ for (const event of decoder.push(JSON.parse(line)))
16
+ await emit(event);
17
+ }
18
+ catch { }
19
+ } const [code, signal] = await exit; const result = decoder.finish(); if (!result.ok) {
20
+ process.stderr.write(`${result.error}\n`);
21
+ return 1;
22
+ } return code === 0 ? 0 : (code ?? (signal ? 128 : 1)); }
23
+ const { values } = parseArgs({ args: process.argv.slice(2), options: { bin: { type: "string" } } });
24
+ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
25
+ if (!values.bin)
26
+ throw new Error("--bin is required");
27
+ runPi(values.bin).then(code => { process.exitCode = code; }).catch(error => { process.stderr.write(`Pi runner failed: ${String(error)}\n`); process.exitCode = 1; });
28
+ }
@@ -0,0 +1,17 @@
1
+ export function buildPiArgs(options) { return ["--mode", "json", "--print", "--no-session", "--system-prompt", options.systemPrompt, ...(options.model ? ["--model", options.model] : []), ...(options.reasoning && options.reasoning !== "default" ? ["--thinking", options.reasoning] : []), options.wakePrompt]; }
2
+ export function createPiEventDecoder() { let sessionId = null; let finalText = ""; let error = null; let sawTurnEnd = false; let usage; return { push(raw) { const e = (raw ?? {}); const out = []; if (e.type === "session" && typeof e.id === "string") {
3
+ sessionId = e.id;
4
+ out.push({ type: "thread.started", thread_id: e.id });
5
+ } const ae = e.assistantMessageEvent; if (e.type === "message_update" && ae?.type === "text_delta" && typeof ae.delta === "string") {
6
+ finalText += ae.delta;
7
+ out.push({ type: "pi.text_delta", text: ae.delta });
8
+ } if (e.type === "message_update" && typeof ae?.type === "string" && /tool(call|_call)/iu.test(ae.type))
9
+ out.push({ type: "pi.tool_call", ...(ae.name ? { title: ae.name } : {}) }); if (e.type === "message_end" && e.message?.role === "assistant")
10
+ usage = e.message.usage; if (e.type === "turn_end") {
11
+ sawTurnEnd = true;
12
+ const u = usage;
13
+ out.push({ type: "turn.completed", ...(u ? { usage: { input_tokens: u.input ?? 0, output_tokens: u.output ?? 0, cache_read_input_tokens: u.cacheRead ?? 0, cache_creation_input_tokens: u.cacheWrite ?? 0 } } : {}) });
14
+ } if (e.type === "error" || e.type === "agent_error") {
15
+ error = typeof e.error === "string" ? e.error : (e.error?.message ?? "Pi error");
16
+ out.push({ type: "pi.error", message: error });
17
+ } return out; }, finish() { return { ok: error === null && sawTurnEnd, ...(error === null && sawTurnEnd ? {} : { error: error ?? "Pi stream ended without turn completion" }), finalText, sessionId, usage }; } }; }
package/dist/serve.js CHANGED
@@ -14,6 +14,7 @@ import { reconcileExecutionJournal } from "./execution-recovery.js";
14
14
  import { initReleaseSlog, reportDaemonConnectionClose, reportReleaseError } from "./release-channel-guard.js";
15
15
  import { handleWorkspaceControlRequest } from "./workspace-control-handler.js";
16
16
  import { inspectRaftWorkspace, importRaftWorkspace } from "./workspace-import.js";
17
+ import { withMemoryAccess } from "./local-memory/migration-access.js";
17
18
  import { normalizeScheduledContext } from "./scheduled-report.js";
18
19
  import { formatDaemonLogLine } from "./log-format.js";
19
20
  import { reportAgentRunComplete } from "./scheduled-run-report.js";
@@ -50,6 +51,8 @@ import { probeRuntimeHealth } from "./runtime-health.js";
50
51
  import { createServeMarketplace } from "./marketplace-serve.js";
51
52
  import { buildControlPlaneUrl } from "./control-plane-url.js";
52
53
  import { resolveDaemonLocalStateRootFromEnv } from "./daemon-local-home.js";
54
+ import { createMemoryMigrationController } from "./memory-migration-controller.js";
55
+ import { createMemoryMigrationJournal, replayExecutionJournals } from "./memory-migration-journal.js";
53
56
  import { bindStorageServerIdentity, createStorageIdentityAdmission, } from "./storage-serve-adapter.js";
54
57
  export { buildControlPlaneUrl } from "./control-plane-url.js";
55
58
  // normalize.ts 的活动种类 → activity 枚举
@@ -71,6 +74,8 @@ export function serve(config, opts = {}) {
71
74
  let reconnectTimer = null;
72
75
  let stopPromise = null;
73
76
  let executionJournal;
77
+ let migrationExecutionJournal;
78
+ let migrationJournalReady = false;
74
79
  const injectedExecutionJournal = opts.execution?.journal;
75
80
  const agentMemory = opts.execution?.dependencies?.agentMemory
76
81
  ?? (config.agentMemory === null ? undefined : createAgentMemoryBridge(config.agentMemory));
@@ -85,6 +90,15 @@ export function serve(config, opts = {}) {
85
90
  startupGapMs: config.executionLimits.startupGapMs,
86
91
  });
87
92
  const localSlots = createSharedSlotManager(config.executionLimits);
93
+ const memoryMigrations = createMemoryMigrationController({ config, coordinator: hostCoordinator,
94
+ isAdmissionClosed: () => stopped || storageIdentityAdmission.isAdmissionClosed(),
95
+ journal: () => { if (!migrationJournalReady)
96
+ throw new Error("memory_migration_journal_unavailable"); return migrationExecutionJournal; }, send: frame => {
97
+ if (frame.type === "execution:completed")
98
+ completionRetransmitter.track(frame);
99
+ else if (ws?.readyState === WebSocket.OPEN)
100
+ ws.send(JSON.stringify(frame));
101
+ } });
88
102
  const sharedSlots = hostCoordinatedSlotManager(localSlots, hostCoordinator);
89
103
  const concurrencyTelemetry = createExecutionConcurrencyTelemetry(sharedSlots, {
90
104
  ...(opts.execution?.concurrencySnapshotIntervalMs === undefined
@@ -232,7 +246,7 @@ export function serve(config, opts = {}) {
232
246
  return null;
233
247
  };
234
248
  const sendSnapshot = async (reqId) => {
235
- const entries = (await executionJournal.replay()).map(snapshotEntry).filter((entry) => entry !== null);
249
+ const entries = (await replayExecutionJournals(executionJournal, migrationExecutionJournal)).map(snapshotEntry).filter((entry) => entry !== null);
236
250
  safeExecutionSend(ExecutionSnapshotSchema.parse({
237
251
  type: "execution:snapshot", protocolVersion: 1, reqId, entries,
238
252
  }));
@@ -313,6 +327,12 @@ export function serve(config, opts = {}) {
313
327
  machineRuntime.open(openedSocket, { capabilities: capabilities.machineHello, beforePublish: Promise.resolve(), connectionId });
314
328
  opts.onOpen?.(ws);
315
329
  void sendSnapshot(`reconnect-${randomUUID()}`);
330
+ // 普通回执不等待维护日志IO;两类记录分别重放到同一发送器。
331
+ void migrationExecutionJournal.replay().then(entries => {
332
+ for (const entry of entries)
333
+ if (entry.completion && !entry.completionAcknowledged)
334
+ completionRetransmitter.track(entry.completion);
335
+ }).catch(() => dslog("memory_migration.journal_unavailable", "维护回执重放失败,普通回执继续", { level: "ERROR" }));
316
336
  void executionJournal.replay()
317
337
  .then((entries) => {
318
338
  for (const entry of entries) {
@@ -387,7 +407,8 @@ export function serve(config, opts = {}) {
387
407
  return;
388
408
  if (frame.type === "execution:completion-ack") {
389
409
  try {
390
- await executionJournal.acknowledgeCompletion(frame.executionId);
410
+ const journal = await executionJournal.get(frame.executionId) ? executionJournal : migrationExecutionJournal;
411
+ await journal.acknowledgeCompletion(frame.executionId);
391
412
  const attempts = completionRetransmitter.acknowledge(frame.executionId);
392
413
  dslog("execution.completion_acknowledged", "execution completion ACK 已持久化", {
393
414
  execution_id: frame.executionId,
@@ -413,6 +434,8 @@ export function serve(config, opts = {}) {
413
434
  return;
414
435
  }
415
436
  if (frame.type === "execution:cancel") {
437
+ if (memoryMigrations.cancel(frame.executionId))
438
+ return;
416
439
  if (!knownExecutionHashes.has(frame.executionId)
417
440
  && !executionRuns.has(frame.executionId)
418
441
  && await executionJournal.get(frame.executionId) === null)
@@ -488,7 +511,7 @@ export function serve(config, opts = {}) {
488
511
  let reservation = sharedSlots.reserve(spec.agent.handle, "execution", executionClass);
489
512
  if (!reservation.accepted) {
490
513
  await rejectQueuedAdmission({ journal: executionJournal, knownExecutionHashes, executionRuns,
491
- report: reportExecutionFrame, replay: sendJournalStatus }, spec, executionClass, reservation.facts);
514
+ report: reportExecutionFrame, replay: sendJournalStatus }, spec, executionClass, reservation.facts, reservation.rejectionReason);
492
515
  return;
493
516
  }
494
517
  knownExecutionHashes.set(spec.executionId, hash);
@@ -735,6 +758,8 @@ export function serve(config, opts = {}) {
735
758
  // ready 帧带 server 视角的 machineId/workspaceId → 作为后续所有日志的默认关联键
736
759
  const r = msg;
737
760
  setSlogDefaults({ machine_id: r.machineId, workspace_id: r.workspaceId });
761
+ if (r.workspaceId && r.machineId)
762
+ memoryMigrations.setIdentity({ workspaceId: r.workspaceId, machineId: r.machineId });
738
763
  if (opts.storageRuntime !== undefined && typeof r.workspaceId === "string" && typeof r.machineId === "string") {
739
764
  const bindingSocket = ws;
740
765
  bindStorageServerIdentity(opts.storageRuntime, { workspaceId: r.workspaceId, machineId: r.machineId }, {
@@ -801,6 +826,8 @@ export function serve(config, opts = {}) {
801
826
  }
802
827
  catch { /* server 会按请求超时处理 */ } }))
803
828
  return;
829
+ if (await memoryMigrations.handle(msg))
830
+ return;
804
831
  if (await agentAbilityRuntime.tryHandleControlMessage(msg, serverCapabilities, (frame) => ws?.send(JSON.stringify(frame))))
805
832
  return;
806
833
  // 导入 raft agent 工作区:inspect 反填 name/description;import 复制用户内容
@@ -815,7 +842,7 @@ export function serve(config, opts = {}) {
815
842
  try {
816
843
  const data2 = req.type === "raft:inspect"
817
844
  ? await inspectRaftWorkspace(req.path)
818
- : await importRaftWorkspace(req.path, join(config.agentsRoot, req.handle));
845
+ : await withMemoryAccess(join(config.agentsRoot, req.handle), () => importRaftWorkspace(req.path, join(config.agentsRoot, req.handle)), { create: true });
819
846
  if (req.type === "raft:import") {
820
847
  log(`📦 已导入 raft 工作区 → ${req.handle}: 复制 ${data2.copied.length} 项`);
821
848
  }
@@ -1259,6 +1286,9 @@ export function serve(config, opts = {}) {
1259
1286
  writeStderr: (line) => process.stderr.write(line),
1260
1287
  });
1261
1288
  await runProjectSkillStartupCleanup(config.agentsRoot, executionJournal, dslog);
1289
+ migrationExecutionJournal = createMemoryMigrationJournal(config.agentsRoot);
1290
+ await migrationExecutionJournal.reconcileAfterRestart().then(() => { migrationJournalReady = true; })
1291
+ .catch(() => dslog("memory_migration.journal_unavailable", "迁移日志恢复失败,普通执行继续,迁移请求单独报错", { level: "ERROR" }));
1262
1292
  await connect();
1263
1293
  })();
1264
1294
  const stop = () => {
@@ -1276,6 +1306,7 @@ export function serve(config, opts = {}) {
1276
1306
  const webSocketClosed = closeWebSocketWithinDeadline(ws, deadline.signal);
1277
1307
  const pending = [
1278
1308
  machineRuntime.stop(),
1309
+ memoryMigrations.stop(),
1279
1310
  webSocketClosed,
1280
1311
  updateController.drain(),
1281
1312
  marketplace.stop(),
@@ -1294,6 +1325,8 @@ export function serve(config, opts = {}) {
1294
1325
  if (executionJournal !== undefined) {
1295
1326
  await executionJournal.close({ signal: deadline.signal });
1296
1327
  }
1328
+ if (migrationExecutionJournal !== undefined)
1329
+ await migrationExecutionJournal.close({ signal: deadline.signal });
1297
1330
  }
1298
1331
  finally {
1299
1332
  deadline.dispose();
@@ -59,6 +59,7 @@ export function createSharedSlotManager(limits) {
59
59
  if (exclusive) {
60
60
  return {
61
61
  accepted: false,
62
+ rejectionReason: "maintenance",
62
63
  facts,
63
64
  ready: Promise.resolve(),
64
65
  isQueued: () => false,
@@ -77,6 +78,7 @@ export function createSharedSlotManager(limits) {
77
78
  || laneQueuedTotal >= limits.maxQueuedTotal)) {
78
79
  return {
79
80
  accepted: false,
81
+ rejectionReason: queuedForAgent >= limits.maxQueuedPerAgent ? "agent_queue_full" : "machine_queue_full",
80
82
  facts,
81
83
  ready: Promise.resolve(),
82
84
  isQueued: () => false,
@@ -61,6 +61,26 @@ export function supervisorLaunch(request) {
61
61
  }),
62
62
  };
63
63
  }
64
+ if (request.runtime === "pi") {
65
+ if (request.effectivePermission !== "full_access") {
66
+ throw new Error(`Pi cannot enforce ${request.effectivePermission} permission`);
67
+ }
68
+ return {
69
+ command: process.execPath,
70
+ args: [
71
+ fileURLToPath(new URL("./runtimes/pi-runner.js", import.meta.url)),
72
+ "--bin", request.bin,
73
+ ],
74
+ cwd: request.cwd,
75
+ env: request.env,
76
+ stdinText: JSON.stringify({
77
+ systemPrompt: request.systemPrompt,
78
+ wakePrompt: request.wakePrompt,
79
+ ...(request.model === undefined ? {} : { model: request.model }),
80
+ ...(request.reasoning === undefined ? {} : { reasoning: request.reasoning }),
81
+ }),
82
+ };
83
+ }
64
84
  if (request.runtime === "opencode") {
65
85
  if (request.effectivePermission !== "full_access") {
66
86
  throw new Error(`OpenCode cannot enforce ${request.effectivePermission} permission`);
package/dist/token.js CHANGED
@@ -7,6 +7,7 @@ export async function mintAgentToken(serverUrl, machineToken, handle, displayNam
7
7
  },
8
8
  // 把本轮唤醒的线程根钉到 per-launch 令牌:服务端据此让 task/消息默认绑/回当前线程。
9
9
  body: JSON.stringify({
10
+ memoryStateGuardVersion: 1,
10
11
  handle,
11
12
  ...(displayName ? { displayName } : {}),
12
13
  ...(options.wakeThreadRoot ? { wakeThreadRoot: options.wakeThreadRoot } : {}),
@@ -1,9 +1,22 @@
1
1
  /** 控制面文件/技能查看的按需处理,独立于执行生命周期。 */
2
- import { join } from "node:path";
2
+ import { lstat } from "node:fs/promises";
3
+ import { join, resolve } from "node:path";
3
4
  import { readSharedMessageFile } from "./message-file-preview.js";
4
5
  import { listWorkspace, readWorkspaceFile } from "./workspace-fs.js";
5
6
  import { listSkills } from "./skills.js";
6
7
  import { listRuntimeModels } from "./list-models.js";
8
+ import { withMemoryAccess } from "./local-memory/migration-access.js";
9
+ async function listProtectedWorkspace(root, path) {
10
+ try {
11
+ await lstat(root);
12
+ }
13
+ catch (error) {
14
+ if (error.code !== "ENOENT")
15
+ throw error;
16
+ return { root: resolve(root), path: path.replace(/^\/+/, ""), entries: [] };
17
+ }
18
+ return withMemoryAccess(root, () => listWorkspace(root, path));
19
+ }
7
20
  export async function handleWorkspaceControlRequest(raw, agentsRoot, registry, models = listRuntimeModels) {
8
21
  if (!raw || typeof raw !== "object")
9
22
  return null;
@@ -15,8 +28,8 @@ export async function handleWorkspaceControlRequest(raw, agentsRoot, registry, m
15
28
  try {
16
29
  const root = join(agentsRoot, request.handle);
17
30
  const data = request.type === "fs:read-shared" ? await readSharedMessageFile(agentsRoot, raw, registry)
18
- : request.type === "fs:list" ? await listWorkspace(root, request.path)
19
- : request.type === "fs:read" ? await readWorkspaceFile(root, request.path)
31
+ : request.type === "fs:list" ? await listProtectedWorkspace(root, request.path)
32
+ : request.type === "fs:read" ? await withMemoryAccess(root, () => readWorkspaceFile(root, request.path))
20
33
  : request.type === "skills:list" ? await listSkills(request.runtime)
21
34
  : { models: await models(request.runtime ?? "") };
22
35
  return { type: "fs:result", reqId: request.reqId, ok: true, data };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nowcrew/daemon",
3
- "version": "0.6.91-beta.1",
3
+ "version": "0.6.91-beta.10",
4
4
  "type": "module",
5
5
  "description": "crew daemon — 运行在用户机器:拉起/管理 agent 进程,注入 crew CLI,归一化 runtime 事件",
6
6
  "license": "Apache-2.0",