@nowcrew/daemon 0.6.30 → 0.6.31

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 (40) hide show
  1. package/dist/agent-ability/materializer.js +4 -4
  2. package/dist/config.js +3 -3
  3. package/dist/execution-runner.js +17 -13
  4. package/dist/host-execution-coordinator.js +169 -52
  5. package/dist/local-executor.js +107 -34
  6. package/dist/project-skills/reconciler.js +5 -2
  7. package/dist/remote/claude-bridge.js +558 -0
  8. package/dist/remote/claude-channel.js +164 -0
  9. package/dist/remote/codex-client.js +451 -0
  10. package/dist/remote/codex-runtime.js +77 -0
  11. package/dist/remote/config.js +135 -0
  12. package/dist/remote/gateway.js +879 -0
  13. package/dist/remote/identity.js +39 -0
  14. package/dist/remote/owner.js +77 -0
  15. package/dist/remote/protocol.js +211 -0
  16. package/dist/remote/remote-cli.js +254 -0
  17. package/dist/remote/runtime-probe.js +182 -0
  18. package/dist/remote/session-discovery.js +249 -0
  19. package/dist/remote/wrapper.js +40 -0
  20. package/dist/remote-web/assets/index-B_6VM_tw.js +94 -0
  21. package/dist/remote-web/assets/index-L6EiQbJn.css +1 -0
  22. package/dist/remote-web/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  23. package/dist/remote-web/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  24. package/dist/remote-web/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  25. package/dist/remote-web/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  26. package/dist/remote-web/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  27. package/dist/remote-web/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  28. package/dist/remote-web/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  29. package/dist/remote-web/icons/nowwork-192.png +0 -0
  30. package/dist/remote-web/icons/nowwork-512.png +0 -0
  31. package/dist/remote-web/icons/nowwork.svg +7 -0
  32. package/dist/remote-web/index.html +20 -0
  33. package/dist/remote-web/manifest.webmanifest +13 -0
  34. package/dist/remote-web/sw.js +12 -0
  35. package/dist/runner.js +6 -1
  36. package/dist/runtime-startup-gate.js +47 -23
  37. package/dist/runtimes/codex-app-server-runner.js +6 -0
  38. package/dist/serve.js +55 -3
  39. package/dist/shared-execution-slots.js +75 -16
  40. package/package.json +1 -1
@@ -19,7 +19,7 @@ import { decodeExternalOutputEvent, extractExternalAnswer, ExternalAnswerDecoder
19
19
  import { cleanupMaterializedAttachments as cleanupAttachments, executionAttachmentDirectory, materializeAttachments, } from "./attachments.js";
20
20
  import { routeRuntimeAttachments, runtimeCapability, } from "./runtime-capabilities.js";
21
21
  import { awaitWithCancellation, RuntimeCancelledError, } from "./runtime-cancellation.js";
22
- import { isRuntimeReadyEvent, } from "./runtime-startup-gate.js";
22
+ import { isRuntimeReadyEvent, isRuntimeRunningEvent, } from "./runtime-startup-gate.js";
23
23
  import { dslog } from "./slog.js";
24
24
  import { boundedDiagnosticJsonArray } from "./diagnostic-json.js";
25
25
  import { ProjectProjectionError, } from "./project-skills/reconciler.js";
@@ -61,6 +61,7 @@ function logMemoryPruneDiagnosticsFailure(input, traceId, phase, error) {
61
61
  });
62
62
  }
63
63
  const STDERR_TAIL_CAP = 2_000;
64
+ const RUNTIME_RUNNING_CALLBACK_TIMEOUT_MS = 10_000;
64
65
  const MEMORY_PRUNE_DIAGNOSTICS_TIMEOUT_MS = 2_000;
65
66
  const LOCAL_MEMORY_DIAGNOSTICS_TIMEOUT_MS = 250;
66
67
  const CLAUDE_INSTRUCTION_WARNING = "claude_additional_directory_instructions_unverified";
@@ -266,7 +267,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
266
267
  }
267
268
  let materialized = null;
268
269
  let knownAttachmentDirectory = null;
269
- let startupReservation = null;
270
+ const startupReservationState = { current: null };
270
271
  let memoryPruneTraceId = null;
271
272
  let memoryPruneRuntimeExitCode;
272
273
  let memoryPruneExecutorCompleted = false;
@@ -479,31 +480,11 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
479
480
  const launchRuntime = dependencies.launchRuntime ?? launchLegacyRuntime;
