@agent-native/core 0.201.0-nightly-20261005153421 → 0.201.0
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/README.md +22 -22
- package/dist/a2a/server.d.ts +2 -2
- package/dist/a2a/server.js +13 -2
- package/dist/agent/thread-data-builder.d.ts +4 -0
- package/dist/agent/thread-data-builder.js +8 -0
- package/dist/agent-sql/analysis.d.ts +31 -0
- package/dist/agent-sql/analysis.js +169 -0
- package/dist/agent-sql/index.d.ts +4 -0
- package/dist/agent-sql/index.js +4 -0
- package/dist/agent-sql/lexer.d.ts +37 -0
- package/dist/agent-sql/lexer.js +357 -0
- package/dist/agent-sql/postgres.d.ts +51 -0
- package/dist/agent-sql/postgres.js +197 -0
- package/dist/agent-sql/query.d.ts +30 -0
- package/dist/agent-sql/query.js +514 -0
- package/dist/catalog.json +24 -24
- package/dist/client/analytics/index.d.ts +1 -1
- package/dist/client/analytics/index.js +1 -1
- package/dist/client/analytics.d.ts +8 -0
- package/dist/client/analytics.js +17 -2
- package/dist/client/session-replay.js +3 -2
- package/dist/identity/offboard.js +31 -3
- package/dist/identity/rekey.d.ts +12 -1
- package/dist/identity/rekey.js +77 -6
- package/dist/identity/retired-emails.d.ts +23 -0
- package/dist/identity/retired-emails.js +55 -0
- package/dist/mcp/actions/create-org-service-token.js +20 -8
- package/dist/mcp/actions/service-token-access.d.ts +1 -0
- package/dist/mcp/actions/service-token-access.js +41 -24
- package/dist/mcp/build-server.d.ts +20 -6
- package/dist/mcp/build-server.js +131 -35
- package/dist/mcp/connect-route.d.ts +4 -3
- package/dist/mcp/connect-route.js +140 -82
- package/dist/mcp/connect-store.d.ts +17 -11
- package/dist/mcp/connect-store.js +88 -60
- package/dist/mcp/credential-issuance.d.ts +23 -0
- package/dist/mcp/credential-issuance.js +77 -0
- package/dist/mcp/credential-membership.d.ts +26 -0
- package/dist/mcp/credential-membership.js +67 -0
- package/dist/mcp/oauth-route.js +133 -45
- package/dist/mcp/oauth-store.d.ts +15 -5
- package/dist/mcp/oauth-store.js +119 -34
- package/dist/mcp/oauth-token.d.ts +4 -0
- package/dist/mcp/oauth-token.js +5 -0
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/mcp/server.js +14 -0
- package/dist/onboarding/default-steps.js +1 -1
- package/dist/org/handlers.d.ts +1 -1
- package/dist/org/index.d.ts +2 -2
- package/dist/org/membership.d.ts +3 -1
- package/dist/org/membership.js +5 -2
- package/dist/provider-api/actions/custom-provider-registration.d.ts +12 -12
- package/dist/provider-api/actions/provider-api.d.ts +10 -10
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resource-changes/store.d.ts +2 -0
- package/dist/resource-changes/store.js +4 -0
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/scripts/db/exec.js +28 -101
- package/dist/scripts/db/patch.js +14 -34
- package/dist/scripts/db/postgres-client.d.ts +8 -1
- package/dist/scripts/db/postgres-client.js +20 -6
- package/dist/scripts/db/query.js +31 -33
- package/dist/scripts/db/safety.d.ts +47 -2
- package/dist/scripts/db/safety.js +383 -137
- package/dist/scripts/db/scoping.d.ts +1 -0
- package/dist/scripts/db/scoping.js +3 -2
- package/dist/search/index-store.d.ts +21 -0
- package/dist/search/index-store.js +47 -0
- package/dist/search/indexer.js +35 -32
- package/dist/search/registry.d.ts +6 -3
- package/dist/search/registry.js +30 -8
- package/dist/secrets/routes.d.ts +6 -6
- package/dist/server/agent-chat-plugin.js +18 -6
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-engine-default-model-route.d.ts +1 -1
- package/dist/server/agent-run-context.js +9 -0
- package/dist/server/attribution.d.ts +2 -0
- package/dist/server/attribution.js +7 -1
- package/dist/server/auth.js +9 -0
- package/dist/server/credential-membership-unavailable.d.ts +13 -0
- package/dist/server/credential-membership-unavailable.js +22 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +1 -0
- package/dist/server/onboarding-html.js +1 -1
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/recap-image-route.js +4 -0
- package/dist/server/release-schema.js +4 -0
- package/dist/shared/docs-url.d.ts +3 -1
- package/dist/shared/docs-url.js +6 -3
- package/dist/shared/environment-lanes.d.ts +8 -0
- package/dist/shared/environment-lanes.js +26 -0
- package/dist/templates/chat/.agents/skills/security/SKILL.md +9 -2
- package/dist/templates/chat/.agents/skills/storing-data/SKILL.md +12 -7
- package/dist/templates/chat/README.md +2 -2
- package/dist/templates/default/.agents/skills/security/SKILL.md +9 -2
- package/dist/templates/default/.agents/skills/storing-data/SKILL.md +12 -7
- package/dist/templates/default/app/routes/home.tsx +5 -1
- package/dist/templates/headless/.agents/skills/security/SKILL.md +9 -2
- package/dist/templates/headless/.agents/skills/storing-data/SKILL.md +12 -7
- package/dist/templates/workspace-core/.agents/skills/security/SKILL.md +9 -2
- package/dist/templates/workspace-core/.agents/skills/storing-data/SKILL.md +12 -7
- package/dist/tracking/providers.js +13 -1
- package/docs/content/authentication.mdx +5 -1
- package/docs/content/durable-background-runs.mdx +17 -8
- package/docs/content/external-agents.mdx +3 -1
- package/docs/content/mcp-protocol.mdx +6 -2
- package/docs/content/security.mdx +1 -1
- package/package.json +5 -4
- package/src/templates/chat/.agents/skills/security/SKILL.md +9 -2
- package/src/templates/chat/.agents/skills/storing-data/SKILL.md +12 -7
- package/src/templates/chat/README.md +2 -2
- package/src/templates/default/.agents/skills/security/SKILL.md +9 -2
- package/src/templates/default/.agents/skills/storing-data/SKILL.md +12 -7
- package/src/templates/default/app/routes/home.tsx +5 -1
- package/src/templates/headless/.agents/skills/security/SKILL.md +9 -2
- package/src/templates/headless/.agents/skills/storing-data/SKILL.md +12 -7
- package/src/templates/workspace-core/.agents/skills/security/SKILL.md +9 -2
- package/src/templates/workspace-core/.agents/skills/storing-data/SKILL.md +12 -7
|
@@ -42,6 +42,53 @@ export function ensureSearchIndexTables(injectedClient) {
|
|
|
42
42
|
});
|
|
43
43
|
return ensured;
|
|
44
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Raises an index's target version, leaving the rebuild for the first drain
|
|
47
|
+
* at that version to claim. A process on a lower version reads the index as
|
|
48
|
+
* outdated from then on and serves its previous search. Returns the target
|
|
49
|
+
* now in force, which is higher than `version` when a newer build raised it.
|
|
50
|
+
*/
|
|
51
|
+
export async function raiseSearchIndexTarget(exec, target) {
|
|
52
|
+
await exec.execute({
|
|
53
|
+
sql: `INSERT INTO ${SEARCH_INDEX_STATE_TABLE} (app, resource_type, target_version)
|
|
54
|
+
VALUES (?, ?, ?)
|
|
55
|
+
ON CONFLICT (app, resource_type) DO UPDATE SET
|
|
56
|
+
target_version = EXCLUDED.target_version,
|
|
57
|
+
rebuild_high_seq = NULL,
|
|
58
|
+
rebuild_started_at = NULL,
|
|
59
|
+
rebuild_completed_at = NULL
|
|
60
|
+
WHERE ${SEARCH_INDEX_STATE_TABLE}.target_version < EXCLUDED.target_version`,
|
|
61
|
+
args: [target.app, target.type, target.version],
|
|
62
|
+
});
|
|
63
|
+
const { rows } = await exec.execute({
|
|
64
|
+
sql: `SELECT target_version FROM ${SEARCH_INDEX_STATE_TABLE} WHERE app = ? AND resource_type = ?`,
|
|
65
|
+
args: [target.app, target.type],
|
|
66
|
+
});
|
|
67
|
+
const [row] = rows;
|
|
68
|
+
if (!row)
|
|
69
|
+
throw new Error("Raising the search index target left no row.");
|
|
70
|
+
return Number(row.target_version);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Marks the index as needing a rebuild at its current version, including a
|
|
74
|
+
* rebuild still queueing rows: its queue may predate the writes this
|
|
75
|
+
* invalidation is for. Nothing to do before the search tables exist: there
|
|
76
|
+
* is no index yet.
|
|
77
|
+
*/
|
|
78
|
+
export async function invalidateSearchIndex(exec, target) {
|
|
79
|
+
const { rows } = await exec.execute({
|
|
80
|
+
sql: `SELECT 1 AS present WHERE to_regclass(?) IS NOT NULL`,
|
|
81
|
+
args: [SEARCH_INDEX_STATE_TABLE],
|
|
82
|
+
});
|
|
83
|
+
if (!rows.length)
|
|
84
|
+
return;
|
|
85
|
+
await exec.execute({
|
|
86
|
+
sql: `UPDATE ${SEARCH_INDEX_STATE_TABLE}
|
|
87
|
+
SET rebuild_high_seq = NULL, rebuild_started_at = NULL, rebuild_completed_at = NULL
|
|
88
|
+
WHERE app = ? AND resource_type = ?`,
|
|
89
|
+
args: [target.app, target.type],
|
|
90
|
+
});
|
|
91
|
+
}
|
|
45
92
|
async function ensureAll(injectedClient) {
|
|
46
93
|
const options = { injectedClient };
|
|
47
94
|
await ensureTableExists(SEARCH_RESOURCES_TABLE, SEARCH_RESOURCES_CREATE_SQL, options);
|
package/dist/search/indexer.js
CHANGED
|
@@ -13,7 +13,7 @@ import { getAppConfig } from "../app-config/index.js";
|
|
|
13
13
|
import { getDbExec } from "../db/client.js";
|
|
14
14
|
import { RESOURCE_CHANGE_MAX_ATTEMPTS, claimResourceChanges, completeResourceChanges, enqueueAllResourceChanges, failResourceChanges, hasPendingResourceChanges, resourceChangeBacklogColumns, resourceChangeCaptureInstalled, subscribeResourceChangeConsumer, } from "../resource-changes/store.js";
|
|
15
15
|
import { getRequestRunContext } from "../server/request-context.js";
|
|
16
|
-
import { SEARCH_INDEX_STATE_TABLE, SEARCH_RESOURCES_TABLE, } from "./index-store.js";
|
|
16
|
+
import { SEARCH_INDEX_STATE_TABLE, SEARCH_RESOURCES_TABLE, invalidateSearchIndex, } from "./index-store.js";
|
|
17
17
|
import { SEARCH_CHANGE_CONSUMER, listSearchableResources, searchableResourceSource, } from "./registry.js";
|
|
18
18
|
import { buildSearchVector, normalizeSearchText, utf8ByteLength, } from "./tokenize.js";
|
|
19
19
|
const CLAIM_BATCH = 50;
|
|
@@ -247,7 +247,7 @@ async function captureInstalled(exec, registration, runtime) {
|
|
|
247
247
|
const source = searchableResourceSource(registration);
|
|
248
248
|
const installed = await resourceChangeCaptureInstalled(exec, source);
|
|
249
249
|
if (!installed) {
|
|
250
|
-
await
|
|
250
|
+
await invalidateSearchIndex(exec, registration);
|
|
251
251
|
if (runtime.captureInstalled !== false) {
|
|
252
252
|
console.error(`[search] Change capture for ${registration.app}/${registration.type} is missing or disabled on table "${source.table}". ` +
|
|
253
253
|
"Add searchIndexMigration() for it to the app's runMigrations list, or re-enable its triggers. " +
|
|
@@ -261,27 +261,6 @@ async function captureInstalled(exec, registration, runtime) {
|
|
|
261
261
|
runtime.captureVerifiedAt = Date.now();
|
|
262
262
|
return installed;
|
|
263
263
|
}
|
|
264
|
-
/**
|
|
265
|
-
* Marks the index as needing a rebuild at its current version. Nothing to do
|
|
266
|
-
* before the search tables exist: there is no index yet.
|
|
267
|
-
*/
|
|
268
|
-
async function invalidateIndex(exec, registration) {
|
|
269
|
-
const { rows } = await exec.execute({
|
|
270
|
-
sql: `SELECT to_regclass(?) IS NOT NULL AS present`,
|
|
271
|
-
args: [SEARCH_INDEX_STATE_TABLE],
|
|
272
|
-
});
|
|
273
|
-
const [table] = rows;
|
|
274
|
-
if (!table)
|
|
275
|
-
throw new Error("Looking up the search index table returned no row.");
|
|
276
|
-
if (!flag(table.present))
|
|
277
|
-
return;
|
|
278
|
-
await exec.execute({
|
|
279
|
-
sql: `UPDATE ${SEARCH_INDEX_STATE_TABLE}
|
|
280
|
-
SET rebuild_high_seq = NULL, rebuild_started_at = NULL, rebuild_completed_at = NULL
|
|
281
|
-
WHERE app = ? AND resource_type = ? AND rebuild_high_seq IS NOT NULL`,
|
|
282
|
-
args: [registration.app, registration.type],
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
264
|
function flag(value) {
|
|
286
265
|
return value === true || value === "t" || value === "true";
|
|
287
266
|
}
|
|
@@ -332,7 +311,15 @@ function rebuildNeedsQueueing(registration, state) {
|
|
|
332
311
|
* one, then queues every source row once. Only one process wins either, and
|
|
333
312
|
* from a version bump on, the fence stops older processes. Queueing replaces
|
|
334
313
|
* changes they already hold, so none of their in-flight work can complete
|
|
335
|
-
* what the rebuild queued.
|
|
314
|
+
* what the rebuild queued. The queue counts toward completion only if no
|
|
315
|
+
* other transaction has written the index state since the claim: an
|
|
316
|
+
* invalidation or a later claim meanwhile means it may miss writes.
|
|
317
|
+
*
|
|
318
|
+
* The claim is the row's `xmin`, the transaction that last wrote it, not its
|
|
319
|
+
* timestamp: `now()` is when a transaction began, so a later claim can carry
|
|
320
|
+
* the same one. Writes in the claim's own transaction keep its `xmin`, so
|
|
321
|
+
* nothing may invalidate between this claim and its publish on a shared
|
|
322
|
+
* transaction.
|
|
336
323
|
*/
|
|
337
324
|
async function startRebuild(exec, registration, state) {
|
|
338
325
|
const { rows } = !state || state.targetVersion < registration.version
|
|
@@ -345,7 +332,7 @@ async function startRebuild(exec, registration, state) {
|
|
|
345
332
|
rebuild_high_seq = NULL,
|
|
346
333
|
rebuild_completed_at = NULL
|
|
347
334
|
WHERE ${SEARCH_INDEX_STATE_TABLE}.target_version < EXCLUDED.target_version
|
|
348
|
-
RETURNING
|
|
335
|
+
RETURNING xmin::text AS claim`,
|
|
349
336
|
args: [registration.app, registration.type, registration.version],
|
|
350
337
|
})
|
|
351
338
|
: await exec.execute({
|
|
@@ -353,7 +340,7 @@ async function startRebuild(exec, registration, state) {
|
|
|
353
340
|
WHERE app = ? AND resource_type = ? AND target_version = ?
|
|
354
341
|
AND rebuild_high_seq IS NULL AND rebuild_completed_at IS NULL
|
|
355
342
|
AND (rebuild_started_at IS NULL OR rebuild_started_at < now() - make_interval(secs => ?))
|
|
356
|
-
RETURNING
|
|
343
|
+
RETURNING xmin::text AS claim`,
|
|
357
344
|
args: [
|
|
358
345
|
registration.app,
|
|
359
346
|
registration.type,
|
|
@@ -361,18 +348,28 @@ async function startRebuild(exec, registration, state) {
|
|
|
361
348
|
REBUILD_ENQUEUE_RETRY_MS / 1000,
|
|
362
349
|
],
|
|
363
350
|
});
|
|
364
|
-
|
|
351
|
+
const [claimed] = rows;
|
|
352
|
+
if (!claimed)
|
|
365
353
|
return;
|
|
366
354
|
const highSeq = await enqueueAllResourceChanges(exec, searchableResourceSource(registration), SEARCH_CHANGE_CONSUMER, "rebuild");
|
|
367
355
|
await exec.execute({
|
|
368
356
|
sql: `UPDATE ${SEARCH_INDEX_STATE_TABLE} SET rebuild_high_seq = ?::bigint
|
|
369
|
-
WHERE app = ? AND resource_type = ? AND target_version =
|
|
370
|
-
|
|
357
|
+
WHERE app = ? AND resource_type = ? AND target_version = ?
|
|
358
|
+
AND xmin = ?::xid`,
|
|
359
|
+
args: [
|
|
360
|
+
highSeq,
|
|
361
|
+
registration.app,
|
|
362
|
+
registration.type,
|
|
363
|
+
registration.version,
|
|
364
|
+
String(claimed.claim),
|
|
365
|
+
],
|
|
371
366
|
});
|
|
372
367
|
}
|
|
373
368
|
/**
|
|
374
|
-
* Marks the rebuild complete once every change it queued is processed
|
|
375
|
-
*
|
|
369
|
+
* Marks the rebuild complete once every change it queued is processed, if
|
|
370
|
+
* it's still the rebuild that was checked: an invalidation meanwhile clears
|
|
371
|
+
* its highest `seq`, which leaves the rows to be queued again. Returns
|
|
372
|
+
* whether the state may have changed.
|
|
376
373
|
*/
|
|
377
374
|
async function completeRebuildIfDone(exec, registration, state) {
|
|
378
375
|
if (state.rebuildHighSeq === null)
|
|
@@ -386,8 +383,14 @@ async function completeRebuildIfDone(exec, registration, state) {
|
|
|
386
383
|
sql: `UPDATE ${SEARCH_INDEX_STATE_TABLE}
|
|
387
384
|
SET index_version = target_version, rebuild_completed_at = now()
|
|
388
385
|
WHERE app = ? AND resource_type = ? AND target_version = ? AND rebuild_completed_at IS NULL
|
|
386
|
+
AND rebuild_high_seq = ?::bigint
|
|
389
387
|
RETURNING target_version`,
|
|
390
|
-
args: [
|
|
388
|
+
args: [
|
|
389
|
+
registration.app,
|
|
390
|
+
registration.type,
|
|
391
|
+
state.targetVersion,
|
|
392
|
+
state.rebuildHighSeq,
|
|
393
|
+
],
|
|
391
394
|
});
|
|
392
395
|
if (rows.length) {
|
|
393
396
|
// Rows whose source row is gone are deletes the feed missed: the source
|
|
@@ -45,9 +45,12 @@ export declare function listSearchableResources(): SearchableResourceRegistratio
|
|
|
45
45
|
export declare function unregisterSearchableResource(app: string, type: string): void;
|
|
46
46
|
export declare function searchableResourceSource(registration: SearchableResourceRegistration): ResourceChangeSource;
|
|
47
47
|
/**
|
|
48
|
-
* A named migration that creates the search tables
|
|
49
|
-
*
|
|
50
|
-
*
|
|
48
|
+
* A named migration that creates the search tables, raises the index's
|
|
49
|
+
* target to the registration's version, and installs change capture on the
|
|
50
|
+
* registration's table. Add it to the app's `runMigrations`
|
|
51
|
+
* list with the app's next version number. It's recorded under `name` plus a
|
|
52
|
+
* fingerprint of the capture SQL, so a release that changes that SQL installs
|
|
53
|
+
* it again.
|
|
51
54
|
*/
|
|
52
55
|
export declare function searchIndexMigration(registration: SearchableResourceRegistration, entry: {
|
|
53
56
|
version: number;
|
package/dist/search/registry.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { getTableName } from "drizzle-orm";
|
|
2
2
|
import { deferMigration } from "../db/migrations.js";
|
|
3
3
|
import { registerRecurringSweepHandler } from "../jobs/sweep-hooks.js";
|
|
4
|
-
import { assertResourceKey, installResourceChangeCapture, registerAfterWriteDrain, resourceChangeTriggerNames, } from "../resource-changes/store.js";
|
|
5
|
-
import { ensureSearchIndexTables } from "./index-store.js";
|
|
4
|
+
import { assertResourceKey, installResourceChangeCapture, registerAfterWriteDrain, resourceChangeCaptureFingerprint, resourceChangeCaptureInstalled, resourceChangeTriggerNames, } from "../resource-changes/store.js";
|
|
5
|
+
import { ensureSearchIndexTables, invalidateSearchIndex, raiseSearchIndexTarget, } from "./index-store.js";
|
|
6
6
|
/** The change-feed consumer name the search index subscribes as. */
|
|
7
7
|
export const SEARCH_CHANGE_CONSUMER = "search";
|
|
8
8
|
const registrations = new Map();
|
|
@@ -73,21 +73,43 @@ export function searchableResourceSource(registration) {
|
|
|
73
73
|
};
|
|
74
74
|
}
|
|
75
75
|
/**
|
|
76
|
-
* A named migration that creates the search tables
|
|
77
|
-
*
|
|
78
|
-
*
|
|
76
|
+
* A named migration that creates the search tables, raises the index's
|
|
77
|
+
* target to the registration's version, and installs change capture on the
|
|
78
|
+
* registration's table. Add it to the app's `runMigrations`
|
|
79
|
+
* list with the app's next version number. It's recorded under `name` plus a
|
|
80
|
+
* fingerprint of the capture SQL, so a release that changes that SQL installs
|
|
81
|
+
* it again.
|
|
79
82
|
*/
|
|
80
83
|
export function searchIndexMigration(registration, entry) {
|
|
84
|
+
const source = searchableResourceSource(registration);
|
|
81
85
|
return {
|
|
82
86
|
version: entry.version,
|
|
83
|
-
|
|
87
|
+
// Under the bare name, a database that recorded an earlier build's
|
|
88
|
+
// capture would never get this one, and search would stay on the
|
|
89
|
+
// fallback for good.
|
|
90
|
+
name: `${entry.name}@${resourceChangeCaptureFingerprint(source)}`,
|
|
84
91
|
sql: {},
|
|
85
92
|
run: async (exec) => {
|
|
86
93
|
await ensureSearchIndexTables(exec);
|
|
87
|
-
|
|
88
|
-
//
|
|
94
|
+
// Before capture exists: a build still running a lower version would
|
|
95
|
+
// otherwise see this capture, finish a rebuild at its own version, and
|
|
96
|
+
// answer from rows it didn't write.
|
|
97
|
+
const target = await raiseSearchIndexTarget(exec, registration);
|
|
98
|
+
// A newer build owns the index and its capture, which this build's
|
|
99
|
+
// older SQL would replace.
|
|
100
|
+
if (target > registration.version)
|
|
101
|
+
return;
|
|
102
|
+
const hadCapture = await resourceChangeCaptureInstalled(exec, source);
|
|
103
|
+
const installed = await installResourceChangeCapture(exec, source, SEARCH_CHANGE_CONSUMER);
|
|
104
|
+
// A busy table: try again at the next migration run, which serverless
|
|
105
|
+
// hosts reach only at their next release, rather than block writes.
|
|
89
106
|
if (!installed)
|
|
90
107
|
return deferMigration();
|
|
108
|
+
// Writes made while capture was missing were never recorded. A search
|
|
109
|
+
// that saw it missing has already discarded the finished index, but
|
|
110
|
+
// nothing guarantees one ran.
|
|
111
|
+
if (!hadCapture)
|
|
112
|
+
await invalidateSearchIndex(exec, registration);
|
|
91
113
|
},
|
|
92
114
|
};
|
|
93
115
|
}
|
package/dist/secrets/routes.d.ts
CHANGED
|
@@ -73,32 +73,32 @@ export declare function createWriteSecretHandler(): import("h3").EventHandlerWit
|
|
|
73
73
|
ok?: undefined;
|
|
74
74
|
status?: undefined;
|
|
75
75
|
} | {
|
|
76
|
+
error?: undefined;
|
|
76
77
|
errorCode?: undefined;
|
|
77
78
|
retryable?: undefined;
|
|
78
79
|
ok: boolean;
|
|
79
80
|
status: string;
|
|
80
|
-
error?: undefined;
|
|
81
81
|
} | {
|
|
82
82
|
ok?: undefined;
|
|
83
83
|
error: string;
|
|
84
84
|
removed?: undefined;
|
|
85
85
|
} | {
|
|
86
|
+
error?: undefined;
|
|
86
87
|
ok: boolean;
|
|
87
88
|
removed: boolean;
|
|
88
|
-
error?: undefined;
|
|
89
89
|
}>>;
|
|
90
90
|
export declare function createTestSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
91
91
|
ok?: undefined;
|
|
92
92
|
error: string;
|
|
93
93
|
note?: undefined;
|
|
94
94
|
} | {
|
|
95
|
+
error?: undefined;
|
|
95
96
|
ok: boolean;
|
|
96
97
|
note?: undefined;
|
|
97
|
-
error?: undefined;
|
|
98
98
|
} | {
|
|
99
|
+
error?: undefined;
|
|
99
100
|
ok: boolean;
|
|
100
101
|
note: string;
|
|
101
|
-
error?: undefined;
|
|
102
102
|
} | {
|
|
103
103
|
note?: undefined;
|
|
104
104
|
ok: boolean;
|
|
@@ -126,11 +126,11 @@ export declare function createAdHocSecretHandler(): import("h3").EventHandlerWit
|
|
|
126
126
|
} | {
|
|
127
127
|
error: string;
|
|
128
128
|
} | {
|
|
129
|
+
error?: undefined;
|
|
129
130
|
ok: boolean;
|
|
130
131
|
key: string;
|
|
131
|
-
error?: undefined;
|
|
132
132
|
} | {
|
|
133
|
+
error?: undefined;
|
|
133
134
|
ok: boolean;
|
|
134
135
|
removed: boolean;
|
|
135
|
-
error?: undefined;
|
|
136
136
|
}>>;
|
|
@@ -49,6 +49,7 @@ import { normalizeDatabaseToolsMode } from "../scripts/db/tool-mode.js";
|
|
|
49
49
|
import { getSetting, putSetting } from "../settings/store.js";
|
|
50
50
|
import { retryContextFromRequest, } from "../shared/agent-chat-run-not-started.js";
|
|
51
51
|
import { ANALYTICS_CLIENT_PLATFORM_BODY_FIELD, normalizeAnalyticsClientPlatform, } from "../shared/analytics-platform.js";
|
|
52
|
+
import { backgroundAgentTurnIdForReceipt } from "../shared/background-agent-session.js";
|
|
52
53
|
import { docsUrl } from "../shared/docs-url.js";
|
|
53
54
|
import { stripSqlParams } from "../shared/error-noise.js";
|
|
54
55
|
import { track } from "../tracking/registry.js";
|
|
@@ -2901,6 +2902,22 @@ export function createAgentChatPlugin(options) {
|
|
|
2901
2902
|
? { refusedRetry: details.retryContext ?? {} }
|
|
2902
2903
|
: {}),
|
|
2903
2904
|
});
|
|
2905
|
+
// Background agent sessions send their operation id as
|
|
2906
|
+
// queuedMessageId (Content binds comment AI turns to it). It never
|
|
2907
|
+
// enters the queue, so it has no promotion claim; the session's
|
|
2908
|
+
// derived turn id is what marks the request as one.
|
|
2909
|
+
const queuedMessage = !details.queuedMessageId
|
|
2910
|
+
? undefined
|
|
2911
|
+
: details.turnId ===
|
|
2912
|
+
backgroundAgentTurnIdForReceipt(threadId, details.queuedMessageId)
|
|
2913
|
+
? {
|
|
2914
|
+
kind: "background-operation",
|
|
2915
|
+
id: details.queuedMessageId,
|
|
2916
|
+
}
|
|
2917
|
+
: {
|
|
2918
|
+
id: details.queuedMessageId,
|
|
2919
|
+
claimId: details.queuedMessageClaimId,
|
|
2920
|
+
};
|
|
2904
2921
|
let submissionFailure;
|
|
2905
2922
|
await updateThreadData(threadId, "{}", "", thread.preview, thread.messageCount, {
|
|
2906
2923
|
transformThreadData: (threadData) => {
|
|
@@ -2917,12 +2934,7 @@ export function createAgentChatPlugin(options) {
|
|
|
2917
2934
|
submissionFailure = "invalid_thread_data";
|
|
2918
2935
|
return threadData;
|
|
2919
2936
|
}
|
|
2920
|
-
const result = applySubmittedUserMessage(repo, userMessage,
|
|
2921
|
-
? {
|
|
2922
|
-
id: details.queuedMessageId,
|
|
2923
|
-
claimId: details.queuedMessageClaimId,
|
|
2924
|
-
}
|
|
2925
|
-
: undefined);
|
|
2937
|
+
const result = applySubmittedUserMessage(repo, userMessage, queuedMessage);
|
|
2926
2938
|
if (!("repo" in result)) {
|
|
2927
2939
|
submissionFailure = result.status;
|
|
2928
2940
|
return threadData;
|
|
@@ -66,12 +66,12 @@ export declare function createAgentEngineApiKeyHandler(): import("h3").EventHand
|
|
|
66
66
|
code: any;
|
|
67
67
|
} | {
|
|
68
68
|
errorCode?: undefined;
|
|
69
|
-
error?: undefined;
|
|
70
69
|
code?: undefined;
|
|
71
70
|
ok: boolean;
|
|
72
71
|
key: string;
|
|
73
72
|
baseUrlKey?: string;
|
|
74
73
|
scope: AgentEngineApiKeyScope;
|
|
75
74
|
defaultModel?: import("./agent-engine-default-model-route.js").SavedKeyDefaultModelOutcome;
|
|
75
|
+
error?: undefined;
|
|
76
76
|
}>>;
|
|
77
77
|
export {};
|
|
@@ -8,8 +8,8 @@ export declare const AGENT_ENGINE_API_KEY_ACTION_NAME = "agent-engine-api-key";
|
|
|
8
8
|
* resolveEngine's credential detection.
|
|
9
9
|
*/
|
|
10
10
|
export declare function createAgentEngineDisconnectHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
11
|
-
ok?: undefined;
|
|
12
11
|
error: string;
|
|
12
|
+
ok?: undefined;
|
|
13
13
|
scope?: undefined;
|
|
14
14
|
} | {
|
|
15
15
|
ok: boolean;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createError, getHeader } from "h3";
|
|
2
2
|
import { ANALYTICS_CLIENT_PLATFORM_BODY_FIELD, ANALYTICS_CLIENT_PLATFORM_HEADER, normalizeAnalyticsClientPlatform, } from "../shared/analytics-platform.js";
|
|
3
3
|
import { SYNTHETIC_TRAFFIC_HEADER, isSyntheticTrafficValue, } from "../shared/test-traffic.js";
|
|
4
|
+
import { CREDENTIAL_MEMBERSHIP_UNAVAILABLE_MESSAGE, isCredentialMembershipUnavailable, } from "./credential-membership-unavailable.js";
|
|
4
5
|
import { getRequestContext, getRequestIdentityAuthenticatedAtMs, getRequestIdentitySessionToken, runWithRequestContext, } from "./request-context.js";
|
|
5
6
|
const resolveOrgIdForEmail = (...args) => import("../org/context.js").then(({ resolveOrgIdForEmail }) => resolveOrgIdForEmail(...args));
|
|
6
7
|
const getOrgContext = (...args) => import("../org/context.js").then(({ getOrgContext }) => getOrgContext(...args));
|
|
@@ -99,6 +100,14 @@ export async function resolveAgentRunOwnerContext(event, options = {}) {
|
|
|
99
100
|
return seeded;
|
|
100
101
|
const { getSession } = await import("./auth.js");
|
|
101
102
|
const session = await getSession(event);
|
|
103
|
+
if (!session?.email && isCredentialMembershipUnavailable(event)) {
|
|
104
|
+
throw createError({
|
|
105
|
+
statusCode: 503,
|
|
106
|
+
statusMessage: "Service Unavailable",
|
|
107
|
+
message: CREDENTIAL_MEMBERSHIP_UNAVAILABLE_MESSAGE,
|
|
108
|
+
headers: { "Retry-After": "5" },
|
|
109
|
+
});
|
|
110
|
+
}
|
|
102
111
|
if (session?.email) {
|
|
103
112
|
const orgScope = getRequestContext()?.orgScope;
|
|
104
113
|
const identityAuthenticatedAtMs = getRequestIdentityAuthenticatedAtMs(event, session.email);
|
|
@@ -15,6 +15,8 @@ const STRING_FIELDS = [
|
|
|
15
15
|
"vector_source",
|
|
16
16
|
"landing_path",
|
|
17
17
|
"landing_referrer",
|
|
18
|
+
"site_referrer",
|
|
19
|
+
"site_landing_path",
|
|
18
20
|
"landed_at",
|
|
19
21
|
"capture_truncated",
|
|
20
22
|
];
|
|
@@ -111,8 +113,10 @@ export function deriveReferralSource(ft) {
|
|
|
111
113
|
path.includes("/share-plan/")) {
|
|
112
114
|
return "plan_share";
|
|
113
115
|
}
|
|
114
|
-
if (isExternalReferrerHost(ft?.landing_referrer)
|
|
116
|
+
if (isExternalReferrerHost(ft?.landing_referrer) ||
|
|
117
|
+
isExternalReferrerHost(ft?.site_referrer)) {
|
|
115
118
|
return "external";
|
|
119
|
+
}
|
|
116
120
|
return "direct";
|
|
117
121
|
}
|
|
118
122
|
export function deriveSignupAttribution(ft) {
|
|
@@ -139,6 +143,8 @@ export function deriveSignupAttribution(ft) {
|
|
|
139
143
|
setIf("vector_source", ft.vector_source);
|
|
140
144
|
setIf("first_touch_path", ft.landing_path);
|
|
141
145
|
setIf("landing_referrer", ft.landing_referrer);
|
|
146
|
+
setIf("site_referrer", ft.site_referrer);
|
|
147
|
+
setIf("site_landing_path", ft.site_landing_path);
|
|
142
148
|
if (ft.capture_truncated === "1")
|
|
143
149
|
out.attribution_truncated = "true";
|
|
144
150
|
return out;
|
package/dist/server/auth.js
CHANGED
|
@@ -69,6 +69,7 @@ import { createBetterAuthSessionForEmail, ensureGoogleAuthIdentity, getBetterAut
|
|
|
69
69
|
import { BUILDER_CONNECT_PARAM, BUILDER_RELAY_PATH, BUILDER_RELAY_STATE_PARAM, verifyBuilderConnectTokenAndGetOwner, verifyBuilderPreviewRelayStateForCallback, } from "./builder-browser.js";
|
|
70
70
|
import { frameworkSessionHintCookieName, resolveAuthCookieNamespace, } from "./cookie-namespace.js";
|
|
71
71
|
import { getAllowedCorsOrigin, readCorsAllowedOrigins, } from "./cors-origins.js";
|
|
72
|
+
import { isCredentialMembershipUnavailable, markCredentialMembershipUnavailable, respondCredentialMembershipUnavailable, } from "./credential-membership-unavailable.js";
|
|
72
73
|
import { resolveDeployEnvironment } from "./deploy-environment.js";
|
|
73
74
|
import { getSignInBlockingSettingKeys } from "./deploy-settings.js";
|
|
74
75
|
import { readDesktopSso, writeDesktopSso, clearDesktopSso, } from "./desktop-sso.js";
|
|
@@ -777,7 +778,12 @@ export async function getMcpOAuthBearerSession(event) {
|
|
|
777
778
|
const result = await verifyAuth(authHeader, undefined, {
|
|
778
779
|
resourceUrl: getMcpOAuthAudiences(event),
|
|
779
780
|
allowDevOpen: false,
|
|
781
|
+
requestOrigin: getOrigin(event),
|
|
780
782
|
});
|
|
783
|
+
if (!result.authed && result.unavailable) {
|
|
784
|
+
markCredentialMembershipUnavailable(event);
|
|
785
|
+
return null;
|
|
786
|
+
}
|
|
781
787
|
const identity = result.authed ? result.identity : undefined;
|
|
782
788
|
if (!identity?.userEmail)
|
|
783
789
|
return null;
|
|
@@ -3008,6 +3014,9 @@ function createAuthGuardFn(app) {
|
|
|
3008
3014
|
}
|
|
3009
3015
|
return;
|
|
3010
3016
|
}
|
|
3017
|
+
if (isCredentialMembershipUnavailable(event)) {
|
|
3018
|
+
return respondCredentialMembershipUnavailable(event);
|
|
3019
|
+
}
|
|
3011
3020
|
if (p.startsWith("/api/") || p.startsWith("/_agent-native/")) {
|
|
3012
3021
|
setResponseStatus(event, 401);
|
|
3013
3022
|
// Dev-only breadcrumb for the loopback origin-label trap: the session
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type H3Event } from "h3";
|
|
2
|
+
export declare const CREDENTIAL_MEMBERSHIP_UNAVAILABLE_MESSAGE = "Organization membership could not be verified. Retry shortly.";
|
|
3
|
+
/**
|
|
4
|
+
* Records that this request's bearer token verified but its organization
|
|
5
|
+
* membership could not be checked. Such a request has no session, but it is
|
|
6
|
+
* not unauthenticated: answer a retryable 503, never a 401 that tells the
|
|
7
|
+
* client to sign in again.
|
|
8
|
+
*/
|
|
9
|
+
export declare function markCredentialMembershipUnavailable(event: H3Event): void;
|
|
10
|
+
export declare function isCredentialMembershipUnavailable(event: H3Event): boolean;
|
|
11
|
+
export declare function respondCredentialMembershipUnavailable(event: H3Event): {
|
|
12
|
+
error: string;
|
|
13
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { setResponseHeader, setResponseStatus } from "h3";
|
|
2
|
+
const CONTEXT_KEY = "__anCredentialMembershipUnavailable";
|
|
3
|
+
export const CREDENTIAL_MEMBERSHIP_UNAVAILABLE_MESSAGE = "Organization membership could not be verified. Retry shortly.";
|
|
4
|
+
/**
|
|
5
|
+
* Records that this request's bearer token verified but its organization
|
|
6
|
+
* membership could not be checked. Such a request has no session, but it is
|
|
7
|
+
* not unauthenticated: answer a retryable 503, never a 401 that tells the
|
|
8
|
+
* client to sign in again.
|
|
9
|
+
*/
|
|
10
|
+
export function markCredentialMembershipUnavailable(event) {
|
|
11
|
+
const holder = event;
|
|
12
|
+
(holder.context ??= {})[CONTEXT_KEY] = true;
|
|
13
|
+
}
|
|
14
|
+
export function isCredentialMembershipUnavailable(event) {
|
|
15
|
+
return (event.context?.[CONTEXT_KEY] ===
|
|
16
|
+
true);
|
|
17
|
+
}
|
|
18
|
+
export function respondCredentialMembershipUnavailable(event) {
|
|
19
|
+
setResponseStatus(event, 503);
|
|
20
|
+
setResponseHeader(event, "Retry-After", "5");
|
|
21
|
+
return { error: CREDENTIAL_MEMBERSHIP_UNAVAILABLE_MESSAGE };
|
|
22
|
+
}
|
package/dist/server/index.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export { createOpenRouteHandler, type OpenRouteOptions } from "./open-route.js";
|
|
|
13
13
|
export { createEmbedStartRouteHandler, buildEmbedStartPath, type EmbedStartRouteOptions, } from "./embed-route.js";
|
|
14
14
|
export { createEmbedSessionTicket, consumeEmbedSessionTicket, normalizeEmbedTargetPath, requestHasEmbedAuthMarker, resolveEmbedSessionFromRequest, setEmbedSessionCookie, signEmbedSessionToken, verifyEmbedSessionToken, type ConsumedEmbedSessionTicket, type ConsumeEmbedSessionTicketOptions, type EmbedSessionTicket, type EmbedSessionTicketInput, type EmbedSessionTokenClaims, type ResolvedEmbedSession, type VerifyEmbedSessionTokenResult, } from "./embed-session.js";
|
|
15
15
|
export { createSSEHandler, type SSEHandlerOptions } from "./sse.js";
|
|
16
|
+
export { isCredentialMembershipUnavailable } from "./credential-membership-unavailable.js";
|
|
16
17
|
export { mountAuthMiddleware, autoMountAuth, registerAuthPublicPaths, getSession, getMcpOAuthBearerSession, logout, COOKIE_NAME, addSession, removeSession, getSessionEmail, getFrameworkSessionCookieValues, setFrameworkSessionCookie, setFirstRunOnboardingCookie, clearFrameworkSessionCookies, runAuthGuard, registerDesktopExchange, prepareDesktopOAuthBrowserBinding, matchesDesktopOAuthBrowserBinding, DESKTOP_OAUTH_BROWSER_BINDING_COOKIE, setDesktopExchange, setDesktopExchangeError, safeReturnPath, type DesktopExchangeErrorPayload, type AuthSession, type AuthOptions, } from "./auth.js";
|
|
17
18
|
export { handleIdentitySso, ensureIdentityUser, getIdentityHubUrl, isIdentitySsoEnabled, isIdentitySsoBypassPath, identitySsoLoginButtonHtml, IDENTITY_SSO_BOOTSTRAP_ACTIVATE_PATH, IDENTITY_SSO_BOOTSTRAP_BINDING_COOKIE, clearIdentitySsoBootstrapBindingCookie, getIdentitySsoBootstrapBindingCookie, setIdentitySsoBootstrapBindingCookie, IDENTITY_SSO_PROVIDER_ID, IDENTITY_SSO_SCOPE, IDENTITY_SSO_DESKTOP_COMPLETE_PATH, } from "./identity-sso.js";
|
|
18
19
|
export { createBetterAuthSessionForEmail, ensureGoogleAuthIdentity, hasGoogleAuthIdentity, setBetterAuthSessionCookie, } from "./better-auth-instance.js";
|
package/dist/server/index.js
CHANGED
|
@@ -13,6 +13,7 @@ export { createOpenRouteHandler } from "./open-route.js";
|
|
|
13
13
|
export { createEmbedStartRouteHandler, buildEmbedStartPath, } from "./embed-route.js";
|
|
14
14
|
export { createEmbedSessionTicket, consumeEmbedSessionTicket, normalizeEmbedTargetPath, requestHasEmbedAuthMarker, resolveEmbedSessionFromRequest, setEmbedSessionCookie, signEmbedSessionToken, verifyEmbedSessionToken, } from "./embed-session.js";
|
|
15
15
|
export { createSSEHandler } from "./sse.js";
|
|
16
|
+
export { isCredentialMembershipUnavailable } from "./credential-membership-unavailable.js";
|
|
16
17
|
export { mountAuthMiddleware, autoMountAuth, registerAuthPublicPaths, getSession, getMcpOAuthBearerSession, logout, COOKIE_NAME, addSession, removeSession, getSessionEmail, getFrameworkSessionCookieValues, setFrameworkSessionCookie, setFirstRunOnboardingCookie, clearFrameworkSessionCookies, runAuthGuard, registerDesktopExchange, prepareDesktopOAuthBrowserBinding, matchesDesktopOAuthBrowserBinding, DESKTOP_OAUTH_BROWSER_BINDING_COOKIE, setDesktopExchange, setDesktopExchangeError, safeReturnPath, } from "./auth.js";
|
|
17
18
|
export { handleIdentitySso, ensureIdentityUser, getIdentityHubUrl, isIdentitySsoEnabled, isIdentitySsoBypassPath, identitySsoLoginButtonHtml, IDENTITY_SSO_BOOTSTRAP_ACTIVATE_PATH, IDENTITY_SSO_BOOTSTRAP_BINDING_COOKIE, clearIdentitySsoBootstrapBindingCookie, getIdentitySsoBootstrapBindingCookie, setIdentitySsoBootstrapBindingCookie, IDENTITY_SSO_PROVIDER_ID, IDENTITY_SSO_SCOPE, IDENTITY_SSO_DESKTOP_COMPLETE_PATH, } from "./identity-sso.js";
|
|
18
19
|
export { createBetterAuthSessionForEmail, ensureGoogleAuthIdentity, hasGoogleAuthIdentity, setBetterAuthSessionCookie, } from "./better-auth-instance.js";
|
|
@@ -1125,7 +1125,7 @@ export function getOnboardingHtml(opts = {}) {
|
|
|
1125
1125
|
authDescription: marketing.authDescription ?? marketingPresentation?.description,
|
|
1126
1126
|
learnMoreUrl: marketing.learnMoreUrl ??
|
|
1127
1127
|
(marketingSlug
|
|
1128
|
-
? `https://agent-native.com/apps/${marketingSlug}`
|
|
1128
|
+
? `https://agent-native.com/apps/${marketingSlug}?utm_source=app&utm_medium=product&utm_content=onboarding-learn-more`
|
|
1129
1129
|
: undefined),
|
|
1130
1130
|
}
|
|
1131
1131
|
: undefined,
|
|
@@ -54,8 +54,8 @@ export declare function createRealtimeTokenHandler(): import("h3").EventHandlerW
|
|
|
54
54
|
expiresAt?: undefined;
|
|
55
55
|
ttlSeconds?: undefined;
|
|
56
56
|
} | {
|
|
57
|
+
error?: undefined;
|
|
57
58
|
token: string;
|
|
58
59
|
expiresAt: string;
|
|
59
60
|
ttlSeconds: number;
|
|
60
|
-
error?: undefined;
|
|
61
61
|
}>>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { defineEventHandler, getHeader, getMethod, readRawBody, setResponseHeader, setResponseStatus, } from "h3";
|
|
2
2
|
import { getMcpOAuthBearerSession, getSession, } from "./auth.js";
|
|
3
|
+
import { isCredentialMembershipUnavailable, respondCredentialMembershipUnavailable, } from "./credential-membership-unavailable.js";
|
|
3
4
|
import { getAppUrl } from "./google-oauth.js";
|
|
4
5
|
import { RECAP_IMAGE_CONTENT_TYPE, RECAP_IMAGE_MAX_BYTES, getRecapImage, isValidRecapImageToken, saveRecapImage, } from "./recap-image-store.js";
|
|
5
6
|
const PNG_MAGIC = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
|
|
@@ -50,6 +51,9 @@ async function readPngFromRequest(event) {
|
|
|
50
51
|
}
|
|
51
52
|
async function handleUpload(event) {
|
|
52
53
|
const session = await resolveUploadSession(event);
|
|
54
|
+
if (!session?.email && isCredentialMembershipUnavailable(event)) {
|
|
55
|
+
return respondCredentialMembershipUnavailable(event);
|
|
56
|
+
}
|
|
53
57
|
if (!session?.email) {
|
|
54
58
|
setResponseStatus(event, 401);
|
|
55
59
|
return { error: "Authentication required" };
|
|
@@ -137,6 +137,10 @@ const FRAMEWORK_SCHEMA_ENSURES = [
|
|
|
137
137
|
"IdentityLinks",
|
|
138
138
|
() => import("../integrations/identity-links-store.js").then((m) => m.ensureTable()),
|
|
139
139
|
],
|
|
140
|
+
[
|
|
141
|
+
"IdentityRetiredEmails",
|
|
142
|
+
() => import("../identity/retired-emails.js").then((m) => m.ensureTable()),
|
|
143
|
+
],
|
|
140
144
|
[
|
|
141
145
|
"IdentitySso",
|
|
142
146
|
() => import("./identity-sso-store.js").then((m) => m.ensureTable()),
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
export declare const AGENT_NATIVE_DOCS_ORIGIN = "https://www.agent-native.com";
|
|
2
2
|
export type DocsUrlOptions = {
|
|
3
3
|
hash?: string;
|
|
4
|
+
source?: string;
|
|
4
5
|
medium?: string;
|
|
5
|
-
|
|
6
|
+
/** Defaults to "docs" once any UTM option is set; `null` leaves it off. */
|
|
7
|
+
campaign?: string | null;
|
|
6
8
|
content?: string | null;
|
|
7
9
|
};
|
|
8
10
|
export declare function docsUrl(slug: string, options?: DocsUrlOptions): string;
|
package/dist/shared/docs-url.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
export const AGENT_NATIVE_DOCS_ORIGIN = "https://www.agent-native.com";
|
|
2
2
|
function applyDocsUtm(params, options) {
|
|
3
|
-
const wantsUtm = options.
|
|
3
|
+
const wantsUtm = options.source != null ||
|
|
4
|
+
options.medium != null ||
|
|
4
5
|
options.campaign != null ||
|
|
5
6
|
options.content != null;
|
|
6
7
|
if (!wantsUtm)
|
|
7
8
|
return;
|
|
8
|
-
params.set("utm_source", "agent-native");
|
|
9
|
+
params.set("utm_source", options.source ?? "agent-native");
|
|
9
10
|
params.set("utm_medium", options.medium ?? "product");
|
|
10
|
-
|
|
11
|
+
const campaign = options.campaign === undefined ? "docs" : options.campaign;
|
|
12
|
+
if (campaign)
|
|
13
|
+
params.set("utm_campaign", campaign);
|
|
11
14
|
if (options.content)
|
|
12
15
|
params.set("utm_content", options.content);
|
|
13
16
|
}
|
|
@@ -33,6 +33,14 @@ export interface EnvironmentBadgeTargets {
|
|
|
33
33
|
productionHost: string;
|
|
34
34
|
}
|
|
35
35
|
export declare function resolveEnvironmentTargets(hostname: string | undefined): EnvironmentBadgeTargets | null;
|
|
36
|
+
/**
|
|
37
|
+
* Moves an endpoint on a production lane host to that host's beta lane when
|
|
38
|
+
* the calling app is itself served from beta, so beta apps report to beta
|
|
39
|
+
* Analytics. Both lanes share one database: this picks which build ingests an
|
|
40
|
+
* event, not where the event is stored. Endpoints on any other host are an
|
|
41
|
+
* explicit choice and pass through unchanged.
|
|
42
|
+
*/
|
|
43
|
+
export declare function resolveLaneEndpoint(endpoint: string, appHostname: string | undefined): string;
|
|
36
44
|
export declare function buildEnvironmentUrl(sourceHref: string, targetHost: string): string | null;
|
|
37
45
|
export declare function buildAutomaticBetaRedirectUrl(sourceHref: string, betaHost: string): string | null;
|
|
38
46
|
export declare function buildEnvironmentOptOutUrl(sourceHref: string, targetHost: string, now?: number): string | null;
|