@evident-ai/runner-cdk 3.4.1-dev.2f1b44b → 3.4.1-dev.38181ff

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,14 +1,9 @@
1
1
  #!/usr/bin/env bash
2
2
  #
3
3
  # Re-dial with the identity /run left behind. There is no step that could fetch
4
- # a fresh runner key, so the one from /run is what resumes. Also restarts the
5
- # session-DB replicator /suspend stopped before the snapshot (#812 WI-4):
6
- # litestream does not survive a suspend/resume freeze on this design (Q2) —
7
- # /suspend stops it and /resume starts a fresh one, the same pattern already
8
- # proven for the tunnel. The interval credential sync loop (#1868 WI-3) is the
9
- # same story one function over: /suspend stops it too, so a resumed VM that
10
- # never restarted it here would never sync credentials again for the rest of
11
- # its life.
4
+ # a fresh runner key, so the one from /run is what resumes. The CLI delegated by
5
+ # start_tunnel owns the credential loop and the session-DB replicator's
6
+ # post-resume start.
12
7
  set -euo pipefail
13
8
 
14
9
  # shellcheck source=./common.sh
@@ -42,14 +37,18 @@ fi
42
37
  read -r runner_key
43
38
  } <"${CONTEXT_FILE}"
44
39
 
40
+ # Restore the id so the resumed CLI can self-report and acknowledge any
41
+ # outstanding recycle request (#1906). Older images may not have this file.
42
+ if [ -s "${MICROVM_ID_FILE}" ]; then
43
+ MICROVM_ID="$(cat "${MICROVM_ID_FILE}")"
44
+ export MICROVM_ID
45
+ fi
46
+
45
47
  # Tolerant, never fatal: a resume that fails costs the user their whole
46
48
  # session, so a broken durable-state config degrades to "no session-DB
47
- # restart" rather than a failed resume (the same Q3 reasoning /run's
48
- # session-DB step follows). start_litestream's own guards (disabled/marker/
49
- # already-running) handle the rest — this needs no logic of its own.
49
+ # replication" rather than a failed resume. The CLI's own guards handle the
50
+ # rest this needs no logic of its own.
50
51
  load_state_config || warn "could not resolve durable-state config; the session DB will not resume replicating"
51
- start_litestream
52
- start_credential_sync
53
52
 
54
53
  # Diagnostic-only, unlike /run's gate: a failed resume costs the user their
55
54
  # whole session, so this never exits — it only converts a silent "resumed but
@@ -74,9 +73,8 @@ start_credential_sync
74
73
  # list, so a later edit cannot accidentally foreground the check again.
75
74
  ( check_runner_key "${runner_key}" "${api_url}" || true ) &
76
75
 
77
- # Not waited on: the CLI backs off and keeps retrying its own dial with no
78
- # attempt cap (apps/cli/src/lib/tunnel/connection.ts), and a control-plane
79
- # reclaim pass (the api-worker lifecycle cron) is what decides whether a
76
+ # Not waited on: the Evident CLI backs off and keeps retrying its own dial with no
77
+ # attempt cap, and Evident's control-plane reclaim pass is what decides whether a
80
78
  # runner that never reconnects gets suspended.
81
79
  start_tunnel "${runner_key}" "${api_url}" "${tunnel_url}"
82
80
 
@@ -28,7 +28,6 @@ cleanup() {
28
28
  stop_tunnel || warn "stop_tunnel failed while cleaning up"
29
29
  stop_opencode || warn "stop_opencode failed while cleaning up"
30
30
  kill_litestream || warn "kill_litestream failed while cleaning up"
31
- stop_credential_sync || warn "stop_credential_sync failed while cleaning up"
32
31
  }
33
32
  trap cleanup EXIT
34
33
 
@@ -52,20 +51,20 @@ regenerate_machine_id
52
51
  # reads it, and left behind for the hooks that flush back to the same prefix.
53
52
  printf '%s\n' "${state_prefix}" >"${STATE_PREFIX_FILE}"
54
53
 
55
- # 3 — start reading the litestream binary NOW so that read overlaps step 4,
56
- # which needs a different binary (node), instead of landing inside step 5, which
57
- # start_opencode has to wait for. Backgrounded and never waited on.
54
+ # 3 — start reading the litestream and aws CLI binaries NOW. The reads run in
55
+ # the background and overlap the CLI's own node startup and auth round trip,
56
+ # keeping their cold first touches off the path to the first credential fetch.
58
57
  prewarm_litestream
58
+ prewarm_aws_cli
59
59
 
60
- # 4 — credential stores, restored before anything that reads them starts. Fatal
61
- # only if persistence itself is unavailable (an unset bucket/prefix, or a broken
62
- # synchroniser bundle) — a VM with no model credentials yet still boots.
63
- restore_credentials
60
+ # 4 — export the durable-state config before anything that reads it. Fatal,
61
+ # because the CLI and the teardown hooks must agree on this runner's state prefix.
62
+ load_state_config || exit 1
64
63
 
65
64
  # 5 — does the runner key in this payload actually authenticate against