480
481
  if (dependencies.cancellation?.isRequested())
481
482
  throw new RuntimeCancelledError();
482
- startupReservation = dependencies.startupGate?.reserve(runtime.name) ?? null;
483
- if (startupReservation !== null) {
484
- const startupQueueEnteredAt = Date.now();
485
- dslog("runtime.start_queued", "runtime 等待启动许可", {
486
- execution_id: input.executionId,
487
- runtime: runtime.name,
488
- queued: startupReservation.isQueued(),
489
- });
490
- try {
491
- await awaitWithCancellation(startupReservation.ready, dependencies.cancellation);
492
- dslog("runtime.start_granted", "runtime 获得启动许可", {
493
- execution_id: input.executionId,
494
- runtime: runtime.name,
495
- startup_queue_ms: Date.now() - startupQueueEnteredAt,
496
- });
497
- }
498
- catch (error) {
499
- startupReservation.release();
500
- throw error;
501
- }
502
- }
503
- const runtimeLaunchAt = Date.now();
504
- memoryPruneFailurePhase = "runtime_launch";
505
483
  let activeProjectSkillRuntimeRoot;
506
- const launchPreparedRuntime = async (runtimeRoot, abilityContext) => {
484
+ const abilityContext = input.abilityRelease !== undefined && dependencies.abilityRelease !== undefined
485
+ ? await dependencies.abilityRelease.prepareAndLaunch(input.launch.agentsRoot, input.handle, input.abilityRelease, async (context) => context)
486
+ : undefined;
487
+ const prepareRuntimeLaunch = async (runtimeRoot) => {
507
488
  activeProjectSkillRuntimeRoot = runtimeRoot;
508
489
  const effectiveSystemPrompt = abilityContext === undefined
509
490
  ? systemPrompt
@@ -602,24 +583,55 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
602
583
  projectContext: input.projectContext,
603
584
  }),
604
585
  };
586
+ return launchRequest;
587
+ };
588
+ let runtimeLaunchAt = Date.now();
589
+ const launchPreparedRuntime = async (runtimeRoot) => {
590
+ const launchRequest = await prepareRuntimeLaunch(runtimeRoot);
591
+ if (dependencies.cancellation?.isRequested())
592
+ throw new RuntimeCancelledError();
593
+ const startupLane = memoryPruneTraceId === null ? "foreground" : "memory_prune";
594
+ startupReservationState.current = dependencies.startupGate?.reserve(runtime.name, startupLane) ?? null;
595
+ const startupReservation = startupReservationState.current;
596
+ if (startupReservation !== null) {
597
+ const startupQueueEnteredAt = Date.now();
598
+ dslog("runtime.start_queued", "runtime 等待启动许可", {
599
+ execution_id: input.executionId,
600
+ runtime: runtime.name,
601
+ startup_lane: startupLane,
602
+ queued: startupReservation.isQueued(),
603
+ });
604
+ try {
605
+ await awaitWithCancellation(startupReservation.ready, dependencies.cancellation);
606
+ dslog("runtime.start_granted", "runtime 获得启动许可", {
607
+ execution_id: input.executionId,
608
+ runtime: runtime.name,
609
+ startup_lane: startupLane,
610
+ startup_queue_ms: Date.now() - startupQueueEnteredAt,
611
+ });
612
+ }
613
+ catch (error) {
614
+ startupReservation.release();
615
+ throw error;
616
+ }
617
+ }
618
+ await callbacks.onRuntimeStarting?.(runtime.name);
619
+ runtimeLaunchAt = Date.now();
605
620
  memoryPruneFailurePhase = "runtime_launch";
606
621
  return launchRuntime(launchRequest);
607
622
  };
608
- const launchWithAbility = (runtimeRoot) => input.abilityRelease !== undefined && dependencies.abilityRelease !== undefined
609
- ? dependencies.abilityRelease.prepareAndLaunch(input.launch.agentsRoot, input.handle, input.abilityRelease, (abilityContext) => launchPreparedRuntime(runtimeRoot, abilityContext))
610
- : launchPreparedRuntime(runtimeRoot);
611
623
  let child;
