@stackbone/cli 0.2.2 → 0.2.4

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/CHANGELOG.md CHANGED
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.3] - 2026-07-06
11
+
12
+ ### Fixed
13
+
14
+ - **`stackbone init` (and every other command) could crash with `ERR_MODULE_NOT_FOUND: Cannot find package 'drizzle-orm'`** on some `pnpm dlx`/global-install layouts. The CLI carried a dead "marker import" of `drizzle-kit/api` purely so the bundler would register `drizzle-kit` as a dependency — but that import is loaded eagerly on every CLI boot, and `drizzle-kit/api` itself requires `drizzle-orm` without declaring it as a dependency. On pnpm setups with an isolated `node_modules` layout, `drizzle-kit` can't see `drizzle-orm` even when it's installed right next to it, so the CLI crashed before running any command. Removed the marker import; `drizzle-kit` is now declared explicitly in the build config instead, and the CLI only touches `drizzle-kit` at all when a migration is genuinely being generated (`db migrate create`, or `stackbone dev`'s auto-migrate).
15
+
10
16
  ## [0.2.2] - 2026-07-06
11
17
 
12
18
  ### Fixed
package/main.js CHANGED
@@ -38,7 +38,6 @@ import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
38
38
  import { cancel, intro, isCancel, log, note, outro, select, spinner, text as text$1 } from "@clack/prompts";
39
39
  import open$1 from "open";
40
40
  import { readMigrationFiles } from "drizzle-orm/migrator";
41
- import "drizzle-kit/api";
42
41
  import { createServer } from "node:net";
43
42
  import { create } from "tar";
44
43
  import { Readable } from "node:stream";
@@ -2989,7 +2988,7 @@ var DEV_HMAC_SECRET = "dev-stackbone-local-emulator-hmac-secret";
2989
2988
  //#endregion
2990
2989
  //#region ../../libs/runtime/agent-emulator/src/lib/server/contract.ts
2991
2990
  init_src$14();
2992
- var STACKBONE_CLI_BUILD_VERSION = "0.2.2";
2991
+ var STACKBONE_CLI_BUILD_VERSION = "0.2.4";
2993
2992
  /**
2994
2993
  * Capabilities the local emulator advertises on `GET /api/contract`, derived
2995
2994
  * from the route registry above so the advertised set and the mounted routes
@@ -3020,7 +3019,11 @@ var buildContractResponse = () => contractResponseSchema.parse({
3020
3019
  });
3021
3020
  //#endregion
3022
3021
  //#region ../../libs/runtime/agent-emulator/src/lib/server/cors.ts
3023
- var DEFAULT_ALLOWED_ORIGINS = ["https://app.stackbone.ai", "http://localhost:*"];
3022
+ var DEFAULT_ALLOWED_ORIGINS = [
3023
+ "https://app.stackbone.ai",
3024
+ "https://chat.stackbone.ai",
3025
+ "http://localhost:*"
3026
+ ];
3024
3027
  var escapeRegex = (input) => input.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
3025
3028
  var compilePattern = (pattern) => {
3026
3029
  const escaped = escapeRegex(pattern.trim()).replace(/\*/g, "[^/.]+");
