@integrity-labs/agt-cli 0.28.725 → 0.28.726

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin/agt.js CHANGED
@@ -41,12 +41,12 @@ import {
41
41
  success,
42
42
  table,
43
43
  warn
44
- } from "../chunk-MHB3SEYC.js";
44
+ } from "../chunk-Z3N7J2MG.js";
45
45
  import {
46
46
  getProjectDir,
47
47
  isSessionResumeDisabled,
48
48
  readDirectChatSessionState
49
- } from "../chunk-ATJU3SDV.js";
49
+ } from "../chunk-ZCTFF4PA.js";
50
50
  import {
51
51
  AnchorSessionClient,
52
52
  CHANNEL_REGISTRY,
@@ -5467,7 +5467,7 @@ import { execFileSync, execSync } from "child_process";
5467
5467
  import { existsSync as existsSync11, realpathSync as realpathSync2 } from "fs";
5468
5468
  import chalk18 from "chalk";
5469
5469
  import ora16 from "ora";
5470
- var cliVersion = true ? "0.28.725" : "dev";
5470
+ var cliVersion = true ? "0.28.726" : "dev";
5471
5471
  async function fetchLatestVersion() {
5472
5472
  const host2 = getHost();
5473
5473
  if (!host2) return null;
@@ -6654,7 +6654,7 @@ function handleError(err) {
6654
6654
  }
6655
6655
 
6656
6656
  // src/bin/agt.ts
6657
- var cliVersion2 = true ? "0.28.725" : "dev";
6657
+ var cliVersion2 = true ? "0.28.726" : "dev";
6658
6658
  var program = new Command();
6659
6659
  program.name("agt").description("Augmented CLI \u2014 agent provisioning and management").version(cliVersion2).option("--json", "Emit machine-readable JSON output (suppress spinners and colors)").option("--skip-update-check", "Skip the automatic update check on startup");
6660
6660
  program.hook("preAction", async (thisCommand, actionCommand) => {
@@ -13,7 +13,7 @@ import {
13
13
  parseEnvFileEntries,
14
14
  parseEnvIntegrations,
15
15
  shellQuote
16
- } from "./chunk-ATJU3SDV.js";
16
+ } from "./chunk-ZCTFF4PA.js";
17
17
  import {
18
18
  BIND_FAILURE_QUARANTINE_THRESHOLD,
19
19
  INTEGRATIONS_SECTION_END,
@@ -6428,7 +6428,7 @@ function exchangeFailureKind(err) {
6428
6428
  }
6429
6429
 
6430
6430
  // src/lib/api-client.ts
6431
- var agtCliVersion = true ? "0.28.725" : "dev";
6431
+ var agtCliVersion = true ? "0.28.726" : "dev";
6432
6432
  var lastConfigHash = null;
6433
6433
  function setConfigHash(hash) {
6434
6434
  lastConfigHash = hash && hash.length > 0 ? hash : null;
@@ -10474,4 +10474,4 @@ export {
10474
10474
  managerInstallSystemUnitCommand,
10475
10475
  managerUninstallSystemUnitCommand
10476
10476
  };
10477
- //# sourceMappingURL=chunk-MHB3SEYC.js.map
10477
+ //# sourceMappingURL=chunk-Z3N7J2MG.js.map
@@ -3134,6 +3134,150 @@ function trackUnclaimedDialog(codeName, hash, io, config) {
3134
3134
  var unclaimedDialogCounts = /* @__PURE__ */ new Map();
3135
3135
  var unclaimedDialogSeen = /* @__PURE__ */ new Map();
3136
3136
 
3137
+ // src/lib/model-policy.ts
3138
+ var TRANSPORTS = ["anthropic-direct", "bedrock", "gateway"];
3139
+ var WIRE_SCHEMAS = ["anthropic_messages", "openai_chat_completions"];
3140
+ var CREDENTIAL_SOURCES = [
3141
+ "max_subscription",
3142
+ "api_key",
3143
+ "gateway_token",
3144
+ "aws_role"
3145
+ ];
3146
+ var ROLES = ["primary", "secondary"];
3147
+ var isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
3148
+ function parseBinding(raw) {
3149
+ if (!isRecord(raw)) return null;
3150
+ const role = raw["role"];
3151
+ const transport = raw["transport"];
3152
+ const wireSchema = raw["wire_schema"];
3153
+ const credentialSource = raw["credential_source"];
3154
+ if (typeof role !== "string" || !ROLES.includes(role)) return null;
3155
+ if (typeof transport !== "string" || !TRANSPORTS.includes(transport)) return null;
3156
+ if (typeof wireSchema !== "string" || !WIRE_SCHEMAS.includes(wireSchema)) return null;
3157
+ if (typeof credentialSource !== "string" || !CREDENTIAL_SOURCES.includes(credentialSource)) {
3158
+ return null;
3159
+ }
3160
+ const baseUrlRaw = raw["base_url"];
3161
+ const baseUrl = typeof baseUrlRaw === "string" && baseUrlRaw.trim() !== "" ? baseUrlRaw : null;
3162
+ if (transport === "gateway" !== (baseUrl !== null)) return null;
3163
+ return {
3164
+ role,
3165
+ transport,
3166
+ wireSchema,
3167
+ baseUrl,
3168
+ credentialSource
3169
+ };
3170
+ }
3171
+ function parseModelPolicy(raw) {
3172
+ if (!isRecord(raw)) return null;
3173
+ const policyId = raw["policy_id"];
3174
+ const name = raw["name"];
3175
+ const revision = raw["revision"];
3176
+ const resolvedFrom = raw["resolved_from"];
3177
+ if (typeof policyId !== "string" || policyId === "") return null;
3178
+ if (typeof name !== "string") return null;
3179
+ if (typeof revision !== "number" || !Number.isFinite(revision)) return null;
3180
+ if (typeof resolvedFrom !== "string" || !["agent", "host", "organization"].includes(resolvedFrom)) {
3181
+ return null;
3182
+ }
3183
+ const bindingsRaw = raw["bindings"];
3184
+ if (!Array.isArray(bindingsRaw) || bindingsRaw.length === 0) return null;
3185
+ const bindings = [];
3186
+ for (const b of bindingsRaw) {
3187
+ const parsed = parseBinding(b);
3188
+ if (!parsed) return null;
3189
+ bindings.push(parsed);
3190
+ }
3191
+ return {
3192
+ policyId,
3193
+ name,
3194
+ revision,
3195
+ resolvedFrom,
3196
+ bindings
3197
+ };
3198
+ }
3199
+ function modelPolicyAuthComponent(policy) {
3200
+ if (!policy) return null;
3201
+ return `${policy.policyId}@${policy.revision}`;
3202
+ }
3203
+ function modelPolicyEgressDomains(policy) {
3204
+ if (!policy) return [];
3205
+ const out = /* @__PURE__ */ new Set();
3206
+ for (const b of policy.bindings) {
3207
+ if (!b.baseUrl) continue;
3208
+ try {
3209
+ const host = new URL(b.baseUrl).hostname.trim().toLowerCase();
3210
+ if (host) out.add(host);
3211
+ } catch {
3212
+ }
3213
+ }
3214
+ return [...out];
3215
+ }
3216
+ function resolveBindingCredential(binding, availableGatewayToken) {
3217
+ switch (binding.transport) {
3218
+ case "anthropic-direct":
3219
+ return { kind: "host-default" };
3220
+ case "bedrock":
3221
+ return { kind: "blocked", reason: "bedrock-not-implemented" };
3222
+ case "gateway": {
3223
+ if (!binding.baseUrl) return { kind: "blocked", reason: "credential-path-not-built" };
3224
+ if (!availableGatewayToken) {
3225
+ return { kind: "blocked", reason: "credential-path-not-built" };
3226
+ }
3227
+ return { kind: "gateway", baseUrl: binding.baseUrl, authToken: availableGatewayToken };
3228
+ }
3229
+ }
3230
+ }
3231
+ function primaryBinding(policy) {
3232
+ if (!policy) return null;
3233
+ return policy.bindings.find((b) => b.role === "primary") ?? null;
3234
+ }
3235
+ function sourceSetsAnthropicApiKey(source) {
3236
+ switch (source) {
3237
+ case "max_subscription":
3238
+ return false;
3239
+ case "api_key":
3240
+ return false;
3241
+ case "gateway_token":
3242
+ return false;
3243
+ case "aws_role":
3244
+ return false;
3245
+ }
3246
+ }
3247
+ function policyPutsAnthropicApiKeyInEnv(policy) {
3248
+ if (!policy) return false;
3249
+ return policy.bindings.some((b) => sourceSetsAnthropicApiKey(b.credentialSource));
3250
+ }
3251
+ function resolveModelPolicySpawnEnv(input) {
3252
+ const { policy, openRouterMode, gatewayToken, codeName } = input;
3253
+ const primary = primaryBinding(policy);
3254
+ if (!policy || !primary) return { env: {}, notice: null };
3255
+ if (openRouterMode) {
3256
+ return {
3257
+ env: {},
3258
+ notice: `[model-policy] '${codeName}' has policy '${policy.name}' but is in OpenRouter mode \u2014 OpenRouter wins, policy not applied (ENG-9483: unifying the two actuators is a separate migration)`
3259
+ };
3260
+ }
3261
+ const resolution = resolveBindingCredential(primary, gatewayToken);
3262
+ switch (resolution.kind) {
3263
+ case "gateway":
3264
+ return {
3265
+ env: {
3266
+ ANTHROPIC_BASE_URL: resolution.baseUrl,
3267
+ ANTHROPIC_AUTH_TOKEN: resolution.authToken
3268
+ },
3269
+ notice: `[model-policy] '${codeName}' inference via gateway ${resolution.baseUrl} (policy '${policy.name}' rev ${policy.revision}, from ${policy.resolvedFrom})`
3270
+ };
3271
+ case "blocked":
3272
+ return {
3273
+ env: {},
3274
+ notice: `[model-policy] '${codeName}' policy '${policy.name}' NOT applied \u2014 reason=${resolution.reason}; keeping existing auth (transport=${primary.transport}, credential_source=${primary.credentialSource})`
3275
+ };
3276
+ case "host-default":
3277
+ return { env: {}, notice: null };
3278
+ }
3279
+ }
3280
+
3137
3281
  // src/lib/persistent-session.ts
3138
3282
  var OPENROUTER_ANTHROPIC_BASE_URL = "https://openrouter.ai/api";
3139
3283
  function syncClaudeCredsToRoot() {
@@ -3170,7 +3314,8 @@ function syncClaudeCredsToRoot() {
3170
3314
  return false;
3171
3315
  }
3172
3316
  }
3173
- function resolveOAuthCredAction(claudeAuthMode, openRouterMode) {
3317
+ function resolveOAuthCredAction(claudeAuthMode, openRouterMode, policy, policyApplied) {
3318
+ if (policy && policyApplied) return policyPutsAnthropicApiKeyInEnv(policy) ? "purge" : "sync";
3174
3319
  if (openRouterMode) return "sync";
3175
3320
  return claudeAuthMode === "api_key" ? "purge" : "sync";
3176
3321
  }
@@ -3227,8 +3372,13 @@ function egressMode(codeName) {
3227
3372
  return "allowlist";
3228
3373
  }
3229
3374
  var VALID_EGRESS_DOMAIN = /^\.?([a-z0-9-]+\.)+[a-z0-9-]+$/;
3230
- function buildEgressAllowlist(toolsFrontmatter) {
3375
+ function buildEgressAllowlist(toolsFrontmatter, policyDomains = []) {
3231
3376
  const domains = new Set(EGRESS_BASELINE_DOMAINS);
3377
+ for (const d of policyDomains) {
3378
+ if (typeof d !== "string") continue;
3379
+ const norm = d.trim().toLowerCase();
3380
+ if (VALID_EGRESS_DOMAIN.test(norm)) domains.add(norm);
3381
+ }
3232
3382
  for (const tool of toolsFrontmatter?.tools ?? []) {
3233
3383
  for (const d of tool.network?.allowlist_domains ?? []) {
3234
3384
  if (typeof d !== "string") continue;
@@ -3279,7 +3429,7 @@ function restartEgressSidecar(codeName) {
3279
3429
  }
3280
3430
  }
3281
3431
  function buildDockerRunCommand(args) {
3282
- const { codeName, agentId, wrapperPath, projectDir, homeDir, runId, passApiKey, passOpenRouter, egress, forwardSlackReplyBinding, forwardBlockTurnEndAllMarkers, forwardKanbanWaiting, forwardNotifyDispatch, forwardTurnFailureNotice, forwardChannelSecrets } = args;
3432
+ const { codeName, agentId, wrapperPath, projectDir, homeDir, runId, passApiKey, passOpenRouter, passModelPolicyGateway, egress, forwardSlackReplyBinding, forwardBlockTurnEndAllMarkers, forwardKanbanWaiting, forwardNotifyDispatch, forwardTurnFailureNotice, forwardChannelSecrets } = args;
3283
3433
  const q = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
3284
3434
  const agentDir2 = join6(homeDir, ".augmented", codeName);
3285
3435
  const agentIdDir = join6(homeDir, ".augmented", agentId);
@@ -3307,6 +3457,10 @@ function buildDockerRunCommand(args) {
3307
3457
  envArgs.push("-e ANTHROPIC_MODEL");
3308
3458
  envArgs.push("-e ANTHROPIC_SMALL_FAST_MODEL");
3309
3459
  }
3460
+ if (passModelPolicyGateway) {
3461
+ envArgs.push("-e ANTHROPIC_BASE_URL");
3462
+ envArgs.push("-e ANTHROPIC_AUTH_TOKEN");
3463
+ }
3310
3464
  if (runId) envArgs.push(`-e ${q(`AGT_RUN_ID=${runId}`)}`);
3311
3465
  envArgs.push("-e AGT_API_KEY");
3312
3466
  envArgs.push("-e AGT_HOST");
@@ -3694,6 +3848,14 @@ function spawnSession(config, session) {
3694
3848
  const { codeName, projectDir, mcpConfigPath, claudeMdPath, channels, devChannels, apiHost, log: log2 } = config;
3695
3849
  const claudeAuthMode = config.claudeAuthMode ?? "subscription";
3696
3850
  const openRouterMode = !!config.openRouter;
3851
+ const { env: policyEnv, notice: policyNotice } = resolveModelPolicySpawnEnv({
3852
+ policy: config.modelPolicy ?? null,
3853
+ openRouterMode,
3854
+ gatewayToken: config.modelPolicyGatewayToken ?? null,
3855
+ codeName
3856
+ });
3857
+ if (policyNotice) log2(policyNotice);
3858
+ const policyEnvActive = Object.keys(policyEnv).length > 0;
3697
3859
  const tmuxSession = `agt-${codeName}`;
3698
3860
  log2(
3699
3861
  `[persistent-session] Starting tmux session '${tmuxSession}' for '${codeName}' (auth=${openRouterMode ? "openrouter" : claudeAuthMode})`
@@ -3714,7 +3876,14 @@ function spawnSession(config, session) {
3714
3876
  execSync2(`tmux kill-session -t ${tmuxSession} 2>/dev/null`, { stdio: "ignore" });
3715
3877
  } catch {
3716
3878
  }
3717
- if (resolveOAuthCredAction(claudeAuthMode, openRouterMode) === "sync") {
3879
+ if (resolveOAuthCredAction(
3880
+ claudeAuthMode,
3881
+ openRouterMode,
3882
+ config.modelPolicy ?? null,
3883
+ // Not `!!config.modelPolicy`: a blocked binding means the agent is still
3884
+ // on its legacy auth, so the legacy purge rule must govern it.
3885
+ policyEnvActive
3886
+ ) === "sync") {
3718
3887
  const credsSynced = syncClaudeCredsToRoot();
3719
3888
  const onLinuxRoot = platform() === "linux" && typeof process.getuid === "function" && process.getuid() === 0;
3720
3889
  if (openRouterMode) {
@@ -3817,6 +3986,10 @@ function spawnSession(config, session) {
3817
3986
  if (or.smallFastModel) {
3818
3987
  tmuxSessionEnvArgs.push("-e", `ANTHROPIC_SMALL_FAST_MODEL=${or.smallFastModel}`);
3819
3988
  }
3989
+ } else if (policyEnvActive) {
3990
+ for (const [k, v] of Object.entries(policyEnv)) {
3991
+ tmuxSessionEnvArgs.push("-e", `${k}=${v}`);
3992
+ }
3820
3993
  } else if (claudeAuthMode === "api_key" && config.anthropicApiKey) {
3821
3994
  tmuxSessionEnvArgs.push("-e", `ANTHROPIC_API_KEY=${config.anthropicApiKey}`);
3822
3995
  }
@@ -3853,10 +4026,19 @@ function spawnSession(config, session) {
3853
4026
  projectDir,
3854
4027
  homeDir: sessionHomeDir,
3855
4028
  runId: config.runId ?? void 0,
3856
- passApiKey: !openRouterMode && claudeAuthMode === "api_key" && !!config.anthropicApiKey,
4029
+ // ENG-9483 slice 2: `!policyEnvActive` mirrors the tmux `-e` chain's
4030
+ // precedence exactly. Without it an api_key host with a gateway policy
4031
+ // would forward BOTH the policy's auth token and the host's
4032
+ // ANTHROPIC_API_KEY into the container — the confused-deputy state the
4033
+ // purge exists to prevent, reintroduced through the docker boundary.
4034
+ passApiKey: !openRouterMode && !policyEnvActive && claudeAuthMode === "api_key" && !!config.anthropicApiKey,
3857
4035
  // ENG-7152: forward the OpenRouter ANTHROPIC_* vars (by name) from the
3858
4036
  // session-shell env into the container, same posture as passApiKey.
3859
4037
  passOpenRouter: openRouterMode,
4038
+ // ENG-9483 slice 2: for an isolated agent, claude sees ONLY what is
4039
+ // forwarded here — see the flag's docblock for why omitting this is a
4040
+ // silent fallback to the host's auth rather than a visible failure.
4041
+ passModelPolicyGateway: policyEnvActive,
3860
4042
  egress,
3861
4043
  // ENG-6476: forward the reply-routing flag env if it will be present in the
3862
4044
  // session env (operator-set OR materialized from the flag above).
@@ -3891,7 +4073,7 @@ function spawnSession(config, session) {
3891
4073
  agentId: config.agentId,
3892
4074
  runId: config.runId
3893
4075
  });
3894
- const apiKeyEnv = !openRouterMode && claudeAuthMode === "api_key" && config.anthropicApiKey ? { ANTHROPIC_API_KEY: config.anthropicApiKey } : {};
4076
+ const apiKeyEnv = !openRouterMode && !policyEnvActive && claudeAuthMode === "api_key" && config.anthropicApiKey ? { ANTHROPIC_API_KEY: config.anthropicApiKey } : {};
3895
4077
  const openRouterEnv = openRouterMode ? {
3896
4078
  ANTHROPIC_BASE_URL: OPENROUTER_ANTHROPIC_BASE_URL,
3897
4079
  ANTHROPIC_AUTH_TOKEN: config.openRouter.authToken,
@@ -3903,6 +4085,7 @@ function spawnSession(config, session) {
3903
4085
  HOME: tmuxEnv["HOME"],
3904
4086
  ...apiKeyEnv,
3905
4087
  ...openRouterEnv,
4088
+ ...policyEnv,
3906
4089
  ...channelSecretEnv,
3907
4090
  ...config.runId ? { AGT_RUN_ID: config.runId } : {},
3908
4091
  AGT_API_KEY: tmuxEnv["AGT_API_KEY"],
@@ -4646,6 +4829,9 @@ export {
4646
4829
  checkChannelInputs,
4647
4830
  takeWatchdogGiveUpCount,
4648
4831
  creditWatchdogGiveUpCount,
4832
+ parseModelPolicy,
4833
+ modelPolicyAuthComponent,
4834
+ modelPolicyEgressDomains,
4649
4835
  resolveOAuthCredAction,
4650
4836
  resolveClaudeBinary,
4651
4837
  isolationMode,
@@ -4694,4 +4880,4 @@ export {
4694
4880
  stopAllSessionsAndWait,
4695
4881
  getProjectDir
4696
4882
  };
4697
- //# sourceMappingURL=chunk-ATJU3SDV.js.map
4883
+ //# sourceMappingURL=chunk-ZCTFF4PA.js.map