@nowcrew/daemon 0.6.41 → 0.6.42

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.
package/dist/config.js CHANGED
@@ -15,13 +15,10 @@ export const DEFAULT_EXECUTION_LIMITS = Object.freeze({
15
15
  maxQueuedPerAgent: 32,
16
16
  maxParallelTotal: 10,
17
17
  maxQueuedTotal: 128,
18
- maxStartingTotal: 10,
19
- maxStartingPerRuntime: 10,
20
- startupGapMs: 500,
18
+ maxStartingTotal: 1,
19
+ maxStartingPerRuntime: 1,
20
+ startupGapMs: 3_000,
21
21
  startupTimeoutMs: 120_000,
22
- maxQueueWaitMs: 5 * 60_000,
23
- maxFirstOutputWaitMs: 2 * 60_000,
24
- firstOutputGraceMs: 3 * 60_000,
25
22
  });
26
23
  // Fits the largest mandatory v1 lifecycle envelope (UUID + timestamps + outcome facts) with margin.
27
24
  export const MIN_EXECUTION_EVENT_BYTES = 512;
@@ -88,9 +85,6 @@ export function loadConfig(env = process.env) {
88
85
  maxStartingPerRuntime: positiveIntegerEnv(env, "CREW_EXECUTION_MAX_STARTING_PER_RUNTIME", DEFAULT_EXECUTION_LIMITS.maxStartingPerRuntime),
89
86
  startupGapMs: positiveIntegerEnv(env, "CREW_EXECUTION_START_GAP_MS", DEFAULT_EXECUTION_LIMITS.startupGapMs),
90
87
  startupTimeoutMs: positiveIntegerEnv(env, "CREW_EXECUTION_STARTUP_TIMEOUT_MS", DEFAULT_EXECUTION_LIMITS.startupTimeoutMs),
91
- maxQueueWaitMs: positiveIntegerEnv(env, "CREW_EXECUTION_MAX_QUEUE_WAIT_MS", DEFAULT_EXECUTION_LIMITS.maxQueueWaitMs),
92
- maxFirstOutputWaitMs: positiveIntegerEnv(env, "CREW_EXECUTION_MAX_FIRST_OUTPUT_WAIT_MS", DEFAULT_EXECUTION_LIMITS.maxFirstOutputWaitMs),
93
- firstOutputGraceMs: positiveIntegerEnv(env, "CREW_EXECUTION_FIRST_OUTPUT_GRACE_MS", DEFAULT_EXECUTION_LIMITS.firstOutputGraceMs),
94
88
  });