@@ -13709,7 +13712,15 @@ var init_trigger_descriptor = __esmMin((() => {
13709
13712
  /** Dot-path, per item, to the sortable marker used to compute the next cursor. */
13710
13713
  field: z.string(),
13711
13714
  /** Request arg set to the stored cursor so the provider filters server-side. */
13712
- param: z.string()
13715
+ param: z.string(),
13716
+ /**
13717
+ * Optional template for the parameter value — when present, the stored cursor
13718
+ * is interpolated into this template via `{cursor}` instead of injected as a
13719
+ * bare value. Use when the provider needs a full expression rather than a raw
13720
+ * value, e.g. `receivedDateTime gt {cursor}` for OData `$filter` params.
13721
+ * Absent = backward-compatible bare-value injection.
13722
+ */
13723
+ cursorTemplate: z.string().optional()
13713
13724
  }), z.object({
13714
13725
  kind: z.literal("opaque"),
13715
13726
  /** Dot-path in the list response to the next opaque token. */
@@ -13729,6 +13740,23 @@ var init_trigger_descriptor = __esmMin((() => {
13729
13740
  pageToken: z.object({
13730
13741
  requestParam: z.string(),
13731
13742
  responsePath: z.string()
13743
+ }).optional(),
13744
+ /**
13745
+ * Offset-based pagination via `$skip`/`$top` — an alternative to `pageToken`
13746
+ * for providers whose OpenAPI spec does not expose the page cursor (e.g.
13747
+ * Microsoft Graph, which paginates via `@odata.nextLink` URLs but only
13748
+ * exposes `$skip` as a query parameter). When present, the engine calls the
13749
+ * list operation with `pageSizeParam` set to `stride` on every request, and
13750
+ * increments `param` by `stride` each page. Stops when a page returns fewer
13751
+ * items than `stride`. Mutually exclusive with `pageToken`.
13752
+ */
13753
+ pageSkip: z.object({
13754
+ /** Query param name for the offset, e.g. `$skip`. */
13755
+ param: z.string(),
13756
+ /** Query param name for the page size, e.g. `$top`. */
13757
+ pageSizeParam: z.string(),
13758
+ /** Number of items per page, also the stride for incrementing `param`. */
13759
+ stride: z.number()
13732
13760
  }).optional()
13733
13761
  });
13734
13762
  triggerItemStepSchema = z.object({
@@ -13897,6 +13925,173 @@ var init_registry_store = __esmMin((() => {
13897
13925
  };
13898
13926
  }));
13899
13927
  //#endregion
13928
+ //#region ../../libs/shared/connect-registry/src/lib/catalog/gmail/gmail.connector.ts
13929
+ var GMAIL_CONNECTOR;
13930
+ var init_gmail_connector = __esmMin((() => {
13931
+ GMAIL_CONNECTOR = {
13932
+ id: "gmail",
13933
+ displayName: "Gmail",
13934
+ description: "Send email and read messages from a connected Gmail account.",
13935
+ logoUrl: "https://t0.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=https://mail.google.com&size=64",
13936
+ category: "email",
13937
+ protocol: "openapi",
13938
+ baseUrl: "https://gmail.googleapis.com/gmail/v1/users/me",
13939
+ authMode: "oauth",
13940
+ placement: { kind: "bearer" },
13941
+ scopes: [
13942
+ "openid",
13943
+ "email",
13944
+ "profile",
13945
+ "https://www.googleapis.com/auth/gmail.readonly",
13946
+ "https://www.googleapis.com/auth/gmail.send"
13947
+ ],
13948
+ authorizeUrl: "https://accounts.google.com/o/oauth2/v2/auth",
13949
+ tokenUrl: "https://oauth2.googleapis.com/token",
13950
+ authorizeParams: {
13951
+ access_type: "offline",
13952
+ prompt: "consent"
13953
+ },
13954
+ specSource: {
13955
+ kind: "url",
13956
+ url: "https://api.apis.guru/v2/specs/googleapis.com/gmail/v1/openapi.json"
13957
+ },
13958
+ triggers: [{
13959
+ id: "message-received",
13960
+ displayName: "Message received",
13961
+ description: "Fires when a new message arrives in the inbox.",
13962
+ sampleData: {
13963
+ messageId: "18e0a1b2c3d4e5f6",
13964
+ threadId: "18e0a1b2c3d4e5f6",
13965
+ from: "Ada Lovelace <ada@example.com>",
13966
+ to: "agent@stackbone.ai",
13967
+ subject: "Quarterly report",
13968
+ snippet: "Here is the quarterly report you asked for…",
13969
+ date: "Mon, 10 Jun 2024 09:15:00 +0000",
13970
+ raw: {
13971
+ messageId: "18e0a1b2c3d4e5f6",
13972
+ threadId: "18e0a1b2c3d4e5f6",
13973
+ historyId: "987654"
13974
+ }
13975
+ },
13976
+ poll: {
13977
+ kind: "strategy",
13978
+ strategy: "gmail.history"
13979
+ }
13980
+ }]
13981
+ };
13982
+ }));
13983
+ //#endregion
13984
+ //#region ../../libs/shared/connect-registry/src/lib/catalog/outlook/outlook.connector.ts
13985
+ var OUTLOOK_CONNECTOR;
13986
+ var init_outlook_connector = __esmMin((() => {
13987
+ OUTLOOK_CONNECTOR = {
13988
+ id: "outlook",
13989
+ displayName: "Outlook",
13990
+ description: "Send email and read messages from a connected Outlook account.",
13991
+ logoUrl: "https://t0.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=http://microsoft.com&size=64",
13992
+ category: "email",
13993
+ protocol: "openapi",
13994
+ baseUrl: "https://graph.microsoft.com/v1.0",
13995
+ authMode: "oauth",
13996
+ placement: { kind: "bearer" },
13997
+ scopes: [
13998
+ "openid",
13999
+ "email",
14000
+ "profile",
14001
+ "Mail.Read",
14002
+ "Mail.Send",
14003
+ "offline_access"
14004
+ ],
14005
+ authorizeUrl: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
14006
+ tokenUrl: "https://login.microsoftonline.com/common/oauth2/v2.0/token",
14007
+ specSource: {
14008
+ kind: "url",
14009
+ url: "https://raw.githubusercontent.com/microsoftgraph/msgraph-metadata/master/openapi/v1.0/default.yaml"
14010
+ },
14011
+ authorizeParams: { prompt: "consent" },
14012
+ triggers: [{
14013
+ id: "message-received",
14014
+ displayName: "Message received",
14015
+ description: "Fires when a new message arrives in the Inbox.",
14016
+ sampleData: {
14017
+ messageId: "AAMkAD...",
14018
+ conversationId: "AAQkAD...",
14019
+ from: {
14020
+ name: "Ada Lovelace",
14021
+ address: "ada@example.com"
14022
+ },
14023
+ subject: "Quarterly report",
14024
+ snippet: "Here is the quarterly report...",
14025
+ date: "2024-06-10T09:15:00Z",
14026
+ isRead: false
14027
+ },
14028
+ poll: {
14029
+ kind: "declarative",
14030
+ list: {
14031
+ operationId: "me.mailFolders.ListMessages",
14032
+ args: { "mailFolder-id": "Inbox" },
14033
+ itemsPath: "value",
14034
+ pageSkip: {
14035
+ param: "$skip",
14036
+ pageSizeParam: "$top",
14037
+ stride: 100
14038
+ }
14039
+ },
14040
+ cursor: {
14041
+ kind: "timestamp",
14042
+ field: "receivedDateTime",
14043
+ param: "$filter",
14044
+ cursorTemplate: "receivedDateTime gt {cursor}"
14045
+ },
14046
+ dedup: { idPath: "id" },
14047
+ map: {
14048
+ messageId: "id",
14049
+ conversationId: "conversationId",
14050
+ from: "from",
14051
+ subject: "subject",
14052
+ snippet: "bodyPreview",
14053
+ date: "receivedDateTime",
14054
+ isRead: "isRead"
14055
+ }
14056
+ }
14057
+ }]
14058
+ };
14059
+ }));
14060
+ //#endregion
14061
+ //#region ../../libs/shared/connect-registry/src/lib/catalog/telegram/telegram.connector.ts
14062
+ var TELEGRAM_CONNECTOR;
14063
+ var init_telegram_connector = __esmMin((() => {
14064
+ TELEGRAM_CONNECTOR = {
14065
+ id: "telegram",
14066
+ displayName: "Telegram",
14067
+ description: "Send and receive messages through a Telegram bot.",
14068
+ logoUrl: "https://t0.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=https://telegram.org/&size=64",
14069
+ category: "messaging",
14070
+ protocol: "openapi",
14071
+ baseUrl: "https://api.telegram.org",
14072
+ authMode: "api_key",
14073
+ placement: { kind: "bearer" },
14074
+ scopes: []
14075
+ };
14076
+ }));
14077
+ //#endregion
14078
+ //#region ../../libs/shared/connect-registry/src/lib/catalog/whatsapp/whatsapp.connector.ts
14079
+ var WHATSAPP_CONNECTOR;
14080
+ var init_whatsapp_connector = __esmMin((() => {
14081
+ WHATSAPP_CONNECTOR = {
14082
+ id: "whatsapp",
14083
+ displayName: "WhatsApp",
14084
+ description: "Send and receive messages through a WhatsApp bot.",
14085
+ logoUrl: "https://t0.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=https://www.whatsapp.com/&size=64",
14086
+ category: "messaging",
14087
+ protocol: "openapi",
14088
+ baseUrl: "https://api.whatsapp.com/v1",
14089
+ authMode: "api_key",
14090
+ placement: { kind: "bearer" },
14091
+ scopes: []
14092
+ };
14093
+ }));
14094
+ //#endregion
13900
14095
  //#region ../../libs/shared/connect-registry/src/lib/catalog/built-in-catalog.ts
13901
14096
  /**
13902
14097
  * Seed `store` with the built-in catalog. Returns `loadFromJson`'s outcome so the
@@ -13908,91 +14103,15 @@ function loadBuiltInCatalog(store) {
13908
14103
  }
13909
14104
  var BUILT_IN_CONNECTOR_DOCS;
13910
14105
  var init_built_in_catalog = __esmMin((() => {
14106
+ init_gmail_connector();
14107
+ init_outlook_connector();
14108
+ init_telegram_connector();
14109
+ init_whatsapp_connector();
13911
14110
  BUILT_IN_CONNECTOR_DOCS = [
13912
- {
13913
- id: "gmail",
13914
- displayName: "Gmail",
13915
- description: "Send email and read messages from a connected Gmail account.",
13916
- logoUrl: "https://t0.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=https://mail.google.com&size=64",
13917
- category: "email",
13918
- protocol: "openapi",
13919
- baseUrl: "https://gmail.googleapis.com/gmail/v1/users/me",
13920
- authMode: "oauth",
13921
- placement: { kind: "bearer" },
13922
- scopes: [
13923
- "openid",
13924
- "email",
13925
- "profile",
13926
- "https://www.googleapis.com/auth/gmail.readonly",
13927
- "https://www.googleapis.com/auth/gmail.send"
13928
- ],
13929
- authorizeUrl: "https://accounts.google.com/o/oauth2/v2/auth",
13930
- tokenUrl: "https://oauth2.googleapis.com/token",
13931
- authorizeParams: {
13932
- access_type: "offline",
13933
- prompt: "consent"
13934
- },
13935
- specSource: {
13936
- kind: "url",
13937
- url: "https://api.apis.guru/v2/specs/googleapis.com/gmail/v1/openapi.json"
13938
- },
13939
- triggers: [{
13940
- id: "message-received",
13941
- displayName: "Message received",
13942
- description: "Fires when a new message arrives in the inbox.",
13943
- sampleData: {
13944
- messageId: "18e0a1b2c3d4e5f6",
13945
- threadId: "18e0a1b2c3d4e5f6",
13946
- from: "Ada Lovelace <ada@example.com>",
13947
- to: "agent@stackbone.ai",
13948
- subject: "Quarterly report",
13949
- snippet: "Here is the quarterly report you asked for…",
13950
- date: "Mon, 10 Jun 2024 09:15:00 +0000",
13951
- raw: {
13952
- messageId: "18e0a1b2c3d4e5f6",
13953
- threadId: "18e0a1b2c3d4e5f6",
13954
- historyId: "987654"
13955
- }
13956
- },
13957
- poll: {
13958
- kind: "strategy",
13959
- strategy: "gmail.history"
13960
- }
13961
- }]
13962
- },
13963
- {
13964
- id: "outlook",
13965
- displayName: "Outlook",
13966
- description: "Send email and read messages from a connected Outlook account.",
13967
- logoUrl: "https://t0.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=http://microsoft.com&size=64",
13968
- category: "email",
13969
- protocol: "openapi",
13970
- baseUrl: "https://graph.microsoft.com/v1.0",
13971
- authMode: "oauth",
13972
- placement: { kind: "bearer" },
13973
- scopes: [
13974
- "openid",
13975
- "email",
13976
- "profile",
13977
- "Mail.Read",
13978
- "Mail.Send",
13979
- "offline_access"
13980
- ],
13981
- authorizeUrl: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
13982
- tokenUrl: "https://login.microsoftonline.com/common/oauth2/v2.0/token"
13983
- },
13984
- {
13985
- id: "telegram",
13986
- displayName: "Telegram",
13987
- description: "Send and receive messages through a Telegram bot.",
13988
- logoUrl: "https://t0.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=https://telegram.org/&size=64",
13989
- category: "messaging",
13990
- protocol: "openapi",
13991
- baseUrl: "https://api.telegram.org",
13992
- authMode: "api_key",
13993
- placement: { kind: "bearer" },
13994
- scopes: []
13995
- }
14111
+ GMAIL_CONNECTOR,
14112
+ OUTLOOK_CONNECTOR,
14113
+ TELEGRAM_CONNECTOR,
14114
+ WHATSAPP_CONNECTOR
13996
14115
  ];
13997
14116
  }));
13998
14117
  //#endregion
@@ -14565,14 +14684,17 @@ function latestTimestamp(items, field) {
14565
14684
  function createDeclarativePoll(spec) {
14566
14685
  return async (ctx, cursor) => {
14567
14686
  const baseArgs = { ...spec.list.args ?? {} };
14568
- if (cursor !== null && cursor !== void 0) if (spec.cursor.kind === "timestamp") baseArgs[spec.cursor.param] = cursor;
14687
+ if (cursor !== null && cursor !== void 0) if (spec.cursor.kind === "timestamp") baseArgs[spec.cursor.param] = spec.cursor.cursorTemplate ? spec.cursor.cursorTemplate.replace("{cursor}", String(cursor)) : cursor;
14569
14688
  else baseArgs[spec.cursor.requestParam] = cursor;
14570
14689
  const rawItems = [];
14571
14690
  let opaqueNext;
14572
14691
  let pageToken;
14573
14692
  for (let page = 0; page < MAX_PAGES; page += 1) {
14574
14693
  const args = { ...baseArgs };
14575
- if (pageToken !== null && pageToken !== void 0 && spec.list.pageToken) args[spec.list.pageToken.requestParam] = pageToken;
14694
+ if (spec.list.pageSkip) {
14695
+ args[spec.list.pageSkip.pageSizeParam] = spec.list.pageSkip.stride;
14696
+ if (page > 0) args[spec.list.pageSkip.param] = page * spec.list.pageSkip.stride;
14697
+ } else if (pageToken !== null && pageToken !== void 0 && spec.list.pageToken) args[spec.list.pageToken.requestParam] = pageToken;
14576
14698
  const response = await ctx.runOperation(spec.list.operationId, args);
14577
14699
  const items = getPath(response, spec.list.itemsPath);
14578
14700
  if (Array.isArray(items)) rawItems.push(...items);
@@ -14580,8 +14702,12 @@ function createDeclarativePoll(spec) {
14580
14702
  const next = getPath(response, spec.cursor.responsePath);
14581
14703
  if (next !== null && next !== void 0) opaqueNext = next;
14582
14704
  }
14583
- pageToken = spec.list.pageToken ? getPath(response, spec.list.pageToken.responsePath) : void 0;
14584
- if (pageToken === null || pageToken === void 0) break;
14705
+ if (spec.list.pageSkip) {
14706
+ if (!Array.isArray(items) || items.length < spec.list.pageSkip.stride) break;
14707
+ } else {
14708
+ pageToken = spec.list.pageToken ? getPath(response, spec.list.pageToken.responsePath) : void 0;
14709
+ if (pageToken === null || pageToken === void 0) break;
14710
+ }
14585
14711
  }
14586
14712
  let items = rawItems;
14587
14713
  if (spec.item) {
@@ -14607,6 +14733,113 @@ var init_declarative_poll = __esmMin((() => {
14607
14733
  MAX_PAGES = 100;
14608
14734
  }));
14609
14735
  //#endregion
14736
+ //#region ../../libs/shared/connect-registry/src/lib/catalog/gmail/gmail.history-strategy.ts
14737
+ function isGmailCursor(cursor) {
14738
+ return typeof cursor === "object" && cursor !== null && typeof cursor.historyId === "string" && cursor.historyId.length > 0;
14739
+ }
14740
+ /** Read a single header value (case-insensitive) off a Gmail message payload. */
14741
+ function header(message, name) {
14742
+ const match = (message.payload?.headers ?? []).find((h) => (h.name ?? "").toLowerCase() === name.toLowerCase());
14743
+ return typeof match?.value === "string" ? match.value : "";
14744
+ }
14745
+ /** Fetch one message (metadata format) and normalize it to a poll item. */
14746
+ async function fetchMessageEvent(ctx, id) {
14747
+ const message = await ctx.runOperation("gmail.users.messages.get", {
14748
+ userId: USER_ID,
14749
+ id,
14750
+ format: "metadata"
14751
+ });
14752
+ const payload = {
14753
+ messageId: message.id ?? id,
14754
+ threadId: message.threadId ?? "",
14755
+ from: header(message, "From"),
14756
+ to: header(message, "To"),
14757
+ subject: header(message, "Subject"),
14758
+ snippet: typeof message.snippet === "string" ? message.snippet : "",
14759
+ date: header(message, "Date"),
14760
+ raw: {
14761
+ messageId: message.id ?? id,
14762
+ threadId: message.threadId ?? "",
14763
+ historyId: typeof message.historyId === "string" ? message.historyId : null
14764
+ }
14765
+ };
14766
+ return {
14767
+ id: message.id ?? id,
14768
+ payload
14769
+ };
14770
+ }
14771
+ function readHistoryId(event) {
14772
+ const raw = (event?.payload)?.raw;
14773
+ return raw && typeof raw.historyId === "string" ? raw.historyId : null;
14774
+ }
14775
+ /**
14776
+ * Bootstrap path (empty cursor): list the most recent inbox messages, normalize
14777
+ * them, and seed the cursor from the newest message's `historyId`. Dedup is the
14778
+ * platform's job (by message id), so a first poll legitimately returns the recent
14779
+ * window and the next poll resumes from the history API.
14780
+ */
14781
+ async function bootstrap(ctx) {
14782
+ const items = await mapWithConcurrency(((await ctx.runOperation("gmail.users.messages.list", {
14783
+ userId: USER_ID,
14784
+ maxResults: BOOTSTRAP_MAX_RESULTS,
14785
+ labelIds: "INBOX"
14786
+ })).messages ?? []).map((m) => m.id).filter((id) => typeof id === "string" && id.length > 0), 6, (id) => fetchMessageEvent(ctx, id));
14787
+ const seedHistoryId = readHistoryId(items[0]);
14788
+ return {
14789
+ items,
14790
+ nextCursor: seedHistoryId ? { historyId: seedHistoryId } : null
14791
+ };
14792
+ }
14793
+ /**
14794
+ * Incremental path (valid cursor): pull history records added since the persisted
14795
+ * `historyId` via `history.list`, drain EVERY page (so messages on pages 2+ are
14796
+ * never skipped when the cursor advances), normalize each new arrival, and move
14797
+ * the cursor to the response's latest `historyId`.
14798
+ */
14799
+ async function pollFromCursor(ctx, cursor) {
14800
+ const addedIds = [];
14801
+ let latestHistoryId = cursor.historyId;
14802
+ let pageToken;
14803
+ for (let page = 0; page < MAX_HISTORY_PAGES; page += 1) {
14804
+ const history = await ctx.runOperation("gmail.users.history.list", {
14805
+ userId: USER_ID,
14806
+ startHistoryId: cursor.historyId,
14807
+ historyTypes: "messageAdded",
14808
+ labelId: "INBOX",
14809
+ ...pageToken ? { pageToken } : {}
14810
+ });
14811
+ for (const record of history.history ?? []) for (const added of record.messagesAdded ?? []) {
14812
+ const id = added.message?.id;
14813
+ if (typeof id === "string" && id.length > 0) addedIds.push(id);
14814
+ }
14815
+ if (typeof history.historyId === "string" && history.historyId.length > 0) latestHistoryId = history.historyId;
14816
+ if (typeof history.nextPageToken === "string" && history.nextPageToken.length > 0) pageToken = history.nextPageToken;
14817
+ else break;
14818
+ }
14819
+ return {
14820
+ items: await mapWithConcurrency(addedIds, 6, (id) => fetchMessageEvent(ctx, id)),
14821
+ nextCursor: { historyId: latestHistoryId }
14822
+ };
14823
+ }
14824
+ var USER_ID, BOOTSTRAP_MAX_RESULTS, MAX_HISTORY_PAGES, gmailHistoryPoll;
14825
+ var init_gmail_history_strategy = __esmMin((() => {
14826
+ init_concurrency();
14827
+ USER_ID = "me";
14828
+ BOOTSTRAP_MAX_RESULTS = 25;
14829
+ MAX_HISTORY_PAGES = 100;
14830
+ gmailHistoryPoll = async (ctx, cursor) => {
14831
+ if (!isGmailCursor(cursor)) return bootstrap(ctx);
14832
+ return pollFromCursor(ctx, cursor);
14833
+ };
14834
+ }));
14835
+ //#endregion
14836
+ //#region ../../libs/shared/connect-registry/src/lib/catalog/built-in-strategies.ts
14837
+ var BUILT_IN_STRATEGIES;
14838
+ var init_built_in_strategies = __esmMin((() => {
14839
+ init_gmail_history_strategy();
14840
+ BUILT_IN_STRATEGIES = { "gmail.history": gmailHistoryPoll };
14841
+ }));
14842
+ //#endregion
14610
14843
  //#region ../../libs/shared/connect-registry/src/index.ts
14611
14844
  var init_src$3 = __esmMin((() => {
14612
14845
  init_types$1();
@@ -14619,6 +14852,7 @@ var init_src$3 = __esmMin((() => {
14619
14852
  init_trigger_descriptor();
14620
14853
  init_declarative_poll();
14621
14854
  init_concurrency();
14855
+ init_built_in_strategies();
14622
14856
  }));
14623
14857
  //#endregion
14624
14858
  //#region ../../libs/platform/connect-broker/src/lib/executor/spec-introspection.ts
@@ -16692,8 +16926,14 @@ function invalidRequest(message) {
16692
16926
  }
16693
16927
  //#endregion
16694
16928
  //#region ../../libs/runtime/agent-emulator/src/lib/deep-agents/registry.ts
16695
- var DEEP_AGENTS_WELL_KNOWN_DIR, isPlainRecord, readConfigStrings, toMetadata, isDeepAgentDefinition, bundleAndLoad, loadPrebuilt, createDeepAgentRegistry, errMessage$9, matchChangedPathsToAgents;
16929
+ var SYSTEM_DEEP_AGENTS, isInternalDeepAgent, DEEP_AGENTS_WELL_KNOWN_DIR, isPlainRecord, readConfigStrings, toMetadata, isDeepAgentDefinition, bundleAndLoad, loadPrebuilt, createDeepAgentRegistry, errMessage$9, matchChangedPathsToAgents;
16696
16930
  var init_registry$1 = __esmMin((() => {
16931
+ SYSTEM_DEEP_AGENTS = new Set(["rag-ingest"]);
16932
+ isInternalDeepAgent = (agent) => {
16933
+ if (agent.$internal === false) return false;
16934
+ if (agent.$internal === true) return true;
16935
+ return SYSTEM_DEEP_AGENTS.has(agent.name);
16936
+ };
16697
16937
  DEEP_AGENTS_WELL_KNOWN_DIR = ".well-known/deep-agents";
16698
16938
  isPlainRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
16699
16939
  readConfigStrings = (config) => {
@@ -16785,7 +17025,8 @@ var init_registry$1 = __esmMin((() => {
16785
17025
  metadata: toMetadata(def)
16786
17026
  },
16787
17027
  createGraph: def.createGraph.bind(def),
16788
- supportsCheckpointer: def.capabilities?.checkpointer === true
17028
+ supportsCheckpointer: def.capabilities?.checkpointer === true,
17029
+ $internal: isInternalDeepAgent(agent)
16789
17030
  });
16790
17031
  logger.info(`deep agent "${agent.name}" ready (model ${def.metadata.model}).`);
16791
17032
  return true;
@@ -16826,7 +17067,7 @@ var init_registry$1 = __esmMin((() => {
16826
17067
  return {
16827
17068
  list: () => {
16828
17069
  const checkpointerReady = opts.checkpointer?.status?.() === "ready";
16829
- return [...registered.values()].map((e) => ({
17070
+ return [...registered.values()].filter((e) => !e.$internal).map((e) => ({
16830
17071
  name: e.view.name,
16831
17072
  metadata: e.view.metadata,
16832
17073
  durable: e.supportsCheckpointer && checkpointerReady
@@ -22237,13 +22478,19 @@ var init_workflow_step_world_port = __esmMin((() => {
22237
22478
  }));
22238
22479
  //#endregion
22239
22480
  //#region ../../libs/runtime/agent-emulator/src/lib/workflow/registry.ts
22240
- var hasDeclaredSchema, workflowStartTrigger, buildManifest;
22481
+ var hasDeclaredSchema, workflowStartTrigger, SYSTEM_WORKFLOWS, isInternalWorkflow, buildManifest;
22241
22482
  var init_registry = __esmMin((() => {
22242
22483
  hasDeclaredSchema = (pair) => pair != null && (pair.input != null || pair.output != null);
22243
22484
  workflowStartTrigger = (name) => `POST /api/workflows/${name}/start`;
22485
+ SYSTEM_WORKFLOWS = new Set(["rag-ingest"]);
22486
+ isInternalWorkflow = (workflow) => {
22487
+ if (workflow.$internal === false) return false;
22488
+ if (workflow.$internal === true) return true;
22489
+ return SYSTEM_WORKFLOWS.has(workflow.name);
22490
+ };
22244
22491
  buildManifest = (input) => ({
22245
22492
  agents: [],
22246
- workflows: input.workspace.workflows.map((w) => {
22493
+ workflows: input.workspace.workflows.filter((w) => !isInternalWorkflow(w)).map((w) => {
22247
22494
  const steps = input.workflowSteps?.get(w.name);
22248
22495
  return {
22249
22496
  name: w.name,
@@ -22485,6 +22732,28 @@ var init_workflow_start_service = __esmMin((() => {
22485
22732
  return triggerWorkflowStartAndWait(deps, name, input);
22486
22733
  }
22487
22734
  });
22735
+ }));
22736
+ //#endregion
22737
+ //#region ../../libs/runtime/agent-emulator/src/lib/workflow/approval-recorder-bridge.ts
22738
+ var createApprovalRecorder;
22739
+ var init_approval_recorder_bridge = __esmMin((() => {
22740
+ createApprovalRecorder = (deps) => ({ async request(args) {
22741
+ const context = deps.getInvocationContext?.();
22742
+ const enriched = { ...args };
22743
+ if (context?.runId !== void 0) enriched["runId"] = context.runId;
22744
+ if (context?.requestedByStepId !== void 0) enriched["requestedByStepId"] = context.requestedByStepId;
22745
+ try {
22746
+ const result = await deps.request(enriched);
22747
+ if (result.error) {
22748
+ const code = result.error.code ?? "approval_persist_failed";
22749
+ const message = result.error.message ?? "Failed to write the approval to the agent database.";
22750
+ throw new Error(`requestApproval could not persist the approvals row (${code}): ${message} The workflow step will retry — a HITL decision is never quietly lost.`);
22751
+ }
22752
+ } catch (err) {
22753
+ console.error("[approval-recorder] failed to persist approvals row:", err instanceof Error ? err.message : String(err));
22754
+ throw err;
22755
+ }
22756
+ } });
22488
22757
  })), DEFAULT_TZ, TICK_JOB_NAME$1, DECL_PREFIX, DYN_PREFIX, declSchedulerId, dynSchedulerId, planDeclarativeReconcile, expandDeclaredSchedules, parseSchedulerId, consoleLogger$2, WorkflowCronScheduler, createWorkflowSchedulerBridge;
22489
22758
  var init_workflow_cron_scheduler = __esmMin((() => {
22490
22759
  DEFAULT_TZ = "UTC";
@@ -23054,111 +23323,11 @@ var init_intake_scheduler = __esmMin((() => {
23054
23323
  };
23055
23324
  }));
23056
23325
  //#endregion
23057
- //#region ../../libs/runtime/agent-emulator/src/lib/connect-inbound/gmail-history-strategy.ts
23058
- function isGmailCursor(cursor) {
23059
- return typeof cursor === "object" && cursor !== null && typeof cursor.historyId === "string" && cursor.historyId.length > 0;
23060
- }
23061
- /** Read a single header value (case-insensitive) off a Gmail message payload. */
23062
- function header(message, name) {
23063
- const match = (message.payload?.headers ?? []).find((h) => (h.name ?? "").toLowerCase() === name.toLowerCase());
23064
- return typeof match?.value === "string" ? match.value : "";
23065
- }
23066
- /** Fetch one message (metadata format) and normalize it to a poll item. */
23067
- async function fetchMessageEvent(ctx, id) {
23068
- const message = await ctx.runOperation("gmail.users.messages.get", {
23069
- userId: USER_ID,
23070
- id,
23071
- format: "metadata"
23072
- });
23073
- const payload = {
23074
- messageId: message.id ?? id,
23075
- threadId: message.threadId ?? "",
23076
- from: header(message, "From"),
23077
- to: header(message, "To"),
23078
- subject: header(message, "Subject"),
23079
- snippet: typeof message.snippet === "string" ? message.snippet : "",
23080
- date: header(message, "Date"),
23081
- raw: {
23082
- messageId: message.id ?? id,
23083
- threadId: message.threadId ?? "",
23084
- historyId: typeof message.historyId === "string" ? message.historyId : null
23085
- }
23086
- };
23087
- return {
23088
- id: message.id ?? id,
23089
- payload
23090
- };
23091
- }
23092
- function readHistoryId(event) {
23093
- const raw = (event?.payload)?.raw;
23094
- return raw && typeof raw.historyId === "string" ? raw.historyId : null;
23095
- }
23096
- /**
23097
- * Bootstrap path (empty cursor): list the most recent inbox messages, normalize
23098
- * them, and seed the cursor from the newest message's `historyId`. Dedup is the
23099
- * platform's job (by message id), so a first poll legitimately returns the recent
23100
- * window and the next poll resumes from the history API.
23101
- */
23102
- async function bootstrap(ctx) {
23103
- const items = await mapWithConcurrency(((await ctx.runOperation("gmail.users.messages.list", {
23104
- userId: USER_ID,
23105
- maxResults: BOOTSTRAP_MAX_RESULTS,
23106
- labelIds: "INBOX"
23107
- })).messages ?? []).map((m) => m.id).filter((id) => typeof id === "string" && id.length > 0), 6, (id) => fetchMessageEvent(ctx, id));
23108
- const seedHistoryId = readHistoryId(items[0]);
23109
- return {
23110
- items,
23111
- nextCursor: seedHistoryId ? { historyId: seedHistoryId } : null
23112
- };
23113
- }
23114
- /**
23115
- * Incremental path (valid cursor): pull history records added since the persisted
23116
- * `historyId` via `history.list`, drain EVERY page (so messages on pages 2+ are
23117
- * never skipped when the cursor advances), normalize each new arrival, and move
23118
- * the cursor to the response's latest `historyId`.
23119
- */
23120
- async function pollFromCursor(ctx, cursor) {
23121
- const addedIds = [];
23122
- let latestHistoryId = cursor.historyId;
23123
- let pageToken;
23124
- for (let page = 0; page < MAX_HISTORY_PAGES; page += 1) {
23125
- const history = await ctx.runOperation("gmail.users.history.list", {
23126
- userId: USER_ID,
23127
- startHistoryId: cursor.historyId,
23128
- historyTypes: "messageAdded",
23129
- labelId: "INBOX",
23130
- ...pageToken ? { pageToken } : {}
23131
- });
23132
- for (const record of history.history ?? []) for (const added of record.messagesAdded ?? []) {
23133
- const id = added.message?.id;
23134
- if (typeof id === "string" && id.length > 0) addedIds.push(id);
23135
- }
23136
- if (typeof history.historyId === "string" && history.historyId.length > 0) latestHistoryId = history.historyId;
23137
- if (typeof history.nextPageToken === "string" && history.nextPageToken.length > 0) pageToken = history.nextPageToken;
23138
- else break;
23139
- }
23140
- return {
23141
- items: await mapWithConcurrency(addedIds, 6, (id) => fetchMessageEvent(ctx, id)),
23142
- nextCursor: { historyId: latestHistoryId }
23143
- };
23144
- }
23145
- var USER_ID, BOOTSTRAP_MAX_RESULTS, MAX_HISTORY_PAGES, gmailHistoryPoll;
23146
- var init_gmail_history_strategy = __esmMin((() => {
23147
- init_src$3();
23148
- USER_ID = "me";
23149
- BOOTSTRAP_MAX_RESULTS = 25;
23150
- MAX_HISTORY_PAGES = 100;
23151
- gmailHistoryPoll = async (ctx, cursor) => {
23152
- if (!isGmailCursor(cursor)) return bootstrap(ctx);
23153
- return pollFromCursor(ctx, cursor);
23154
- };
23155
- }));
23156
- //#endregion
23157
23326
  //#region ../../libs/runtime/agent-emulator/src/lib/connect-inbound/trigger-strategies.ts
23158
23327
  var STRATEGIES, resolveStrategy;
23159
23328
  var init_trigger_strategies = __esmMin((() => {
23160
- init_gmail_history_strategy();
23161
- STRATEGIES = { "gmail.history": gmailHistoryPoll };
23329
+ init_src$3();
23330
+ STRATEGIES = { ...BUILT_IN_STRATEGIES };
23162
23331
  resolveStrategy = (name) => STRATEGIES[name] ?? null;
23163
23332
  }));
23164
23333
  //#endregion
@@ -23389,6 +23558,7 @@ var init_runtime_mount = __esmMin((() => {
23389
23558
  init_workflow_run_projector();
23390
23559
  init_workflow_invoker();
23391
23560
  init_workflow_start_service();
23561
+ init_approval_recorder_bridge();
23392
23562
  init_workflow_cron_scheduler();
23393
23563
  init_workflow_hook_resume();
23394
23564
  init_workflow_frame_sse();
@@ -23468,6 +23638,11 @@ var init_runtime_mount = __esmMin((() => {
23468
23638
  let setWorkflowStarter;
23469
23639
  let setWorkflowScheduler;
23470
23640
  let setDeepAgentInvoker;
23641
+ let setAiUsageReporter;
23642
+ let setApprovalRecorder;
23643
+ let approvalRequest;
23644
+ let setConnectorInvoker;
23645
+ let connectorInvoker;
23471
23646
  try {
23472
23647
  const sdk = await importProject("@stackbone/sdk");
23473
23648
  runWithInvocationContext = sdk.runWithInvocationContext;
@@ -23475,12 +23650,34 @@ var init_runtime_mount = __esmMin((() => {
23475
23650
  setWorkflowStarter = sdk.setWorkflowStarter;
23476
23651
  setWorkflowScheduler = sdk.setWorkflowScheduler;
23477
23652
  setDeepAgentInvoker = sdk.setDeepAgentInvoker;
23653
+ setAiUsageReporter = sdk.setAiUsageReporter;
23654
+ setApprovalRecorder = sdk.setApprovalRecorder;
23655
+ setConnectorInvoker = sdk.setConnectorInvoker;
23656
+ connectorInvoker = sdk.createConnectInvoker?.();
23657
+ const ambientStackbone = sdk.stackbone;
23658
+ if (ambientStackbone) approvalRequest = async (args) => {
23659
+ const approval = ambientStackbone.approval;
23660
+ if (!approval?.request) return {
23661
+ data: null,
23662
+ error: {
23663
+ code: "approval_unavailable",
23664
+ message: "The project SDK exposes no ambient approval writer."
23665
+ }
23666
+ };
23667
+ return approval.request(args);
23668
+ };
23669
+ else approvalRequest = void 0;
23478
23670
  } catch {
23479
23671
  runWithInvocationContext = void 0;
23480
23672
  getInvocationContext = void 0;
23481
23673
  setWorkflowStarter = void 0;
23482
23674
  setWorkflowScheduler = void 0;
23483
23675
  setDeepAgentInvoker = void 0;
23676
+ setAiUsageReporter = void 0;
23677
+ setApprovalRecorder = void 0;
23678
+ approvalRequest = void 0;
23679
+ setConnectorInvoker = void 0;
23680
+ connectorInvoker = void 0;
23484
23681
  }
23485
23682
  const stepUrl = pathToFileURL(resolve(projectRoot, WELL_KNOWN_DIR$2, "step.mjs")).href;
23486
23683
  await (runtimeEnv.generation ? import(`${stepUrl}?v=${runtimeEnv.generation}`) : import(stepUrl));
@@ -23492,7 +23689,12 @@ var init_runtime_mount = __esmMin((() => {
23492
23689
  resumeHook,
23493
23690
  setWorkflowStarter,
23494
23691
  setWorkflowScheduler,
23495
- setDeepAgentInvoker
23692
+ setDeepAgentInvoker,
23693
+ setAiUsageReporter,
23694
+ setApprovalRecorder,
23695
+ approvalRequest,
23696
+ setConnectorInvoker,
23697
+ connectorInvoker
23496
23698
  };
23497
23699
  };
23498
23700
  mountWorkflowRuntime = async (app, opts) => {
@@ -23578,6 +23780,21 @@ var init_runtime_mount = __esmMin((() => {
23578
23780
  return invoker(name, input, options);
23579
23781
  });
23580
23782
  }
23783
+ if (rt.setAiUsageReporter) {
23784
+ const invocations = opts.invocations;
23785
+ rt.setAiUsageReporter((usage) => {
23786
+ const runId = rt.getInvocationContext?.()?.runId;
23787
+ if (!runId || !invocations) return;
23788
+ invocations.addRunTokens(runId, usage).catch((err) => {
23789
+ console.warn(`[ai-usage] token fold failed for run ${runId}: ${err instanceof Error ? err.message : String(err)}`);
23790
+ });
23791
+ });
23792
+ }
23793
+ if (rt.setApprovalRecorder && rt.approvalRequest) rt.setApprovalRecorder(createApprovalRecorder({
23794
+ request: rt.approvalRequest,
23795
+ ...rt.getInvocationContext ? { getInvocationContext: rt.getInvocationContext } : {}
23796
+ }));
23797
+ if (rt.setConnectorInvoker && rt.connectorInvoker) rt.setConnectorInvoker(rt.connectorInvoker);
23581
23798
  return rt;
23582
23799
  }).catch((err) => {
23583
23800
  runtimePromise = null;
@@ -23808,7 +24025,11 @@ var init_runtime_mount = __esmMin((() => {
23808
24025
  await cronScheduler?.close();
23809
24026
  await intakeRuntime?.close();
23810
24027
  if (runtimePromise) try {
23811
- (await runtimePromise).setDeepAgentInvoker?.(void 0);
24028
+ const rt = await runtimePromise;
24029
+ rt.setDeepAgentInvoker?.(void 0);
24030
+ rt.setAiUsageReporter?.(void 0);
24031
+ rt.setApprovalRecorder?.(void 0);
24032
+ rt.setConnectorInvoker?.(void 0);
23812
24033
  } catch {}
23813
24034
  },
23814
24035
  triggerWorkflowStart: (name, input) => triggerWorkflowStart(startDeps, name, input),
@@ -30827,6 +31048,62 @@ var findWorkflowFiles = async (projectRoot, extraModules = []) => {
30827
31048
  return files;
30828
31049
  };
30829
31050
  /**
31051
+ * Scan the final `workflow.vm.js` text for any `require(...)` call and throw an
31052
+ * actionable error if one is found.
31053
+ *
31054
+ * The workflow VM bundle runs on `node:vm`, which has NO `require`. A `require(`
31055
+ * token surviving into the bundle means something reachable from a workflow body
31056
+ * (a helper, a transitive import) dragged a Node-only dependency into the VM graph
31057
+ * — the Workflow SDK (WDK) compiler inlines the whole module, and the inlined
31058
+ * `require()` throws `ReferenceError: require is not defined` at instantiation,
31059
+ * crashing EVERY workflow in the project with an opaque `USER_ERROR` / "Unknown
31060
+ * error" (the bug this guard makes impossible to ship silently).
31061
+ *
31062
+ * Pure + exported so it is unit-testable in isolation (prior art:
31063
+ * `foldSchemasIntoManifest`). False-positive discipline: it matches `require(`
31064
+ * only as a CALL shape, not the substring inside a comment or a string literal
31065
+ * that is not actually invoked — the VM bundle is generated code, so a literal
31066
+ * `require(` in a string is still a smell worth flagging, but the guard reports
31067
+ * the surrounding context so a human can tell a real leak from a benign mention.
31068
+ */
31069
+ var assertNoLeakedNodeRequire = (bundleText) => {
31070
+ const requirePattern = /require\s*\(\s*['"`]/g;
31071
+ const matches = [];
31072
+ const lines = bundleText.split("\n");
31073
+ for (const match of bundleText.matchAll(requirePattern)) {
31074
+ const index = match.index ?? 0;
31075
+ let line = 1;
31076
+ let column = 1;
31077
+ let consumed = 0;
31078
+ for (let i = 0; i < lines.length; i += 1) {
31079
+ const lineText = lines[i];
31080
+ if (lineText === void 0) break;
31081
+ const lineEnd = consumed + lineText.length + 1;
31082
+ if (index < lineEnd) {
31083
+ line = i + 1;
31084
+ column = index - consumed + 1;
31085
+ break;
31086
+ }
31087
+ consumed = lineEnd;
31088
+ }
31089
+ const start = Math.max(0, index - 80);
31090
+ const end = Math.min(bundleText.length, index + 120);
31091
+ const snippet = bundleText.slice(start, end).replace(/\s+/g, " ").trim();
31092
+ matches.push({
31093
+ index,
31094
+ line,
31095
+ column,
31096
+ snippet
31097
+ });
31098
+ }
31099
+ if (matches.length === 0) return;
31100
+ const first = matches[0];
31101
+ if (!first) return;
31102
+ const listing = matches.slice(0, 5).map((m, i) => ` ${i + 1}. line ${m.line}:${m.column} …${m.snippet}…`).join("\n");
31103
+ const more = matches.length > 5 ? `\n (and ${matches.length - 5} more)` : "";
31104
+ throw new Error(`Workflow VM bundle contains ${matches.length} require() call(s) — the workflow VM (node:vm) has no \`require\`, so the bundle will crash at instantiation with \`ReferenceError: require is not defined\`, breaking EVERY workflow in this project.\nThis means a Node-only dependency (e.g. node:crypto, the Postgres driver, the OpenAI / AWS SDKs) is reachable from a workflow body or a "use step" that was not properly externalized. The most common cause is a workflow-steps helper that touches Node at module scope — it must reach Node only through a runtime-planted binding (see @stackbone/sdk/workflow-steps/*) or be wrapped in a "use step" that the WDK stubs out of the VM graph.\nFirst occurrence: line ${first.line}:${first.column}\n${listing}${more}\nFix: move the Node-bearing code behind a runtime binding, wrap the call in a "use step" function, or import the helper from @stackbone/sdk/workflow-steps/* (the per-domain entry that the WDK keeps out of the VM body).`);
31105
+ };
31106
+ /**
30830
31107
  * Build the workspace's workflows into `<projectRoot>/.well-known/workflow/v1/`:
30831
31108
  * - `step.mjs` — steps bundle (runtime external)
30832
31109
  * - `workflow.vm.js` — the workflow VM bundle text fed to `workflowEntrypoint`
@@ -30855,7 +31132,7 @@ var buildWorkflows = async (projectRoot, workflows, logger) => {
30855
31132
  stepsBundlePath: `./${WELL_KNOWN_DIR}/step.mjs`,
30856
31133
  workflowsBundlePath: `./${WELL_KNOWN_DIR}/workflow.vm.js`,
30857
31134
  webhookBundlePath: `./${WELL_KNOWN_DIR}/webhook.mjs`,
30858
- externalPackages: ["@stackbone/sdk", "@stackbone/sdk/*"]
31135
+ externalPackages: []
30859
31136
  });
30860
31137
  const prevCwd = process.cwd();
30861
31138
  let manifest;
@@ -30882,6 +31159,7 @@ var buildWorkflows = async (projectRoot, workflows, logger) => {
30882
31159
  const streaming = await detectStreamingWorkflows(projectRoot, workflows);
30883
31160
  foldSchemasIntoManifest(manifest, workflows, schemas, streaming, schedules);
30884
31161
  await writeFile(resolve(wkDir, "workflow.vm.js"), interimBundleText, "utf8");
31162
+ assertNoLeakedNodeRequire(interimBundleText);
30885
31163
  await writeFile(resolve(wkDir, "manifest.json"), JSON.stringify(manifest), "utf8");
30886
31164
  for (const name of [
30887
31165
  "step.mjs",
@@ -31176,7 +31454,8 @@ var loadWorkspace = async (projectRoot) => {
31176
31454
  if (!isWorkspace(workspace)) throw new Error(`${configPath} must default-export a workspace with \`agents\` and \`workflows\` arrays (via \`defineWorkspace({ agents, workflows })\`).`);
31177
31455
  return {
31178
31456
  agents: workspace.agents,
31179
- workflows: workspace.workflows
31457
+ workflows: workspace.workflows,
31458
+ deepAgents: workspace.deepAgents
31180
31459
  };
31181
31460
  };
31182
31461
  //#endregion
@@ -31246,10 +31525,11 @@ var discoverDeepAgents = async (root) => {
31246
31525
  /**
31247
31526
  * Derive `{ agents, workflows, deepAgents }` for the workspace rooted at `root`.
31248
31527
  * `agents` is always `[]` (there is no server-per-agent runtime). A `stackbone.config.ts`
31249
- * override wins for workflows; deep agents are always discovered by folder
31250
- * convention (the config format does not declare them), and `deepAgents` is
31251
- * omitted entirely when none are found so an empty workspace is byte-identical to
31252
- * before.
31528
+ * override wins for workflows AND deep agents; when the config declares `deepAgents`,
31529
+ * those win over auto-discovery (use to mark agents as `$internal` or customize the
31530
+ * registry). When omitted, deep agents are discovered by folder convention and
31531
+ * `deepAgents` is omitted entirely when none are found so an empty workspace is
31532
+ * byte-identical to before.
31253
31533
  */
31254
31534
  var discoverWorkspace = async (root) => {
31255
31535
  const deepAgents = await discoverDeepAgents(root);
@@ -31257,10 +31537,18 @@ var discoverWorkspace = async (root) => {
31257
31537
  ...ws,
31258
31538
  deepAgents
31259
31539
  };
31260
- if (await fileExists$1(resolve(root, "stackbone.config.ts"))) return withDeepAgents({
31261
- agents: [],
31262
- workflows: (await loadWorkspace(root)).workflows
31263
- });
31540
+ if (await fileExists$1(resolve(root, "stackbone.config.ts"))) {
31541
+ const ws = await loadWorkspace(root);
31542
+ const resultDeepAgents = ws.deepAgents ?? deepAgents;
31543
+ const baseResult = {
31544
+ agents: [],
31545
+ workflows: ws.workflows
31546
+ };
31547
+ return resultDeepAgents.length === 0 ? baseResult : {
31548
+ ...baseResult,
31549
+ deepAgents: resultDeepAgents
31550
+ };
31551
+ }
31264
31552
  return withDeepAgents({
31265
31553
  agents: [],
31266
31554
  workflows: await discoverWorkflows(root)
@@ -35174,6 +35462,7 @@ is for offline work or when the relay handshake fails.
35174
35462
  The emulator enforces an explicit CORS allowlist (no \`*\`). Default origins:
35175
35463
 
35176
35464
  - \`https://app.stackbone.ai\`
35465
+ - \`https://chat.stackbone.ai\`
35177
35466
  - \`http://localhost:*\` (any port; covers any local dev server you run)
35178
35467
 
35179
35468
  Add origins **per-repo** via \`stackbone.config.json\` at the project root
@@ -35185,6 +35474,7 @@ Add origins **per-repo** via \`stackbone.config.json\` at the project root
35185
35474
  "studio": {
35186
35475
  "corsOrigins": [
35187
35476
  "https://app.stackbone.ai",
35477
+ "https://chat.stackbone.ai",
35188
35478
  "https://staging.stackbone.ai",
35189
35479
  "http://localhost:*",
35190
35480
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackbone/cli",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "license": "UNLICENSED",
5
5
  "type": "module",
6
6
  "bin": {
Binary file
Binary file