@integrity-labs/agt-cli 0.28.528 → 0.28.530

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.
@@ -5544,6 +5544,12 @@ var HTTP_PROBE_PROVIDERS = /* @__PURE__ */ new Set([
5544
5544
  // includes 'github') — a narrower, honest "the token we stored is valid"
5545
5545
  // check. Unifying the monitor onto the central probe is sub-issue C's call.
5546
5546
  ]);
5547
+ var BUNDLED_STDIO_PROBE_TOOLKITS = {
5548
+ "augmented-help-kb": {
5549
+ tool: "search_knowledge_base",
5550
+ args: { query: "connectivity probe", limit: 1 }
5551
+ }
5552
+ };
5547
5553
  var CLI_PROBE_ARGS = {
5548
5554
  gcloud: ["version"],
5549
5555
  // ENG-6206: `gh --version` only proves the binary exists, not that it's
@@ -5600,6 +5606,21 @@ function resolveConnectivityProbe(input) {
5600
5606
  httpProvider: definitionId
5601
5607
  };
5602
5608
  }
5609
+ const bundledStdio = BUNDLED_STDIO_PROBE_TOOLKITS[definitionId];
5610
+ if (bundledStdio) {
5611
+ const override = mcpOverrideFrom(input.connectivityTest);
5612
+ const probeTool = override.probeTool ?? bundledStdio.tool;
5613
+ const probeArgs = override.probeTool ? override.probeArgs : bundledStdio.args;
5614
+ return {
5615
+ kind: "mcp_stdio",
5616
+ sourceType: "mcp_server",
5617
+ readOnly: true,
5618
+ label: `${definitionId}: local-stdio MCP handshake + ${probeTool}`,
5619
+ centralReachable: false,
5620
+ probeTool,
5621
+ ...probeArgs ? { probeArgs } : {}
5622
+ };
5623
+ }
5603
5624
  if (getOAuthProvider(definitionId)?.mcpUrl) {
5604
5625
  return {
5605
5626
  kind: "mcp_tools_list",
@@ -6012,6 +6033,22 @@ var SITE_RESOLUTION_ERROR_PATTERNS = [
6012
6033
  "name resolution",
6013
6034
  "name not resolved"
6014
6035
  ];
6036
+ var SCOPE_DEFICIT_ERROR_PATTERNS = [
6037
+ "insufficient_scope",
6038
+ "insufficient scope",
6039
+ "insufficient permission",
6040
+ "missing scope",
6041
+ "required scope",
6042
+ "requires the scope",
6043
+ "requires the following scope",
6044
+ "scope is required",
6045
+ "access scope",
6046
+ "oauth scope",
6047
+ // Shopify custom-app specifics.
6048
+ "read_content",
6049
+ "write_content",
6050
+ "sales channel is not enabled"
6051
+ ];
6015
6052
  function isReadonlyToolDescriptor(t) {
6016
6053
  if (!t?.name)
6017
6054
  return false;
@@ -6062,9 +6099,17 @@ function isSiteResolutionError(message) {
6062
6099
  const m = message.toLowerCase();
6063
6100
  return SITE_RESOLUTION_ERROR_PATTERNS.some((p2) => m.includes(p2));
6064
6101
  }
6102
+ function isScopeDeficitError(message) {
6103
+ const m = message.toLowerCase();
6104
+ if (SCOPE_DEFICIT_ERROR_PATTERNS.some((p2) => m.includes(p2)))
6105
+ return true;
6106
+ return m.includes("scope") && ["doesn't have", "does not have", "not have the", "not granted", "lacks the", "not authorized for"].some((p2) => m.includes(p2));
6107
+ }
6065
6108
  function classifyToolCallFailure(text) {
6066
6109
  if (isAccountResolutionError(text))
6067
6110
  return "account";
6111
+ if (isScopeDeficitError(text))
6112
+ return "scope";
6068
6113
  if (isUpstreamAuthError(text))
6069
6114
  return "auth";
6070
6115
  if (isSiteResolutionError(text))
@@ -6190,6 +6235,13 @@ async function probeComposioMcpToolCall(config, fetchImpl = fetch) {
6190
6235
  details: baseDetails
6191
6236
  };
6192
6237
  }
6238
+ if (kind === "scope") {
6239
+ return {
6240
+ status: "degraded",
6241
+ message: `Live tool call '${toolName}' was refused for a missing permission \u2014 the connection is valid, but its app/token isn't granted the scope this tool needs. Grant the required scope in the provider app and re-authorise (this is NOT a reconnect): ${snippet}`,
6242
+ details: { ...baseDetails, reason: "scope_deficit" }
6243
+ };
6244
+ }
6193
6245
  if (kind === "auth") {
6194
6246
  return {
6195
6247
  status: "down",
@@ -7389,6 +7441,45 @@ var INTEGRATION_REGISTRY = [
7389
7441
  { id: "augmented-support:propose-writes", name: "Propose Self-Remediation", description: "Propose creating an agent in your own org; executed only after a human approves a server-rendered diff.", access: "write" }
7390
7442
  ]
7391
7443
  },
7444
+ {
7445
+ // ENG-8332: the Augmented Team HELP knowledge base, split out of
7446
+ // `augmented-support` so an agent can be given the docs WITHOUT also being
7447
+ // given a ticket-filing button, an agent-proposal button, and org-wide read
7448
+ // of agents, hosts, integrations, alerts, flags and the audit log. That
7449
+ // bundling was why "just give everyone augmented-support" was the wrong
7450
+ // answer, and the cost was agents answering platform questions from
7451
+ // inference instead of documentation (the CS-1529/CS-1530 case).
7452
+ //
7453
+ // Provisions the @integrity-labs/augmented-help-kb-mcp stdio broker, a
7454
+ // read-only client over /host/kb - the single enumerated carve-out from the
7455
+ // ADR-0032 org-lock invariant (ENG-7119). KB content is platform-curated
7456
+ // and org-neutral, so this grants NO view of any org's state; the client
7457
+ // has no POST transport at all, so writes are structurally impossible
7458
+ // rather than merely unregistered.
7459
+ //
7460
+ // auth `none` - no end-user OAuth, the host JWT is the credential. `beta`
7461
+ // while distribution widens. NOT in the Add Integration picker (no
7462
+ // `installable`): granted deliberately, like its two siblings.
7463
+ //
7464
+ // NAMING (ENG-7281): this is the "help knowledge base" - shared, published,
7465
+ // org-neutral PRODUCT help. It is NOT the org knowledge store that
7466
+ // `knowledge_search` / `context_search` read. Keep the two distinct in every
7467
+ // surface that names them.
7468
+ id: "augmented-help-kb",
7469
+ name: "Augmented Help KB",
7470
+ category: "knowledge",
7471
+ description: "The Augmented Team help knowledge base: search and read the shared, published product documentation (how-tos, common fixes, FAQs). Identical for every organization and org-neutral by design - it grants no view of your org's own state, and cannot file tickets or propose changes. Distinct from your organization's own knowledge store.",
7472
+ supported_auth_types: ["none"],
7473
+ beta: true,
7474
+ capabilities: [
7475
+ {
7476
+ id: "augmented-help-kb:read",
7477
+ name: "Read Help Docs",
7478
+ description: "Search and read published Augmented Team help articles (org-neutral product documentation). Read-only; no ticket filing, no agent proposals, no org-state reads.",
7479
+ access: "read"
7480
+ }
7481
+ ]
7482
+ },
7392
7483
  {
7393
7484
  id: "firecrawl",
7394
7485
  name: "Firecrawl",
@@ -9321,13 +9412,104 @@ function encodeClaudeProjectPath(projectDir) {
9321
9412
  return "-" + projectDir.replace(/^\//, "").replace(/[/.]/g, "-");
9322
9413
  }
9323
9414
 
9415
+ // ../../packages/core/dist/claude-code-usage/occupancy-qualification.js
9416
+ var DEFAULT_MIN_OUTPUT_TOKENS = 25;
9417
+ var DEFAULT_NEIGHBOURHOOD_MS = 12e4;
9418
+ var BUCKET_MS = 6e4;
9419
+ function isSyntheticOrErrorRecord(record, msg) {
9420
+ if (msg.model === "<synthetic>")
9421
+ return true;
9422
+ if (record.isApiErrorMessage === true)
9423
+ return true;
9424
+ if (typeof record.error === "string" && record.error)
9425
+ return true;
9426
+ if (typeof record.apiErrorStatus === "number")
9427
+ return true;
9428
+ return false;
9429
+ }
9430
+ function resolveFloor(value) {
9431
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
9432
+ return DEFAULT_MIN_OUTPUT_TOKENS;
9433
+ }
9434
+ return value;
9435
+ }
9436
+ function nonNegInt2(value) {
9437
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : 0;
9438
+ }
9439
+ function extractQualifyingTurns(jsonl, options = {}) {
9440
+ const floor = resolveFloor(options.minOutputTokens);
9441
+ const startMs = options.startMs ?? Number.NEGATIVE_INFINITY;
9442
+ const endMs = options.endMs ?? Number.POSITIVE_INFINITY;
9443
+ const byId = /* @__PURE__ */ new Map();
9444
+ let syntheticCounter = 0;
9445
+ for (const line2 of jsonl.split("\n")) {
9446
+ const trimmed = line2.trim();
9447
+ if (!trimmed)
9448
+ continue;
9449
+ let obj;
9450
+ try {
9451
+ obj = JSON.parse(trimmed);
9452
+ } catch {
9453
+ continue;
9454
+ }
9455
+ if (typeof obj !== "object" || obj === null)
9456
+ continue;
9457
+ const record = obj;
9458
+ if (record.type !== "assistant")
9459
+ continue;
9460
+ const message = record.message;
9461
+ if (typeof message !== "object" || message === null)
9462
+ continue;
9463
+ const msg = message;
9464
+ if (isSyntheticOrErrorRecord(record, msg))
9465
+ continue;
9466
+ const usage = msg.usage;
9467
+ if (typeof usage !== "object" || usage === null)
9468
+ continue;
9469
+ const ts = record.timestamp;
9470
+ if (typeof ts !== "string" || !ts)
9471
+ continue;
9472
+ const tsMs = new Date(ts).getTime();
9473
+ if (!Number.isFinite(tsMs))
9474
+ continue;
9475
+ const id = typeof msg.id === "string" && msg.id ? msg.id : `__noid_${syntheticCounter++}`;
9476
+ byId.set(id, {
9477
+ tsMs,
9478
+ outputTokens: nonNegInt2(usage.output_tokens)
9479
+ });
9480
+ }
9481
+ const out = [];
9482
+ for (const entry of byId.values()) {
9483
+ if (entry.outputTokens < floor)
9484
+ continue;
9485
+ if (entry.tsMs < startMs || entry.tsMs > endMs)
9486
+ continue;
9487
+ out.push(entry.tsMs);
9488
+ }
9489
+ out.sort((a, b) => a - b);
9490
+ return out;
9491
+ }
9492
+ function isBucketQualified(bucketStartMs2, turnTimes, neighbourhoodMs = DEFAULT_NEIGHBOURHOOD_MS) {
9493
+ const from = bucketStartMs2 - neighbourhoodMs;
9494
+ const to = bucketStartMs2 + BUCKET_MS + neighbourhoodMs;
9495
+ for (const t of turnTimes) {
9496
+ if (t > to)
9497
+ return false;
9498
+ if (t >= from)
9499
+ return true;
9500
+ }
9501
+ return false;
9502
+ }
9503
+
9324
9504
  // ../../packages/core/dist/account-enforcement/marker.js
9325
9505
  var ACCOUNT_ENFORCEMENT_MARKER_FILENAME = "account-enforcement.json";
9326
9506
  var ACCOUNT_ENFORCEMENT_MARKER_VERSION = 1;
9327
- function serializeAccountEnforcementMarker(level) {
9507
+ function serializeAccountEnforcementMarker(level, text) {
9508
+ const trimmed = typeof text === "string" ? text.trim() : "";
9328
9509
  const marker = {
9329
9510
  version: ACCOUNT_ENFORCEMENT_MARKER_VERSION,
9330
- level
9511
+ level,
9512
+ ...trimmed ? { text: trimmed } : {}
9331
9513
  };
9332
9514
  return JSON.stringify(marker);
9333
9515
  }
@@ -10495,6 +10677,32 @@ var FLAG_REGISTRY = [
10495
10677
  // Best-effort reach estimate only; not part of the schema fingerprint.
10496
10678
  since: "0.28.504"
10497
10679
  },
10680
+ {
10681
+ key: "utilization-occupancy-qualifier",
10682
+ description: "Utilization busy-minutes gate (ENG-8465): shadow = report every measured bucket exactly as before while COUNTING how many a transcript qualifier would drop; enforce = drop measured buckets that have no qualifying assistant turn (>= 25 output tokens, non-synthetic) within \xB1120s. The qualifier is the merged @augmented/core primitive (extractQualifyingTurns / isBucketQualified); the host reads a bounded transcript tail incl. <sessionId>/subagents/ at drain time and FAILS OPEN (credits ungated) when a transcript cannot be read. Read host-side via hostFlagStore (getString) in the manager; consumer ships in this PR's agt-cli auto-publish bump. Both modes emit the OccupancyQualificationClassifications shadow metric (qualified positive-control + unqualified_dropped signal + unreadable), so per-agent impact is measured before the flip.",
10683
+ flagType: "enum",
10684
+ allowedValues: ["shadow", "enforce"],
10685
+ // Declared safe value is `shadow`: it reproduces pre-flag billing exactly, so
10686
+ // rollout changes no agent's minutes while the shadow metric accrues the
10687
+ // enforce-flip evidence. `enforce` lowers a billing-adjacent number, so it is a
10688
+ // deliberate, audited flip — never a flag-DB-read-error default.
10689
+ defaultValue: "shadow",
10690
+ // Operator per-host override (ADR-0022 highest precedence). No _ENABLED/_DISABLED/
10691
+ // _MODE suffix, and read via hostFlagStore not a raw process.env gate, so not a
10692
+ // new env gate under ENG-6252. Never pinned in sst.config.ts (ENG-8303).
10693
+ envVar: "AGT_UTILIZATION_OCCUPANCY_QUALIFIER",
10694
+ // enforce lowers a billing-adjacent number, so flipping/weakening it is guarded
10695
+ // like the other enforcement-mode flags.
10696
+ sensitive: true,
10697
+ // Reach threshold (ENG-8465): the host reader (occupancyQualifierMode in
10698
+ // manager-worker.ts) lands in this PR's agt-cli auto-publish patch bump, which
10699
+ // is the first published version to contain it (through 0.28.528 does not). An
10700
+ // UNSET `since` makes classifyHostReach report `honors` (full coverage), which
10701
+ // would greenlight an `enforce` flip while older hosts silently stay `shadow`.
10702
+ // Best-effort estimate (like docker-hygiene's), in the safe direction — a
10703
+ // not-yet-published version reports 0% honor until hosts update. (CodeRabbit)
10704
+ since: "0.28.529"
10705
+ },
10498
10706
  {
10499
10707
  key: "composio-hitl-mode",
10500
10708
  description: "Composio managed-toolkits HITL gate (ENG-6027): off = kill switch, shadow = resolve + audit the tier decision but never block, enforce = block write_high_risk+ calls and route them to Human Approval (an Approve/Deny card). Since ENG-7828 enforce ALWAYS routes to a Human Approval card by default; set hitl-approval-routing=off to make enforce a hard block with no approval instead. Per-org rollout from the admin Feature Flags page; ENG-7672 migrated this off the fleet-wide COMPOSIO_HITL_MODE env var.",
@@ -10901,6 +11109,15 @@ var FLAG_REGISTRY = [
10901
11109
  // runbook gates on it. Leaving the placeholder in place is safe (nothing
10902
11110
  // flips); shipping a guessed number is not.
10903
11111
  since: "0.29.0"
11112
+ },
11113
+ {
11114
+ key: "help-kb-auto-grant",
11115
+ description: "Auto-grant the `augmented-help-kb` integration at ORG scope when an organization is provisioned (ENG-8542), so every agent in that org gets product-doc search without a per-agent operator action. One organization_integrations row reaches every agent in the org: POST /host/agent-integrations merges org -> team -> agent and buildMcpJson's predicate reads that merged set with no scope filter. ON: the two org-provisioning call sites write the row (status=active, created_by=the org owner). OFF (default): they write nothing - byte-for-byte today's behaviour. STAGED ROLLOUT IS THE POINT. ENG-8332 argues against widening distribution of the help KB until two things close, because distributing a corpus with wrong articles \"converts one agent's wrong answer into everyone's\": (1) a live 200 from GET /host/kb/search as the anon role has still never been observed - ENG-7260 was closed on remediation evidence, not on a live call, and the deploy seed log is SERVICE-ROLE evidence that says nothing about the anon grant; (2) the `site-structure` article is unverified against the current IA. Turning this on for ONE org and watching that org's help-kb connectivity-probe verdicts (ENG-8332 added a real mcp_stdio probe that calls search_knowledge_base) is the cheap positive control for (1) - an ENG-7260-class 500 then reports as `down` per agent instead of as silence. Widen only after it comes back healthy. REVOKING IS DELETING THE ROW, not flipping this off: the flag gates GRANTING, so an already-granted org keeps its row when the flag goes off. Evaluated API-side only, per org, inside grantHelpKbToOrg; no host-side envVar and deliberately no sst.config.ts entry (a literal default there would pin it - ENG-8303).",
11116
+ flagType: "boolean",
11117
+ // Declared safe value is `false` = grant nothing. A flag-store outage must
11118
+ // never auto-attach an integration nobody asked for, and both ENG-8332
11119
+ // prerequisites above are still open.
11120
+ defaultValue: false
10904
11121
  }
10905
11122
  ];
10906
11123
  var REGISTRY_BY_KEY = new Map(FLAG_REGISTRY.map((definition) => [definition.key, definition]));
@@ -12720,13 +12937,13 @@ var TurnOutcomeTracker = class {
12720
12937
  };
12721
12938
 
12722
12939
  // src/lib/busy-bucket-ledger.ts
12723
- var BUCKET_MS = 6e4;
12940
+ var BUCKET_MS2 = 6e4;
12724
12941
  var MAX_ACCRUAL_SPAN_MS = 10 * 6e4;
12725
12942
  var MAX_RETAINED_BUCKETS = 240;
12726
12943
  var BILL_WHOLE_MINUTES = true;
12727
12944
  var MIN_BILLABLE_MS = 100;
12728
12945
  function bucketStartMs(atMs) {
12729
- return Math.floor(atMs / BUCKET_MS) * BUCKET_MS;
12946
+ return Math.floor(atMs / BUCKET_MS2) * BUCKET_MS2;
12730
12947
  }
12731
12948
  var BusyBucketLedger = class {
12732
12949
  state = /* @__PURE__ */ new Map();
@@ -12748,11 +12965,11 @@ var BusyBucketLedger = class {
12748
12965
  if (toMs <= fromMs) return;
12749
12966
  const start = Math.max(fromMs, toMs - MAX_ACCRUAL_SPAN_MS);
12750
12967
  const led = this.entry(codeName);
12751
- for (let b = bucketStartMs(start); b < toMs; b += BUCKET_MS) {
12752
- const overlap = Math.min(toMs, b + BUCKET_MS) - Math.max(start, b);
12968
+ for (let b = bucketStartMs(start); b < toMs; b += BUCKET_MS2) {
12969
+ const overlap = Math.min(toMs, b + BUCKET_MS2) - Math.max(start, b);
12753
12970
  if (overlap <= 0) continue;
12754
12971
  const prev = led.buckets.get(b) ?? 0;
12755
- led.buckets.set(b, Math.min(BUCKET_MS, prev + overlap));
12972
+ led.buckets.set(b, Math.min(BUCKET_MS2, prev + overlap));
12756
12973
  }
12757
12974
  this.evict(led);
12758
12975
  }
@@ -12820,7 +13037,7 @@ var BusyBucketLedger = class {
12820
13037
  if (!Number.isFinite(at)) continue;
12821
13038
  const key = bucketStartMs(at);
12822
13039
  const prev = led.buckets.get(key) ?? 0;
12823
- led.buckets.set(key, Math.min(BUCKET_MS, prev + b.seconds * 1e3));
13040
+ led.buckets.set(key, Math.min(BUCKET_MS2, prev + b.seconds * 1e3));
12824
13041
  }
12825
13042
  this.evict(led);
12826
13043
  }
@@ -15320,6 +15537,9 @@ export {
15320
15537
  UNKNOWN_RATE_LIMIT,
15321
15538
  pickNewerClassification,
15322
15539
  classifyTranscriptRateLimit,
15540
+ DEFAULT_NEIGHBOURHOOD_MS,
15541
+ extractQualifyingTurns,
15542
+ isBucketQualified,
15323
15543
  ACCOUNT_ENFORCEMENT_MARKER_FILENAME,
15324
15544
  serializeAccountEnforcementMarker,
15325
15545
  KANBAN_CHECK_COMMAND,
@@ -15411,4 +15631,4 @@ export {
15411
15631
  stopAllSessionsAndWait,
15412
15632
  getProjectDir
15413
15633
  };
15414
- //# sourceMappingURL=chunk-PR3QSOLI.js.map
15634
+ //# sourceMappingURL=chunk-Q4XGKOHZ.js.map