66
- # Evident? Checked here, before opencode or the tunnel spend any of this boot's
67
- # SIGTERM budget on a key that cannot work, and before anything has started
68
- # that cleanup would need to tear down. Fatal ONLY on contrary evidence
65
+ # Evident? Checked here, before the tunnel spends any of this boot's time on a key
66
+ # that cannot work, and before anything has started that cleanup would need to
67
+ # tear down. Fatal ONLY on contrary evidence
69
68
  # (check_runner_key's own contract, common.sh): a key the API actively rejects
70
69
  # cannot work regardless, and failing here costs ~5s against the ~10 minutes a
71
70
  # runner that can never connect would otherwise burn before the lifecycle cron
@@ -73,49 +72,14 @@ restore_credentials
73
72
  # it warns and this VM still boots.
74
73
  check_runner_key "${runner_key}" "${api_url}" || exit 1
75
74
 
76
- # 6 — the OpenCode session DB (#812 WI-2), restored before opencode opens it
77
- # the only place this can happen. Bare, like restore_credentials above:
78
- # restore_session_db never returns non-zero (Q3 — nothing about the session
79
- # DB may ever fail /run), so there is deliberately no `||` here to catch.
80
- restore_session_db
81
- log "session DB restore done ${SECONDS}s into the hook"
82
-
83
- # 7 — integrity-check the restored DB (#1868 WI-4), after the config file
84
- # exists (verify_session_db reads it) and before anything opens the DB —
85
- # the only window in which that's true. The ONE step in this sequence that
86
- # can still fail /run past the runner-key gate: a 34 means the corrupt DB's
87
- # separation/disposal could not be proven safe (see verify_session_db's own
88
- # comment for why that's a deliberate exception to "nothing about the
89
- # session DB may ever fail /run").
90
- verify_session_db || exit 1
91
-
92
- # 8 — apply the overlay before OpenCode resolves its project configuration.
93
- apply_runner_opencode_config
94
-
95
- # 9 — configure git after credentials are restored and before agent shells start.
96
- configure_github_access
97
-
98
- # 10 — opencode. Started here, not at build time: a warm process in the shared
99
- # snapshot would carry its installation id and database into every VM. Not
100
- # waited on: a slow opencode boot is not a reason to fail /run (the tunnel CLI
101
- # auto-starts opencode when it finds none healthy,
102
- # apps/cli/src/commands/ensure-opencode.ts, and the api-worker lifecycle cron
103
- # reclaims a runner that never comes online).
104
- start_opencode
105
-
106
- # 11 — begin replicating the session DB (#812 WI-3), now that opencode has
107
- # opened it and before any work can arrive over the tunnel. Bare, like
108
- # restore_session_db above: start_litestream never returns non-zero (every
109
- # guard inside it is its own `return 0`), so there is nothing here for
110
- # `set -e` to abort on.
111
- start_litestream
112
-
113
- # 11a — the interval credential sync (#1868 WI-3), matching ECS's own
114
- # post-litestream position. Bare for the same reason: every guard inside
115
- # start_credential_sync is its own `return 0`.
116
- start_credential_sync
75
+ # 6 — preserve the VM id across suspend/resume. The runtime always supplies it
76
+ # for a /run hook, and the subshell keeps the persisted value protected.
77
+ (
78
+ umask 077
79
+ printf '%s\n' "${MICROVM_ID}" >"${MICROVM_ID_FILE}"
80
+ )
117
81
 
118
- # 12 — the first per-VM identity on the wire. The subshell's umask makes the file
82
+ # 7 — the first per-VM identity on the wire. The subshell's umask makes the file
119
83
  # unreadable to anyone else from the moment it exists, before the key is in it.
120
84
  (
121
85
  umask 077
@@ -123,10 +87,9 @@ start_credential_sync
123
87
  >"${CONTEXT_FILE}"
124
88
  )
125
89
 
126
- # Not waited on: the CLI backs off and keeps retrying its own dial with no
127
- # attempt cap (apps/cli/src/lib/tunnel/connection.ts), and a control-plane
128
- # reclaim pass (the api-worker lifecycle cron) is what decides whether a
90
+ # Not waited on: the Evident CLI backs off and keeps retrying its own dial with no
91
+ # attempt cap, and Evident's control-plane reclaim pass is what decides whether a
129
92
  # runner that never connects gets suspended.
130
- start_tunnel "${runner_key}" "${api_url}" "${tunnel_url}"
93
+ start_tunnel "${runner_key}" "${api_url}" "${tunnel_url}" true true
131
94
 
132
95
  run_succeeded=true
@@ -8,11 +8,10 @@ set -euo pipefail
8
8
  # shellcheck source=./common.sh
9
9
  source "$(dirname "$0")/common.sh"
10
10
 
11
- # Stopped BEFORE the boundary flush (#1868 WI-3): the interval loop and this
12
- # flush must not race each other on the same credential stores.
13
- stop_credential_sync
14
- sync_credentials
15
- stop_tunnel
11
+ # load_state_config exports PERSISTENCE_BUCKET, which flush_session_db below
12
+ # requires; the CLI flush completes, or is proven absent, before teardown continues.
13
+ load_state_config || warn "could not resolve durable-state config; neither the credential flush nor the session-DB flush below can run"
14
+ stop_runner_and_flush_credentials
16
15
 