612
624
  if (input.projectSkills !== undefined && dependencies.projectSkills !== undefined) {
613
625
  const launchOwner = dependencies.projectSkillLeaseSafeLaunchOwner;
614
626
  if (launchOwner === undefined)
615
627
  throw new ProjectProjectionError("skill_projection_failed");
616
- child = await dependencies.projectSkills.prepareAndLaunch(input.launch.agentsRoot, input.executionId, input.handle, input.projectSkills, launchOwner.claim(launchWithAbility), (warning) => callbacks.onConsole?.({
628
+ child = await dependencies.projectSkills.prepareAndLaunch(input.launch.agentsRoot, input.executionId, input.handle, input.projectSkills, launchOwner.claim(launchPreparedRuntime), (warning) => callbacks.onConsole?.({
617
629
  stream: "system",
618
630
  text: formatProjectSkillRuntimeWarning(warning),
619
631
  }));
620
632
  }
621
633
  else {
622
- child = await launchWithAbility();
634
+ child = await launchPreparedRuntime();
623
635
  }
624
636
  localMemoryTelemetry?.markRuntimeStarted();
625
637
  memoryPruneFailurePhase = "runtime_execution";
@@ -640,15 +652,24 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
640
652
  // 每轮独立:tool_use/result 关联状态不能跨 execution 泄漏。
641
653
  const consoleFormatter = createConsoleFormatter();
642
654
  let runtimeReady = false;
655
+ let runtimeRunning = false;
643
656
  let resolveRuntimeReady;
644
657
  const runtimeReadySignal = new Promise((resolve) => { resolveRuntimeReady = resolve; });
645
658
  let runtimeReadyNotification = Promise.resolve();
659
+ let runtimeRunningNotification = Promise.resolve();
660
+ let rejectRuntimeRunningFailure;
661
+ const runtimeRunningFailure = new Promise((_resolve, reject) => {
662
+ rejectRuntimeRunningFailure = reject;
663
+ });
664
+ // The failure can arrive in the same stdout turn before the exit race is installed.
665
+ // Attach a handler immediately while retaining the original rejection for that race.
666
+ void runtimeRunningFailure.catch(() => undefined);
646
667
  const markRuntimeReady = () => {
647
668
  if (runtimeReady)
648
669
  return;
649
670
  runtimeReady = true;
650
671
  localMemoryTelemetry?.markRuntimeReady();
651
- startupReservation?.release();
672
+ startupReservationState.current?.release();
652
673
  dslog("runtime.start_ready", "runtime 已完成初始化", {
653
674
  execution_id: input.executionId,
654
675
  runtime: runtime.name,
@@ -657,6 +678,51 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
657
678
  runtimeReadyNotification = Promise.resolve(callbacks.onRuntimeReady?.(runtime.name));
658
679
  resolveRuntimeReady();
659
680
  };
681
+ const markRuntimeRunning = () => {
682
+ if (runtimeRunning)
683
+ return;
684
+ runtimeRunning = true;
685
+ let callbackTimer;
686
+ runtimeRunningNotification = Promise.race([
687
+ Promise.resolve().then(() => callbacks.onRuntimeRunning?.(runtime.name)),
688
+ new Promise((_resolve, reject) => {
689
+ const callbackTimeoutMs = dependencies.runtimeRunningCallbackTimeoutMs
690
+ ?? RUNTIME_RUNNING_CALLBACK_TIMEOUT_MS;
691
+ callbackTimer = setTimeout(() => reject(new Error(`${runtime.name} runtime-running callback timed out after ${callbackTimeoutMs}ms`)), callbackTimeoutMs);
692
+ }),
693
+ ]).finally(() => {
694
+ if (callbackTimer !== undefined)
695
+ clearTimeout(callbackTimer);
696
+ });
697
+ void runtimeRunningNotification.catch((error) => {
698
+ // Persisting the running phase is part of execution liveness. Propagate
699
+ // its failure immediately: cancellation is best-effort and must not be
700
+ // allowed to keep the execution or its slots alive indefinitely.
701
+ rejectRuntimeRunningFailure(error);
702
+ try {
703
+ void child.cancel?.().catch((cancelError) => {
704
+ dslog("runtime.running_callback_cancel_failed", "runtime-running 回调失败后的取消也失败", {
705
+ level: "ERROR",
706
+ execution_id: input.executionId,
707
+ runtime: runtime.name,
708
+ error_message: cancelError instanceof Error
709
+ ? cancelError.message.slice(0, 500)
710
+ : String(cancelError).slice(0, 500),
711
+ });
712
+ });
713
+ }
714
+ catch (cancelError) {
715
+ dslog("runtime.running_callback_cancel_failed", "runtime-running 回调失败后的取消也失败", {
716
+ level: "ERROR",
717
+ execution_id: input.executionId,
718
+ runtime: runtime.name,
719
+ error_message: cancelError instanceof Error
720
+ ? cancelError.message.slice(0, 500)
721
+ : String(cancelError).slice(0, 500),
722
+ });
723
+ }
724
+ });
725
+ };
660
726
  const readline = createInterface({ input: child.stdout });
661
727
  readline.on("line", (line) => {
662
728
  const event = parseLine(line);
@@ -665,6 +731,11 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
665
731
  localMemoryTelemetry?.observe(event);
666
732
  if (isRuntimeReadyEvent(runtime.name, event))
667
733
  markRuntimeReady();
734
+ if (isRuntimeRunningEvent(runtime.name, event)) {
735
+ if (!runtimeReady)
736
+ markRuntimeReady();
737
+ markRuntimeRunning();
738
+ }
668
739
  const meta = extractRunMeta(event);
669
740
  if (meta.sessionId)
670
741
  sessionId = meta.sessionId;
@@ -723,6 +794,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
723
794
  child.stderr.on("end", () => codexStartupStageParser?.finish());
724
795
  let runtimeExit;
725
796
  try {
797
+ const startupReservation = startupReservationState.current;
726
798
  if (startupReservation !== null) {
727
799
  let startupTimer;
728
800
  let startupOutcome;
@@ -761,7 +833,8 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
761
833
  }
762
834
  await runtimeReadyNotification;
763
835
  }
764
- runtimeExit = await awaitWithCancellation(child.exit, dependencies.cancellation);
836
+ runtimeExit = await awaitWithCancellation(Promise.race([child.exit, runtimeRunningFailure]), dependencies.cancellation);
837
+ await runtimeRunningNotification;
765
838
  }
766
839
  catch (error) {
767
840
  if (error instanceof RuntimeCancelledError) {
@@ -831,7 +904,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
831
904
  };
832
905
  }
833
906
  finally {
834
- startupReservation?.release();
907
+ startupReservationState.current?.release();
835
908
  try {
836
909
  await localMemoryTelemetry?.finish();
837
910
  }
@@ -105,7 +105,7 @@ export const canonicalTrainingSkillLinkTarget = (source, link, trainingSkillsRoo
105
105
  : null;
106
106
  };
107
107
  export const isTrainingSkillLink = (source, link, trainingSkillsRoot, platform) => canonicalTrainingSkillLinkTarget(source, link, trainingSkillsRoot, platform) !== null;
108
- const preserveNonProjectSkills = async (projectionTarget, trainingSkillsRoot, platform) => {
108
+ const preserveNonProjectSkills = async (projectionTarget, trainingSkillsRoot, platform, projectSkillNames) => {
109
109
  const names = new Set();
110
110
  if (!await exists(projectionTarget.target))
111
111
  return names;
@@ -130,6 +130,8 @@ const preserveNonProjectSkills = async (projectionTarget, trainingSkillsRoot, pl
130
130
  const canonicalTarget = canonicalTrainingSkillLinkTarget(source, link, trainingSkillsRoot, platform);
131
131
  if (canonicalTarget === null)
132
132
  continue;
133
+ if (projectSkillNames.has(entry.name))
134
+ continue;
133
135
  await symlink(platform === "win32" ? canonicalTarget : link, destination, platform === "win32" ? "junction" : "dir");
134
136
  }
135
137
  else {
@@ -194,6 +196,7 @@ export function createProjectSkillsReconciler(deps) {
194
196
  resolutionRecords.push(Object.freeze({ ...binding, sourcePath: null, mode: "missing" }));
195
197
  }
196
198
  }
199
+ const linkedNames = new Set(linked.map((item) => item.binding.skillName));
197
200
  const id = randomUUID();
198
201
  const targets = projectionTargets(agentRoot, id);
199
202
  const nextProjectionDiagnostics = [];
@@ -202,7 +205,7 @@ export function createProjectSkillsReconciler(deps) {
202
205
  await mkdir(dirname(target.target), { recursive: true });
203
206
  await mkdir(target.staging, { mode: 0o700 });
204
207
  await markProjectionStaging(target, id);
205
- const preservedNames = await preserveNonProjectSkills(target, join(agentRoot, "training", "skills"), platform);
208
+ const preservedNames = await preserveNonProjectSkills(target, join(agentRoot, "training", "skills"), platform, linkedNames);
206
209
  for (const item of linked) {
207
210
  if (preservedNames.has(item.binding.skillName)) {
208
211
  throw new ProjectProjectionError("skill_name_conflict");