@agenthippo-ai/agenthippo 0.2.3-dataplane → 1.110.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/agentpack/index.js +3 -1
- package/out/agentpack/paths.js +25 -2
- package/out/analytics/dashboard/localFleetAdapter.js +82 -2
- package/out/analytics/dashboard/provider.js +13 -2
- package/out/analytics/dashboard/webview/panel.js +3 -2
- package/out/analytics/identity.js +0 -0
- package/out/analytics/service.js +29 -25
- package/out/analytics/sessionEvents.js +100 -24
- package/out/auth/authBaseUrl.js +59 -0
- package/out/auth/bootstrapApply.js +9 -10
- package/out/auth/brokerClient.js +48 -14
- package/out/auth/provider.js +8 -21
- package/out/auth/sessionStore.js +3 -40
- package/out/auth/signInFlow.js +2 -1
- package/out/auth-broker/config.js +50 -5
- package/out/auth-broker/server.js +126 -26
- package/out/auth-broker/store.js +25 -24
- package/out/authority/warrant.js +206 -0
- package/out/branding.js +27 -1
- package/out/cli/commands/conformance.js +249 -0
- package/out/cli/commands/data-plane.js +151 -0
- package/out/cli/commands/deploy.js +1 -0
- package/out/cli/commands/gateway.js +4 -0
- package/out/cli/commands/pack.js +182 -3
- package/out/cli/commands/sandbox.js +351 -0
- package/out/cli/commands/serve.js +1145 -130
- package/out/cli/commands/store.js +12 -4
- package/out/cli/credential-context.js +17 -0
- package/out/cli/data-plane/gateway.js +28 -5
- package/out/cli/data-plane/index.js +18 -2
- package/out/cli/data-plane/registry.js +9 -0
- package/out/cli/data-plane/remote.js +106 -0
- package/out/cli/data-plane/sidecar.js +225 -0
- package/out/cli/deploy/layers/identity.js +88 -4
- package/out/cli/deploy-init.js +89 -24
- package/out/cli/deps.js +106 -2
- package/out/cli/idp-init.js +35 -0
- package/out/cli/index.js +171 -17
- package/out/cli/pack-install-ref.js +15 -0
- package/out/cli/pack-loader.js +8 -1
- package/out/cli/pack-runtime.js +61 -0
- package/out/cli/runtime.js +35 -1
- package/out/cli/serve-hardening.js +260 -14
- package/out/cli/utils.js +12 -1
- package/out/engines/base.js +32 -3
- package/out/engines/claude.js +33 -15
- package/out/engines/codex.js +111 -27
- package/out/engines/custom.js +15 -1
- package/out/evidence/sessionShip.js +643 -0
- package/out/extension.js +11 -2
- package/out/gateway/bridge.js +40 -2
- package/out/gateway/client.js +34 -1
- package/out/gateway/installSource.js +68 -0
- package/out/gateway/lifecycle.js +185 -51
- package/out/gateway/outboundPaths.js +4 -1
- package/out/gateway/secretsPreflight.js +90 -0
- package/out/gateway/service.js +7 -2
- package/out/headless/index.js +25 -2
- package/out/live-e2e/mcp-demo/server.mjs +32 -0
- package/out/mcp.js +30 -0
- package/out/pack/caps.js +271 -0
- package/out/pack/governor.js +326 -0
- package/out/pack/lease-schema.js +273 -0
- package/out/pack/lease.js +682 -0
- package/out/pack/reconcile.js +217 -0
- package/out/pack/signature.js +839 -0
- package/out/remote/handleRemoteChat.js +44 -3
- package/out/remote/remoteTrust.js +277 -0
- package/out/remote/serveClient.js +52 -5
- package/out/runner/host.js +141 -0
- package/out/runner/protocol.js +128 -0
- package/out/runner/remoteEngine.js +166 -0
- package/out/runtime/interfaces/edit-tracker.js +2 -0
- package/out/runtime/vscode/vscode-edit-tracker.js +2 -0
- package/out/sandbox/containerDriver.js +572 -0
- package/out/sandbox/processDriver.js +318 -0
- package/out/sandbox/sandboxRunner.js +303 -0
- package/out/sandbox/support.js +301 -0
- package/out/sandbox/types.js +55 -0
- package/out/scripts/agentide-litellm.sh +67 -5
- package/out/scripts/agentide_litellm_hooks_v1.py +266 -21
- package/out/scripts/analytics/agentide-litellm-otel.config.yaml +98 -132
- package/out/scripts/analytics/setup-analytics.ps1 +45 -14
- package/out/scripts/analytics/setup-analytics.sh +106 -15
- package/out/scripts/analytics/start-analytics.sh +86 -14
- package/out/scripts/gateway/setup-gateway.sh +72 -32
- package/out/shared/models.js +11 -110
- package/out/spotlight/install.js +7 -1
- package/out/store/index.js +41 -10
- package/out/sync.js +3 -1
- package/out/test/analytics.test.js +162 -23
- package/out/test/auth-baseurl.test.js +132 -0
- package/out/test/auth-bootstrapApply.test.js +27 -6
- package/out/test/auth-broker-hardening.test.js +227 -0
- package/out/test/auth-broker.test.js +59 -9
- package/out/test/branding.test.js +13 -0
- package/out/test/caps-governor.test.js +460 -0
- package/out/test/codex-new-file-survival.test.js +119 -0
- package/out/test/configuration-scopes.test.js +131 -0
- package/out/test/data-plane-engine-wiring.test.js +32 -0
- package/out/test/data-plane.test.js +380 -0
- package/out/test/deploy-init.test.js +47 -1
- package/out/test/gateway-client.test.js +28 -0
- package/out/test/gateway-install-source.test.js +111 -0
- package/out/test/gateway-lifecycle.test.js +1 -1
- package/out/test/gateway-outbound.test.js +7 -0
- package/out/test/gateway-secrets-preflight.test.js +127 -0
- package/out/test/gateway-session-routing.test.js +18 -0
- package/out/test/governor-hardening.test.js +222 -0
- package/out/test/headless-sync.test.js +22 -0
- package/out/test/lease-signature.test.js +357 -0
- package/out/test/lease-trust-root.test.js +273 -0
- package/out/test/lease-validation.test.js +224 -0
- package/out/test/lease.test.js +114 -0
- package/out/test/localFleetAdapter.test.js +91 -0
- package/out/test/mcp.test.js +27 -1
- package/out/test/pack-admission.test.js +216 -0
- package/out/test/pack-digest-parity.test.js +140 -0
- package/out/test/pack-reconcile.test.js +183 -0
- package/out/test/pack-signature-artifact.test.js +103 -0
- package/out/test/pack-signature.test.js +176 -0
- package/out/test/pack-trust-root.test.js +202 -0
- package/out/test/remote-trust.test.js +242 -0
- package/out/test/runner-protocol.test.js +249 -0
- package/out/test/sandbox-driver.test.js +553 -0
- package/out/test/sandbox-hardening.test.js +406 -0
- package/out/test/sandbox-runner.test.js +343 -0
- package/out/test/sandbox-scope.test.js +449 -0
- package/out/test/serve-cron-acceptance.test.js +370 -0
- package/out/test/serve-gateway-turn.test.js +122 -0
- package/out/test/serve-hardening.test.js +227 -15
- package/out/test/serveClient.test.js +6 -5
- package/out/test/sessionShip.test.js +618 -0
- package/out/test/spotlightPublish.test.js +28 -0
- package/out/test/store-install-safe.test.js +69 -0
- package/out/test/tls-bypass.test.js +90 -0
- package/out/test/warrant.test.js +138 -0
- package/out/utils.js +46 -9
- package/package.json +1 -1
package/out/cli/index.js
CHANGED
|
@@ -75,6 +75,7 @@ const commander_1 = require("commander");
|
|
|
75
75
|
const chat_1 = require("./commands/chat");
|
|
76
76
|
const ask_1 = require("./commands/ask");
|
|
77
77
|
const serve_1 = require("./commands/serve");
|
|
78
|
+
const data_plane_1 = require("./commands/data-plane");
|
|
78
79
|
const auth_broker_1 = require("./commands/auth-broker");
|
|
79
80
|
const deploy_1 = require("./commands/deploy");
|
|
80
81
|
const idp_1 = require("./commands/idp");
|
|
@@ -86,7 +87,36 @@ const probe_custom_engine_1 = require("./commands/probe-custom-engine");
|
|
|
86
87
|
const deps_1 = require("./deps");
|
|
87
88
|
const branding_1 = require("../branding");
|
|
88
89
|
const agentEnvFile_1 = require("../utils/agentEnvFile");
|
|
89
|
-
const
|
|
90
|
+
const caps_1 = require("../pack/caps");
|
|
91
|
+
/**
|
|
92
|
+
* `--max-concurrent-sandboxes`, honouring the deprecated `--max-concurrent-cells`.
|
|
93
|
+
*
|
|
94
|
+
* The current spelling wins outright when both are given; the old one is honoured with a loud
|
|
95
|
+
* notice rather than dropped, because dropping it would silently REMOVE a blast-radius cap from a
|
|
96
|
+
* deployment that believes it still has one.
|
|
97
|
+
*/
|
|
98
|
+
function resolveMaxConcurrentSandboxes(options) {
|
|
99
|
+
const current = parseInt(options.maxConcurrentSandboxes ?? '', 10);
|
|
100
|
+
if (Number.isFinite(current) && current > 0) {
|
|
101
|
+
return current;
|
|
102
|
+
}
|
|
103
|
+
const legacy = parseInt(options.maxConcurrentCells ?? '', 10);
|
|
104
|
+
if (Number.isFinite(legacy) && legacy > 0) {
|
|
105
|
+
console.warn(`Warning: --max-concurrent-cells is DEPRECATED; use ${caps_1.DEPRECATED_CAP_SPELLINGS['--max-concurrent-cells']}. Honouring it for now.`);
|
|
106
|
+
return legacy;
|
|
107
|
+
}
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
/** Single source: extensions/agentide/package.json (bundled into the compiled CLI by Bun). */
|
|
111
|
+
function resolveCliVersion() {
|
|
112
|
+
try {
|
|
113
|
+
return require('../../package.json').version ?? 'dev';
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return 'dev';
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const VERSION = resolveCliVersion();
|
|
90
120
|
const CLI_DESCRIPTION = [
|
|
91
121
|
`${branding_1.AGENTIDE_BRAND_NAME} CLI - Run agent packs from the command line`,
|
|
92
122
|
'',
|
|
@@ -102,14 +132,22 @@ const CLI_DESCRIPTION = [
|
|
|
102
132
|
' AGENTHIPPO_STORE_REPO Override store GitHub repo (owner/repo; CLI store commands)',
|
|
103
133
|
'',
|
|
104
134
|
'Quickstart:',
|
|
105
|
-
` AGENTHIPPO_LITELLM_BASE_URL="http://localhost:4000" AGENTHIPPO_LITELLM_API_KEY="dummy" AGENTHIPPO_DEFAULT_MODEL="litellm/gpt-5.
|
|
135
|
+
` AGENTHIPPO_LITELLM_BASE_URL="http://localhost:4000" AGENTHIPPO_LITELLM_API_KEY="dummy" AGENTHIPPO_DEFAULT_MODEL="litellm/gpt-5.5" AGENTHIPPO_DEFAULT_ENGINE="codex" ${branding_1.AGENTIDE_CLI_NAME} ask "Reply with exactly: CLI_TEST_OK"`,
|
|
106
136
|
'',
|
|
107
137
|
'Agent Packs:',
|
|
108
138
|
` Loaded from ~/${branding_1.AGENTIDE_DIR_NAME}/agents/<name>/ or absolute/relative paths.`,
|
|
109
139
|
' Pack permissions control filesystem access scope.',
|
|
110
140
|
].join('\n');
|
|
111
141
|
const program = new commander_1.Command();
|
|
112
|
-
program.hook('preAction', async () => {
|
|
142
|
+
program.hook('preAction', async (_thisCommand, actionCommand) => {
|
|
143
|
+
// A SANDBOX must never absorb ambient credentials. Every other command merges ~/.agent-hippo/.env
|
|
144
|
+
// as a convenience, but doing that inside a sandbox would silently hand the sandboxed engine
|
|
145
|
+
// every key on the host — defeating the driver's curated env allowlist (observed live: the
|
|
146
|
+
// cursor engine picked up CURSOR_API_KEY this way). A sandbox receives only what its driver
|
|
147
|
+
// explicitly passes.
|
|
148
|
+
if (actionCommand?.name() === 'sandbox') {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
113
151
|
await (0, agentEnvFile_1.mergeAgentHomeEnvIntoProcessEnv)();
|
|
114
152
|
});
|
|
115
153
|
program
|
|
@@ -126,7 +164,7 @@ program
|
|
|
126
164
|
.description('Start interactive chat session')
|
|
127
165
|
.option('-a, --agent <name>', 'Agent pack to use')
|
|
128
166
|
.option('-e, --engine <id>', 'Override pack engine: claude|codex|<custom-engine-id>')
|
|
129
|
-
.option('-m, --model <id>', 'Override pack model (e.g. litellm/gpt-
|
|
167
|
+
.option('-m, --model <id>', 'Override pack model (e.g. litellm/gpt-5.6-luna)')
|
|
130
168
|
.option('-w, --workspace <path>', 'Workspace directory', process.cwd())
|
|
131
169
|
.action(async (options) => {
|
|
132
170
|
const verbose = program.opts().verbose;
|
|
@@ -145,7 +183,7 @@ program
|
|
|
145
183
|
.description('Send a single message and exit')
|
|
146
184
|
.option('-a, --agent <name>', 'Agent pack to use')
|
|
147
185
|
.option('-e, --engine <id>', 'Override pack engine: claude|codex|<custom-engine-id>')
|
|
148
|
-
.option('-m, --model <id>', 'Override pack model (e.g. litellm/gpt-
|
|
186
|
+
.option('-m, --model <id>', 'Override pack model (e.g. litellm/gpt-5.6-luna)')
|
|
149
187
|
.option('-w, --workspace <path>', 'Workspace directory', process.cwd())
|
|
150
188
|
.option('-f, --file <path>', 'Include file content in context')
|
|
151
189
|
.option('--json', 'Output as JSON')
|
|
@@ -176,7 +214,7 @@ program
|
|
|
176
214
|
.requiredOption('--prompt <message>', 'Prompt text')
|
|
177
215
|
.option('--pack <name>', 'Agent pack to use')
|
|
178
216
|
.option('-e, --engine <id>', 'Override pack engine: claude|codex|<custom-engine-id>')
|
|
179
|
-
.option('-m, --model <id>', 'Override pack model (e.g. litellm/gpt-
|
|
217
|
+
.option('-m, --model <id>', 'Override pack model (e.g. litellm/gpt-5.6-luna)')
|
|
180
218
|
.option('-w, --workspace <path>', 'Workspace directory', process.cwd())
|
|
181
219
|
.option('--session-id <id>', 'Optional session id for the caller')
|
|
182
220
|
.option('--system-prompt <text>', 'Optional system prompt override')
|
|
@@ -204,7 +242,7 @@ program
|
|
|
204
242
|
.description('Start HTTP server')
|
|
205
243
|
.requiredOption('-a, --agent <name>', 'Agent pack to use (required)')
|
|
206
244
|
.option('-e, --engine <id>', 'Override pack engine: claude|codex|<custom-engine-id>')
|
|
207
|
-
.option('-m, --model <id>', 'Override pack model (e.g. litellm/gpt-
|
|
245
|
+
.option('-m, --model <id>', 'Override pack model (e.g. litellm/gpt-5.6-luna)')
|
|
208
246
|
.option('-p, --port <port>', 'Port to listen on', '3000')
|
|
209
247
|
.option('--host <address>', 'Address to bind the HTTP server (default 127.0.0.1; use 0.0.0.0 in containers/behind a proxy)')
|
|
210
248
|
.option('-w, --workspace <path>', 'Default workspace directory', process.cwd())
|
|
@@ -213,18 +251,56 @@ program
|
|
|
213
251
|
.option('--state-dir <path>', 'Persist session state here (honors AGENTHIPPO_HOME; replaces the tmpdir default)')
|
|
214
252
|
.option('--max-turns-per-session <n>', 'Cap turns per session (abuse/runaway guard); 0 = unlimited', '0')
|
|
215
253
|
.option('--www-dir <path>', 'Serve deploy/www/index.html at GET / (static browser chat UI)')
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
.option('--
|
|
220
|
-
.option('--
|
|
254
|
+
.option('--sandbox <mode>', 'Run each session\'s engine in a sandbox: process (tier 0, no boundary) or container (tier 2, one disposable container per session with only its declared routes). Unset = in-process (default, unchanged)')
|
|
255
|
+
.option('--sandbox-image <ref>', '--sandbox container: sandbox image pinned BY DIGEST (image@sha256:...)')
|
|
256
|
+
.option('--sandbox-scope <scope>', '--sandbox: what one sandbox holds — session (default, one per conversation) or user (one per verified user on this instance, so their sessions share a container, workspace, TTL and kill). Unidentified callers get one each either way')
|
|
257
|
+
.option('--pull-packs', 'Fetch the version the lease pins from the store instead of only refusing others. Always verifies the signature; a fetch or verification failure keeps serving the current version')
|
|
258
|
+
.option('--require-signed-pack', 'Refuse to start a sandbox unless the pack is signed by a trusted key (the lease may also require this; neither side can waive the other). Now the DEFAULT — kept so existing invocations still mean something')
|
|
259
|
+
.option('--allow-unsigned-pack', 'LOCAL DEVELOPMENT ONLY: run a pack this instance cannot verify. Signature checking is required by default; this disables it and warns loudly on every boot. A lease that sets requireSignedPack still overrides it')
|
|
260
|
+
.option('--require-signed-lease', 'Refuse to act on a lease that is not signed (ES256 compact JWS) by a trusted key. Tighten-only with lease.requireSignedLease, and sticky once a signed lease has been seen')
|
|
261
|
+
.option('--lease-max-staleness <seconds>', 'Refuse turns after this long with no successful lease refresh, so network denial cannot hold a revoked fleet open (default 86400; 0 disables)')
|
|
262
|
+
.option('--sandbox-ttl <seconds>', '--sandbox: reap an idle sandbox after N seconds; the next message recreates it and resumes the session', '1800')
|
|
263
|
+
.option('--shared-workspace', '--sandbox container: mount ONE workspace into every session\'s sandbox (default: one per session). Sessions can then read and overwrite each other\'s files — only for a single trust domain')
|
|
264
|
+
// Control plane (serve-v4-clean §5). Caps are enforced by revoking a sandbox's routes, so they
|
|
265
|
+
// need --sandbox; a lease may impose tighter values than these, and never looser ones.
|
|
266
|
+
.option('--max-session-seconds <seconds>', '--sandbox: cap one session\'s wall-clock lifetime; a breach revokes or destroys its sandbox')
|
|
267
|
+
.option('--max-concurrent-sandboxes <n>', '--sandbox: cap live sandboxes on this instance (blast radius)')
|
|
268
|
+
// Deprecated pre-rename spelling. Hidden so it does not advertise itself, honoured so an
|
|
269
|
+
// existing deployment does not silently lose its cap, and loud so it gets migrated.
|
|
270
|
+
.addOption(new commander_1.Option('--max-concurrent-cells <n>', 'DEPRECATED alias for --max-concurrent-sandboxes').hideHelp())
|
|
271
|
+
.option('--cap-on-breach <action>', '--sandbox: what a cap breach does — revoke-inference (default) or destroy-sandbox. An unrecognised value fails CLOSED to destroy-sandbox')
|
|
272
|
+
.option('--lease-poll <seconds>', '--sandbox: how often to re-check the lease, so a kill switch stops RUNNING sandboxes. Worst-case kill latency is 2x this (poll + cache); an admin refresh is immediate', '30')
|
|
221
273
|
.action(async (options) => {
|
|
222
|
-
if (options.production || options.corsOrigin) {
|
|
223
|
-
console.warn('[serve] --production/--cors-origin are deprecated no-ops; auth and CORS are enforced by the deploy edge proxy.');
|
|
224
|
-
}
|
|
225
274
|
const verbose = program.opts().verbose;
|
|
226
275
|
await (0, deps_1.ensureDeps)(verbose);
|
|
227
276
|
await (0, serve_1.serveCommand)({
|
|
277
|
+
sandbox: options.sandbox === 'container' ? 'container' : options.sandbox === 'process' ? 'process' : undefined,
|
|
278
|
+
sandboxImage: options.sandboxImage,
|
|
279
|
+
// Anything but an explicit `user` means session scope: an unrecognised value must not
|
|
280
|
+
// quietly widen what a sandbox holds.
|
|
281
|
+
sandboxScope: options.sandboxScope === 'user' ? 'user' : 'session',
|
|
282
|
+
requireSignedPack: Boolean(options.requireSignedPack),
|
|
283
|
+
allowUnsignedPack: Boolean(options.allowUnsignedPack),
|
|
284
|
+
requireSignedLease: Boolean(options.requireSignedLease),
|
|
285
|
+
leaseMaxStalenessSeconds: options.leaseMaxStaleness !== undefined
|
|
286
|
+
? (Number.isFinite(Number(options.leaseMaxStaleness)) ? Number(options.leaseMaxStaleness) : undefined)
|
|
287
|
+
: undefined,
|
|
288
|
+
pullPacks: Boolean(options.pullPacks),
|
|
289
|
+
sandboxTtlSeconds: parseInt(options.sandboxTtl, 10) || undefined,
|
|
290
|
+
sharedWorkspace: Boolean(options.sharedWorkspace),
|
|
291
|
+
maxSessionSeconds: parseInt(options.maxSessionSeconds, 10) || undefined,
|
|
292
|
+
maxConcurrentSandboxes: resolveMaxConcurrentSandboxes(options),
|
|
293
|
+
// Shared with the env path (pack/caps.ts) so a value that is valid in one is valid in the
|
|
294
|
+
// other. An unrecognised action no longer becomes `undefined` — which mergeCaps then
|
|
295
|
+
// defaulted to the WEAKER revoke-inference, silently downgrading what the operator typed.
|
|
296
|
+
capOnBreach: (() => {
|
|
297
|
+
const r = (0, caps_1.resolveOnBreach)(options.capOnBreach, '--cap-on-breach');
|
|
298
|
+
for (const w of r.warnings) {
|
|
299
|
+
console.warn(`Warning: ${w}`);
|
|
300
|
+
}
|
|
301
|
+
return r.action;
|
|
302
|
+
})(),
|
|
303
|
+
leasePollSeconds: parseInt(options.leasePoll, 10) || undefined,
|
|
228
304
|
agent: options.agent,
|
|
229
305
|
engine: options.engine,
|
|
230
306
|
model: options.model,
|
|
@@ -239,6 +315,70 @@ program
|
|
|
239
315
|
verbose,
|
|
240
316
|
});
|
|
241
317
|
});
|
|
318
|
+
// Conformance — the deployment proves its own claims (serve-v4-clean.md §11 step 5).
|
|
319
|
+
program
|
|
320
|
+
.command('conformance')
|
|
321
|
+
.description('Probe a deployment against its own claims: credential confinement, isolation tier, cost-cap binding, warrant integrity, rollback/kill switch. Exit 1 on any failure, 2 when no deployment was probed (--sandbox/--tier omitted) — only exit 0 means a real deployment was measured.')
|
|
322
|
+
.option('--sandbox <id>', 'Running sandbox container id/name — required to probe INSIDE a sandbox')
|
|
323
|
+
.option('--tier <n>', 'Declared isolation tier (0-3); probed, not trusted')
|
|
324
|
+
.option('--runtime <bin>', 'Container runtime binary', 'docker')
|
|
325
|
+
.option('--json', 'Emit JSON')
|
|
326
|
+
.action(async (options) => {
|
|
327
|
+
const { conformanceCommand } = await import('./commands/conformance.js');
|
|
328
|
+
const code = await conformanceCommand({
|
|
329
|
+
sandbox: options.sandbox,
|
|
330
|
+
tier: options.tier === undefined ? undefined : parseInt(options.tier, 10),
|
|
331
|
+
runtime: options.runtime,
|
|
332
|
+
json: Boolean(options.json),
|
|
333
|
+
});
|
|
334
|
+
process.exitCode = code;
|
|
335
|
+
});
|
|
336
|
+
// Sandbox runner — runs INSIDE a sandbox; holds no credential (serve-v4-clean.md §3).
|
|
337
|
+
program
|
|
338
|
+
.command('sandbox')
|
|
339
|
+
.description('Run the in-sandbox engine host (sandbox runner). Speaks the frame protocol to serve over a unix socket or stdio; holds no data or model credential.')
|
|
340
|
+
.requiredOption('-a, --agent <path>', 'Pack directory mounted into the sandbox')
|
|
341
|
+
.option('--socket <path>', 'Unix socket to listen on (container driver)')
|
|
342
|
+
.option('--stdio', 'Speak the protocol on stdin/stdout instead (process driver)')
|
|
343
|
+
// No default: it must fall back to <state-dir>/workspace inside the command, or a
|
|
344
|
+
// host-launched sandbox (where --state-dir is a real host path) inherits the container-image
|
|
345
|
+
// path and dies on mkdir.
|
|
346
|
+
.option('-w, --workspace <path>', 'Workspace directory (default: <state-dir>/workspace)')
|
|
347
|
+
.option('--state-dir <path>', 'State directory', '/state')
|
|
348
|
+
.option('--sandbox-id <id>', 'Sandbox id (joins evidence records)')
|
|
349
|
+
.action(async (options) => {
|
|
350
|
+
const { sandboxCommand } = await import('./commands/sandbox.js');
|
|
351
|
+
await sandboxCommand({
|
|
352
|
+
agent: options.agent,
|
|
353
|
+
socket: options.socket,
|
|
354
|
+
stdio: Boolean(options.stdio),
|
|
355
|
+
workspace: options.workspace,
|
|
356
|
+
stateDir: options.stateDir,
|
|
357
|
+
sandboxId: options.sandboxId,
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
program
|
|
361
|
+
.command('data-plane')
|
|
362
|
+
.description('Run the connector as a sidecar (holds the backend base credential; the agent container does not)')
|
|
363
|
+
.requiredOption('-a, --agent <name>', 'Agent pack (reads spec.dataPlane connector + provider)')
|
|
364
|
+
.option('-w, --workspace <path>', 'Workspace directory', process.cwd())
|
|
365
|
+
.option('--gateway-port <port>', 'Port the engine dials (nonce-auth MCP)', '9090')
|
|
366
|
+
.option('--control-port <port>', 'Port serve dials to register turns (bearer-auth)', '9091')
|
|
367
|
+
.option('--host <address>', 'Bind address (default 0.0.0.0 for the pod network)')
|
|
368
|
+
.option('--state-dir <path>', 'Where data-plane audit events are written. Same precedence as serve: --state-dir, then AGENTHIPPO_HOME, then tmpdir')
|
|
369
|
+
.action(async (options) => {
|
|
370
|
+
const verbose = program.opts().verbose;
|
|
371
|
+
await (0, deps_1.ensureDeps)(verbose);
|
|
372
|
+
await (0, data_plane_1.dataPlaneSidecarCommand)({
|
|
373
|
+
agent: options.agent,
|
|
374
|
+
workspace: options.workspace,
|
|
375
|
+
gatewayPort: parseInt(options.gatewayPort, 10),
|
|
376
|
+
controlPort: parseInt(options.controlPort, 10),
|
|
377
|
+
host: options.host,
|
|
378
|
+
stateDir: options.stateDir,
|
|
379
|
+
verbose,
|
|
380
|
+
});
|
|
381
|
+
});
|
|
242
382
|
program
|
|
243
383
|
.command('auth-broker')
|
|
244
384
|
.description('Start the AgentHippo auth broker (OAuth + IDE bootstrap API)')
|
|
@@ -262,6 +402,7 @@ program
|
|
|
262
402
|
.option('--idp-url <url>', 'edge-fed: central IdP base URL browsers redirect to for login (e.g. https://auth.agenthippo.ai)', 'https://auth.agenthippo.ai')
|
|
263
403
|
.option('--cookie-domain <domain>', 'edge-fed: parent domain the shared auth cookie is scoped to (e.g. .agenthippo.ai)', '.agenthippo.ai')
|
|
264
404
|
.option('--colocate', 'edge-fed + vm/compose only: ride the org IdP\'s already-running Caddy + auth-broker instead of shipping this agent\'s own (PLAN/deploy.md §4 state 4)')
|
|
405
|
+
.option('--edge-turn-connector <addr>', 'EDGE turn registration (P1 fix): connector sidecar control-API address (e.g. mypack-connector:9091); the generated route registers each chat turn via forward_auth /edge/turn and copies X-Agent-Turn-Nonce — the agent container never holds the register token')
|
|
265
406
|
.option('--model-routing <mode>', 'Cloud Run + edge-fed: sidecar (in-pod LiteLLM+OTEL, default) or gateway (agent → gateway.agenthippo.ai directly, lean)')
|
|
266
407
|
.option('--force', 'Overwrite existing files')
|
|
267
408
|
.action(async (action, pack, options) => {
|
|
@@ -283,6 +424,7 @@ program
|
|
|
283
424
|
cookieDomain: options.cookieDomain,
|
|
284
425
|
colocate: Boolean(options.colocate),
|
|
285
426
|
modelRouting,
|
|
427
|
+
edgeTurnConnector: options.edgeTurnConnector,
|
|
286
428
|
});
|
|
287
429
|
});
|
|
288
430
|
// IdP command - generate the central IdP stack (org identity infra, set up once)
|
|
@@ -304,16 +446,28 @@ program
|
|
|
304
446
|
// Pack command - pack management
|
|
305
447
|
program
|
|
306
448
|
.command('pack <action>')
|
|
307
|
-
.description('Manage agent packs (list, validate, install)')
|
|
449
|
+
.description('Manage agent packs (list, validate, sign, verify, digest, keygen, install)')
|
|
308
450
|
.argument('[name]', 'Pack name or path')
|
|
309
|
-
.option('-o, --output <path>', 'Output path for export')
|
|
451
|
+
.option('-o, --output <path>', 'Output path for export, or for a generated signing key')
|
|
452
|
+
.option('--key <path>', 'pack sign: PEM file holding the ES256 private key')
|
|
453
|
+
.option('--kid <id>', 'pack sign/keygen: key id published in JWKS so verifiers can find the public half')
|
|
454
|
+
.option('--keys <path>', 'pack verify: JSON file of kid -> public PEM')
|
|
455
|
+
.option('--jwks <url>', 'pack verify: JWKS endpoint to fetch public halves from')
|
|
456
|
+
.option('--to <dir>', 'pack install: destination folder (flat layout for serve/deploy)')
|
|
457
|
+
.option('-f, --force', 'pack install: overwrite an existing folder')
|
|
310
458
|
.option('-w, --workspace <path>', 'Workspace directory for local packs', process.cwd())
|
|
311
459
|
.action(async (action, name, options) => {
|
|
312
460
|
await (0, pack_1.packCommand)({
|
|
313
461
|
action,
|
|
314
462
|
name,
|
|
315
463
|
output: options.output,
|
|
464
|
+
to: options.to,
|
|
465
|
+
force: options.force,
|
|
316
466
|
workspace: options.workspace,
|
|
467
|
+
key: options.key,
|
|
468
|
+
kid: options.kid,
|
|
469
|
+
keys: options.keys,
|
|
470
|
+
jwks: options.jwks,
|
|
317
471
|
verbose: program.opts().verbose,
|
|
318
472
|
});
|
|
319
473
|
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*---------------------------------------------------------------------------------------------
|
|
3
|
+
* Copyright (c) AgentHippo.ai. All rights reserved.
|
|
4
|
+
*--------------------------------------------------------------------------------------------*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.parsePackInstallRef = parsePackInstallRef;
|
|
7
|
+
/** Parse `slug` or `slug@version` from `pack install` positional arg. */
|
|
8
|
+
function parsePackInstallRef(name) {
|
|
9
|
+
const trimmed = name.trim();
|
|
10
|
+
const at = trimmed.lastIndexOf('@');
|
|
11
|
+
if (at > 0) {
|
|
12
|
+
return { slug: trimmed.slice(0, at), version: trimmed.slice(at + 1) || undefined };
|
|
13
|
+
}
|
|
14
|
+
return { slug: trimmed };
|
|
15
|
+
}
|
package/out/cli/pack-loader.js
CHANGED
|
@@ -373,7 +373,14 @@ function getApiConfig() {
|
|
|
373
373
|
const litellmBaseUrl = getEnvVar('AGENTHIPPO_LITELLM_BASE_URL', 'AGENTIDE_LITELLM_BASE_URL');
|
|
374
374
|
const anthropicApiKey = process.env.ANTHROPIC_API_KEY || process.env.CLAUDE_API_KEY;
|
|
375
375
|
const openaiApiKey = process.env.OPENAI_API_KEY;
|
|
376
|
-
|
|
376
|
+
// The URL alone defines the trust domain. When a LiteLLM/gateway URL is configured, ALL
|
|
377
|
+
// engine traffic routes there — so it must authenticate with the LiteLLM key (or
|
|
378
|
+
// resolveApiRouting's 'sk-dummy' fallback), NEVER a provider secret, and must carry the
|
|
379
|
+
// analytics metadata headers. Requiring key+URL here once created a silent half-state:
|
|
380
|
+
// URL-only routed every call to the gateway with the REAL provider key as auth and no
|
|
381
|
+
// metadata at all (verified live 2026-07-27 with a header-capture stub). A missing key now
|
|
382
|
+
// yields a clean 401 against a strict gateway instead of a leaked credential.
|
|
383
|
+
const useLiteLLM = Boolean(litellmBaseUrl);
|
|
377
384
|
return {
|
|
378
385
|
// Kept only for the "is any credential present at all?" boot check; routing never uses it.
|
|
379
386
|
apiKey: litellmApiKey || anthropicApiKey || openaiApiKey,
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*---------------------------------------------------------------------------------------------
|
|
3
|
+
* Copyright (c) AgentHippo.ai. All rights reserved.
|
|
4
|
+
*--------------------------------------------------------------------------------------------*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createPackRunner = createPackRunner;
|
|
7
|
+
/**
|
|
8
|
+
* One way to stand up a `HeadlessRunner` for an agent pack.
|
|
9
|
+
*
|
|
10
|
+
* `serve` and `sandbox` run the SAME engines against the SAME pack; the only difference is where
|
|
11
|
+
* the process sits. Every time the sandbox re-derived a piece of that setup itself it drifted from
|
|
12
|
+
* serve and broke live (the hand-rolled agent.yaml parser, the engine executable paths, the
|
|
13
|
+
* skipped `ensureDeps`). So the wiring lives here once and both call it: engine discovery,
|
|
14
|
+
* bundled prompts, the child env patch, and the credential shape.
|
|
15
|
+
*
|
|
16
|
+
* What is NOT here is deliberately not shared: credentials are the caller's business. serve
|
|
17
|
+
* holds provider keys; a sandbox only ever gets a per-sandbox, budget-capped model token that arrives
|
|
18
|
+
* on the run frame. Both express that through `credentials`, and nothing in this module can
|
|
19
|
+
* widen it.
|
|
20
|
+
*/
|
|
21
|
+
const headless_1 = require("../headless");
|
|
22
|
+
const runtime_1 = require("./runtime");
|
|
23
|
+
const utils_1 = require("./utils");
|
|
24
|
+
/**
|
|
25
|
+
* Build the runner. Engine executables are resolved here rather than by the SDKs themselves:
|
|
26
|
+
* the engines ship WITH agenthippo, and a runtime image does not carry the SDK's own bundled
|
|
27
|
+
* cli.js — without these paths every turn inside a sandbox fails with "executable not found".
|
|
28
|
+
*/
|
|
29
|
+
function createPackRunner(options) {
|
|
30
|
+
return (0, headless_1.createHeadlessRunner)({
|
|
31
|
+
storagePath: options.storagePath,
|
|
32
|
+
analyticsSessionsDir: options.analyticsSessionsDir,
|
|
33
|
+
packId: options.packId,
|
|
34
|
+
packVersion: options.packVersion,
|
|
35
|
+
provenance: options.provenance,
|
|
36
|
+
bundledPromptsDir: (0, utils_1.findBundledBasePromptsDir)(),
|
|
37
|
+
apiKey: options.credentials.apiKey,
|
|
38
|
+
baseUrl: options.credentials.baseUrl,
|
|
39
|
+
useLiteLLM: options.credentials.useLiteLLM,
|
|
40
|
+
litellmApiKey: options.credentials.litellmApiKey,
|
|
41
|
+
openaiApiKey: options.credentials.openaiApiKey,
|
|
42
|
+
anthropicApiKey: options.credentials.anthropicApiKey,
|
|
43
|
+
logger: options.logger,
|
|
44
|
+
freshState: options.freshState ?? false,
|
|
45
|
+
claudeCodeExecutablePath: (0, utils_1.findClaudeCodePath)(),
|
|
46
|
+
codexExecutablePath: (0, utils_1.findCodexPath)(),
|
|
47
|
+
permissionHandler: options.permissionHandler,
|
|
48
|
+
// Gives engine children AGENTHIPPO_WORKSPACE_ROOT / AGENTHIPPO_ACTIVE_AGENT and puts the
|
|
49
|
+
// agenthippo binary on their PATH — without it a pack skill that shells out to the CLI
|
|
50
|
+
// cannot find it, which is exactly the kind of silent difference a sandbox must not have.
|
|
51
|
+
cliEnvPatch: (0, runtime_1.buildCliEnvPatch)({
|
|
52
|
+
cliPath: process.execPath,
|
|
53
|
+
workspaceRoot: options.workspaceRoot,
|
|
54
|
+
agentId: options.packId,
|
|
55
|
+
chatSessionId: options.chatSessionId,
|
|
56
|
+
baseEnv: process.env,
|
|
57
|
+
omitGatewayToken: options.omitGatewayToken,
|
|
58
|
+
}),
|
|
59
|
+
...(options.claudeCheapModel ? { engineSettings: { claudeCheapModel: options.claudeCheapModel } } : {}),
|
|
60
|
+
});
|
|
61
|
+
}
|
package/out/cli/runtime.js
CHANGED
|
@@ -42,6 +42,8 @@ exports.getBundledToolsHealth = getBundledToolsHealth;
|
|
|
42
42
|
exports.getCliHealth = getCliHealth;
|
|
43
43
|
exports.buildSessionEnvPatch = buildSessionEnvPatch;
|
|
44
44
|
exports.buildCliEnvPatch = buildCliEnvPatch;
|
|
45
|
+
exports.readProvenanceEnvFromProcess = readProvenanceEnvFromProcess;
|
|
46
|
+
exports.buildProvenanceEnv = buildProvenanceEnv;
|
|
45
47
|
exports.runCli = runCli;
|
|
46
48
|
const fs = __importStar(require("fs"));
|
|
47
49
|
const os = __importStar(require("os"));
|
|
@@ -320,7 +322,7 @@ function buildCliEnvPatch(args) {
|
|
|
320
322
|
// nonce. AGENTHIPPO_EGRESS_URL in the process env is that signal — with it set, never bake
|
|
321
323
|
// the token into a child env (this closes §7.4 gap 1 for custom engines; codex/claude are
|
|
322
324
|
// covered by serve stripping the token from its own process.env).
|
|
323
|
-
if (!process.env.AGENTHIPPO_EGRESS_URL?.trim()) {
|
|
325
|
+
if (!args.omitGatewayToken && !process.env.AGENTHIPPO_EGRESS_URL?.trim()) {
|
|
324
326
|
const gatewayToken = readOpenClawGatewayToken(args.baseEnv);
|
|
325
327
|
if (gatewayToken) {
|
|
326
328
|
env[exports.OPENCLAW_GATEWAY_TOKEN_ENV] = gatewayToken;
|
|
@@ -368,6 +370,38 @@ function buildCliEnvPatch(args) {
|
|
|
368
370
|
}
|
|
369
371
|
return env;
|
|
370
372
|
}
|
|
373
|
+
/** Read provenance stamp env vars injected into sandbox children by serve. */
|
|
374
|
+
function readProvenanceEnvFromProcess() {
|
|
375
|
+
const pick = (key) => process.env[key]?.trim() || undefined;
|
|
376
|
+
const tierRaw = pick('AGENTIDE_ANALYTICS_TIER');
|
|
377
|
+
const tier = tierRaw !== undefined ? Number(tierRaw) : undefined;
|
|
378
|
+
return {
|
|
379
|
+
policyVersion: pick('AGENTIDE_ANALYTICS_POLICY_VERSION'),
|
|
380
|
+
packDigest: pick('AGENTIDE_ANALYTICS_PACK_DIGEST'),
|
|
381
|
+
engineVersion: pick('AGENTIDE_ANALYTICS_ENGINE_VERSION'),
|
|
382
|
+
tier: tier !== undefined && Number.isFinite(tier) ? tier : undefined,
|
|
383
|
+
instanceId: pick('AGENTIDE_ANALYTICS_INSTANCE_ID'),
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
function buildProvenanceEnv(stamp) {
|
|
387
|
+
const env = {};
|
|
388
|
+
if (stamp.policyVersion?.trim()) {
|
|
389
|
+
env.AGENTIDE_ANALYTICS_POLICY_VERSION = stamp.policyVersion.trim();
|
|
390
|
+
}
|
|
391
|
+
if (stamp.packDigest?.trim()) {
|
|
392
|
+
env.AGENTIDE_ANALYTICS_PACK_DIGEST = stamp.packDigest.trim();
|
|
393
|
+
}
|
|
394
|
+
if (stamp.engineVersion?.trim()) {
|
|
395
|
+
env.AGENTIDE_ANALYTICS_ENGINE_VERSION = stamp.engineVersion.trim();
|
|
396
|
+
}
|
|
397
|
+
if (stamp.tier !== undefined) {
|
|
398
|
+
env.AGENTIDE_ANALYTICS_TIER = String(stamp.tier);
|
|
399
|
+
}
|
|
400
|
+
if (stamp.instanceId?.trim()) {
|
|
401
|
+
env.AGENTIDE_ANALYTICS_INSTANCE_ID = stamp.instanceId.trim();
|
|
402
|
+
}
|
|
403
|
+
return env;
|
|
404
|
+
}
|
|
371
405
|
async function runCli(binaryPath, args, opts) {
|
|
372
406
|
return await new Promise((resolve, reject) => {
|
|
373
407
|
const child = (0, child_process_1.spawn)(binaryPath, args, {
|