@agent-native/core 0.223.0 → 0.223.1-nightly-20261103004150

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.
@@ -25,9 +25,9 @@ export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import
25
25
  ok: boolean;
26
26
  }>>;
27
27
  export declare const postCollabText: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
28
- text?: undefined;
29
28
  ok?: undefined;
30
29
  error: string;
30
+ text?: undefined;
31
31
  } | {
32
32
  error?: undefined;
33
33
  ok: boolean;
@@ -13,9 +13,9 @@ export declare const postCollabPatch: import("h3").EventHandlerWithFetch<import(
13
13
  ok: boolean;
14
14
  }>>;
15
15
  export declare const getCollabJson: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
16
+ data?: undefined;
16
17
  error: string;
17
18
  docId?: undefined;
18
- data?: undefined;
19
19
  } | {
20
20
  error?: undefined;
21
21
  docId: string;
@@ -33,9 +33,9 @@ declare const _default: import("../../action.js").ActionDefinition<{
33
33
  idempotencyKey: string;
34
34
  deleted: boolean;
35
35
  } | {
36
+ id?: undefined;
36
37
  error: string;
37
38
  url?: undefined;
38
- id?: undefined;
39
39
  provider?: undefined;
40
40
  configured?: undefined;
41
41
  } | {
@@ -45,8 +45,8 @@ declare const _default: import("../../action.js").ActionDefinition<{
45
45
  provider: string;
46
46
  configured?: undefined;
47
47
  } | {
48
- url?: undefined;
49
48
  id?: undefined;
49
+ url?: undefined;
50
50
  provider?: undefined;
51
51
  error: string;
52
52
  configured: boolean;
@@ -5,7 +5,7 @@ import type { McpApprovalPriorRun } from "./execute-action-call.js";
5
5
  /** One argument of the call, as a person reads it. */
6
6
  export interface McpApprovalField {
7
7
  label: string;
8
- /** As the call wrote it. */
8
+ /** As the call wrote it, with a yes or no, a list or an object in words. */
9
9
  value: string;
10
10
  /** Set when `value` names a point in time, to show in the reader's zone. */
11
11
  time?: McpApprovalTime;
@@ -8,6 +8,10 @@ const COPY_KEY = /^b?cc$/i;
8
8
  const EMAIL = /^[^\s@<>(),;:]+@[^\s@<>(),;:]+\.[^\s@<>(),;:]+$/;
9
9
  const ADDRESS_SEPARATOR = /[,;\n]/;
10
10
  const NAMED_ADDRESS = /^[^<>]*<([^<>]+)>$/;
11
+ // An object that names one person by these keys (`{ email, displayName }`)
12
+ // reads as `Dana <dana@example.com>`.
13
+ const PERSON_ADDRESS_KEY = /^(?:e_?mail|e_?mail_?address|address)$/i;
14
+ const PERSON_NAME_KEY = /^(?:name|display_?name|full_?name)$/i;
11
15
  const TIME_ZONE_KEY = /time_?zone$|^tz$/i;
12
16
  // A choice written as an identifier (`externalOnly`, `high_priority`) reads
13
17
  // better split into words; `America/New_York` or `New York` doesn't.
@@ -195,7 +199,7 @@ function zoneArgument(key, args) {
195
199
  }
196
200
  return undefined;
197
201
  }
198
- function readableValue(value, labels) {
202
+ function readableValue(value, labels, depth = 0) {
199
203
  if (value === null || value === undefined)
200
204
  return undefined;
201
205
  if (typeof value === "string")
@@ -204,13 +208,49 @@ function readableValue(value, labels) {
204
208
  return String(value);
205
209
  if (typeof value === "boolean")
206
210
  return value ? labels.yes : labels.no;
211
+ if (depth > MAX_DEPTH)
212
+ return JSON.stringify(value);
207
213
  if (Array.isArray(value)) {
208
214
  const items = value
209
- .map((item) => readableValue(item, labels))
215
+ .map((item) => readableValue(item, labels, depth + 1))
210
216
  .filter((item) => item !== undefined);
211
217
  return items.length > 0 ? items.join(", ") : undefined;
212
218
  }
213
- return JSON.stringify(value);
219
+ return readableObject(value, labels, depth);
220
+ }
221
+ /**
222
+ * An object in words, with every value it holds: one that names a person as
223
+ * `Dana <dana@example.com>`, followed by anything else it says
224
+ * (`(Optional: Yes)`), and any other as its values with their labels. Inside
225
+ * another value, those labeled values sit in parentheses, so a list of
226
+ * objects reads one object at a time.
227
+ */
228
+ function readableObject(record, labels, depth) {
229
+ const entries = Object.entries(record).flatMap(([key, item]) => {
230
+ const text = readableValue(item, labels, depth + 1);
231
+ return text === undefined || text === "" ? [] : [{ key, item, text }];
232
+ });
233
+ const address = entries.find((entry) => PERSON_ADDRESS_KEY.test(entry.key) &&
234
+ typeof entry.item === "string" &&
235
+ EMAIL.test(entry.item.trim()));
236
+ const name = address
237
+ ? entries.find((entry) => PERSON_NAME_KEY.test(entry.key) &&
238
+ typeof entry.item === "string" &&
239
+ entry.item.trim() !== "")
240
+ : undefined;
241
+ const rest = entries
242
+ .filter((entry) => entry !== address && entry !== name)
243
+ .map((entry) => `${humanizeIdentifier(entry.key)}: ${entry.text}`)
244
+ .join(", ");
245
+ if (!address) {
246
+ if (!rest)
247
+ return undefined;
248
+ return depth > 0 ? `(${rest})` : rest;
249
+ }
250
+ const person = name
251
+ ? `${name.text.trim()} <${address.text.trim()}>`
252
+ : address.text.trim();
253
+ return rest ? `${person} (${rest})` : person;
214
254
  }
215
255
  /**
216
256
  * The start of the call's JSON, when the call was too large to store and
@@ -1,3 +1,4 @@
1
+ import type { H3Event } from "h3";
1
2
  export interface AutoJoinDomainResult {
2
3
  joined: Array<{
3
4
  orgId: string;
@@ -9,5 +10,6 @@ export declare function __resetDomainMatchCacheForTests(): void;
9
10
  export declare function hasAutoJoinDomainMatch(rawEmail: string): Promise<boolean>;
10
11
  export interface AutoJoinDomainOptions {
11
12
  activateJoinedOrg?: "if-missing" | "always" | "never";
13
+ event?: H3Event;
12
14
  }
13
15
  export declare function autoJoinDomainMatchingOrgs(rawEmail: string, options?: AutoJoinDomainOptions): Promise<AutoJoinDomainResult>;
@@ -6,8 +6,12 @@ import { setActiveOrgId } from "./active-org.js";
6
6
  import { CROSS_APP_ORG_FEDERATION_FLAG } from "./feature-flags.js";
7
7
  import { isFreeEmailProvider } from "./free-email-providers.js";
8
8
  import { invalidateMemberOrgCaches } from "./request-org-cache.js";
9
+ import { trackDomainJoined } from "./track-domain-joined.js";
9
10
  const nanoid = () => globalThis.crypto?.randomUUID?.().replace(/-/g, "") ??
10
11
  Math.random().toString(36).slice(2) + Date.now().toString(36);
12
+ function sleep(ms) {
13
+ return new Promise((resolve) => setTimeout(resolve, ms));
14
+ }
11
15
  const NO_DOMAIN_MATCH_TTL_MS = 60_000;
12
16
  const noDomainMatchCache = createTtlCache({
13
17
  ttlMs: NO_DOMAIN_MATCH_TTL_MS,
@@ -94,6 +98,7 @@ export async function autoJoinDomainMatchingOrgs(rawEmail, options = {}) {
94
98
  return { joined: [], activeOrgId: null };
95
99
  }
96
100
  const joined = [];
101
+ const telemetryPromises = [];
97
102
  let federationSkipped = false;
98
103
  let federationUnavailable = false;
99
104
  let localJoinAttempted = false;
@@ -128,7 +133,17 @@ export async function autoJoinDomainMatchingOrgs(rawEmail, options = {}) {
128
133
  // Race with a parallel join (e.g. user accepted an invite to the
129
134
  // same org milliseconds earlier). The unique constraint keeps the
130
135
  // existing membership intact; just skip this org.
136
+ continue;
131
137
  }
138
+ telemetryPromises.push(trackDomainJoined({
139
+ email,
140
+ orgId: m.orgId,
141
+ federated: m.federated,
142
+ ...(options.event ? { event: options.event } : {}),
143
+ }));
144
+ }
145
+ if (telemetryPromises.length > 0) {
146
+ await Promise.race([Promise.all(telemetryPromises), sleep(1500)]);
132
147
  }
133
148
  if (federationSkipped && !localJoinAttempted && !federationUnavailable) {
134
149
  noDomainMatchCache.set(domain, true);
@@ -358,6 +358,7 @@ async function resolveOrgContextUncached(event) {
358
358
  const membershipsBeforeJoin = memberships;
359
359
  const joined = await autoJoinDomainMatchingOrgs(email, {
360
360
  activateJoinedOrg: "never",
361
+ event,
361
362
  });
362
363
  const joinedOrgId = joined.joined[0]?.orgId ?? null;
363
364
  if (joinedOrgId) {
@@ -90,9 +90,9 @@ export declare const setWorkspaceAppDefaultVisibilityHandler: import("h3").Event
90
90
  visibility: WorkspaceAppDefaultVisibility;
91
91
  }>>;
92
92
  export declare const createOrgHandler: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
93
- id?: undefined;
94
93
  name?: undefined;
95
94
  success: boolean;
95
+ id?: undefined;
96
96
  role?: undefined;
97
97
  } | {
98
98
  success?: undefined;
@@ -180,9 +180,9 @@ export declare const getMyOrgHandler: import("h3").EventHandlerWithFetch<import(
180
180
  soloDeploymentAdmin: boolean | undefined;
181
181
  }>>;
182
182
  export declare const createOrgHandler: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
183
- name?: undefined;
184
183
  success: boolean;
185
184
  id?: undefined;
185
+ name?: undefined;
186
186
  role?: undefined;
187
187
  } | {
188
188
  success?: undefined;
@@ -0,0 +1,7 @@
1
+ import type { H3Event } from "h3";
2
+ export declare function trackDomainJoined(input: {
3
+ email: string;
4
+ orgId: string;
5
+ federated: boolean;
6
+ event?: H3Event;
7
+ }): Promise<void>;
@@ -0,0 +1,30 @@
1
+ import { registerBackgroundWork } from "./track-invite-accepted.js";
2
+ export function trackDomainJoined(input) {
3
+ let promise;
4
+ try {
5
+ promise = Promise.all([
6
+ import("../tracking/registry.js"),
7
+ import("../app-config/index.js"),
8
+ ])
9
+ .then(async ([{ track, flushTracking }, { getAppConfig }]) => {
10
+ const app = getAppConfig().app.slug ?? "unknown";
11
+ track("org_domain_joined", {
12
+ app,
13
+ template: app,
14
+ org_id: input.orgId,
15
+ role: "member",
16
+ federated: input.federated,
17
+ }, { userId: input.email });
18
+ await flushTracking();
19
+ })
20
+ .catch(() => {
21
+ console.warn("[org] Could not emit domain join telemetry");
22
+ });
23
+ }
24
+ catch {
25
+ console.warn("[org] Could not emit domain join telemetry");
26
+ promise = Promise.resolve();
27
+ }
28
+ registerBackgroundWork(input.event, promise);
29
+ return promise;
30
+ }
@@ -75,8 +75,9 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
75
75
  user: "user";
76
76
  }>>;
77
77
  }, z.core.$strip>>, {
78
- deleted?: undefined;
79
78
  id?: undefined;
79
+ provider?: undefined;
80
+ deleted?: undefined;
80
81
  message?: undefined;
81
82
  providers: {
82
83
  id: string;
@@ -88,13 +89,12 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
88
89
  updatedAt: number;
89
90
  }[];
90
91
  count: number;
91
- provider?: undefined;
92
92
  found?: undefined;
93
93
  registered?: undefined;
94
94
  label?: undefined;
95
95
  } | {
96
- deleted?: undefined;
97
96
  id?: undefined;
97
+ deleted?: undefined;
98
98
  message?: undefined;
99
99
  providers?: undefined;
100
100
  count?: undefined;
@@ -103,30 +103,30 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
103
103
  registered?: undefined;
104
104
  label?: undefined;
105
105
  } | {
106
+ provider?: undefined;
106
107
  deleted?: undefined;
107
108
  message?: undefined;
108
109
  providers?: undefined;
109
110
  count?: undefined;
110
- provider?: undefined;
111
111
  found: boolean;
112
112
  id: string;
113
113
  registered?: undefined;
114
114
  label?: undefined;
115
115
  } | {
116
+ provider?: undefined;
116
117
  message?: undefined;
117
118
  providers?: undefined;
118
119
  count?: undefined;
119
- provider?: undefined;
120
120
  found?: undefined;
121
121
  deleted: boolean;
122
122
  id: string;
123
123
  registered?: undefined;
124
124
  label?: undefined;
125
125
  } | {
126
+ provider?: undefined;
126
127
  deleted?: undefined;
127
128
  providers?: undefined;
128
129
  count?: undefined;
129
- provider?: undefined;
130
130
  found?: undefined;
131
131
  registered: boolean;
132
132
  id: string;
@@ -55,8 +55,8 @@ export declare function createWriteSecretHandler(): import("h3").EventHandlerWit
55
55
  errorCode: "secret_managed_elsewhere";
56
56
  managedBy: SecretManagedBy;
57
57
  } | {
58
- errorCode?: undefined;
59
58
  error: string;
59
+ errorCode?: undefined;
60
60
  ok?: undefined;
61
61
  status?: undefined;
62
62
  } | {
@@ -65,8 +65,8 @@ export declare function createWriteSecretHandler(): import("h3").EventHandlerWit
65
65
  ok?: undefined;
66
66
  status?: undefined;
67
67
  } | {
68
- errorCode?: undefined;
69
68
  error?: undefined;
69
+ errorCode?: undefined;
70
70
  ok: boolean;
71
71
  status: string;
72
72
  } | {
@@ -54,8 +54,8 @@ export declare function resolveAgentEngineApiKeyWriteTarget(event: H3Event, scop
54
54
  error: string;
55
55
  }>;
56
56
  export declare function createAgentEngineApiKeyHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
57
- error: string;
58
57
  errorCode?: undefined;
58
+ error: string;
59
59
  code?: undefined;
60
60
  } | {
61
61
  error: string;
@@ -66,8 +66,8 @@ export declare function createAgentEngineApiKeyHandler(): import("h3").EventHand
66
66
  error: string;
67
67
  code: ProviderKeyCheckCode;
68
68
  } | {
69
- error?: undefined;
70
69
  errorCode?: undefined;
70
+ error?: undefined;
71
71
  code?: undefined;
72
72
  ok: boolean;
73
73
  key: string;
@@ -17,9 +17,9 @@ export type FirstTouchSyncResult = {
17
17
  * browser's `an_ft` and `an_lt` again, so Safari keeps them their 30 days.
18
18
  */
19
19
  export declare function createFirstTouchHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
20
+ stored?: undefined;
20
21
  error: string;
21
22
  reason?: undefined;
22
- stored?: undefined;
23
23
  firstTouch?: undefined;
24
24
  } | {
25
25
  error?: undefined;
@@ -17,8 +17,8 @@
17
17
  * typed JSON-in / JSON-out).
18
18
  */
19
19
  export declare function createTranscribeVoiceHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
20
- error: string;
21
20
  text?: undefined;
21
+ error: string;
22
22
  } | {
23
23
  error?: undefined;
24
24
  text: string;
@@ -1,6 +1,6 @@
1
1
  export const DEFAULT_APP_STATUS = "alpha";
2
2
  export const APP_STATUS = {
3
- clips: "alpha",
3
+ clips: "beta",
4
4
  design: "alpha",
5
5
  slides: "alpha",
6
6
  };
@@ -159,6 +159,18 @@ Real replies that missed, and what to send instead:
159
159
  - Use the connected Slack integration for every Slack read and write. Confirm
160
160
  the connected profile is Steve before the first write.
161
161
 
162
+ ## Dry run
163
+
164
+ When the invocation says "dry run", run every step, but nothing reaches a
165
+ reporter, a reviewer, or `main`:
166
+
167
+ - No reactions or replies. Record each reaction you'd add and the exact reply
168
+ you'd send, linking the fix branch where the PR would go.
169
+ - Treat other runs' claims as unclaimed; still honor Steve's `:mag:` and 👀.
170
+ - Publish each fix group as its own branch; skip PRs, merges, `/ship`, and
171
+ issue comments.
172
+ - Skip the Steve profile check; use the state the invocation names.
173
+
162
174
  ## 1. Collect (keep it under ~15 minutes)
163
175
 
164
176
  Work in a task-owned worktree on fresh `origin/main`. If you start in a shared
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.223.0",
3
+ "version": "0.223.1-nightly-20261103004150",
4
4
  "description": "The agentic application framework for building autonomous agents with intuitive UIs",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -747,7 +747,7 @@
747
747
  "yaml": "^2.9.0",
748
748
  "yjs": "^13.6.33",
749
749
  "zod": "^4.3.6",
750
- "@agent-native/agentkit": "^0.223.0",
750
+ "@agent-native/agentkit": "^0.223.1-nightly-20261103004150",
751
751
  "@agent-native/recap-cli": "^0.5.94"
752
752
  },
753
753
  "devDependencies": {
@@ -820,7 +820,7 @@
820
820
  "xlsx": "^0.18.5"
821
821
  },
822
822
  "peerDependencies": {
823
- "@agent-native/core-corpus": ">=0.196.0",
823
+ "@agent-native/core-corpus": "0.223.1-nightly-20261103004150",
824
824
  "@ai-sdk/anthropic": ">=3",
825
825
  "@ai-sdk/cohere": ">=3",
826
826
  "@ai-sdk/google": ">=3",
@@ -159,6 +159,18 @@ Real replies that missed, and what to send instead:
159
159
  - Use the connected Slack integration for every Slack read and write. Confirm
160
160
  the connected profile is Steve before the first write.
161
161
 
162
+ ## Dry run
163
+
164
+ When the invocation says "dry run", run every step, but nothing reaches a
165
+ reporter, a reviewer, or `main`:
166
+
167
+ - No reactions or replies. Record each reaction you'd add and the exact reply
168
+ you'd send, linking the fix branch where the PR would go.
169
+ - Treat other runs' claims as unclaimed; still honor Steve's `:mag:` and 👀.
170
+ - Publish each fix group as its own branch; skip PRs, merges, `/ship`, and
171
+ issue comments.
172
+ - Skip the Steve profile check; use the state the invocation names.
173
+
162
174
  ## 1. Collect (keep it under ~15 minutes)
163
175
 
164
176
  Work in a task-owned worktree on fresh `origin/main`. If you start in a shared