@nowcrew/daemon 0.6.11 → 0.6.13

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 (42) hide show
  1. package/README.md +6 -3
  2. package/dist/agent-ability/materializer.js +4 -3
  3. package/dist/agent-ability/resolver.js +13 -3
  4. package/dist/agent-ability/types.js +11 -7
  5. package/dist/config.js +4 -2
  6. package/dist/execution-runner.js +4 -1
  7. package/dist/host-execution-coordinator.js +3 -3
  8. package/dist/main.js +0 -0
  9. package/dist/memory-prune-diagnostics.js +6 -0
  10. package/dist/remote/claude-bridge.js +558 -0
  11. package/dist/remote/claude-channel.js +164 -0
  12. package/dist/remote/codex-client.js +451 -0
  13. package/dist/remote/codex-runtime.js +77 -0
  14. package/dist/remote/config.js +135 -0
  15. package/dist/remote/gateway.js +879 -0
  16. package/dist/remote/identity.js +39 -0
  17. package/dist/remote/owner.js +77 -0
  18. package/dist/remote/protocol.js +211 -0
  19. package/dist/remote/remote-cli.js +254 -0
  20. package/dist/remote/runtime-probe.js +182 -0
  21. package/dist/remote/session-discovery.js +249 -0
  22. package/dist/remote/wrapper.js +40 -0
  23. package/dist/remote-web/assets/index-B_6VM_tw.js +94 -0
  24. package/dist/remote-web/assets/index-L6EiQbJn.css +1 -0
  25. package/dist/remote-web/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  26. package/dist/remote-web/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  27. package/dist/remote-web/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  28. package/dist/remote-web/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  29. package/dist/remote-web/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  30. package/dist/remote-web/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  31. package/dist/remote-web/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  32. package/dist/remote-web/icons/nowwork-192.png +0 -0
  33. package/dist/remote-web/icons/nowwork-512.png +0 -0
  34. package/dist/remote-web/icons/nowwork.svg +7 -0
  35. package/dist/remote-web/index.html +20 -0
  36. package/dist/remote-web/manifest.webmanifest +13 -0
  37. package/dist/remote-web/sw.js +12 -0
  38. package/dist/serve.js +9 -3
  39. package/dist/shared-execution-slots.js +17 -9
  40. package/dist/slog.js +11 -0
  41. package/dist/workspace.js +18 -17
  42. package/package.json +9 -10
package/README.md CHANGED
@@ -371,9 +371,10 @@ CREW_RUNTIME_SAFE=1
371
371
  CREW_EXECUTION_MAX_PROMPT_BYTES=256000
372
372
  CREW_EXECUTION_MAX_TIMEOUT_MS=10800000
373
373
  CREW_EXECUTION_MAX_EVENT_BYTES=64000
374
- CREW_MAX_PARALLEL=4
374
+ CREW_MAX_PARALLEL=10
375
+ CREW_SCHEDULED_MAX_PARALLEL=4
375
376
  CREW_EXECUTION_MAX_QUEUED_PER_AGENT=32
376
- CREW_EXECUTION_MAX_PARALLEL_TOTAL=4
377
+ CREW_EXECUTION_MAX_PARALLEL_TOTAL=10
377
378
  CREW_EXECUTION_MAX_QUEUED_TOTAL=128
378
379
  CREW_EXECUTION_MAX_STARTING_TOTAL=1
379
380
  CREW_EXECUTION_MAX_STARTING_PER_RUNTIME=1
@@ -381,7 +382,9 @@ CREW_EXECUTION_START_GAP_MS=3000
381
382
  CREW_EXECUTION_STARTUP_TIMEOUT_MS=120000