17
16
  # opencode is deliberately NOT stopped here (#812 WI-4) — it is inside the
18
17
  # snapshot and must resume with it — so a turn still in flight may not be
@@ -8,11 +8,10 @@ set -uo pipefail
8
8
  # shellcheck source=./common.sh
9
9
  source "$(dirname "$0")/common.sh"
10
10
 
11
- # Stopped BEFORE the boundary flush (#1868 WI-3): the interval loop and this
12
- # flush must not race each other on the same credential stores.
13
- stop_credential_sync
14
- sync_credentials
15
- stop_tunnel
11
+ # load_state_config exports PERSISTENCE_BUCKET, which flush_session_db below
12
+ # requires; the CLI flush completes, or is proven absent, before teardown continues.
13
+ load_state_config || warn "could not resolve durable-state config; neither the credential flush nor the session-DB flush below can run"
14
+ stop_runner_and_flush_credentials
16
15
 
17
16
  # Writers stopped BEFORE litestream's final sync (#812 WI-4, the
18
17
  # ordered-shutdown invariant, plan §6): otherwise litestream could snapshot
@@ -32,6 +31,6 @@ flush_session_db
32
31
  # this hook runs under `set -u` (above), so an unbound one aborts the script
33
32
  # AT THIS LINE, leaving the runner key sitting in CONTEXT_FILE on a VM that is
34
33
  # being torn down. That is the one thing this line exists to prevent.
35
- rm -f "${CONTEXT_FILE}" "${SESSION_DB_NO_REPLICATE_MARKER}"
34
+ rm -f "${CONTEXT_FILE}" "${SESSION_DB_NO_REPLICATE_MARKER}" "${CREDENTIAL_FLUSH_MARKER_FILE}"
36
35
 
37
36
  exit 0
@@ -60,8 +60,7 @@ class EvidentWaker extends constructs_1.Construct {
60
60
  // dist/waker-lambda/handler.js, so neither this package's published npm
61
61
  // artifact (which ships dist/ already built) nor a consuming app needs
62
62
  // that private workspace package, or esbuild, on synth's PATH. Requires
63
- // a build before synth when consuming this package via `workspace:*` —
64
- // see infrastructure/evident-runner's README "local gate".
63
+ // a build before synth when consuming this package via `workspace:*`.
65
64
  //
66
65
  // Resolved from the PACKAGE ROOT (../.. ), not `__dirname` directly:
67
66
  // `__dirname` is `dist/waker` at runtime (compiled) but `src/waker` when
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evident-ai/runner-cdk",
3
- "version": "3.4.1-dev.2f1b44b",
3
+ "version": "3.4.1-dev.38181ff",
4
4
  "description": "Reusable CDK constructs for an Evident agent runner: a single scale-to-zero Fargate runner (task + service + per-agent self-stop role + waker Lambda), or a per-session AWS Lambda MicroVM that boots on demand and suspends between messages. Instantiate once per agent from your own stack.",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -14,8 +14,8 @@
14
14
  "dist"
15
15
  ],
16
16
  "scripts": {
17
- "build": "pnpm run build-bundled-deps && ts-node scripts/build.ts",
18
- "//build-bundled-deps": "scripts/build.ts esbuild-bundles runner/docker-images/microvm/hook-server.ts, which imports @evident-ai/lambda-microvm-runtime by its built `main`. @evident-ai/lambda-microvm-cdk is a workspace:* devDependency whose types resolve through its own dist/, so tsc --project tsconfig.build.json cannot compile src/microvm/{shapes,construct}.ts without it. Chained into `build` rather than left to turbo's `^build` because publish-runner-cdk.yaml and infrastructure/evident-runner's build-runner-cdk-dep invoke `pnpm --filter @evident-ai/runner-cdk build` directly, which bypasses turbo entirely.",
17
+ "build": "pnpm run build-bundled-deps && ts-node scripts/build.ts && node -e 'const m=require(\"./package.json\").main; if (!require(\"node:fs\").existsSync(m)) { console.error(`build produced no ${m}`); process.exit(1); }'",
18
+ "//build-bundled-deps": "scripts/build.ts esbuild-bundles runner/docker-images/microvm/hook-server.ts, which imports @evident-ai/lambda-microvm-runtime by its built `main`. @evident-ai/lambda-microvm-cdk is a workspace:* devDependency whose types resolve through its own dist/, so tsc --project tsconfig.build.json cannot compile src/microvm/{shapes,construct}.ts without it. Chained into `build` rather than left to turbo's `^build` because callers outside turbo invoke `pnpm --filter @evident-ai/runner-cdk build` directly, which bypasses turbo entirely.",
19
19
  "build-bundled-deps": "pnpm --filter @evident-ai/lambda-microvm-cdk build && pnpm --filter @evident-ai/lambda-microvm-runtime build",
20
20
  "typecheck": "tsc --noEmit",
21
21
  "test": "node --test --require ts-node/register 'src/**/*.test.ts'",