@agent-native/core 0.164.12 → 0.164.17
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/corpus/README.md +1 -1
- package/corpus/templates/analytics/netlify.toml +4 -0
- package/corpus/templates/clips/actions/request-transcript.ts +10 -6
- package/corpus/templates/content/actions/_database-source-utils.ts +25 -2
- package/corpus/templates/content/actions/_database-utils.ts +2 -0
- package/corpus/templates/content/actions/_local-folder-source.ts +186 -2
- package/corpus/templates/content/actions/connect-local-folder-source.ts +23 -8
- package/corpus/templates/content/actions/disconnect-local-folder-source.ts +3 -3
- package/corpus/templates/content/actions/resolve-local-folder-conflict.ts +2 -2
- package/corpus/templates/content/actions/sync-local-folder-source.ts +130 -12
- package/corpus/templates/content/app/components/LocalFolderLiveSync.tsx +77 -0
- package/corpus/templates/content/app/components/editor/BubbleToolbar.tsx +298 -3
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +354 -8
- package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +23 -1
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +81 -1
- package/corpus/templates/content/app/components/editor/database/sidebar.tsx +36 -0
- package/corpus/templates/content/app/components/editor/extensions/NotionExtensions.tsx +18 -4
- package/corpus/templates/content/app/components/editor/local-source-write-state.ts +52 -0
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +244 -6
- package/corpus/templates/content/app/components/sidebar/local-source-hierarchy.ts +189 -0
- package/corpus/templates/content/app/hooks/use-create-page.ts +26 -4
- package/corpus/templates/content/app/hooks/use-documents.ts +83 -0
- package/corpus/templates/content/app/i18n-data.ts +239 -0
- package/corpus/templates/content/app/lib/desktop-content-files.ts +57 -1
- package/corpus/templates/content/app/lib/local-content-source-files.ts +210 -34
- package/corpus/templates/content/app/lib/local-folder-live-sync.ts +191 -0
- package/corpus/templates/content/app/root.tsx +2 -1
- package/corpus/templates/content/app/routes/_app.local-files.tsx +103 -38
- package/corpus/templates/content/docs/product/capabilities/content.source.file-folder.md +2 -2
- package/corpus/templates/content/docs/product/features/content.feature.bring-your-local-work.md +3 -3
- package/corpus/templates/content/docs/product/roadmap.md +2 -2
- package/corpus/templates/content/docs/solutions/2026-08-18-colored-text-highlighting-shape.md +693 -0
- package/corpus/templates/content/docs/solutions/2026-08-18-content-navigation-coherence-shape.md +482 -0
- package/corpus/templates/content/package.json +1 -0
- package/corpus/templates/content/shared/api.ts +25 -0
- package/corpus/templates/content/shared/nfm.ts +14 -3
- package/corpus/templates/factory/AGENTS.md +24 -29
- package/corpus/templates/factory/actions/get-slack-feedback-context.ts +8 -28
- package/corpus/templates/factory/actions/get-triage-config.ts +1 -0
- package/corpus/templates/factory/actions/govern-agent-native-pull-request.ts +67 -59
- package/corpus/templates/factory/actions/list-triage-items.ts +6 -1
- package/corpus/templates/factory/actions/reconcile-triage-run.ts +7 -1
- package/corpus/templates/factory/actions/save-triage-config.ts +27 -7
- package/corpus/templates/factory/actions/start-builder-for-item.ts +113 -31
- package/corpus/templates/factory/actions/view-screen.ts +12 -0
- package/corpus/templates/factory/app/components/factory/FactorySettingsView.tsx +457 -0
- package/corpus/templates/factory/app/components/factory/FactoryWorkspaceActions.tsx +53 -0
- package/corpus/templates/factory/app/components/layout/Layout.tsx +2 -0
- package/corpus/templates/factory/app/components/layout/Sidebar.tsx +3 -1
- package/corpus/templates/factory/app/hooks/use-navigation-state.ts +10 -7
- package/corpus/templates/factory/app/i18n/en-US.ts +3 -0
- package/corpus/templates/factory/app/routes/agents.tsx +22 -1
- package/corpus/templates/factory/app/routes/factory-settings.tsx +34 -0
- package/corpus/templates/factory/app/routes/factory.tsx +33 -323
- package/corpus/templates/factory/server/connectors/slack.ts +62 -0
- package/corpus/templates/factory/server/db/schema.ts +1 -0
- package/corpus/templates/factory/server/lib/slack-feedback-prompt.ts +30 -0
- package/corpus/templates/factory/server/plugins/agent-chat.ts +9 -8
- package/corpus/templates/factory/server/plugins/factory-migrations.ts +7 -0
- package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +43 -25
- package/corpus/templates/factory/server/triage/ai-services-git.ts +2 -0
- package/corpus/templates/factory/server/triage/pr-monitor.ts +2 -1
- package/corpus/templates/factory/server/triage/pr-policy.ts +197 -19
- package/corpus/templates/factory/server/triage/review-skill-alignment.ts +127 -0
- package/corpus/templates/factory/server/triage/slack-client.ts +79 -6
- package/dist/app-config/auth.d.ts +4 -0
- package/dist/app-config/auth.js +7 -0
- package/dist/app-config/schema.d.ts +3 -0
- package/dist/app-config/schema.js +2 -0
- package/dist/cli/code-agent-executor.js +6 -1
- package/dist/cli/code-agent-mcp-config.d.ts +2 -2
- package/dist/cli/code-agent-mcp-config.js +34 -9
- package/dist/client/MultiTabAssistantChat.js +28 -1
- package/dist/client/org/OrgSwitcher.js +8 -4
- package/dist/client/use-chat-threads.d.ts +1 -0
- package/dist/client/use-chat-threads.js +13 -3
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/routes.d.ts +1 -1
- package/dist/mcp-client/config.d.ts +11 -0
- package/dist/mcp-client/config.js +33 -26
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +5 -5
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +2 -2
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/auth.js +17 -3
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/vite/client.js +2 -0
- package/package.json +1 -1
|
@@ -2,13 +2,18 @@ import { resolveConnectorSecret } from "../connectors/credentials.js";
|
|
|
2
2
|
import {
|
|
3
3
|
getChannelHistory as readChannelHistory,
|
|
4
4
|
addEyesReaction as writeEyesReaction,
|
|
5
|
+
authTest as readAuthTest,
|
|
6
|
+
getEyesReaction as readEyesReaction,
|
|
5
7
|
getThread as readThread,
|
|
6
8
|
getTeamInfo as readTeamInfo,
|
|
7
9
|
getUserInfo as readUserInfo,
|
|
8
10
|
postThreadReply as writeThreadReply,
|
|
9
11
|
type ChannelHistoryResult,
|
|
12
|
+
type SlackAuthTestResult,
|
|
13
|
+
type SlackMessage,
|
|
10
14
|
type SlackPostMessageResult,
|
|
11
15
|
type SlackReactionResult,
|
|
16
|
+
type SlackReactionState,
|
|
12
17
|
type SlackTeamInfo,
|
|
13
18
|
type SlackUserInfo,
|
|
14
19
|
type SlackTokenResolver,
|
|
@@ -40,14 +45,38 @@ function createTokenResolver({
|
|
|
40
45
|
|
|
41
46
|
export function createSlackReader(identity: SlackReaderIdentity) {
|
|
42
47
|
const tokenResolver = createTokenResolver(identity);
|
|
48
|
+
const identities = new Map<Workspace, SlackAuthTestResult>();
|
|
49
|
+
|
|
50
|
+
async function getAgentNativeIdentity(
|
|
51
|
+
workspace: Workspace,
|
|
52
|
+
): Promise<SlackAuthTestResult> {
|
|
53
|
+
const existing = identities.get(workspace);
|
|
54
|
+
if (existing) return existing;
|
|
55
|
+
const auth = await readAuthTest(workspace, tokenResolver);
|
|
56
|
+
const userName = auth.userName.trim().replace(/^@/, "").toLowerCase();
|
|
57
|
+
if (userName !== "agent-native") {
|
|
58
|
+
throw new Error(
|
|
59
|
+
`Slack credential authenticated as @${auth.userName}, not @agent-native.`,
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
identities.set(workspace, auth);
|
|
63
|
+
return auth;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function verifyAgentNativeIdentity(
|
|
67
|
+
workspace: Workspace,
|
|
68
|
+
): Promise<void> {
|
|
69
|
+
await getAgentNativeIdentity(workspace);
|
|
70
|
+
}
|
|
43
71
|
|
|
44
72
|
return {
|
|
45
|
-
getChannelHistory(
|
|
73
|
+
async getChannelHistory(
|
|
46
74
|
workspace: Workspace,
|
|
47
75
|
channelId: string,
|
|
48
76
|
limit?: number,
|
|
49
77
|
cursor?: string,
|
|
50
78
|
): Promise<ChannelHistoryResult> {
|
|
79
|
+
await verifyAgentNativeIdentity(workspace);
|
|
51
80
|
return readChannelHistory(
|
|
52
81
|
workspace,
|
|
53
82
|
channelId,
|
|
@@ -56,19 +85,25 @@ export function createSlackReader(identity: SlackReaderIdentity) {
|
|
|
56
85
|
tokenResolver,
|
|
57
86
|
);
|
|
58
87
|
},
|
|
59
|
-
getTeamInfo(workspace: Workspace): Promise<SlackTeamInfo> {
|
|
88
|
+
async getTeamInfo(workspace: Workspace): Promise<SlackTeamInfo> {
|
|
89
|
+
await verifyAgentNativeIdentity(workspace);
|
|
60
90
|
return readTeamInfo(workspace, tokenResolver);
|
|
61
91
|
},
|
|
62
|
-
getUserInfo(
|
|
92
|
+
async getUserInfo(
|
|
93
|
+
workspace: Workspace,
|
|
94
|
+
userId: string,
|
|
95
|
+
): Promise<SlackUserInfo> {
|
|
96
|
+
await verifyAgentNativeIdentity(workspace);
|
|
63
97
|
return readUserInfo(workspace, userId, tokenResolver);
|
|
64
98
|
},
|
|
65
|
-
getThread(
|
|
99
|
+
async getThread(
|
|
66
100
|
workspace: Workspace,
|
|
67
101
|
channelId: string,
|
|
68
102
|
threadTs: string,
|
|
69
103
|
limit?: number,
|
|
70
104
|
cursor?: string,
|
|
71
105
|
): Promise<ThreadRepliesResult> {
|
|
106
|
+
await verifyAgentNativeIdentity(workspace);
|
|
72
107
|
return readThread(
|
|
73
108
|
workspace,
|
|
74
109
|
channelId,
|
|
@@ -78,19 +113,21 @@ export function createSlackReader(identity: SlackReaderIdentity) {
|
|
|
78
113
|
tokenResolver,
|
|
79
114
|
);
|
|
80
115
|
},
|
|
81
|
-
addEyesReaction(
|
|
116
|
+
async addEyesReaction(
|
|
82
117
|
workspace: Workspace,
|
|
83
118
|
channelId: string,
|
|
84
119
|
timestamp: string,
|
|
85
120
|
): Promise<SlackReactionResult> {
|
|
121
|
+
await verifyAgentNativeIdentity(workspace);
|
|
86
122
|
return writeEyesReaction(workspace, channelId, timestamp, tokenResolver);
|
|
87
123
|
},
|
|
88
|
-
postThreadReply(
|
|
124
|
+
async postThreadReply(
|
|
89
125
|
workspace: Workspace,
|
|
90
126
|
channelId: string,
|
|
91
127
|
threadTs: string,
|
|
92
128
|
text: string,
|
|
93
129
|
): Promise<SlackPostMessageResult> {
|
|
130
|
+
await verifyAgentNativeIdentity(workspace);
|
|
94
131
|
return writeThreadReply(
|
|
95
132
|
workspace,
|
|
96
133
|
channelId,
|
|
@@ -99,5 +136,41 @@ export function createSlackReader(identity: SlackReaderIdentity) {
|
|
|
99
136
|
tokenResolver,
|
|
100
137
|
);
|
|
101
138
|
},
|
|
139
|
+
async getEyesReaction(
|
|
140
|
+
workspace: Workspace,
|
|
141
|
+
channelId: string,
|
|
142
|
+
timestamp: string,
|
|
143
|
+
): Promise<SlackReactionState> {
|
|
144
|
+
await verifyAgentNativeIdentity(workspace);
|
|
145
|
+
return readEyesReaction(workspace, channelId, timestamp, tokenResolver);
|
|
146
|
+
},
|
|
147
|
+
async getCompleteThread(
|
|
148
|
+
workspace: Workspace,
|
|
149
|
+
channelId: string,
|
|
150
|
+
threadTs: string,
|
|
151
|
+
): Promise<{ messages: SlackMessage[]; hasMore: boolean }> {
|
|
152
|
+
const messages: SlackMessage[] = [];
|
|
153
|
+
let cursor: string | undefined;
|
|
154
|
+
for (let page = 0; page < 100; page += 1) {
|
|
155
|
+
const result = await this.getThread(
|
|
156
|
+
workspace,
|
|
157
|
+
channelId,
|
|
158
|
+
threadTs,
|
|
159
|
+
100,
|
|
160
|
+
cursor,
|
|
161
|
+
);
|
|
162
|
+
messages.push(...result.messages);
|
|
163
|
+
if (!result.has_more) return { messages, hasMore: false };
|
|
164
|
+
if (!result.next_cursor) {
|
|
165
|
+
throw new Error(
|
|
166
|
+
"Slack thread pagination is incomplete because the provider omitted its next cursor.",
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
cursor = result.next_cursor;
|
|
170
|
+
}
|
|
171
|
+
return { messages, hasMore: true };
|
|
172
|
+
},
|
|
173
|
+
verifyAgentNativeIdentity,
|
|
174
|
+
getAgentNativeIdentity,
|
|
102
175
|
};
|
|
103
176
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const authConfig = z.object({
|
|
3
|
+
disableDesktopSsoFallbackInDevelopment: z.boolean().default(false).meta({
|
|
4
|
+
env: "AGENT_NATIVE_DISABLE_DESKTOP_SSO_FALLBACK",
|
|
5
|
+
doc: "Disable the loopback Desktop SSO fallback in development so isolated acceptance runs can use their configured local identity. Ignored in production.",
|
|
6
|
+
}),
|
|
7
|
+
});
|
|
@@ -32,6 +32,9 @@ export declare const appConfigSchema: z.ZodObject<{
|
|
|
32
32
|
packageName: z.ZodOptional<z.ZodString>;
|
|
33
33
|
template: z.ZodOptional<z.ZodString>;
|
|
34
34
|
}, z.core.$strip>>;
|
|
35
|
+
auth: z.ZodPrefault<z.ZodObject<{
|
|
36
|
+
disableDesktopSsoFallbackInDevelopment: z.ZodDefault<z.ZodBoolean>;
|
|
37
|
+
}, z.core.$strip>>;
|
|
35
38
|
integrations: z.ZodPrefault<z.ZodObject<{
|
|
36
39
|
allowUnverifiedWebhooks: z.ZodDefault<z.ZodBoolean>;
|
|
37
40
|
}, z.core.$strip>>;
|
|
@@ -2,6 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import { a2aConfig } from "./a2a.js";
|
|
3
3
|
import { agentConfig } from "./agent.js";
|
|
4
4
|
import { appConfig } from "./app.js";
|
|
5
|
+
import { authConfig } from "./auth.js";
|
|
5
6
|
import { integrationsConfig } from "./integrations.js";
|
|
6
7
|
import { privateBlobConfig } from "./private-blob.js";
|
|
7
8
|
import { workspaceConfig } from "./workspace.js";
|
|
@@ -25,6 +26,7 @@ export const appConfigSchema = z.object({
|
|
|
25
26
|
a2a: a2aConfig.prefault({}),
|
|
26
27
|
agent: agentConfig.prefault({}),
|
|
27
28
|
app: appConfig.prefault({}),
|
|
29
|
+
auth: authConfig.prefault({}),
|
|
28
30
|
integrations: integrationsConfig.prefault({}),
|
|
29
31
|
privateBlob: privateBlobConfig.prefault({}),
|
|
30
32
|
workspace: workspaceConfig.prefault({}),
|
|
@@ -1226,8 +1226,13 @@ async function executeCodexCliRun(options) {
|
|
|
1226
1226
|
const streamToolOutputToStdout = options.streamToolOutputToStdout ??
|
|
1227
1227
|
process.env.AGENT_NATIVE_CODE_AGENT_STRUCTURED_STDOUT !== "1";
|
|
1228
1228
|
const additionalSkillsRoot = process.env.AGENT_NATIVE_CODE_AGENT_SKILLS_ROOT?.trim();
|
|
1229
|
+
// Desktop has already resolved and capability-scoped its MCP catalog before
|
|
1230
|
+
// spawning the packaged runner. Reading persisted settings again here would
|
|
1231
|
+
// both widen that boundary and require the app's native database binding in
|
|
1232
|
+
// the standalone runner process.
|
|
1233
|
+
const mcpConfig = process.env.MCP_SERVERS === undefined ? await buildMergedConfig() : null;
|
|
1229
1234
|
const args = [
|
|
1230
|
-
...codexMcpConfigArgs(),
|
|
1235
|
+
...codexMcpConfigArgs(mcpConfig),
|
|
1231
1236
|
"--ask-for-approval",
|
|
1232
1237
|
"never",
|
|
1233
1238
|
"--sandbox",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type McpConfig } from "../mcp-client/config.js";
|
|
2
2
|
/**
|
|
3
3
|
* Keep a desktop coding run on the app registry's MCP servers. The normal
|
|
4
4
|
* server-side manager merges persisted MCP settings for web requests, but a
|
|
@@ -18,4 +18,4 @@ export declare function mergeCodeAgentMcpConfig(config: McpConfig | null, enviro
|
|
|
18
18
|
* coding session receives the workspace apps, not the user's global MCP
|
|
19
19
|
* catalog.
|
|
20
20
|
*/
|
|
21
|
-
export declare function codexMcpConfigArgs(environment?: NodeJS.ProcessEnv): string[];
|
|
21
|
+
export declare function codexMcpConfigArgs(config?: McpConfig | null, environment?: NodeJS.ProcessEnv): string[];
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolveDesktopChildComputerMcpServer, } from "../mcp-client/config.js";
|
|
1
2
|
const DESKTOP_MCP_ALLOWLIST_ENV = "AGENT_NATIVE_CODE_AGENT_MCP_SERVER_ALLOWLIST";
|
|
2
3
|
function parseServerAllowlist(raw) {
|
|
3
4
|
if (raw === undefined)
|
|
@@ -17,6 +18,9 @@ export function restrictCodeAgentMcpConfig(config, environment = process.env) {
|
|
|
17
18
|
const allowlist = parseServerAllowlist(environment[DESKTOP_MCP_ALLOWLIST_ENV]);
|
|
18
19
|
if (!allowlist || !config)
|
|
19
20
|
return config;
|
|
21
|
+
const desktopServer = findDesktopChildComputerMcpServer(config, environment);
|
|
22
|
+
if (desktopServer)
|
|
23
|
+
allowlist.add(desktopServer.id);
|
|
20
24
|
const servers = Object.fromEntries(Object.entries(config.servers).filter(([id]) => allowlist.has(id)));
|
|
21
25
|
return { ...config, servers };
|
|
22
26
|
}
|
|
@@ -59,6 +63,18 @@ function parseMcpServers(environment) {
|
|
|
59
63
|
return [id, value];
|
|
60
64
|
}));
|
|
61
65
|
}
|
|
66
|
+
function desktopChildComputerServerConfig(environment) {
|
|
67
|
+
return resolveDesktopChildComputerMcpServer({}, environment)?.config ?? null;
|
|
68
|
+
}
|
|
69
|
+
function findDesktopChildComputerMcpServer(config, environment) {
|
|
70
|
+
const expected = desktopChildComputerServerConfig(environment);
|
|
71
|
+
if (!expected || expected.type !== "http")
|
|
72
|
+
return null;
|
|
73
|
+
const match = Object.entries(config.servers).find(([, server]) => server.type === "http" &&
|
|
74
|
+
server.url === expected.url &&
|
|
75
|
+
server.headers?.Authorization === expected.headers?.Authorization);
|
|
76
|
+
return match ? { id: match[0], config: match[1] } : null;
|
|
77
|
+
}
|
|
62
78
|
/**
|
|
63
79
|
* Add host-provided app/plugin servers to the normal merged config. The
|
|
64
80
|
* filesystem and environment layers have different precedence rules, so the
|
|
@@ -66,14 +82,23 @@ function parseMcpServers(environment) {
|
|
|
66
82
|
*/
|
|
67
83
|
export function mergeCodeAgentMcpConfig(config, environment = process.env) {
|
|
68
84
|
const environmentServers = parseMcpServers(environment);
|
|
69
|
-
|
|
85
|
+
const servers = {
|
|
86
|
+
...(config?.servers ?? {}),
|
|
87
|
+
...environmentServers,
|
|
88
|
+
};
|
|
89
|
+
const existingDesktopServer = config
|
|
90
|
+
? findDesktopChildComputerMcpServer(config, environment)
|
|
91
|
+
: null;
|
|
92
|
+
const desktopServer = existingDesktopServer
|
|
93
|
+
? null
|
|
94
|
+
: resolveDesktopChildComputerMcpServer(servers, environment);
|
|
95
|
+
if (desktopServer)
|
|
96
|
+
servers[desktopServer.id] = desktopServer.config;
|
|
97
|
+
if (Object.keys(servers).length === 0)
|
|
70
98
|
return config;
|
|
71
99
|
return {
|
|
72
100
|
...(config ?? { servers: {} }),
|
|
73
|
-
servers
|
|
74
|
-
...(config?.servers ?? {}),
|
|
75
|
-
...environmentServers,
|
|
76
|
-
},
|
|
101
|
+
servers,
|
|
77
102
|
source: config?.source
|
|
78
103
|
? `${config.source}+desktop-environment`
|
|
79
104
|
: "env:MCP_SERVERS",
|
|
@@ -85,8 +110,8 @@ export function mergeCodeAgentMcpConfig(config, environment = process.env) {
|
|
|
85
110
|
* coding session receives the workspace apps, not the user's global MCP
|
|
86
111
|
* catalog.
|
|
87
112
|
*/
|
|
88
|
-
export function codexMcpConfigArgs(environment = process.env) {
|
|
89
|
-
const servers =
|
|
113
|
+
export function codexMcpConfigArgs(config = null, environment = process.env) {
|
|
114
|
+
const servers = restrictCodeAgentMcpConfig(mergeCodeAgentMcpConfig(config, environment), environment)?.servers ?? {};
|
|
90
115
|
if (Object.keys(servers).length === 0)
|
|
91
116
|
return [];
|
|
92
117
|
const args = [];
|
|
@@ -94,9 +119,9 @@ export function codexMcpConfigArgs(environment = process.env) {
|
|
|
94
119
|
if (server.type !== "http" || !server.url)
|
|
95
120
|
continue;
|
|
96
121
|
const key = codexConfigKey(serverId);
|
|
97
|
-
args.push("-c", `mcp_servers.${
|
|
122
|
+
args.push("-c", `mcp_servers.${key}.url=${tomlString(server.url)}`);
|
|
98
123
|
if (server.headers && Object.keys(server.headers).length > 0) {
|
|
99
|
-
args.push("-c", `mcp_servers.${
|
|
124
|
+
args.push("-c", `mcp_servers.${key}.http_headers=${tomlInlineTable(server.headers)}`);
|
|
100
125
|
}
|
|
101
126
|
}
|
|
102
127
|
return args;
|
|
@@ -515,7 +515,7 @@ export function MultiTabAssistantChat({ showTabBar = true, renderHeader, renderO
|
|
|
515
515
|
threadUrlParamName,
|
|
516
516
|
threadUrlSyncEnabled,
|
|
517
517
|
]);
|
|
518
|
-
const { threads, activeThreadId, isLoading, createThread, switchThread: switchThreadState, forkThread, saveThreadData, generateTitle, searchThreads, loadMoreThreads, refreshThreads, hasMoreThreads, isLoadingMoreThreads, threadsLoadError, isNewThread, pinThread, renameThread, } = useChatThreads(apiUrl, storageKey, scope, {
|
|
518
|
+
const { threads, activeThreadId, isLoading, createThread, switchThread: switchThreadState, forkThread, saveThreadData, generateTitle, searchThreads, loadMoreThreads, refreshThreads, hasMoreThreads, isLoadingMoreThreads, threadsLoadError, restoredThreadIdOnListFailure, isNewThread, pinThread, renameThread, } = useChatThreads(apiUrl, storageKey, scope, {
|
|
519
519
|
restoreActiveThread,
|
|
520
520
|
routeThreadId: threadUrlSyncEnabled
|
|
521
521
|
? urlThreadId
|
|
@@ -1017,7 +1017,34 @@ export function MultiTabAssistantChat({ showTabBar = true, renderHeader, renderO
|
|
|
1017
1017
|
// optimistic id for a brand-new session); the activeThreadId effect above
|
|
1018
1018
|
// adds it to openTabIds without spinning up a duplicate thread.
|
|
1019
1019
|
const autoCreatingRef = useRef(false);
|
|
1020
|
+
const restoreFailureReplacementInFlightRef = useRef(null);
|
|
1020
1021
|
const lastTabReplacementInFlightRef = useRef(false);
|
|
1022
|
+
useEffect(() => {
|
|
1023
|
+
if (isLoading ||
|
|
1024
|
+
!restoredThreadIdOnListFailure ||
|
|
1025
|
+
activeThreadId !== restoredThreadIdOnListFailure ||
|
|
1026
|
+
restoreFailureReplacementInFlightRef.current ===
|
|
1027
|
+
restoredThreadIdOnListFailure) {
|
|
1028
|
+
return;
|
|
1029
|
+
}
|
|
1030
|
+
restoreFailureReplacementInFlightRef.current =
|
|
1031
|
+
restoredThreadIdOnListFailure;
|
|
1032
|
+
void createThread().then((id) => {
|
|
1033
|
+
if (!id) {
|
|
1034
|
+
restoreFailureReplacementInFlightRef.current = null;
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1037
|
+
newThreadIds.current.add(id);
|
|
1038
|
+
setOpenTabIds([id]);
|
|
1039
|
+
writeThreadUrl(null, { replace: true });
|
|
1040
|
+
});
|
|
1041
|
+
}, [
|
|
1042
|
+
activeThreadId,
|
|
1043
|
+
createThread,
|
|
1044
|
+
isLoading,
|
|
1045
|
+
restoredThreadIdOnListFailure,
|
|
1046
|
+
writeThreadUrl,
|
|
1047
|
+
]);
|
|
1021
1048
|
useEffect(() => {
|
|
1022
1049
|
if (isLoading || autoCreatingRef.current)
|
|
1023
1050
|
return;
|
|
@@ -7,6 +7,7 @@ import { setBrowserDemoModeEnabled } from "../../demo/browser-state.js";
|
|
|
7
7
|
import { shouldOfferWorkspace } from "../../org/workspace-url.js";
|
|
8
8
|
import { agentNativePath } from "../api-path.js";
|
|
9
9
|
import { useT } from "../i18n.js";
|
|
10
|
+
import { buildSignInReturnHref } from "../require-session.js";
|
|
10
11
|
import { useDemoModeStatus } from "../use-demo-mode-status.js";
|
|
11
12
|
import { notifySessionInvalidated, useSession } from "../use-session.js";
|
|
12
13
|
import { useOrg, useSwitchOrg, useCreateOrg, useInviteMember, useAcceptInvitation, useJoinByDomain, } from "./hooks.js";
|
|
@@ -142,16 +143,19 @@ export function OrgSwitcher({ className, hideWhenSingle, reserveSpace, compact,
|
|
|
142
143
|
return;
|
|
143
144
|
setSigningOut(true);
|
|
144
145
|
try {
|
|
145
|
-
await fetch(agentNativePath("/_agent-native/auth/logout"), {
|
|
146
|
+
const response = await fetch(agentNativePath("/_agent-native/auth/logout"), {
|
|
146
147
|
method: "POST",
|
|
147
148
|
credentials: "include",
|
|
148
149
|
});
|
|
150
|
+
if (!response.ok) {
|
|
151
|
+
console.warn("Logout request returned an error before sign-in", response.status);
|
|
152
|
+
}
|
|
149
153
|
}
|
|
150
|
-
catch {
|
|
151
|
-
|
|
154
|
+
catch (error) {
|
|
155
|
+
console.warn("Unable to complete logout request before sign-in", error);
|
|
152
156
|
}
|
|
153
157
|
notifySessionInvalidated();
|
|
154
|
-
window.location.
|
|
158
|
+
window.location.replace(buildSignInReturnHref());
|
|
155
159
|
};
|
|
156
160
|
if (!org) {
|
|
157
161
|
return reserveSpace && isLoading ? (_jsx(OrgSwitcherLoadingPlaceholder, { className: className })) : null;
|
|
@@ -96,6 +96,7 @@ export declare function useChatThreads(apiUrl?: string, storageKey?: string, sco
|
|
|
96
96
|
hasMoreThreads: boolean;
|
|
97
97
|
isLoadingMoreThreads: boolean;
|
|
98
98
|
threadsLoadError: string | null;
|
|
99
|
+
restoredThreadIdOnListFailure: string | null;
|
|
99
100
|
isNewThread: (id: string) => boolean;
|
|
100
101
|
};
|
|
101
102
|
export {};
|
|
@@ -157,6 +157,7 @@ export function useChatThreads(apiUrl = agentNativePath("/_agent-native/agent-ch
|
|
|
157
157
|
const [hasMoreThreads, setHasMoreThreads] = useState(false);
|
|
158
158
|
const [isLoadingMoreThreads, setIsLoadingMoreThreads] = useState(false);
|
|
159
159
|
const [threadsLoadError, setThreadsLoadError] = useState(null);
|
|
160
|
+
const [restoredThreadIdOnListFailure, setRestoredThreadIdOnListFailure] = useState(null);
|
|
160
161
|
const nextThreadsOffsetRef = useRef(0);
|
|
161
162
|
const latestFetchRequestRef = useRef(0);
|
|
162
163
|
const threadsRef = useRef(threads);
|
|
@@ -483,12 +484,20 @@ export function useChatThreads(apiUrl = agentNativePath("/_agent-native/agent-ch
|
|
|
483
484
|
const loadedThreads = await fetchThreads();
|
|
484
485
|
const restoredId = activeThreadIdRef.current;
|
|
485
486
|
if (loadedThreads === undefined) {
|
|
486
|
-
//
|
|
487
|
-
//
|
|
488
|
-
// the
|
|
487
|
+
// Keep the saved id intact for retry/history, but tell the owning
|
|
488
|
+
// surface to replace the visible tab. A failed list request cannot
|
|
489
|
+
// prove that the saved thread is gone; it only proves that this mount
|
|
490
|
+
// cannot safely restore it.
|
|
491
|
+
if (restoredId &&
|
|
492
|
+
autoCreate &&
|
|
493
|
+
!routeControlsActiveThread &&
|
|
494
|
+
!newlyCreatedRef.current.has(restoredId)) {
|
|
495
|
+
setRestoredThreadIdOnListFailure(restoredId);
|
|
496
|
+
}
|
|
489
497
|
setIsLoading(false);
|
|
490
498
|
return;
|
|
491
499
|
}
|
|
500
|
+
setRestoredThreadIdOnListFailure(null);
|
|
492
501
|
// Exempts route-owned threads (the URL names what the user asked for) and
|
|
493
502
|
// ids this client generated, which have never reached the server.
|
|
494
503
|
const lookupRestored = Boolean(restoredId &&
|
|
@@ -1074,6 +1083,7 @@ export function useChatThreads(apiUrl = agentNativePath("/_agent-native/agent-ch
|
|
|
1074
1083
|
hasMoreThreads,
|
|
1075
1084
|
isLoadingMoreThreads,
|
|
1076
1085
|
threadsLoadError,
|
|
1086
|
+
restoredThreadIdOnListFailure,
|
|
1077
1087
|
isNewThread,
|
|
1078
1088
|
};
|
|
1079
1089
|
}
|
|
@@ -62,11 +62,11 @@ export declare const postAwareness: import("h3").EventHandlerWithFetch<import("h
|
|
|
62
62
|
error: string;
|
|
63
63
|
states?: undefined;
|
|
64
64
|
} | {
|
|
65
|
-
error?: undefined;
|
|
66
65
|
states: {
|
|
67
66
|
clientId: number;
|
|
68
67
|
state: string;
|
|
69
68
|
}[];
|
|
69
|
+
error?: undefined;
|
|
70
70
|
}>>;
|
|
71
71
|
/**
|
|
72
72
|
* GET /_agent-native/collab/:docId/users
|
|
@@ -77,9 +77,9 @@ export declare const getActiveUsers: import("h3").EventHandlerWithFetch<import("
|
|
|
77
77
|
error: string;
|
|
78
78
|
users?: undefined;
|
|
79
79
|
} | {
|
|
80
|
-
error?: undefined;
|
|
81
80
|
users: {
|
|
82
81
|
clientId: number;
|
|
83
82
|
lastSeen: number;
|
|
84
83
|
}[];
|
|
84
|
+
error?: undefined;
|
|
85
85
|
}>>;
|
package/dist/collab/routes.d.ts
CHANGED
|
@@ -26,8 +26,8 @@ export declare const getCollabState: import("h3").EventHandlerWithFetch<import("
|
|
|
26
26
|
* Body: { update: string (base64), requestSource?: string }
|
|
27
27
|
*/
|
|
28
28
|
export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
29
|
-
error: string;
|
|
30
29
|
ok?: undefined;
|
|
30
|
+
error: string;
|
|
31
31
|
} | {
|
|
32
32
|
error?: undefined;
|
|
33
33
|
ok: boolean;
|
|
@@ -60,12 +60,23 @@ export interface McpConfig {
|
|
|
60
60
|
/** Where the config was loaded from (workspace root path, app path, or "env") */
|
|
61
61
|
source?: string;
|
|
62
62
|
}
|
|
63
|
+
export interface DesktopChildComputerMcpServer {
|
|
64
|
+
id: string;
|
|
65
|
+
config: McpHttpServerConfig;
|
|
66
|
+
}
|
|
63
67
|
/**
|
|
64
68
|
* Load MCP configuration.
|
|
65
69
|
*
|
|
66
70
|
* @param startDir - Directory to start the upward search from (defaults to cwd)
|
|
67
71
|
*/
|
|
68
72
|
export declare function loadMcpConfig(startDir?: string): McpConfig | null;
|
|
73
|
+
/**
|
|
74
|
+
* The desktop process supplies this configuration only to a child it spawned
|
|
75
|
+
* for a specific Agent run. Raw MCP config cannot opt into this trust path:
|
|
76
|
+
* the explicit child gate, loopback URL, and strong per-run bearer are all
|
|
77
|
+
* required. Existing user servers always win on key collisions.
|
|
78
|
+
*/
|
|
79
|
+
export declare function resolveDesktopChildComputerMcpServer(existingServers: Record<string, McpServerConfig>, environment?: NodeJS.ProcessEnv): DesktopChildComputerMcpServer | null;
|
|
69
80
|
/**
|
|
70
81
|
* Auto-detect the claude-in-chrome MCP server if it's installed but no
|
|
71
82
|
* config file exists. Gated by `AGENT_NATIVE_DISABLE_MCP_AUTODETECT`.
|
|
@@ -96,39 +96,46 @@ export function loadMcpConfig(startDir) {
|
|
|
96
96
|
* the explicit child gate, loopback URL, and strong per-run bearer are all
|
|
97
97
|
* required. Existing user servers always win on key collisions.
|
|
98
98
|
*/
|
|
99
|
-
function
|
|
100
|
-
if (
|
|
101
|
-
return
|
|
102
|
-
const url =
|
|
103
|
-
const token =
|
|
99
|
+
export function resolveDesktopChildComputerMcpServer(existingServers, environment = process.env) {
|
|
100
|
+
if (environment.AGENT_NATIVE_DESKTOP_CHILD !== "1")
|
|
101
|
+
return null;
|
|
102
|
+
const url = environment.AGENT_NATIVE_DESKTOP_COMPUTER_MCP_URL?.trim();
|
|
103
|
+
const token = environment.AGENT_NATIVE_DESKTOP_COMPUTER_MCP_TOKEN?.trim();
|
|
104
104
|
if (!url || !token || !/^[A-Za-z0-9_-]{32,}$/.test(token))
|
|
105
|
-
return
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
catch {
|
|
117
|
-
return base;
|
|
105
|
+
return null;
|
|
106
|
+
if (!URL.canParse(url))
|
|
107
|
+
return null;
|
|
108
|
+
const parsed = new URL(url);
|
|
109
|
+
if (parsed.protocol !== "http:" ||
|
|
110
|
+
parsed.hostname !== "127.0.0.1" ||
|
|
111
|
+
parsed.pathname !== "/mcp" ||
|
|
112
|
+
parsed.username ||
|
|
113
|
+
parsed.password) {
|
|
114
|
+
return null;
|
|
118
115
|
}
|
|
119
|
-
const servers = { ...(base?.servers ?? {}) };
|
|
120
116
|
let serverId = DESKTOP_COMPUTER_SERVER_ID;
|
|
121
|
-
for (let suffix = 2;
|
|
117
|
+
for (let suffix = 2; existingServers[serverId]; suffix += 1) {
|
|
122
118
|
serverId = `${DESKTOP_COMPUTER_SERVER_ID}-${suffix}`;
|
|
123
119
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
120
|
+
return {
|
|
121
|
+
id: serverId,
|
|
122
|
+
config: {
|
|
123
|
+
type: "http",
|
|
124
|
+
url,
|
|
125
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
126
|
+
description: "Authenticated computer control for this Agent Native desktop task",
|
|
127
|
+
},
|
|
129
128
|
};
|
|
129
|
+
}
|
|
130
|
+
function mergeDesktopChildComputerConfig(base) {
|
|
131
|
+
const desktopServer = resolveDesktopChildComputerMcpServer(base?.servers ?? {});
|
|
132
|
+
if (!desktopServer)
|
|
133
|
+
return base;
|
|
130
134
|
return {
|
|
131
|
-
servers
|
|
135
|
+
servers: {
|
|
136
|
+
...(base?.servers ?? {}),
|
|
137
|
+
[desktopServer.id]: desktopServer.config,
|
|
138
|
+
},
|
|
132
139
|
source: base?.source ? `${base.source}+desktop-child` : "desktop-child",
|
|
133
140
|
};
|
|
134
141
|
}
|
|
@@ -11,14 +11,14 @@
|
|
|
11
11
|
* DELETE /_agent-native/notifications/:id — delete
|
|
12
12
|
*/
|
|
13
13
|
export declare function createNotificationsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<"" | import("./types.js").Notification[] | {
|
|
14
|
-
error?: undefined;
|
|
15
14
|
count: number;
|
|
16
15
|
updated?: undefined;
|
|
16
|
+
error?: undefined;
|
|
17
17
|
ok?: undefined;
|
|
18
18
|
} | {
|
|
19
|
-
error?: undefined;
|
|
20
19
|
count?: undefined;
|
|
21
20
|
updated: number;
|
|
21
|
+
error?: undefined;
|
|
22
22
|
ok?: undefined;
|
|
23
23
|
} | {
|
|
24
24
|
count?: undefined;
|
|
@@ -26,8 +26,8 @@ export declare function createNotificationsHandler(): import("h3").EventHandlerW
|
|
|
26
26
|
error: string;
|
|
27
27
|
ok?: undefined;
|
|
28
28
|
} | {
|
|
29
|
-
error?: undefined;
|
|
30
29
|
count?: undefined;
|
|
31
30
|
updated?: undefined;
|
|
31
|
+
error?: undefined;
|
|
32
32
|
ok: boolean;
|
|
33
33
|
}>>;
|
|
@@ -41,27 +41,27 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
41
41
|
thumbsUpRate: number;
|
|
42
42
|
avgEvalScore: number;
|
|
43
43
|
} | {
|
|
44
|
-
error?: undefined;
|
|
45
|
-
ok?: undefined;
|
|
46
44
|
summary: import("./types.js").TraceSummary;
|
|
47
45
|
spans: import("./types.js").TraceSpan[];
|
|
48
46
|
id?: undefined;
|
|
49
|
-
} | {
|
|
50
47
|
error?: undefined;
|
|
51
48
|
ok?: undefined;
|
|
49
|
+
} | {
|
|
52
50
|
summary?: undefined;
|
|
53
51
|
spans?: undefined;
|
|
54
52
|
id: string;
|
|
55
|
-
|
|
53
|
+
error?: undefined;
|
|
56
54
|
ok?: undefined;
|
|
55
|
+
} | {
|
|
57
56
|
summary?: undefined;
|
|
58
57
|
spans?: undefined;
|
|
59
58
|
id?: undefined;
|
|
60
59
|
error: any;
|
|
60
|
+
ok?: undefined;
|
|
61
61
|
} | {
|
|
62
|
-
error?: undefined;
|
|
63
62
|
summary?: undefined;
|
|
64
63
|
spans?: undefined;
|
|
65
64
|
id?: undefined;
|
|
65
|
+
error?: undefined;
|
|
66
66
|
ok: boolean;
|
|
67
67
|
}>>;
|
|
@@ -51,8 +51,8 @@ export declare function handleDeleteResource(event: any): Promise<{
|
|
|
51
51
|
error: string;
|
|
52
52
|
ok?: undefined;
|
|
53
53
|
} | {
|
|
54
|
-
ok: boolean;
|
|
55
54
|
error?: undefined;
|
|
55
|
+
ok: boolean;
|
|
56
56
|
}>;
|
|
57
57
|
/** POST /_agent-native/resources/upload — upload a file as a resource */
|
|
58
58
|
export declare function handleUploadResource(event: any): Promise<import("./store.js").Resource | {
|
|
@@ -73,10 +73,10 @@ export declare function handleUploadResource(event: any): Promise<import("./stor
|
|
|
73
73
|
runId: string | null;
|
|
74
74
|
expiresAt: number | null;
|
|
75
75
|
metadata: string | null;
|
|
76
|
+
error?: undefined;
|
|
76
77
|
url: string;
|
|
77
78
|
provider: string;
|
|
78
79
|
storageSetupRequired?: undefined;
|
|
79
|
-
error?: undefined;
|
|
80
80
|
} | {
|
|
81
81
|
error: string;
|
|
82
82
|
storageSetupRequired: boolean;
|