382
383
  ```
383
384
 
384
- `MAX_PARALLEL_TOTAL` is the machine-wide process cap shared by protocol-v1 and legacy work.
385
+ `CREW_MAX_PARALLEL` limits normal executions per Agent. `CREW_SCHEDULED_MAX_PARALLEL` separately
386
+ limits scheduled executions per Agent. `MAX_PARALLEL_TOTAL` is the machine-wide process cap shared by
387
+ protocol-v1 and legacy work.
385
388
  Runtime startup is a separate FIFO gate: by default only one Claude, Codex, or Kimi process may be
386
389
  initializing at a time, and launches are spaced by three seconds. A process leaves the startup gate
387
390
  only after its runtime-specific ready event; startup timeout cancels the owned process tree.
@@ -440,8 +440,6 @@ const materializeWorkspaceTraining = async (agentsRoot, handle, ability) => {
440
440
  for (const mount of mounts) {
441
441
  const sources = verified.files.filter((file) => file.path.endsWith(".md")
442
442
  && (file.path === mount.path || file.path.startsWith(`${mount.path}/`)));
443
- if (sources.length === 0)
444
- throw new Error(`ability_memory_mount_empty:${mount.path}`);
445
443
  for (const source of sources) {
446
444
  const relativePath = source.path === mount.path
447
445
  ? source.path.split("/").at(-1)
@@ -467,7 +465,10 @@ const materializeWorkspaceTraining = async (agentsRoot, handle, ability) => {
467
465
  : join(sourceRoot, asset.source);
468
466
  await cp(source, target, { recursive: true, preserveTimestamps: true });
469
467
  }
470
- const evalPaths = [verified.manifest.spec.evals.policy, ...verified.manifest.spec.evals.cases];
468
+ const evalPaths = [
469
+ ...(verified.manifest.spec.evals.policy === undefined ? [] : [verified.manifest.spec.evals.policy]),
470
+ ...verified.manifest.spec.evals.cases,
471
+ ];
471
472
  for (const path of evalPaths) {
472
473
  const target = join(staging, "evals", layerRelativePath(path, "evals"));
473
474
  await mkdir(dirname(target), { recursive: true });
@@ -253,9 +253,19 @@ const scan = (files, manifest) => {
253
253
  if (executable.has(extname(file.path).toLowerCase()))
254
254
  findings.push({ code: "executable_asset", path: file.path });
255
255
  }
256
- const required = new Set(["instructions", "memory-policy", "memory-seed", "workspace", "eval-policy", "eval-cases"]);
256
+ const required = new Set(["instructions"]);
257
257
  if (manifest.spec.skills.length > 0)
258
258
  required.add("skills");
259
+ if (manifest.spec.memory.policy.length > 0)
260
+ required.add("memory-policy");
261
+ if (manifest.spec.memory.seeds.length > 0)
262
+ required.add("memory-seed");
263
+ if (manifest.spec.workspace.assets.length > 0)
264
+ required.add("workspace");
265
+ if (manifest.spec.evals.policy !== undefined)
266
+ required.add("eval-policy");
267
+ if (manifest.spec.evals.cases.length > 0)
268
+ required.add("eval-cases");
259
269
  return { findings, required };
260
270
  };
261
271
  const lockedManifestPaths = (manifest) => [
@@ -264,7 +274,7 @@ const lockedManifestPaths = (manifest) => [
264
274
  ...manifest.spec.memory.policy.map((entry) => entry.path),
265
275
  ...manifest.spec.memory.seeds.map((entry) => entry.path),
266
276
  ...manifest.spec.workspace.assets.map((entry) => entry.source),
267
- manifest.spec.evals.policy,
277
+ ...(manifest.spec.evals.policy === undefined ? [] : [manifest.spec.evals.policy]),
268
278
  ...manifest.spec.evals.cases,
269
279
  ];
270
280
  export const evaluateAbilityCases = (files, manifest, lock, memory) => {
@@ -367,7 +377,7 @@ export async function resolveAbilityRelease(agentsRoot, command) {
367
377
  const assertions = [
368
378
  { name: "manifest_lock_verified", passed: true },
369
379
  { name: "manifest_asset_paths_locked", passed: lockedManifestPaths(manifest).every((path) => lockedPaths.has(path)) },
370
- { name: "all_five_layers_resolvable", passed: [...security.required].every((kind) => kind === "skills" ? kinds.has(kind) || (manifest.spec.skills.length > 0 && lock.dependencies.length > 0) : kinds.has(kind)) },
380
+ { name: "declared_layers_resolvable", passed: [...security.required].every((kind) => kind === "skills" ? kinds.has(kind) || (manifest.spec.skills.length > 0 && lock.dependencies.length > 0) : kinds.has(kind)) },
371
381
  { name: "dependencies_pinned", passed: lock.dependencies.every((dependency) => /^[a-f0-9]{40}$/u.test(dependency.commit)) },
372
382
  { name: "security_scan_clear", passed: security.findings.length === 0 },
373
383
  ...evaluateAbilityCases(files, manifest, lock, memory),
@@ -61,16 +61,20 @@ export const DaemonAbilityManifestSchema = z.object({
61
61
  skills: z.array(z.object({
62
62
  name: z.string().min(1).max(128), source: z.union([LocalSkillSchema, GitSkillSchema]),
63
63
  mount: SafeAbilityPathSchema, mode: ModeSchema, trust: z.string().max(128).optional(),
64
- }).strict()).max(500),
64
+ }).strict()).max(500).default([]),
65
65
  memory: z.object({
66
- policy: z.array(z.object({ path: SafeAbilityPathSchema, mode: ModeSchema, mergeKey: z.string().optional() }).strict()).max(100),
67
- seeds: z.array(z.object({ path: SafeAbilityPathSchema, mode: ModeSchema, mergeKey: z.string().optional() }).strict()).max(1_000),
68
- runtime: z.object({ store: z.literal("nowcrew-managed"), promotion: z.literal("pull-request"), retentionDays: z.number().int().positive().optional() }).strict(),
69
- }).strict(),
66
+ policy: z.array(z.object({ path: SafeAbilityPathSchema, mode: ModeSchema, mergeKey: z.string().optional() }).strict()).max(100).default([]),
67
+ seeds: z.array(z.object({ path: SafeAbilityPathSchema, mode: ModeSchema, mergeKey: z.string().optional() }).strict()).max(1_000).default([]),
68
+ runtime: z.object({ store: z.literal("nowcrew-managed"), promotion: z.literal("pull-request"), retentionDays: z.number().int().positive().optional() }).strict()
69
+ .default({ store: "nowcrew-managed", promotion: "pull-request" }),
70
+ }).strict().default({ policy: [], seeds: [], runtime: { store: "nowcrew-managed", promotion: "pull-request" } }),
70
71
  workspace: z.object({ assets: z.array(z.object({
71
72
  source: SafeAbilityPathSchema, target: WorkspaceAbilityTargetSchema, mode: ModeSchema,
72
- }).strict()).max(1_000) }).strict(),
73
- evals: z.object({ policy: SafeAbilityPathSchema, cases: z.array(SafeAbilityPathSchema).min(1).max(1_000) }).strict(),
73
+ }).strict()).max(1_000).default([]) }).strict().default({ assets: [] }),
74
+ evals: z.object({
75
+ policy: SafeAbilityPathSchema.optional(),
76
+ cases: z.array(SafeAbilityPathSchema).max(1_000).default([]),
77
+ }).strict().default({ cases: [] }),
74
78
  compatibility: z.object({
75
79
  providers: z.array(z.enum(["claude", "codex", "kimi", "hermes", "opencode", "deepseek-harness"])).min(1),
76
80
  minNowCrewVersion: z.string().regex(/^\d+\.\d+\.\d+$/u),
package/dist/config.js CHANGED
@@ -10,9 +10,10 @@ export const DEFAULT_EXECUTION_LIMITS = Object.freeze({
10
10
  maxPromptBytes: 256_000,
11
11
  maxTimeoutMs: 3 * 60 * 60_000,
12
12
  maxEventBytes: 64_000,
13
- maxParallelPerAgent: 4,
13
+ maxParallelPerAgent: 10,
14
+ maxParallelScheduledPerAgent: 4,
14
15
  maxQueuedPerAgent: 32,
15
- maxParallelTotal: 4,
16
+ maxParallelTotal: 10,
16
17
  maxQueuedTotal: 128,
17
18
  maxStartingTotal: 1,
18
19
  maxStartingPerRuntime: 1,
@@ -68,6 +69,7 @@ export function loadConfig(env = process.env) {
68
69
  maxTimeoutMs: integerEnvAtMost(env, "CREW_EXECUTION_MAX_TIMEOUT_MS", DEFAULT_EXECUTION_LIMITS.maxTimeoutMs, MAX_EXECUTION_TIMEOUT_MS),
69
70
  maxEventBytes: integerEnvAtLeast(env, "CREW_EXECUTION_MAX_EVENT_BYTES", DEFAULT_EXECUTION_LIMITS.maxEventBytes, MIN_EXECUTION_EVENT_BYTES),
70
71
  maxParallelPerAgent: positiveIntegerEnv(env, "CREW_MAX_PARALLEL", DEFAULT_EXECUTION_LIMITS.maxParallelPerAgent),
72
+ maxParallelScheduledPerAgent: positiveIntegerEnv(env, "CREW_SCHEDULED_MAX_PARALLEL", DEFAULT_EXECUTION_LIMITS.maxParallelScheduledPerAgent),
71
73
  maxQueuedPerAgent: positiveIntegerEnv(env, "CREW_EXECUTION_MAX_QUEUED_PER_AGENT", DEFAULT_EXECUTION_LIMITS.maxQueuedPerAgent),
72
74
  maxParallelTotal: positiveIntegerEnv(env, "CREW_EXECUTION_MAX_PARALLEL_TOTAL", DEFAULT_EXECUTION_LIMITS.maxParallelTotal),
73
75
  maxQueuedTotal: positiveIntegerEnv(env, "CREW_EXECUTION_MAX_QUEUED_TOTAL", DEFAULT_EXECUTION_LIMITS.maxQueuedTotal),
@@ -231,7 +231,10 @@ function admission(spec, config, dependencies, at) {
231
231
  || dependencies.facts.queuedTotal < 0) {
232
232
  return { rejected: rejection(spec.executionId, "invalid_spec", "Invalid local resource facts", at) };
233
233
  }
234
- const agentAtCapacity = dependencies.facts.activeForAgent >= config.executionLimits.maxParallelPerAgent;
234
+ const maxParallelForAgent = spec.context.scheduledRunId === undefined
235
+ ? config.executionLimits.maxParallelPerAgent
236
+ : config.executionLimits.maxParallelScheduledPerAgent;
237
+ const agentAtCapacity = dependencies.facts.activeForAgent >= maxParallelForAgent;
235
238
  const machineAtCapacity = dependencies.facts.activeTotal >= config.executionLimits.maxParallelTotal;
236
239
  const slotInvalid = dependencies.slot?.state === "ready"
237
240
  ? agentAtCapacity || machineAtCapacity
@@ -4,7 +4,7 @@ import { mkdir, open, readFile, rename, rm, writeFile } from "node:fs/promises";
4
4
  import { randomUUID } from "node:crypto";
5
5
  import { defaultProcessController } from "./execution-journal.js";
6
6
  import { createJournalLease, createJournalLeaseRegistry, JournalLockedError, } from "./execution-journal-lock.js";
7
- const HOST_EXECUTION_SLOT_COUNT = 4;
7
+ const HOST_EXECUTION_SLOT_COUNT = 10;
8
8
  const HOST_STARTUP_GAP_MS = 3_000;
9
9
  const RETRY_MS = 50;
10
10
  export function defaultHostExecutionCoordinatorRoot(userHome = homedir()) {
@@ -215,8 +215,8 @@ export function createHostExecutionCoordinator(options = {}) {
215
215
  }
216
216
  export function hostCoordinatedSlotManager(local, host) {
217
217
  return {
218
- reserve: (handle, kind) => {
219
- const localReservation = local.reserve(handle, kind);
218
+ reserve: (handle, kind, executionClass) => {
219
+ const localReservation = local.reserve(handle, kind, executionClass);
220
220
  if (!localReservation.accepted)
221
221
  return localReservation;
222
222
  const hostReservation = host.reserveExecution(localReservation.ready);
package/dist/main.js CHANGED
File without changes
@@ -372,6 +372,10 @@ export function evaluateMemoryPrunePostcondition(before, after, execution) {
372
372
  outcome = "source_work_log_empty";
373
373
  else if (!facts.work_log_cleared)
374
374
  outcome = "work_log_not_cleared";
375
+ else if (!after.memory.exists)
376
+ outcome = "shared_memory_missing_after";
377
+ else if ((after.memory.size ?? 0) === 0)
378
+ outcome = "shared_memory_empty_after";
375
379
  else if (facts.lessons_changed && facts.shared_memory_has_lessons_reference) {
376
380
  outcome = "promoted_and_indexed";
377
381
  }
@@ -381,6 +385,8 @@ export function evaluateMemoryPrunePostcondition(before, after, execution) {
381
385
  }
382
386
  else if (facts.lessons_changed)
383
387
  outcome = "promoted_without_index_change";
388
+ else if (facts.shared_memory_changed)
389
+ outcome = "shared_memory_updated";
384
390
  else
385
391
  outcome = "no_files_changed";
386
392
  return { outcome, ...facts };