@nowcrew/daemon 0.6.19 → 0.6.21

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/atomic-no-replace-rename.js +91 -0
  2. package/dist/completion-retransmitter-logging.js +16 -0
  3. package/dist/completion-retransmitter.js +39 -4
  4. package/dist/control-plane-url.js +4 -2
  5. package/dist/directory-projection-publication.js +105 -0
  6. package/dist/directory-projection.js +20 -4
  7. package/dist/execution-journal.js +40 -4
  8. package/dist/execution-posix-stop-proof.js +82 -0
  9. package/dist/execution-runner.js +68 -8
  10. package/dist/local-executor.js +67 -52
  11. package/dist/machine-info.js +8 -5
  12. package/dist/project-skills/capability.js +109 -0
  13. package/dist/project-skills/controller-convergence.js +57 -0
  14. package/dist/project-skills/controller.js +80 -24
  15. package/dist/project-skills/initialized-reconciler.js +4 -4
  16. package/dist/project-skills/projection-state-domain.js +19 -2
  17. package/dist/project-skills/projection-state-store.js +3 -2
  18. package/dist/project-skills/projection-state-transaction.js +5 -1
  19. package/dist/project-skills/projection-state.js +1 -1
  20. package/dist/project-skills/reconciler.js +275 -102
  21. package/dist/project-skills/runtime-launch.js +102 -0
  22. package/dist/project-skills/runtime-root-bootstrap.js +47 -0
  23. package/dist/project-skills/runtime-root-domain.js +268 -0
  24. package/dist/project-skills/runtime-root-gc.js +293 -0
  25. package/dist/project-skills/runtime-root-lease-artifact.js +46 -0
  26. package/dist/project-skills/runtime-root-leases.js +487 -0
  27. package/dist/project-skills/runtime-root-source-identity.js +60 -0
  28. package/dist/project-skills/runtime-root-startup.js +49 -0
  29. package/dist/project-skills/runtime-root-state-artifact-domain.js +143 -0
  30. package/dist/project-skills/runtime-root-state-index.js +356 -0
  31. package/dist/project-skills/runtime-root-store.js +722 -0
  32. package/dist/project-skills/serve-capability.js +28 -0
  33. package/dist/project-skills/serve-startup.js +22 -0
  34. package/dist/project-skills/types.js +1 -0
  35. package/dist/runtimes/codex-home-migration-cli.js +26 -0
  36. package/dist/runtimes/codex-home-migration.js +112 -0
  37. package/dist/runtimes/codex-home.js +200 -17
  38. package/dist/serve.js +60 -79
  39. package/dist/supervised-runtime.js +1 -5
  40. package/package.json +2 -1
@@ -14,10 +14,13 @@ import { readBoundImDecisionFile, resetBoundImDecisionFile } from "./bound-im-de
14
14
  import { RuntimeCancelledError } from "./runtime-cancellation.js";
15
15
  import { supervisorLaunch } from "./supervised-runtime.js";
16
16
  import { appendAgentMemoryContext } from "./agent-memory/policy.js";
17
+ import { ProjectSkillRuntimeOwnershipUnverifiedError, } from "./project-skills/reconciler.js";
17
18
  import { projectSkillExecutionProjection, projectSkillProjectionErrorCode } from "./project-skills/execution-adapter.js";
19
+ import { redactProjectSkillRuntimeRootError, } from "./project-skills/runtime-launch.js";
18
20
  import { createProjectRegistry } from "./project-skills/registry.js";
19
21
  import { ProjectContextUnavailableError, resolveProjectContext } from "./project-workspaces/resolver.js";
20
22
  import { PROJECT_WORKSPACES_CAPABILITY } from "./machine-info.js";
23
+ import { PROJECT_SKILL_PROJECTION_V2_CAPABILITY } from "./project-skills/types.js";
21
24
  export { supervisorLaunch } from "./supervised-runtime.js";
