aui-agent-builder 0.3.104 → 0.3.106

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.
@@ -1 +1 @@
1
- {"version":3,"file":"import-agent.d.ts","sourceRoot":"","sources":["../../src/commands/import-agent.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAwDH,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;OAMG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,OAAO,wBAAwB,EAAE,UAAU,CAAC;IACzD,MAAM,CAAC,EAAE,OAAO,oCAAoC,EAAE,WAAW,EAAE,CAAC;IACpE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAmED,wBAAsB,WAAW,CAC/B,OAAO,CAAC,EAAE,MAAM,EAChB,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAiBf;AA0PD,wBAAsB,sBAAsB,CAC1C,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAgBf;AAoFD,wBAAsB,kBAAkB,CACtC,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAgBf"}
1
+ {"version":3,"file":"import-agent.d.ts","sourceRoot":"","sources":["../../src/commands/import-agent.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAwDH,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;OAMG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,OAAO,wBAAwB,EAAE,UAAU,CAAC;IACzD,MAAM,CAAC,EAAE,OAAO,oCAAoC,EAAE,WAAW,EAAE,CAAC;IACpE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAmED,wBAAsB,WAAW,CAC/B,OAAO,CAAC,EAAE,MAAM,EAChB,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAiBf;AAqVD,wBAAsB,sBAAsB,CAC1C,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAgBf;AAoFD,wBAAsB,kBAAkB,CACtC,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAgBf"}
@@ -128,69 +128,148 @@ async function _importAgent(parentSpan, agentId, options = {}) {
128
128
  // those are `agent_management_id`s (also 24-char hex, but a
129
129
  // different document family). Same regex, different collection.
130
130
  //
131
- // Try agent-management FIRST (it's the new path and the most
132
- // common id today), fall back to the legacy networks lookup on
133
- // 404 so existing scripts that pass `network_id` keep working.
134
- // Whichever resolves wins, and we cache the resolved `AgentInfo`
135
- // as `knownAgentInfo` so `selectVersionForAgent` / `doImport`
136
- // skip a duplicate lookup AND the new `/pull` endpoint is
137
- // reachable on the first import attempt.
131
+ // Resolution order (first success wins, then we cache the resolved
132
+ // `AgentInfo` as `knownAgentInfo` so `selectVersionForAgent` /
133
+ // `doImport` skip a duplicate lookup AND the new `/pull` endpoint
134
+ // is reachable on the first import attempt):
135
+ //
136
+ // 0) listAgents with BOTH `network_id=<input>` AND
137
+ // `network_category_id=<input>` handles the case where the
138
+ // input is either a legacy `network_id` (regular agent) or a
139
+ // `network_category_id` (template). The server OR's the two
140
+ // filters, so a single call covers both shapes (verified
141
+ // 2026-05-27 against staging — see thread with Adli). If the
142
+ // input is actually an `agent_management_id`, listAgents
143
+ // returns no items (an agent_management_id is almost never
144
+ // also a network_id / category_id of a DIFFERENT agent), and
145
+ // we fall through to step 1.
146
+ //
147
+ // Originally added (2026-05-26) as a [TEMP] workaround for
148
+ // the BFF passing `network_id` instead of `agent_management_id`.
149
+ // Promoted to the canonical first step once we extended it
150
+ // to also resolve `network_category_id` — there is no longer
151
+ // a "remove once the BFF migrates" exit condition because the
152
+ // template case has no other resolver (templates never appear
153
+ // in `networks.get` and `getAgent(category_id)` 404s).
154
+ //
155
+ // 1) getAgent(<input>) — treat the id as an agent_management_id.
156
+ // Still the canonical path for callers that already have it.
157
+ //
158
+ // 2) networks.get(<input>) — last-ditch legacy networks lookup.
159
+ // Kept for back-compat with old scripts that have neither
160
+ // flow available (e.g. agent-management endpoint down).
138
161
  const spinner = startSpinner("Fetching agent...");
139
162
  let resolved = false;
140
163
  let lastErr;
141
- // 1) Treat the id as an agent-management UUID.
164
+ // 0) listAgents with both `network_id` and `network_category_id`
165
+ // set to the input id — server OR's the filters, returning
166
+ // either a regular agent (matched by `scope.network_id`) or a
167
+ // template (matched by `scope.network_category_id`). If the
168
+ // input is actually an `agent_management_id`, neither filter
169
+ // matches and we fall through to step 1.
142
170
  try {
143
- const ami = await client.agentManagement.getAgent(effectiveAgentId);
144
- knownAgentInfo = ami;
145
- selectedAgent = agentInfoToNetwork(ami);
146
- // Bias the client scope toward the agent's own org/account so
147
- // downstream listAgents/listVersions calls don't 403 against the
148
- // session's default scope.
149
- const scopeUpdate = {};
150
- if (ami.scope?.account_id)
151
- scopeUpdate.accountId = ami.scope.account_id;
152
- if (ami.scope?.organization_id)
153
- scopeUpdate.organizationId = ami.scope.organization_id;
154
- if (scopeUpdate.accountId || scopeUpdate.organizationId) {
155
- client.setScope(scopeUpdate);
171
+ const resp = await client.agentManagement.listAgents(client.getOrganizationId(), 1, 50, {
172
+ network_id: effectiveAgentId,
173
+ network_category_id: effectiveAgentId,
174
+ });
175
+ // `find` (not `[0]`) to be defensive about the server returning
176
+ // unrelated rows. We require `scope.network_id === <input>` for
177
+ // the regular-agent case OR `scope.network_category_id === <input>`
178
+ // for the template case so siblings on the same listing page
179
+ // can't accidentally win.
180
+ const match = resp.items.find((a) => a.scope.network_id === effectiveAgentId ||
181
+ a.scope.network_category_id === effectiveAgentId);
182
+ if (match) {
183
+ knownAgentInfo = match;
184
+ selectedAgent = agentInfoToNetwork(match);
185
+ // Bias scope toward the agent's own org/account (mirrors the
186
+ // step-1 success branch — keep both in sync). Templates have
187
+ // `scope.account_id === null` (category-scoped, not account-
188
+ // scoped) — skipping the scope update in that case keeps the
189
+ // session's org context as-is, which is what downstream
190
+ // template flows expect.
191
+ const scopeUpdate = {};
192
+ if (match.scope?.account_id)
193
+ scopeUpdate.accountId = match.scope.account_id;
194
+ if (match.scope?.organization_id)
195
+ scopeUpdate.organizationId = match.scope.organization_id;
196
+ if (scopeUpdate.accountId || scopeUpdate.organizationId) {
197
+ client.setScope(scopeUpdate);
198
+ }
199
+ const matchedVia = match.scope.network_id === effectiveAgentId
200
+ ? "network_id"
201
+ : "network_category_id";
202
+ spinner.succeed(`Found agent (via listAgents ${matchedVia} lookup): ${match.name}`);
203
+ resolved = true;
204
+ }
205
+ else if (process.env.AUI_DEBUG) {
206
+ console.log(`[debug] listAgents(network_id=${effectiveAgentId}, network_category_id=${effectiveAgentId}) returned ${resp.items.length} row(s) but none matched; trying getAgent next`);
156
207
  }
157
- spinner.succeed(`Found agent (via agent-management): ${ami.name}`);
158
- resolved = true;
159
208
  }
160
209
  catch (err) {
161
- lastErr = err;
162
- const status = err instanceof AUIAPIError
163
- ? err.status
164
- : (err.statusCode ??
165
- err.status);
166
- // Fall through to the legacy networks lookup on ANY non-auth
167
- // error — not just 404. Rationale (2026-05-24):
168
- //
169
- // - 404 ⇒ id isn't an agent_management_id; try as network_id.
170
- // - 422 ⇒ the new agent-settings response schema has stricter
171
- // validation than some legacy docs (e.g. the post-`kind`
172
- // rollout returned 422 for any pre-rollout agent until
173
- // a backfill landed). The legacy networks endpoint
174
- // lives in a different service and doesn't share that
175
- // validation, so it can still resolve the same agent.
176
- // - 5xx / network blip ⇒ a transient agent-settings failure
177
- // shouldn't block the import when there's an alternate
178
- // resolver available; the legacy attempt will either
179
- // succeed (problem masked) or 4xx itself (we report
180
- // the more informative agent-settings error via
181
- // `lastErr` at the end of step 2).
182
- //
183
- // Auth errors (401/403) STILL fail loudly here — no point in
184
- // trying the next endpoint with the same credentials.
185
- if (isAuthError(err)) {
186
- spinner.fail("Authentication failed");
187
- handleAuthError(err);
188
- throw err;
189
- }
210
+ // Listing failed (auth / 5xx / org context wrong). Don't bail
211
+ // fall through to the existing dual-shape resolution. Worst
212
+ // case we make the same call sequence we'd have made before
213
+ // this resolver was added.
190
214
  if (process.env.AUI_DEBUG) {
191
- console.log(`[debug] agentManagement.getAgent(${effectiveAgentId}) ${status ?? "non-auth error"}; trying legacy networks.get next`);
215
+ console.log(`[debug] listAgents(network_id=${effectiveAgentId}, network_category_id=${effectiveAgentId}) failed: ${err instanceof Error ? err.message : err}; trying getAgent next`);
192
216
  }
193
217
  }
218
+ // 1) Treat the id as an agent-management UUID.
219
+ if (!resolved) {
220
+ try {
221
+ const ami = await client.agentManagement.getAgent(effectiveAgentId);
222
+ knownAgentInfo = ami;
223
+ selectedAgent = agentInfoToNetwork(ami);
224
+ // Bias the client scope toward the agent's own org/account so
225
+ // downstream listAgents/listVersions calls don't 403 against the
226
+ // session's default scope.
227
+ const scopeUpdate = {};
228
+ if (ami.scope?.account_id)
229
+ scopeUpdate.accountId = ami.scope.account_id;
230
+ if (ami.scope?.organization_id)
231
+ scopeUpdate.organizationId = ami.scope.organization_id;
232
+ if (scopeUpdate.accountId || scopeUpdate.organizationId) {
233
+ client.setScope(scopeUpdate);
234
+ }
235
+ spinner.succeed(`Found agent (via agent-management): ${ami.name}`);
236
+ resolved = true;
237
+ }
238
+ catch (err) {
239
+ lastErr = err;
240
+ const status = err instanceof AUIAPIError
241
+ ? err.status
242
+ : (err.statusCode ??
243
+ err.status);
244
+ // Fall through to the legacy networks lookup on ANY non-auth
245
+ // error — not just 404. Rationale (2026-05-24):
246
+ //
247
+ // - 404 ⇒ id isn't an agent_management_id; try as network_id.
248
+ // - 422 ⇒ the new agent-settings response schema has stricter
249
+ // validation than some legacy docs (e.g. the post-`kind`
250
+ // rollout returned 422 for any pre-rollout agent until
251
+ // a backfill landed). The legacy networks endpoint
252
+ // lives in a different service and doesn't share that
253
+ // validation, so it can still resolve the same agent.
254
+ // - 5xx / network blip ⇒ a transient agent-settings failure
255
+ // shouldn't block the import when there's an alternate
256
+ // resolver available; the legacy attempt will either
257
+ // succeed (problem masked) or 4xx itself (we report
258
+ // the more informative agent-settings error via
259
+ // `lastErr` at the end of step 2).
260
+ //
261
+ // Auth errors (401/403) STILL fail loudly here — no point in
262
+ // trying the next endpoint with the same credentials.
263
+ if (isAuthError(err)) {
264
+ spinner.fail("Authentication failed");
265
+ handleAuthError(err);
266
+ throw err;
267
+ }
268
+ if (process.env.AUI_DEBUG) {
269
+ console.log(`[debug] agentManagement.getAgent(${effectiveAgentId}) → ${status ?? "non-auth error"}; trying legacy networks.get next`);
270
+ }
271
+ }
272
+ } // close: if (!resolved) — wrapping step 1 (added with step 0 above)
194
273
  // 2) Fall back: treat the id as a legacy network_id.
195
274
  if (!resolved) {
196
275
  try {
@@ -1035,55 +1114,72 @@ knownAgentInfo) {
1035
1114
  }
1036
1115
  }
1037
1116
  }
1038
- // ─── Use the new pull endpoint (strict, no legacy fallback) ───
1117
+ // ─── Mode dispatch (with pure-legacy short-circuit) ─────────────
1118
+ //
1119
+ // Three layers, in order:
1120
+ //
1121
+ // 1. No agent_management_id at all (pure legacy)
1122
+ // → The agent exists in `networks` (step 2 of resolution) but
1123
+ // not in agent-management. It predates the agent-management
1124
+ // rollout entirely, which means it predates `bundle_mode`,
1125
+ // which means it can only be records-mode. Skip the
1126
+ // `detectAgentBundleMode` call (it'd need an
1127
+ // agent_management_id we don't have) and force-route to
1128
+ // records-mode dispatch. `exportAgent(...)` is happy with
1129
+ // scope filters alone — no agent_management_id needed.
1039
1130
  //
1040
- // STRICT MODE (2026-05-18 owner directive): the v3 agent-settings
1041
- // /pull endpoint is the only supported import path. If it fails for
1042
- // any reason (404, auth, 5xx, network), we surface a clear error
1043
- // instead of falling back to the legacy per-entity exports. The
1044
- // legacy `client.exportAgent` flow is preserved below but commented
1045
- // out it's the bootstrap path for agents that haven't been
1046
- // Pushed via the new endpoint yet, and may be re-enabled later if a
1047
- // migration adapter is needed.
1131
+ // Concrete repro (2026-05-26): `aui import 6a0cb854...` (a
1132
+ // legacy `test-v15` network) found the agent via
1133
+ // `networks.get` but `listAgents(network_id=…)` returned
1134
+ // nothing. Without this branch, the import bailed with
1135
+ // "Could not resolve agent_management_id for this agent"
1136
+ // even though `exportAgent` would have worked.
1048
1137
  //
1049
- // What this means for the user:
1050
- // - The agent MUST have at least one Push via the new endpoint
1051
- // (creates the first blob revision at `v{N}.1`).
1052
- // - The agent MUST have an `agent_management_id` (resolvable via
1053
- // `listAgents(network_id=…)`) and a `version_id`.
1054
- // - 404 from /pull "no blobs at this version" → user must run
1055
- // `aui push` first (or re-import via the deprecated bootstrap
1056
- // path if temporarily re-enabled).
1057
- // - Any other error bubbles unchanged.
1058
- if (!versionId) {
1138
+ // 2. Agent_management_id present call `detectAgentBundleMode`
1139
+ // to read the agent's `bundle_mode` flag. Routes to /pull
1140
+ // (bundle) or per-entity /view (records) accordingly.
1141
+ //
1142
+ // 3. Bundle mode + no version_id → hard error. /pull is keyed
1143
+ // on version_id in the URL and can't fall back to scope
1144
+ // filters. Records-mode + no version_id is fine
1145
+ // `exportAgent` handles it via scope-level filters.
1146
+ let importModeResolution;
1147
+ if (!effectiveAgentMgmtId) {
1148
+ // (1) Pure legacy — force records mode, skip the dispatcher call
1149
+ // entirely (it'd throw since we have no id to pass it).
1150
+ importModeResolution = { mode: "records", source: "no_agent_management_id" };
1151
+ if (process.env.AUI_DEBUG) {
1152
+ console.log(`[debug] doImport: no agent_management_id for network ${networkId} — forcing records-mode dispatch (pure-legacy agent)`);
1153
+ }
1154
+ }
1155
+ else {
1156
+ // (2) Normal dispatch path — agent_management_id resolved, ask
1157
+ // the server which mode to use. Reuses `knownAgentInfo` so we
1158
+ // don't pay an extra `GET /v1/agents/{id}` round-trip.
1159
+ importModeResolution = await detectAgentBundleMode(client, effectiveAgentMgmtId, knownAgentInfo);
1160
+ }
1161
+ // (3) Bundle mode requires version_id. Records mode doesn't —
1162
+ // `exportAgent` falls back to scope filters when versionId is
1163
+ // undefined (the legacy contract that's been in the CLI since
1164
+ // before version management existed).
1165
+ if (importModeResolution.mode === "bundle" && !versionId) {
1059
1166
  throw new ConfigError("No version_id available for the new /pull endpoint.", {
1060
- suggestion: "Pass --version <versionId>, or re-run `aui import` interactively so a version can be selected. Legacy agents without version management cannot be imported under the strict no-fallback policy.",
1167
+ suggestion: "Pass --version <versionId>, or re-run `aui import` interactively so a version can be selected. " +
1168
+ "Bundle-mode agents must have at least one push (creates v{N}.1) before they can be imported via /pull. " +
1169
+ "If this is a fresh bundle-mode agent, run `aui push` from the source project first, then re-import.",
1061
1170
  });
1062
1171
  }
1063
- if (!effectiveAgentMgmtId) {
1064
- throw new ConfigError("Could not resolve agent_management_id for this agent.", {
1065
- suggestion: "Pass the agent_management_id directly as the positional arg, or check that the agent exists in agent-management (`listAgents(network_id=…)`).",
1172
+ // Bundle mode ALSO requires an agent_management_id (it's in the
1173
+ // /pull URL path). This was implicit before `detectAgentBundleMode`
1174
+ // would have thrown but now that we short-circuit to records when
1175
+ // agent_management_id is missing, we should only ever reach this
1176
+ // branch when bundle mode was a real positive detection. Keep the
1177
+ // guard for defensive symmetry with the records path above.
1178
+ if (importModeResolution.mode === "bundle" && !effectiveAgentMgmtId) {
1179
+ throw new ConfigError("Could not resolve agent_management_id for this bundle-mode agent.", {
1180
+ suggestion: "Pass the agent_management_id directly as the positional arg, or verify the agent exists in agent-management (`listAgents(network_id=…)`).",
1066
1181
  });
1067
1182
  }
1068
- // ─── Mode dispatch ────────────────────────────────────────────────
1069
- // Restored 2026-05-24. Detect `Agent.bundle_mode` once via the agent
1070
- // record we already resolved, then either:
1071
- //
1072
- // - bundle mode → call new `/pull` endpoint (existing logic
1073
- // below; strict 404 → "run aui push first")
1074
- // - records mode → fall back to per-entity `/view` endpoints via
1075
- // `client.exportAgent(...)` and reconstruct
1076
- // canonical .aui.json files locally
1077
- //
1078
- // The two surfaces are mutually exclusive at the API layer; calling
1079
- // the wrong one returns 422 (or empty payload for legacy reads
1080
- // against blob-mode), which the user can't recover from at the
1081
- // CLI level. We dispatch BEFORE any /pull or /view call so the
1082
- // first attempt is always the right surface.
1083
- // Reuse the AgentInfo from step 1 / selectAgentFromList when we have
1084
- // it — saves one `GET /v1/agents/{id}` and avoids the duplicate-fetch
1085
- // observed in the 2026-05-24 trace.
1086
- const importModeResolution = await detectAgentBundleMode(client, effectiveAgentMgmtId, knownAgentInfo);
1087
1183
  if (span) {
1088
1184
  span.setAttribute("import.dispatch.mode", importModeResolution.mode);
1089
1185
  span.setAttribute("import.dispatch.mode_source", importModeResolution.source);
@@ -1203,6 +1299,13 @@ knownAgentInfo) {
1203
1299
  }
1204
1300
  else {
1205
1301
  // ─── Blob-mode (new /pull endpoint) ─────────────────────────
1302
+ //
1303
+ // `versionId!` assertion: we reach this branch only when
1304
+ // `useLegacyImport === false` (bundle mode), and the conditional
1305
+ // throw above ("No version_id available for the new /pull
1306
+ // endpoint.") already guarantees `versionId` is defined in
1307
+ // that case. TS can't narrow across the throw, so the
1308
+ // assertion is documented intent rather than a hand-wave.
1206
1309
  let pullData = null;
1207
1310
  try {
1208
1311
  pullData = await tryPullViaBlobEndpoint(client, effectiveAgentMgmtId, versionId, options.tag);