@agent-native/core 0.203.2-nightly-20261007213109 → 0.204.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/dist/a2a/handlers.js +57 -1
- package/dist/a2a/server.js +37 -1
- package/dist/action.d.ts +1 -0
- package/dist/action.js +26 -3
- package/dist/agent/contain-principal.d.ts +9 -0
- package/dist/agent/contain-principal.js +53 -0
- package/dist/agent/production-agent.js +32 -2
- package/dist/agent/run-manager.js +14 -2
- package/dist/agent/run-store.js +66 -12
- package/dist/audit/actions/export-audit-events.d.ts +1 -1
- package/dist/audit/actions/export-audit-events.js +2 -2
- package/dist/audit/actions/export-audit-ocsf.d.ts +11 -0
- package/dist/audit/actions/export-audit-ocsf.js +183 -0
- package/dist/audit/actions/list-audit-events.d.ts +1 -1
- package/dist/audit/actions/list-audit-events.js +3 -3
- package/dist/audit/config.d.ts +1 -1
- package/dist/audit/config.js +5 -1
- package/dist/audit/index.d.ts +1 -0
- package/dist/audit/index.js +1 -0
- package/dist/audit/ocsf.d.ts +70 -0
- package/dist/audit/ocsf.js +228 -0
- package/dist/audit/record.js +1 -1
- package/dist/audit/store.js +9 -2
- package/dist/audit/types.d.ts +12 -1
- package/dist/cli/agent.js +2 -11
- package/dist/cli/connect.js +7 -2
- package/dist/client/use-db-sync.d.ts +13 -0
- package/dist/client/use-db-sync.js +22 -1
- package/dist/coding-tools/run-code.js +22 -1
- package/dist/coding-tools/sandbox/local-child-process-adapter.js +20 -0
- package/dist/collab/client.js +36 -3
- package/dist/collab/routes.d.ts +9 -4
- package/dist/collab/routes.js +15 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/deploy/build.js +15 -8
- package/dist/deploy/workspace-deploy.js +88 -13
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/file-upload/builder.js +18 -4
- package/dist/framework-tools.js +3 -0
- package/dist/identity/rekey.js +6 -0
- package/dist/jobs/actions/manage-recurring-job.d.ts +1 -1
- package/dist/localization/core-messages/supplemental/ar-SA.js +15 -0
- package/dist/localization/core-messages/supplemental/de-DE.js +15 -0
- package/dist/localization/core-messages/supplemental/en-US.js +15 -0
- package/dist/localization/core-messages/supplemental/es-ES.js +15 -0
- package/dist/localization/core-messages/supplemental/fr-FR.js +15 -0
- package/dist/localization/core-messages/supplemental/hi-IN.js +15 -0
- package/dist/localization/core-messages/supplemental/ja-JP.js +15 -0
- package/dist/localization/core-messages/supplemental/ko-KR.js +15 -0
- package/dist/localization/core-messages/supplemental/pt-BR.js +15 -0
- package/dist/localization/core-messages/supplemental/zh-CN.js +15 -0
- package/dist/localization/core-messages/supplemental/zh-TW.js +15 -0
- package/dist/localization/mcp-settings-messages.d.ts +15 -0
- package/dist/mcp/actions/create-org-service-token.d.ts +8 -0
- package/dist/mcp/actions/create-org-service-token.js +93 -4
- package/dist/mcp/actions/list-org-service-tokens.d.ts +2 -0
- package/dist/mcp/actions/list-org-service-tokens.js +36 -8
- package/dist/mcp/actions/service-principal-input.d.ts +48 -0
- package/dist/mcp/actions/service-principal-input.js +102 -0
- package/dist/mcp/actions/set-service-principal-lifecycle.d.ts +15 -0
- package/dist/mcp/actions/set-service-principal-lifecycle.js +150 -0
- package/dist/mcp/actions/set-service-principal-policy.d.ts +14 -0
- package/dist/mcp/actions/set-service-principal-policy.js +105 -0
- package/dist/mcp/build-server.js +117 -13
- package/dist/mcp/connect-route.js +116 -4
- package/dist/mcp/connect-store.d.ts +7 -0
- package/dist/mcp/connect-store.js +21 -14
- package/dist/mcp-client/oauth-routes.js +2 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +7 -7
- package/dist/org/index.d.ts +2 -2
- package/dist/org/service-principal-guard.d.ts +64 -0
- package/dist/org/service-principal-guard.js +140 -0
- package/dist/org/service-principal-policy.d.ts +104 -0
- package/dist/org/service-principal-policy.js +272 -0
- package/dist/provider-api/actions/custom-provider-registration.d.ts +5 -5
- package/dist/provider-api/actions/provider-api.d.ts +13 -13
- package/dist/scripts/dev/index.js +2 -8
- package/dist/scripts/parse-args.d.ts +7 -0
- package/dist/scripts/parse-args.js +76 -0
- package/dist/scripts/utils.d.ts +1 -1
- package/dist/scripts/utils.js +1 -1
- package/dist/secrets/routes.d.ts +11 -11
- package/dist/server/action-discovery.js +16 -42
- package/dist/server/agent-chat/script-entries.js +3 -8
- package/dist/server/agent-chat-plugin.js +8 -42
- package/dist/server/agent-engine-default-model-route.d.ts +2 -2
- package/dist/server/agent-engine-ollama-models-route.d.ts +1 -1
- package/dist/server/auth.js +5 -2
- package/dist/server/core-routes-plugin.js +25 -6
- package/dist/server/google-oauth.js +8 -4
- package/dist/server/identity-sso.js +2 -1
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +1 -0
- package/dist/server/poll.d.ts +9 -0
- package/dist/server/poll.js +86 -7
- package/dist/server/query-echo-safe-redirect.d.ts +10 -0
- package/dist/server/query-echo-safe-redirect.js +102 -0
- package/dist/server/release-schema.js +4 -0
- package/dist/server/workspace-provider-oauth.js +2 -1
- package/dist/templates/chat/.agents/skills/actions/references/action-fields.md +1 -0
- package/dist/templates/default/.agents/skills/actions/references/action-fields.md +1 -0
- package/dist/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +24 -25
- package/dist/templates/factory/.agents/skills/review-latest-feedback/references/slack-reaction-replies.md +33 -0
- package/dist/templates/headless/.agents/skills/actions/references/action-fields.md +1 -0
- package/dist/templates/workspace-core/.agents/skills/actions/references/action-fields.md +1 -0
- package/dist/triggers/actions/manage-automation.d.ts +7 -7
- package/dist/vite/client.js +1 -0
- package/docs/content/audit-log.mdx +15 -0
- package/docs/content/external-agents-catalog.mdx +19 -0
- package/docs/content/http-api.mdx +21 -2
- package/docs/content/pr-visual-recap.mdx +3 -1
- package/docs/content/template-clips-embed.mdx +1 -1
- package/docs/content/turn-into-app.mdx +118 -0
- package/package.json +4 -4
- package/src/templates/chat/.agents/skills/actions/references/action-fields.md +1 -0
- package/src/templates/default/.agents/skills/actions/references/action-fields.md +1 -0
- package/src/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +24 -25
- package/src/templates/factory/.agents/skills/review-latest-feedback/references/slack-reaction-replies.md +33 -0
- package/src/templates/headless/.agents/skills/actions/references/action-fields.md +1 -0
- package/src/templates/workspace-core/.agents/skills/actions/references/action-fields.md +1 -0
package/dist/cli/connect.js
CHANGED
|
@@ -1695,7 +1695,7 @@ export async function runServiceTokenMint(parsed, deps = {}) {
|
|
|
1695
1695
|
const serviceName = parsed.serviceToken?.trim() ?? "";
|
|
1696
1696
|
if (!parsed.url) {
|
|
1697
1697
|
logErr(" --service-token requires the app URL.");
|
|
1698
|
-
logErr(" Usage: npx @agent-native/core@latest connect <url> --service-token <name> [--ttl-days <1-
|
|
1698
|
+
logErr(" Usage: npx @agent-native/core@latest connect <url> --service-token <name> [--ttl-days <1-3650>]");
|
|
1699
1699
|
return false;
|
|
1700
1700
|
}
|
|
1701
1701
|
if (!serviceName) {
|
|
@@ -1790,7 +1790,7 @@ Usage:
|
|
|
1790
1790
|
No-browser fallback. Skip the device flow and write the entry with
|
|
1791
1791
|
the supplied token (get it from the app's Connect page).
|
|
1792
1792
|
|
|
1793
|
-
npx @agent-native/core@latest connect <url> --service-token <name> [--ttl-days <1-
|
|
1793
|
+
npx @agent-native/core@latest connect <url> --service-token <name> [--ttl-days <1-3650>]
|
|
1794
1794
|
Mint an ORG service token for CI (e.g. the PLAN_RECAP_TOKEN secret for
|
|
1795
1795
|
PR Visual Recap). Authenticates you via the browser device flow, then
|
|
1796
1796
|
mints a token owned by your ORGANIZATION — it keeps working if you
|
|
@@ -1831,6 +1831,11 @@ export async function runConnect(args, deps = {}) {
|
|
|
1831
1831
|
return;
|
|
1832
1832
|
}
|
|
1833
1833
|
const parsed = parseConnectArgs(args);
|
|
1834
|
+
if (parsed.ttlDays !== undefined && parsed.serviceToken === undefined) {
|
|
1835
|
+
logErr(" --ttl-days is only supported with --service-token. Set a personal token lifetime on the app's Connect page.");
|
|
1836
|
+
process.exitCode = 1;
|
|
1837
|
+
return;
|
|
1838
|
+
}
|
|
1834
1839
|
if (parsed.mode) {
|
|
1835
1840
|
let ok;
|
|
1836
1841
|
if (parsed.mode === "reconnect" || parsed.mode === "reauth") {
|
|
@@ -21,6 +21,19 @@ export declare function registerCollabActivityResource(resource: {
|
|
|
21
21
|
resourceType: string;
|
|
22
22
|
resourceId: string;
|
|
23
23
|
}): () => void;
|
|
24
|
+
type CollabActivityEvent = {
|
|
25
|
+
source?: string;
|
|
26
|
+
resourceType?: string;
|
|
27
|
+
resourceId?: string;
|
|
28
|
+
requestSource?: string;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* For a collab connection's own ~12 s poll. A viewer on a screen nobody else
|
|
32
|
+
* is on has no presence to notice, but that poll still carries the design's
|
|
33
|
+
* resource-scoped events from the other screens, and the shared transport is
|
|
34
|
+
* asleep on the 1-5 minute idle cadence until something wakes it.
|
|
35
|
+
*/
|
|
36
|
+
export declare function noteCollabPollActivity(events: readonly CollabActivityEvent[]): void;
|
|
24
37
|
/**
|
|
25
38
|
* Declare that another person is working on the same resource as this tab.
|
|
26
39
|
* While any lease is held, the shared transport polls every 2.5 s whenever no
|
|
@@ -72,11 +72,20 @@ function collabBoostWanted() {
|
|
|
72
72
|
}
|
|
73
73
|
return ((collabBoostLeases > 0 && collabBoostFresh()) || hasRecentResourceActivity);
|
|
74
74
|
}
|
|
75
|
+
// The server mirrors every saved file into its Yjs doc under the "agent"
|
|
76
|
+
// source, so a lone editor's own save comes back as a collab event that is not
|
|
77
|
+
// from anyone else. Only a Yjs update a browser tab posted names its sender.
|
|
78
|
+
function isHumanCollabUpdate(event) {
|
|
79
|
+
return (event.source === "collab" &&
|
|
80
|
+
typeof event.requestSource === "string" &&
|
|
81
|
+
event.requestSource !== "" &&
|
|
82
|
+
event.requestSource !== "agent");
|
|
83
|
+
}
|
|
75
84
|
function noteCollaboratorActivity(events) {
|
|
76
85
|
const ownSource = getBrowserTabId();
|
|
77
86
|
const until = Date.now() + COLLAB_ACTIVITY_WINDOW_MS;
|
|
78
87
|
for (const event of events) {
|
|
79
|
-
if (event.source !== "action" ||
|
|
88
|
+
if ((event.source !== "action" && !isHumanCollabUpdate(event)) ||
|
|
80
89
|
typeof event.resourceType !== "string" ||
|
|
81
90
|
event.resourceType === "" ||
|
|
82
91
|
typeof event.resourceId !== "string" ||
|
|
@@ -89,6 +98,18 @@ function noteCollaboratorActivity(events) {
|
|
|
89
98
|
}
|
|
90
99
|
}
|
|
91
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* For a collab connection's own ~12 s poll. A viewer on a screen nobody else
|
|
103
|
+
* is on has no presence to notice, but that poll still carries the design's
|
|
104
|
+
* resource-scoped events from the other screens, and the shared transport is
|
|
105
|
+
* asleep on the 1-5 minute idle cadence until something wakes it.
|
|
106
|
+
*/
|
|
107
|
+
export function noteCollabPollActivity(events) {
|
|
108
|
+
const wasWanted = collabBoostWanted();
|
|
109
|
+
noteCollaboratorActivity(events);
|
|
110
|
+
if (!wasWanted && collabBoostWanted())
|
|
111
|
+
notifyCollabBoostChange();
|
|
112
|
+
}
|
|
92
113
|
function collabBoostFresh() {
|
|
93
114
|
return Date.now() - collabBoostActivityAt <= COLLAB_BOOST_IDLE_CEILING_MS;
|
|
94
115
|
}
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
import crypto from "node:crypto";
|
|
32
32
|
import http from "node:http";
|
|
33
33
|
import { createRunner, RunError } from "run";
|
|
34
|
+
import { parseServiceIdentityEmail } from "../org/service-identity.js";
|
|
34
35
|
import { getRequestContext, getRequestRunContext, getRequestUserEmail, runWithRequestContext, } from "../server/request-context.js";
|
|
35
36
|
import { failExpiredSandboxExecution, getSandboxExecutionForOwner, } from "./sandbox/executions-store.js";
|
|
36
37
|
import { BACKGROUND_DEFAULT_TIMEOUT_MS, BACKGROUND_MAX_TIMEOUT_MS, SANDBOX_EXECUTION_REDRIVE_AFTER_MS, driveSandboxExecution, enqueueSandboxExecution, getSandboxAdapter, isQueuedSandboxAdapter, registerSandboxExecutionRunner, resolveExecutionSandboxAdapter, } from "./sandbox/index.js";
|
|
@@ -501,8 +502,22 @@ function createBridgeInvoker(options) {
|
|
|
501
502
|
if (callBudget)
|
|
502
503
|
callBudget.count += 1;
|
|
503
504
|
usedTools.add(toolName);
|
|
505
|
+
let servicePrincipalRefusedError;
|
|
504
506
|
try {
|
|
505
|
-
const
|
|
507
|
+
const context = options.context;
|
|
508
|
+
if (context && parseServiceIdentityEmail(context.userEmail)) {
|
|
509
|
+
const guard = await import("../org/service-principal-guard.js");
|
|
510
|
+
servicePrincipalRefusedError = guard.ServicePrincipalRefusedError;
|
|
511
|
+
await guard.enforceServicePrincipalActionGrant({
|
|
512
|
+
email: context.userEmail,
|
|
513
|
+
orgId: context.orgId,
|
|
514
|
+
actionName: toolName,
|
|
515
|
+
caller: context.caller,
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
// Name the child, not `run-code`: a service principal's grant is checked
|
|
519
|
+
// against the action actually being run.
|
|
520
|
+
const run = () => entry.run(childArgs, options.context && { ...options.context, actionName: toolName });
|
|
506
521
|
const result = options.context?.credentialScope === "org"
|
|
507
522
|
? await runWithRequestContext({
|
|
508
523
|
...getRequestContext(),
|
|
@@ -516,6 +531,12 @@ function createBridgeInvoker(options) {
|
|
|
516
531
|
return formatBridgeResult(result);
|
|
517
532
|
}
|
|
518
533
|
catch (error) {
|
|
534
|
+
if (error instanceof BridgeInvocationError)
|
|
535
|
+
throw error;
|
|
536
|
+
if (servicePrincipalRefusedError &&
|
|
537
|
+
error instanceof servicePrincipalRefusedError) {
|
|
538
|
+
throw new BridgeInvocationError(error.statusCode, error.message);
|
|
539
|
+
}
|
|
519
540
|
throw new BridgeInvocationError(500, error instanceof Error ? error.message : String(error));
|
|
520
541
|
}
|
|
521
542
|
};
|
|
@@ -21,6 +21,7 @@ function sandboxWriteAllowPaths(tmpDir) {
|
|
|
21
21
|
return [...paths];
|
|
22
22
|
}
|
|
23
23
|
let cachedPermissionFlag;
|
|
24
|
+
let cachedNetPermissionSupport;
|
|
24
25
|
function resolvePermissionFlag() {
|
|
25
26
|
if (cachedPermissionFlag !== undefined)
|
|
26
27
|
return cachedPermissionFlag;
|
|
@@ -42,6 +43,19 @@ function resolvePermissionFlag() {
|
|
|
42
43
|
cachedPermissionFlag = null;
|
|
43
44
|
return null;
|
|
44
45
|
}
|
|
46
|
+
function supportsNetPermissionFlag(permissionFlag) {
|
|
47
|
+
if (cachedNetPermissionSupport !== undefined) {
|
|
48
|
+
return cachedNetPermissionSupport;
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
const probe = spawnSync(process.execPath, [permissionFlag, "--allow-net", "-e", "process.exit(0)"], { timeout: 10_000, stdio: "ignore" });
|
|
52
|
+
cachedNetPermissionSupport = probe.status === 0;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
cachedNetPermissionSupport = false;
|
|
56
|
+
}
|
|
57
|
+
return cachedNetPermissionSupport;
|
|
58
|
+
}
|
|
45
59
|
export class LocalChildProcessAdapter {
|
|
46
60
|
id = "local-child-process";
|
|
47
61
|
async run(request) {
|
|
@@ -60,6 +74,12 @@ export class LocalChildProcessAdapter {
|
|
|
60
74
|
const nodeArgs = permissionFlag
|
|
61
75
|
? [
|
|
62
76
|
permissionFlag,
|
|
77
|
+
// Node 25+ also denies network under --permission. The sandbox
|
|
78
|
+
// bridge uses loopback HTTP, and direct outbound requests are
|
|
79
|
+
// already part of its unauthenticated execution contract.
|
|
80
|
+
...(supportsNetPermissionFlag(permissionFlag)
|
|
81
|
+
? ["--allow-net"]
|
|
82
|
+
: []),
|
|
63
83
|
...sandboxReadAllowPaths(tmpDir).map((allowedPath) => `--allow-fs-read=${allowedPath}`),
|
|
64
84
|
...sandboxWriteAllowPaths(tmpDir).map((allowedPath) => `--allow-fs-write=${allowedPath}`),
|
|
65
85
|
tmpFile,
|
package/dist/collab/client.js
CHANGED
|
@@ -3,7 +3,7 @@ import { Awareness } from "y-protocols/awareness";
|
|
|
3
3
|
import * as Y from "yjs";
|
|
4
4
|
import { agentNativePath } from "../client/api-path.js";
|
|
5
5
|
import { useAvatarUrl } from "../client/use-avatar.js";
|
|
6
|
-
import { acquireCollabPollBoost, registerCollabActivityResource, subscribeSyncEvents, } from "../client/use-db-sync.js";
|
|
6
|
+
import { acquireCollabPollBoost, noteCollabPollActivity, registerCollabActivityResource, subscribeSyncEvents, } from "../client/use-db-sync.js";
|
|
7
7
|
import { REALTIME_CAP_NO_AWARENESS, REALTIME_CAP_POLL_LIVE, } from "../realtime-protocol.js";
|
|
8
8
|
import { dedupeCollabUsersByEmail } from "./types.js";
|
|
9
9
|
export { dedupeCollabUsersByEmail, emailToColor, emailToName, } from "./types.js";
|
|
@@ -151,6 +151,8 @@ class CollabDocConnection {
|
|
|
151
151
|
pollCycleCount = 0;
|
|
152
152
|
pollVersion = 0;
|
|
153
153
|
lastPolledVersion = 0;
|
|
154
|
+
pollCursor = null;
|
|
155
|
+
pollActivityBaselineReady = false;
|
|
154
156
|
stateVectorFetch = null;
|
|
155
157
|
stateVectorAbortControllers = new Set();
|
|
156
158
|
sseActive = false;
|
|
@@ -415,7 +417,9 @@ class CollabDocConnection {
|
|
|
415
417
|
});
|
|
416
418
|
}
|
|
417
419
|
fetchInitialState() {
|
|
418
|
-
fetch(`${this.baseUrl}/${this.docId}/state
|
|
420
|
+
fetch(`${this.baseUrl}/${this.docId}/state`, {
|
|
421
|
+
headers: { "X-Agent-Native-Poll-Baseline": "1" },
|
|
422
|
+
}).then(async (res) => {
|
|
419
423
|
if (this.disposed)
|
|
420
424
|
return;
|
|
421
425
|
if (res.status === 404 || res.status === 403) {
|
|
@@ -433,6 +437,17 @@ class CollabDocConnection {
|
|
|
433
437
|
this.markInitializationFailed("invalid-payload");
|
|
434
438
|
return;
|
|
435
439
|
}
|
|
440
|
+
const baseline = data.activityBaseline;
|
|
441
|
+
if (baseline?.status === "ready" &&
|
|
442
|
+
Number.isSafeInteger(baseline.version) &&
|
|
443
|
+
baseline.version >= 0 &&
|
|
444
|
+
typeof baseline.cursor === "string" &&
|
|
445
|
+
baseline.cursor.length > 0) {
|
|
446
|
+
this.pollVersion = baseline.version;
|
|
447
|
+
this.lastPolledVersion = baseline.version;
|
|
448
|
+
this.pollCursor = baseline.cursor;
|
|
449
|
+
this.pollActivityBaselineReady = true;
|
|
450
|
+
}
|
|
436
451
|
if (data.state) {
|
|
437
452
|
try {
|
|
438
453
|
const binary = base64ToUint8Array(data.state);
|
|
@@ -807,12 +822,16 @@ class CollabDocConnection {
|
|
|
807
822
|
return;
|
|
808
823
|
this.flushPendingUpdates();
|
|
809
824
|
try {
|
|
810
|
-
const
|
|
825
|
+
const pollUrl = this.pollCursor
|
|
826
|
+
? `/_agent-native/poll?cursor=${encodeURIComponent(this.pollCursor)}`
|
|
827
|
+
: `/_agent-native/poll?since=${this.pollVersion}`;
|
|
828
|
+
const res = await fetch(agentNativePath(pollUrl));
|
|
811
829
|
if (!res.ok)
|
|
812
830
|
throw new Error("HTTP " + res.status);
|
|
813
831
|
const data = await res.json();
|
|
814
832
|
if (!this.syncActive || this.disposed)
|
|
815
833
|
return;
|
|
834
|
+
const hadActivityBaseline = this.pollActivityBaselineReady;
|
|
816
835
|
const { version, events } = data;
|
|
817
836
|
const versionGap = version - this.lastPolledVersion;
|
|
818
837
|
const hadGap = versionGap > POLL_RING_BUFFER_SIZE;
|
|
@@ -832,6 +851,20 @@ class CollabDocConnection {
|
|
|
832
851
|
}
|
|
833
852
|
}
|
|
834
853
|
}
|
|
854
|
+
if (hadActivityBaseline) {
|
|
855
|
+
noteCollabPollActivity(events);
|
|
856
|
+
}
|
|
857
|
+
else {
|
|
858
|
+
const reconciliation = await this.fetchStateVector();
|
|
859
|
+
if (reconciliation.status !== "synced") {
|
|
860
|
+
throw reconciliation.status === "failed"
|
|
861
|
+
? reconciliation.error
|
|
862
|
+
: new Error("Could not reconcile state without an activity baseline");
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
this.pollActivityBaselineReady = true;
|
|
866
|
+
if (typeof data.cursor === "string")
|
|
867
|
+
this.pollCursor = data.cursor;
|
|
835
868
|
this.pollVersion = version;
|
|
836
869
|
this.lastPolledVersion = version;
|
|
837
870
|
this.pollCycleCount++;
|
package/dist/collab/routes.d.ts
CHANGED
|
@@ -1,23 +1,28 @@
|
|
|
1
1
|
export declare const getCollabState: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
2
2
|
error: string;
|
|
3
|
-
docId?: undefined;
|
|
4
|
-
state?: undefined;
|
|
5
3
|
} | {
|
|
6
4
|
error?: undefined;
|
|
7
5
|
docId: string;
|
|
8
6
|
state: string;
|
|
7
|
+
activityBaseline?: {
|
|
8
|
+
status: "ready";
|
|
9
|
+
version: number;
|
|
10
|
+
cursor: string;
|
|
11
|
+
} | {
|
|
12
|
+
status: "unavailable";
|
|
13
|
+
};
|
|
9
14
|
}>>;
|
|
10
15
|
export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
11
|
-
error: string;
|
|
12
16
|
ok?: undefined;
|
|
17
|
+
error: string;
|
|
13
18
|
} | {
|
|
14
19
|
error?: undefined;
|
|
15
20
|
ok: boolean;
|
|
16
21
|
}>>;
|
|
17
22
|
export declare const postCollabText: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
18
|
-
text?: undefined;
|
|
19
23
|
ok?: undefined;
|
|
20
24
|
error: string;
|
|
25
|
+
text?: undefined;
|
|
21
26
|
} | {
|
|
22
27
|
error?: undefined;
|
|
23
28
|
ok: boolean;
|
package/dist/collab/routes.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineEventHandler, setResponseStatus, setResponseHeader, getRouterParam, getQuery, } from "h3";
|
|
1
|
+
import { defineEventHandler, setResponseStatus, setResponseHeader, getRouterParam, getQuery, getRequestHeader, } from "h3";
|
|
2
2
|
import { readBody } from "../server/h3-helpers.js";
|
|
3
3
|
import { uint8ArrayToBase64, base64ToUint8Array } from "./storage.js";
|
|
4
4
|
import * as manager from "./ydoc-manager.js";
|
|
@@ -25,6 +25,19 @@ export const getCollabState = defineEventHandler(async (event) => {
|
|
|
25
25
|
}
|
|
26
26
|
const query = getQuery(event);
|
|
27
27
|
const encodedStateVector = typeof query.stateVector === "string" ? query.stateVector : null;
|
|
28
|
+
let activityBaseline;
|
|
29
|
+
if (getRequestHeader(event, "x-agent-native-poll-baseline") === "1") {
|
|
30
|
+
try {
|
|
31
|
+
const { getCurrentPollBaseline } = await import("../server/poll.js");
|
|
32
|
+
activityBaseline = {
|
|
33
|
+
status: "ready",
|
|
34
|
+
...(await getCurrentPollBaseline()),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
activityBaseline = { status: "unavailable" };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
28
41
|
let state;
|
|
29
42
|
if (encodedStateVector) {
|
|
30
43
|
try {
|
|
@@ -41,6 +54,7 @@ export const getCollabState = defineEventHandler(async (event) => {
|
|
|
41
54
|
return {
|
|
42
55
|
docId,
|
|
43
56
|
state: uint8ArrayToBase64(state),
|
|
57
|
+
...(activityBaseline ? { activityBaseline } : {}),
|
|
44
58
|
};
|
|
45
59
|
});
|
|
46
60
|
export const postCollabUpdate = defineEventHandler(async (event) => {
|
package/dist/deploy/build.js
CHANGED
|
@@ -984,6 +984,18 @@ export function generateWorkerEntry(routes, pluginPaths, defaultPluginStems = []
|
|
|
984
984
|
{ status: 405, headers: { "Content-Type": "application/json" } }
|
|
985
985
|
);
|
|
986
986
|
}
|
|
987
|
+
const actionSession = ${varName}.requiresAuth === true
|
|
988
|
+
? await getGeneratedSession(event)
|
|
989
|
+
: undefined;
|
|
990
|
+
const actionContext = uiActionContext ?? (actionSession?.email
|
|
991
|
+
? { userEmail: actionSession.email, orgId: actionSession.orgId ?? null }
|
|
992
|
+
: undefined);
|
|
993
|
+
if (${varName}.requiresAuth === true && !actionContext?.userEmail) {
|
|
994
|
+
return new Response(
|
|
995
|
+
JSON.stringify({ error: "Unauthorized" }),
|
|
996
|
+
{ status: 401, headers: { "Content-Type": "application/json" } },
|
|
997
|
+
);
|
|
998
|
+
}
|
|
987
999
|
const params = ${a.method === "get" ? "parseActionSearchParams(event.url.searchParams)" : "(await readBody(event)) ?? {}"};
|
|
988
1000
|
try {
|
|
989
1001
|
const caller =
|
|
@@ -994,16 +1006,11 @@ export function generateWorkerEntry(routes, pluginPaths, defaultPluginStems = []
|
|
|
994
1006
|
caller,
|
|
995
1007
|
requestHeaders: event.req.headers,
|
|
996
1008
|
actionName: ${JSON.stringify(a.name)},
|
|
997
|
-
...(
|
|
998
|
-
? {
|
|
999
|
-
userEmail: uiActionContext.userEmail,
|
|
1000
|
-
orgId: uiActionContext.orgId ?? null,
|
|
1001
|
-
}
|
|
1002
|
-
: {}),
|
|
1009
|
+
...(actionContext ?? {}),
|
|
1003
1010
|
};
|
|
1004
1011
|
const runAction = () => ${varName}.run(params, actionRunContext);
|
|
1005
|
-
const result =
|
|
1006
|
-
? await runWithGeneratedRequestContext(
|
|
1012
|
+
const result = actionContext
|
|
1013
|
+
? await runWithGeneratedRequestContext(actionContext, runAction)
|
|
1007
1014
|
: await runAction();
|
|
1008
1015
|
if (typeof result === "string") { try { return JSON.parse(result); } catch { return result; } }
|
|
1009
1016
|
return result;
|
|
@@ -64,19 +64,97 @@ function builtinAgentsEnvSnippet() {
|
|
|
64
64
|
`;
|
|
65
65
|
}
|
|
66
66
|
function workspaceDirectoryEnvSnippet(workspaceApps) {
|
|
67
|
-
const
|
|
68
|
-
|
|
67
|
+
const configuredOrgDirectoryUrl = getAppConfig().workspace.orgDirectoryUrl?.trim();
|
|
68
|
+
const dispatchApp = workspaceApps.find((app) => app.isDispatch);
|
|
69
|
+
if (!configuredOrgDirectoryUrl && !dispatchApp) {
|
|
69
70
|
return builtinAgentsEnvSnippet();
|
|
70
71
|
}
|
|
72
|
+
const runtimeDirectoryResolver = dispatchApp
|
|
73
|
+
? `
|
|
74
|
+
function logInvalidDirectoryBase() {
|
|
75
|
+
const logKey = Symbol.for("agent-native.workspace.invalid-directory-base");
|
|
76
|
+
if (globalThis[logKey]) return;
|
|
77
|
+
globalThis[logKey] = true;
|
|
78
|
+
console.error("[workspace] Invalid organization directory base URL");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function resolveRuntimeDirectoryUrl() {
|
|
82
|
+
const isVercelProduction =
|
|
83
|
+
processRef.env.VERCEL_ENV?.trim().toLowerCase() === "production";
|
|
84
|
+
const vercelCandidates = processRef.env.VERCEL
|
|
85
|
+
? (isVercelProduction
|
|
86
|
+
? [
|
|
87
|
+
processRef.env.VERCEL_PROJECT_PRODUCTION_URL,
|
|
88
|
+
processRef.env.VERCEL_URL,
|
|
89
|
+
processRef.env.VERCEL_BRANCH_URL,
|
|
90
|
+
]
|
|
91
|
+
: [processRef.env.VERCEL_URL, processRef.env.VERCEL_BRANCH_URL]
|
|
92
|
+
)
|
|
93
|
+
.filter(Boolean)
|
|
94
|
+
.map((candidate) =>
|
|
95
|
+
/^https?:\\/\\//i.test(candidate)
|
|
96
|
+
? candidate
|
|
97
|
+
: "https://" + candidate,
|
|
98
|
+
)
|
|
99
|
+
: [];
|
|
100
|
+
const candidates = [
|
|
101
|
+
processRef.env.APP_URL,
|
|
102
|
+
processRef.env.URL,
|
|
103
|
+
processRef.env.DEPLOY_URL,
|
|
104
|
+
processRef.env.BETTER_AUTH_URL,
|
|
105
|
+
...vercelCandidates,
|
|
106
|
+
processRef.env.WORKSPACE_GATEWAY_URL,
|
|
107
|
+
processRef.env.VITE_WORKSPACE_GATEWAY_URL,
|
|
108
|
+
].filter(Boolean);
|
|
109
|
+
let loopbackUrl;
|
|
110
|
+
for (const candidate of candidates) {
|
|
111
|
+
let baseUrl;
|
|
112
|
+
try {
|
|
113
|
+
baseUrl = new URL(candidate);
|
|
114
|
+
} catch {
|
|
115
|
+
logInvalidDirectoryBase();
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (baseUrl.protocol !== "http:" && baseUrl.protocol !== "https:") {
|
|
119
|
+
logInvalidDirectoryBase();
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
const directoryUrl = new URL(${JSON.stringify(dispatchApp.path)}, baseUrl)
|
|
123
|
+
.toString()
|
|
124
|
+
.replace(/\\/$/, "");
|
|
125
|
+
const hostname = baseUrl.hostname.toLowerCase().replace(/\\.$/, "");
|
|
126
|
+
const mappedIpv4 = hostname.match(
|
|
127
|
+
/^\\[::ffff:([0-9a-f]{1,4}):[0-9a-f]{1,4}\\]$/,
|
|
128
|
+
);
|
|
129
|
+
const isMappedIpv4Loopback =
|
|
130
|
+
mappedIpv4 !== null &&
|
|
131
|
+
(Number.parseInt(mappedIpv4[1], 16) >> 8) === 0x7f;
|
|
132
|
+
if (
|
|
133
|
+
hostname === "localhost" ||
|
|
134
|
+
hostname.endsWith(".localhost") ||
|
|
135
|
+
/^127(?:\\.\\d{1,3}){3}$/.test(hostname) ||
|
|
136
|
+
/^0(?:\\.\\d{1,3}){3}$/.test(hostname) ||
|
|
137
|
+
hostname === "[::1]" ||
|
|
138
|
+
hostname === "::1" ||
|
|
139
|
+
hostname === "[::]" ||
|
|
140
|
+
hostname === "::" ||
|
|
141
|
+
isMappedIpv4Loopback
|
|
142
|
+
) {
|
|
143
|
+
loopbackUrl ??= directoryUrl;
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
return directoryUrl;
|
|
147
|
+
}
|
|
148
|
+
return loopbackUrl ?? null;
|
|
149
|
+
}
|
|
150
|
+
`
|
|
151
|
+
: "";
|
|
71
152
|
return `${builtinAgentsEnvSnippet()}
|
|
153
|
+
${runtimeDirectoryResolver}
|
|
72
154
|
const directoryOrigin =
|
|
73
155
|
processRef.env.AGENT_NATIVE_ORG_DIRECTORY_URL ||
|
|
74
|
-
${JSON.stringify(
|
|
75
|
-
|
|
76
|
-
processRef.env.APP_URL ||
|
|
77
|
-
processRef.env.URL ||
|
|
78
|
-
processRef.env.DEPLOY_URL ||
|
|
79
|
-
processRef.env.BETTER_AUTH_URL;
|
|
156
|
+
${JSON.stringify(configuredOrgDirectoryUrl ?? null)} ||
|
|
157
|
+
${dispatchApp ? "resolveRuntimeDirectoryUrl()" : "null"};
|
|
80
158
|
if (directoryOrigin) {
|
|
81
159
|
processRef.env.AGENT_NATIVE_ORG_DIRECTORY_URL = directoryOrigin;
|
|
82
160
|
}
|
|
@@ -167,11 +245,8 @@ function buildOneApp(workspaceRoot, appsDir, app, preset, execFile, workspaceApp
|
|
|
167
245
|
const appDir = path.join(appsDir, app);
|
|
168
246
|
const workspaceAppAudience = workspaceAppAudienceForApp(workspaceApps, app);
|
|
169
247
|
const workspaceAppRouteAccess = workspaceAppRouteAccessForApp(workspaceApps, app);
|
|
170
|
-
const workspaceGatewayUrl =
|
|
171
|
-
const orgDirectoryUrl = getAppConfig().workspace.orgDirectoryUrl?.trim()
|
|
172
|
-
(workspaceApps.some((entry) => entry.isDispatch)
|
|
173
|
-
? workspaceGatewayUrl
|
|
174
|
-
: null);
|
|
248
|
+
const workspaceGatewayUrl = workspaceBaseUrl();
|
|
249
|
+
const orgDirectoryUrl = getAppConfig().workspace.orgDirectoryUrl?.trim();
|
|
175
250
|
const workspaceOAuthUrl = workspaceOAuthOrigin(workspaceGatewayUrl);
|
|
176
251
|
const frameworkRoutePrefix = workspaceFrameworkRoutePrefixEnv();
|
|
177
252
|
const env = {
|
|
@@ -12,8 +12,22 @@ function makeBody(bytes, mimeType) {
|
|
|
12
12
|
? new Blob([bytes], { type: mimeType })
|
|
13
13
|
: bytes;
|
|
14
14
|
}
|
|
15
|
+
// Builder's `/api/v1/upload` endpoint sniffs these two Content-Types as a
|
|
16
|
+
// JSON-bodied request (its legacy `{ image: "data:..." }` upload shape)
|
|
17
|
+
// instead of raw file bytes, so it responds 400 "No image specified" for any
|
|
18
|
+
// real `application/json` or `text/plain` file — even though every other
|
|
19
|
+
// mimetype in `UPLOAD_ALLOWED_MIME_PREFIXES` (csv, pdf, zip, docs, video,
|
|
20
|
+
// octet-stream, other text/* subtypes) uploads through it without issue. The
|
|
21
|
+
// signed-URL flow PUTs raw bytes straight to GCS, so it never hits that
|
|
22
|
+
// body-sniffing path.
|
|
23
|
+
function builderMisparsesContentTypeAsJsonBody(mimeType) {
|
|
24
|
+
const normalized = mimeType.toLowerCase();
|
|
25
|
+
return normalized === "application/json" || normalized === "text/plain";
|
|
26
|
+
}
|
|
15
27
|
function shouldUseSignedUrlUpload(bytes, mimeType) {
|
|
16
|
-
return (bytes.byteLength > LARGE_FILE_THRESHOLD_BYTES ||
|
|
28
|
+
return (bytes.byteLength > LARGE_FILE_THRESHOLD_BYTES ||
|
|
29
|
+
/^video\//i.test(mimeType) ||
|
|
30
|
+
builderMisparsesContentTypeAsJsonBody(mimeType));
|
|
17
31
|
}
|
|
18
32
|
function fetchWithTimeout(url, init) {
|
|
19
33
|
const controller = new AbortController();
|
|
@@ -75,10 +89,10 @@ async function withAssetAuthorization(run, held = {}) {
|
|
|
75
89
|
}
|
|
76
90
|
}
|
|
77
91
|
}
|
|
78
|
-
async function
|
|
92
|
+
async function uploadViaSignedUrl(input, held, bareMimeType, bytes) {
|
|
79
93
|
const name = input.filename ?? "upload";
|
|
80
94
|
const mb = (bytes.byteLength / (1024 * 1024)).toFixed(1);
|
|
81
|
-
console.log(`[builder-upload]
|
|
95
|
+
console.log(`[builder-upload] signed-url path: ${name} ${mb}MB ${bareMimeType}`);
|
|
82
96
|
console.log(`[builder-upload] step 1: requesting signed URL`);
|
|
83
97
|
const { uploadUrl, assetId, requiredHeaders } = await withAssetAuthorization(({ authorization, apiKey }) => requestBuilderSignedUrl(authorization, apiKey, name, bareMimeType, bytes.byteLength), held);
|
|
84
98
|
console.log(`[builder-upload] step 1 ok: assetId=${assetId}`);
|
|
@@ -228,7 +242,7 @@ export const builderFileUploadProvider = {
|
|
|
228
242
|
const bytes = data instanceof Uint8Array ? data : new Uint8Array(data);
|
|
229
243
|
const mb = (bytes.byteLength / (1024 * 1024)).toFixed(1);
|
|
230
244
|
if (shouldUseSignedUrlUpload(bytes, bareMimeType)) {
|
|
231
|
-
return
|
|
245
|
+
return uploadViaSignedUrl(input, held, bareMimeType, bytes);
|
|
232
246
|
}
|
|
233
247
|
console.log(`[builder-upload] small-file path: ${filename ?? "upload"} ${mb}MB ${bareMimeType}`);
|
|
234
248
|
const response = await withAssetAuthorization(({ authorization, apiKey }) => {
|
package/dist/framework-tools.js
CHANGED
|
@@ -165,6 +165,7 @@ export const CORE_ACTION_GROUPS = {
|
|
|
165
165
|
"list-audit-events": "audit",
|
|
166
166
|
"get-audit-event": "audit",
|
|
167
167
|
"export-audit-events": "audit",
|
|
168
|
+
"export-audit-ocsf": "audit",
|
|
168
169
|
// Observability promotion reuses `labs` until a dedicated group exists.
|
|
169
170
|
// A new FRAMEWORK_TOOL_GROUPS member is filtered at thirteen composition
|
|
170
171
|
// sites; do not add `observability` in the same change as this action.
|
|
@@ -189,6 +190,8 @@ export const CORE_ACTION_GROUPS = {
|
|
|
189
190
|
"create-org-service-token": "orgServiceTokens",
|
|
190
191
|
"list-org-service-tokens": "orgServiceTokens",
|
|
191
192
|
"revoke-org-service-token": "orgServiceTokens",
|
|
193
|
+
"set-service-principal-policy": "orgServiceTokens",
|
|
194
|
+
"set-service-principal-lifecycle": "orgServiceTokens",
|
|
192
195
|
"list-review-comments": "review",
|
|
193
196
|
"create-review-comment": "review",
|
|
194
197
|
"reply-review-comment": "review",
|
package/dist/identity/rekey.js
CHANGED
|
@@ -112,6 +112,12 @@ export const IDENTITY_REKEY_COLUMNS = [
|
|
|
112
112
|
// A missing connect-token row reads as "not revoked", so revoke, never delete.
|
|
113
113
|
{ table: "mcp_connect_tokens", column: "owner_email", offboard: "revoke" },
|
|
114
114
|
{ table: "mcp_connect_tokens", column: "created_by" },
|
|
115
|
+
// The accountable owner outlives their account: the principal stays governed and its owner reads as stale.
|
|
116
|
+
{
|
|
117
|
+
table: "service_principal_policies",
|
|
118
|
+
column: "owner_email",
|
|
119
|
+
offboard: "retain",
|
|
120
|
+
},
|
|
115
121
|
{ table: "mcp_oauth_codes", column: "owner_email", offboard: "delete" },
|
|
116
122
|
{
|
|
117
123
|
table: "mcp_oauth_codes",
|
|
@@ -11,9 +11,9 @@ declare const _default: import("../../action.js").ActionDefinition<{
|
|
|
11
11
|
enabled?: undefined;
|
|
12
12
|
nextRun?: undefined;
|
|
13
13
|
} | {
|
|
14
|
+
deleted?: undefined;
|
|
14
15
|
name: string;
|
|
15
16
|
enabled: boolean;
|
|
16
17
|
nextRun: string;
|
|
17
|
-
deleted?: undefined;
|
|
18
18
|
}>;
|
|
19
19
|
export default _default;
|
|
@@ -54,6 +54,21 @@ export const mcpConnectMessages = {
|
|
|
54
54
|
couldNotCreate: "تعذر إنشاء الرمز.",
|
|
55
55
|
networkError: "حدث خطأ في الشبكة. حاول مرة أخرى.",
|
|
56
56
|
urlTitle: "عنوان MCP الخاص بك",
|
|
57
|
+
servicePrincipals: "كيانات الخدمة",
|
|
58
|
+
principalUngoverned: "غير خاضع للحوكمة",
|
|
59
|
+
principalActive: "نشط",
|
|
60
|
+
principalSuspended: "معلّق",
|
|
61
|
+
principalRetired: "متقاعد",
|
|
62
|
+
principalUngovernedHint: "لم يتم تعيين مالك أو إذن للإجراءات.",
|
|
63
|
+
principalOwner: "المالك",
|
|
64
|
+
principalRisk: "المخاطر",
|
|
65
|
+
riskLow: "منخفضة",
|
|
66
|
+
riskMedium: "متوسطة",
|
|
67
|
+
riskHigh: "عالية",
|
|
68
|
+
suspend: "تعليق",
|
|
69
|
+
resume: "استئناف",
|
|
70
|
+
couldNotUpdatePrincipal: "تعذّر تحديث كيان الخدمة.",
|
|
71
|
+
containmentIncomplete: "تم التحديث، لكن تعذّر إيقاف بعض عمليات التشغيل أو الرموز. حاول مرة أخرى.",
|
|
57
72
|
};
|
|
58
73
|
export const mcpSettingsMessages = {
|
|
59
74
|
mcpTitle: "MCP",
|
|
@@ -54,6 +54,21 @@ export const mcpConnectMessages = {
|
|
|
54
54
|
couldNotCreate: "Token konnte nicht erstellt werden.",
|
|
55
55
|
networkError: "Netzwerkfehler. Bitte versuche es erneut.",
|
|
56
56
|
urlTitle: "Deine MCP-URL",
|
|
57
|
+
servicePrincipals: "Dienstprinzipale",
|
|
58
|
+
principalUngoverned: "Nicht verwaltet",
|
|
59
|
+
principalActive: "Aktiv",
|
|
60
|
+
principalSuspended: "Gesperrt",
|
|
61
|
+
principalRetired: "Stillgelegt",
|
|
62
|
+
principalUngovernedHint: "Kein Eigentümer und keine Aktionsfreigabe festgelegt.",
|
|
63
|
+
principalOwner: "Eigentümer",
|
|
64
|
+
principalRisk: "Risiko",
|
|
65
|
+
riskLow: "niedrig",
|
|
66
|
+
riskMedium: "mittel",
|
|
67
|
+
riskHigh: "hoch",
|
|
68
|
+
suspend: "Sperren",
|
|
69
|
+
resume: "Fortsetzen",
|
|
70
|
+
couldNotUpdatePrincipal: "Der Dienstprinzipal konnte nicht aktualisiert werden.",
|
|
71
|
+
containmentIncomplete: "Aktualisiert, aber einige Ausführungen oder Token konnten nicht gestoppt werden. Versuche es erneut.",
|
|
57
72
|
};
|
|
58
73
|
export const mcpSettingsMessages = {
|
|
59
74
|
mcpTitle: "MCP",
|