@integrity-labs/agt-cli 0.28.527 → 0.28.529

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.
@@ -3151,10 +3151,28 @@ later tick:
3151
3151
  - **\`kanban_update\` with notes** if blocked but maybe unblockable later; leave it
3152
3152
  \`in_progress\` and pick up other work.
3153
3153
 
3154
- **Empty board:** if \`todo\` + \`in_progress\` are empty but \`backlog\` has items, don't
3155
- self-assign \u2014 ask your manager once which to pick up, then stand down (don't
3156
- re-escalate every tick). If \`backlog\` is empty too, say "All clear, no pending work"
3157
- once and stand down.
3154
+ **Empty \`todo\` but a non-empty \`backlog\`: PULL, don't ask.** (CS-1549) Move the
3155
+ highest-priority backlog item to \`in_progress\` and work it. Asking your manager
3156
+ which one to pick is the old rule and it was wrong: it made the backlog drain
3157
+ only as fast as a human remembered to push work.
3158
+
3159
+ Escalate INSTEAD of pulling only when the top item is genuinely ambiguous \u2014 you
3160
+ cannot tell what "done" would look like \u2014 and then ask one specific question and
3161
+ pull the NEXT item meanwhile. "I wasn't sure" is not a reason to end a turn idle.
3162
+
3163
+ **\`waiting\` is not working.** Cards parked on a PR review or a human are not your
3164
+ work in progress; they are someone else's. A board reading "0 in progress, 5
3165
+ waiting, 8 backlog" means you are IDLE, not busy. Judge your load by
3166
+ \`in_progress\` alone, and keep it at one.
3167
+
3168
+ **\`needs_attention\` is NOT pullable.** The reaper parks stalled cards there
3169
+ for a human. Pulling one just re-stalls it, so it is not available work \u2014 and
3170
+ the turn-end check excludes it for the same reason.
3171
+
3172
+ **Genuinely nothing to do** \u2014 \`todo\`, \`in_progress\` and \`backlog\` all empty, or
3173
+ every remaining item deliberately parked \u2014 say "All clear, no pending work" once
3174
+ and stand down. Say which it is; "all clear" while eight items sit in the backlog
3175
+ is the failure this rule exists to stop.
3158
3176
 
3159
3177
  `;
3160
3178
  }
