@integrity-labs/agt-cli 0.28.424 → 0.28.426

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
@@ -40,7 +40,7 @@ import {
40
40
  success,
41
41
  table,
42
42
  warn
43
- } from "../chunk-2QBY2QVX.js";
43
+ } from "../chunk-XDODHW3I.js";
44
44
  import {
45
45
  AnchorSessionClient,
46
46
  CHANNEL_REGISTRY,
@@ -70,7 +70,7 @@ import {
70
70
  renderTemplate,
71
71
  resolveChannels,
72
72
  serializeManifestForSlackCli
73
- } from "../chunk-CWIWI2ZX.js";
73
+ } from "../chunk-HXP4I5QZ.js";
74
74
  import "../chunk-XWVM4KPK.js";
75
75
 
76
76
  // src/bin/agt.ts
@@ -4829,7 +4829,7 @@ import { execFileSync, execSync } from "child_process";
4829
4829
  import { existsSync as existsSync10, realpathSync as realpathSync2 } from "fs";
4830
4830
  import chalk18 from "chalk";
4831
4831
  import ora16 from "ora";
4832
- var cliVersion = true ? "0.28.424" : "dev";
4832
+ var cliVersion = true ? "0.28.426" : "dev";
4833
4833
  async function fetchLatestVersion() {
4834
4834
  const host2 = getHost();
4835
4835
  if (!host2) return null;
@@ -6001,7 +6001,7 @@ function handleError(err) {
6001
6001
  }
6002
6002
 
6003
6003
  // src/bin/agt.ts
6004
- var cliVersion2 = true ? "0.28.424" : "dev";
6004
+ var cliVersion2 = true ? "0.28.426" : "dev";
6005
6005
  var program = new Command();
6006
6006
  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");
6007
6007
  program.hook("preAction", async (thisCommand, actionCommand) => {
@@ -5902,6 +5902,86 @@ var INTEGRATION_REGISTRY = [
5902
5902
  ]
5903
5903
  }
5904
5904
  },
5905
+ {
5906
+ // Social Scraping: a PREMIUM, platform-managed integration on the Deck /
5907
+ // Firecrawl / X-Search model (ADR-0031, epic ENG-6920; ADR-0053). Backed by
5908
+ // Apify, but deliberately NOT surfaced as an "Apify" integration - the
5909
+ // integration is what a customer picks, and Apify is a vendor, not a job.
5910
+ // Splitting by job also means opt-in, rate card and monthly budget are keyed
5911
+ // per definition, so an org can take Local Business Data without taking the
5912
+ // pricier, more ToS-sensitive social scraping.
5913
+ //
5914
+ // Augmented holds ONE Apify account whose token (APIFY_API_TOKEN, read
5915
+ // from SSM - the API Lambda env is at the 4KB cap) every customer run bills
5916
+ // back to, so the key is a single platform-held secret, NOT a per-agent
5917
+ // credential. Auth type is `none` - customers never enter a key. Tools run on
5918
+ // a brokered-REST lane (apify-brokers.ts) so each run is metered at one
5919
+ // chokepoint and gated on a per-org monthly USD cap + the per-org premium
5920
+ // opt-in.
5921
+ //
5922
+ // "PPE-only" is guaranteed by a curated allowlist: the broker only ever runs
5923
+ // Actors on it, so a rental / pay-per-usage Actor can never surprise-bill the
5924
+ // shared account. (Apify's x402 rail enforces PPE-only at the payment layer;
5925
+ // we are on the account-key rail - Option A in the spike.) See
5926
+ // docs/design/apify-premium-option-a-ppe-allowlist.md.
5927
+ //
5928
+ // No `installable` here on purpose: this is a seed integration
5929
+ // (integration-definitions.json), so the seed catalog already makes it
5930
+ // picker-visible. This registry entry exists ONLY to carry the `premium`
5931
+ // descriptor that `isPremiumDefinition` reads - which arms the per-org opt-in
5932
+ // gate, the priced acknowledgement modal, and the admin pricing tab.
5933
+ id: "social-scraping",
5934
+ name: "Social Scraping",
5935
+ category: "social",
5936
+ description: "Pull public posts and videos from TikTok, Instagram and YouTube - captions, media, engagement counts and URLs - for brand, competitor and campaign research. Augmented Team manages the scraping account for you; there is no key to enter.",
5937
+ supported_auth_types: ["none"],
5938
+ capabilities: [
5939
+ { id: "social-scraping:scrape", name: "Scrape Social Posts", description: "Scrape public TikTok, Instagram and YouTube content (social_scrape_tiktok, social_scrape_instagram, social_scrape_youtube).", access: "write" },
5940
+ { id: "social-scraping:status", name: "Check Scrape Runs", description: "Poll a running scrape for status + results (social_scrape_status).", access: "read" }
5941
+ ],
5942
+ docs_url: "https://docs.apify.com/api/v2",
5943
+ beta: true,
5944
+ // Premium (billable). The upstream pay-per-event cost is per-Actor and
5945
+ // VARIABLE (each Actor's creator sets its own event prices), unlike X-Search's
5946
+ // fixed per-post price. So we meter the ACTUAL cost charged to us for each
5947
+ // run, as a pass-through: `units` = the run's `usageTotalUsd` in whole US
5948
+ // cents (the physical unit `usd_cent`), and the rate card prices each cent at
5949
+ // a managed markup (1.5x USD). This keeps margin safe across cheap and
5950
+ // expensive scrapes alike. `event_type` must match what the broker writes to
5951
+ // integration_usage_events. Until the rate row is seeded a run prices at 0
5952
+ // (budget gate inert) - the rate-card migration ships in the same PR.
5953
+ premium: {
5954
+ pricing: "usage",
5955
+ note: "Billed per result on the upstream pay-per-event cost of each scrape, at a managed markup.",
5956
+ meters: [{ event_type: "actor_run", unit: "usd_cent" }]
5957
+ }
5958
+ },
5959
+ {
5960
+ // Local Business Data: the second Apify-backed premium integration (ADR-0053).
5961
+ // Same platform-held account, same pass-through metering model, but a separate
5962
+ // definition so its opt-in, rate card and monthly budget are independent of
5963
+ // Social Scraping's - see the note on that entry for why the split exists.
5964
+ //
5965
+ // Distinct from Firecrawl on purpose: Firecrawl covers generic web
5966
+ // scrape/crawl/search, and we deliberately do NOT ship an Apify-backed
5967
+ // duplicate of that. This is the local-places job Firecrawl does not do.
5968
+ id: "local-business-data",
5969
+ name: "Local Business Data",
5970
+ category: "knowledge",
5971
+ description: "Find local businesses on Google Maps and pull their public listing details - name, address, phone, website, category, rating and review count - for local market research, competitor lists and lead research. Augmented Team manages the data account for you; there is no key to enter.",
5972
+ supported_auth_types: ["none"],
5973
+ capabilities: [
5974
+ { id: "local-business-data:search", name: "Search Local Businesses", description: "Search Google Maps for businesses and return their public listing details (local_business_search).", access: "write" },
5975
+ { id: "local-business-data:status", name: "Check Searches", description: "Poll a running search for status + results (local_business_search_status).", access: "read" }
5976
+ ],
5977
+ docs_url: "https://docs.apify.com/api/v2",
5978
+ beta: true,
5979
+ premium: {
5980
+ pricing: "usage",
5981
+ note: "Billed per place returned on the upstream pay-per-event cost of each search, at a managed markup.",
5982
+ meters: [{ event_type: "actor_run", unit: "usd_cent" }]
5983
+ }
5984
+ },
5905
5985
  {
5906
5986
  // Seed-catalog toolkit (composed by the ultimate-app-coder bundle). Registered
5907
5987
  // here only so the manager's ensureToolkitCli installs the binary via the
@@ -10364,8 +10444,150 @@ var TurnOutcomeTracker = class {
10364
10444
  }
10365
10445
  };
10366
10446
 
10447
+ // src/lib/busy-bucket-ledger.ts
10448
+ var BUCKET_MS = 6e4;
10449
+ var MAX_ACCRUAL_SPAN_MS = 10 * 6e4;
10450
+ var MAX_RETAINED_BUCKETS = 240;
10451
+ var BILL_WHOLE_MINUTES = true;
10452
+ var MIN_BILLABLE_MS = 100;
10453
+ function bucketStartMs(atMs) {
10454
+ return Math.floor(atMs / BUCKET_MS) * BUCKET_MS;
10455
+ }
10456
+ var BusyBucketLedger = class {
10457
+ state = /* @__PURE__ */ new Map();
10458
+ entry(codeName) {
10459
+ let cur = this.state.get(codeName);
10460
+ if (!cur) {
10461
+ cur = { buckets: /* @__PURE__ */ new Map(), openSince: null };
10462
+ this.state.set(codeName, cur);
10463
+ }
10464
+ return cur;
10465
+ }
10466
+ /**
10467
+ * Credit occupancy for the span [fromMs, toMs), splitting it across every
10468
+ * minute bucket it covers. Safe to call with any ordering — a reversed or
10469
+ * zero-length span is a no-op rather than a negative credit.
10470
+ */
10471
+ accrueSpan(codeName, fromMs, toMs) {
10472
+ if (!Number.isFinite(fromMs) || !Number.isFinite(toMs)) return;
10473
+ if (toMs <= fromMs) return;
10474
+ const start = Math.max(fromMs, toMs - MAX_ACCRUAL_SPAN_MS);
10475
+ const led = this.entry(codeName);
10476
+ for (let b = bucketStartMs(start); b < toMs; b += BUCKET_MS) {
10477
+ const overlap = Math.min(toMs, b + BUCKET_MS) - Math.max(start, b);
10478
+ if (overlap <= 0) continue;
10479
+ const prev = led.buckets.get(b) ?? 0;
10480
+ led.buckets.set(b, Math.min(BUCKET_MS, prev + overlap));
10481
+ }
10482
+ this.evict(led);
10483
+ }
10484
+ /** The agent started doing work. Idempotent while already occupied. */
10485
+ open(codeName, atMs) {
10486
+ const led = this.entry(codeName);
10487
+ if (led.openSince == null) led.openSince = atMs;
10488
+ }
10489
+ /**
10490
+ * Accrue everything owed up to `nowMs` WITHOUT ending the occupancy. This is
10491
+ * what makes a long turn fill every bucket it spans: each probe pass ticks,
10492
+ * banking the elapsed slice and moving the watermark forward.
10493
+ */
10494
+ tick(codeName, nowMs) {
10495
+ const led = this.state.get(codeName);
10496
+ if (!led || led.openSince == null) return;
10497
+ if (nowMs <= led.openSince) return;
10498
+ this.accrueSpan(codeName, led.openSince, nowMs);
10499
+ led.openSince = nowMs;
10500
+ }
10501
+ /** The agent stopped doing work. Banks the final slice. */
10502
+ close(codeName, atMs) {
10503
+ const led = this.state.get(codeName);
10504
+ if (!led || led.openSince == null) return;
10505
+ this.accrueSpan(codeName, led.openSince, atMs);
10506
+ led.openSince = null;
10507
+ }
10508
+ /**
10509
+ * Take every CLOSED bucket for reporting, removing it from the ledger.
10510
+ *
10511
+ * Only closed buckets — a bucket whose minute has not yet elapsed is still
10512
+ * accruing, and reporting it early would send a partial value that the next
10513
+ * drain would have to correct. The open bucket stays and is drained once it
10514
+ * closes.
10515
+ *
10516
+ * DESTRUCTIVE, deliberately: this is the same read-and-reset contract as the
10517
+ * watchdog give-up counters, and it carries the same obligation — the caller
10518
+ * MUST `credit()` the result back if the POST fails, or a transient 5xx
10519
+ * permanently deletes billable occupancy that cannot be reconstructed.
10520
+ */
10521
+ drainClosed(codeName, nowMs) {
10522
+ const led = this.state.get(codeName);
10523
+ if (!led) return [];
10524
+ this.tick(codeName, nowMs);
10525
+ const openBucket = bucketStartMs(nowMs);
10526
+ const out = [];
10527
+ for (const [b, ms] of [...led.buckets].sort((x, y) => x[0] - y[0])) {
10528
+ if (b >= openBucket) continue;
10529
+ led.buckets.delete(b);
10530
+ if (ms < MIN_BILLABLE_MS) continue;
10531
+ const seconds = BILL_WHOLE_MINUTES ? 60 : Math.min(60, Math.ceil(ms / 1e3));
10532
+ out.push({ bucket: new Date(b).toISOString(), seconds });
10533
+ }
10534
+ return out;
10535
+ }
10536
+ /**
10537
+ * Put drained buckets back after a failed POST. Merges rather than replaces,
10538
+ * so occupancy accrued since the drain is preserved.
10539
+ */
10540
+ credit(codeName, buckets) {
10541
+ if (buckets.length === 0) return;
10542
+ const led = this.entry(codeName);
10543
+ for (const b of buckets) {
10544
+ const at = Date.parse(b.bucket);
10545
+ if (!Number.isFinite(at)) continue;
10546
+ const key = bucketStartMs(at);
10547
+ const prev = led.buckets.get(key) ?? 0;
10548
+ led.buckets.set(key, Math.min(BUCKET_MS, prev + b.seconds * 1e3));
10549
+ }
10550
+ this.evict(led);
10551
+ }
10552
+ /** Agents currently holding reportable state. */
10553
+ trackedAgents() {
10554
+ return [...this.state.keys()];
10555
+ }
10556
+ /**
10557
+ * Drop everything for an agent. Called from the same teardown that resets
10558
+ * turn health, so a fresh session is not born holding the dead one's
10559
+ * occupancy.
10560
+ */
10561
+ reset(codeName) {
10562
+ this.state.delete(codeName);
10563
+ }
10564
+ evict(led) {
10565
+ if (led.buckets.size <= MAX_RETAINED_BUCKETS) return;
10566
+ const ordered = [...led.buckets.keys()].sort((a, b) => a - b);
10567
+ for (const k of ordered.slice(0, led.buckets.size - MAX_RETAINED_BUCKETS)) {
10568
+ led.buckets.delete(k);
10569
+ }
10570
+ }
10571
+ };
10572
+ var sharedBusyBuckets = new BusyBucketLedger();
10573
+
10367
10574
  // src/lib/opencode-activity-tracker.ts
10368
10575
  var OpencodeActivityTracker = class {
10576
+ /**
10577
+ * ENG-8116: the same lifecycle also drives the duration ledger. opencode is
10578
+ * the runtime that can measure occupancy EXACTLY — beginTurn/endTurn bracket
10579
+ * real work — so the spans go straight in rather than being inferred from a
10580
+ * file mtime the way Claude Code's have to be.
10581
+ *
10582
+ * Kept inside this class deliberately, rather than having callers poke both:
10583
+ * the lifecycle discipline that makes occupancy correct (the `finally`, the
10584
+ * live-session guard) already lives at one call site, and a second one to
10585
+ * keep in sync is exactly how a leaked in-flight count would bill an idle
10586
+ * agent around the clock.
10587
+ */
10588
+ constructor(ledger = sharedBusyBuckets) {
10589
+ this.ledger = ledger;
10590
+ }
10369
10591
  state = /* @__PURE__ */ new Map();
10370
10592
  entry(codeName) {
10371
10593
  let cur = this.state.get(codeName);
@@ -10376,8 +10598,9 @@ var OpencodeActivityTracker = class {
10376
10598
  return cur;
10377
10599
  }
10378
10600
  /** A turn has been dispatched to the serve. Call BEFORE awaiting it. */
10379
- beginTurn(codeName) {
10601
+ beginTurn(codeName, now = Date.now()) {
10380
10602
  this.entry(codeName).inFlight += 1;
10603
+ this.ledger.open(codeName, now);
10381
10604
  }
10382
10605
  /**
10383
10606
  * A dispatched turn has resolved. Call in a `finally`, so a throw cannot
@@ -10391,6 +10614,7 @@ var OpencodeActivityTracker = class {
10391
10614
  const cur = this.entry(codeName);
10392
10615
  cur.inFlight = Math.max(0, cur.inFlight - 1);
10393
10616
  if (counted) cur.lastActiveAt = now;
10617
+ if (cur.inFlight === 0) this.ledger.close(codeName, now);
10394
10618
  }
10395
10619
  /**
10396
10620
  * Seconds since this agent was last doing work, or null if it never has been
@@ -10414,6 +10638,7 @@ var OpencodeActivityTracker = class {
10414
10638
  */
10415
10639
  reset(codeName) {
10416
10640
  this.state.delete(codeName);
10641
+ this.ledger.reset(codeName);
10417
10642
  }
10418
10643
  };
10419
10644
 
@@ -12688,6 +12913,7 @@ export {
12688
12913
  sha256,
12689
12914
  hashFile,
12690
12915
  execFilePromiseLong,
12916
+ sharedBusyBuckets,
12691
12917
  readProvisionedOpencodeModel,
12692
12918
  startOpencodeSession,
12693
12919
  readOpencodePaneLogTail,
@@ -12745,4 +12971,4 @@ export {
12745
12971
  stopAllSessionsAndWait,
12746
12972
  getProjectDir
12747
12973
  };
12748
- //# sourceMappingURL=chunk-CWIWI2ZX.js.map
12974
+ //# sourceMappingURL=chunk-HXP4I5QZ.js.map