95
89
  return {
96
90
  serverUrl,
@@ -4,7 +4,7 @@ import { DaemonToServerExecutionFrameSchema, ExecutionCompletedSchema, Execution
4
4
  import { JournalConflictError } from "./execution-journal.js";
5
5
  import { boundExecutionFrame } from "./execution-event-limit.js";
6
6
  import { mintAgentToken } from "./token.js";
7
- import { executeLocal, RuntimeRunningCallbackTimeoutError, withLocalExecutionFacts, } from "./local-executor.js";
7
+ import { executeLocal, withLocalExecutionFacts, } from "./local-executor.js";
8
8
  import { startDormantSupervisor, } from "./execution-supervisor.js";
9
9
  import { CLAUDE_EFFORT_LEVELS } from "./runtimes/claude.js";
10
10
  import { CODEX_EFFORT_LEVELS } from "./runtimes/codex.js";
@@ -12,17 +12,18 @@ import { KIMI_EFFORT_LEVELS } from "./runtimes/kimi.js";
12
12
  import { executionBackendCapability } from "./execution-backend.js";
13
13
  import { readBoundImDecisionFile, resetBoundImDecisionFile } from "./bound-im-decision.js";
14
14
  import { RuntimeCancelledError } from "./runtime-cancellation.js";
15
+ import { HostReservationCancelledError } from "./host-execution-coordinator.js";
15
16
  import { supervisorLaunch } from "./supervised-runtime.js";
16
17
  import { appendAgentMemoryContext } from "./agent-memory/policy.js";
17
18
  import { ProjectSkillRuntimeOwnershipUnverifiedError, } from "./project-skills/reconciler.js";
18
19
  import { projectSkillExecutionProjection, projectSkillProjectionErrorCode } from "./project-skills/execution-adapter.js";
19
20
  import { redactProjectSkillRuntimeRootError, } from "./project-skills/runtime-launch.js";
20
21
  import { createProjectRegistry } from "./project-skills/registry.js";
21
- import { ProjectDependencyError, } from "./project-dependencies.js";
22
22
  import { ProjectContextUnavailableError, resolveProjectContext } from "./project-workspaces/resolver.js";
23
23
  import { PROJECT_WORKSPACES_CAPABILITY } from "./machine-info.js";
24
24
  import { PROJECT_SKILL_PROJECTION_V2_CAPABILITY } from "./project-skills/types.js";
25
25
  import { dslog } from "./slog.js";
26
+ import { identityRosterPromptFacts } from "./identity-roster-telemetry.js";
26
27
  export { supervisorLaunch } from "./supervised-runtime.js";
27
28
  const ACTIVITY_KIND = {
28
29
  init: "working",
@@ -297,12 +298,6 @@ class ExecutionCancelledError extends Error {
297
298
  this.name = "ExecutionCancelledError";
298
299
  }
299
300
  }
300
- class ExecutionQueueTimeoutError extends Error {
301
- constructor(timeoutMs) {
302
- super(`Execution queue wait exceeded ${timeoutMs}ms`);
303
- this.name = "ExecutionQueueTimeoutError";
304
- }
305
- }
306
301
  async function cancellable(promise, cancellation) {
307
302
  if (cancellation === undefined)
308
303
  return promise;
@@ -313,30 +308,13 @@ async function cancellable(promise, cancellation) {
313
308
  cancellation.requested.then(() => { throw new ExecutionCancelledError(); }),
314
309
  ]);
315
310
  }
316
- async function cancellableWithTimeout(promise, timeoutMs, cancellation, timeoutError) {
317
- let timer;
318
- try {
319
- return await Promise.race([
320
- cancellable(promise, cancellation),
321
- new Promise((_resolve, reject) => {
322
- timer = setTimeout(() => reject(timeoutError), timeoutMs);
323
- }),
324
- ]);
325
- }
326
- finally {
327
- if (timer !== undefined)
328
- clearTimeout(timer);
329
- }
330
- }
331
311
  function failedCompletion(spec, error, startedAt, finishedAt) {
332
312
  const message = error instanceof Error ? error.message : String(error);
333
- const errorCode = error instanceof ExecutionQueueTimeoutError
313
+ const errorCode = error instanceof HostReservationCancelledError
334
314
  ? "queue_timeout"
335
315
  : error instanceof ProjectContextUnavailableError
336
316
  ? error.code
337
- : error instanceof ProjectDependencyError
338
- ? error.code
339
- : projectSkillProjectionErrorCode(error) ?? "local_execution_failed";
317
+ : projectSkillProjectionErrorCode(error) ?? "local_execution_failed";
340
318
  return ExecutionCompletedSchema.parse({
341
319
  type: "execution:completed",
342
320
  protocolVersion: 1,
@@ -367,6 +345,15 @@ export async function runExecution(config, input, dependencies) {
367
345
  return { kind: "rejected", frame };
368
346
  }
369
347
  const spec = parsed.data;
348
+ dslog("execution.identity_roster_received", "daemon 已收到执行提示词中的工作区身份名册", {
349
+ execution_id: spec.executionId,
350
+ agent_handle: spec.agent.handle,
351
+ channel_id: spec.context.channelId,
352
+ thread_id: spec.context.threadId,
353
+ wake_message_id: spec.context.wakeMessageId,
354
+ runtime: spec.runtime.name,
355
+ ...identityRosterPromptFacts(spec.instructions.systemPrompt),
356
+ });
370
357
  const capabilityRejection = projectWorkspaceCapabilityRejection(spec, dependencies.capabilities, initialAt);
371
358
  if (capabilityRejection !== null) {
372
359
  const frame = ExecutionRejectedSchema.parse(boundExecutionFrame(capabilityRejection, config.executionLimits.maxEventBytes));
@@ -411,7 +398,6 @@ export async function runExecution(config, input, dependencies) {
411
398
  return { kind: "rejected", frame };
412
399
  }
413
400
  const { permission } = checked;
414
- const effectiveTimeoutMs = spec.runtime.timeoutMs ?? config.executionLimits.maxTimeoutMs;
415
401
  const accepted = await dependencies.journal.accept(spec.executionId, specHash, {
416
402
  runtime: spec.runtime.name,
417
403
  ...(spec.runtime.model === undefined ? {} : { model: spec.runtime.model }),
@@ -480,12 +466,7 @@ export async function runExecution(config, input, dependencies) {
480
466
  };
481
467
  let startedAt = accepted.acceptedAt;
482
468
  let runtimeCancel = null;
483
- let timeout;
484
- let firstOutputTimer;
485
- let timedOut = false;
486
- let firstOutputTimedOut = false;
487
469
  let completion;
488
- let abandonedRuntimeRunningCompletion = null;
489
470
  let memoryCaptureFinalText = null;
490
471
  let boundImDecision = spec.reporting.allowBoundImDecision
491
472
  ? "silent"
@@ -496,24 +477,11 @@ export async function runExecution(config, input, dependencies) {
496
477
  });
497
478
  try {
498
479
  if (dependencies.slot !== undefined) {
499
- await cancellableWithTimeout(dependencies.slot.ready, config.executionLimits.maxQueueWaitMs, dependencies.cancellation, new ExecutionQueueTimeoutError(config.executionLimits.maxQueueWaitMs));
480
+ await cancellable(dependencies.slot.ready, dependencies.cancellation);
500
481
  }
501
482
  let projectContext;
502
483
  let sessionContextFingerprint;
503
484
  const logicalProjectContext = spec.workspace.projectContext;
504
- dslog("project_dependencies.preflight", "项目依赖准备前置状态", {
505
- execution_id: spec.executionId,
506
- agent_handle: spec.agent.handle,
507
- has_ability_release: spec.agent.abilityRelease !== undefined,
508
- ability_release_id: spec.agent.abilityRelease?.releaseId,
509
- ability_root_commit: spec.agent.abilityRelease?.rootCommit,
510
- has_project_context: logicalProjectContext !== undefined,
511
- project_ids: JSON.stringify(logicalProjectContext?.projectIds ?? []),
512
- project_context_primary: logicalProjectContext?.primaryProjectId,
513
- dependency_prepare_expected: spec.agent.abilityRelease !== undefined
514
- && logicalProjectContext !== undefined
515
- && logicalProjectContext.projectIds.length > 0,
516
- });
517
485
  if (logicalProjectContext !== undefined && logicalProjectContext.projectIds.length > 0) {
518
486
  const projectSnapshot = {
519
487
  projectIds: logicalProjectContext.projectIds,
@@ -521,50 +489,7 @@ export async function runExecution(config, input, dependencies) {
521
489
  ? {}
522
490
  : { primaryProjectId: logicalProjectContext.primaryProjectId }),
523
491
  };
524
- const projectWorkspaceRegistry = dependencies.projectWorkspaceRegistry
525
- ?? dependencies.projectRegistry
526
- ?? createProjectRegistry(config.agentsRoot);
527
- if (spec.agent.abilityRelease !== undefined && dependencies.projectDependencies !== undefined) {
528
- if (dependencies.abilityRelease !== undefined) {
529
- await cancellable(dependencies.abilityRelease.apply(config.agentsRoot, spec.agent.handle, spec.agent.abilityRelease), dependencies.cancellation);
530
- }
531
- const preparedProjectDependencies = await cancellable(dependencies.projectDependencies.prepare({
532
- trainingRoot: join(config.agentsRoot, spec.agent.handle, "training"),
533
- projectsRoot: join(config.agentsRoot, "Projects"),
534
- projectIds: projectSnapshot.projectIds,
535
- }), dependencies.cancellation);
536
- for (const project of preparedProjectDependencies) {
537
- dslog("project_dependencies.prepared", "Training project dependency prepared", {
538
- execution_id: spec.executionId,
539
- agent_handle: spec.agent.handle,
540
- project_id: project.projectId,
541
- project_branch: project.branch,
542
- project_branch_source: project.branchSource,
543
- project_commit: project.resolvedCommit,
544
- project_checkout_mode: project.checkoutMode,
545
- });
546
- }
547
- }
548
- else if (spec.agent.abilityRelease === undefined) {
549
- dslog("project_dependencies.skipped", "缺少 abilityRelease,未执行项目依赖准备", {
550
- level: "WARN",
551
- execution_id: spec.executionId,
552
- agent_handle: spec.agent.handle,
553
- project_ids: JSON.stringify(projectSnapshot.projectIds),
554
- reason: "ability_release_missing",
555
- });
556
- }
557
- else {
558
- dslog("project_dependencies.skipped", "Daemon 未提供项目依赖准备器", {
559
- level: "WARN",
560
- execution_id: spec.executionId,
561
- agent_handle: spec.agent.handle,
562
- project_ids: JSON.stringify(projectSnapshot.projectIds),
563
- ability_release_id: spec.agent.abilityRelease.releaseId,
564
- reason: "provisioner_missing",
565
- });
566
- }
567
- projectContext = await cancellable(resolveProjectContext(projectSnapshot, projectWorkspaceRegistry), dependencies.cancellation);
492
+ projectContext = await cancellable(resolveProjectContext(projectSnapshot, dependencies.projectWorkspaceRegistry ?? createProjectRegistry(config.agentsRoot)), dependencies.cancellation);
568
493
  sessionContextFingerprint = projectSessionContextFingerprint(spec.runtime.name, projectSnapshot);
569
494
  }
570
495
  let recalledMemory = "";
@@ -583,88 +508,23 @@ export async function runExecution(config, input, dependencies) {
583
508
  let consoleSequence = 0;
584
509
  let externalOutputSequence = 0;
585
510
  const callbacks = {
586
- onRuntimeStarting: () => {
587
- dependencies.onRuntimePhase?.("starting", spec.runtime.name);
588
- },
589
- onRuntimeRunning: async () => {
590
- const callbackStartedAt = Date.now();
591
- let callbackStage = "journal";
511
+ onRuntimeReady: async () => {
592
512
  if (dependencies.cancellation?.isRequested())
593
513
  return;
594
- const cancel = runtimeCancel;
595
- if (cancel !== null && timeout === undefined && !dependencies.cancellation?.isRequested()) {
596
- if (effectiveTimeoutMs > config.executionLimits.maxFirstOutputWaitMs + config.executionLimits.firstOutputGraceMs) {
597
- firstOutputTimer = setTimeout(() => {
598
- firstOutputTimedOut = true;
599
- try {
600
- void cancel().catch(rejectCancellationFailure);
601
- }
602
- catch (error) {
603
- rejectCancellationFailure(error);
604
- }
605
- }, config.executionLimits.maxFirstOutputWaitMs + config.executionLimits.firstOutputGraceMs);
606
- }
607
- timeout = setTimeout(() => {
608
- timedOut = true;
609
- try {
610
- void cancel().catch(rejectCancellationFailure);
611
- }
612
- catch (error) {
613
- rejectCancellationFailure(error);
614
- }
615
- }, effectiveTimeoutMs);
616
- }
617
- dependencies.onRuntimePhase?.("running", spec.runtime.name);
618
- const journalStartedAt = Date.now();
619
- try {
620
- const ready = await dependencies.journal.markRuntimeReady(spec.executionId, now().toISOString());
621
- const journalMs = Date.now() - journalStartedAt;
622
- if (ready.runtimeReadyAt === null) {
623
- throw new Error(`Execution ${spec.executionId} runtime readiness was not persisted`);
624
- }
625
- if (abandonedRuntimeRunningCompletion !== null) {
626
- await dependencies.journal.complete(spec.executionId, abandonedRuntimeRunningCompletion);
627
- await dependencies.report(abandonedRuntimeRunningCompletion);
628
- return;
629
- }
630
- startedAt = ready.runtimeReadyAt;
631
- callbackStage = "started_report";
632
- const reportStartedAt = Date.now();
633
- const reportDelivered = await reportBestEffort(dependencies.report, boundExecutionFrame(DaemonToServerExecutionFrameSchema.parse({
634
- type: "execution:started",
635
- protocolVersion: 1,
636
- executionId: spec.executionId,
637
- at: startedAt,
638
- }), config.executionLimits.maxEventBytes), bestEffortTimeoutMs);
639
- dslog("execution.runtime_running_callback", "runtime-running callback 已完成", {
640
- execution_id: spec.executionId,
641
- runtime: spec.runtime.name,
642
- outcome: "succeeded",
643
- journal_ms: journalMs,
644
- started_report_ms: Date.now() - reportStartedAt,
645
- started_report_delivered: reportDelivered,
646
- callback_total_ms: Date.now() - callbackStartedAt,
647
- });
648
- }
649
- catch (error) {
650
- dslog("execution.runtime_running_callback", "runtime-running callback 失败", {
651
- level: "ERROR",
652
- execution_id: spec.executionId,
653
- runtime: spec.runtime.name,
654
- outcome: "failed",
655
- failed_stage: callbackStage,
656
- callback_total_ms: Date.now() - callbackStartedAt,
657
- error_message: error instanceof Error ? error.message.slice(0, 500) : String(error).slice(0, 500),
658
- });
659
- throw error;
514
+ const ready = await dependencies.journal.markRuntimeReady(spec.executionId, now().toISOString());
515
+ if (ready.runtimeReadyAt === null) {
516
+ throw new Error(`Execution ${spec.executionId} runtime readiness was not persisted`);
660
517
  }
518
+ startedAt = ready.runtimeReadyAt;
519
+ await reportBestEffort(dependencies.report, boundExecutionFrame(DaemonToServerExecutionFrameSchema.parse({
520
+ type: "execution:started",
521
+ protocolVersion: 1,
522
+ executionId: spec.executionId,
523
+ at: startedAt,
524
+ }), config.executionLimits.maxEventBytes), bestEffortTimeoutMs);
661
525
  },
662
526
  ...(spec.reporting.streamActivity ? {
663
527
  onActivity: (activity) => {
664
- if (firstOutputTimer !== undefined) {
665
- clearTimeout(firstOutputTimer);
666
- firstOutputTimer = undefined;
667
- }
668
528
  const frame = DaemonToServerExecutionFrameSchema.parse({
669
529
  type: "execution:activity",
670
530
  protocolVersion: 1,
@@ -683,10 +543,6 @@ export async function runExecution(config, input, dependencies) {
683
543
  } : {}),
684
544
  ...(spec.reporting.streamConsole ? {
685
545
  onConsole: (chunk) => {
686
- if (firstOutputTimer !== undefined) {
687
- clearTimeout(firstOutputTimer);
688
- firstOutputTimer = undefined;
689
- }
690
546
  const frame = DaemonToServerExecutionFrameSchema.parse({
691
547
  type: "execution:console",
692
548
  protocolVersion: 1,
@@ -706,10 +562,6 @@ export async function runExecution(config, input, dependencies) {
706
562
  } : {}),
707
563
  ...(spec.context.externalResponseSessionId || spec.context.answerStream ? {
708
564
  onExternalOutput: (text) => {
709
- if (firstOutputTimer !== undefined) {
710
- clearTimeout(firstOutputTimer);
711
- firstOutputTimer = undefined;
712
- }
713
565
  const frame = DaemonToServerExecutionFrameSchema.parse({
714
566
  type: "execution:output",
715
567
  protocolVersion: 1,
@@ -883,10 +735,6 @@ export async function runExecution(config, input, dependencies) {
883
735
  execute(localInput, callbacks, localDependencies),
884
736
  cancellationFailure,
885
737
  ]);
886
- if (timeout !== undefined)
887
- clearTimeout(timeout);
888
- if (firstOutputTimer !== undefined)
889
- clearTimeout(firstOutputTimer);
890
738
  const finishedAt = now().toISOString();
891
739
  if (result.exitCode === 0 && result.finalText?.trim())
892
740
  memoryCaptureFinalText = result.finalText;
@@ -896,32 +744,7 @@ export async function runExecution(config, input, dependencies) {
896
744
  await resetBoundImDecision(path);
897
745
  boundImDecision = selected?.decision ?? "silent";
898
746
  }
899
- completion = ExecutionCompletedSchema.parse(firstOutputTimedOut ? {
900
- type: "execution:completed",
901
- protocolVersion: 1,
902
- executionId: spec.executionId,
903
- outcome: "cancelled",
904
- errorCode: "runtime_no_first_output",
905
- errorMessage: "Runtime produced no activity before the first-output deadline",
906
- runtime: result.runtime,
907
- ...(result.model === null ? {} : { model: result.model }),
908
- resumed: result.resumed,
909
- startedAt,
910
- finishedAt,
911
- } : timedOut ? {
912
- type: "execution:completed",
913
- protocolVersion: 1,
914
- executionId: spec.executionId,
915
- outcome: "cancelled",
916
- errorCode: "timeout",
917
- errorMessage: "Execution exceeded its local timeout",
918
- runtime: result.runtime,
919
- ...(result.model === null ? {} : { model: result.model }),
920
- resumed: result.resumed,
921
- ...(boundImDecision ? { boundImDecision } : {}),
922
- startedAt,
923
- finishedAt,
924
- } : {
747
+ completion = ExecutionCompletedSchema.parse({
925
748
  type: "execution:completed",
926
749
  protocolVersion: 1,
927
750
  executionId: spec.executionId,
@@ -956,10 +779,6 @@ export async function runExecution(config, input, dependencies) {
956
779
  });
957
780
  }
958
781
  catch (error) {
959
- if (timeout !== undefined)
960
- clearTimeout(timeout);
961
- if (firstOutputTimer !== undefined)
962
- clearTimeout(firstOutputTimer);
963
782
  const cancelled = error instanceof ExecutionCancelledError || error instanceof RuntimeCancelledError;
964
783
  if (cancelled) {
965
784
  await closeLaunchGate();
@@ -976,7 +795,7 @@ export async function runExecution(config, input, dependencies) {
976
795
  else {
977
796
  await proveActiveSupervisorStopped(error, spec.agent.projectSkillBindingGeneration !== undefined);
978
797
  }
979
- const failureCompletion = cancelled
798
+ completion = cancelled
980
799
  ? ExecutionCompletedSchema.parse({
981
800
  type: "execution:completed",
982
801
  protocolVersion: 1,
@@ -991,12 +810,6 @@ export async function runExecution(config, input, dependencies) {
991
810
  finishedAt: now().toISOString(),
992
811
  })
993
812
  : failedCompletion(spec, error, startedAt, now().toISOString());
994
- if (error instanceof RuntimeRunningCallbackTimeoutError) {
995
- abandonedRuntimeRunningCompletion = boundExecutionFrame(failureCompletion, config.executionLimits.maxEventBytes);
996
- await telemetry.closeAndDrain();
997
- throw error;
998
- }
999
- completion = failureCompletion;
1000
813
  }
1001
814
  completion = boundExecutionFrame(completion, config.executionLimits.maxEventBytes);
1002
815
  await telemetry.closeAndDrain();
@@ -10,6 +10,12 @@ const HOST_STARTUP_SLOT_COUNT = 10;
10
10
  const HOST_STARTUP_GAP_MS = 500;
11
11
  const RETRY_MS = 50;
12
12
  const RELEASE_RETRY_MAX_MS = 5_000;
13
+ export class HostReservationCancelledError extends Error {
14
+ name = "HostReservationCancelledError";
15
+ constructor(message = "host execution reservation was cancelled before it could be granted") {
16
+ super(message);
17
+ }
18
+ }
13
19
  export function defaultHostExecutionCoordinatorRoot(userHome = homedir()) {
14
20
  // Deliberately independent of CREW_DAEMON_HOME and agentsRoot: every profile
15
21
  // owned by this OS user must share the same physical-compute safety boundary.
@@ -0,0 +1,37 @@
1
+ import { dslog } from "./slog.js";
2
+ const IDENTITY_ROSTER_OPEN = "<nowwork_workspace_identity_roster>";
3
+ const IDENTITY_ROSTER_CLOSE = "</nowwork_workspace_identity_roster>";
4
+ export function identityRosterPromptFacts(prompt) {
5
+ const start = prompt.indexOf(IDENTITY_ROSTER_OPEN);
6
+ if (start < 0)
7
+ return { identity_roster_marker_present: false };
8
+ const payloadStart = start + IDENTITY_ROSTER_OPEN.length;
9
+ const close = prompt.indexOf(IDENTITY_ROSTER_CLOSE, payloadStart);
10
+ const payload = prompt.slice(payloadStart, close < 0 ? prompt.length : close).trim();
11
+ let entryCount;
12
+ try {
13
+ const parsed = JSON.parse(payload);
14
+ if (Array.isArray(parsed))
15
+ entryCount = parsed.length;
16
+ }
17
+ catch {
18
+ // Keep the marker as evidence even when a malformed prompt reaches the daemon.
19
+ }
20
+ return {
21
+ identity_roster_marker_present: true,
22
+ identity_roster_section_bytes: Buffer.byteLength(payload, "utf8"),
23
+ ...(entryCount === undefined ? {} : { identity_roster_entry_count: entryCount }),
24
+ };
25
+ }
26
+ export function logIdentityRosterRuntimePrepared(input, runtime, prompt) {
27
+ dslog("execution.identity_roster_runtime_prepared", "daemon 已将工作区身份名册准备给 runtime", {
28
+ execution_id: input.executionId,
29
+ agent_handle: input.handle,
30
+ channel_id: input.channelId,
31
+ task_key: input.taskKey,
32
+ wake_message_id: input.wakeMessageId,
33
+ runtime,
34
+ prompt_bytes: Buffer.byteLength(prompt, "utf8"),
35
+ ...identityRosterPromptFacts(prompt),
36
+ });
37
+ }
@@ -26,6 +26,7 @@ import { boundedDiagnosticJsonArray } from "./diagnostic-json.js";
26
26
  import { ProjectProjectionError, } from "./project-skills/reconciler.js";
27
27
  import { formatProjectSkillRuntimeWarning } from "./project-skills/runtime-warning.js";
28
28
  import { projectSkillRuntimeDirectories, redactProjectSkillRuntimeRootText, redactProjectSkillRuntimeRootValue, } from "./project-skills/runtime-launch.js";
29
+ import { logIdentityRosterRuntimePrepared } from "./identity-roster-telemetry.js";
29
30
  import { diffMemoryPruneNotes, evaluateMemoryPrunePostcondition, inspectMemoryPruneFilesWithinDeadline, parseMemoryPruneTraceId, } from "./memory-prune-diagnostics.js";
30
31
  import { createLocalMemoryTelemetry, logLocalMemoryContextPrepareFailure, logLocalMemoryDiagnosticsFailure, } from "./local-memory-telemetry.js";
31
32
  import { CodexStartupStageParser } from "./codex-startup-stage.js";
@@ -547,6 +548,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
547
548
  ? {}
548
549
  : { memorySeedCreated: executionWorkspace.memorySeedCreated }),
549
550
  });
551
+ logIdentityRosterRuntimePrepared(input, runtime.name, effectiveSystemPrompt);
550
552
  if (abilityContext !== undefined) {
551
553
  dslog("ability.execution.context_loaded", "Agent training context loaded from workspace", {
552
554
  level: "INFO", execution_id: input.executionId, agent_handle: input.handle,
@@ -4,23 +4,11 @@ export const DEFAULT_FIRST_PROGRESS_TIMEOUT_MS = 120_000;
4
4
  * the runtime's configured total timeout remains authoritative; long-running tools are not killed
5
5
  * merely because they produce no output.
6
6
  */
7
- export function startFirstProgressWatchdog(onTimeout, timeoutMs = DEFAULT_FIRST_PROGRESS_TIMEOUT_MS) {
7
+ export function startFirstProgressWatchdog(_onTimeout, timeoutMs = DEFAULT_FIRST_PROGRESS_TIMEOUT_MS) {
8
8
  if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
9
9
  throw new RangeError("First-progress timeout must be a positive finite number");
10
10
  }
11
- let active = true;
12
- const timer = setTimeout(() => {
13
- if (!active)
14
- return;
15
- active = false;
16
- onTimeout();
17
- }, timeoutMs);
18
- timer.unref?.();
19
- const stop = () => {
20
- if (!active)
21
- return;
22
- active = false;
23
- clearTimeout(timer);
24
- };
25
- return { observe: stop, stop };
11
+ // Progress is observational only. Runtime tasks must not be terminated because
12
+ // an upstream provider is temporarily silent.
13
+ return { observe: () => undefined, stop: () => undefined };
26
14
  }
package/dist/workspace.js CHANGED
@@ -10,7 +10,7 @@
10
10
  */
11
11
  import { mkdir, writeFile, readFile, chmod, access } from "node:fs/promises";
12
12
  import { createHash, randomUUID } from "node:crypto";
13
- import { join } from "node:path";
13
+ import { dirname, join } from "node:path";
14
14
  import { dslog } from "./slog.js";
15
15
  /** 文件系统安全的 taskKey:仅留 [\w.-],其余转 _,截断,避免路径穿越/超长。 */
16
16
  export function safeKey(key) {
@@ -86,6 +86,10 @@ export async function prepareWorkspace(input) {
86
86
  await mkdir(runDir, { recursive: true });
87
87
  workLogPath = join(runDir, "work-log.md");
88
88
  }
89
+ // The first turn legitimately has no progress yet, but the path is part of the
90
+ // Runtime contract and memory-prune diagnostics expect it to be inspectable.
91
+ await mkdir(dirname(workLogPath), { recursive: true });
92
+ await writeFile(workLogPath, "", { flag: "a" });
89
93
  const workLog = (await exists(workLogPath)) ? await readFile(workLogPath, "utf8") : "";
90
94
  // resumeKey 可跨不同 task cwd 维持同一底层会话;协议键用 opaque 映射,legacy 保持 safeKey。
91
95
  // 未绑定项目时保留原有分支,避免改变旧 sessionDir 与 import 语义。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nowcrew/daemon",
3
- "version": "0.6.41",
3
+ "version": "0.6.42",
4
4
  "type": "module",
5
5
  "description": "crew daemon — 运行在用户机器:拉起/管理 agent 进程,注入 crew CLI,归一化 runtime 事件",
6
6
  "license": "Apache-2.0",