@@ -5526,6 +5544,12 @@ var HTTP_PROBE_PROVIDERS = /* @__PURE__ */ new Set([
5526
5544
  // includes 'github') — a narrower, honest "the token we stored is valid"
5527
5545
  // check. Unifying the monitor onto the central probe is sub-issue C's call.
5528
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
+ };
5529
5553
  var CLI_PROBE_ARGS = {
5530
5554
  gcloud: ["version"],
5531
5555
  // ENG-6206: `gh --version` only proves the binary exists, not that it's
@@ -5582,6 +5606,21 @@ function resolveConnectivityProbe(input) {
5582
5606
  httpProvider: definitionId
5583
5607
  };
5584
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
+ }
5585
5624
  if (getOAuthProvider(definitionId)?.mcpUrl) {
5586
5625
  return {
5587
5626
  kind: "mcp_tools_list",
@@ -5994,6 +6033,22 @@ var SITE_RESOLUTION_ERROR_PATTERNS = [
5994
6033
  "name resolution",
5995
6034
  "name not resolved"
5996
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
+ ];
5997
6052
  function isReadonlyToolDescriptor(t) {
5998
6053
  if (!t?.name)
5999
6054
  return false;
@@ -6044,9 +6099,17 @@ function isSiteResolutionError(message) {
6044
6099
  const m = message.toLowerCase();
6045
6100
  return SITE_RESOLUTION_ERROR_PATTERNS.some((p2) => m.includes(p2));
6046
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
+ }
6047
6108
  function classifyToolCallFailure(text) {
6048
6109
  if (isAccountResolutionError(text))
6049
6110
  return "account";
6111
+ if (isScopeDeficitError(text))
6112
+ return "scope";
6050
6113
  if (isUpstreamAuthError(text))
6051
6114
  return "auth";
6052
6115
  if (isSiteResolutionError(text))
@@ -6172,6 +6235,13 @@ async function probeComposioMcpToolCall(config, fetchImpl = fetch) {
6172
6235
  details: baseDetails
6173
6236
  };
6174
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
+ }
6175
6245
  if (kind === "auth") {
6176
6246
  return {
6177
6247
  status: "down",
@@ -7371,6 +7441,45 @@ var INTEGRATION_REGISTRY = [
7371
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" }
7372
7442
  ]
7373
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
+ },
7374
7483
  {
7375
7484
  id: "firecrawl",
7376
7485
  name: "Firecrawl",
@@ -9303,13 +9412,104 @@ function encodeClaudeProjectPath(projectDir) {
9303
9412
  return "-" + projectDir.replace(/^\//, "").replace(/[/.]/g, "-");
9304
9413
  }
9305
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
+
9306
9504
  // ../../packages/core/dist/account-enforcement/marker.js
9307
9505
  var ACCOUNT_ENFORCEMENT_MARKER_FILENAME = "account-enforcement.json";
9308
9506
  var ACCOUNT_ENFORCEMENT_MARKER_VERSION = 1;
9309
- function serializeAccountEnforcementMarker(level) {
9507
+ function serializeAccountEnforcementMarker(level, text) {
9508
+ const trimmed = typeof text === "string" ? text.trim() : "";
9310
9509
  const marker = {
9311
9510
  version: ACCOUNT_ENFORCEMENT_MARKER_VERSION,
9312
- level
9511
+ level,
9512
+ ...trimmed ? { text: trimmed } : {}
9313
9513
  };
9314
9514
  return JSON.stringify(marker);
9315
9515
  }
@@ -10477,6 +10677,32 @@ var FLAG_REGISTRY = [
10477
10677
  // Best-effort reach estimate only; not part of the schema fingerprint.
10478
10678
  since: "0.28.504"
10479
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
+ },
10480
10706
  {
10481
10707
  key: "composio-hitl-mode",
10482
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.",
@@ -10883,6 +11109,15 @@ var FLAG_REGISTRY = [
10883
11109
  // runbook gates on it. Leaving the placeholder in place is safe (nothing
10884
11110
  // flips); shipping a guessed number is not.
10885
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
10886
11121
  }
10887
11122
  ];
10888
11123
  var REGISTRY_BY_KEY = new Map(FLAG_REGISTRY.map((definition) => [definition.key, definition]));
@@ -12702,13 +12937,13 @@ var TurnOutcomeTracker = class {
12702
12937
  };
12703
12938
 
12704
12939
  // src/lib/busy-bucket-ledger.ts
12705
- var BUCKET_MS = 6e4;
12940
+ var BUCKET_MS2 = 6e4;
12706
12941
  var MAX_ACCRUAL_SPAN_MS = 10 * 6e4;
12707
12942
  var MAX_RETAINED_BUCKETS = 240;
12708
12943
  var BILL_WHOLE_MINUTES = true;
12709
12944
  var MIN_BILLABLE_MS = 100;
12710
12945
  function bucketStartMs(atMs) {
12711
- return Math.floor(atMs / BUCKET_MS) * BUCKET_MS;
12946
+ return Math.floor(atMs / BUCKET_MS2) * BUCKET_MS2;
12712
12947
  }
12713
12948
  var BusyBucketLedger = class {
12714
12949
  state = /* @__PURE__ */ new Map();
@@ -12730,11 +12965,11 @@ var BusyBucketLedger = class {
12730
12965
  if (toMs <= fromMs) return;
12731
12966
  const start = Math.max(fromMs, toMs - MAX_ACCRUAL_SPAN_MS);
12732
12967
  const led = this.entry(codeName);
12733
- for (let b = bucketStartMs(start); b < toMs; b += BUCKET_MS) {
12734
- 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);
12735
12970
  if (overlap <= 0) continue;
12736
12971
  const prev = led.buckets.get(b) ?? 0;
12737
- led.buckets.set(b, Math.min(BUCKET_MS, prev + overlap));
12972
+ led.buckets.set(b, Math.min(BUCKET_MS2, prev + overlap));
12738
12973
  }
12739
12974
  this.evict(led);
12740
12975
  }
@@ -12802,7 +13037,7 @@ var BusyBucketLedger = class {
12802
13037
  if (!Number.isFinite(at)) continue;
12803
13038
  const key = bucketStartMs(at);
12804
13039
  const prev = led.buckets.get(key) ?? 0;
12805
- 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));
12806
13041
  }
12807
13042
  this.evict(led);
12808
13043
  }
@@ -15302,6 +15537,9 @@ export {
15302
15537
  UNKNOWN_RATE_LIMIT,
15303
15538
  pickNewerClassification,
15304
15539
  classifyTranscriptRateLimit,
15540
+ DEFAULT_NEIGHBOURHOOD_MS,
15541
+ extractQualifyingTurns,
15542
+ isBucketQualified,
15305
15543
  ACCOUNT_ENFORCEMENT_MARKER_FILENAME,
15306
15544
  serializeAccountEnforcementMarker,
15307
15545
  KANBAN_CHECK_COMMAND,
@@ -15393,4 +15631,4 @@ export {
15393
15631
  stopAllSessionsAndWait,
15394
15632
  getProjectDir
15395
15633
  };
15396
- //# sourceMappingURL=chunk-Z4P4VEDU.js.map
15634
+ //# sourceMappingURL=chunk-Q4XGKOHZ.js.map