diff --git a/docs/SURFACE.md b/docs/SURFACE.md index 31c03343d..1cb1e25c4 100644 --- a/docs/SURFACE.md +++ b/docs/SURFACE.md @@ -255,11 +255,23 @@ No process runs between events: the handler wakes, executes to its next await, p **Project-config discovery:** starting in the flow file's directory, `flows check` walks parent directories through the filesystem root and selects the first readable `flows.json`. That nearest file is the whole project config; it is not merged with outer files. Its schema is `{ "cli"?: , "executors"?: [], "models"?: [], "mcp"?: }`; unknown keys, malformed model entries, and duplicates fail closed as `config_invalid`. A nearer config therefore defines a self-contained nested project boundary and prevents accidental inheritance of outer credentials, executors, or model approvals. The selected path is printed with project-level resolutions and named in refusals; if it declares no `cli` or models, outer configs remain shadowed. At gate 1, a trigger executor is considered registered only when its name is present in this author-written `executors` array; `flows check` does not yet contact a registry, broker, or RelayCron, and absence is `no_executor`. - Implementation status for issue #132: this named-agent contract currently - ships in the canonical declarative YAML/JSON compiler. Matching - `FlowHeader.agents` TypeScript types depend on the separately reviewed, - unmerged `@relayflows/surface` package in PR #134 and are a follow-on after - that package lands; this compiler slice does not duplicate that package. + Implementation status for issue #132: this named-agent contract ships for + both dialects. `@relayflows/surface`'s `FlowHeader.agents` (a + `Record`) declares the same named-agent map in + TypeScript; `authored-flow-agents.ts` lowers `f.agent(name, options)` by selecting + a declared entry by matching `name` against it (falling through to the + project CLI default when `name` matches nothing, so existing calls + that use `name` only for step-id readability are unaffected), and + `options.cli`/`options.model` are step-level overrides with the same + highest-priority resolution as the declarative dialect's inline + `cli`/`model` step fields. Both dialects share the same underlying + `FlowSpec.agents` / preflight resolution (`packages/sdk/src/preflight.ts`, + `compile.ts`) — this is one contract compiled from two front ends, not two + separate implementations. + Every TS header declaration is checked before the body runs, including + unused agents. Within one execution, successful readiness probes are reused + for the same CLI path, base directory, and model; overriding either field + checks the resulting pair. Project configuration is captured for that execution. 7. **Two dialects, one journal.** Declarative YAML — data, fully preflightable, sage's compile target, gate 9's self-authoring output. Imperative TS — journal-memoized function, maximum ergonomics. YAML is canonical; TS is the power tool. TS preflights its declared surface (agents, helpers, tools, identity), not arbitrary control flow — declared honestly per covenant 2. ### Structured output declarations diff --git a/docs/evidence/pr-sweep-0917/remediation-245-retry.md b/docs/evidence/pr-sweep-0917/remediation-245-retry.md new file mode 100644 index 000000000..f84e4aab2 --- /dev/null +++ b/docs/evidence/pr-sweep-0917/remediation-245-retry.md @@ -0,0 +1,138 @@ +# PR #245 remediation retry — 2026-09-17 + +## Daemon preparation + +Literal command: + +```sh +cd packages/sdk && PATH=/Users/khaliqgant/.cargo/bin:$PATH npm run test:prep +``` + +Captured output: + +```text +> @relayflows/sdk@2.0.14 test:prep +> ( cd ../../kernel && sh ../ops/cargo.sh build ) && ( [ ! -d ../../testdata/preflight ] || find ../../testdata/preflight -name '*-cli' -type f -exec chmod +x {} + ) + + Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.22s +``` + +Literal worktree-key derivation command: + +```sh +printf %s "$PWD" | cksum +``` + +Captured output: + +```text +1580583111 84 +``` + +The resulting live-suite binary path is +`/Users/khaliqgant/.relayflows-toolchain/target/1580583111/debug/relayflowd`. + +## Surface build and type tests + +Literal command: + +```sh +cd packages/surface && npm run build && npm run typecheck && npm run typecheck:regressions && npm run typecheck:examples && npx vitest run tests/flow.test.ts +``` + +Captured output: + +```text +> @relayflows/surface@2.0.14 build +> tsc + +> @relayflows/surface@2.0.14 typecheck +> tsc --noEmit + +> @relayflows/surface@2.0.14 typecheck:regressions +> tsc -p ../../regressions/tsconfig.json && tsc -p tsconfig.test.json && node scripts/check-generated-helpers.mjs + +HELPERS_GENERATED_OK airtable.ts, asana.ts, azure-blob.ts, box.ts, calendly.ts, clickup.ts, clients.ts, cloudflare.ts, confluence.ts, daytona.ts, docker-hub.ts, dropbox.ts, fathom.ts, gcp.ts, gcs.ts, github.ts, gitlab.ts, gmail.ts, google-calendar.ts, google-drive.ts, granola.ts, hubspot.ts, index.ts, intercom.ts, jira.ts, linear.ts, mailgun.ts, mixpanel.ts, neon.ts, notion.ts, onedrive.ts, pipedrive.ts, postgres.ts, posthog.ts, providers.ts, ramp.ts, recall.ts, reddit.ts, redis.ts, s3.ts, salesforce.ts, segment.ts, sendgrid.ts, sharepoint.ts, shopify.ts, shortcut.ts, slack.ts, stripe.ts, teams.ts, telegram.ts, webhook-server.ts, x.ts, zendesk.ts + +> @relayflows/surface@2.0.14 typecheck:examples +> tsc -p ../../examples/tsconfig.json + + RUN v2.1.9 /Volumes/Paris Drive/AgentWorkforce/.worktrees/flows-v2-lead-0913/pr-remediation-245/packages/surface + + ✓ tests/flow.test.ts (24 tests) 5ms + + Test Files 1 passed (1) + Tests 24 passed (24) + Start at 17:31:42 + Duration 341ms (transform 88ms, setup 0ms, collect 85ms, tests 5ms, environment 0ms, prepare 87ms) +``` + +## SDK build and typechecks + +Literal command: + +```sh +cd packages/sdk && npm run build && npm run typecheck && npm run typecheck:tests +``` + +Captured output: + +```text +> @relayflows/sdk@2.0.14 build +> tsc && node scripts/make-cli-executable.mjs + +> @relayflows/sdk@2.0.14 typecheck +> tsc --noEmit && tsc -p tsconfig.type-tests.json + +> @relayflows/sdk@2.0.14 typecheck:tests +> tsc -p tsconfig.tests.json +``` + +## Focused authored and live named-agent suites + +Literal command: + +```sh +cd packages/sdk && RELAYFLOWD_BIN=/Users/khaliqgant/.relayflows-toolchain/target/1580583111/debug/relayflowd npx vitest run tests/authored-named-agents.test.ts tests/authored-agent-failure.test.ts tests/direct-input.test.ts tests/live-named-agents.test.ts +``` + +Captured output: + +```text + RUN v2.1.9 /Volumes/Paris Drive/AgentWorkforce/.worktrees/flows-v2-lead-0913/pr-remediation-245/packages/sdk + + ✓ tests/authored-agent-failure.test.ts (5 tests) 6ms + ✓ tests/authored-named-agents.test.ts (7 tests) 669ms + ✓ named agent declarations > lets a step-level cli override win over the named declaration 337ms + ✓ named agent declarations > does not treat an unrelated f.agent name as a named-agent selector 317ms + ✓ tests/live-named-agents.test.ts (1 test) 1547ms + ✓ named agents against a live kernel > dispatches two distinct named agents declared in the flow header 1546ms + ✓ tests/direct-input.test.ts (7 tests) 21820ms + ✓ direct .flow.ts input through the built CLI and live runtime > returns exit 3 for an authored human handoff and persists its outcome 1435ms + ✓ direct .flow.ts input through the built CLI and live runtime > returns exit 1 for an authored step_failed verdict and persists its outcome 858ms + ✓ direct .flow.ts input through the built CLI and live runtime > executes inline and file JSON input through relayflowd 2547ms + ✓ direct .flow.ts input through the built CLI and live runtime > refuses missing and malformed input before contacting relayflowd 2623ms + ✓ direct .flow.ts input through the built CLI and live runtime > surfaces the specific preflight refusal kind for an unresolved f.agent, not a generic invalid_spec 12740ms + ✓ direct .flow.ts input through the built CLI and live runtime > does not run the authored body before daemon availability 815ms + ✓ direct .flow.ts input through the built CLI and live runtime > refuses oversized file input before contacting relayflowd 800ms + + Test Files 4 passed (4) + Tests 20 passed (20) + Start at 17:31:58 + Duration 22.09s (transform 367ms, setup 0ms, collect 1.83s, tests 24.04s, environment 0ms, prepare 215ms) +``` + +## Diff whitespace check + +Literal command: + +```sh +git diff --check +``` + +Captured output: + +```text +``` + +The command exited 0 with no output. diff --git a/docs/evidence/pr-sweep-0917/remediation-245.md b/docs/evidence/pr-sweep-0917/remediation-245.md new file mode 100644 index 000000000..cc7797869 --- /dev/null +++ b/docs/evidence/pr-sweep-0917/remediation-245.md @@ -0,0 +1,187 @@ +# PR #245 remediation — 2026-09-17 + +## Remote-head guard + +Initial literal command: + +```sh +git fetch origin main refs/pull/245/head && git ls-remote origin 'refs/pull/245/head' +``` + +Captured output: + +```text +From github.com:AgentWorkforce/flows + * branch main -> FETCH_HEAD + * branch refs/pull/245/head -> FETCH_HEAD + c8c68315..45417541 main -> origin/main +b88244113be0ca330b01871fdd0588ac483e5f59 refs/pull/245/head +``` + +Sweep-baseline lookup command: + +```sh +rg -n --hidden --glob '!node_modules' 'PR #245|feat/ts-named-agents|b88244113be0ca330b01871fdd0588ac483e5f59' docs/evidence . 2>/dev/null || true +``` + +Captured matching sweep evidence: + +```text +docs/evidence/pr-sweep-0917/inventory.md:30:| #245 | `b88244113be0ca330b01871fdd0588ac483e5f59` | khaliqgant | no | CONFLICTING | none | 0 | decision 13 authoring surface; **fix_required** (conflict; review FAILURE). | +``` + +The fetched remote head equals the sweep baseline: `b88244113be0ca330b01871fdd0588ac483e5f59`. + +## Rebase + +Literal command: + +```sh +git rebase origin/main +``` + +Captured result: + +```text +Rebasing (1/2) +CONFLICT (content): Merge conflict in packages/relayflows/package-lock.json +CONFLICT (content): Merge conflict in packages/relayflows/package.json +CONFLICT (content): Merge conflict in packages/runtime-darwin-arm64/package.json +CONFLICT (content): Merge conflict in packages/runtime-linux-x64/package.json +CONFLICT (content): Merge conflict in packages/sdk/package-lock.json +CONFLICT (content): Merge conflict in packages/sdk/package.json +CONFLICT (content): Merge conflict in packages/sdk/src/authored-flow-executor.ts +CONFLICT (content): Merge conflict in packages/sdk/src/cli/check.ts +CONFLICT (content): Merge conflict in packages/sdk/src/cli/direct-run.ts +CONFLICT (content): Merge conflict in packages/sdk/src/preflight.ts +CONFLICT (content): Merge conflict in packages/surface/package-lock.json +CONFLICT (content): Merge conflict in packages/surface/package.json +CONFLICT (content): Merge conflict in packages/surface/src/context.ts +CONFLICT (content): Merge conflict in packages/surface/src/flow.ts +``` + +Resolution retained current-main package versions and authored worker/preflight paths, then integrated the named-agent declaration map, per-step CLI/model overrides, up-front declaration preflight, and causal runtime diagnostics. Generated package locks were regenerated from the resolved manifests. + +## Validation + +Surface command (the package script first failed because `bun` is unavailable; this equivalent uses the installed Node tooling): + +```sh +cd packages/surface && npm run build && npx tsc -p tsconfig.test.json && npx vitest run tests/flow.test.ts +``` + +Captured output: + +```text +> @relayflows/surface@2.0.14 build +> tsc + + RUN v2.1.9 /Volumes/Paris Drive/AgentWorkforce/.worktrees/flows-v2-lead-0913/pr-remediation-245/packages/surface + + ✓ tests/flow.test.ts (24 tests) 5ms + + Test Files 1 passed (1) + Tests 24 passed (24) +``` + +SDK command: + +```sh +cd packages/sdk && npm run build && npx vitest run tests/authored-named-agents.test.ts tests/authored-agent-failure.test.ts tests/direct-input.test.ts +``` + +Captured output: + +```text +> @relayflows/sdk@2.0.14 build +> tsc && node scripts/make-cli-executable.mjs + + RUN v2.1.9 /Volumes/Paris Drive/AgentWorkforce/.worktrees/flows-v2-lead-0913/pr-remediation-245/packages/sdk + + ✓ tests/authored-agent-failure.test.ts (5 tests) 5ms + ✓ tests/authored-named-agents.test.ts (7 tests) 474ms + ✓ tests/direct-input.test.ts (7 tests) 22781ms + + Test Files 3 passed (3) + Tests 19 passed (19) +``` + +SDK typecheck command: + +```sh +cd packages/sdk && npm run typecheck && npm run typecheck:tests +``` + +Captured output: + +```text +> @relayflows/sdk@2.0.14 typecheck +> tsc --noEmit && tsc -p tsconfig.type-tests.json + +> @relayflows/sdk@2.0.14 typecheck:tests +> tsc -p tsconfig.tests.json +``` + +The live named-agent suite is not green in this environment. Literal command: + +```sh +cd packages/sdk && npx vitest run tests/live-named-agents.test.ts +``` + +Captured output: + +```text + RUN v2.1.9 /Volumes/Paris Drive/AgentWorkforce/.worktrees/flows-v2-lead-0913/pr-remediation-245/packages/sdk + + FAIL tests/live-named-agents.test.ts [ tests/live-named-agents.test.ts ] +Error: Build this checkout's daemon and set RELAYFLOWD_BIN to it: /Volumes/Paris Drive/AgentWorkforce/.worktrees/flows-v2-lead-0913/pr-remediation-245/kernel/target/debug/relayflowd + + Test Files 1 failed (1) + Tests 1 skipped (1) +``` + +Its documented preparation command also cannot run because the environment has no Rust compiler: + +```sh +cd packages/sdk && npm run test:prep +``` + +Captured output: + +```text +> @relayflows/sdk@2.0.14 test:prep +> ( cd ../../kernel && sh ../ops/cargo.sh build ) && ( [ ! -d ../../testdata/preflight ] || find ../../testdata/preflight -name '*-cli' -type f -exec chmod +x {} + ) + +error: could not execute process `rustc -vV` (never executed) + +Caused by: + No such file or directory (os error 2) +``` + +Because this required affected suite is blocked and not green, this remediation must not update the remote PR branch. + +## Final remote-head guard and update + +Final literal command (run even though no push is allowed): + +```sh +git fetch origin refs/pull/245/head && git ls-remote origin 'refs/pull/245/head' && git rev-parse HEAD && git status --short --branch +``` + +Captured output: + +```text +From github.com:AgentWorkforce/flows + * branch refs/pull/245/head -> FETCH_HEAD +b88244113be0ca330b01871fdd0588ac483e5f59 refs/pull/245/head +2ea87580b5dd6cd1c7d420ce5961c0ccd6f5e97a +## feat/ts-named-agents...origin/feat/ts-named-agents [ahead 88, behind 2] + M packages/sdk/src/authored-worker-step.ts + M packages/sdk/src/cli/check-typescript.ts + M packages/sdk/src/cli/check.ts + M packages/sdk/src/preflight.ts + M packages/surface/src/flow.ts +?? docs/evidence/pr-sweep-0917/ +``` + +The remote PR head still equals the sweep head. The last committed local remediation SHA and exact branch head are `2ea87580b5dd6cd1c7d420ce5961c0ccd6f5e97a`. The worktree deliberately remains uncommitted and the remote branch was not updated: the required live named-agent suite is not green in this environment. diff --git a/docs/evidence/pr245/README.md b/docs/evidence/pr245/README.md new file mode 100644 index 000000000..ceb0c50ec --- /dev/null +++ b/docs/evidence/pr245/README.md @@ -0,0 +1,38 @@ +# PR 245 verification (2026-09-10) + +The recorded commands and complete outputs are adjacent to this file. +The full SDK run uses the daemon built from this checkout and the local +surface 2.0.9 tarball. It preceded the final model-syntax regression; the later +focused run includes that additional test. The subsequent review follow-up +adds diagnostic-selection and failure-contract regressions; its captured +build, typechecks, and seven-suite run are in `review-followup.txt`. The unchanged package gate verifies +the 2.0.9 manifests and surface tarball with Bun 1.4.0; the host Bun 1.3.14 +cannot read the repository lockfile. + +A full-suite invocation from the repository root (`sdk-wrong-cwd.txt`) failed: +six assertions depend on the SDK working directory, and one wrapper abort +readiness assertion timed out. Running from `packages/sdk` produced the final +output in `sdk-full.txt`, including that abort test. No tests were weakened. + +- `sdk-focused.txt`: named declarations, overrides, before-body refusal, + direct CLI refusal kinds, malformed model syntax, and declarative preflight. +- `named-test-root-cwd.txt`: the new live suite also works from the repository + root without RELAYFLOWD_BIN; its fallback is relative to the test file. +- `sdk-full.txt`: full SDK run; three provider/environment-dependent skips + are visible in the output. +- `kernel-tests.txt`: full Rust workspace tests, including crash/resume tests. +- `packed-consumer.txt`: the unchanged package gate, including surface tests, + TypeScript regressions, and the packed runtime and type consumers. + +The live named-agent test uses two distinct local wrapper executables, a real +daemon, and a real SDK worker. It asserts CLI identity and model for both +named selections, a combined override, model-only and CLI-only overrides, +and one successful model probe per distinct pair. It does not claim to test +provider service availability. + +For the negative control in `named-selection-mutated.txt`, the exact change +was `{ agent: name }` -> `{ agent: Object.keys(namedAgents!)[0] }` in +`packages/sdk/src/authored-flow-agents.ts`. The original bytes were saved, +the live test was run and failed on fixer's returned identity/model, and the +saved bytes were restored byte-for-byte before the passing run recorded in +`named-selection-restored.txt`. Both command outputs and exits are included. diff --git a/docs/evidence/pr245/kernel-tests.txt b/docs/evidence/pr245/kernel-tests.txt new file mode 100644 index 000000000..44dc2086a --- /dev/null +++ b/docs/evidence/pr245/kernel-tests.txt @@ -0,0 +1,355 @@ +$ /Users/khaliqgant/.cargo/bin/cargo test --manifest-path kernel/Cargo.toml + Compiling bitflags v2.13.1 + Compiling rustix v1.1.4 + Compiling getrandom v0.4.3 + Compiling errno v0.3.14 + Compiling fastrand v2.5.0 + Compiling relayflowd-core v0.1.0 (/Users/khaliqgant/lanes/flows245/repo/kernel/relayflowd-core) + Compiling rusqlite v0.37.0 + Compiling relayflowd-journal v0.1.0 (/Users/khaliqgant/lanes/flows245/repo/kernel/relayflowd-journal) + Compiling relayflowd v0.1.0 (/Users/khaliqgant/lanes/flows245/repo/kernel/relayflowd) + Compiling tempfile v3.27.0 + Finished `test` profile [unoptimized + debuginfo] target(s) in 7.17s + Running unittests src/lib.rs (kernel/target/debug/deps/relayflowd-3287862779f564ca) + +running 40 tests +test engine::remote::worker_failure_detail_tests::a_null_or_blank_output_yields_no_detail ... ok +test engine::remote::worker_failure_detail_tests::a_string_output_is_carried_verbatim_and_trimmed ... ok +test engine::remote::worker_failure_detail_tests::an_output_at_the_boundary_is_not_truncated ... ok +test engine::remote::worker_failure_detail_tests::a_non_string_output_is_rendered_rather_than_dropped ... ok +test engine::remote::worker_failure_detail_tests::truncation_does_not_split_a_multi_byte_char ... ok +test engine::boot_identity_tests::every_engine_in_this_process_shares_one_boot_id ... ok +test server::client::tests::resume_waits_while_the_heartbeat_renewed_lease_is_live ... ok +test server::channels::tests::unknown_verb_never_falls_through_to_receive ... ok +test server::liveness::tests::sweep_id_buckets_by_the_interval ... ok +test exec_det::tests::captures_deterministic_output ... ok +test exec_det::tests::timeout_has_an_explicit_completion_reason ... ok +test server::liveness::tests::sweep_pass_healthy_subscription_is_a_noop ... ok +test server::tests::agent::contract::an_agent_worker_attaching_without_pins_is_refused_at_attach ... ok +test engine::wake::claim_guard_tests::a_disarmed_guard_leaves_the_claim_alone ... ok +test server::liveness::tests::sweep_pass_latches_after_journaling_and_next_bucket_is_empty ... ok +test engine::wake::claim_guard_tests::a_panic_between_claim_and_register_still_releases ... ok +test engine::wake::claim_guard_tests::an_armed_guard_releases_the_claim_when_dropped ... ok +test server::tests::agent::contract::an_oversized_trajectory_tail_is_refused_at_step_complete ... ok +test engine::wake::claim_guard_tests::a_guard_only_releases_its_own_run ... ok +test server::tests::agent::contract::agent_without_a_compatible_worker_parks_without_starting ... ok +test server::tests::hello_enforces_protocol_version ... ok +test server::tests::agent::contract::an_agent_worker_missing_a_declared_surface_parks_the_run_instead_of_erroring ... ok +test server::tests::run_resume_asks_the_registry_instead_of_treating_an_orphan_file_as_a_run ... ok +test server::tests::agent::contract::an_llm_completion_claiming_an_effect_fails_closed_with_the_reason_journaled ... ok +test server::tests::a_failed_disconnect_journal_append_is_retained_and_retried_not_dropped ... ok +test server::tests::run_start_fails_closed_on_an_unknown_verification_key ... ok +test server::tests::agent::pins::reset_worker_reporting_a_revision_other_than_its_pin_fails_closed_as_worker_error ... ok +test socket_path::tests::deep_data_dir_produces_short_socket_path ... ok +test socket_path::tests::different_data_dirs_yield_different_sockets ... ok +test socket_path::tests::relative_and_absolute_data_dirs_agree ... ok +test socket_path::tests::same_data_dir_yields_same_socket ... ok +test server::tests::agent::contract::a_replacement_worker_that_never_reported_the_pinned_surface_is_not_dispatched_to ... ok +test server::tests::run_resume_refuses_a_journal_that_never_recorded_its_run ... ok +test server::tests::run_resume_adopts_a_real_journal_whose_registry_row_is_missing ... ok +test server::tests::agent::pins::consecutive_agent_steps_on_different_surfaces_each_start_from_their_own_pins ... ok +test server::tests::run_resume_refuses_a_valid_journal_that_belongs_to_another_run ... ok +test server::tests::stopped_heartbeats_past_the_deadline_journal_lease_expired_and_release_the_step ... ok +test exec_det::tests::timeout_kills_the_whole_process_group ... ok +test server::tests::agent::pins::a_replacement_worker_at_a_different_revision_is_not_dispatched_the_stale_pins ... ok +test server::tests::an_entry_appended_during_watch_registration_is_delivered_exactly_once ... ok + +test result: ok. 40 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.57s + + Running unittests src/main.rs (kernel/target/debug/deps/relayflowd-ae3ceb16e5d803ec) + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + + Running tests/crash_resume.rs (kernel/target/debug/deps/crash_resume-4619905d2726c36c) + +running 40 tests +test concurrency::cancel_closes_the_lease_and_rejects_a_late_completion ... ok +test agent::resume_without_a_worker_parks_immediately_instead_of_timing_out ... ok +test concurrency::cancel_and_completion_race_has_one_terminal_fact ... ok +test agent::rung_c_sigkill_between_agent_completion_and_final_effect_memoizes_the_agent ... ok +test agent::rung_c_sigkill_after_final_effect_replays_results_without_redispatch ... ok +test channels::channels_reject_foreign_workers_stale_attempts_and_invalid_acknowledgements ... ok +test agent::rung_c_reset_sigkill_mid_edit_restores_pins_dedupes_effect_and_explains_attempts ... ok +test agent::rung_c_crash_between_effect_election_and_the_provider_call_performs_it_exactly_once ... ok +test concurrency::concurrent_resumes_lease_exactly_one_attempt ... ok +test concurrency::run_start_dispatches_every_independent_lane_before_any_completion ... ok +test concurrency::live_resume_leaves_an_active_lease_running ... ok +test concurrency::server_restart_recovers_every_parallel_lease_without_duplicate_success ... ok +test llm::llm_verification_exhaustion_is_a_declared_failure_kind ... ok +test llm::serve_plumbs_watch_events_and_replayable_stream_verbs ... ok +test agent::rung_c_sigkill_boundaries_resume_only_unfinished_steps_via_real_cli ... ok +test llm::failing_llm_verification_schedules_a_durable_retry_and_succeeds ... ok +test llm::completed_llm_output_is_memoized_when_serve_dies_during_the_next_step ... ok +test llm::sigkill_after_the_final_rung_b_effect_resumes_without_redispatching_llm ... ok +test pin_projection::rejected_completion_cannot_forge_inspect_retry_pins_over_the_real_socket ... ok +test llm::sigkill_under_serve_mid_llm_releases_the_lease_and_finishes_via_cli_resume ... ok +test llm::worker_killed_while_holding_a_lease_is_explained_and_released_on_cli_resume ... ok +test memory::memory_sigkill_after_injection_replays_pack_and_charges_it_once ... ok +test protocol_admission::every_mutating_run_verb_refuses_terminal_before_changing_state ... ok +test llm::sigkill_sweep_covers_before_and_between_the_rung_b_steps ... ok +test parallel_lifecycle::terminal_failure_drains_or_explains_every_live_sibling ... ok +test sigkill_after_cancel_request_resumes_to_one_canceled_fact ... ok +test parallel_lifecycle::overlapping_agent_conflict_survives_server_crash_and_resume ... ok +test sigkill_mid_step_replaces_and_explains_the_dead_attempt ... ok +test surface_identity::aliases_are_rejected_and_external_ancestors_serialize_over_real_sockets ... ok +test parallel_lifecycle::overlapping_agent_lanes_serialize_while_disjoint_lanes_merge_in_either_order ... ok +test sigkill_under_serve_resumes_the_socket_started_run ... ok +test worker_capacity::two_workers_receive_a_deterministic_fair_capacity_bounded_batch ... ok +test worker_capacity::default_capacity_one_reopens_only_after_durable_completion_or_crash ... ok +test placement::declared_placement_keeps_one_source_tree_across_resume ... ok +test workspace_identity::workspace_aliases_are_refused_and_canonical_subtrees_serialize_over_real_sockets ... ok +test channels::channels_sigkill_resume_redelivers_unacked_messages_with_exactly_once_effects ... ok +test sigkill_sweep_covers_every_hello_step_boundary ... ok +test placement::sigkill_before_first_step_preserves_the_submitted_workspace ... ok +test placement::sigkill_mid_step_keeps_the_route_and_source_tree ... ok +test parallel_lifecycle::renewed_parallel_leases_survive_the_original_grant_and_remain_distinct ... ok + +test result: ok. 40 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 36.82s + + Running tests/daemon_lifecycle.rs (kernel/target/debug/deps/daemon_lifecycle-e483798b2ec9e14a) + +running 6 tests +test connection_file_is_published_only_after_the_socket_is_live ... ok +test deep_data_dir_still_binds ... ok +test clean_shutdown_removes_advertisement_and_socket ... ok +test a_second_serve_on_a_served_data_dir_refuses_and_the_first_keeps_serving ... ok +test a_sigkilled_daemons_successor_starts_cleanly ... ok +test sigkill_leaves_a_stale_file_with_a_dead_pid ... ok + +test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.05s + + Running tests/event_wake.rs (kernel/target/debug/deps/event_wake-bc602276d1cebcd0) + +running 3 tests +test two_racing_deliveries_of_one_event_produce_exactly_one_run ... ok +test matching_event_wakes_once_with_fresh_context ... ok +test a_resumed_run_dispatches_the_original_wake_context ... ok + +test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s + + Running tests/hn_monitor_integration.rs (kernel/target/debug/deps/hn_monitor_integration-6b22693ad979dd80) + +running 1 test +test hn_story_event_wakes_monitor_once_with_story_context ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.05s + + Running tests/invalid_schema_preflight.rs (kernel/target/debug/deps/invalid_schema_preflight-2c352b9351d5acf8) + +running 3 tests +test invalid_json_schema_is_refused_before_journal_or_command ... ok +test unbounded_json_schema_is_refused_before_journal_or_command ... ok +test legitimately_recursive_json_schema_still_starts ... ok + +test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 3.43s + + Running tests/memory.rs (kernel/target/debug/deps/memory-a74848a8274a290e) + +running 5 tests +test rejected_journal_fact_releases_reservation_and_never_dispatches ... ok +test llm_dispatch_receives_same_pack_after_resume_without_provider ... ok +test over_budget_and_provider_errors_fail_without_dispatch_or_charge ... ok +test replay_and_resume_need_no_provider_and_script_receives_recorded_pack ... ok +test semantic_retry_reuses_memory_without_a_second_charge ... ok + +test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.04s + + Running tests/memory_epoch.rs (kernel/target/debug/deps/memory_epoch-edee30a47cad81b5) + +running 1 test +test epoch_carries_pack_and_exact_charge_and_refuses_duplicate_injection ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s + + Running tests/parallel_driver.rs (kernel/target/debug/deps/parallel_driver-74d9d10149c534bd) + +running 4 tests +test stop_after_one_holds_for_an_independent_deterministic_batch ... ok +test pause_before_second_independent_step_holds_the_driver_boundary ... ok +test backpressured_or_mismatched_lane_does_not_drop_a_later_dispatch ... ok +test crash_boundaries_resume_the_real_driver_with_one_effect_per_lane ... ok + +test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.06s + + Running tests/placement_pins.rs (kernel/target/debug/deps/placement_pins-893a8773fbe94aee) + +running 3 tests +test unsupported_local_pty_is_refused_before_an_earlier_step_can_run ... ok +test default_worker_pins_the_declared_worktree_base_commit_and_refuses_missing_source ... ok +test a_resumed_attempt_keeps_the_original_pin_after_the_worktree_head_moves ... ok + +test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.22s + + Running tests/placement_routing.rs (kernel/target/debug/deps/placement_routing-aabf5947f3ca76b4) + +running 3 tests +test a_failed_routing_append_never_starts_or_dispatches_work ... ok +test crash_between_routing_and_start_does_not_redecide ... ok +test worker_retry_consumes_the_original_routing_fact ... ok + +test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s + + Running tests/routing_diagnostics.rs (kernel/target/debug/deps/routing_diagnostics-ddddee3a427ee9ae) + +running 2 tests +test duplicate_routes_have_a_distinct_diagnostic_and_leave_the_original_fact_intact ... ok +test malformed_routes_name_the_same_field_at_append_replay_and_epoch_replay ... ok + +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s + + Running tests/spec_review_routing.rs (kernel/target/debug/deps/spec_review_routing-0731f97e90d67407) + +running 4 tests +test attempt_scoped_route_is_rejected_at_append_and_replay ... ok +test malformed_epoch_routes_are_rejected_before_commit ... ok +test epoch_cannot_drop_or_replace_a_durable_route ... ok +test workspace_pin_peels_tags_and_refuses_non_commit_objects ... ok + +test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.17s + + Running tests/subscription_liveness.rs (kernel/target/debug/deps/subscription_liveness-9506d11f678a9586) + +running 3 tests +test submit_event_upserts_subscription_row_and_sweep_flags_it_stale_after_budget ... ok +test stale_transition_is_journaled_as_subscription_stale_entry_in_the_last_known_run ... ok +test a_fresh_arrival_re_arms_the_latch_and_the_next_silence_can_stale_again ... ok + +test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.03s + + Running unittests src/lib.rs (kernel/target/debug/deps/relayflowd_core-b1fe3b3250e9e7a2) + +running 60 tests +test clock::tests::simulated_clock_is_explicitly_advanced ... ok +test channel::tests::forged_deliveries_and_acknowledgements_fail_closed ... ok +test channel::tests::send_retry_is_stable_and_conflicting_content_is_rejected ... ok +test journal::tests::memory_journal_assigns_sequences_and_rolls_epochs ... ok +test channel::tests::malformed_payloads_and_invalid_new_channel_appends_leave_state_unchanged ... ok +test channel::tests::delivery_replay_and_independent_acknowledged_offsets ... ok +test machine::parallel_tests::external_ancestor_and_descendant_paths_conflict_but_siblings_do_not ... ok +test machine::parallel_tests::failed_run_drains_open_siblings_before_terminal_entry ... ok +test machine::parallel_tests::parallel_lanes_do_not_cross_the_dependency_barrier_early ... ok +test machine::parallel_tests::workspace_ancestor_and_descendant_paths_conflict_but_siblings_do_not ... ok +test machine::parallel_tests::machine_starts_every_runnable_step_in_authored_order ... ok +test machine::parallel_tests::crash_resume_preserves_each_parallel_lease_exactly_once ... ok +test machine::parallel_tests::every_declared_mutable_surface_participates_in_conflict_selection ... ok +test machine::tests::all_backing_off_steps_return_timers ... ok +test machine::parallel_tests::overlapping_agent_surfaces_are_serialized_in_authored_order ... ok +test machine::tests::cancel_request_closes_the_active_lease_before_the_terminal_fact ... ok +test machine::tests::every_reason_label_matches_its_serialized_form ... ok +test machine::tests::durable_cancel_request_outranks_crash_recovery ... ok +test machine::tests::crashed_attempt_does_not_consume_an_iteration ... ok +test machine::parallel_tests::disjoint_agent_lanes_merge_pins_in_either_completion_order ... ok +test machine::tests::machine_starts_runnable_step_with_stable_effect_key ... ok +test machine::tests::repeated_cancel_request_is_idempotent ... ok +test machine::tests::successful_memo_is_never_scheduled_again ... ok +test machine::tests::verification_failure_schedules_a_durable_retry ... ok +test machine::tests::inspect_recovery_injects_the_dirty_pin_completion_reason_and_tail ... ok +test machine::tests::manual_recovery_parks_needs_human_and_never_redispatches ... ok +test machine::tests::worker_reported_failure_without_detail_still_records_a_verification ... ok +test memory::tests::caps_compare_exact_decimals_and_each_token_dimension ... ok +test retry::tests::jitter_is_repeatable_and_bounded ... ok +test machine::tests::reset_recovery_dispatches_the_original_pinned_revision ... ok +test machine::tests::every_failed_run_terminates_with_declared_completion_reasons ... ok +test schema::tests::in_document_uri_references_resolve_to_the_node_they_name ... ok +test schema::tests::refusal_names_the_cycle_it_found ... ok +test spec::tests::a_misspelled_step_level_key_is_a_parse_error ... ok +test spec::tests::a_misspelled_verification_gate_key_is_a_parse_error_not_a_dropped_gate ... ok +test spec::tests::cycles_are_rejected ... ok +test spec::tests::preflight_data_is_fail_closed ... ok +test spec::tests::external_surface_paths_must_have_one_canonical_spelling ... ok +test spec::tests::spec_version_is_semver_and_gated ... ok +test schema::tests::references_the_bound_leaves_opaque_are_refused_by_the_engine ... ok +test spec::tests::unknown_root_and_nested_fields_are_rejected ... ok +test spec::tests::zero_agent_flow_is_valid ... ok +test spec::tests::workspace_mounts_and_worktrees_must_have_one_canonical_spelling ... ok +test state::budget::tests::adds_costs_exactly_beyond_machine_decimal_precision ... ok +test state::budget::tests::overflow_and_malformed_cost_leave_total_unchanged ... ok +test state::tests::budget_decimal_strings_add_without_floats ... ok +test state::tests::end_pin_chain_is_enforced_and_a_broken_chain_is_a_hard_error ... ok +test state::tests::a_completion_that_omits_a_surface_does_not_drop_it_from_the_pin_chain ... ok +test state::tests::journal_replays_data_gate_verdict_without_rerunning_completed_code ... ok +test verify::tests::an_unbounded_schema_in_a_journal_fails_its_gate_instead_of_aborting ... ok +test verify::tests::deterministic_output_requires_successful_exit_and_content ... ok +test spec::tests::the_full_ladder_parses_in_the_one_dialect ... ok +test verify::tests::json_schema_is_a_control_gate ... ok +test schema::tests::a_property_named_ref_is_not_a_reference ... ok +test schema::tests::shared_declarations_and_boolean_schemas_are_validated ... ok +test schema::tests::every_refused_corpus_schema_compiles_but_is_refused_by_the_bound ... ok +test schema::tests::every_accepted_corpus_schema_is_accepted ... ok +test spec::tests::sdk_boundary_rejects_a_10_000_step_cycle_with_a_typed_error ... ok +test spec::tests::sdk_boundary_accepts_a_valid_10_000_step_reverse_chain ... ok +test schema::tests::deeply_nested_schemas_do_not_overflow_the_checker ... ok + +test result: ok. 60 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.60s + + Running tests/spec_parity.rs (kernel/target/debug/deps/spec_parity-bbda6cf1e1cf1c19) + +running 9 tests +test the_kernel_parses_the_deterministic_rung_and_stamps_the_same_hash ... ok +test step_memory_has_identical_canonical_bytes_and_hash ... ok +test the_kernel_parses_the_event_triggered_spec_and_stamps_the_same_hash ... ok +test placement_requirements_have_identical_canonical_bytes_and_hash ... ok +test the_kernel_parses_the_rung_c_agent_spec_and_stamps_the_same_hash ... ok +test memory_declaration_acceptance_matches_the_sdk_corpus ... ok +test placement_declaration_acceptance_matches_the_sdk_corpus ... ok +test the_kernel_parses_the_sdk_compiled_spec_and_stamps_the_same_hash ... ok +test the_kernel_parses_the_rung_b_spec_and_stamps_the_same_hash ... ok + +test result: ok. 9 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s + + Running unittests src/lib.rs (kernel/target/debug/deps/relayflowd_journal-d13cb7954335385c) + +running 28 tests +test registry::tests::a_same_boot_claim_with_no_run_yet_is_a_duplicate_not_wreckage ... ok +test registry::tests::releasing_a_claim_lets_the_same_boot_retry ... ok +test registry::tests::a_previous_boots_claim_with_no_run_is_repaired ... ok +test registry::tests::registry_is_a_rebuildable_run_locator ... ok +test registry::tests::a_registered_run_dedupes_across_boots ... ok +test registry::tests::a_pre_migration_registry_gains_boot_id_and_its_claims_are_repairable ... ok +test registry::tests::releasing_is_scoped_to_the_claiming_run ... ok +test channel::tests::stale_attempts_and_raw_forged_acknowledgements_cannot_change_offsets ... ok +test subscriptions::tests::prune_sweep_claims_deletes_only_rows_older_than_cutoff ... ok +test subscriptions::tests::last_run_for_subscription_returns_the_lex_greatest_ulid_regardless_of_insertion ... ok +test subscriptions::tests::detect_without_latch_stays_available_for_the_next_sweep ... ok +test subscriptions::tests::latch_is_a_no_op_if_a_fresh_event_arrived_between_detect_and_latch ... ok +test subscriptions::tests::sweep_election_gives_the_first_caller_the_result_and_second_gets_empty ... ok +test channel::tests::channels_cross_segment_boundaries_and_terminal_runs_reject_mutations ... ok +test subscriptions::tests::last_run_for_subscription_returns_none_before_first_arrival ... ok +test subscriptions::tests::sweep_ignores_subscriptions_whose_silence_is_still_within_budget ... ok +test subscriptions::tests::sweep_does_not_re_emit_the_same_stale_row_on_a_later_tick ... ok +test subscriptions::tests::sweep_marks_row_stale_when_silence_exceeds_budget ... ok +test subscriptions::tests::upsert_after_stale_re_arms_and_next_silence_can_re_emit ... ok +test subscriptions::tests::upsert_is_idempotent_across_bumps_and_preserves_event_type_updates ... ok +test tests::an_unconfirmed_election_is_reclaimed_by_the_next_attempt_not_treated_as_done ... ok +test tests::append_is_durable_and_monotonic_after_reopen ... ok +test tests::failed_commit_is_returned_not_swallowed ... ok +test channel::tests::failed_channel_writes_never_expose_delivery_or_advance_acknowledged_offset ... ok +test tests::rollover_is_atomic_scaffolding_for_epoch_resume ... ok +test tests::effects_are_deduplicated_at_the_journal_boundary ... ok +test tests::terminal_run_refuses_every_later_entry_atomically ... ok +test channel::tests::independent_connections_serialize_send_receive_and_acknowledgement ... ok + +test result: ok. 28 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.08s + + Doc-tests relayflowd + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + + Doc-tests relayflowd_core + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + + Doc-tests relayflowd_journal + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + + +exit=0 diff --git a/docs/evidence/pr245/named-selection-mutated.txt b/docs/evidence/pr245/named-selection-mutated.txt new file mode 100644 index 000000000..f3255f824 --- /dev/null +++ b/docs/evidence/pr245/named-selection-mutated.txt @@ -0,0 +1,42 @@ +$ cd packages/sdk +$ RELAYFLOWD_BIN=/Users/khaliqgant/lanes/flows245/repo/kernel/target/debug/relayflowd npm exec -- vitest run tests/live-named-agents.test.ts + + RUN v2.1.9 /Users/khaliqgant/lanes/flows245/repo/packages/sdk + + ❯ tests/live-named-agents.test.ts (1 test | 1 failed) 1349ms + × named agents against a live kernel > dispatches two distinct named agents declared in the flow header 1349ms + → expected { cli: 'reviewer', …(2) } to deeply equal { Object (cli, model, ...) } + +⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯ + + FAIL tests/live-named-agents.test.ts > named agents against a live kernel > dispatches two distinct named agents declared in the flow header +AssertionError: expected { cli: 'reviewer', …(2) } to deeply equal { Object (cli, model, ...) } + +- Expected ++ Received + + Object { +- "cli": "fixer", +- "model": "model-b", ++ "cli": "reviewer", ++ "model": "model-a", + "task": "fix what reviewer found", + } + + ❯ tests/live-named-agents.test.ts:125:31 + 123| expect(result.completionReason).toBe('success'); + 124| expect(captured['reviewer']).toEqual({ cli: 'reviewer', model: 'mo… + 125| expect(captured['fixer']).toEqual({ cli: 'fixer', model: 'model-b'… + | ^ + 126| expect(captured['override']).toEqual({ cli: 'fixer', model: 'model… + 127| }); + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯ + + Test Files 1 failed (1) + Tests 1 failed (1) + Start at 22:28:29 + Duration 1.69s (transform 97ms, setup 0ms, collect 150ms, tests 1.35s, environment 0ms, prepare 37ms) + + +exit=1 diff --git a/docs/evidence/pr245/named-selection-restored.txt b/docs/evidence/pr245/named-selection-restored.txt new file mode 100644 index 000000000..8c8346d6f --- /dev/null +++ b/docs/evidence/pr245/named-selection-restored.txt @@ -0,0 +1,15 @@ +$ cd packages/sdk +$ RELAYFLOWD_BIN=/Users/khaliqgant/lanes/flows245/repo/kernel/target/debug/relayflowd npm exec -- vitest run tests/live-named-agents.test.ts + + RUN v2.1.9 /Users/khaliqgant/lanes/flows245/repo/packages/sdk + + ✓ tests/live-named-agents.test.ts (1 test) 1423ms + ✓ named agents against a live kernel > dispatches two distinct named agents declared in the flow header 1422ms + + Test Files 1 passed (1) + Tests 1 passed (1) + Start at 22:28:31 + Duration 1.80s (transform 96ms, setup 0ms, collect 152ms, tests 1.42s, environment 0ms, prepare 25ms) + + +exit=0 diff --git a/docs/evidence/pr245/named-test-root-cwd.txt b/docs/evidence/pr245/named-test-root-cwd.txt new file mode 100644 index 000000000..f6bdf245f --- /dev/null +++ b/docs/evidence/pr245/named-test-root-cwd.txt @@ -0,0 +1,14 @@ +$ env -u RELAYFLOWD_BIN npm exec --prefix packages/sdk -- vitest run --root packages/sdk tests/live-named-agents.test.ts + + RUN v2.1.9 /Users/khaliqgant/lanes/flows245/repo/packages/sdk + + ✓ tests/live-named-agents.test.ts (1 test) 1376ms + ✓ named agents against a live kernel > dispatches two distinct named agents declared in the flow header 1376ms + + Test Files 1 passed (1) + Tests 1 passed (1) + Start at 22:43:42 + Duration 1.80s (transform 113ms, setup 0ms, collect 207ms, tests 1.38s, environment 0ms, prepare 39ms) + + +exit=0 diff --git a/docs/evidence/pr245/packed-consumer.txt b/docs/evidence/pr245/packed-consumer.txt new file mode 100644 index 000000000..0e4b0e772 --- /dev/null +++ b/docs/evidence/pr245/packed-consumer.txt @@ -0,0 +1,107 @@ +$ PATH=/tmp/flows245-tools/node_modules/.bin:$PATH bash scripts/surface-package-gate.sh +bun install v1.4.0 (34cbb9a40) + +1 package installed [95.00ms] +$ tsc +$ bun run build && tsc -p tsconfig.test.json && vitest run +$ tsc + + RUN v2.1.9 /Users/khaliqgant/lanes/flows245/repo/packages/surface + + ✓ tests/flow.test.ts (11 tests) 4ms + + Test Files 1 passed (1) + Tests 11 passed (11) + Start at 22:28:02 + Duration 245ms (transform 33ms, setup 0ms, collect 26ms, tests 4ms, environment 0ms, prepare 43ms) + +$ tsc -p ../../regressions/tsconfig.json +bun pack v1.4.0 (34cbb9a40) + +packed 1.14KB package.json +packed 2.77KB README.md +packed 2.50KB dist/cloud.d.ts +packed 2.30KB dist/cloud.d.ts.map +packed 44B dist/cloud.js +packed 102B dist/cloud.js.map +packed 0.63KB dist/completion.d.ts +packed 346B dist/completion.d.ts.map +packed 0.51KB dist/completion.js +packed 364B dist/completion.js.map +packed 1.24KB dist/context.d.ts +packed 1.0KB dist/context.d.ts.map +packed 46B dist/context.js +packed 106B dist/context.js.map +packed 2.17KB dist/flow.d.ts +packed 1.84KB dist/flow.d.ts.map +packed 10.50KB dist/flow.js +packed 9.74KB dist/flow.js.map +packed 500B dist/index.d.ts +packed 492B dist/index.d.ts.map +packed 147B dist/index.js +packed 200B dist/index.js.map +packed 171B dist/runtime.d.ts +packed 216B dist/runtime.d.ts.map +packed 83B dist/runtime.js +packed 148B dist/runtime.js.map +packed 310B dist/step.d.ts +packed 304B dist/step.d.ts.map +packed 43B dist/step.js +packed 100B dist/step.js.map +packed 2.0KB src/cloud.ts +packed 0.61KB src/completion.ts +packed 1.16KB src/context.ts +packed 12.73KB src/flow.ts +packed 495B src/index.ts +packed 144B src/runtime.ts +packed 272B src/step.ts + +/tmp/relayflows-surface-pack.GXxJob/relayflows-surface-2.0.9.tgz + +Total files: 37 +Shasum: 1240967230113d0476c64ec1513b729cd30b334c +Integrity: sha512-/ncqrlLB+vliv[...]W1FYhjc67ikEQ== +Unpacked size: 57.57KB +Packed size: 16.0KB + +added 58 packages, and audited 59 packages in 1s + +16 packages are looking for funding + run `npm fund` for details + +6 vulnerabilities (4 moderate, 1 high, 1 critical) + +To address issues that do not require attention, run: + npm audit fix + +To address all issues (including breaking changes), run: + npm audit fix --force + +Run `npm audit` for details. + +> @relayflows/sdk@2.0.9 typecheck +> tsc --noEmit && tsc -p tsconfig.type-tests.json + +bun add v1.4.0 (34cbb9a40) +Resolving dependencies +Resolved, downloaded and extracted [1] +Saved lockfile + +installed @relayflows/surface@/tmp/relayflows-surface-pack.GXxJob/relayflows-surface-2.0.9.tgz + +1 package installed [6.00ms] +PACKED_RUNTIME_OK name=packed-runtime-consumer completionReason=success +PACKED_RUNTIME_REFUSAL_OK invalidHeaders=9 forgedHandle=refused +PACKED_TYPESCRIPT_OK + + RUN v2.1.9 /Users/khaliqgant/lanes/flows245/repo/packages/sdk + + ✓ tests/authored-flow.test.ts (24 tests) 637ms + + Test Files 1 passed (1) + Tests 24 passed (24) + Start at 22:28:07 + Duration 1.01s (transform 103ms, setup 0ms, collect 160ms, tests 637ms, environment 0ms, prepare 28ms) + + +exit=0 diff --git a/docs/evidence/pr245/review-followup.txt b/docs/evidence/pr245/review-followup.txt new file mode 100644 index 000000000..2d54500fb --- /dev/null +++ b/docs/evidence/pr245/review-followup.txt @@ -0,0 +1,58 @@ +$ cd /Users/khaliqgant/lanes/flows245/repo/packages/sdk +$ export RELAYFLOWD_BIN=/Users/khaliqgant/lanes/flows245/repo/kernel/target/debug/relayflowd +$ npm run typecheck + +> @relayflows/sdk@2.0.9 typecheck +> tsc --noEmit && tsc -p tsconfig.type-tests.json + +exit: 0 +$ npm run build + +> @relayflows/sdk@2.0.9 build +> tsc && node scripts/make-cli-executable.mjs + +exit: 0 +$ npm run typecheck:tests + +> @relayflows/sdk@2.0.9 typecheck:tests +> tsc -p tsconfig.tests.json + +exit: 0 +$ npm exec -- vitest run tests/authored-agent-failure.test.ts tests/authored-flow.test.ts tests/authored-named-agents.test.ts tests/live-named-agents.test.ts tests/direct-input.test.ts tests/preflight.test.ts tests/cli.test.ts + + RUN v2.1.9 /Users/khaliqgant/lanes/flows245/repo/packages/sdk + + ✓ tests/authored-agent-failure.test.ts (5 tests) 5ms + ✓ tests/preflight.test.ts (27 tests) 23ms + ✓ tests/authored-flow.test.ts (24 tests) 703ms + ✓ tests/authored-named-agents.test.ts (7 tests) 919ms + ✓ named agent declarations > lets a step-level cli override win over the named declaration 346ms + ✓ named agent declarations > does not treat an unrelated f.agent name as a named-agent selector 559ms + ✓ tests/live-named-agents.test.ts (1 test) 2206ms + ✓ named agents against a live kernel > dispatches two distinct named agents declared in the flow header 2206ms + ✓ tests/direct-input.test.ts (5 tests) 6754ms + ✓ direct .flow.ts input through the built CLI and live runtime > executes inline and file JSON input through relayflowd 3181ms + ✓ direct .flow.ts input through the built CLI and live runtime > refuses missing and malformed input before contacting relayflowd 2177ms + ✓ direct .flow.ts input through the built CLI and live runtime > surfaces the specific preflight refusal kind for an unresolved f.agent, not a generic invalid_spec 514ms + ✓ direct .flow.ts input through the built CLI and live runtime > does not import or execute authored code before daemon availability 442ms + ✓ direct .flow.ts input through the built CLI and live runtime > refuses oversized file input before contacting relayflowd 439ms + ✓ tests/cli.test.ts (63 tests) 12089ms + ✓ flows check CLI > binds a checked relative wrapper to the flow directory for worker execution 820ms + ✓ flows check CLI > uses the raw Claude adapter model flag instead of accepting auth status as model proof 386ms + ✓ flows check CLI > refuses a typo model before probing or contacting relayflowd 397ms + ✓ flows check CLI > maps every input refusal path to its declared kind without raw exceptions 1440ms + ✓ flows run/resume CLI over the journal protocol > parses run options, submits the kernel dialect, and exits 0 on success 454ms + ✓ flows run/resume CLI over the journal protocol > exits 1 and emits the declared completionReason for a failed run 852ms + ✓ flows run/resume CLI over the journal protocol > reports a needs_human agent step as parked for human recovery 621ms + ✓ flows run/resume CLI over the journal protocol > maps only run_not_found resumes to exit 2 4132ms + + Test Files 7 passed (7) + Tests 132 passed (132) + Start at 23:06:42 + Duration 12.60s (transform 386ms, setup 0ms, collect 1.47s, tests 22.70s, environment 1ms, prepare 295ms) + +exit: 0 + +$ cd /Users/khaliqgant/lanes/flows245/repo +$ npm exec --prefix packages/sdk -- tsc --noEmit --target ES2022 --module ESNext --moduleResolution Bundler --strict --esModuleInterop --skipLibCheck packages/sdk/tests/authored-agent-failure.test.ts +exit: 0 diff --git a/docs/evidence/pr245/sdk-focused.txt b/docs/evidence/pr245/sdk-focused.txt new file mode 100644 index 000000000..ce99eeeee --- /dev/null +++ b/docs/evidence/pr245/sdk-focused.txt @@ -0,0 +1,39 @@ +$ cd packages/sdk && RELAYFLOWD_BIN=/Users/khaliqgant/lanes/flows245/repo/kernel/target/debug/relayflowd npm exec -- vitest run tests/authored-flow.test.ts tests/authored-named-agents.test.ts tests/live-named-agents.test.ts tests/direct-input.test.ts tests/preflight.test.ts tests/cli.test.ts + + RUN v2.1.9 /Users/khaliqgant/lanes/flows245/repo/packages/sdk + + ✓ tests/preflight.test.ts (27 tests) 22ms + ✓ tests/authored-flow.test.ts (24 tests) 640ms + ✓ tests/authored-named-agents.test.ts (7 tests) 922ms + ✓ named agent declarations > lets a step-level cli override win over the named declaration 369ms + ✓ named agent declarations > does not treat an unrelated f.agent name as a named-agent selector 547ms + ✓ tests/live-named-agents.test.ts (1 test) 1771ms + ✓ named agents against a live kernel > dispatches two distinct named agents declared in the flow header 1771ms + ✓ tests/direct-input.test.ts (5 tests) 7878ms + ✓ direct .flow.ts input through the built CLI and live runtime > executes inline and file JSON input through relayflowd 2713ms + ✓ direct .flow.ts input through the built CLI and live runtime > refuses missing and malformed input before contacting relayflowd 2539ms + ✓ direct .flow.ts input through the built CLI and live runtime > surfaces the specific preflight refusal kind for an unresolved f.agent, not a generic invalid_spec 1094ms + ✓ direct .flow.ts input through the built CLI and live runtime > does not import or execute authored code before daemon availability 706ms + ✓ direct .flow.ts input through the built CLI and live runtime > refuses oversized file input before contacting relayflowd 825ms + ✓ tests/cli.test.ts (63 tests) 16528ms + ✓ flows check CLI > binds a checked relative wrapper to the flow directory for worker execution 847ms + ✓ flows check CLI > uses the raw Claude adapter model flag instead of accepting auth status as model proof 398ms + ✓ flows check CLI > refuses a typo model before probing or contacting relayflowd 635ms + ✓ flows check CLI > maps every input refusal path to its declared kind without raw exceptions 2039ms + ✓ flows run/resume CLI over the journal protocol > parses run options, submits the kernel dialect, and exits 0 on success 880ms + ✓ flows run/resume CLI over the journal protocol > exits 1 and emits the declared completionReason for a failed run 592ms + ✓ flows run/resume CLI over the journal protocol > exits 3 and names the parked llm step 697ms + ✓ flows run/resume CLI over the journal protocol > reports a needs_human agent step as parked for human recovery 641ms + ✓ flows run/resume CLI over the journal protocol > classifies a typed hello refusal as a protocol error, not an unreachable daemon 622ms + ✓ flows run/resume CLI over the journal protocol > follows a dispatched worker step instead of reporting a protocol error 706ms + ✓ flows run/resume CLI over the journal protocol > bounds a worker wait by its lease and reports what it is waiting for 2408ms + ✓ flows run/resume CLI over the journal protocol > resumes a parked run from snapshot step types without reading journal sequence one 1377ms + ✓ flows run/resume CLI over the journal protocol > maps only run_not_found resumes to exit 2 3277ms + + Test Files 6 passed (6) + Tests 127 passed (127) + Start at 22:42:04 + Duration 16.99s (transform 311ms, setup 0ms, collect 1.12s, tests 27.76s, environment 1ms, prepare 288ms) + + +exit=0 diff --git a/docs/evidence/pr245/sdk-full.txt b/docs/evidence/pr245/sdk-full.txt new file mode 100644 index 000000000..f29316458 --- /dev/null +++ b/docs/evidence/pr245/sdk-full.txt @@ -0,0 +1,147 @@ +$ cd packages/sdk && RELAYFLOWD_BIN=/Users/khaliqgant/lanes/flows245/repo/kernel/target/debug/relayflowd npm exec -- vitest run + + RUN v2.1.9 /Users/khaliqgant/lanes/flows245/repo/packages/sdk + +stdout | tests/live-kernel.test.ts +LIVE_KERNEL relayflowd=/Users/khaliqgant/lanes/flows245/repo/kernel/target/debug/relayflowd +LIVE_KERNEL flows=/Users/khaliqgant/lanes/flows245/repo/packages/sdk/dist/cli.js + + ✓ tests/journal-client.test.ts (14 tests) 68ms + ✓ tests/daemon-lifecycle.test.ts (42 tests) 19ms + ✓ tests/validate.test.ts (68 tests) 13ms + ✓ tests/preflight.test.ts (27 tests) 47ms + ✓ tests/observer-link.test.ts (39 tests) 211ms + ✓ tests/tick-source.test.ts (33 tests) 20ms + ✓ tests/cloud-run.test.ts (47 tests) 137ms + ✓ tests/verb-field-lint.test.ts (78 tests) 243ms + ✓ tests/authored-flow.test.ts (24 tests) 658ms + ✓ tests/authored-flow-lifecycle-executor.test.ts (27 tests) 532ms + ✓ tests/gate-contract.test.ts (20 tests) 222ms + ✓ tests/backlog-picker.test.ts (14 tests) 108ms + ✓ tests/cli-hn-monitor.test.ts (16 tests) 108ms + ✓ tests/tick-runner.test.ts (22 tests) 1249ms + ✓ CLI argument parsing refuses coercion rather than accepting it > refuses --interval-ms exponent notation as an invocation error 369ms + ✓ tests/authored-flow-operation.test.ts (23 tests) 345ms + ✓ tests/backlog-picker-flow.test.ts (6 tests) 597ms + ✓ tests/work-package-consumer.test.ts (13 tests) 183ms + ✓ tests/spec-parity.test.ts (31 tests) 164ms + ✓ tests/worker-lease.test.ts (7 tests) 5ms + ✓ tests/typed-output.test.ts (14 tests) 104ms + ✓ tests/authored-named-agents.test.ts (6 tests) 1520ms + ✓ named agent declarations > lets a step-level cli override win over the named declaration 583ms + ✓ named agent declarations > does not treat an unrelated f.agent name as a named-agent selector 930ms + ✓ tests/model-selection.test.ts (10 tests) 11ms + ✓ tests/relayflowd-path.test.ts (10 tests) 2ms + ✓ tests/live-named-agents.test.ts (1 test) 2253ms + ✓ named agents against a live kernel > dispatches two distinct named agents declared in the flow header 2253ms + ✓ tests/local-dev-ux.test.ts (8 tests) 13ms + ✓ tests/json-schema-bound.test.ts (71 tests) 1388ms + ✓ JSON Schema termination bound > walks a deep schema with an explicit stack rather than recursion 1161ms + ✓ tests/dependency-validation.test.ts (6 tests) 243ms + ✓ tests/deterministic-llm.test.ts (5 tests) 34ms + ✓ tests/hn-poller.test.ts (6 tests) 3ms + ✓ tests/dir-watcher-poller.test.ts (6 tests) 2ms + ✓ tests/hello-deterministic.test.ts (5 tests) 9ms + ✓ tests/work-package-validator.test.ts (7 tests) 3ms + ✓ tests/bin.test.ts (7 tests) 601ms + ↓ tests/real-cli-adapters.test.ts (3 tests | 3 skipped) + ✓ tests/parse-json-output.test.ts (7 tests) 1ms + ✓ tests/cli-adapter.test.ts (3 tests) 2ms + ✓ tests/journal-client-completion.test.ts (4 tests) 100ms + ✓ tests/direct-input.test.ts (5 tests) 6382ms + ✓ direct .flow.ts input through the built CLI and live runtime > executes inline and file JSON input through relayflowd 2323ms + ✓ direct .flow.ts input through the built CLI and live runtime > refuses missing and malformed input before contacting relayflowd 2051ms + ✓ direct .flow.ts input through the built CLI and live runtime > surfaces the specific preflight refusal kind for an unresolved f.agent, not a generic invalid_spec 556ms + ✓ direct .flow.ts input through the built CLI and live runtime > does not import or execute authored code before daemon availability 506ms + ✓ direct .flow.ts input through the built CLI and live runtime > refuses oversized file input before contacting relayflowd 945ms + ✓ tests/classify-outcome.test.ts (2 tests) 2216ms + ✓ classifyOutcome > gives up and reports when a running run never becomes classifiable 2060ms + ✓ tests/direct-run-failure.test.ts (4 tests) 2ms + ✓ tests/stop-process-group.test.ts (6 tests) 7684ms + ✓ every stop reaches the process group, not just the direct child > exits the run after an execution-timeout stop 1022ms + ✓ every stop reaches the process group, not just the direct child > exits the run after a protocol terminate stop 865ms + ✓ every stop reaches the process group, not just the direct child > kills a SIGTERM-deaf grandchild after a protocol terminate stop 2131ms + ✓ every stop reaches the process group, not just the direct child > kills a SIGTERM-deaf grandchild after an execution-timeout stop 2218ms + ✓ every stop reaches the process group, not just the direct child > holds the loop open long enough for the escalation to run 1147ms + ✓ tests/placement.test.ts (54 tests) 9ms + ✓ tests/memory.test.ts (18 tests) 3ms + ✓ tests/worker-platform.test.ts (1 test) 1ms + ✓ tests/worker-cli-abort.test.ts (2 tests) 2560ms + ✓ stops claude and its process group when lease ownership is lost 1313ms + ✓ stops wrapper.mjs and its process group when lease ownership is lost 1247ms + ✓ tests/cli-progress-wait.test.ts (2 tests) 1168ms + ✓ run starts the wait clock on its first observed lease 734ms + ✓ resume starts the wait clock on its first observed lease 434ms + ✓ tests/daemon-lifecycle-live.test.ts (9 tests) 8908ms + ✓ flows run against a data dir with no daemon (§6 test 7) > cold start spawns exactly one daemon, the run succeeds, and the daemon outlives the CLI 1106ms + ✓ flows run against a data dir with no daemon (§6 test 7) > polls, bounded, for a daemon that holds the lock before it binds 1702ms + ✓ flows run against a data dir with no daemon (§6 test 7) > attaches to a serving daemon that has not published a connection file 577ms + ✓ flows run against a data dir with no daemon (§6 test 7) > a second run attaches to the daemon the first one started, spawning nothing 1390ms + ✓ flows run against a data dir with no daemon (§6 test 7) > detects a stale connection file left by a hard kill and starts a fresh daemon 1375ms + ✓ concurrent invocations against one empty data dir (§6 test 15) > ends with exactly one daemon owning the socket, and both runs succeed 972ms + ✓ refusals from a spawn that cannot produce a daemon > names relayflowd_not_found rather than falling through to PATH 422ms + ✓ refusals from a spawn that cannot produce a daemon > names daemon_start_failed and quotes the daemon log when startup dies 607ms + ✓ refusals from a spawn that cannot produce a daemon > refuses a daemon speaking another protocol version instead of binding over it 755ms + ✓ tests/cli.test.ts (63 tests) 11465ms + ✓ flows check CLI > binds a checked relative wrapper to the flow directory for worker execution 457ms + ✓ flows check CLI > uses Codex login status and reports a rejected model as unavailable, not unauthenticated 618ms + ✓ flows check CLI > accepts an exact allowlisted named-agent model and probes that model 416ms + ✓ flows check CLI > checks the same named-agent contract from declarative JSON 633ms + ✓ flows check CLI > refuses a typo model before probing or contacting relayflowd 418ms + ✓ flows check CLI > distinguishes an allowlisted but inaccessible model from broken auth 637ms + ✓ flows check CLI > maps every input refusal path to its declared kind without raw exceptions 917ms + ✓ flows run/resume CLI over the journal protocol > exits 1 and emits the declared completionReason for a failed run 595ms + ✓ flows run/resume CLI over the journal protocol > exits 3 and names the parked llm step 599ms + ✓ flows run/resume CLI over the journal protocol > reports a needs_human agent step as parked for human recovery 322ms + ✓ flows run/resume CLI over the journal protocol > classifies a typed hello refusal as a protocol error, not an unreachable daemon 558ms + ✓ flows run/resume CLI over the journal protocol > follows a dispatched worker step instead of reporting a protocol error 385ms + ✓ flows run/resume CLI over the journal protocol > bounds a worker wait by its lease and reports what it is waiting for 629ms + ✓ flows run/resume CLI over the journal protocol > maps only run_not_found resumes to exit 2 2625ms + ✓ tests/worker-cli.test.ts (13 tests) 21725ms + ✓ custom wrapper execution identity > passes an explicit safe environment at identification and execution 312ms + ✓ custom wrapper execution identity > refuses a wrapper symlink retarget before delivering private values 311ms + ✓ custom wrapper execution identity > bounds wrapper execution after acknowledgement 427ms + ✓ custom wrapper execution identity > bounds captured wrapper output 561ms + ✓ custom wrapper execution identity > refuses a duplicate execute protocol frame 473ms + ✓ custom wrapper execution bounds are reader-owned > resolves when a conforming wrapper leaks a stdio pipe to a background helper 2181ms + ✓ custom wrapper execution bounds are reader-owned > resolves when the leaked helper inherits stderr only 1981ms + ✓ custom wrapper execution bounds are reader-owned > resolves when a wrapper leaks a stdio pipe and exits before identifying 3257ms + ✓ custom wrapper execution bounds are reader-owned > journals a completionReason at the default bound when a wrapper leaks a stdio pipe 11256ms + ✓ custom wrapper execution bounds are reader-owned > accepts the same over-8KiB payload whether or not it coalesces with the execute token 421ms + ✓ tests/local-agent-live.test.ts (5 tests) 39452ms + ✓ built CLI local agent against a real daemon > dispatches through the wrapper and keeps --json stdout report-shaped 1045ms + ✓ built CLI local agent against a real daemon > runs beyond the initial 30-second lease without a second invocation 35758ms + ✓ built CLI local agent against a real daemon > renders actual agent completion in text output 762ms + ✓ built CLI local agent against a real daemon > returns a failed run when the agent process fails 820ms + ✓ built CLI local agent against a real daemon > refuses a workspace it cannot pin before invoking the agent 1066ms +stdout | tests/live-kernel.test.ts > built flows CLI against live relayflowd > hn-monitor analyze-story reaches done through the real Claude analyzer CLI +LIVE_ANALYZER ready: claude -p --model claude-haiku-4-5-20251001 round-trip OK + +stdout | tests/live-kernel.test.ts > built flows CLI against live relayflowd > hn-monitor analyze-story reaches done through the real Claude analyzer CLI +LIVE_ANALYZER analysis: {"reasoning":"This story is directly relevant to AI agents and automation as it describes an autonomous agent performing self-directed software engineering tasks (opening and reviewing pull requests), which is a core application of agentic AI systems.","relevance_score":9,"story_title":"Show HN: an agent that opens and reviews its own pull requests [wake-nonce-7f3a91c4]"} + +stdout | tests/live-kernel.test.ts > surface resume after a real daemon kill > resumes a three-step run with each successful completion exactly once +LIVE_KERNEL kill -9 pid=38647 run=01M26GNXCYY4060TWD9TGSGTY3 while step=two state=Running + + ✓ tests/live-kernel.test.ts (30 tests) 60224ms + ✓ built flows CLI against live relayflowd > runs rung (a), parks rung (b), and keeps JSON report-shaped 3215ms + ✓ built flows CLI against live relayflowd > allows a deterministic run to exceed the bounded request timeout 32212ms + ✓ built flows CLI against live relayflowd > follows a live worker dispatch through flows run 737ms + ✓ built flows CLI against live relayflowd > runs an agent CLI end to end through the SDK worker 326ms + ✓ built flows CLI against live relayflowd > f.agent lowers to a real agent step and dispatches through a live worker 802ms + ✓ built flows CLI against live relayflowd > f.agent's default flowPath anchors on cwd, not cwd's parent 736ms + ✓ built flows CLI against live relayflowd > can always get a parked run to a late-attaching worker 5590ms + ✓ built flows CLI against live relayflowd > reports a real manual-recovery NeedsHuman state as parked 495ms + ✓ built flows CLI against live relayflowd > AgentWorker passes a declared model to an identified wrapper as RELAYFLOW_MODEL 343ms + ✓ built flows CLI against live relayflowd > hn-monitor analyze-story reaches done through the real Claude analyzer CLI 9452ms + ✓ built flows CLI against live relayflowd > preflights before journaling and names an unreachable socket 1696ms + ✓ built flows CLI against live relayflowd > starts exactly one daemon when two runs race for one empty data dir 1009ms + ✓ surface resume after a real daemon kill > resumes a three-step run with each successful completion exactly once 1666ms + + Test Files 50 passed | 1 skipped (51) + Tests 941 passed | 3 skipped (944) + Start at 22:37:22 + Duration 60.67s (transform 849ms, setup 0ms, collect 3.92s, tests 173.02s, environment 4ms, prepare 1.90s) + + +exit=0 diff --git a/docs/evidence/pr245/sdk-wrong-cwd.txt b/docs/evidence/pr245/sdk-wrong-cwd.txt new file mode 100644 index 000000000..21d581fae --- /dev/null +++ b/docs/evidence/pr245/sdk-wrong-cwd.txt @@ -0,0 +1,449 @@ +$ RELAYFLOWD_BIN=/Users/khaliqgant/lanes/flows245/repo/kernel/target/debug/relayflowd npm exec --prefix packages/sdk -- vitest run --root packages/sdk + + RUN v2.1.9 /Users/khaliqgant/lanes/flows245/repo/packages/sdk + +stdout | tests/live-kernel.test.ts +LIVE_KERNEL relayflowd=/Users/khaliqgant/lanes/flows245/repo/kernel/target/debug/relayflowd +LIVE_KERNEL flows=/Users/khaliqgant/lanes/flows245/repo/packages/sdk/dist/cli.js + + ✓ tests/journal-client.test.ts (14 tests) 72ms + ✓ tests/daemon-lifecycle.test.ts (42 tests) 29ms + ✓ tests/validate.test.ts (68 tests) 13ms + ✓ tests/preflight.test.ts (27 tests) 31ms + ✓ tests/observer-link.test.ts (39 tests) 181ms + ✓ tests/tick-source.test.ts (33 tests) 20ms + ✓ tests/cloud-run.test.ts (47 tests) 108ms + ✓ tests/verb-field-lint.test.ts (78 tests) 195ms + ✓ tests/authored-flow.test.ts (24 tests) 650ms + ✓ tests/gate-contract.test.ts (20 tests) 85ms + ✓ tests/authored-flow-lifecycle-executor.test.ts (27 tests) 523ms + ✓ tests/cli-hn-monitor.test.ts (16 tests) 111ms + ✓ tests/backlog-picker.test.ts (14 tests) 91ms + ✓ tests/tick-runner.test.ts (22 tests) 810ms + ✓ tests/authored-flow-operation.test.ts (23 tests) 277ms + ✓ tests/backlog-picker-flow.test.ts (6 tests) 589ms + ❯ tests/work-package-consumer.test.ts (13 tests | 1 failed) 193ms + × scope existence > accepts a package scoping files that really are present 3ms + → expected false to be true // Object.is equality + ✓ tests/spec-parity.test.ts (31 tests) 218ms + ✓ tests/worker-lease.test.ts (7 tests) 11ms + ✓ tests/authored-named-agents.test.ts (6 tests) 1403ms + ✓ named agent declarations > lets a step-level cli override win over the named declaration 559ms + ✓ named agent declarations > does not treat an unrelated f.agent name as a named-agent selector 838ms + ✓ tests/typed-output.test.ts (14 tests) 203ms + ✓ tests/model-selection.test.ts (10 tests) 14ms + ✓ tests/relayflowd-path.test.ts (10 tests) 4ms + ❯ tests/local-agent-live.test.ts (5 tests | 5 failed) 401ms + × built CLI local agent against a real daemon > dispatches through the wrapper and keeps --json stdout report-shaped 90ms + → node:internal/modules/cjs/loader:1478 + throw err; + ^ + +Error: Cannot find module '/Users/khaliqgant/lanes/flows245/repo/dist/cli.js' + at Module._resolveFilename (node:internal/modules/cjs/loader:1475:15) + at wrapResolveFilename (node:internal/modules/cjs/loader:1048:27) + at defaultResolveImplForCJSLoading (node:internal/modules/cjs/loader:1072:10) + at resolveForCJSWithHooks (node:internal/modules/cjs/loader:1093:12) + at Module._load (node:internal/modules/cjs/loader:1261:25) + at wrapModuleLoad (node:internal/modules/cjs/loader:255:19) + at Module.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:154:5) + at node:internal/main/run_main_module:33:47 { + code: 'MODULE_NOT_FOUND', + requireStack: [] +} + +Node.js v25.8.1 +: expected 1 to be +0 // Object.is equality + × built CLI local agent against a real daemon > runs beyond the initial 30-second lease without a second invocation 68ms + → node:internal/modules/cjs/loader:1478 + throw err; + ^ + +Error: Cannot find module '/Users/khaliqgant/lanes/flows245/repo/dist/cli.js' + at Module._resolveFilename (node:internal/modules/cjs/loader:1475:15) + at wrapResolveFilename (node:internal/modules/cjs/loader:1048:27) + at defaultResolveImplForCJSLoading (node:internal/modules/cjs/loader:1072:10) + at resolveForCJSWithHooks (node:internal/modules/cjs/loader:1093:12) + at Module._load (node:internal/modules/cjs/loader:1261:25) + at wrapModuleLoad (node:internal/modules/cjs/loader:255:19) + at Module.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:154:5) + at node:internal/main/run_main_module:33:47 { + code: 'MODULE_NOT_FOUND', + requireStack: [] +} + +Node.js v25.8.1 +: expected 1 to be +0 // Object.is equality + × built CLI local agent against a real daemon > renders actual agent completion in text output 76ms + → node:internal/modules/cjs/loader:1478 + throw err; + ^ + +Error: Cannot find module '/Users/khaliqgant/lanes/flows245/repo/dist/cli.js' + at Module._resolveFilename (node:internal/modules/cjs/loader:1475:15) + at wrapResolveFilename (node:internal/modules/cjs/loader:1048:27) + at defaultResolveImplForCJSLoading (node:internal/modules/cjs/loader:1072:10) + at resolveForCJSWithHooks (node:internal/modules/cjs/loader:1093:12) + at Module._load (node:internal/modules/cjs/loader:1261:25) + at wrapModuleLoad (node:internal/modules/cjs/loader:255:19) + at Module.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:154:5) + at node:internal/main/run_main_module:33:47 { + code: 'MODULE_NOT_FOUND', + requireStack: [] +} + +Node.js v25.8.1 +: expected 1 to be +0 // Object.is equality + × built CLI local agent against a real daemon > returns a failed run when the agent process fails 92ms + → expected 'node:internal/modules/cjs/loader:1478…' to contain '✗ agent-1' + × built CLI local agent against a real daemon > refuses a workspace it cannot pin before invoking the agent 74ms + → node:internal/modules/cjs/loader:1478 + throw err; + ^ + +Error: Cannot find module '/Users/khaliqgant/lanes/flows245/repo/dist/cli.js' + at Module._resolveFilename (node:internal/modules/cjs/loader:1475:15) + at wrapResolveFilename (node:internal/modules/cjs/loader:1048:27) + at defaultResolveImplForCJSLoading (node:internal/modules/cjs/loader:1072:10) + at resolveForCJSWithHooks (node:internal/modules/cjs/loader:1093:12) + at Module._load (node:internal/modules/cjs/loader:1261:25) + at wrapModuleLoad (node:internal/modules/cjs/loader:255:19) + at Module.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:154:5) + at node:internal/main/run_main_module:33:47 { + code: 'MODULE_NOT_FOUND', + requireStack: [] +} + +Node.js v25.8.1 +: expected 1 to be 2 // Object.is equality + ✓ tests/local-dev-ux.test.ts (8 tests) 18ms + ✓ tests/live-named-agents.test.ts (1 test) 2798ms + ✓ named agents against a live kernel > dispatches two distinct named agents declared in the flow header 2797ms + ✓ tests/deterministic-llm.test.ts (5 tests) 61ms + ✓ tests/dependency-validation.test.ts (6 tests) 459ms + ✓ tests/json-schema-bound.test.ts (71 tests) 2118ms + ✓ JSON Schema termination bound > walks a deep schema with an explicit stack rather than recursion 1697ms + ✓ tests/hn-poller.test.ts (6 tests) 3ms + ✓ tests/dir-watcher-poller.test.ts (6 tests) 3ms + ✓ tests/work-package-validator.test.ts (7 tests) 3ms + ✓ tests/hello-deterministic.test.ts (5 tests) 9ms + ↓ tests/real-cli-adapters.test.ts (3 tests | 3 skipped) + ✓ tests/bin.test.ts (7 tests) 707ms + ✓ tests/parse-json-output.test.ts (7 tests) 2ms + ✓ tests/cli-adapter.test.ts (3 tests) 2ms + ✓ tests/journal-client-completion.test.ts (4 tests) 100ms + ✓ tests/direct-run-failure.test.ts (4 tests) 2ms + ✓ tests/cli-progress-wait.test.ts (2 tests) 1029ms + ✓ run starts the wait clock on its first observed lease 389ms + ✓ resume starts the wait clock on its first observed lease 640ms + ✓ tests/placement.test.ts (54 tests) 8ms + ✓ tests/stop-process-group.test.ts (6 tests) 7454ms + ✓ every stop reaches the process group, not just the direct child > exits the run after an execution-timeout stop 866ms + ✓ every stop reaches the process group, not just the direct child > exits the run after a protocol terminate stop 897ms + ✓ every stop reaches the process group, not just the direct child > kills a SIGTERM-deaf grandchild after a protocol terminate stop 2169ms + ✓ every stop reaches the process group, not just the direct child > kills a SIGTERM-deaf grandchild after an execution-timeout stop 2058ms + ✓ every stop reaches the process group, not just the direct child > holds the loop open long enough for the escalation to run 1155ms + ✓ every stop reaches the process group, not just the direct child > terminate() forces a group that outlives SIGTERM 309ms + ✓ tests/classify-outcome.test.ts (2 tests) 2217ms + ✓ classifyOutcome > gives up and reports when a running run never becomes classifiable 2061ms + ✓ tests/memory.test.ts (18 tests) 4ms + ✓ tests/worker-platform.test.ts (1 test) 2ms + ✓ tests/direct-input.test.ts (5 tests) 7739ms + ✓ direct .flow.ts input through the built CLI and live runtime > executes inline and file JSON input through relayflowd 3075ms + ✓ direct .flow.ts input through the built CLI and live runtime > refuses missing and malformed input before contacting relayflowd 2042ms + ✓ direct .flow.ts input through the built CLI and live runtime > surfaces the specific preflight refusal kind for an unresolved f.agent, not a generic invalid_spec 1058ms + ✓ direct .flow.ts input through the built CLI and live runtime > does not import or execute authored code before daemon availability 774ms + ✓ direct .flow.ts input through the built CLI and live runtime > refuses oversized file input before contacting relayflowd 788ms + ✓ tests/daemon-lifecycle-live.test.ts (9 tests) 10352ms + ✓ flows run against a data dir with no daemon (§6 test 7) > cold start spawns exactly one daemon, the run succeeds, and the daemon outlives the CLI 1730ms + ✓ flows run against a data dir with no daemon (§6 test 7) > polls, bounded, for a daemon that holds the lock before it binds 1670ms + ✓ flows run against a data dir with no daemon (§6 test 7) > attaches to a serving daemon that has not published a connection file 535ms + ✓ flows run against a data dir with no daemon (§6 test 7) > a second run attaches to the daemon the first one started, spawning nothing 1919ms + ✓ flows run against a data dir with no daemon (§6 test 7) > detects a stale connection file left by a hard kill and starts a fresh daemon 962ms + ✓ concurrent invocations against one empty data dir (§6 test 15) > ends with exactly one daemon owning the socket, and both runs succeed 949ms + ✓ refusals from a spawn that cannot produce a daemon > names relayflowd_not_found rather than falling through to PATH 1055ms + ✓ refusals from a spawn that cannot produce a daemon > names daemon_start_failed and quotes the daemon log when startup dies 748ms + ✓ refusals from a spawn that cannot produce a daemon > refuses a daemon speaking another protocol version instead of binding over it 784ms + ✓ tests/cli.test.ts (63 tests) 12130ms + ✓ flows check CLI > binds a checked relative wrapper to the flow directory for worker execution 348ms + ✓ flows check CLI > uses Codex login status and reports a rejected model as unavailable, not unauthenticated 578ms + ✓ flows check CLI > refuses a nonconforming custom wrapper without calling it an authentication failure 407ms + ✓ flows check CLI > accepts an exact allowlisted named-agent model and probes that model 669ms + ✓ flows check CLI > checks the same named-agent contract from declarative JSON 562ms + ✓ flows check CLI > distinguishes an allowlisted but inaccessible model from broken auth 391ms + ✓ flows check CLI > maps every input refusal path to its declared kind without raw exceptions 1380ms + ✓ flows run/resume CLI over the journal protocol > parses run options, submits the kernel dialect, and exits 0 on success 713ms + ✓ flows run/resume CLI over the journal protocol > exits 1 and emits the declared completionReason for a failed run 647ms + ✓ flows run/resume CLI over the journal protocol > follows a dispatched worker step instead of reporting a protocol error 938ms + ✓ flows run/resume CLI over the journal protocol > bounds a worker wait by its lease and reports what it is waiting for 834ms + ✓ flows run/resume CLI over the journal protocol > resumes a parked run from snapshot step types without reading journal sequence one 660ms + ✓ flows run/resume CLI over the journal protocol > maps only run_not_found resumes to exit 2 1847ms + ❯ tests/worker-cli-abort.test.ts (2 tests | 1 failed) 6248ms + ✓ stops claude and its process group when lease ownership is lost 1241ms + × stops wrapper.mjs and its process group when lease ownership is lost 5007ms + → expected false to be true // Object.is equality + ✓ tests/worker-cli.test.ts (13 tests) 21567ms + ✓ custom wrapper execution identity > refuses a wrapper symlink retarget before delivering private values 355ms + ✓ custom wrapper execution identity > bounds wrapper execution after acknowledgement 384ms + ✓ custom wrapper execution identity > bounds captured wrapper output 503ms + ✓ custom wrapper execution identity > refuses a duplicate execute protocol frame 432ms + ✓ custom wrapper execution bounds are reader-owned > resolves when a conforming wrapper leaks a stdio pipe to a background helper 2213ms + ✓ custom wrapper execution bounds are reader-owned > resolves when the leaked helper inherits stderr only 1789ms + ✓ custom wrapper execution bounds are reader-owned > resolves when a wrapper leaks a stdio pipe and exits before identifying 3253ms + ✓ custom wrapper execution bounds are reader-owned > journals a completionReason at the default bound when a wrapper leaks a stdio pipe 11256ms + ✓ custom wrapper execution bounds are reader-owned > accepts the same over-8KiB payload whether or not it coalesces with the execute token 570ms +stdout | tests/live-kernel.test.ts > built flows CLI against live relayflowd > hn-monitor analyze-story reaches done through the real Claude analyzer CLI +LIVE_ANALYZER ready: claude -p --model claude-haiku-4-5-20251001 round-trip OK + +stdout | tests/live-kernel.test.ts > built flows CLI against live relayflowd > hn-monitor analyze-story reaches done through the real Claude analyzer CLI +LIVE_ANALYZER analysis: {"reasoning":"This story directly describes an AI agent that automates software development workflows by autonomously opening and reviewing pull requests, which is a core application of agent-based automation. The capability demonstrated is highly relevant to the agent and workflow automation domain.","relevance_score":10,"story_title":"Show HN: an agent that opens and reviews its own pull requests [wake-nonce-7f3a91c4]"} + +stdout | tests/live-kernel.test.ts > surface resume after a real daemon kill > resumes a three-step run with each successful completion exactly once +LIVE_KERNEL kill -9 pid=32221 run=01M26GK4GPTQQFVYNQ750JN88T while step=two state=Running + + ✓ tests/live-kernel.test.ts (30 tests) 61223ms + ✓ built flows CLI against live relayflowd > runs rung (a), parks rung (b), and keeps JSON report-shaped 5599ms + ✓ built flows CLI against live relayflowd > allows a deterministic run to exceed the bounded request timeout 32283ms + ✓ built flows CLI against live relayflowd > follows a live worker dispatch through flows run 1102ms + ✓ built flows CLI against live relayflowd > f.agent lowers to a real agent step and dispatches through a live worker 305ms + ✓ built flows CLI against live relayflowd > f.agent's default flowPath anchors on cwd, not cwd's parent 314ms + ✓ built flows CLI against live relayflowd > can always get a parked run to a late-attaching worker 5563ms + ✓ built flows CLI against live relayflowd > reports a real manual-recovery NeedsHuman state as parked 809ms + ✓ built flows CLI against live relayflowd > AgentWorker passes a declared model to an identified wrapper as RELAYFLOW_MODEL 351ms + ✓ built flows CLI against live relayflowd > hn-monitor analyze-story reaches done through the real Claude analyzer CLI 8927ms + ✓ built flows CLI against live relayflowd > preflights before journaling and names an unreachable socket 923ms + ✓ built flows CLI against live relayflowd > starts exactly one daemon when two runs race for one empty data dir 825ms + ✓ surface resume after a real daemon kill > resumes a three-step run with each successful completion exactly once 1935ms + +⎯⎯⎯⎯⎯⎯⎯ Failed Tests 7 ⎯⎯⎯⎯⎯⎯⎯ + + FAIL tests/local-agent-live.test.ts > built CLI local agent against a real daemon > dispatches through the wrapper and keeps --json stdout report-shaped +AssertionError: node:internal/modules/cjs/loader:1478 + throw err; + ^ + +Error: Cannot find module '/Users/khaliqgant/lanes/flows245/repo/dist/cli.js' + at Module._resolveFilename (node:internal/modules/cjs/loader:1475:15) + at wrapResolveFilename (node:internal/modules/cjs/loader:1048:27) + at defaultResolveImplForCJSLoading (node:internal/modules/cjs/loader:1072:10) + at resolveForCJSWithHooks (node:internal/modules/cjs/loader:1093:12) + at Module._load (node:internal/modules/cjs/loader:1261:25) + at wrapModuleLoad (node:internal/modules/cjs/loader:255:19) + at Module.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:154:5) + at node:internal/main/run_main_module:33:47 { + code: 'MODULE_NOT_FOUND', + requireStack: [] +} + +Node.js v25.8.1 +: expected 1 to be +0 // Object.is equality + +- Expected ++ Received + +- 0 ++ 1 + + ❯ tests/local-agent-live.test.ts:65:58 + 63| const f = fixture(); + 64| const result = f.invoke('--json'); + 65| expect(result.status, result.stderr + result.stdout).toBe(0); + | ^ + 66| expect(JSON.parse(result.stdout)).toMatchObject({ ok: true, status… + 67| expect(readFileSync(f.marker, 'utf8')).toBe('hello'); + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/7]⎯ + + FAIL tests/local-agent-live.test.ts > built CLI local agent against a real daemon > runs beyond the initial 30-second lease without a second invocation +AssertionError: node:internal/modules/cjs/loader:1478 + throw err; + ^ + +Error: Cannot find module '/Users/khaliqgant/lanes/flows245/repo/dist/cli.js' + at Module._resolveFilename (node:internal/modules/cjs/loader:1475:15) + at wrapResolveFilename (node:internal/modules/cjs/loader:1048:27) + at defaultResolveImplForCJSLoading (node:internal/modules/cjs/loader:1072:10) + at resolveForCJSWithHooks (node:internal/modules/cjs/loader:1093:12) + at Module._load (node:internal/modules/cjs/loader:1261:25) + at wrapModuleLoad (node:internal/modules/cjs/loader:255:19) + at Module.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:154:5) + at node:internal/main/run_main_module:33:47 { + code: 'MODULE_NOT_FOUND', + requireStack: [] +} + +Node.js v25.8.1 +: expected 1 to be +0 // Object.is equality + +- Expected ++ Received + +- 0 ++ 1 + + ❯ tests/local-agent-live.test.ts:73:58 + 71| const f = fixture(0, undefined, 35_000); + 72| const result = f.invoke('--json'); + 73| expect(result.status, result.stderr + result.stdout).toBe(0); + | ^ + 74| expect(JSON.parse(result.stdout)).toMatchObject({ ok: true, comple… + 75| expect(readFileSync(f.marker, 'utf8')).toBe('hello'); + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/7]⎯ + + FAIL tests/local-agent-live.test.ts > built CLI local agent against a real daemon > renders actual agent completion in text output +AssertionError: node:internal/modules/cjs/loader:1478 + throw err; + ^ + +Error: Cannot find module '/Users/khaliqgant/lanes/flows245/repo/dist/cli.js' + at Module._resolveFilename (node:internal/modules/cjs/loader:1475:15) + at wrapResolveFilename (node:internal/modules/cjs/loader:1048:27) + at defaultResolveImplForCJSLoading (node:internal/modules/cjs/loader:1072:10) + at resolveForCJSWithHooks (node:internal/modules/cjs/loader:1093:12) + at Module._load (node:internal/modules/cjs/loader:1261:25) + at wrapModuleLoad (node:internal/modules/cjs/loader:255:19) + at Module.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:154:5) + at node:internal/main/run_main_module:33:47 { + code: 'MODULE_NOT_FOUND', + requireStack: [] +} + +Node.js v25.8.1 +: expected 1 to be +0 // Object.is equality + +- Expected ++ Received + +- 0 ++ 1 + + ❯ tests/local-agent-live.test.ts:79:58 + 77| it('renders actual agent completion in text output', () => { + 78| const result = fixture().invoke(); + 79| expect(result.status, result.stderr + result.stdout).toBe(0); + | ^ + 80| expect(result.stderr).toContain('✓ agent-1 (agent) [agent: complet… + 81| }); + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[3/7]⎯ + + FAIL tests/local-agent-live.test.ts > built CLI local agent against a real daemon > returns a failed run when the agent process fails +AssertionError: expected 'node:internal/modules/cjs/loader:1478…' to contain '✗ agent-1' + +- Expected ++ Received + +- ✗ agent-1 ++ node:internal/modules/cjs/loader:1478 ++ throw err; ++ ^ ++ ++ Error: Cannot find module '/Users/khaliqgant/lanes/flows245/repo/dist/cli.js' ++ at Module._resolveFilename (node:internal/modules/cjs/loader:1475:15) ++ at wrapResolveFilename (node:internal/modules/cjs/loader:1048:27) ++ at defaultResolveImplForCJSLoading (node:internal/modules/cjs/loader:1072:10) ++ at resolveForCJSWithHooks (node:internal/modules/cjs/loader:1093:12) ++ at Module._load (node:internal/modules/cjs/loader:1261:25) ++ at wrapModuleLoad (node:internal/modules/cjs/loader:255:19) ++ at Module.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:154:5) ++ at node:internal/main/run_main_module:33:47 { ++ code: 'MODULE_NOT_FOUND', ++ requireStack: [] ++ } ++ ++ Node.js v25.8.1 ++ + + ❯ tests/local-agent-live.test.ts:85:27 + 83| const result = fixture(7).invoke(); + 84| expect(result.status, result.stderr + result.stdout).toBe(1); + 85| expect(result.stderr).toContain('✗ agent-1'); + | ^ + 86| expect(result.stderr).not.toContain('[agent: completed]'); + 87| }); + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[4/7]⎯ + + FAIL tests/local-agent-live.test.ts > built CLI local agent against a real daemon > refuses a workspace it cannot pin before invoking the agent +AssertionError: node:internal/modules/cjs/loader:1478 + throw err; + ^ + +Error: Cannot find module '/Users/khaliqgant/lanes/flows245/repo/dist/cli.js' + at Module._resolveFilename (node:internal/modules/cjs/loader:1475:15) + at wrapResolveFilename (node:internal/modules/cjs/loader:1048:27) + at defaultResolveImplForCJSLoading (node:internal/modules/cjs/loader:1072:10) + at resolveForCJSWithHooks (node:internal/modules/cjs/loader:1093:12) + at Module._load (node:internal/modules/cjs/loader:1261:25) + at wrapModuleLoad (node:internal/modules/cjs/loader:255:19) + at Module.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:154:5) + at node:internal/main/run_main_module:33:47 { + code: 'MODULE_NOT_FOUND', + requireStack: [] +} + +Node.js v25.8.1 +: expected 1 to be 2 // Object.is equality + +- Expected ++ Received + +- 2 ++ 1 + + ❯ tests/local-agent-live.test.ts:91:58 + 89| const f = fixture(0, 'repo'); + 90| const result = f.invoke(); + 91| expect(result.status, result.stderr + result.stdout).toBe(2); + | ^ + 92| expect(result.stderr).toContain('stream-only'); + 93| expect(existsSync(f.marker)).toBe(false); + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[5/7]⎯ + + FAIL tests/work-package-consumer.test.ts > scope existence > accepts a package scoping files that really are present +AssertionError: expected false to be true // Object.is equality + +- Expected ++ Received + +- true ++ false + + ❯ tests/work-package-consumer.test.ts:176:47 + 174| // Injection exists to make the check testable, not to disable it. + 175| const real = { ...scoped, files_in_scope: ['package.json'] }; + 176| expect(consumeWorkPackage(real).accepted).toBe(true); + | ^ + 177| }); + 178| }); + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[6/7]⎯ + + FAIL tests/worker-cli-abort.test.ts > stops wrapper.mjs and its process group when lease ownership is lost +AssertionError: expected false to be true // Object.is equality + +- Expected ++ Received + +- true ++ false + + ❯ tests/worker-cli-abort.test.ts:30:34 + 28| const deadline = Date.now() + 5000; + 29| while (!existsSync(childPid) && Date.now() < deadline) await new P… + 30| expect(existsSync(childPid)).toBe(true); + | ^ + 31| controller.abort(new Error('lease rejected')); + 32| expect((await running).exit_code).toBeNull(); + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[7/7]⎯ + + Test Files 3 failed | 47 passed | 1 skipped (51) + Tests 7 failed | 934 passed | 3 skipped (944) + Start at 22:35:50 + Duration 61.72s (transform 889ms, setup 0ms, collect 4.21s, tests 142.49s, environment 4ms, prepare 1.62s) + + +exit=1 diff --git a/packages/relayflows/package-lock.json b/packages/relayflows/package-lock.json index 0d90007fb..2c62ba1bd 100644 --- a/packages/relayflows/package-lock.json +++ b/packages/relayflows/package-lock.json @@ -970,9 +970,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.7.tgz", - "integrity": "sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz", + "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==", "funding": [ { "type": "github", @@ -1234,9 +1234,9 @@ } }, "node_modules/js-yaml": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.4.1.tgz", - "integrity": "sha512-28R/k+NAjeuf7+CKlTxWZVExJGwVVLwY06DgEnOMz2gEpfNkDcD7QvyiVPT0xy0XXhU8vHsd4Ot42OOPdJG7dQ==", + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.4.2.tgz", + "integrity": "sha512-m+aqu+LwO1O6sIopafj8HUVl5aawITwZQe/yHpMCKjaWBaA/d07B/QdMb3529REftiU+RMMHL3Vlsw3hON7vWg==", "funding": [ { "type": "github", diff --git a/packages/sdk/package-lock.json b/packages/sdk/package-lock.json index 91b2f87c2..74cf3955e 100644 --- a/packages/sdk/package-lock.json +++ b/packages/sdk/package-lock.json @@ -447,9 +447,9 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", "dev": true, "license": "MIT" }, @@ -501,6 +501,9 @@ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -562,9 +565,9 @@ } }, "node_modules/@relayfile/core": { - "version": "0.10.57", - "resolved": "https://registry.npmjs.org/@relayfile/core/-/core-0.10.57.tgz", - "integrity": "sha512-oKwNJKGovzdl/6IF3sSVZFsiG2ITH7jXnmFgxbCIYPZ+J7QsQ2zvBhT35sPGiNcD4FfiMpyX9OB8LJXOefNdcQ==", + "version": "0.10.63", + "resolved": "https://registry.npmjs.org/@relayfile/core/-/core-0.10.63.tgz", + "integrity": "sha512-gMK63uZGsuWVnjmDHpUb/sb6P0HukMU2e4tJHohUfEVLfl9qe12KJ2ySsN91LUEAs+MsiULSkvVJVlrWUiWVJw==", "license": "Apache-2.0", "peer": true, "engines": { @@ -572,9 +575,9 @@ } }, "node_modules/@relayfile/mount-darwin-arm64": { - "version": "0.10.57", - "resolved": "https://registry.npmjs.org/@relayfile/mount-darwin-arm64/-/mount-darwin-arm64-0.10.57.tgz", - "integrity": "sha512-yCcjQ7HoCJECnjtZeG7eZwLXa4RAT3RAVAAymdIqKZbMe3wbkF1kg3YNQHuRqYsDEIQzGkA4k5AsITmG1j2PfQ==", + "version": "0.10.63", + "resolved": "https://registry.npmjs.org/@relayfile/mount-darwin-arm64/-/mount-darwin-arm64-0.10.63.tgz", + "integrity": "sha512-zA4fRSBe4YRWeYYqpZKvX6HsY/04G0tOwl58lTcvy2r9jEwNDJNCpqwtPS2yCoqQTyWay5sXjHx+qDhS5YiPjQ==", "cpu": [ "arm64" ], @@ -586,9 +589,9 @@ "peer": true }, "node_modules/@relayfile/mount-darwin-x64": { - "version": "0.10.57", - "resolved": "https://registry.npmjs.org/@relayfile/mount-darwin-x64/-/mount-darwin-x64-0.10.57.tgz", - "integrity": "sha512-5Ze1a16Mxc2PTj2Q+AdtRZIMKQ4sxJIIXwPjdWD+9rTMEQeqs7xzSZ1WRIjI89FzyNHBjx/+gzD9FK8Kjuc7Yg==", + "version": "0.10.63", + "resolved": "https://registry.npmjs.org/@relayfile/mount-darwin-x64/-/mount-darwin-x64-0.10.63.tgz", + "integrity": "sha512-4lBUHuizQr1Mxbt/uvegojoPZsU00Od5w6VjsGgFKaffuCAkaYT0E1WKuQb7Qvt95ZuiCk97fkumsP9U+RWbdA==", "cpu": [ "x64" ], @@ -600,9 +603,9 @@ "peer": true }, "node_modules/@relayfile/mount-linux-arm64": { - "version": "0.10.57", - "resolved": "https://registry.npmjs.org/@relayfile/mount-linux-arm64/-/mount-linux-arm64-0.10.57.tgz", - "integrity": "sha512-+2MhcDwzb7KSC3qjKWOW+9EnZ8mFKJiVns5fiJwO1oThiAd3CnnCfJzYkbEWRKIL0dH5wg4npdayF23DiBtB5w==", + "version": "0.10.63", + "resolved": "https://registry.npmjs.org/@relayfile/mount-linux-arm64/-/mount-linux-arm64-0.10.63.tgz", + "integrity": "sha512-Nsgw1Hp1ITPtw975TBQ8tm1OtAMjb9gCl8BkbChuMQ4oPWCONXDbDuJON1A9K/U81Msr8RNURfMAyQdFd3gmXA==", "cpu": [ "arm64" ], @@ -614,9 +617,9 @@ "peer": true }, "node_modules/@relayfile/mount-linux-x64": { - "version": "0.10.57", - "resolved": "https://registry.npmjs.org/@relayfile/mount-linux-x64/-/mount-linux-x64-0.10.57.tgz", - "integrity": "sha512-Y0OGuGEpglQsUrrR0uzjBzqFyrM8wU5jG1tELJ8LBNhedNUEUupGKD7ixs+i4k9kR8Q68voVsQTfzdgqzW3M+Q==", + "version": "0.10.63", + "resolved": "https://registry.npmjs.org/@relayfile/mount-linux-x64/-/mount-linux-x64-0.10.63.tgz", + "integrity": "sha512-dsA1amLyR8Kpwowil5UGFmT9cM2C7Gbdv/BklsbMY+Ev5zCzngV6Cqo9N3SqwHc96g0mEdh5DkHo4gBhcnWucA==", "cpu": [ "x64" ], @@ -639,13 +642,13 @@ } }, "node_modules/@relayfile/sdk": { - "version": "0.10.57", - "resolved": "https://registry.npmjs.org/@relayfile/sdk/-/sdk-0.10.57.tgz", - "integrity": "sha512-9AJIEyyruqVogbdqVfY7q0pjuXJy8IhzxR/1pvwWJiybU1BZO8LFNurlFePTAo/uhLG9cshr0YJRBxv2WbG0Sg==", + "version": "0.10.63", + "resolved": "https://registry.npmjs.org/@relayfile/sdk/-/sdk-0.10.63.tgz", + "integrity": "sha512-XVagKtSIumjG497WWmKHIPVBn6BYZf8nOCNO/hwebjzyiI2zig88JYQgQWnrA0FzAZI+JZF1dvy8gI8lb/OsTQ==", "license": "Apache-2.0", "peer": true, "dependencies": { - "@relayfile/core": "0.10.57", + "@relayfile/core": "0.10.63", "ignore": "^7.0.5", "tar": "^7.5.10" }, @@ -653,10 +656,10 @@ "node": ">=18" }, "optionalDependencies": { - "@relayfile/mount-darwin-arm64": "0.10.57", - "@relayfile/mount-darwin-x64": "0.10.57", - "@relayfile/mount-linux-arm64": "0.10.57", - "@relayfile/mount-linux-x64": "0.10.57" + "@relayfile/mount-darwin-arm64": "0.10.63", + "@relayfile/mount-darwin-x64": "0.10.63", + "@relayfile/mount-linux-arm64": "0.10.63", + "@relayfile/mount-linux-x64": "0.10.63" } }, "node_modules/@relayflows/surface": { @@ -675,9 +678,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.0.tgz", - "integrity": "sha512-70TeIFezKKy65LgAVyQh+w94/gjWhvPWaLaGGeMEgVrPkQhuj/M5bAYYZzIFUj9Y69oHyTm5Um/R6gcLh4A8JA==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.3.tgz", + "integrity": "sha512-w3Jnvi1ocaVm/c7yVPpfB98XeSRBMyzp6njL5MVVbGyXjpmUkN+s6Hp4t0PqhGCCaI1ZHMKXt/w0lA1RCaLVcw==", "cpu": [ "arm" ], @@ -689,9 +692,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.63.0.tgz", - "integrity": "sha512-YC86tYIHK6M1IV+wbzO+Bxk8RCBr6ZyWYgWxUCzaZD8mc8rrFoIJDNzDrkHBYRc/wKdrsIXmm6/F7NzrAO+OrA==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.63.3.tgz", + "integrity": "sha512-uI/ESiaIbbRYAEhzy8PCUWDp1hB0bjAqM06mW9flOoNO4Q8DQpeoREhBR5Hegfl+wpXiguyJv6XSPzEN7OxyHQ==", "cpu": [ "arm64" ], @@ -703,9 +706,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.63.0.tgz", - "integrity": "sha512-oI+ECtUcli0y0fi4xpW82GdPIXdTkI8G8DSjG2LRuw09fPAGykaWYH/hXxiKuTxiAjiPSTIIuYUqof5Z2hShWw==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.63.3.tgz", + "integrity": "sha512-oxhrd1jmXLwWZ83eQYDXxuqRdkqkzrjR3JobKeuUyfdNZo11FuQIvqEOZhyIT7OBHxXoGslDDjN0cQcM6T0TqQ==", "cpu": [ "arm64" ], @@ -717,9 +720,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.63.0.tgz", - "integrity": "sha512-NwV+1s7TiKrMe4owHyKB/dTLD7ZJD0YEBEhIz+hvav1Cu1GReJjF+rsdNwjzENQeIAbE/CoNiaAc5Vz2h5DPAA==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.63.3.tgz", + "integrity": "sha512-7/YiIMghVE8DrxKvNdorAaJVdriOFgOIpdStnPx8ppx5zfTwC3jBCSEAIzB7JD5404m65THl6H93UTTVUvypmg==", "cpu": [ "x64" ], @@ -731,9 +734,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.63.0.tgz", - "integrity": "sha512-tWtHBTu5gOPK4u4Urtk4qAHW3zZ9rQAmbssO8gp7ELvGTGI3aCiq6NqyTQ0PCIg7KbHJF2UkGDDs77YZGxfjCA==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.63.3.tgz", + "integrity": "sha512-GXFZRRoMAytaI5z6N3Zhfw0WL18Q0M8r95D5hlC4GqE/lGk8pbSJNUBoOWDfbm6dTciqHj2nU87tI5f6XhQiOg==", "cpu": [ "arm64" ], @@ -745,9 +748,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.63.0.tgz", - "integrity": "sha512-2qPoJiwTvtHQ27NnYvTnsgk8laXWYuVmNESG8WFZBcEPKLfZ3I27qBJarjVRQtwGeYyRfq5ZowHXih9lm2BItw==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.63.3.tgz", + "integrity": "sha512-77W+8X3ddYgPxUpB8nZFQs2Mq+wc4HVlcSRtApXLjYBcnPMkttrSnU8VwKQjeWYhMsITHFs5cWBQ8vz1Q+5RHQ==", "cpu": [ "x64" ], @@ -759,13 +762,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.63.0.tgz", - "integrity": "sha512-FQwsTRvLNuHoTdICABJQfbPUSEueISGmnpT06tXTMpfprf5NiKLSXKA0A+w45wJnCmZAnzgqBwbt6ARFuyOi5w==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.63.3.tgz", + "integrity": "sha512-FVkwK+iUC+mq+GipVK46rRVticfAPtvPUNlqlGXUDxdVk/UGjQiiiUVPUrEXdSpU2ufU0XxLGyTqDtBidDOVmg==", "cpu": [ "arm" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -773,13 +779,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.63.0.tgz", - "integrity": "sha512-BBVTXziw8mY1a4ZbWME9tZyfzqXCDPqaC7Z3heQ29p5dkvXzwL0NwelO8zLa8c3RBKvl3YTuSnBgsBhYBtwjIw==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.63.3.tgz", + "integrity": "sha512-+aGU1t3398yQOVj1Bz8o3e+KtswxAPvO+mtxtNdfXYMkXIHu7XhhkCD7/DEH9q8tF8uhDnMWvfpUKI8y1sZJsg==", "cpu": [ "arm" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -787,13 +796,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.63.0.tgz", - "integrity": "sha512-w2Iyy9+RqKwx3d9qWMKsJg0FfRBsY0/pXNv0mCQ3ueRvJI6+QAScfD4nrMlzFLs2HNVW6Ew+mtZfDl9b7Ew5/Q==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.63.3.tgz", + "integrity": "sha512-cR0kjpRXR2KJ2oQK8E2KTPtphs+b9hZ8IhTZubNryt/RsqgdOZBQ2Zq0q5UedtiIi0rs3jVhJh55RE1ZHUVGUA==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -801,13 +813,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.63.0.tgz", - "integrity": "sha512-YK++KtrFRHYE0P6/RtYEAy9t8F37znP+K03RrIuLPYOL6SVlObRumf/0OE4V/h63xL9DwkWbNssZfmA9hawuDA==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.63.3.tgz", + "integrity": "sha512-y1RYi4Q3/9ByVWSSt9kX2ustE0B7kFYbJ6zZdVZVyqopZs3yhCTwRfrjIX4vezUJInma/Gs6BOFDJg7yZmJ0IQ==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -815,13 +830,16 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.63.0.tgz", - "integrity": "sha512-aBfOG6fP7YkkPmTqPwufRJeFyz7WPpECv9XNbnsk9+vg7rxdih0lbtEel7jcRng4LZrrmU3FfitCFyEj4BWDWg==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.63.3.tgz", + "integrity": "sha512-DNhEA5viIj3Z5bZLE4z4oV8N5ozWqDwyt7T6KG7VdLDJ0nW+rNOYlphBl4/3HQkK75qipPLsVOfStHHOwN9WSg==", "cpu": [ "loong64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -829,13 +847,16 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.63.0.tgz", - "integrity": "sha512-LGaHEOeHNAag9VuS1Crs5DFg4RrU9MPi2nVnNJk9DTePx/B6RRYKVmrIXt2h7YOJlwjaFJ6lwtFDliZxScTLrQ==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.63.3.tgz", + "integrity": "sha512-17gQCqrIpXBX2Cmi9/TygnVOqGbzsba/iaqcYSL8FY7lNugg+7AiYNs5c5nKWD+NRQha36Sa0CqkJqH4XVHwnQ==", "cpu": [ "loong64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -843,13 +864,16 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.63.0.tgz", - "integrity": "sha512-jClvk+J0FC3b7Udvegiw5/4hErbHtmsNsQgENnKXDWtNCJXsJYZH5WURvu7imDOO38xYml24eeh5x3A04ppwCw==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.63.3.tgz", + "integrity": "sha512-6LwVnZRIyINpdku/yOcI8Tm9YqLmhHK5emmlOOnW9tO0SYEm1FmKPcsSAGp0NBlqR2P04xaND4jvN6sTHqhq8A==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -857,13 +881,16 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.63.0.tgz", - "integrity": "sha512-0OJlaGK+8+B777Ql5okIpD7ua5Ro9+VB9Ve0OKa28OQJZ1RbuUBVNHK/e3pr4BROqsyPl1JrPO1ZxJseCNffcA==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.63.3.tgz", + "integrity": "sha512-xMUqkTXlEUtI/p5AAukMwBRr1enU3efsTeF+bskeFfk8t1C9rcC8sLREcZXmTfAXEbvRdJVSonVJez3TMlbR3w==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -871,13 +898,16 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.63.0.tgz", - "integrity": "sha512-Ygsx+HoNH7afwi1bTIXbnTvVnsO+zurPLSYxybV1hHFVU72OWOCl6v05ql/z0hkpAPx+DK7Kn9Bi7MayCcjLTA==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.63.3.tgz", + "integrity": "sha512-S3E94co9F9WRRqEaUoQZ38K1gCz6KiM+nL7/3ijq7fDGF3OznjS5TasgYITlvl27GQKtu4lOAOsr5MFwkijvOA==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -885,13 +915,16 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.63.0.tgz", - "integrity": "sha512-pDQxtMGb+OvG3fLwR2OkZlSd47hW+kWg4BYMG/++sR6RqorQccwPTDsxda5hPwiIeIErAnCF9ma3SAU06bdQtQ==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.63.3.tgz", + "integrity": "sha512-1QtRDwG42x5BJI3s9mxu5rEjDnfbSnk20HQ9/ylTAYnSwYwxMVb+Vgu34wzzTQ7ogqBybebgQNUDAvZVQ38DbA==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -899,13 +932,16 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.63.0.tgz", - "integrity": "sha512-0BnUG9mS8I4SSHr3XsxVhuCMEiu+rX61xxZF5vujso4LaiAGFZFxvDjg6Xn6tLPNTUAfuCvQYas4LMQMVsKRSQ==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.63.3.tgz", + "integrity": "sha512-BQhejF6ZXOpxbngiNTP12GCGQeaDVL2QXGeBVViKIYzFHM5RKxTxwUMB1fr1BeNFphFMpnRqC5QSXFSa4z6UQw==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -913,13 +949,16 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.63.0.tgz", - "integrity": "sha512-Adu/VttB1dpPNW+FEacrZ+xVm9tFty84+RrFzsqlFaPxoJB+9XXyDGtp5dCOoBwGBIEVH0To7lExFXEx0BIF4A==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.63.3.tgz", + "integrity": "sha512-SXagRwnI2Wlwlitllu59UK/nGVbD1CKPcNqDplHwIC4BqJcpXFjD32d1R/RbuISa95HdQrZM3/7v4bKiowFaLA==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -927,13 +966,16 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.63.0.tgz", - "integrity": "sha512-NQ3bDvjUbFKmP23671xUlXtKmqVsUBd6M4PQCvbmNtOy06hnQIdKHy8oG/6S3R/S6He1JgPk6A5VT+prAJMYEw==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.63.3.tgz", + "integrity": "sha512-2IPozoEALRCziGqE8O9KMK60PMu5TS1huv4fwoeCexj+WjmcwFtX9CTOVbfXCUqcELAubEwRFPYlzb/WvwY2HQ==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -941,9 +983,9 @@ ] }, "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.63.0.tgz", - "integrity": "sha512-u2eDAl4+0aFvA13GxlGBtTI3SS3sdgwgtV0HyjZ0QaQVCgNE+jqNGey+GtxWiq+wxr/UycAx/OnfJzApCFamvA==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.63.3.tgz", + "integrity": "sha512-AoxqosUHT9IX54hFn2TiN6A7d6ZKTtE6pd2bqWtqkkNJ6HJGaU6FRouGX8L1O7R/ZwsnCnpQrHzb4pDEx+UHRQ==", "cpu": [ "x64" ], @@ -955,9 +997,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.63.0.tgz", - "integrity": "sha512-XvRb5vfW3wAZQ+ZUG21AnHHDKtNcw99eigzEhjr//NZ3u7SoBaPP0seSc7FgP7p1epAEdAoZckMW9WY/+4w70w==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.63.3.tgz", + "integrity": "sha512-d+CaftKgmkFBzCwezMqqy1d0QNNYugqLCMcYVQWBy5SS2YfeMP8Q8ripkgx9O8IyBXXLHrJ+aaCV4U96usv6Yg==", "cpu": [ "arm64" ], @@ -969,9 +1011,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.63.0.tgz", - "integrity": "sha512-iZPmniy4kNBf5yo2RezbkYNNK5HPbXE9+g+twnbqSng7dtLEJy1SKoxiE/ni4FDacjyuZpEeb9U054N4EoKHYw==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.63.3.tgz", + "integrity": "sha512-xXlDF6nR1eOuXbdDy5Hl5fmtY7teUDevF/k0O7IPoZe4Tpmdv+lgdE5JRsnhQtt37ql9P0VF2kAN9a0OCZdo+Q==", "cpu": [ "arm64" ], @@ -983,9 +1025,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.63.0.tgz", - "integrity": "sha512-mFBBd+LF37fnE8JnYUOH+imj0aPFPK30vpar4ehJkgnLj9sZn8ZxiRENmLtgIwxK7TC8klF6N57fxdNBwQoqOA==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.63.3.tgz", + "integrity": "sha512-YtXAgLN+JP7Ay6qG3eWhc7IHMQPzLc8r3uvhAvlJIoCz/4Q32+Bl9Fmnywidh8v1GOIMmymjovfqY9ETAtysvA==", "cpu": [ "ia32" ], @@ -997,9 +1039,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.63.0.tgz", - "integrity": "sha512-ujeqEY3B+zbGn3Z4Q03cUBG/LGWnBJncVT36WER31LcOsQk9+1dmINKKtvmmfChUvRbK1G0R8OhMWFgHgaZtAw==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.63.3.tgz", + "integrity": "sha512-WuWtSJRNo549vzcfZyEgfqb6zeSgn1F+UE5kQ+BCjzz0W4MGCjntUHkZVc1VRuAM7+ULaSyhiPxD1spyewFvkQ==", "cpu": [ "x64" ], @@ -1011,9 +1053,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.63.0.tgz", - "integrity": "sha512-hncn90N4sOky0L2LKE5oESKLbxCPeVo4eLA2LSMoDzM+879ml4WSr+Rr4DWknNIVVvS1Hirkc9hx02W6YxS8rQ==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.63.3.tgz", + "integrity": "sha512-+lIKX7O0+IGe7WuhATaAMMeT7B76vfhXH/l9wLQL+nvyhbw2ohYCKIdWL56JfDu75CWt5oKRP4QFH/jkMtBquA==", "cpu": [ "x64" ], @@ -1069,9 +1111,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.20.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.1.tgz", - "integrity": "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==", + "version": "22.20.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.3.tgz", + "integrity": "sha512-DZmzkmwHzXrLPAXPyKNDzlIwMMUZCVacoD25ywdy5YTKGbOx/2ld+Q38Im2zJ0vBuZP5Prd3VZutKZyXwkOS8A==", "dev": true, "license": "MIT", "dependencies": { @@ -1222,9 +1264,9 @@ } }, "node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -1330,6 +1372,22 @@ "url": "https://opencollective.com/express" } }, + "node_modules/body-parser/node_modules/iconv-lite": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -1337,9 +1395,9 @@ "license": "ISC" }, "node_modules/brace-expansion": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", - "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" @@ -1707,18 +1765,6 @@ "url": "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/fb55/encoding-sniffer?sponsor=1" } }, - "node_modules/encoding-sniffer/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", @@ -1932,9 +1978,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.7.tgz", - "integrity": "sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz", + "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==", "funding": [ { "type": "github", @@ -2084,9 +2130,9 @@ } }, "node_modules/hono": { - "version": "4.13.7", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.13.7.tgz", - "integrity": "sha512-c8/gF9ac8Y78/agExVocyLevgR+JlpNB444Py0FSX8pJoPdYUfUzRcXtYEYGwt6l19qIlVZPN5Mfsw9jFShmQQ==", + "version": "4.13.8", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.13.8.tgz", + "integrity": "sha512-/Gng7NfoykZl2pjukW5Z6+8Yxm3BPRf86GTbQnt0SbySkvax4fyL4H3HhY1cCpBGmiW9XDRFzRV+CXK2W8QudQ==", "license": "MIT", "engines": { "node": ">=16.9.0" @@ -2144,19 +2190,15 @@ } }, "node_modules/iconv-lite": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", - "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" } }, "node_modules/ignore": { @@ -2176,9 +2218,9 @@ "license": "ISC" }, "node_modules/ip-address": { - "version": "10.7.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.0.tgz", - "integrity": "sha512-BGFsyJd5mpXp3rK6jIdADLNgpJUK1jnjzvYF8lK+VyDab9JAmqN0YOKDdP17HlgKb2+ehPgDc8EtnRLbGCAMhA==", + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.2.tgz", + "integrity": "sha512-7H/2gFSIitxc0hG3nOI1glS8QLo/EHBFFLk8vEUjXY/xu0AdL8jZ9U1IzO2PUm0d2D/ofQcAifb0g6OBkt8U7w==", "license": "MIT", "engines": { "node": ">= 12" @@ -2215,9 +2257,9 @@ } }, "node_modules/js-yaml": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.4.1.tgz", - "integrity": "sha512-28R/k+NAjeuf7+CKlTxWZVExJGwVVLwY06DgEnOMz2gEpfNkDcD7QvyiVPT0xy0XXhU8vHsd4Ot42OOPdJG7dQ==", + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.4.2.tgz", + "integrity": "sha512-m+aqu+LwO1O6sIopafj8HUVl5aawITwZQe/yHpMCKjaWBaA/d07B/QdMb3529REftiU+RMMHL3Vlsw3hON7vWg==", "funding": [ { "type": "github", @@ -2369,9 +2411,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.18", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", - "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", + "version": "3.3.19", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.19.tgz", + "integrity": "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==", "dev": true, "funding": [ { @@ -2581,9 +2623,9 @@ } }, "node_modules/postcss": { - "version": "8.5.26", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", - "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", + "version": "8.5.28", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", + "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", "dev": true, "funding": [ { @@ -2601,7 +2643,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.17", + "nanoid": "^3.3.18", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -2610,9 +2652,9 @@ } }, "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.8.tgz", + "integrity": "sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ==", "license": "MIT", "dependencies": { "forwarded": "0.2.0", @@ -2620,6 +2662,10 @@ }, "engines": { "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/qs": { @@ -2666,6 +2712,22 @@ "node": ">= 0.10" } }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/re2js": { "version": "2.8.6", "resolved": "https://registry.npmjs.org/re2js/-/re2js-2.8.6.tgz", @@ -2685,9 +2747,9 @@ } }, "node_modules/rollup": { - "version": "4.63.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.63.0.tgz", - "integrity": "sha512-T5vnZ2y4QqC3/4P+w2+JO+Q/OVdnPsv4XcSYJYMEn0R9/jjl5AgLwO9LAZMzP2lN71O6pypn91rB7lDstUkfrQ==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.63.3.tgz", + "integrity": "sha512-1i2XreiAoMMXuPGD6Msj2xWrMMkHojNRKivInxGQcg7/1KuPuYlfUutLyh4drnOxUTHX9cHI4wFoat8D/NKaBw==", "dev": true, "license": "MIT", "dependencies": { @@ -2702,31 +2764,31 @@ }, "optionalDependencies": { "@napi-rs/lzma-linux-x64-gnu": "1.5.1", - "@rollup/rollup-android-arm-eabi": "4.63.0", - "@rollup/rollup-android-arm64": "4.63.0", - "@rollup/rollup-darwin-arm64": "4.63.0", - "@rollup/rollup-darwin-x64": "4.63.0", - "@rollup/rollup-freebsd-arm64": "4.63.0", - "@rollup/rollup-freebsd-x64": "4.63.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.63.0", - "@rollup/rollup-linux-arm-musleabihf": "4.63.0", - "@rollup/rollup-linux-arm64-gnu": "4.63.0", - "@rollup/rollup-linux-arm64-musl": "4.63.0", - "@rollup/rollup-linux-loong64-gnu": "4.63.0", - "@rollup/rollup-linux-loong64-musl": "4.63.0", - "@rollup/rollup-linux-ppc64-gnu": "4.63.0", - "@rollup/rollup-linux-ppc64-musl": "4.63.0", - "@rollup/rollup-linux-riscv64-gnu": "4.63.0", - "@rollup/rollup-linux-riscv64-musl": "4.63.0", - "@rollup/rollup-linux-s390x-gnu": "4.63.0", - "@rollup/rollup-linux-x64-gnu": "4.63.0", - "@rollup/rollup-linux-x64-musl": "4.63.0", - "@rollup/rollup-openbsd-x64": "4.63.0", - "@rollup/rollup-openharmony-arm64": "4.63.0", - "@rollup/rollup-win32-arm64-msvc": "4.63.0", - "@rollup/rollup-win32-ia32-msvc": "4.63.0", - "@rollup/rollup-win32-x64-gnu": "4.63.0", - "@rollup/rollup-win32-x64-msvc": "4.63.0", + "@rollup/rollup-android-arm-eabi": "4.63.3", + "@rollup/rollup-android-arm64": "4.63.3", + "@rollup/rollup-darwin-arm64": "4.63.3", + "@rollup/rollup-darwin-x64": "4.63.3", + "@rollup/rollup-freebsd-arm64": "4.63.3", + "@rollup/rollup-freebsd-x64": "4.63.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.63.3", + "@rollup/rollup-linux-arm-musleabihf": "4.63.3", + "@rollup/rollup-linux-arm64-gnu": "4.63.3", + "@rollup/rollup-linux-arm64-musl": "4.63.3", + "@rollup/rollup-linux-loong64-gnu": "4.63.3", + "@rollup/rollup-linux-loong64-musl": "4.63.3", + "@rollup/rollup-linux-ppc64-gnu": "4.63.3", + "@rollup/rollup-linux-ppc64-musl": "4.63.3", + "@rollup/rollup-linux-riscv64-gnu": "4.63.3", + "@rollup/rollup-linux-riscv64-musl": "4.63.3", + "@rollup/rollup-linux-s390x-gnu": "4.63.3", + "@rollup/rollup-linux-x64-gnu": "4.63.3", + "@rollup/rollup-linux-x64-musl": "4.63.3", + "@rollup/rollup-openbsd-x64": "4.63.3", + "@rollup/rollup-openharmony-arm64": "4.63.3", + "@rollup/rollup-win32-arm64-msvc": "4.63.3", + "@rollup/rollup-win32-ia32-msvc": "4.63.3", + "@rollup/rollup-win32-x64-gnu": "4.63.3", + "@rollup/rollup-win32-x64-msvc": "4.63.3", "fsevents": "~2.3.2" } }, @@ -3253,18 +3315,6 @@ "node": ">=18" } }, - "node_modules/whatwg-encoding/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/whatwg-mimetype": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", @@ -3323,9 +3373,9 @@ } }, "node_modules/yaml": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", - "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz", + "integrity": "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==", "license": "ISC", "bin": { "yaml": "bin.mjs" @@ -3338,9 +3388,9 @@ } }, "node_modules/zod": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.2.tgz", - "integrity": "sha512-lh5RCAGFa1Cm2hjtNwLQhSs/AsqdWnTQaBER9fEwN/88pSh7KOtJavtBx/0VlkN/uFd61SwYmljLMDAsHlvzBQ==", + "version": "4.6.5", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.5.tgz", + "integrity": "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==", "license": "MIT", "funding": { "url": "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/sponsors/colinhacks" diff --git a/packages/sdk/src/authored-flow-error.ts b/packages/sdk/src/authored-flow-error.ts index e372f24a5..e0fd7acf4 100644 --- a/packages/sdk/src/authored-flow-error.ts +++ b/packages/sdk/src/authored-flow-error.ts @@ -1,3 +1,4 @@ +import type { CheckFailureKind } from './failure-kinds.js'; import type { CompletionReason as ProtocolCompletionReason, RunCompletionReason as ProtocolRunCompletionReason, @@ -36,6 +37,15 @@ export class AuthoredFlowExecutionError extends Error { message: string, readonly completionReason?: ProtocolCompletionReason | ProtocolRunCompletionReason, readonly runId?: string, + /** + * Set only for `code === 'agent_cli_unresolved'`: the specific preflight + * refusal kind (`cli_missing`, `model_unknown`, `model_unavailable`, ...) + * `checkAuthoredFlow` actually produced, so a caller can distinguish a bad + * model declaration from a missing CLI the same way the declarative + * `flows check` path's `report.diagnostics[].kind` already lets it — + * collapsing every refusal to one generic code would lose that taxonomy. + */ + readonly refusalKind?: CheckFailureKind, ) { super(`${code}: ${message}`); this.name = 'AuthoredFlowExecutionError'; diff --git a/packages/sdk/src/authored-flow-executor.ts b/packages/sdk/src/authored-flow-executor.ts index 004564ab9..2c84ff0ad 100644 --- a/packages/sdk/src/authored-flow-executor.ts +++ b/packages/sdk/src/authored-flow-executor.ts @@ -29,6 +29,7 @@ import { observeStep, type ProgressEvent } from './progress.js'; import { parseStepTimeout } from './compile.js'; import { getAuthoredFlowDefinition } from './authored-flow.js'; import type { GetFlowDefinition } from './authored-flow-loader.js'; +import { createAuthoredFlowChecker } from './cli/check.js'; import type { RunLifecycleOptions } from './cli/run.js'; import { AuthoredFlowExecutionError, @@ -168,7 +169,7 @@ export async function executeAuthoredFlow( ...(options.onWait !== undefined ? { onWait: options.onWait } : {}), }; const definition = getDefinition(handle); - const headerFields = Object.keys(definition.header).filter(key => key !== 'tools' && key !== 'budget' && key !== 'memory'); + const headerFields = Object.keys(definition.header).filter(key => key !== 'tools' && key !== 'budget' && key !== 'memory' && key !== 'agents'); if (definition.header.tools && Object.keys(definition.header.tools).some(key => !['mcp', ...helperProviders.map(p => p.namespace)].includes(key))) headerFields.push('tools'); if (definition.header.tools?.relayfile !== undefined) headerFields.push('tools.relayfile'); const helperPreflight = checkSlackHelpers(definition); @@ -182,6 +183,15 @@ export async function executeAuthoredFlow( `flow "${definition.name}" uses unsupported header fields: ${headerFields.join(', ')}`, ); } + const checker = createAuthoredFlowChecker(flowPath); + if (definition.header.agents !== undefined && Object.keys(definition.header.agents).length > 0) { + const report = checker.declarations(definition.header.agents); + const refusal = report.diagnostics.find(diagnostic => diagnostic.severity === 'refusal'); + if (refusal !== undefined) { + throw new AuthoredFlowExecutionError('agent_cli_unresolved', refusal.message, + undefined, undefined, refusal.kind); + } + } const checkedMcp = await checkMcpHeader(definition, flowPath); if (!checkedMcp.report.ok) throw new McpPreflightError(checkedMcp.report); @@ -208,7 +218,7 @@ export async function executeAuthoredFlow( const worker = authoredWorkerRunner( definition, journal, flowPath, journalSteps, waitOptions, - localAgentStream, budget, definition.header.budget, options.rootRunId, + localAgentStream, budget, definition.header.budget, options.rootRunId, checker, ); function llmOperation(strings: TemplateStringsArray, ...values: unknown[]): Step; @@ -307,14 +317,13 @@ export async function executeAuthoredFlow( llm: llmOperation, agent(name, options) { assertOperationAllowed('agent', definition.name, requestedCompletion); - void name; // Authored headers do not yet declare reusable named agents. const id = `agent-${nextStep++}`; let agentOp!: AuthoredFlowOperation; agentOp = new AuthoredFlowOperation( id, 'agent', () => assertOperationAllowed('agent', definition.name, requestedCompletion), - () => observeStep(id, 'agent', () => worker.agent(id, options, agentOp.namedGate), onProgress), + () => observeStep(id, 'agent', () => worker.agent(id, name, options, agentOp.namedGate), onProgress), lifecycle, ); return trackStep(authoredSteps, agentOp); diff --git a/packages/sdk/src/authored-step-output.ts b/packages/sdk/src/authored-step-output.ts index 3174d40bc..39f2d288b 100644 --- a/packages/sdk/src/authored-step-output.ts +++ b/packages/sdk/src/authored-step-output.ts @@ -18,6 +18,12 @@ import type { AuthoredFlowJournalStep } from './authored-flow-executor.js'; * terminal state. Given that, a single read from the start of this run's * (small, single-step) journal is enough — no polling here, and no run * outcome ever needs re-checking. + * + * `journalSteps` records observed journal facts, not successful output + * conversions: a valid completion is appended before checking its reason or + * decoding output. A later refusal must not erase that fact. Missing or + * malformed completion entries append nothing. Execution propagates errors + * and does not return a successful result or automatically retry this read. */ export async function readCompletedStepOutput( journal: JournalClient, diff --git a/packages/sdk/src/authored-worker-step.ts b/packages/sdk/src/authored-worker-step.ts index cb7e670f4..2d717dc28 100644 --- a/packages/sdk/src/authored-worker-step.ts +++ b/packages/sdk/src/authored-worker-step.ts @@ -2,7 +2,7 @@ import type { AuthoredBudget } from './authored-budget.js'; import { parseBudget } from './budget.js'; import type { AgentOptions, AgentResult, LlmOptions, NamedGate } from '@relayflows/surface'; import { compileSpec, toKernelSpec } from './compile.js'; -import { checkAuthoredFlow } from './cli/check.js'; +import { checkAuthoredFlow, type AuthoredFlowChecker } from './cli/check.js'; import { classifyOutcome, type RunLifecycleOptions } from './cli/run.js'; import type { PreflightDiagnostic } from './preflight.js'; import { AuthoredFlowExecutionError } from './authored-flow-error.js'; @@ -17,20 +17,28 @@ const WORKSPACE_PERMISSION_ANNOTATION = /:\s*(readonly|readwrite)\s*$/i; /** Agent and LLM calls share the exact declarative preflight and lease wait. */ export function authoredWorkerRunner( - definition: { name: string }, journal: JournalClient, flowPath: string, + definition: { name: string; header?: { agents?: Readonly>> } }, journal: JournalClient, flowPath: string, journalSteps: AuthoredFlowJournalStep[], waitOptions: RunLifecycleOptions, localAgentStream?: string, budget?: AuthoredBudget, headerBudget?: unknown, - rootRunId?: string, + rootRunId?: string, checker?: AuthoredFlowChecker, ) { - async function run(step: StepSpec): Promise { + async function run(step: StepSpec, agents?: FlowSpec['agents']): Promise { const id = step.id; - const authoring: FlowSpec = { version: SPEC_SCHEMA_VERSION, name: `${definition.name}/${id}`, steps: [step], ...(headerBudget === undefined ? {} : { budget: parseBudget(headerBudget) }) }; + const authoring: FlowSpec = { + version: SPEC_SCHEMA_VERSION, + name: `${definition.name}/${id}`, + steps: [step], + ...(agents === undefined ? {} : { agents }), + ...(headerBudget === undefined ? {} : { budget: parseBudget(headerBudget) }), + }; // The kernel never resolves a `cli` on its own — every declarative // `flows run`/`flows check` binds it first via this exact function // (cli/check.ts), searching for the nearest flows.json from `flowPath` // and real-probing auth/model readiness. An authored agent step gets // nothing for free just because it was declared in TS instead of YAML. - const { report, flow: resolved } = checkAuthoredFlow(authoring, flowPath); + const { report, flow: resolved } = checker === undefined + ? checkAuthoredFlow(authoring, flowPath) + : checker.check(authoring); if (!report.ok || resolved === undefined) { const refusal = report.diagnostics.find( (diagnostic): diagnostic is PreflightDiagnostic & { severity: 'refusal' } => @@ -42,6 +50,9 @@ export function authoredWorkerRunner( refusal?.message ?? `flow "${definition.name}" step "${id}": no CLI could be resolved for f.${step.type} ` + `(searched for flows.json from "${flowPath}")`, + undefined, + undefined, + refusal?.kind, ); } const spec = toKernelSpec(resolved); @@ -56,11 +67,14 @@ export function authoredWorkerRunner( // (renewing as the lease renews, per docs/SURFACE.md §5's WAITING // [worker_lease] contract), never an unrelated fixed deadline. const execution = await classifyOutcome(journal, 'run', outcome, report, '', waitOptions); + // Preflight refusals happen before runStart. Runtime classification returns + // success, failure, or parked; retain the causal diagnostic rather than a + // later warning if that classifier's contract expands. if (execution.exitCode === 3) { const parked = execution.report.parkedStep; throw new AuthoredFlowExecutionError( step.type === 'llm' ? 'llm_parked' : 'agent_parked', - execution.report.diagnostics.at(-1)?.message + [...execution.report.diagnostics].reverse().find(diagnostic => diagnostic.severity === 'parked')?.message ?? `flow "${definition.name}" step "${id}" parked` + (parked !== undefined ? ` (${parked.type})` : '') + ': no worker is attached to run it.', @@ -72,7 +86,9 @@ export function authoredWorkerRunner( const reason = execution.report.completionReason; throw new AuthoredFlowExecutionError( 'step_failed', - execution.report.diagnostics.at(-1)?.message + [...execution.report.diagnostics].reverse().find( + diagnostic => diagnostic.severity === 'failure' || diagnostic.severity === 'refusal', + )?.message ?? `flow "${definition.name}" step "${id}" did not complete successfully ` + `(status: ${execution.report.status ?? 'unknown'})`, isSurfaceCompletionReason(reason) ? reason : undefined, @@ -88,7 +104,7 @@ export function authoredWorkerRunner( } return { - async agent(id: string, options: AgentOptions, verification?: NamedGate): Promise { + async agent(id: string, name: string, options: AgentOptions, verification?: NamedGate): Promise { if (options.workspace !== undefined && localAgentStream !== undefined) { throw new AuthoredFlowExecutionError('unsupported_workspace_permission', 'The local agent worker accepts stream-only steps. Remove workspace or attach a worker that holds its revision pins.'); @@ -129,16 +145,21 @@ export function authoredWorkerRunner( `f.agent options.transport must be 'direct' or 'relay' (got ${JSON.stringify(options.transport)}).`, ); } + const namedAgents = definition.header?.agents; + const matchesNamedAgent = namedAgents !== undefined + && Object.hasOwn(namedAgents, name) + && namedAgents[name] !== undefined; const output = await run({ id, type: 'agent', instruction: options.task, ...(localAgentStream === undefined ? {} : { surfaces: { streams: [{ stream: localAgentStream }] } }), + ...(matchesNamedAgent ? { agent: name } : {}), ...(options.workspace === undefined ? {} : { surfaces: { workspace: [{ surface: options.workspace }] } }), ...(options.cli === undefined ? {} : { cli: options.cli }), ...(options.model === undefined ? {} : { model: options.model }), ...(options.cwd === undefined ? {} : { cwd: options.cwd }), ...(options.transport === undefined ? {} : { transport: options.transport }), ...(verification === undefined ? {} : { verification }), - }); + }, namedAgents === undefined ? undefined : { ...namedAgents }); if (typeof output !== 'object' || output === null || Array.isArray(output)) { throw new AuthoredFlowExecutionError('journal_protocol_violation', `step "${id}" produced a non-object output`); } diff --git a/packages/sdk/src/cli/check-typescript.ts b/packages/sdk/src/cli/check-typescript.ts index cf35cba3d..caebd8452 100644 --- a/packages/sdk/src/cli/check-typescript.ts +++ b/packages/sdk/src/cli/check-typescript.ts @@ -28,7 +28,7 @@ export async function checkMcpHeader( path: string, ): Promise { const empty = { servers: Object.freeze({}), inventory: Object.freeze({}) }; - const KNOWN_HEADER_FIELDS = new Set(['tools', 'budget', 'identity', 'memory', 'workspace', 'use']); + const KNOWN_HEADER_FIELDS = new Set(['tools', 'budget', 'identity', 'memory', 'workspace', 'use', 'agents']); const header = definition.header ?? {}; const unsupported = Object.keys(header).filter(key => !KNOWN_HEADER_FIELDS.has(key)); if (header.tools?.relayfile !== undefined) unsupported.push('tools.relayfile'); diff --git a/packages/sdk/src/cli/check.ts b/packages/sdk/src/cli/check.ts index d6a2bf2c5..54febe0d0 100644 --- a/packages/sdk/src/cli/check.ts +++ b/packages/sdk/src/cli/check.ts @@ -14,7 +14,7 @@ import { } from '../cli-adapter.js'; import { MODEL_ENV } from '../worker-cli.js'; import { modelNameError } from '../model-name.js'; -import type { FlowSpec } from '../spec.js'; +import type { FlowSpec, NamedAgentSpec } from '../spec.js'; import type { McpServerConfig } from '../spec.js'; import { parseMcpConfig } from '../mcp-config.js'; import type { StepGateInspection } from '../gate-contract.js'; @@ -26,6 +26,8 @@ import { type CliProbeResult, type PreflightDiagnostic, type PreflightProbes, + type PreflightOptions, + type PreflightResult, } from '../preflight.js'; export interface ProjectConfig { @@ -104,43 +106,84 @@ export function checkFlow(path: string): CheckExecution { /** Preflight a validated authored flow through the same path as YAML/JSON. */ export function checkAuthoredFlow(authoring: FlowSpec, path: string, projectConfig?: ProjectConfig): CheckExecution { - const absolutePath = resolve(path); + return createAuthoredFlowChecker(path, projectConfig).check(authoring); +} + +export interface AuthoredFlowChecker { + check(authoring: FlowSpec): CheckExecution; + declarations(agents: Readonly>>): CheckReport; +} + +/** One execution snapshots project policy and shares successful readiness probes. */ +export function createAuthoredFlowChecker(path: string, projectConfig?: ProjectConfig): AuthoredFlowChecker { + const directory = dirname(resolve(path)); + let config: ProjectConfig; + let options: PreflightOptions; try { - const config = projectConfig ?? readProjectConfig(dirname(absolutePath)); - const probes = systemProbes(dirname(absolutePath), config); - const result = preflight(authoring, { - projectCli: config.cli, - projectConfigPath: config.path, - projectSearchStart: dirname(absolutePath), - models: config.models, - ...(config.path !== undefined ? { modelRegistryPath: config.path } : {}), - probes, - }); - const flow = result.ok - ? bindResolvedCliPaths( - compileSpec(authoring), - result.resolutions, - dirname(absolutePath), - config.directory, - ) - : undefined; - return { - report: { - ok: result.ok, - path, - ...(config.path !== undefined ? { projectConfigPath: config.path } : {}), - gates: result.gates, - resolutions: result.resolutions, - diagnostics: result.diagnostics, + config = projectConfig ?? readProjectConfig(directory); + const probes = systemProbes(directory, config); + // This cache spans checks within one authored execution. Preflight's + // inner cache only deduplicates probes within a single spec check. + // Refusals are never reused: an override must collect its own failure. + const ready = new Map(); + options = { + projectCli: config.cli, projectConfigPath: config.path, + projectSearchStart: directory, models: config.models, + ...(config.path === undefined ? {} : { modelRegistryPath: config.path }), + probes: { + ...probes, + cli(cli, source, model) { + const base = source === 'project' ? config.directory : directory; + const key = JSON.stringify([canonicalCli(cli, base), base, model ?? null]); + const cached = ready.get(key); + if (cached !== undefined) return cached; + const result = probes.cli(cli, source, model); + if (result.exists && result.authenticated && result.supported !== false + && (model === undefined || result.modelAvailable === true)) ready.set(key, result); + return result; + }, }, - ...(flow !== undefined ? { flow } : {}), }; } catch (error) { - const failure = error instanceof CheckFailure - ? error + const failure = error instanceof CheckFailure ? error : new CheckFailure('invalid_spec', `Flow "${path}" could not be checked as a Relayflow spec.`); - return { report: inputFailureReport(failure, path) }; + const report = inputFailureReport(failure, path); + return { check: (_authoring: FlowSpec): CheckExecution => ({ report }), + declarations: (_agents: Readonly>>) => report }; } + const reportFor = (result: PreflightResult): CheckReport => ({ + ...result, path, + ...(config.path === undefined ? {} : { projectConfigPath: config.path }), + }); + const check = (authoring: FlowSpec): CheckExecution => { + try { + const result = preflight(authoring, options) as PreflightResult; + const flow = result.ok + ? bindResolvedCliPaths(compileSpec(authoring), result.resolutions, directory, config.directory) + : undefined; + return { report: reportFor(result), ...(flow === undefined ? {} : { flow }) }; + } catch (error) { + const failure = error instanceof CheckFailure ? error + : new CheckFailure('invalid_spec', `Flow "${path}" could not be checked as a Relayflow spec.`); + return { report: inputFailureReport(failure, path) }; + } + }; + return { + declarations(agents: Readonly>>): CheckReport { + return check({ + version: '0.1.0', + name: '__authored_declarations__', + agents: { ...agents }, + steps: Object.keys(agents).map((agent) => ({ + id: `declaration-${agent}`, + type: 'agent' as const, + agent, + instruction: 'declaration preflight', + })), + }).report; + }, + check, + }; } /** diff --git a/packages/sdk/src/cli/direct-run.ts b/packages/sdk/src/cli/direct-run.ts index f1cfccf98..1a96bbac4 100644 --- a/packages/sdk/src/cli/direct-run.ts +++ b/packages/sdk/src/cli/direct-run.ts @@ -136,6 +136,17 @@ export async function runDirectFlow( || error.code === 'agent_cli_unresolved' || error.code === 'llm_cli_unresolved' || error.code === 'unsupported_workspace_permission'))) { + // `agent_cli_unresolved` carries the real preflight refusal kind + // (cli_missing, model_unknown, model_unavailable, ...) it was wrapped + // from, so a caller inspecting `report.diagnostics[].kind` gets the + // same closed taxonomy `flows check` gives the declarative dialect + // instead of every refusal collapsing into one generic 'invalid_spec'. + // `unsupported_header`/`unsupported_workspace_permission` have no + // preflight-diagnostic counterpart — they're authoring-level refusals + // intrinsic to the TS executor — so 'invalid_spec' remains accurate for those. + const kind = error instanceof AuthoredFlowExecutionError + ? error.refusalKind ?? 'invalid_spec' + : 'invalid_spec'; return { exitCode: 2, report: { @@ -144,7 +155,7 @@ export async function runDirectFlow( error.code === 'helper_slack.credential_missing' || error.code === 'helper_slack.mount_required' || error.code === 'budget_syntax_invalid' - ) ? error.code : 'invalid_spec', + ) ? error.code : kind, message: error.message, }, path)), socketPath, diff --git a/packages/sdk/src/preflight-probe-error.ts b/packages/sdk/src/preflight-probe-error.ts new file mode 100644 index 000000000..04a39b8c9 --- /dev/null +++ b/packages/sdk/src/preflight-probe-error.ts @@ -0,0 +1,7 @@ +import type { CliProbeFailureDetail } from './preflight.js'; + +export class CliProbeError extends Error { + constructor(readonly detail: CliProbeFailureDetail) { + super('CLI probe failed'); + } +} diff --git a/packages/sdk/src/preflight.ts b/packages/sdk/src/preflight.ts index 1b4ecb0d8..f3a0cb879 100644 --- a/packages/sdk/src/preflight.ts +++ b/packages/sdk/src/preflight.ts @@ -22,6 +22,7 @@ export type CliResolutionSource = 'step' | 'named' | 'flow' | 'project'; export interface CliResolution { stepId: string; cli: string; + /** Relative CLI base: project config for project; flow file for all others. */ source: CliResolutionSource; /** Effective model probed with the CLI. */ model?: string; @@ -45,11 +46,8 @@ export type CliProbeFailureDetail = | `timeout:${number}ms` | `signal:${string}`; -export class CliProbeError extends Error { - constructor(readonly detail: CliProbeFailureDetail) { - super('CLI probe failed'); - } -} +import { CliProbeError } from './preflight-probe-error.js'; +export { CliProbeError } from './preflight-probe-error.js'; type CliProbeOutcome = | { result: CliProbeResult } @@ -84,6 +82,11 @@ export interface PreflightOptions { projectSearchStart?: string; /** Exact, project-owned model allowlist from the nearest flows.json. */ models?: readonly string[]; + /** + * Presence enables named-agent allowlist enforcement; an absent/empty models + * list then allows no models. Absence means no project policy for named + * declarations. Explicit step models still require allowlist membership. + */ modelRegistryPath?: string; probes: PreflightProbes; } @@ -525,7 +528,6 @@ function probeFailedMessage( } return `${prefix}: the probe timed out after ${detail.slice('timeout:'.length)}.`; } - function probeTrigger( trigger: TriggerSpec, probes: PreflightProbes, diff --git a/packages/sdk/tests/authored-agent-failure.test.ts b/packages/sdk/tests/authored-agent-failure.test.ts new file mode 100644 index 000000000..52ba3d60e --- /dev/null +++ b/packages/sdk/tests/authored-agent-failure.test.ts @@ -0,0 +1,82 @@ +import { flow } from '@relayflows/surface'; +import { afterEach, describe, expect, it, vi } from 'vitest'; +import { getAuthoredFlowDefinition } from '../src/authored-flow.js'; +import type { AuthoredFlowJournalStep } from '../src/authored-flow-executor.js'; +import { authoredWorkerRunner } from '../src/authored-worker-step.js'; +import { classifyOutcome, type RunExecution } from '../src/cli/run.js'; +import { JournalClient } from '../src/journal-client.js'; +import { preflight } from '../src/preflight.js'; + +vi.mock('../src/cli/run.js', () => ({ classifyOutcome: vi.fn() })); +afterEach(() => vi.restoreAllMocks()); + +describe('authored agent failure contracts', () => { + const report = { ok: true, gates: [], resolutions: [], diagnostics: [] }; + + it.each([1, 3] as const)('preserves exit %s diagnostics despite a trailing warning', async exitCode => { + const journal = new JournalClient('/unused'); + vi.spyOn(journal, 'runStart').mockResolvedValue({ run_id: 'run-1' } as never); + const read = vi.spyOn(journal, 'journalRead'); + const journalSteps: AuthoredFlowJournalStep[] = []; + const diagnostics: RunExecution['report']['diagnostics'] = [ + { severity: exitCode === 3 ? 'parked' : 'failure', kind: 'protocol_error', message: 'actual cause' }, + { severity: 'warning', kind: 'unprovable_effects', message: 'unrelated warning' }, + ]; + const lower = authoredWorkerRunner( + getAuthoredFlowDefinition(flow('failure', {}, async f => f.done('success'))), + journal, '/flow.ts', journalSteps, {}, undefined, undefined, undefined, undefined, + { + declarations: () => report, + check: authored => ({ report, flow: { ...authored, cli: 'wrapper' } }), + }, + ); + for (const entries of [diagnostics, []]) { + vi.mocked(classifyOutcome).mockResolvedValue({ + exitCode, report: { ...report, ok: false, command: 'run', diagnostics: entries }, + }); + await expect(lower.agent('step-1', 'label', { task: 'Work.' })).rejects.toMatchObject({ + code: exitCode === 3 ? 'agent_parked' : 'step_failed', + runId: 'run-1', + message: expect.stringContaining(entries.length ? 'actual cause' : 'flow "failure" step "step-1"'), + }); + } + expect(read).not.toHaveBeenCalled(); + expect(journalSteps).toEqual([]); + }); + + it('retains a real completion when output decoding refuses, but never invents a missing completion', async () => { + const journal = new JournalClient('/unused'); + const journalSteps: AuthoredFlowJournalStep[] = []; + vi.spyOn(journal, 'runStart').mockResolvedValue({ run_id: 'run-1' } as never); + vi.spyOn(journal, 'journalRead').mockResolvedValueOnce({ entries: [{ + entry_type: 'step.completed', step_id: 'step-1', + payload: { completionReason: 'success', output: null }, + }] } as never).mockResolvedValueOnce({ entries: [] } as never); + vi.mocked(classifyOutcome).mockResolvedValue({ + exitCode: 0, report: { ...report, command: 'run', diagnostics: [] }, + }); + const lower = authoredWorkerRunner( + getAuthoredFlowDefinition(flow('failure', {}, async f => f.done('success'))), + journal, '/flow.ts', journalSteps, {}, undefined, undefined, undefined, undefined, + { declarations: () => report, check: authored => ({ report, flow: { ...authored, cli: 'wrapper' } }) }, + ); + await expect(lower.agent('step-1', 'label', { task: 'Work.' })) + .rejects.toMatchObject({ code: 'journal_protocol_violation' }); + expect(journalSteps).toEqual([{ id: 'step-1', runId: 'run-1', completionReason: 'success' }]); + await expect(lower.agent('step-2', 'label', { task: 'Work.' })) + .rejects.toMatchObject({ code: 'journal_protocol_violation' }); + expect(journalSteps).toHaveLength(1); + }); + + it.each([undefined, []])('refuses a present registry with models=%j before any CLI probe', models => { + const cli = vi.fn(); + const result = preflight({ + version: '0.1.0', name: 'declarations', + agents: { reviewer: { cli: 'wrapper', model: 'model-a' } }, + steps: [{ id: 'review', type: 'agent', agent: 'reviewer', instruction: 'Review.' }], + }, { modelRegistryPath: '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/project/flows.json', models, + probes: { cli, executor: () => true, command: () => true } }); + expect(result).toMatchObject({ ok: false, diagnostics: [{ kind: 'model_unknown', agent: 'reviewer' }] }); + expect(cli).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/sdk/tests/authored-named-agents.test.ts b/packages/sdk/tests/authored-named-agents.test.ts new file mode 100644 index 000000000..ff0370994 --- /dev/null +++ b/packages/sdk/tests/authored-named-agents.test.ts @@ -0,0 +1,209 @@ +import { chmodSync, existsSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import { flow } from '@relayflows/surface'; +import { afterEach, describe, expect, it } from 'vitest'; +import { executeAuthoredFlow } from '../src/authored-flow-executor.js'; +import { JournalClient } from '../src/journal-client.js'; + +describe('named agent declarations', () => { + const namedAgentDirectories: string[] = []; + + afterEach(() => { + for (const directory of namedAgentDirectories.splice(0)) { + rmSync(directory, { recursive: true, force: true }); + } + }); + + function namedAgentFixture(models: string[]): { directory: string; flowPath: string } { + const directory = mkdtempSync(join(tmpdir(), 'authored-named-agent-')); + namedAgentDirectories.push(directory); + writeFileSync(join(directory, 'flows.json'), JSON.stringify({ models })); + return { directory, flowPath: join(directory, 'flow.ts') }; + } + + /** + * A minimal custom-wrapper CLI that answers only the identification/auth + * handshake (cli-adapter.ts) — enough for `declaration preflight` (the + * upfront header check) and `checkAuthoredFlow`'s own probing to resolve + * it as real and ready, without needing an execute-request handler: + * every test using this stub expects to refuse before ever dispatching. + */ + function authStubCli(directory: string, label: string): string { + const cli = join(directory, `${label}-cli`); + writeFileSync(cli, `#!/usr/bin/env node +if (process.argv[2] === 'auth' && process.argv[3] === 'status') process.exit(0); +if (process.argv[2] !== '--relayflows-adapter-v1') process.exit(9); +process.stdout.write('relayflows-agent-cli-v1\\n'); +`); + chmodSync(cli, 0o755); + return cli; + } + + it('rejects malformed declared models before probing or entering the body', async () => { + const { directory, flowPath } = namedAgentFixture([]); + rmSync(join(directory, 'flows.json')); // No allowlist refusal can mask an early probe. + const marker = join(directory, 'effect.txt'); + const cli = join(directory, 'must-not-probe'); + writeFileSync(cli, `#!/usr/bin/env node +require('node:fs').writeFileSync(${JSON.stringify(marker)}, 'probe'); +`); + chmodSync(cli, 0o755); + const handle = flow('invalid-declaration-model', { + agents: { reviewer: { cli, model: 'invalid\u0000model' } }, + }, async f => { + writeFileSync(marker, 'body'); + f.done('success'); + }); + await expect(executeAuthoredFlow(handle, new JournalClient('/journal-must-not-be-contacted'), + undefined, { flowPath })).rejects.toMatchObject({ + code: 'agent_cli_unresolved', refusalKind: 'invalid_spec', + message: expect.stringContaining('must not contain control characters'), + }); + expect(existsSync(marker)).toBe(false); + }); + + it('resolves a named declaration\'s CLI, refusing before contacting the journal if it is missing', async () => { + const { directory, flowPath } = namedAgentFixture(['known-model']); + const disconnectedJournal = new JournalClient('/journal-must-not-be-contacted'); + const absentCli = join(directory, 'nonexistent-cli'); + + await expect(executeAuthoredFlow(flow( + 'named-agent-cli-missing', + { agents: { reviewer: { cli: absentCli, model: 'known-model' } } }, + async (f) => { + await f.agent('reviewer', { task: 'review this' }); + f.done('success'); + }, + ), disconnectedJournal, undefined, { flowPath })).rejects.toMatchObject({ + code: 'agent_cli_unresolved', + // The specific preflight refusal kind must survive the wrap into one + // AuthoredFlowExecutionError code, or a caller (direct-run.ts's CLI + // report) can't tell a missing CLI apart from a bad model. + refusalKind: 'cli_missing', + message: expect.stringContaining( + `declares CLI "${absentCli}", but it does not resolve as an executable`, + ), + }); + }); + + it('lets a step-level cli override win over the named declaration', async () => { + const { directory, flowPath } = namedAgentFixture(['known-model']); + const disconnectedJournal = new JournalClient('/journal-must-not-be-contacted'); + // The named declaration's own cli must resolve for real: the upfront + // declaration preflight check now probes it before the body runs, so + // an absent named cli would refuse there instead of exercising the + // override this test is actually about. + const namedCli = authStubCli(directory, 'named'); + const stepCli = join(directory, 'step-absent-cli'); + + await expect(executeAuthoredFlow(flow( + 'named-agent-step-override', + { agents: { reviewer: { cli: namedCli, model: 'known-model' } } }, + async (f) => { + await f.agent('reviewer', { task: 'review this', cli: stepCli }); + f.done('success'); + }, + ), disconnectedJournal, undefined, { flowPath })).rejects.toMatchObject({ + code: 'agent_cli_unresolved', + refusalKind: 'cli_missing', + message: expect.stringContaining(`declares CLI "${stepCli}"`), + }); + }); + + it('does not treat an unrelated f.agent name as a named-agent selector', async () => { + // "reviewer" is declared, but this step names "someone-else" — compile.ts's + // resolveNamedAgent throws "unknown named agent" for any step.agent that + // doesn't resolve, so if lowerAgent set `agent` unconditionally from + // `name`, this would refuse with that error instead of falling through + // to the (missing) flows.json project default like every undecorated + // f.agent call does today. + const { directory, flowPath } = namedAgentFixture(['known-model']); + const disconnectedJournal = new JournalClient('/journal-must-not-be-contacted'); + // A real, resolvable stub — not the bare string "claude" — so the + // upfront declaration preflight check (which now probes "reviewer" + // for real regardless of whether the body selects it) passes + // deterministically instead of depending on whatever CLIs happen to be + // installed and authenticated on the machine running this test. + const reviewerCli = authStubCli(directory, 'reviewer'); + + await expect(executeAuthoredFlow(flow( + 'named-agent-name-not-selector', + { agents: { reviewer: { cli: reviewerCli, model: 'known-model' } } }, + async (f) => { + await f.agent('someone-else', { task: 'x' }); + f.done('success'); + }, + ), disconnectedJournal, undefined, { flowPath })).rejects.toMatchObject({ + code: 'agent_cli_unresolved', + refusalKind: 'cli_unresolved', + message: expect.not.stringContaining('unknown named agent'), + }); + }); + + it('refuses a named declaration whose model is not in the project registry', async () => { + const { directory, flowPath } = namedAgentFixture(['some-other-model']); + const disconnectedJournal = new JournalClient('/journal-must-not-be-contacted'); + + await expect(executeAuthoredFlow(flow( + 'named-agent-model-unknown', + { agents: { reviewer: { cli: join(directory, 'irrelevant-cli'), model: 'unlisted-model' } } }, + async (f) => { + await f.agent('reviewer', { task: 'review this' }); + f.done('success'); + }, + ), disconnectedJournal, undefined, { flowPath })).rejects.toMatchObject({ + code: 'agent_cli_unresolved', + refusalKind: 'model_unknown', + message: expect.stringContaining( + 'Named agent "reviewer" declares model "unlisted-model"', + ), + }); + }); + + it('refuses an unregistered model on a declared-but-never-selected named agent before the body runs', async () => { + // A synchronous body effect detects entry even with a disconnected journal. + const { directory, flowPath } = namedAgentFixture(['some-other-model']); + const disconnectedJournal = new JournalClient('/journal-must-not-be-contacted'); + const marker = join(directory, 'marker.txt'); + + await expect(executeAuthoredFlow(flow( + 'named-agent-unused-model-unknown', + { agents: { unused: { cli: join(directory, 'irrelevant-cli'), model: 'unlisted-model' } } }, + async (f) => { + writeFileSync(marker, 'body entered'); + f.done('success'); + }, + ), disconnectedJournal, undefined, { flowPath })).rejects.toMatchObject({ + code: 'agent_cli_unresolved', + refusalKind: 'model_unknown', + message: expect.stringContaining( + 'Named agent "unused" declares model "unlisted-model"', + ), + }); + expect(existsSync(marker)).toBe(false); + }); + + it("refuses a declared agent's missing CLI before the body runs, even when the body does select it", async () => { + // CLI readiness, like model policy, must refuse before body effects. + const { directory, flowPath } = namedAgentFixture(['known-model']); + const disconnectedJournal = new JournalClient('/journal-must-not-be-contacted'); + const marker = join(directory, 'marker.txt'); + const absentCli = join(directory, 'nonexistent-cli'); + + await expect(executeAuthoredFlow(flow( + 'named-agent-late-cli-missing', + { agents: { reviewer: { cli: absentCli, model: 'known-model' } } }, + async (f) => { + writeFileSync(marker, 'body entered'); + await f.agent('reviewer', { task: 'review this' }); + f.done('success'); + }, + ), disconnectedJournal, undefined, { flowPath })).rejects.toMatchObject({ + code: 'agent_cli_unresolved', + refusalKind: 'cli_missing', + message: expect.stringContaining(`declares CLI "${absentCli}"`), + }); + expect(existsSync(marker)).toBe(false); + }); +}); diff --git a/packages/sdk/tests/direct-input.test.ts b/packages/sdk/tests/direct-input.test.ts index 88ff2174c..dc3644305 100644 --- a/packages/sdk/tests/direct-input.test.ts +++ b/packages/sdk/tests/direct-input.test.ts @@ -22,6 +22,9 @@ const BUILT_CLI = join(ROOT, 'packages', 'sdk', 'dist', 'cli.js'); const FLOW = join(ROOT, 'packages', 'sdk', 'tests', 'fixtures', 'direct-input.flow.ts'); const CONTROL_FLOW = join(ROOT, 'packages', 'sdk', 'tests', 'fixtures', 'direct-output-control.flow.ts'); const SIDE_EFFECT_FLOW = join(ROOT, 'packages', 'sdk', 'tests', 'fixtures', 'pre-journal-side-effect.flow.ts'); +const BAD_MODEL_FLOW = join( + ROOT, 'packages', 'sdk', 'tests', 'fixtures', 'named-agent-bad-model', 'bad-model.flow.ts', +); const TOOLCHAIN_TARGET = process.env['CARGO_TARGET_DIR'] ?? join(process.env['RELAYFLOWS_TOOLCHAIN_HOME'] ?? join(homedir(), '.relayflows-toolchain'), 'target'); const RELAYFLOWD = resolve(process.env['RELAYFLOWD_BIN'] ?? locateRelayflowd()); @@ -203,6 +206,28 @@ describe('direct .flow.ts input through the built CLI and live runtime', () => { expect(missingInputValue.stderr).toContain('REFUSED [invalid_invocation]'); }); + it('surfaces the specific preflight refusal kind for an unresolved f.agent, not a generic invalid_spec', async () => { + // authored-flow-executor.ts's lowerAgent wraps every checkAuthoredFlow + // refusal as one AuthoredFlowExecutionError code (agent_cli_unresolved) + // so f.agent has one throw site regardless of which preflight predicate + // failed — but direct-run.ts used to then hardcode `kind: 'invalid_spec'` + // in the CLI's JSON/text report for all of them, losing the closed + // refusal taxonomy (cli_missing, model_unknown, ...) that `flows check` + // preserves for the declarative dialect. This proves the propagated + // AuthoredFlowExecutionError.refusalKind reaches the actual CLI output. + const directory = temporaryDirectory(); + const dataDir = join(directory, 'data'); + await startDaemon(dataDir); + + const result = invokeCli([ + 'run', BAD_MODEL_FLOW, '--input', '{}', '--data-dir', dataDir, + ]); + expect(result.status, result.stderr).toBe(2); + expect(result.stderr).toContain('REFUSED [model_unknown]'); + expect(result.stderr).not.toContain('REFUSED [invalid_spec]'); + expect(result.stderr).toContain('declares model "unlisted-model"'); + }); + // `--no-spawn` keeps this case about the property it names. `flows run` now // starts a daemon when none is serving (kernel/DAEMON-LIFECYCLE.md §3), so // without the flag the refusal under test would be about the spawn rather diff --git a/packages/sdk/tests/fixtures/named-agent-bad-model/bad-model.flow.ts b/packages/sdk/tests/fixtures/named-agent-bad-model/bad-model.flow.ts new file mode 100644 index 000000000..3ddab621b --- /dev/null +++ b/packages/sdk/tests/fixtures/named-agent-bad-model/bad-model.flow.ts @@ -0,0 +1,8 @@ +import { flow } from '@relayflows/surface'; + +export default flow('bad-model-fixture', { + agents: { reviewer: { cli: 'claude', model: 'unlisted-model' } }, +}, async (f) => { + await f.agent('reviewer', { task: 'review this' }); + f.done('success'); +}); diff --git a/packages/sdk/tests/fixtures/named-agent-bad-model/flows.json b/packages/sdk/tests/fixtures/named-agent-bad-model/flows.json new file mode 100644 index 000000000..4ac8b02d4 --- /dev/null +++ b/packages/sdk/tests/fixtures/named-agent-bad-model/flows.json @@ -0,0 +1 @@ +{ "models": ["listed-model"] } diff --git a/packages/sdk/tests/live-named-agents.test.ts b/packages/sdk/tests/live-named-agents.test.ts new file mode 100644 index 000000000..573671dc7 --- /dev/null +++ b/packages/sdk/tests/live-named-agents.test.ts @@ -0,0 +1,177 @@ +import { chmodSync, existsSync, lstatSync, mkdtempSync, rmSync, writeFileSync, readFileSync, accessSync, constants } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { spawn, type ChildProcess } from 'node:child_process'; +import { afterEach, beforeAll, describe, expect, it } from 'vitest'; +import { flow } from '@relayflows/surface'; +import { socketPathFor } from '../src/daemon-connection.js'; +import { JournalClient } from '../src/journal-client.js'; +import { AgentWorker } from '../src/worker.js'; +import { executeAuthoredFlow } from '../src/authored-flow-executor.js'; + +const RELAYFLOWD = resolve(process.env['RELAYFLOWD_BIN'] + ?? fileURLToPath(new URL('../../../kernel/target/debug/relayflowd', import.meta.url))); +const temporaryDirectories: string[] = []; +const daemons: ChildProcess[] = []; +const clients: JournalClient[] = []; +beforeAll(() => { + try { accessSync(RELAYFLOWD, constants.X_OK); } + catch { + throw new Error(`Build this checkout's daemon and set RELAYFLOWD_BIN to it: ${RELAYFLOWD}`); + } +}); +afterEach(async () => { + for (const client of clients.splice(0)) client.close(); + for (const daemon of daemons.splice(0)) await stopDaemon(daemon); + for (const directory of temporaryDirectories.splice(0)) rmSync(directory, { recursive: true, force: true }); +}); + +describe('named agents against a live kernel', () => { + it('dispatches two distinct named agents declared in the flow header', async () => { + // Distinct binaries echo their identity and received model to detect misdispatch. + const directory = temporaryDirectory('flows-live-named-agents-'); + const dataDir = join(directory, 'data'); + + function stubCli(label: string): string { + const cli = join(directory, `${label}-cli`); + writeFileSync(cli, `#!/usr/bin/env node +if (process.argv[2] === 'auth' && process.argv[3] === 'status') { + require('node:fs').appendFileSync(${JSON.stringify(join(directory, 'probes.jsonl'))}, JSON.stringify({ cli: ${JSON.stringify(label)}, model: process.env.RELAYFLOW_MODEL }) + '\\n'); + process.exit(0); +} +if (process.argv[2] !== '--relayflows-adapter-v1') process.exit(9); +process.stdout.write('relayflows-agent-cli-v1\\n'); +let input = ''; +process.stdin.setEncoding('utf8'); +process.stdin.on('data', chunk => { input += chunk; }); +process.stdin.on('end', () => { + if (input.trim() === '') process.exit(0); + const request = JSON.parse(input); + process.stdout.write('relayflows-agent-cli-v1-execute\\n'); + process.stdout.write(JSON.stringify({ cli: ${JSON.stringify(label)}, model: request.model, task: request.instruction })); +}); +`); + chmodSync(cli, 0o755); + return cli; + } + const reviewerCli = stubCli('reviewer'); + const fixerCli = stubCli('fixer'); + // No project-level `cli` declared — both steps must resolve purely + // through the header's named declarations, proving `cli`/`agent` reached + // the submitted spec rather than silently falling back to a project + // default. `models` allowlists both declared models so preflight's + // model-registry check (preflight.ts's unknownModelDiagnostics) doesn't + // refuse first; the custom-wrapper model-scoped probe (cli-adapter.ts's + // modelReadinessProbe, "relayflows-wrapper-v1" branch) just re-invokes + // the same `auth status` the stub CLIs above already answer. + writeFileSync(join(directory, 'flows.json'), JSON.stringify({ models: ['model-a', 'model-b'] })); + await startDaemon(dataDir); + + const client = await connectClient(dataDir); + await client.hello('live-sdk-named-agents-worker'); + const worker = new AgentWorker(client, { + workerId: 'live-sdk-named-agents-worker', + pins: { + workspace: [{ surface: 'repo', revision_id: 'rev-a' }], + streams: [], + }, + }); + await worker.attach(); + + const runClient = await connectClient(dataDir); + await runClient.hello('live-sdk-named-agents-run'); + const captured: Record = {}; + const handle = flow('named-agents', { + agents: { + reviewer: { cli: reviewerCli, model: 'model-a' }, + fixer: { cli: fixerCli, model: 'model-b' }, + // A valid unused alias is checked without dispatch or duplicate probes. + unused: { cli: reviewerCli, model: 'model-a' }, + }, + }, async (f) => { + captured['reviewer'] = JSON.parse((await f.agent('reviewer', { task: 'review the diff' })).summary); + captured['fixer'] = JSON.parse((await f.agent('fixer', { task: 'fix what reviewer found' })).summary); + // Step-level cli/model must win over the named declaration's own — + // selecting "reviewer" but overriding onto fixer's cli/model proves + // the override is genuinely applied, not just tolerated as a no-op. + captured['override'] = JSON.parse((await f.agent('reviewer', { + task: 'override', cli: fixerCli, model: 'model-b', + })).summary); + captured['modelOnly'] = JSON.parse((await f.agent('reviewer', { + task: 'model-only', model: 'model-b', + })).summary); + captured['cliOnly'] = JSON.parse((await f.agent('reviewer', { + task: 'cli-only', cli: fixerCli, + })).summary); + f.done('success'); + }); + + let result: Awaited>; + try { + result = await executeAuthoredFlow(handle, runClient, undefined, { + flowPath: join(directory, 'named-agents.flow.ts'), + }); + } finally { + await worker.close(); + } + + expect(captured['modelOnly']).toEqual({ cli: 'reviewer', model: 'model-b', task: 'model-only' }); + expect(captured['cliOnly']).toEqual({ cli: 'fixer', model: 'model-a', task: 'cli-only' }); + const probes = readFileSync(join(directory, 'probes.jsonl'), 'utf8').trim().split('\n').map(line => JSON.parse(line)); + // Each distinct CLI/model pair gets one model-scoped readiness probe. + // Selecting a declaration again (including the unused alias) reuses readiness. + expect(probes).toHaveLength(4); + for (const cli of ['reviewer', 'fixer']) { + for (const model of ['model-a', 'model-b']) { + expect(probes.filter(probe => probe.cli === cli && probe.model === model)).toHaveLength(1); + } + } + expect(result.completionReason).toBe('success'); + expect(captured['reviewer']).toEqual({ cli: 'reviewer', model: 'model-a', task: 'review the diff' }); + expect(captured['fixer']).toEqual({ cli: 'fixer', model: 'model-b', task: 'fix what reviewer found' }); + expect(captured['override']).toEqual({ cli: 'fixer', model: 'model-b', task: 'override' }); + }); + +}); + +function temporaryDirectory(prefix: string): string { + const directory = mkdtempSync(join(tmpdir(), prefix)); + temporaryDirectories.push(directory); + return directory; +} + +async function startDaemon(dataDir: string): Promise { + const daemon = spawn(RELAYFLOWD, ['--data-dir', dataDir, 'serve'], { + stdio: ['ignore', 'pipe', 'pipe'], + }); + daemons.push(daemon); + const stderr: Buffer[] = []; + daemon.stderr?.on('data', (chunk: Buffer) => stderr.push(chunk)); + const socket = socketPathFor(dataDir); + const deadline = Date.now() + 5_000; + while (Date.now() < deadline) { + if (existsSync(socket) && lstatSync(socket).isSocket()) return daemon; + if (daemon.exitCode !== null || daemon.signalCode !== null) { + throw new Error(`relayflowd exited before binding ${socket}: ${Buffer.concat(stderr).toString('utf8')}`); + } + await delay(20); + } + throw new Error(`relayflowd did not bind ${socket} within 5000ms`); +} + +async function stopDaemon(daemon: ChildProcess, signal: NodeJS.Signals = 'SIGTERM'): Promise { + if (daemon.exitCode !== null || daemon.signalCode !== null) return; + const exited = new Promise((resolveExit) => daemon.once('exit', () => resolveExit())); + daemon.kill(signal); + await exited; +} + +async function connectClient(dataDir: string): Promise { + const client = new JournalClient(socketPathFor(dataDir), { requestTimeoutMs: 5_000 }); + clients.push(client); + await client.connect(); + return client; +} + +function delay(ms: number): Promise { return new Promise(resolve => setTimeout(resolve, ms)); } diff --git a/packages/surface/package-lock.json b/packages/surface/package-lock.json index 869e237eb..f964082e6 100644 --- a/packages/surface/package-lock.json +++ b/packages/surface/package-lock.json @@ -561,9 +561,9 @@ } }, "node_modules/@relayfile/core": { - "version": "0.10.57", - "resolved": "https://registry.npmjs.org/@relayfile/core/-/core-0.10.57.tgz", - "integrity": "sha512-oKwNJKGovzdl/6IF3sSVZFsiG2ITH7jXnmFgxbCIYPZ+J7QsQ2zvBhT35sPGiNcD4FfiMpyX9OB8LJXOefNdcQ==", + "version": "0.10.63", + "resolved": "https://registry.npmjs.org/@relayfile/core/-/core-0.10.63.tgz", + "integrity": "sha512-gMK63uZGsuWVnjmDHpUb/sb6P0HukMU2e4tJHohUfEVLfl9qe12KJ2ySsN91LUEAs+MsiULSkvVJVlrWUiWVJw==", "license": "Apache-2.0", "peer": true, "engines": { @@ -571,9 +571,9 @@ } }, "node_modules/@relayfile/mount-darwin-arm64": { - "version": "0.10.57", - "resolved": "https://registry.npmjs.org/@relayfile/mount-darwin-arm64/-/mount-darwin-arm64-0.10.57.tgz", - "integrity": "sha512-yCcjQ7HoCJECnjtZeG7eZwLXa4RAT3RAVAAymdIqKZbMe3wbkF1kg3YNQHuRqYsDEIQzGkA4k5AsITmG1j2PfQ==", + "version": "0.10.63", + "resolved": "https://registry.npmjs.org/@relayfile/mount-darwin-arm64/-/mount-darwin-arm64-0.10.63.tgz", + "integrity": "sha512-zA4fRSBe4YRWeYYqpZKvX6HsY/04G0tOwl58lTcvy2r9jEwNDJNCpqwtPS2yCoqQTyWay5sXjHx+qDhS5YiPjQ==", "cpu": [ "arm64" ], @@ -585,9 +585,9 @@ "peer": true }, "node_modules/@relayfile/mount-darwin-x64": { - "version": "0.10.57", - "resolved": "https://registry.npmjs.org/@relayfile/mount-darwin-x64/-/mount-darwin-x64-0.10.57.tgz", - "integrity": "sha512-5Ze1a16Mxc2PTj2Q+AdtRZIMKQ4sxJIIXwPjdWD+9rTMEQeqs7xzSZ1WRIjI89FzyNHBjx/+gzD9FK8Kjuc7Yg==", + "version": "0.10.63", + "resolved": "https://registry.npmjs.org/@relayfile/mount-darwin-x64/-/mount-darwin-x64-0.10.63.tgz", + "integrity": "sha512-4lBUHuizQr1Mxbt/uvegojoPZsU00Od5w6VjsGgFKaffuCAkaYT0E1WKuQb7Qvt95ZuiCk97fkumsP9U+RWbdA==", "cpu": [ "x64" ], @@ -599,9 +599,9 @@ "peer": true }, "node_modules/@relayfile/mount-linux-arm64": { - "version": "0.10.57", - "resolved": "https://registry.npmjs.org/@relayfile/mount-linux-arm64/-/mount-linux-arm64-0.10.57.tgz", - "integrity": "sha512-+2MhcDwzb7KSC3qjKWOW+9EnZ8mFKJiVns5fiJwO1oThiAd3CnnCfJzYkbEWRKIL0dH5wg4npdayF23DiBtB5w==", + "version": "0.10.63", + "resolved": "https://registry.npmjs.org/@relayfile/mount-linux-arm64/-/mount-linux-arm64-0.10.63.tgz", + "integrity": "sha512-Nsgw1Hp1ITPtw975TBQ8tm1OtAMjb9gCl8BkbChuMQ4oPWCONXDbDuJON1A9K/U81Msr8RNURfMAyQdFd3gmXA==", "cpu": [ "arm64" ], @@ -613,9 +613,9 @@ "peer": true }, "node_modules/@relayfile/mount-linux-x64": { - "version": "0.10.57", - "resolved": "https://registry.npmjs.org/@relayfile/mount-linux-x64/-/mount-linux-x64-0.10.57.tgz", - "integrity": "sha512-Y0OGuGEpglQsUrrR0uzjBzqFyrM8wU5jG1tELJ8LBNhedNUEUupGKD7ixs+i4k9kR8Q68voVsQTfzdgqzW3M+Q==", + "version": "0.10.63", + "resolved": "https://registry.npmjs.org/@relayfile/mount-linux-x64/-/mount-linux-x64-0.10.63.tgz", + "integrity": "sha512-dsA1amLyR8Kpwowil5UGFmT9cM2C7Gbdv/BklsbMY+Ev5zCzngV6Cqo9N3SqwHc96g0mEdh5DkHo4gBhcnWucA==", "cpu": [ "x64" ], @@ -639,13 +639,13 @@ } }, "node_modules/@relayfile/sdk": { - "version": "0.10.57", - "resolved": "https://registry.npmjs.org/@relayfile/sdk/-/sdk-0.10.57.tgz", - "integrity": "sha512-9AJIEyyruqVogbdqVfY7q0pjuXJy8IhzxR/1pvwWJiybU1BZO8LFNurlFePTAo/uhLG9cshr0YJRBxv2WbG0Sg==", + "version": "0.10.63", + "resolved": "https://registry.npmjs.org/@relayfile/sdk/-/sdk-0.10.63.tgz", + "integrity": "sha512-XVagKtSIumjG497WWmKHIPVBn6BYZf8nOCNO/hwebjzyiI2zig88JYQgQWnrA0FzAZI+JZF1dvy8gI8lb/OsTQ==", "license": "Apache-2.0", "peer": true, "dependencies": { - "@relayfile/core": "0.10.57", + "@relayfile/core": "0.10.63", "ignore": "^7.0.5", "tar": "^7.5.10" }, @@ -653,16 +653,16 @@ "node": ">=18" }, "optionalDependencies": { - "@relayfile/mount-darwin-arm64": "0.10.57", - "@relayfile/mount-darwin-x64": "0.10.57", - "@relayfile/mount-linux-arm64": "0.10.57", - "@relayfile/mount-linux-x64": "0.10.57" + "@relayfile/mount-darwin-arm64": "0.10.63", + "@relayfile/mount-darwin-x64": "0.10.63", + "@relayfile/mount-linux-arm64": "0.10.63", + "@relayfile/mount-linux-x64": "0.10.63" } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.1.tgz", - "integrity": "sha512-UZ8sUxPTiHWYX9QNdJedb1kDZSpS1t/VPWBWGSgqHNi9w3Cu6IXvu2mzbhiTiPvtrqgTQJ+zqiAq2iPIPilpaQ==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.3.tgz", + "integrity": "sha512-w3Jnvi1ocaVm/c7yVPpfB98XeSRBMyzp6njL5MVVbGyXjpmUkN+s6Hp4t0PqhGCCaI1ZHMKXt/w0lA1RCaLVcw==", "cpu": [ "arm" ], @@ -674,9 +674,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.63.1.tgz", - "integrity": "sha512-cQ4nFQABN5cDvDpbvJ7bMStCpnaVxynZrRMfUJYgxcIk9Sh54FIO1vtfkg0B69REjER77ioZ/ov+eAApx/KmLQ==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.63.3.tgz", + "integrity": "sha512-uI/ESiaIbbRYAEhzy8PCUWDp1hB0bjAqM06mW9flOoNO4Q8DQpeoREhBR5Hegfl+wpXiguyJv6XSPzEN7OxyHQ==", "cpu": [ "arm64" ], @@ -688,9 +688,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.63.1.tgz", - "integrity": "sha512-FQNqd1lRy/0QhDk3xeRIkSBiCpXCiDnZO3YLVdcDKN1UBiKToNftCzcXYNLshmPDUMlu2TdeS8tGcsU6f3YF1Q==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.63.3.tgz", + "integrity": "sha512-oxhrd1jmXLwWZ83eQYDXxuqRdkqkzrjR3JobKeuUyfdNZo11FuQIvqEOZhyIT7OBHxXoGslDDjN0cQcM6T0TqQ==", "cpu": [ "arm64" ], @@ -702,9 +702,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.63.1.tgz", - "integrity": "sha512-pvD16V939D3CloK0+qikpGaxiPrDUXTe7Y5cWOMkMSy7m1cawa8EGy/kXYi/G/cKAC4HDAbSnzCIk1WmsoOKXg==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.63.3.tgz", + "integrity": "sha512-7/YiIMghVE8DrxKvNdorAaJVdriOFgOIpdStnPx8ppx5zfTwC3jBCSEAIzB7JD5404m65THl6H93UTTVUvypmg==", "cpu": [ "x64" ], @@ -716,9 +716,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.63.1.tgz", - "integrity": "sha512-pcFGeL2345VwdTnJhA6zLbew+YgWB0qBG2+dMtXjCicf6+rm6kO6cOoh5VnTe0ZMrMRgRyuHmCJxZWrIdzYuOw==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.63.3.tgz", + "integrity": "sha512-GXFZRRoMAytaI5z6N3Zhfw0WL18Q0M8r95D5hlC4GqE/lGk8pbSJNUBoOWDfbm6dTciqHj2nU87tI5f6XhQiOg==", "cpu": [ "arm64" ], @@ -730,9 +730,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.63.1.tgz", - "integrity": "sha512-mRJlqSRulVzcKq/LKA6ICSIc3K/l4fzlVn/gePn2nXIHy8seRi5z/eeRE0d/XMBxcMldiXtQTSpRj0tkkC3g8Q==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.63.3.tgz", + "integrity": "sha512-77W+8X3ddYgPxUpB8nZFQs2Mq+wc4HVlcSRtApXLjYBcnPMkttrSnU8VwKQjeWYhMsITHFs5cWBQ8vz1Q+5RHQ==", "cpu": [ "x64" ], @@ -744,9 +744,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.63.1.tgz", - "integrity": "sha512-YDUNvVM85TI3g/1OpnqKP1h4NeW/j64DfWMf+G3M809xNk1bJSnpFp4sh83NpmVE5DXnkh8ULor4LTVZKoYLHw==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.63.3.tgz", + "integrity": "sha512-FVkwK+iUC+mq+GipVK46rRVticfAPtvPUNlqlGXUDxdVk/UGjQiiiUVPUrEXdSpU2ufU0XxLGyTqDtBidDOVmg==", "cpu": [ "arm" ], @@ -761,9 +761,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.63.1.tgz", - "integrity": "sha512-7Mcn71p9ZuQFAj+h+dhQXy/yeLePRS2yKRnmW1DijA9thKO5qap0GNOIQK4yQ6iP3SU0Mrb/yWo8h8vgRba8lw==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.63.3.tgz", + "integrity": "sha512-+aGU1t3398yQOVj1Bz8o3e+KtswxAPvO+mtxtNdfXYMkXIHu7XhhkCD7/DEH9q8tF8uhDnMWvfpUKI8y1sZJsg==", "cpu": [ "arm" ], @@ -778,9 +778,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.63.1.tgz", - "integrity": "sha512-4YiLQTX6U4CSl0L9cluep9A9W6UmTfqBDc2/CH6wlu54pl4E7Jn3cOD8oxzvBDEGk/JMKgJ47C8g+radF7mwvg==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.63.3.tgz", + "integrity": "sha512-cR0kjpRXR2KJ2oQK8E2KTPtphs+b9hZ8IhTZubNryt/RsqgdOZBQ2Zq0q5UedtiIi0rs3jVhJh55RE1ZHUVGUA==", "cpu": [ "arm64" ], @@ -795,9 +795,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.63.1.tgz", - "integrity": "sha512-2ra8F7w8OquwZN9z2/fKFnli69wa8PLwaVzRMIPGb13ByMJwC28Fbp8YcVGoUhlYMTt7j5j9bNgpysrN2UM+vw==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.63.3.tgz", + "integrity": "sha512-y1RYi4Q3/9ByVWSSt9kX2ustE0B7kFYbJ6zZdVZVyqopZs3yhCTwRfrjIX4vezUJInma/Gs6BOFDJg7yZmJ0IQ==", "cpu": [ "arm64" ], @@ -812,9 +812,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.63.1.tgz", - "integrity": "sha512-Sy20ncyhjmBP0Ml+UvQbimjlk6VFgjW5uNP+qqwHB00mTE8Bl2C1TuHTlRwK2YoXeZbee5lP2XevBWVkAQAtSQ==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.63.3.tgz", + "integrity": "sha512-DNhEA5viIj3Z5bZLE4z4oV8N5ozWqDwyt7T6KG7VdLDJ0nW+rNOYlphBl4/3HQkK75qipPLsVOfStHHOwN9WSg==", "cpu": [ "loong64" ], @@ -829,9 +829,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.63.1.tgz", - "integrity": "sha512-noITLp8oNjYliPnGWmLyelIHwULGqbHloQHGw1rtxbWhTuWooRpnZarZQJ1y9EUC4szuCusCc+HEpUtxpIwYvA==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.63.3.tgz", + "integrity": "sha512-17gQCqrIpXBX2Cmi9/TygnVOqGbzsba/iaqcYSL8FY7lNugg+7AiYNs5c5nKWD+NRQha36Sa0CqkJqH4XVHwnQ==", "cpu": [ "loong64" ], @@ -846,9 +846,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.63.1.tgz", - "integrity": "sha512-hlxxXd+F1mWiAcaFR7Sv9ZQT6m6UfI8+Vy/kFJzztq2pDMU/0wZ9sish0iszNZvsQDo8Gc0i5yuFEOz5dDf6fA==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.63.3.tgz", + "integrity": "sha512-6LwVnZRIyINpdku/yOcI8Tm9YqLmhHK5emmlOOnW9tO0SYEm1FmKPcsSAGp0NBlqR2P04xaND4jvN6sTHqhq8A==", "cpu": [ "ppc64" ], @@ -863,9 +863,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.63.1.tgz", - "integrity": "sha512-EF7OpqQTQ/BvGqLzUi4rEHuagCV9MugAUXSHemwPW5vxZ75RR+jxO/2j95Ph2dalMpFHSVECjRoioHZgA9zOYA==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.63.3.tgz", + "integrity": "sha512-xMUqkTXlEUtI/p5AAukMwBRr1enU3efsTeF+bskeFfk8t1C9rcC8sLREcZXmTfAXEbvRdJVSonVJez3TMlbR3w==", "cpu": [ "ppc64" ], @@ -880,9 +880,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.63.1.tgz", - "integrity": "sha512-wQO3JesW9PRkwlabQ27y7sPfVOOTLRG73I4F2UYHG5PXun3J9U3y+b7ezVKSYbsvSKGQ1k1cq8Qlun4C9kLt3w==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.63.3.tgz", + "integrity": "sha512-S3E94co9F9WRRqEaUoQZ38K1gCz6KiM+nL7/3ijq7fDGF3OznjS5TasgYITlvl27GQKtu4lOAOsr5MFwkijvOA==", "cpu": [ "riscv64" ], @@ -897,9 +897,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.63.1.tgz", - "integrity": "sha512-ouAGwhO6wHRXdnOVCOsB0tRFkA7nhNB2Nwax6oECXN0YiN8EYUTBAOudADOB1PI+yDL61TeNx/u7MVCzksNbkQ==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.63.3.tgz", + "integrity": "sha512-1QtRDwG42x5BJI3s9mxu5rEjDnfbSnk20HQ9/ylTAYnSwYwxMVb+Vgu34wzzTQ7ogqBybebgQNUDAvZVQ38DbA==", "cpu": [ "riscv64" ], @@ -914,9 +914,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.63.1.tgz", - "integrity": "sha512-q2R38Sn+1J8RxhfJ+T54wSWmyKXWec+9jgDfqO2AtArEqHO5R2aeayp5H5OYLr5UYDVGsVaZPEFUooMhYCdz5A==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.63.3.tgz", + "integrity": "sha512-BQhejF6ZXOpxbngiNTP12GCGQeaDVL2QXGeBVViKIYzFHM5RKxTxwUMB1fr1BeNFphFMpnRqC5QSXFSa4z6UQw==", "cpu": [ "s390x" ], @@ -931,9 +931,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.63.1.tgz", - "integrity": "sha512-gfI5T24WLLuFfSKw7Go/zDXjAAV0fny0swTaDv+WjK7vqcw4cRhFfdsyKL1n+ukI+ooBxn3bVQnyrn06WpI50w==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.63.3.tgz", + "integrity": "sha512-SXagRwnI2Wlwlitllu59UK/nGVbD1CKPcNqDplHwIC4BqJcpXFjD32d1R/RbuISa95HdQrZM3/7v4bKiowFaLA==", "cpu": [ "x64" ], @@ -948,9 +948,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.63.1.tgz", - "integrity": "sha512-4h6XqthmB4Hspji84wvgk+ElodTsGj+dbZqHJHHtKxj4mYq0ANSEEPX9ys3moJueqsRjwpaJYH7874Itwnj2ow==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.63.3.tgz", + "integrity": "sha512-2IPozoEALRCziGqE8O9KMK60PMu5TS1huv4fwoeCexj+WjmcwFtX9CTOVbfXCUqcELAubEwRFPYlzb/WvwY2HQ==", "cpu": [ "x64" ], @@ -965,9 +965,9 @@ ] }, "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.63.1.tgz", - "integrity": "sha512-dlfCOa87o1VAYegLQ9EKilx2JCeRofiyPGhTCmqnuXZ6bMPiycO1rq1+sKoulAp7pGLIsTIw+1x5R+zgh5LhhA==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.63.3.tgz", + "integrity": "sha512-AoxqosUHT9IX54hFn2TiN6A7d6ZKTtE6pd2bqWtqkkNJ6HJGaU6FRouGX8L1O7R/ZwsnCnpQrHzb4pDEx+UHRQ==", "cpu": [ "x64" ], @@ -979,9 +979,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.63.1.tgz", - "integrity": "sha512-cjkLbOlfcm3QGhMM1J5zaZjsw1GggbN6rw9UTSSRrPrR1KkcXnN7Uq9rPw34xImQ9VOY9GN+6u2Zj80B9ptkcw==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.63.3.tgz", + "integrity": "sha512-d+CaftKgmkFBzCwezMqqy1d0QNNYugqLCMcYVQWBy5SS2YfeMP8Q8ripkgx9O8IyBXXLHrJ+aaCV4U96usv6Yg==", "cpu": [ "arm64" ], @@ -993,9 +993,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.63.1.tgz", - "integrity": "sha512-Li1KdUnWGE4N3e1F/B4RTB1ms+nG4WBgjByO46pkeBVX/2UBsY53xf5vK9WygVmnH3RwncIST7lkSdLSY6P9lg==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.63.3.tgz", + "integrity": "sha512-xXlDF6nR1eOuXbdDy5Hl5fmtY7teUDevF/k0O7IPoZe4Tpmdv+lgdE5JRsnhQtt37ql9P0VF2kAN9a0OCZdo+Q==", "cpu": [ "arm64" ], @@ -1007,9 +1007,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.63.1.tgz", - "integrity": "sha512-t4ZYOSoLTgwhuFMrmTMLx/+i1DQVK7HYqMc6kY46EApwi8X0nIVphzdNoThU3xt6n+N5urG1/gxBdCaKDLavfg==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.63.3.tgz", + "integrity": "sha512-YtXAgLN+JP7Ay6qG3eWhc7IHMQPzLc8r3uvhAvlJIoCz/4Q32+Bl9Fmnywidh8v1GOIMmymjovfqY9ETAtysvA==", "cpu": [ "ia32" ], @@ -1021,9 +1021,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.63.1.tgz", - "integrity": "sha512-RgroPfMmKlD1RzSDxvwgcPiy2HNQKoYV7OmwIXDsk73uKW5t6B/V8KIy27SMv/FNXFo/oSBtWc9J0X7t91ezZg==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.63.3.tgz", + "integrity": "sha512-WuWtSJRNo549vzcfZyEgfqb6zeSgn1F+UE5kQ+BCjzz0W4MGCjntUHkZVc1VRuAM7+ULaSyhiPxD1spyewFvkQ==", "cpu": [ "x64" ], @@ -1035,9 +1035,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.63.1.tgz", - "integrity": "sha512-at8QVep6S3h5Y6gSbdGU06bRY5WJkf6WUduM9YtvYMbYhB1MOFfUgc6kehitQXzOtMSaT70q7f9ydPhpqu821w==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.63.3.tgz", + "integrity": "sha512-+lIKX7O0+IGe7WuhATaAMMeT7B76vfhXH/l9wLQL+nvyhbw2ohYCKIdWL56JfDu75CWt5oKRP4QFH/jkMtBquA==", "cpu": [ "x64" ], @@ -1090,9 +1090,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.20.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.2.tgz", - "integrity": "sha512-xlvWf4Vs9n1PEVYwP1n4vvG07M6y8WgvJ2t0vbrWTmijsIHp1cS+uJ2kMIRdY3nHZK0nCYKrPeD171+SzF4/zw==", + "version": "22.20.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.3.tgz", + "integrity": "sha512-DZmzkmwHzXrLPAXPyKNDzlIwMMUZCVacoD25ywdy5YTKGbOx/2ld+Q38Im2zJ0vBuZP5Prd3VZutKZyXwkOS8A==", "dev": true, "license": "MIT", "dependencies": { @@ -1332,6 +1332,22 @@ "url": "https://opencollective.com/express" } }, + "node_modules/body-parser/node_modules/iconv-lite": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -1340,9 +1356,9 @@ "peer": true }, "node_modules/brace-expansion": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", - "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", "license": "MIT", "peer": true, "dependencies": { @@ -1720,19 +1736,6 @@ "url": "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/fb55/encoding-sniffer?sponsor=1" } }, - "node_modules/encoding-sniffer/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "license": "MIT", - "peer": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", @@ -1947,9 +1950,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.7.tgz", - "integrity": "sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz", + "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==", "funding": [ { "type": "github", @@ -2099,9 +2102,9 @@ } }, "node_modules/hono": { - "version": "4.13.7", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.13.7.tgz", - "integrity": "sha512-c8/gF9ac8Y78/agExVocyLevgR+JlpNB444Py0FSX8pJoPdYUfUzRcXtYEYGwt6l19qIlVZPN5Mfsw9jFShmQQ==", + "version": "4.13.8", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.13.8.tgz", + "integrity": "sha512-/Gng7NfoykZl2pjukW5Z6+8Yxm3BPRf86GTbQnt0SbySkvax4fyL4H3HhY1cCpBGmiW9XDRFzRV+CXK2W8QudQ==", "license": "MIT", "engines": { "node": ">=16.9.0" @@ -2161,19 +2164,16 @@ } }, "node_modules/iconv-lite": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", - "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "license": "MIT", + "peer": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" } }, "node_modules/ignore": { @@ -2193,9 +2193,9 @@ "license": "ISC" }, "node_modules/ip-address": { - "version": "10.7.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.0.tgz", - "integrity": "sha512-BGFsyJd5mpXp3rK6jIdADLNgpJUK1jnjzvYF8lK+VyDab9JAmqN0YOKDdP17HlgKb2+ehPgDc8EtnRLbGCAMhA==", + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.2.tgz", + "integrity": "sha512-7H/2gFSIitxc0hG3nOI1glS8QLo/EHBFFLk8vEUjXY/xu0AdL8jZ9U1IzO2PUm0d2D/ofQcAifb0g6OBkt8U7w==", "license": "MIT", "engines": { "node": ">= 12" @@ -2365,9 +2365,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.18", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", - "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", + "version": "3.3.19", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.19.tgz", + "integrity": "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==", "dev": true, "funding": [ { @@ -2611,9 +2611,9 @@ } }, "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.8.tgz", + "integrity": "sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ==", "license": "MIT", "dependencies": { "forwarded": "0.2.0", @@ -2621,6 +2621,10 @@ }, "engines": { "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/qs": { @@ -2667,6 +2671,22 @@ "node": ">= 0.10" } }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -2677,9 +2697,9 @@ } }, "node_modules/rollup": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.63.1.tgz", - "integrity": "sha512-3Df9jsstwhccuEfmAMi9l8XUh/GOkVObmFTU7CCVBysEbcOZLl84jCtaAZMcPiMz2EGKsATzQcU+Xr3n/wU6cg==", + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.63.3.tgz", + "integrity": "sha512-1i2XreiAoMMXuPGD6Msj2xWrMMkHojNRKivInxGQcg7/1KuPuYlfUutLyh4drnOxUTHX9cHI4wFoat8D/NKaBw==", "dev": true, "license": "MIT", "dependencies": { @@ -2694,31 +2714,31 @@ }, "optionalDependencies": { "@napi-rs/lzma-linux-x64-gnu": "1.5.1", - "@rollup/rollup-android-arm-eabi": "4.63.1", - "@rollup/rollup-android-arm64": "4.63.1", - "@rollup/rollup-darwin-arm64": "4.63.1", - "@rollup/rollup-darwin-x64": "4.63.1", - "@rollup/rollup-freebsd-arm64": "4.63.1", - "@rollup/rollup-freebsd-x64": "4.63.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.63.1", - "@rollup/rollup-linux-arm-musleabihf": "4.63.1", - "@rollup/rollup-linux-arm64-gnu": "4.63.1", - "@rollup/rollup-linux-arm64-musl": "4.63.1", - "@rollup/rollup-linux-loong64-gnu": "4.63.1", - "@rollup/rollup-linux-loong64-musl": "4.63.1", - "@rollup/rollup-linux-ppc64-gnu": "4.63.1", - "@rollup/rollup-linux-ppc64-musl": "4.63.1", - "@rollup/rollup-linux-riscv64-gnu": "4.63.1", - "@rollup/rollup-linux-riscv64-musl": "4.63.1", - "@rollup/rollup-linux-s390x-gnu": "4.63.1", - "@rollup/rollup-linux-x64-gnu": "4.63.1", - "@rollup/rollup-linux-x64-musl": "4.63.1", - "@rollup/rollup-openbsd-x64": "4.63.1", - "@rollup/rollup-openharmony-arm64": "4.63.1", - "@rollup/rollup-win32-arm64-msvc": "4.63.1", - "@rollup/rollup-win32-ia32-msvc": "4.63.1", - "@rollup/rollup-win32-x64-gnu": "4.63.1", - "@rollup/rollup-win32-x64-msvc": "4.63.1", + "@rollup/rollup-android-arm-eabi": "4.63.3", + "@rollup/rollup-android-arm64": "4.63.3", + "@rollup/rollup-darwin-arm64": "4.63.3", + "@rollup/rollup-darwin-x64": "4.63.3", + "@rollup/rollup-freebsd-arm64": "4.63.3", + "@rollup/rollup-freebsd-x64": "4.63.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.63.3", + "@rollup/rollup-linux-arm-musleabihf": "4.63.3", + "@rollup/rollup-linux-arm64-gnu": "4.63.3", + "@rollup/rollup-linux-arm64-musl": "4.63.3", + "@rollup/rollup-linux-loong64-gnu": "4.63.3", + "@rollup/rollup-linux-loong64-musl": "4.63.3", + "@rollup/rollup-linux-ppc64-gnu": "4.63.3", + "@rollup/rollup-linux-ppc64-musl": "4.63.3", + "@rollup/rollup-linux-riscv64-gnu": "4.63.3", + "@rollup/rollup-linux-riscv64-musl": "4.63.3", + "@rollup/rollup-linux-s390x-gnu": "4.63.3", + "@rollup/rollup-linux-x64-gnu": "4.63.3", + "@rollup/rollup-linux-x64-musl": "4.63.3", + "@rollup/rollup-openbsd-x64": "4.63.3", + "@rollup/rollup-openharmony-arm64": "4.63.3", + "@rollup/rollup-win32-arm64-msvc": "4.63.3", + "@rollup/rollup-win32-ia32-msvc": "4.63.3", + "@rollup/rollup-win32-x64-gnu": "4.63.3", + "@rollup/rollup-win32-x64-msvc": "4.63.3", "fsevents": "~2.3.2" } }, @@ -3247,19 +3267,6 @@ "node": ">=18" } }, - "node_modules/whatwg-encoding/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "license": "MIT", - "peer": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/whatwg-mimetype": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", @@ -3319,9 +3326,9 @@ } }, "node_modules/yaml": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", - "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz", + "integrity": "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==", "license": "ISC", "peer": true, "bin": { @@ -3335,9 +3342,9 @@ } }, "node_modules/zod": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.2.tgz", - "integrity": "sha512-lh5RCAGFa1Cm2hjtNwLQhSs/AsqdWnTQaBER9fEwN/88pSh7KOtJavtBx/0VlkN/uFd61SwYmljLMDAsHlvzBQ==", + "version": "4.6.5", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.5.tgz", + "integrity": "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==", "license": "MIT", "funding": { "url": "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/sponsors/colinhacks" diff --git a/packages/surface/src/context.ts b/packages/surface/src/context.ts index e924ad6b9..a06e41859 100644 --- a/packages/surface/src/context.ts +++ b/packages/surface/src/context.ts @@ -12,7 +12,9 @@ export interface AgentResult { export interface AgentOptions { task: string; workspace?: string; + /** Overrides a named declaration's (or the flow/project default's) CLI for this step only. */ cli?: string; + /** Overrides a named declaration's model for this step only. No flow/project default exists for model. */ model?: string; /** Working directory for the CLI subprocess; defaults to the flow-runner's cwd. */ cwd?: string; diff --git a/packages/surface/src/flow.ts b/packages/surface/src/flow.ts index 10193c7d7..4df1e139c 100644 --- a/packages/surface/src/flow.ts +++ b/packages/surface/src/flow.ts @@ -4,6 +4,17 @@ type HelperTools = Partial>; import type { Ctx } from "./context.js"; import { webhook, type TriggerSource } from "./triggers.js"; +/** + * A reusable agent CLI/model pair, selectable by name from `f.agent(name, ...)`. + * Both fields are required so selecting a named agent can never inherit a + * host model — mirrors the kernel-spec dialect's `NamedAgentSpec` + * (`packages/sdk/src/spec.ts`), which this compiles into. + */ +export interface NamedAgentDeclaration { + cli: string; + model: string; +} + /** Optional escalation header; the empty header is the common case. */ export interface FlowHeader { /** Relative paths to reusable authored flows composed by this body. */ @@ -13,6 +24,7 @@ export interface FlowHeader { budget?: string | { tokens?: number; dollars?: number; wallclock?: string }; tools?: HelperTools & { relayfile?: string[]; mcp?: string[] }; workspace?: string; + agents?: Record; } export type FlowBody = (f: Ctx, input: Input) => Promise; @@ -28,6 +40,7 @@ export interface ReadonlyFlowHeader { mcp?: readonly string[]; }>; readonly workspace?: string; + readonly agents?: Readonly>>; } /** Immutable definition retained for the SDK's journal-backed runtime. */ @@ -66,7 +79,7 @@ export function flow( body?: FlowBody, ): FlowHandle { const flowBody = typeof headerOrBody === "function" ? headerOrBody : body; - const header = typeof headerOrBody === "function" ? {} : headerOrBody; + const header = snapshotHeader(typeof headerOrBody === "function" ? {} : headerOrBody, `unsupported_header: flow "${name}" header`); if (name.trim().length === 0) { throw new TypeError("flow name must not be empty"); @@ -149,6 +162,35 @@ function isStoredDefinition( && Object.isFrozen(value); } +/** Capture descriptor values once; validation and freezing only see inert data. */ +function snapshotHeader(value: unknown, at = "header", ancestors = new Set()): FlowHeader { + if (typeof value !== "object" || value === null) return value as FlowHeader; + if (ancestors.has(value)) throw new TypeError(`${at}: circular header data`); + ancestors.add(value); + try { + if (Array.isArray(value)) { + const entries = ownDataEntries(value as unknown as Record, at); + // Preserve sparse-array length so the later closed-header validation + // refuses holes instead of treating Array(1) as an empty declaration. + const length = entries.find(([key]) => key === "length")?.[1]; + const result: unknown[] = new Array(typeof length === "number" ? length : 0); + for (const [key, item] of entries) { + if (key === "length") continue; + Object.defineProperty(result, key, { + value: snapshotHeader(item, `${at}.${key}`, ancestors), enumerable: true, + }); + } + return result as unknown as FlowHeader; + } + assertHeaderObject(value, at); + return Object.fromEntries(ownDataEntries(value, at).map(([key, item]) => [ + key, snapshotHeader(item, `${at}.${key}`, ancestors), + ])); + } finally { + ancestors.delete(value); + } +} + function freezeHeader(header: FlowHeader): ReadonlyFlowHeader { const unknownFields = Object.keys(header).filter((field) => ![ "use", @@ -157,6 +199,7 @@ function freezeHeader(header: FlowHeader): ReadonlyFlowHeader { "budget", "tools", "workspace", + "agents", ].includes(field)); if (unknownFields.length > 0) { throw new TypeError(`flow header has unknown fields: ${unknownFields.join(", ")}`); @@ -176,6 +219,16 @@ function freezeHeader(header: FlowHeader): ReadonlyFlowHeader { ? {} : { mcp: Object.freeze([...header.tools.mcp]) }), }); + const agents = header.agents === undefined + ? undefined + : Object.freeze( + Object.fromEntries( + ownDataEntries(header.agents, "header.agents").map(([name, declaration]) => { + const record = declaration as NamedAgentDeclaration; + return [name, Object.freeze({ cli: record.cli, model: record.model })]; + }), + ), + ); return Object.freeze({ ...(header.use === undefined ? {} : { use: Object.freeze([...header.use]) }), ...(header.identity === undefined ? {} : { identity: header.identity }), @@ -183,6 +236,7 @@ function freezeHeader(header: FlowHeader): ReadonlyFlowHeader { ...(header.budget === undefined ? {} : { budget: typeof header.budget === "string" ? header.budget : Object.freeze({ ...header.budget }) }), ...(tools === undefined ? {} : { tools }), ...(header.workspace === undefined ? {} : { workspace: header.workspace }), + ...(agents === undefined ? {} : { agents }), }); } @@ -191,7 +245,7 @@ function assertFlowHeader(value: unknown, flowName: string): asserts value is Fl assertHeaderObject(value, at); assertKnownKeys( value, - ["use", "identity", "memory", "budget", "tools", "workspace"], + ["use", "identity", "memory", "budget", "tools", "workspace", "agents"], at, ); assertOptionalString(value, "identity", at); @@ -235,6 +289,48 @@ function assertFlowHeader(value: unknown, flowName: string): asserts value is Fl assertOptionalStringArray(value.tools, "relayfile", `${at}.tools`); assertOptionalStringArray(value.tools, "mcp", `${at}.tools`); } + + if (value.agents !== undefined) { + assertHeaderObject(value.agents, `${at}.agents`); + for (const [name, declaration] of ownDataEntries(value.agents, `${at}.agents`)) { + if (name !== name.trim() || name.length === 0) { + throw new TypeError(`${at}.agents: agent name ${JSON.stringify(name)} must be a non-empty, trimmed string`); + } + const declarationAt = `${at}.agents.${name}`; + assertHeaderObject(declaration, declarationAt); + assertKnownKeys(declaration, ["cli", "model"], declarationAt); + assertRequiredTrimmedString(declaration, "cli", declarationAt); + assertRequiredTrimmedString(declaration, "model", declarationAt); + } + } +} + +/** + * Reads every own property of `value` via its descriptor rather than + * `Object.entries`/property access, so an accessor (getter) property is + * REJECTED — never invoked — instead of being enumerated as if it were + * ordinary data. `Object.entries` would call the getter, and a stateful + * getter can legally answer validation with one value and a second, + * unvalidated read (e.g. during freezing) with a different one — the closed + * declaration contract must not depend on a property being well-behaved + * across two separate reads. + */ +function ownDataEntries( + value: Record, + at: string, +): [string, unknown][] { + const entries: [string, unknown][] = []; + for (const key of Reflect.ownKeys(value)) { + if (typeof key !== "string") { + throw new TypeError(`${at}: unknown field ${JSON.stringify(String(key))}`); + } + const descriptor = Object.getOwnPropertyDescriptor(value, key); + if (descriptor === undefined || !("value" in descriptor)) { + throw new TypeError(`${at}.${key}: expected a data property`); + } + entries.push([key, descriptor.value]); + } + return entries; } function assertHeaderObject( @@ -277,6 +373,27 @@ function assertOptionalString( } } +/** + * Requires a trimmed, non-empty string — not merely non-empty-after-trim. + * The SDK's own project-config schema only accepts already-trimmed model/cli + * strings (`readProjectConfig`, cli/check.ts); accepting untrimmed values + * here would let a flow author declare " claude " and have it validate at + * authoring time but fail later at f.agent preflight, moving a defect from + * authoring to execution instead of catching it up front. + */ +function assertRequiredTrimmedString( + value: Record, + key: string, + at: string, +): void { + if (typeof value[key] !== "string" || value[key].length === 0) { + throw new TypeError(`${at}.${key}: expected a non-empty string`); + } + if (value[key] !== value[key].trim()) { + throw new TypeError(`${at}.${key}: must not have leading or trailing whitespace`); + } +} + function assertOptionalBoolean( value: Record, key: string, diff --git a/packages/surface/src/index.ts b/packages/surface/src/index.ts index 619664e22..20f7046a4 100644 --- a/packages/surface/src/index.ts +++ b/packages/surface/src/index.ts @@ -28,6 +28,7 @@ export { type FlowHandle, type TriggeredFlowHandle, type FlowHeader, + type NamedAgentDeclaration, } from "./flow.js"; export { flowRunWritebackIdempotency, diff --git a/packages/surface/tests/flow.test.ts b/packages/surface/tests/flow.test.ts index b919104f0..8297a1d83 100644 --- a/packages/surface/tests/flow.test.ts +++ b/packages/surface/tests/flow.test.ts @@ -62,6 +62,103 @@ describe("flow", () => { expect(Object.isFrozen(getFlowDefinition(definition).header.tools?.mcp)).toBe(true); }); + it("accepts named agent declarations and freezes them deeply", () => { + const header = { + agents: { + reviewer: { cli: "claude", model: "claude-sonnet-4-6" }, + fixer: { cli: "codex", model: "gpt-5-codex" }, + }, + }; + const definition = flow( + "multi-agent", + header, + async () => undefined, + ); + + header.agents.reviewer.model = "mutated-after-definition"; + expect(getFlowDefinition(definition).header).toEqual({ + agents: { + reviewer: { cli: "claude", model: "claude-sonnet-4-6" }, + fixer: { cli: "codex", model: "gpt-5-codex" }, + }, + }); + const frozenAgents = getFlowDefinition(definition).header.agents; + expect(Object.isFrozen(frozenAgents)).toBe(true); + expect(Object.isFrozen(frozenAgents?.reviewer)).toBe(true); + }); + + it("rejects an accessor property on the agents map instead of invoking it", () => { + // A getter can legally answer validation with one value and a second, + // separate read (freezing) with a different one — reads must go through + // property descriptors, which reject accessors outright, rather than + // trusting a stateful getter to answer the same way twice. + let reads = 0; + const agents: Record = {}; + Object.defineProperty(agents, "reviewer", { + enumerable: true, + get() { + reads += 1; + return reads === 1 + ? { cli: "checked-cli", model: "model-a" } + : { cli: 42, model: "model-b", permissions: "write-all" }; + }, + }); + + expect(() => flow( + "getter-map", + { agents } as unknown as FlowHeader, + async (f) => f.done("success"), + )).toThrow("header.agents.reviewer: expected a data property"); + expect(reads).toBe(0); + }); + + it("validates and freezes the same captured proxy descriptor values", () => { + let reads = 0; + let descriptorReads = 0; + const declaration = new Proxy({ cli: "checked-cli", model: "model-a" }, { + get() { reads++; return 42; }, + getOwnPropertyDescriptor(target, key) { + descriptorReads++; + const descriptor = Reflect.getOwnPropertyDescriptor(target, key)!; + return { ...descriptor, value: descriptorReads <= 2 ? descriptor.value : 42 }; + }, + }); + const handle = flow("proxy-agent", { agents: { reviewer: declaration } }, async () => undefined); + expect(getFlowDefinition(handle).header.agents?.reviewer).toEqual({ + cli: "checked-cli", model: "model-a", + }); + expect(reads).toBe(0); + expect(descriptorReads).toBe(2); + }); + + it("requires named agent declaration values and names to be trimmed", () => { + const trimCases: { value: unknown; message: string }[] = [ + { + value: { agents: { reviewer: { cli: " claude", model: "m" } } }, + message: "header.agents.reviewer.cli: must not have leading or trailing whitespace", + }, + { + value: { agents: { reviewer: { cli: "claude ", model: "m" } } }, + message: "header.agents.reviewer.cli: must not have leading or trailing whitespace", + }, + { + value: { agents: { reviewer: { cli: "claude", model: " m" } } }, + message: "header.agents.reviewer.model: must not have leading or trailing whitespace", + }, + { + value: { agents: { " reviewer": { cli: "claude", model: "m" } } }, + message: 'header.agents: agent name " reviewer" must be a non-empty, trimmed string', + }, + ]; + for (const trimCase of trimCases) { + expect(() => flow( + "untrimmed-agent", + trimCase.value as FlowHeader, + async () => undefined, + )).toThrow(trimCase.message); + } + }); + it("validates raw header keys and nested values before cloning", () => { const invalidHeaders: { value: unknown; message: string }[] = [ { @@ -108,6 +205,30 @@ describe("flow", () => { value: { tools: { mcp: "github" } }, message: "header.tools.mcp: expected an array of strings", }, + { + value: { agents: { reviewer: { cli: "claude" } } }, + message: "header.agents.reviewer.model: expected a non-empty string", + }, + { + value: { agents: { reviewer: { model: "claude-sonnet-4-6" } } }, + message: "header.agents.reviewer.cli: expected a non-empty string", + }, + { + value: { agents: { reviewer: { cli: "claude", model: "" } } }, + message: "header.agents.reviewer.model: expected a non-empty string", + }, + { + value: { agents: { reviewer: { cli: "claude", model: "m", typo: true } } }, + message: 'header.agents.reviewer: unknown field "typo"', + }, + { + value: { agents: { reviewer: "claude" } }, + message: "header.agents.reviewer: expected an object", + }, + { + value: { agents: "reviewer" }, + message: "header.agents: expected an object", + }, { value: Object.create({ identity: "inherited" }), message: "header: expected a plain object",