22
25
  const ACTIVITY_KIND = {
23
26
  init: "working",
@@ -217,6 +220,10 @@ function rejection(executionId, reason, message, at) {
217
220
  export function projectWorkspaceCapabilityRejection(spec, capabilities, at) {
218
221
  const projectContext = spec.workspace.projectContext;
219
222
  const nativeRuntime = spec.runtime.name === "codex" || spec.runtime.name === "claude";
223
+ if (spec.agent.projectSkillBindingGeneration !== undefined
224
+ && (!nativeRuntime || !capabilities?.includes(PROJECT_SKILL_PROJECTION_V2_CAPABILITY))) {
225
+ return rejection(spec.executionId, "capability_missing", `${PROJECT_SKILL_PROJECTION_V2_CAPABILITY} is unavailable`, at);
226
+ }
220
227
  return projectContext !== undefined
221
228
  && projectContext.projectIds.length > 0
222
229
  && (!nativeRuntime || !capabilities?.includes(PROJECT_WORKSPACES_CAPABILITY))
@@ -423,6 +430,21 @@ export async function runExecution(config, input, dependencies) {
423
430
  const resetBoundImDecision = dependencies.resetBoundImDecision ?? resetBoundImDecisionFile;
424
431
  const telemetry = new TelemetryQueue(dependencies.report, bestEffortTimeoutMs, positiveTelemetryLimit(dependencies.telemetryMaxPendingFrames, DEFAULT_TELEMETRY_MAX_PENDING_FRAMES), Math.max(config.executionLimits.maxEventBytes, positiveTelemetryLimit(dependencies.telemetryMaxPendingBytes, DEFAULT_TELEMETRY_MAX_PENDING_BYTES)));
425
432
  const supervisorState = { active: null, abortOnce: null };
433
+ const proveActiveSupervisorStopped = async (error, retainProjectSkillLease = false) => {
434
+ if (error instanceof ProjectSkillRuntimeOwnershipUnverifiedError)
435
+ throw error;
436
+ if (supervisorState.active === null || supervisorState.abortOnce === null)
437
+ return;
438
+ try {
439
+ await supervisorState.abortOnce();
440
+ }
441
+ catch (abortError) {
442
+ if (retainProjectSkillLease)
443
+ throw new ProjectSkillRuntimeOwnershipUnverifiedError();
444
+ const detail = abortError instanceof Error ? abortError.message : String(abortError);
445
+ throw new AggregateError([error, abortError], `Failed to stop the execution supervisor: ${detail}`);
446
+ }
447
+ };
426
448
  let launchClosed = false;
427
449
  const launchAttempts = new Set();
428
450
  const closeLaunchGate = async () => {
@@ -557,6 +579,22 @@ export async function runExecution(config, input, dependencies) {
557
579
  },
558
580
  } : {}),
559
581
  };
582
+ // This is the sole production brand owner. Pre-spawn rejection has no process; startGuarded
583
+ // aborts its own failures, and this wrapper awaits full-tree stop for every later rejection.
584
+ const projectSkillLeaseSafeLaunchOwner = Object.freeze({
585
+ claim(launch) {
586
+ const safeLaunch = async (root) => {
587
+ try {
588
+ return await launch(root);
589
+ }
590
+ catch (error) {
591
+ await proveActiveSupervisorStopped(error, true);
592
+ throw redactProjectSkillRuntimeRootError(error, root);
593
+ }
594
+ };
595
+ return safeLaunch;
596
+ },
597
+ });
560
598
  const localDependencies = {
561
599
  ...(dependencies.cancellation === undefined ? {} : { cancellation: dependencies.cancellation }),
562
600
  ...(dependencies.startupGate === undefined ? {} : { startupGate: dependencies.startupGate }),
@@ -564,6 +602,7 @@ export async function runExecution(config, input, dependencies) {
564
602
  ? {}
565
603
  : { startupTimeoutMs: dependencies.startupTimeoutMs }),
566
604
  ...(dependencies.projectSkills === undefined ? {} : { projectSkills: dependencies.projectSkills }),
605
+ projectSkillLeaseSafeLaunchOwner,
567
606
  ...(dependencies.abilityRelease === undefined ? {} : { abilityRelease: dependencies.abilityRelease }),
568
607
  launchRuntime: async (request) => {
569
608
  if (launchClosed || dependencies.cancellation?.isRequested())
@@ -574,7 +613,26 @@ export async function runExecution(config, input, dependencies) {
574
613
  try {
575
614
  const processStartedAt = now().toISOString();
576
615
  const launchControl = { cancel: null };
577
- const guarded = await dependencies.journal.startGuarded(spec.executionId, processStartedAt, () => startSupervisor(supervisorLaunch(request)), {
616
+ const startOwnedSupervisor = async () => {
617
+ const handle = await startSupervisor(supervisorLaunch(request));
618
+ let abortPromise = null;
619
+ const abortOnce = () => {
620
+ if (abortPromise === null) {
621
+ try {
622
+ abortPromise = Promise.resolve(handle.abort());
623
+ }
624
+ catch (error) {
625
+ abortPromise = Promise.reject(error);
626
+ }
627
+ }
628
+ return abortPromise;
629
+ };
630
+ const ownedHandle = { ...handle, abort: abortOnce };
631
+ supervisorState.active = ownedHandle;
632
+ supervisorState.abortOnce = abortOnce;
633
+ return ownedHandle;
634
+ };
635
+ const guarded = await dependencies.journal.startGuarded(spec.executionId, processStartedAt, startOwnedSupervisor, {
578
636
  beforeRelease: ({ handle, abort }) => {
579
637
  supervisorState.active = handle;
580
638
  let stopPromise = null;
@@ -736,17 +794,19 @@ export async function runExecution(config, input, dependencies) {
736
794
  const cancelled = error instanceof ExecutionCancelledError || error instanceof RuntimeCancelledError;
737
795
  if (cancelled) {
738
796
  await closeLaunchGate();
739
- await dependencies.cancellation?.waitForStop();
740
- }
741
- else if (supervisorState.active !== null && supervisorState.abortOnce !== null) {
742
797
  try {
743
- await supervisorState.abortOnce();
798
+ await dependencies.cancellation?.waitForStop();
744
799
  }
745
- catch (abortError) {
746
- const detail = abortError instanceof Error ? abortError.message : String(abortError);
747
- throw new AggregateError([error, abortError], `Failed to stop the execution supervisor: ${detail}`);
800
+ catch (stopError) {
801
+ if (spec.agent.projectSkillBindingGeneration !== undefined) {
802
+ throw new ProjectSkillRuntimeOwnershipUnverifiedError();
803
+ }
804
+ throw stopError;
748
805
  }
749
806
  }
807
+ else {
808
+ await proveActiveSupervisorStopped(error, spec.agent.projectSkillBindingGeneration !== undefined);
809
+ }
750
810
  completion = cancelled
751
811
  ? ExecutionCompletedSchema.parse({
752
812
  type: "execution:completed",
@@ -2,7 +2,7 @@ import { createInterface } from "node:readline";
2
2
  import { readFile, rm, writeFile } from "node:fs/promises";
3
3
  import { delimiter, join } from "node:path";
4
4
  import { prepareWorkspace, rotateAgentSession, safeKey, } from "./workspace.js";
5
- import { CLAUDE_ADDITIONAL_DIRECTORY_INSTRUCTIONS_ENV, isClaudeAdditionalDirectoryInstructionsSupported, probeClaudeVersion, resolveOrderedUniqueClaudeDirectories, spawnClaude, } from "./runtimes/claude.js";
5
+ import { CLAUDE_ADDITIONAL_DIRECTORY_INSTRUCTIONS_ENV, isClaudeAdditionalDirectoryInstructionsSupported, probeClaudeVersion, spawnClaude, } from "./runtimes/claude.js";
6
6
  import { spawnCodex } from "./runtimes/codex.js";
7
7
  import { DEEPSEEK_CODEX_MODEL, DEEPSEEK_CODEX_REASONING_LEVELS, materializeDeepSeekCodexHome, } from "./runtimes/codex-deepseek-config.js";
8
8
  import { materializeDefaultCodexHome } from "./runtimes/codex-home.js";
@@ -21,7 +21,9 @@ import { awaitWithCancellation, RuntimeCancelledError, } from "./runtime-cancell
21
21
  import { isRuntimeReadyEvent, } from "./runtime-startup-gate.js";
22
22
  import { dslog } from "./slog.js";
23
23
  import { boundedDiagnosticJsonArray } from "./diagnostic-json.js";
24
+ import { ProjectProjectionError, } from "./project-skills/reconciler.js";
24
25
  import { formatProjectSkillRuntimeWarning } from "./project-skills/runtime-warning.js";
26
+ import { projectSkillRuntimeDirectories, redactProjectSkillRuntimeRootText, redactProjectSkillRuntimeRootValue, } from "./project-skills/runtime-launch.js";
25
27
  import { diffMemoryPruneNotes, evaluateMemoryPrunePostcondition, inspectMemoryPruneFilesWithinDeadline, parseMemoryPruneTraceId, } from "./memory-prune-diagnostics.js";
26
28
  import { createLocalMemoryTelemetry, logLocalMemoryContextPrepareFailure, logLocalMemoryDiagnosticsFailure, } from "./local-memory-telemetry.js";
27
29
  import { CodexStartupStageParser } from "./codex-startup-stage.js";
@@ -195,6 +197,19 @@ async function withKeyedLease(key, operation, cancellation) {
195
197
  }
196
198
  }
197
199
  export async function executeLocal(input, callbacks = {}, dependencies = {}) {
200
+ const versionedProjectSkills = input.projectSkills !== undefined
201
+ && !Array.isArray(input.projectSkills);
202
+ if (versionedProjectSkills && (input.runtime.name !== "codex" && input.runtime.name !== "claude")) {
203
+ throw new ProjectProjectionError("skill_projection_failed");
204
+ }
205
+ if (versionedProjectSkills && dependencies.projectSkills === undefined) {
206
+ throw new ProjectProjectionError("skill_projection_failed");
207
+ }
208
+ if (input.projectSkills !== undefined
209
+ && dependencies.projectSkills !== undefined
210
+ && dependencies.projectSkillLeaseSafeLaunchOwner === undefined) {
211
+ throw new ProjectProjectionError("skill_projection_failed");
212
+ }
198
213
  const stableProtocolRuntime = dependencies.launchRuntime !== undefined;
199
214
  const supportsNativeResume = input.runtime.name === "claude"
200
215
  || (stableProtocolRuntime && runtimeCapability(input.runtime.name).nativeResume);
@@ -263,6 +278,8 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
263
278
  if (key.startsWith("CREW_AGENT_MEMORY_"))
264
279
  delete inheritedEnv[key];
265
280
  }
281
+ const sourceCodexHome = inheritedEnv.CODEX_HOME
282
+ ?? (inheritedEnv.HOME === undefined ? undefined : join(inheritedEnv.HOME, ".codex"));
266
283
  try {
267
284
  if (isDeepSeekCodex && !providerConfig.providerApiKey) {
268
285
  throw new Error("DeepSeek API key is not configured for this Agent");
@@ -270,11 +287,6 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
270
287
  if (isDeepSeekCodex) {
271
288
  await awaitWithCancellation((dependencies.materializeDeepSeekCodexHome ?? materializeDeepSeekCodexHome)(workspace.homeDir), dependencies.cancellation);
272
289
  }
273
- else if (runtime.name === "codex") {
274
- const sourceCodexHome = inheritedEnv.CODEX_HOME
275
- ?? (inheritedEnv.HOME === undefined ? undefined : join(inheritedEnv.HOME, ".codex"));
276
- await awaitWithCancellation((dependencies.materializeDefaultCodexHome ?? materializeDefaultCodexHome)(workspace.homeDir, sourceCodexHome), dependencies.cancellation);
277
- }
278
290
  const supportsNativeResume = runtime.name === "claude"
279
291
  || (dependencies.launchRuntime !== undefined && runtimeCapability(runtime.name).nativeResume);
280
292
  const storedCurrentPrior = input.session.enabled && supportsNativeResume
@@ -336,6 +348,9 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
336
348
  const launchSessionId = resumeSessionId ?? (rotated
337
349
  ? await rotateAgentSession(workspace.sessionDir)
338
350
  : workspace.agentSessionId);
351
+ if (runtime.name === "codex") {
352
+ await awaitWithCancellation((dependencies.materializeDefaultCodexHome ?? materializeDefaultCodexHome)(workspace.homeDir, sourceCodexHome, launchSessionId), dependencies.cancellation);
353
+ }
339
354
  const promptContext = {
340
355
  workspace: executionWorkspace,
341
356
  resuming,
@@ -485,7 +500,9 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
485
500
  }
486
501
  const runtimeLaunchAt = Date.now();
487
502
  memoryPruneFailurePhase = "runtime_launch";
488
- const launchPreparedRuntime = async (abilityContext) => {
503
+ let activeProjectSkillRuntimeRoot;
504
+ const launchPreparedRuntime = async (runtimeRoot, abilityContext) => {
505
+ activeProjectSkillRuntimeRoot = runtimeRoot;
489
506
  const effectiveSystemPrompt = abilityContext === undefined
490
507
  ? systemPrompt
491
508
  : `${systemPrompt}\n\n${abilityContext.prompt}`;
@@ -521,31 +538,16 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
521
538
  skill_count: abilityContext.skillCount,
522
539
  });
523
540
  }
524
- const codexSkillRoots = runtime.name === "codex" && input.projectContext !== undefined
525
- ? [
526
- ...(input.projectSkills === undefined
527
- ? []
528
- : [join(workspace.dir, ".agents", "skills")]),
529
- ...(abilityContext?.skillRoot === undefined ? [] : [abilityContext.skillRoot]),
530
- ]
531
- : [];
532
- const claudeAdditionalDirectories = runtime.name === "claude"
533
- ? input.projectContext === undefined
534
- ? abilityContext?.skillRoot === undefined
535
- ? []
536
- : await resolveOrderedUniqueClaudeDirectories([
537
- join(workspace.dir, ".crew", "claude-skills"),
538
- workspace.dir,
539
- abilityContext.skillRoot,
540
- ])
541
- : await resolveOrderedUniqueClaudeDirectories([
542
- ...input.projectContext.secondary.map((project) => project.root),
543
- ...(input.projectSkills === undefined
544
- ? []
545
- : [join(workspace.dir, ".crew", "claude-skills")]),
546
- ...(abilityContext?.skillRoot === undefined ? [] : [abilityContext.skillRoot]),
547
- ], input.projectContext.primary === undefined ? [] : [input.projectContext.primary.root])
548
- : [];
541
+ const { codexSkillRoots, claudeAdditionalDirectories } = await projectSkillRuntimeDirectories({
542
+ runtime: runtime.name,
543
+ ...(runtimeRoot === undefined ? {} : { runtimeRoot }),
544
+ agentRoot: workspace.dir,
545
+ projectSkillsPresent: input.projectSkills !== undefined,
546
+ ...(input.projectContext === undefined ? {} : { projectContext: input.projectContext }),
547
+ ...(abilityContext?.skillRoot === undefined
548
+ ? {}
549
+ : { abilitySkillRoot: abilityContext.skillRoot }),
550
+ });
549
551
  let runtimeEnv = childEnv;
550
552
  if (runtime.name === "claude"
551
553
  && input.projectContext !== undefined
@@ -571,7 +573,10 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
571
573
  runtime: runtime.name,
572
574
  bin: runtime.name === "deepseek-harness" ? "dsh-acp-demo" : runtime.name,
573
575
  cwd: input.projectContext?.primary?.root ?? executionWorkspace.runDir,
574
- ...(input.projectSkills === undefined && input.abilityRelease === undefined ? {} : { agentRoot: workspace.dir }),
576
+ ...(runtimeRoot !== undefined
577
+ || (input.projectSkills === undefined && input.abilityRelease === undefined)
578
+ ? {}
579
+ : { agentRoot: workspace.dir }),
575
580
  systemPromptPath: workspace.systemPromptPath,
576
581
  systemPrompt: effectiveSystemPrompt,
577
582
  wakePrompt,
@@ -598,17 +603,22 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
598
603
  memoryPruneFailurePhase = "runtime_launch";
599
604
  return launchRuntime(launchRequest);
600
605
  };
601
- const launchWithAbility = () => input.abilityRelease !== undefined && dependencies.abilityRelease !== undefined
602
- ? dependencies.abilityRelease.prepareAndLaunch(input.launch.agentsRoot, input.handle, input.abilityRelease, launchPreparedRuntime)
603
- : launchPreparedRuntime();
604
- const child = input.projectSkills !== undefined && dependencies.projectSkills !== undefined
605
- ? await (Array.isArray(input.projectSkills)
606
- ? dependencies.projectSkills.prepareAndLaunch(input.launch.agentsRoot, input.handle, input.projectSkills, launchWithAbility)
607
- : dependencies.projectSkills.prepareAndLaunch(input.launch.agentsRoot, input.handle, input.projectSkills, launchWithAbility, (warning) => callbacks.onConsole?.({
608
- stream: "system",
609
- text: formatProjectSkillRuntimeWarning(warning),
610
- })))
611
- : await launchWithAbility();
606
+ const launchWithAbility = (runtimeRoot) => input.abilityRelease !== undefined && dependencies.abilityRelease !== undefined
607
+ ? dependencies.abilityRelease.prepareAndLaunch(input.launch.agentsRoot, input.handle, input.abilityRelease, (abilityContext) => launchPreparedRuntime(runtimeRoot, abilityContext))
608
+ : launchPreparedRuntime(runtimeRoot);
609
+ let child;
610
+ if (input.projectSkills !== undefined && dependencies.projectSkills !== undefined) {
611
+ const launchOwner = dependencies.projectSkillLeaseSafeLaunchOwner;
612
+ if (launchOwner === undefined)
613
+ throw new ProjectProjectionError("skill_projection_failed");
614
+ child = await dependencies.projectSkills.prepareAndLaunch(input.launch.agentsRoot, input.executionId, input.handle, input.projectSkills, launchOwner.claim(launchWithAbility), (warning) => callbacks.onConsole?.({
615
+ stream: "system",
616
+ text: formatProjectSkillRuntimeWarning(warning),
617
+ }));
618
+ }
619
+ else {
620
+ child = await launchWithAbility();
621
+ }
612
622
  localMemoryTelemetry?.markRuntimeStarted();
613
623
  memoryPruneFailurePhase = "runtime_execution";
614
624
  if (child.cancel !== undefined) {
@@ -660,7 +670,8 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
660
670
  usage = meta.usage;
661
671
  if (meta.model)
662
672
  observedModel = meta.model;
663
- for (const activity of normalizeEvent(event)) {
673
+ for (const rawActivity of normalizeEvent(event)) {
674
+ const activity = redactProjectSkillRuntimeRootValue(rawActivity, activeProjectSkillRuntimeRoot);
664
675
  if (activity.kind === "sending")
665
676
  sentViaCrew = true;
666
677
  activities.push(activity);
@@ -668,18 +679,20 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
668
679
  }
669
680
  const extracted = extractFinalText(event);
670
681
  if (extracted) {
682
+ const safeExtracted = redactProjectSkillRuntimeRootText(extracted, activeProjectSkillRuntimeRoot);
671
683
  const incremental = typeof event === "object" && event !== null
672
684
  && "type" in event && (event.type === "kimi.acp.text_delta"
673
685
  || event.type === "hermes.acp.text_delta"
674
686
  || event.type === "deepseek-harness.acp.text_delta"
675
687
  || event.type === "opencode.text_delta");
676
- finalText = incremental ? `${finalText ?? ""}${extracted}` : extracted;
688
+ finalText = incremental ? `${finalText ?? ""}${safeExtracted}` : safeExtracted;
677
689
  }
678
690
  for (const text of decodeExternalOutputEvent(runtime.name, event, externalOutput)) {
679
- callbacks.onExternalOutput?.(text);
691
+ callbacks.onExternalOutput?.(redactProjectSkillRuntimeRootText(text, activeProjectSkillRuntimeRoot));
692
+ }
693
+ for (const chunk of consoleFormatter.format(event)) {
694
+ callbacks.onConsole?.(redactProjectSkillRuntimeRootValue(chunk, activeProjectSkillRuntimeRoot));
680
695
  }
681
- for (const chunk of consoleFormatter.format(event))
682
- callbacks.onConsole?.(chunk);
683
696
  });
684
697
  let stderrTail = "";
685
698
  const codexStartupStageState = { last: null };
@@ -700,8 +713,8 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
700
713
  })
701
714
  : null;
702
715
  child.stderr.on("data", (data) => {
703
- process.stderr.write(data);
704
- const text = String(data);
716
+ const text = redactProjectSkillRuntimeRootText(String(data), activeProjectSkillRuntimeRoot);
717
+ process.stderr.write(text);
705
718
  stderrTail = (stderrTail + text).slice(-STDERR_TAIL_CAP);
706
719
  codexStartupStageParser?.push(text);
707
720
  });
@@ -761,7 +774,9 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
761
774
  memoryPruneRuntimeExitCode = exitCode;
762
775
  const errorTail = [
763
776
  stderrTail.trim(),
764
- spawnError,
777
+ spawnError === undefined
778
+ ? undefined
779
+ : redactProjectSkillRuntimeRootText(spawnError, activeProjectSkillRuntimeRoot),
765
780
  terminationSignal ? `terminated by ${terminationSignal}` : undefined,
766
781
  ].filter(Boolean).join(" ").trim();
767
782
  const finish = exitActivity(runtime.name, exitCode, errorTail);
@@ -61,7 +61,9 @@ export const daemonCapabilities = (runtimePlatform = process.platform, readiness
61
61
  export const daemonCapabilityBindings = (runtimePlatform = process.platform, injectedCapabilities) => {
62
62
  const snapshot = injectedCapabilities === undefined
63
63
  ? daemonCapabilities(runtimePlatform)
64
- : Object.freeze([...injectedCapabilities]);
64
+ : (Object.isFrozen(injectedCapabilities)
65
+ ? injectedCapabilities
66
+ : Object.freeze([...injectedCapabilities]));
65
67
  return Object.freeze({
66
68
  controlPlaneUrl: snapshot,
67
69
  machineHello: snapshot,
@@ -169,6 +171,10 @@ export async function collectMachineHello(agentsRoot, executionLimits, runtimePl
169
171
  const executionRuntimes = backend.supported
170
172
  ? await (dependencies.detectExecutable ?? detectExecutionRuntimes)(runtimes)
171
173
  : [];
174
+ const baseCapabilities = dependencies.capabilities ?? daemonCapabilities(runtimePlatform);
175
+ const capabilities = additionalCapabilities.length === 0 && Object.isFrozen(baseCapabilities)
176
+ ? baseCapabilities
177
+ : Object.freeze([...baseCapabilities, ...additionalCapabilities]);
172
178
  return {
173
179
  type: "machine:hello",
174
180
  hostname: hostname(),
@@ -176,10 +182,7 @@ export async function collectMachineHello(agentsRoot, executionLimits, runtimePl
176
182
  daemonVersion: daemonVersion(),
177
183
  runtimes,
178
184
  executionRuntimes,
179
- capabilities: Object.freeze([
180
- ...(dependencies.capabilities ?? daemonCapabilities(runtimePlatform)),
181
- ...additionalCapabilities,
182
- ]),
185
+ capabilities,
183
186
  ...(dependencies.profileName === undefined ? {} : {
184
187
  layoutVersion: 1,
185
188
  profileName: dependencies.profileName,
@@ -0,0 +1,109 @@
1
+ import { PROJECT_SKILLS_CAPABILITY, PROJECT_SKILL_PROJECTION_V2_CAPABILITY, } from "./types.js";
2
+ export function projectSkillV2RolloutReady(platform, readiness) {
3
+ const platformReady = platform === "darwin"
4
+ || platform === "linux"
5
+ || (platform === "win32" && readiness.nativeWindowsSmokePassed);
6
+ return platformReady
7
+ && readiness.runtimeRootStorePrimitiveReady
8
+ && readiness.runtimeRootStoreRecoveryReady
9
+ && readiness.leaseRecoveryReady
10
+ && readiness.controllerReady
11
+ && readiness.executionHandlerReady
12
+ && readiness.codexAdapterReady
13
+ && readiness.claudeAdapterReady;
14
+ }
15
+ export function effectiveProjectSkillCapabilities(capabilities, projectSkillsReady, projectSkillV2Ready) {
16
+ const projectSkillCapabilitiesReady = projectSkillsReady && projectSkillV2Ready;
17
+ if (!projectSkillCapabilitiesReady
18
+ && !capabilities.includes(PROJECT_SKILLS_CAPABILITY)
19
+ && !capabilities.includes(PROJECT_SKILL_PROJECTION_V2_CAPABILITY)
20
+ && Object.isFrozen(capabilities))
21
+ return capabilities;
22
+ const filtered = capabilities.filter((capability) => capability !== PROJECT_SKILL_PROJECTION_V2_CAPABILITY
23
+ && (projectSkillCapabilitiesReady || capability !== PROJECT_SKILLS_CAPABILITY));
24
+ if (!projectSkillCapabilitiesReady || !filtered.includes(PROJECT_SKILLS_CAPABILITY)) {
25
+ return Object.freeze(filtered);
26
+ }
27
+ const v1Index = filtered.indexOf(PROJECT_SKILLS_CAPABILITY);
28
+ return Object.freeze([
29
+ ...filtered.slice(0, v1Index + 1),
30
+ PROJECT_SKILL_PROJECTION_V2_CAPABILITY,
31
+ ...filtered.slice(v1Index + 1),
32
+ ]);
33
+ }
34
+ export function withProjectSkillCapabilityStatus(value, status) {
35
+ return {
36
+ ...value,
37
+ projectSkillsStatus: status,
38
+ capabilities: status === "ready"
39
+ ? value.capabilities
40
+ : effectiveProjectSkillCapabilities(value.capabilities, false, false),
41
+ };
42
+ }
43
+ const settleReadinessGate = async (operation) => {
44
+ try {
45
+ return await operation() === true;
46
+ }
47
+ catch {
48
+ return false;
49
+ }
50
+ };
51
+ export async function createProjectSkillConnectionCapabilitySnapshot(input) {
52
+ const runtimeRootStorePrimitiveReady = await settleReadinessGate(input.probeRuntimeRootStorePrimitive);
53
+ const controllerReady = await settleReadinessGate(input.initializeController);
54
+ const recoveryReady = await settleReadinessGate(input.recoverRuntimeRootsAndLeases);
55
+ const readiness = Object.freeze({
56
+ runtimeRootStorePrimitiveReady,
57
+ runtimeRootStoreRecoveryReady: recoveryReady,
58
+ leaseRecoveryReady: recoveryReady,
59
+ controllerReady,
60
+ executionHandlerReady: true,
61
+ codexAdapterReady: true,
62
+ claudeAdapterReady: true,
63
+ nativeWindowsSmokePassed: false,
64
+ });
65
+ const projectSkillsReady = projectSkillV2RolloutReady(input.platform, readiness);
66
+ return Object.freeze({
67
+ capabilities: effectiveProjectSkillCapabilities(input.baseCapabilities, projectSkillsReady, projectSkillsReady),
68
+ projectSkillsReady,
69
+ readiness,
70
+ });
71
+ }
72
+ export async function prepareProjectSkillConnectionCapabilities(input) {
73
+ const snapshot = await createProjectSkillConnectionCapabilitySnapshot({
74
+ ...input,
75
+ baseCapabilities: [...input.baseCapabilities, ...await input.additionalCapabilities()],
76
+ });
77
+ input.applyReadiness(snapshot.projectSkillsReady);
78
+ return snapshot.capabilities;
79
+ }
80
+ export function createProjectSkillControllerReadiness(input) {
81
+ let status = "initializing";
82
+ let initialization = null;
83
+ const ensure = () => {
84
+ if (status === "ready")
85
+ return Promise.resolve(true);
86
+ if (initialization !== null)
87
+ return initialization;
88
+ status = "initializing";
89
+ const attempt = input.initialize().then(() => {
90
+ status = "ready";
91
+ return true;
92
+ }, (error) => {
93
+ status = "unavailable";
94
+ input.onUnavailable(error);
95
+ return false;
96
+ }).finally(() => {
97
+ if (initialization === attempt)
98
+ initialization = null;
99
+ input.onSettled();
100
+ });
101
+ initialization = attempt;
102
+ return attempt;
103
+ };
104
+ return Object.freeze({
105
+ status: () => status,
106
+ ensure,
107
+ applyPreflight: (ready) => { status = ready ? "ready" : "unavailable"; },
108
+ });
109
+ }
@@ -0,0 +1,57 @@
1
+ import { ProjectProjectionError, } from "./reconciler.js";
2
+ export const MAX_REMEMBERED_PROJECT_SKILL_AGENTS = 64;
3
+ const compareBinary = (left, right) => {
4
+ if (left < right)
5
+ return -1;
6
+ if (left > right)
7
+ return 1;
8
+ return 0;
9
+ };
10
+ const copySnapshot = (snapshot) => Object.freeze({
11
+ bindings: Object.freeze(snapshot.bindings.map((binding) => Object.freeze({ ...binding }))),
12
+ generation: snapshot.generation,
13
+ });
14
+ export function createProjectSkillSnapshotEpoch(maxEntries = MAX_REMEMBERED_PROJECT_SKILL_AGENTS) {
15
+ if (!Number.isSafeInteger(maxEntries) || maxEntries < 1) {
16
+ throw new ProjectProjectionError("skill_projection_failed");
17
+ }
18
+ const snapshots = new Map();
19
+ return Object.freeze({
20
+ begin() {
21
+ snapshots.clear();
22
+ },
23
+ remember(handle, snapshot) {
24
+ const previous = snapshots.get(handle);
25
+ if (previous !== undefined && snapshot.generation < previous.generation)
26
+ return;
27
+ if (previous === undefined && snapshots.size >= maxEntries) {
28
+ throw new ProjectProjectionError("skill_projection_failed");
29
+ }
30
+ snapshots.set(handle, copySnapshot(snapshot));
31
+ },
32
+ forget(handle) {
33
+ snapshots.delete(handle);
34
+ },
35
+ entries() {
36
+ return Object.freeze([...snapshots.entries()]
37
+ .sort(([left], [right]) => compareBinary(left, right))
38
+ .map(([handle, snapshot]) => Object.freeze([handle, copySnapshot(snapshot)])));
39
+ },
40
+ });
41
+ }
42
+ const stableErrorCode = (reason) => reason?.code === "skill_projection_snapshot_corrupt"
43
+ ? "skill_projection_snapshot_corrupt"
44
+ : "skill_projection_failed";
45
+ export function toPathPrivateConvergenceResults(entries, results) {
46
+ return Object.freeze(entries.map(([handle], index) => {
47
+ const result = results[index];
48
+ if (result?.status === "fulfilled") {
49
+ return Object.freeze({ handle, status: "fulfilled" });
50
+ }
51
+ return Object.freeze({
52
+ handle,
53
+ status: "rejected",
54
+ errorCode: stableErrorCode(result?.reason),
55
+ });
56
+ }));
57
+ }