@agent-native/core 0.157.8 → 0.157.10
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/actions/purge-first-party-analytics-postgres.ts +26 -8
- package/corpus/templates/analytics/scripts/first-party-analytics-postgres-retention-dry-run.sql +261 -0
- package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +3 -1
- package/corpus/templates/analytics/server/lib/first-party-analytics-purge.ts +75 -31
- package/corpus/templates/analytics/server/lib/workspace-feature-flags.ts +4 -1
- package/corpus/templates/analytics/server/plugins/db.ts +139 -0
- package/corpus/templates/slides/AGENTS.md +14 -10
- package/corpus/templates/slides/actions/export-pptx.ts +1311 -121
- package/corpus/templates/slides/actions/import-file.ts +28 -1
- package/corpus/templates/slides/actions/import-pptx.ts +69 -42
- package/corpus/templates/slides/app/components/deck/SlideRenderer.tsx +222 -29
- package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +71 -0
- package/corpus/templates/slides/app/global.css +19 -1
- package/corpus/templates/slides/app/lib/export-google-slides-client.ts +44 -5
- package/corpus/templates/slides/app/lib/export-pdf-client.ts +13 -6
- package/corpus/templates/slides/app/lib/export-pptx-client.ts +601 -30
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +18 -1
- package/corpus/templates/slides/server/handlers/import/html-converter.ts +1240 -22
- package/corpus/templates/slides/server/handlers/import/pdf-fidelity-parser.ts +329 -64
- package/corpus/templates/slides/server/handlers/import/pptx-assets.ts +24 -0
- package/corpus/templates/slides/server/lib/source-import.ts +10 -1
- package/corpus/templates/slides/shared/aspect-ratios.ts +13 -1
- package/dist/agent/harness/migrations.d.ts +4 -0
- package/dist/agent/harness/migrations.js +60 -0
- package/dist/agent/production-agent.d.ts +2 -0
- package/dist/agent/production-agent.js +15 -0
- package/dist/agent/run-loop-with-resume.d.ts +1 -0
- package/dist/agent/run-loop-with-resume.js +44 -12
- package/dist/agent/run-migrations.d.ts +8 -0
- package/dist/agent/run-migrations.js +61 -0
- package/dist/automations/service.d.ts +6 -0
- package/dist/automations/service.js +35 -0
- package/dist/chat-threads/migrations.d.ts +1 -0
- package/dist/chat-threads/migrations.js +1 -0
- package/dist/chat-threads/schema-migrations.d.ts +8 -0
- package/dist/chat-threads/schema-migrations.js +60 -0
- package/dist/cli/code-agent-connector.d.ts +4 -1
- package/dist/cli/code-agent-connector.js +154 -23
- package/dist/cli/code-agent-executor.js +255 -13
- package/dist/cli/code-agent-runs.d.ts +1 -0
- package/dist/cli/code-agent-runs.js +2 -1
- package/dist/cli/portal-workspace.d.ts +49 -0
- package/dist/cli/portal-workspace.js +320 -0
- package/dist/client/AssistantChat.d.ts +1 -1
- package/dist/client/AssistantChat.js +43 -61
- package/dist/client/ConnectBuilderCard.js +33 -5
- package/dist/client/agent-chat-adapter.js +4 -0
- package/dist/client/analytics-platform.d.ts +2 -0
- package/dist/client/analytics-platform.js +12 -0
- package/dist/client/analytics.d.ts +3 -0
- package/dist/client/analytics.js +10 -1
- package/dist/client/chat/index.d.ts +1 -0
- package/dist/client/chat/index.js +1 -0
- package/dist/client/chat/message-components.d.ts +2 -0
- package/dist/client/chat/message-components.js +11 -1
- package/dist/client/chat/run-recovery.d.ts +2 -1
- package/dist/client/chat/run-recovery.js +2 -2
- package/dist/client/chat-first/apps-rail.d.ts +1 -1
- package/dist/client/chat-first/apps-rail.js +11 -11
- package/dist/client/chat-first/surface-tabs.d.ts +1 -1
- package/dist/client/chat-first/surface-tabs.js +38 -36
- package/dist/client/chat-first/types.d.ts +5 -0
- package/dist/client/chat-first.d.ts +1 -1
- package/dist/client/chat-first.js +4 -4
- package/dist/client/desktop-local-code-change.d.ts +10 -0
- package/dist/client/desktop-local-code-change.js +16 -0
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.js +1 -0
- package/dist/client/observability/ThumbsFeedback.js +53 -26
- package/dist/client/observability/useObservability.d.ts +1 -1
- package/dist/client/observability/useObservability.js +6 -3
- package/dist/client/terminal/AgentTerminal.d.ts +9 -1
- package/dist/client/terminal/AgentTerminal.js +38 -3
- package/dist/client/terminal/index.d.ts +1 -1
- package/dist/client/terminal/index.js +1 -1
- package/dist/client/track.js +9 -1
- package/dist/client/use-action.js +3 -0
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/deploy/build.js +1 -1
- package/dist/feature-flags/a2a-action-route.js +4 -2
- package/dist/feature-flags/permissions.d.ts +2 -0
- package/dist/feature-flags/permissions.js +6 -1
- package/dist/ingestion/pptx.d.ts +92 -1
- package/dist/ingestion/pptx.js +1180 -115
- package/dist/integrations/index.d.ts +3 -3
- package/dist/integrations/index.js +2 -2
- package/dist/integrations/plugin.js +95 -6
- package/dist/integrations/remote-commands-store.d.ts +5 -0
- package/dist/integrations/remote-commands-store.js +63 -22
- package/dist/integrations/remote-devices-store.d.ts +2 -1
- package/dist/integrations/remote-devices-store.js +46 -0
- package/dist/integrations/remote-types.d.ts +20 -0
- package/dist/jobs/actions/list-recurring-jobs.d.ts +3 -0
- package/dist/jobs/actions/list-recurring-jobs.js +5 -1
- package/dist/jobs/frontmatter.d.ts +16 -0
- package/dist/jobs/frontmatter.js +53 -0
- package/dist/jobs/remote-execution.d.ts +42 -0
- package/dist/jobs/remote-execution.js +287 -0
- package/dist/jobs/scheduler.js +102 -3
- package/dist/jobs/tools.js +50 -3
- package/dist/localization/core-messages/ar-SA.js +4 -0
- package/dist/localization/core-messages/de-DE.js +4 -0
- package/dist/localization/core-messages/en-US.d.ts +4 -0
- package/dist/localization/core-messages/en-US.js +4 -0
- package/dist/localization/core-messages/es-ES.js +4 -0
- package/dist/localization/core-messages/fr-FR.js +4 -0
- package/dist/localization/core-messages/hi-IN.js +4 -0
- package/dist/localization/core-messages/ja-JP.js +4 -0
- package/dist/localization/core-messages/ko-KR.js +4 -0
- package/dist/localization/core-messages/pt-BR.js +4 -0
- package/dist/localization/core-messages/zh-CN.js +4 -0
- package/dist/localization/core-messages/zh-TW.js +4 -0
- package/dist/localization/core-messages.d.ts +4 -0
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/feedback.js +40 -16
- package/dist/observability/routes.d.ts +6 -6
- package/dist/observability/routes.js +15 -3
- package/dist/observability/store.js +2 -0
- package/dist/observability/traces.js +5 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +16 -16
- package/dist/provider-api/actions/provider-api.d.ts +13 -13
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/action-routes.d.ts +7 -5
- package/dist/server/action-routes.js +13 -6
- package/dist/server/agent-chat-plugin.js +6 -0
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-run-context.d.ts +1 -0
- package/dist/server/agent-run-context.js +9 -0
- package/dist/server/better-auth-instance.js +4 -0
- package/dist/server/core-routes-plugin.js +8 -1
- package/dist/server/realtime-voice.js +2 -1
- package/dist/server/release-migrations.js +16 -0
- package/dist/server/request-context.d.ts +2 -0
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/shared/analytics-platform.d.ts +6 -0
- package/dist/shared/analytics-platform.js +14 -0
- package/dist/shared/mcp-embed-headers.d.ts +1 -1
- package/dist/shared/mcp-embed-headers.js +1 -1
- package/dist/styles/agent-native.css +83 -103
- package/dist/terminal/cli-registry.js +5 -0
- package/dist/tracking/registry.js +9 -1
- package/dist/triggers/actions/list-automations.d.ts +3 -0
- package/dist/triggers/actions/list-automations.js +3 -0
- package/dist/triggers/actions/manage-automation.d.ts +9 -0
- package/dist/triggers/actions/manage-automation.js +14 -2
- package/dist/triggers/actions.js +82 -7
- package/dist/usage/migrations.d.ts +4 -0
- package/dist/usage/migrations.js +45 -0
- package/docs/content/template-content-developers.mdx +48 -0
- package/docs/content/toolkit-ui.mdx +57 -0
- package/package.json +3 -3
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export interface PortalHandoff {
|
|
2
|
+
schemaVersion: 1;
|
|
3
|
+
handoffId: string;
|
|
4
|
+
branch: string;
|
|
5
|
+
remoteRef: string;
|
|
6
|
+
commit: string;
|
|
7
|
+
sourceBranch?: string;
|
|
8
|
+
sourceDirty: boolean;
|
|
9
|
+
createdCommit: boolean;
|
|
10
|
+
repositoryName: string;
|
|
11
|
+
remoteName: string;
|
|
12
|
+
envPolicy: "load-local";
|
|
13
|
+
}
|
|
14
|
+
export interface PortalEnvironment {
|
|
15
|
+
values: Record<string, string>;
|
|
16
|
+
files: string[];
|
|
17
|
+
root: string;
|
|
18
|
+
}
|
|
19
|
+
export interface PortalWorkspace {
|
|
20
|
+
handoff: PortalHandoff;
|
|
21
|
+
repositoryPath: string;
|
|
22
|
+
workspacePath: string;
|
|
23
|
+
environmentRoot: string;
|
|
24
|
+
environmentFiles: string[];
|
|
25
|
+
reused: boolean;
|
|
26
|
+
}
|
|
27
|
+
export interface PortalGitResult {
|
|
28
|
+
status: number | null;
|
|
29
|
+
stdout?: string;
|
|
30
|
+
stderr?: string;
|
|
31
|
+
error?: Error;
|
|
32
|
+
}
|
|
33
|
+
export type RunPortalGit = (args: string[], cwd: string, env?: NodeJS.ProcessEnv) => PortalGitResult | Promise<PortalGitResult>;
|
|
34
|
+
export declare function createPortalHandoff(input: {
|
|
35
|
+
sourcePath: string;
|
|
36
|
+
remoteName?: string;
|
|
37
|
+
handoffId?: string;
|
|
38
|
+
now?: Date;
|
|
39
|
+
runGit?: RunPortalGit;
|
|
40
|
+
}): Promise<PortalHandoff>;
|
|
41
|
+
export declare function preparePortalWorkspace(input: {
|
|
42
|
+
handoff: unknown;
|
|
43
|
+
workspacePath?: string;
|
|
44
|
+
worktreeRoot?: string;
|
|
45
|
+
runGit?: RunPortalGit;
|
|
46
|
+
}): Promise<PortalWorkspace>;
|
|
47
|
+
export declare function parsePortalHandoff(value: unknown): PortalHandoff;
|
|
48
|
+
export declare function listPortalEnvironmentFiles(root: string, mode?: string): string[];
|
|
49
|
+
export declare function loadPortalEnvironment(root: string, mode?: string): PortalEnvironment;
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import { execFile as execFileCallback } from "node:child_process";
|
|
2
|
+
import crypto from "node:crypto";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { promisify } from "node:util";
|
|
7
|
+
import { codeAgentStoreRoot } from "./code-agent-runs.js";
|
|
8
|
+
const execFile = promisify(execFileCallback);
|
|
9
|
+
const MAX_GIT_OUTPUT_BYTES = 1_000_000;
|
|
10
|
+
const PORTAL_ENV_FILES = [".env", ".env.local"];
|
|
11
|
+
const PORTAL_PROTECTED_ENV_KEYS = new Set([
|
|
12
|
+
"AGENT_NATIVE_CODE_AGENTS_HOME",
|
|
13
|
+
"AGENT_NATIVE_CODE_AGENT_PERMISSION_MODE",
|
|
14
|
+
"AGENT_NATIVE_COMPUTER_BRIDGE_TOKEN",
|
|
15
|
+
"AGENT_NATIVE_COMPUTER_BRIDGE_URL",
|
|
16
|
+
"AGENT_NATIVE_REMOTE_DEVICE_PATH",
|
|
17
|
+
"ELECTRON_RUN_AS_NODE",
|
|
18
|
+
"HOME",
|
|
19
|
+
"NODE_OPTIONS",
|
|
20
|
+
"PATH",
|
|
21
|
+
"PWD",
|
|
22
|
+
"SHELL",
|
|
23
|
+
]);
|
|
24
|
+
const runPortalGit = async (args, cwd, env) => {
|
|
25
|
+
try {
|
|
26
|
+
const result = await execFile("git", args, {
|
|
27
|
+
cwd,
|
|
28
|
+
encoding: "utf8",
|
|
29
|
+
env: {
|
|
30
|
+
...process.env,
|
|
31
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
32
|
+
...env,
|
|
33
|
+
},
|
|
34
|
+
maxBuffer: MAX_GIT_OUTPUT_BYTES,
|
|
35
|
+
});
|
|
36
|
+
return {
|
|
37
|
+
status: 0,
|
|
38
|
+
stdout: String(result.stdout),
|
|
39
|
+
stderr: String(result.stderr),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
const candidate = error;
|
|
44
|
+
return {
|
|
45
|
+
status: typeof candidate.code === "number" ? candidate.code : 1,
|
|
46
|
+
stdout: candidate.stdout ? String(candidate.stdout) : undefined,
|
|
47
|
+
stderr: candidate.stderr ? String(candidate.stderr) : undefined,
|
|
48
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
export async function createPortalHandoff(input) {
|
|
53
|
+
const executeGit = input.runGit ?? runPortalGit;
|
|
54
|
+
const sourceCandidate = path.resolve(input.sourcePath);
|
|
55
|
+
const remoteName = input.remoteName ?? "origin";
|
|
56
|
+
assertSafeRemoteName(remoteName);
|
|
57
|
+
const handoffId = input.handoffId ?? crypto.randomUUID();
|
|
58
|
+
assertSafeHandoffId(handoffId);
|
|
59
|
+
const repositoryPath = await requiredGitOutput(executeGit, ["rev-parse", "--show-toplevel"], sourceCandidate, "Selected folder is not a Git repository. Choose a Git folder to use Portal.");
|
|
60
|
+
const head = await requiredGitOutput(executeGit, ["rev-parse", "--verify", "HEAD"], repositoryPath, "Portal could not determine the current Git commit.");
|
|
61
|
+
const status = await executeGit(["status", "--porcelain", "--untracked-files=all"], repositoryPath);
|
|
62
|
+
if (status.status !== 0) {
|
|
63
|
+
throw gitFailure(status, "Portal could not inspect the local Git working tree.");
|
|
64
|
+
}
|
|
65
|
+
const remoteUrl = await requiredGitOutput(executeGit, ["remote", "get-url", remoteName], repositoryPath, `Portal could not find the ${remoteName} Git remote.`);
|
|
66
|
+
if (!remoteUrl) {
|
|
67
|
+
throw new Error(`Portal could not find the ${remoteName} Git remote.`);
|
|
68
|
+
}
|
|
69
|
+
const branchResult = await executeGit(["branch", "--show-current"], repositoryPath);
|
|
70
|
+
const sourceBranch = branchResult.status === 0
|
|
71
|
+
? branchResult.stdout?.trim() || undefined
|
|
72
|
+
: undefined;
|
|
73
|
+
const sourceDirty = Boolean(status.stdout?.trim());
|
|
74
|
+
let commit = head;
|
|
75
|
+
let createdCommit = false;
|
|
76
|
+
if (sourceDirty) {
|
|
77
|
+
const tempRoot = await fs.promises.mkdtemp(path.join(os.tmpdir(), "agent-native-portal-index-"));
|
|
78
|
+
const indexPath = path.join(tempRoot, "index");
|
|
79
|
+
try {
|
|
80
|
+
const indexEnv = { GIT_INDEX_FILE: indexPath };
|
|
81
|
+
const added = await executeGit(["add", "--all", "--", "."], repositoryPath, indexEnv);
|
|
82
|
+
if (added.status !== 0) {
|
|
83
|
+
throw gitFailure(added, "Portal could not snapshot local code.");
|
|
84
|
+
}
|
|
85
|
+
const tree = await requiredGitOutput(executeGit, ["write-tree"], repositoryPath, "Portal could not write the local code snapshot.", indexEnv);
|
|
86
|
+
const authorName = (await optionalGitOutput(executeGit, ["config", "--get", "user.name"], repositoryPath)) ?? "Agent Native Portal";
|
|
87
|
+
const authorEmail = (await optionalGitOutput(executeGit, ["config", "--get", "user.email"], repositoryPath)) ?? "portal@agent-native.local";
|
|
88
|
+
const committed = await requiredGitOutput(executeGit, ["commit-tree", tree, "-p", head, "-m", `Portal handoff ${handoffId}`], repositoryPath, "Portal could not create the local code snapshot.", {
|
|
89
|
+
...indexEnv,
|
|
90
|
+
GIT_AUTHOR_NAME: authorName,
|
|
91
|
+
GIT_AUTHOR_EMAIL: authorEmail,
|
|
92
|
+
GIT_COMMITTER_NAME: authorName,
|
|
93
|
+
GIT_COMMITTER_EMAIL: authorEmail,
|
|
94
|
+
});
|
|
95
|
+
commit = committed;
|
|
96
|
+
createdCommit = true;
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
await fs.promises.rm(tempRoot, { recursive: true, force: true });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const branch = portalBranchName(input.now ?? new Date(), handoffId);
|
|
103
|
+
const pushed = await executeGit(["push", "--no-verify", remoteName, `${commit}:refs/heads/${branch}`], repositoryPath);
|
|
104
|
+
if (pushed.status !== 0) {
|
|
105
|
+
throw gitFailure(pushed, "Portal could not push the code snapshot to the Git remote.");
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
schemaVersion: 1,
|
|
109
|
+
handoffId,
|
|
110
|
+
branch,
|
|
111
|
+
remoteRef: `refs/heads/${branch}`,
|
|
112
|
+
commit,
|
|
113
|
+
sourceBranch,
|
|
114
|
+
sourceDirty,
|
|
115
|
+
createdCommit,
|
|
116
|
+
repositoryName: path.basename(repositoryPath),
|
|
117
|
+
remoteName,
|
|
118
|
+
envPolicy: "load-local",
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
export async function preparePortalWorkspace(input) {
|
|
122
|
+
const executeGit = input.runGit ?? runPortalGit;
|
|
123
|
+
const handoff = parsePortalHandoff(input.handoff);
|
|
124
|
+
const configuredWorkspace = input.workspacePath?.trim();
|
|
125
|
+
if (!configuredWorkspace) {
|
|
126
|
+
throw new Error("Portal needs a configured workspacePath on the paired computer.");
|
|
127
|
+
}
|
|
128
|
+
const repositoryPath = await requiredGitOutput(executeGit, ["rev-parse", "--show-toplevel"], path.resolve(configuredWorkspace), "Portal could not find a Git repository on the paired computer.");
|
|
129
|
+
const trackingRef = `refs/remotes/${handoff.remoteName}/${handoff.branch}`;
|
|
130
|
+
const fetched = await executeGit([
|
|
131
|
+
"fetch",
|
|
132
|
+
"--no-tags",
|
|
133
|
+
"--no-prune",
|
|
134
|
+
handoff.remoteName,
|
|
135
|
+
`${handoff.remoteRef}:${trackingRef}`,
|
|
136
|
+
], repositoryPath);
|
|
137
|
+
if (fetched.status !== 0) {
|
|
138
|
+
throw gitFailure(fetched, "Portal could not fetch the code snapshot on the paired computer.");
|
|
139
|
+
}
|
|
140
|
+
const verified = await executeGit(["cat-file", "-e", `${handoff.commit}^{commit}`], repositoryPath);
|
|
141
|
+
if (verified.status !== 0) {
|
|
142
|
+
throw gitFailure(verified, "Portal fetched the branch but could not verify its commit.");
|
|
143
|
+
}
|
|
144
|
+
const worktreeRoot = path.resolve(input.worktreeRoot ?? path.join(codeAgentStoreRoot(), "portals"));
|
|
145
|
+
await fs.promises.mkdir(worktreeRoot, { recursive: true });
|
|
146
|
+
const baseName = `${handoff.handoffId}-${handoff.commit.slice(0, 8)}`;
|
|
147
|
+
let workspacePath = path.join(worktreeRoot, baseName);
|
|
148
|
+
let reused = false;
|
|
149
|
+
if (fs.existsSync(workspacePath)) {
|
|
150
|
+
const existingHead = await executeGit(["rev-parse", "HEAD"], workspacePath);
|
|
151
|
+
const existingStatus = await executeGit(["status", "--porcelain", "--untracked-files=all"], workspacePath);
|
|
152
|
+
if (existingHead.status === 0 &&
|
|
153
|
+
existingHead.stdout?.trim() === handoff.commit &&
|
|
154
|
+
existingStatus.status === 0 &&
|
|
155
|
+
!existingStatus.stdout?.trim()) {
|
|
156
|
+
reused = true;
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
workspacePath = path.join(worktreeRoot, `${baseName}-${crypto.randomUUID().slice(0, 8)}`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (!reused) {
|
|
163
|
+
const created = await executeGit(["worktree", "add", "--detach", workspacePath, handoff.commit], repositoryPath);
|
|
164
|
+
if (created.status !== 0) {
|
|
165
|
+
throw gitFailure(created, "Portal could not create the isolated workspace on the paired computer.");
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
handoff,
|
|
170
|
+
repositoryPath,
|
|
171
|
+
workspacePath,
|
|
172
|
+
environmentRoot: repositoryPath,
|
|
173
|
+
environmentFiles: listPortalEnvironmentFiles(repositoryPath),
|
|
174
|
+
reused,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
export function parsePortalHandoff(value) {
|
|
178
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
179
|
+
throw new Error("Portal handoff metadata is missing.");
|
|
180
|
+
}
|
|
181
|
+
const record = value;
|
|
182
|
+
const handoffId = stringField(record.handoffId);
|
|
183
|
+
const branch = stringField(record.branch);
|
|
184
|
+
const remoteRef = stringField(record.remoteRef);
|
|
185
|
+
const commit = stringField(record.commit);
|
|
186
|
+
const repositoryName = stringField(record.repositoryName);
|
|
187
|
+
const remoteName = stringField(record.remoteName);
|
|
188
|
+
if (!handoffId || !branch || !remoteRef || !commit || !repositoryName) {
|
|
189
|
+
throw new Error("Portal handoff metadata is incomplete.");
|
|
190
|
+
}
|
|
191
|
+
assertSafeHandoffId(handoffId);
|
|
192
|
+
assertSafeBranch(branch);
|
|
193
|
+
assertSafeRemoteName(remoteName || "");
|
|
194
|
+
if (remoteRef !== `refs/heads/${branch}`) {
|
|
195
|
+
throw new Error("Portal handoff metadata has an invalid remote ref.");
|
|
196
|
+
}
|
|
197
|
+
if (!/^[a-f0-9]{7,64}$/i.test(commit)) {
|
|
198
|
+
throw new Error("Portal handoff metadata has an invalid commit.");
|
|
199
|
+
}
|
|
200
|
+
if (record.schemaVersion !== 1 || record.envPolicy !== "load-local") {
|
|
201
|
+
throw new Error("Portal handoff metadata has an unsupported version.");
|
|
202
|
+
}
|
|
203
|
+
if (typeof record.sourceDirty !== "boolean" ||
|
|
204
|
+
typeof record.createdCommit !== "boolean") {
|
|
205
|
+
throw new Error("Portal handoff metadata has invalid snapshot state.");
|
|
206
|
+
}
|
|
207
|
+
const sourceBranch = stringField(record.sourceBranch);
|
|
208
|
+
return {
|
|
209
|
+
schemaVersion: 1,
|
|
210
|
+
handoffId,
|
|
211
|
+
branch,
|
|
212
|
+
remoteRef,
|
|
213
|
+
commit,
|
|
214
|
+
sourceBranch,
|
|
215
|
+
sourceDirty: record.sourceDirty,
|
|
216
|
+
createdCommit: record.createdCommit,
|
|
217
|
+
repositoryName,
|
|
218
|
+
remoteName: remoteName || "origin",
|
|
219
|
+
envPolicy: "load-local",
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
export function listPortalEnvironmentFiles(root, mode = process.env.NODE_ENV || "development") {
|
|
223
|
+
const normalizedMode = mode.trim().replace(/[^A-Za-z0-9_-]/g, "");
|
|
224
|
+
const names = [
|
|
225
|
+
...PORTAL_ENV_FILES,
|
|
226
|
+
...(normalizedMode ? [`.env.${normalizedMode}`] : []),
|
|
227
|
+
...(normalizedMode ? [`.env.${normalizedMode}.local`] : []),
|
|
228
|
+
];
|
|
229
|
+
return [...new Set(names)].filter((name) => {
|
|
230
|
+
try {
|
|
231
|
+
return fs.statSync(path.join(root, name)).isFile();
|
|
232
|
+
// coercion-ok: A missing local env file is an intentional absent value.
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
export function loadPortalEnvironment(root, mode = process.env.NODE_ENV || "development") {
|
|
240
|
+
const resolvedRoot = path.resolve(root);
|
|
241
|
+
const files = listPortalEnvironmentFiles(resolvedRoot, mode);
|
|
242
|
+
const values = {};
|
|
243
|
+
for (const name of files) {
|
|
244
|
+
const filePath = path.join(resolvedRoot, name);
|
|
245
|
+
let content;
|
|
246
|
+
try {
|
|
247
|
+
content = fs.readFileSync(filePath, "utf8");
|
|
248
|
+
}
|
|
249
|
+
catch (error) {
|
|
250
|
+
throw new Error(`Portal could not read ${name} on the paired computer: ${error instanceof Error ? error.message : String(error)}`);
|
|
251
|
+
}
|
|
252
|
+
for (const line of content.split(/\r?\n/)) {
|
|
253
|
+
const match = line.match(/^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)\s*$/);
|
|
254
|
+
if (!match || PORTAL_PROTECTED_ENV_KEYS.has(match[1]))
|
|
255
|
+
continue;
|
|
256
|
+
values[match[1]] = parsePortalEnvValue(match[2]);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return { values, files, root: resolvedRoot };
|
|
260
|
+
}
|
|
261
|
+
function parsePortalEnvValue(value) {
|
|
262
|
+
const trimmed = value.trim();
|
|
263
|
+
if (trimmed.length >= 2 &&
|
|
264
|
+
((trimmed.startsWith('"') && trimmed.endsWith('"')) ||
|
|
265
|
+
(trimmed.startsWith("'") && trimmed.endsWith("'")))) {
|
|
266
|
+
return trimmed.slice(1, -1);
|
|
267
|
+
}
|
|
268
|
+
const comment = trimmed.indexOf(" #");
|
|
269
|
+
return comment >= 0 ? trimmed.slice(0, comment).trimEnd() : trimmed;
|
|
270
|
+
}
|
|
271
|
+
function portalBranchName(now, handoffId) {
|
|
272
|
+
const timestamp = now.toISOString().replace(/\D/g, "").slice(0, 14);
|
|
273
|
+
const suffix = handoffId.slice(0, 8).replace(/[-.]+$/g, "") || "handoff";
|
|
274
|
+
return `portal/${timestamp}-${suffix}`;
|
|
275
|
+
}
|
|
276
|
+
function stringField(value) {
|
|
277
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
278
|
+
}
|
|
279
|
+
function assertSafeHandoffId(value) {
|
|
280
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,119}$/.test(value)) {
|
|
281
|
+
throw new Error("Portal handoff id is invalid.");
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
function assertSafeBranch(value) {
|
|
285
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._/-]{0,180}$/.test(value) ||
|
|
286
|
+
value.includes("..") ||
|
|
287
|
+
value.includes("//") ||
|
|
288
|
+
value.endsWith("/") ||
|
|
289
|
+
value.endsWith(".")) {
|
|
290
|
+
throw new Error("Portal branch is invalid.");
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
function assertSafeRemoteName(value) {
|
|
294
|
+
if (!/^[A-Za-z0-9._-]{1,64}$/.test(value)) {
|
|
295
|
+
throw new Error("Portal Git remote name is invalid.");
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
async function requiredGitOutput(executeGit, args, cwd, fallback, env) {
|
|
299
|
+
const result = await executeGit(args, cwd, env);
|
|
300
|
+
if (result.status !== 0 || !result.stdout?.trim()) {
|
|
301
|
+
throw gitFailure(result, fallback);
|
|
302
|
+
}
|
|
303
|
+
return result.stdout.trim();
|
|
304
|
+
}
|
|
305
|
+
async function optionalGitOutput(executeGit, args, cwd) {
|
|
306
|
+
const result = await executeGit(args, cwd);
|
|
307
|
+
return result.status === 0 ? result.stdout?.trim() || undefined : undefined;
|
|
308
|
+
}
|
|
309
|
+
function gitFailure(result, fallback) {
|
|
310
|
+
const detail = sanitizeGitError(result.stderr?.trim() || result.error?.message);
|
|
311
|
+
return new Error(detail ? `${fallback} ${detail}` : fallback);
|
|
312
|
+
}
|
|
313
|
+
function sanitizeGitError(value) {
|
|
314
|
+
if (!value)
|
|
315
|
+
return undefined;
|
|
316
|
+
return value
|
|
317
|
+
.replace(/https?:\/\/[^\s/@]+(?::[^\s/@]*)?@/gi, "https://REDACTED@")
|
|
318
|
+
.replace(/\s+/g, " ")
|
|
319
|
+
.slice(0, 800);
|
|
320
|
+
}
|
|
@@ -318,7 +318,7 @@ export interface AssistantChatProps {
|
|
|
318
318
|
composerAreaClassName?: string;
|
|
319
319
|
/** Placeholder for the shared composer in its normal idle state. */
|
|
320
320
|
composerPlaceholder?: string;
|
|
321
|
-
/**
|
|
321
|
+
/** Controls the compactness of the provider setup panel attached above the composer. */
|
|
322
322
|
missingApiKeySetupLayout?: BuilderSetupCardLayout;
|
|
323
323
|
/** Visual density for the shared composer shell. */
|
|
324
324
|
composerLayoutVariant?: AgentComposerLayoutVariant;
|
|
@@ -22,13 +22,12 @@ import { readAssistantChatComposerDraft, writeAssistantChatComposerDraft, } from
|
|
|
22
22
|
import { TextStreamingContext } from "./chat/markdown-renderer.js";
|
|
23
23
|
import { CheckpointContext, MessageActionsContext, assistantMessageRunId, UserMessage, AssistantMessage, SelectionAttachedPill, RunningActivityStatus, displayableUserMessageText, isHiddenUserMessage, ServerRunActiveContext, UserStoppedRunContext, } from "./chat/message-components.js";
|
|
24
24
|
import { repoHasAssistantMessage, getRepoMessages, getRepoMessage, shouldImportServerThreadData, dedupeRepoMessagesById, dropEmptyAssistantMessages, withLastAssistantRunDuration, } from "./chat/repo-helpers.js";
|
|
25
|
-
import { BuilderSetupCard,
|
|
25
|
+
import { BuilderSetupCard, LoopLimitContinueCard, RunErrorRecoveryCard, PlanModeCallout, getLoopLimitMetadata, getRunErrorMetadata, getRequestModeMetadata, runErrorKey, } from "./chat/run-recovery.js";
|
|
26
26
|
import { createAgentChatRuntimeAdapter, } from "./chat/runtime.js";
|
|
27
27
|
import { ChatRunningContext, ChatRunningRunIdContext, ChatRunningTurnIdContext, ChatRunDurationContext, SuppressInlineOpenAppContext, ApprovalContext, ReconnectStreamMessage, } from "./chat/tool-call-display.js";
|
|
28
28
|
import { useAgentChatLifecycleTracking } from "./chat/use-agent-chat-lifecycle-tracking.js";
|
|
29
29
|
import { useReconnectReaderOwner } from "./chat/use-reconnect-reader-owner.js";
|
|
30
30
|
import { MessageScroller, MessageScrollerButton, MessageScrollerContent, MessageScrollerItem, MessageScrollerProvider, MessageScrollerViewport, useMessageScroller, } from "./components/ui/message-scroller.js";
|
|
31
|
-
import { Popover, PopoverContent, PopoverTrigger, } from "./components/ui/popover.js";
|
|
32
31
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "./components/ui/tooltip.js";
|
|
33
32
|
import { AgentComposerFrame, TiptapComposer, } from "./composer/index.js";
|
|
34
33
|
import { useAgentDynamicSuggestionsResult, } from "./dynamic-suggestions.js";
|
|
@@ -1501,15 +1500,6 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
1501
1500
|
agentEngineConfigured.state === "missing" &&
|
|
1502
1501
|
missingApiKey;
|
|
1503
1502
|
const isComposerDisabled = composerDisabled;
|
|
1504
|
-
const [missingKeySetupOpen, setMissingKeySetupOpen] = useState(false);
|
|
1505
|
-
const requestMissingKeySetup = useCallback(() => {
|
|
1506
|
-
setMissingKeySetupOpen(true);
|
|
1507
|
-
}, []);
|
|
1508
|
-
useEffect(() => {
|
|
1509
|
-
if (agentEngineConfigured.state !== "configured")
|
|
1510
|
-
return;
|
|
1511
|
-
setMissingKeySetupOpen(false);
|
|
1512
|
-
}, [agentEngineConfigured.state]);
|
|
1513
1503
|
const [authError, setAuthError] = useState(null);
|
|
1514
1504
|
const [authSessionAvailable, setAuthSessionAvailable] = useState(false);
|
|
1515
1505
|
const [queuedMessages, setQueuedMessages] = useState([]);
|
|
@@ -2644,6 +2634,9 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
2644
2634
|
})();
|
|
2645
2635
|
return () => {
|
|
2646
2636
|
cancelled = true;
|
|
2637
|
+
// React StrictMode replays effects without resetting refs. Let the
|
|
2638
|
+
// replay start the restore again after cancelling this attempt.
|
|
2639
|
+
hasRestoredRef.current = false;
|
|
2647
2640
|
};
|
|
2648
2641
|
}
|
|
2649
2642
|
else if (threadId && isNewThread) {
|
|
@@ -2737,6 +2730,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
2737
2730
|
})();
|
|
2738
2731
|
return () => {
|
|
2739
2732
|
cancelled = true;
|
|
2733
|
+
hasRestoredRef.current = false;
|
|
2740
2734
|
};
|
|
2741
2735
|
}
|
|
2742
2736
|
else {
|
|
@@ -2968,7 +2962,6 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
2968
2962
|
// Nudge the shared hook to re-check after a Builder connect.
|
|
2969
2963
|
const handleBuilderConnected = useCallback(() => {
|
|
2970
2964
|
focusComposerAfterConnectRef.current = true;
|
|
2971
|
-
setMissingKeySetupOpen(false);
|
|
2972
2965
|
window.dispatchEvent(new Event("agent-engine:configured-changed"));
|
|
2973
2966
|
}, []);
|
|
2974
2967
|
useEffect(() => {
|
|
@@ -3611,9 +3604,6 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
3611
3604
|
const addToQueue = useCallback(async (text, images, references, attachments, requestMode, intent = "queued", recoveryAction, includeComposerContext = false, trackInRunsTray = false, preserveReconnectAutoRecoveryBudget = false, hideUserMessage = false, submitMessageId, approvedToolCalls, continuationTurnId) => {
|
|
3612
3605
|
if (isAgentChatSubmitCancelled(submitMessageId))
|
|
3613
3606
|
return;
|
|
3614
|
-
if (engineSetupRequired) {
|
|
3615
|
-
requestMissingKeySetup();
|
|
3616
|
-
}
|
|
3617
3607
|
if (!preserveReconnectAutoRecoveryBudget) {
|
|
3618
3608
|
reconnectAutoRecoveryCountRef.current = 0;
|
|
3619
3609
|
}
|
|
@@ -3826,7 +3816,6 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
3826
3816
|
markOptimisticRunning,
|
|
3827
3817
|
engineSetupRequired,
|
|
3828
3818
|
appendThreadMessage,
|
|
3829
|
-
requestMissingKeySetup,
|
|
3830
3819
|
selectedEffort,
|
|
3831
3820
|
selectedEngine,
|
|
3832
3821
|
selectedModel,
|
|
@@ -4032,7 +4021,10 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
4032
4021
|
const latestAssistantWasPlan = latestMessageRole === "assistant" &&
|
|
4033
4022
|
getRequestModeMetadata(latestMessage) === "plan";
|
|
4034
4023
|
const showMissingKeySetup = engineSetupRequired && !authError;
|
|
4035
|
-
const
|
|
4024
|
+
const [missingKeyBouncePulse, setMissingKeyBouncePulse] = useState(0);
|
|
4025
|
+
const bounceMissingKeySetup = useCallback(() => {
|
|
4026
|
+
setMissingKeyBouncePulse((pulse) => pulse + 1);
|
|
4027
|
+
}, []);
|
|
4036
4028
|
const showPlanModeCallout = execMode === "plan" &&
|
|
4037
4029
|
!planModeDisabled &&
|
|
4038
4030
|
!isComposerDisabled &&
|
|
@@ -4121,7 +4113,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
4121
4113
|
(guidedQuestions && guidedQuestions.length > 0) ||
|
|
4122
4114
|
visibleComposerContextItems.length > 0 ||
|
|
4123
4115
|
showPlanModeCallout ||
|
|
4124
|
-
|
|
4116
|
+
showMissingKeySetup);
|
|
4125
4117
|
const approvalResolutionScope = threadId ?? tabId ?? "default";
|
|
4126
4118
|
const [approvalResolutionState, setApprovalResolutionState] = useState(() => ({
|
|
4127
4119
|
scope: approvalResolutionScope,
|
|
@@ -4220,7 +4212,6 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
4220
4212
|
t("agentChat.empty.prompt") }), emptyStateAddon, resolvedSuggestions &&
|
|
4221
4213
|
resolvedSuggestions.length > 0 ? (_jsx("div", { className: "flex w-full max-w-[280px] flex-col gap-1", children: resolvedSuggestions.map((suggestion) => (_jsx("button", { onClick: () => {
|
|
4222
4214
|
if (engineSetupRequired) {
|
|
4223
|
-
requestMissingKeySetup();
|
|
4224
4215
|
return;
|
|
4225
4216
|
}
|
|
4226
4217
|
void addToQueue(suggestion);
|
|
@@ -4283,48 +4274,39 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
4283
4274
|
? { submitLabel: guidedQuestionsSubmitLabel }
|
|
4284
4275
|
: {}), className: "h-auto items-stretch justify-stretch bg-transparent" }) })), composerError && (_jsxs("div", { role: "alert", className: "shrink-0 mx-3 mb-1.5 flex items-start gap-2 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive", children: [_jsx(IconAlertTriangle, { className: "h-3.5 w-3.5 mt-0.5 shrink-0" }), _jsx("span", { className: "flex-1 leading-snug", children: composerError }), _jsx("button", { type: "button", "aria-label": t("agentChat.common.dismissError"), onClick: () => setComposerError(null), className: "shrink-0 opacity-70 hover:opacity-100", children: _jsx(IconX, { className: "h-3 w-3" }) })] })), _jsxs("div", { className: "agent-composer-stack", "data-agent-composer-adjacent-ui": hasComposerAccessoryAboveStack
|
|
4285
4276
|
? "true"
|
|
4286
|
-
: undefined, children: [_jsx(SelectionAttachedPill, {}), showPlanModeCallout && (_jsx(PlanModeCallout, { canImplementPlan: canImplementPlan, onImplementPlan: handleImplementPlan, onSwitchToAct: handleSwitchToAct })),
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
}), undefined, true)
|
|
4320
|
-
: undefined, willQueue: engineSetupRequired || isRunning, onSlashCommand: onSlashCommand, execMode: execMode, onExecModeChange: onExecModeChange, planModeDisabled: planModeDisabled, planModeDisabledReason: planModeDisabledReason, selectedModel: selectedModel ?? defaultModel, selectedEffort: selectedEffort, availableModels: availableModels, availableAgents: availableAgents, selectedAgent: selectedAgent, hostedHarness: hostedHarness, modelListLoading: modelListLoading, onModelChange: shouldShowAssistantChatModelSelector(showModelSelector)
|
|
4321
|
-
? onModelChange
|
|
4322
|
-
: undefined, onEffortChange: onEffortChange, onAgentChange: onAgentChange, imageModelMenu: imageModelMenu, onConnectProvider: onConnectProvider, onConnectLocalRuntime: onConnectLocalRuntime, toolbarSlot: composerToolbarSlot, contextItems: visibleComposerContextItems, onRemoveContextItem: removeComposerContextItem, plusMenuMode: plusMenuMode, layoutVariant: composerLayoutVariant, providerConnectStatusEnabled: providerStatusChecksEnabled, voiceEnabled: true, draftScope: composerDraftScope, interceptBuildRequestsForBuilder: true, onAttachmentError: setComposerError, extraActionButton: composerExtraActionButton, stopButton: showRunningInUI ? (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", onClick: () => stopActiveRun({
|
|
4323
|
-
preserveQueuedMessages: true,
|
|
4324
|
-
}), "aria-label": t("agentChat.composer.stopResponse"), "data-agent-composer-slot": "stop-button", className: "shrink-0 flex h-7 w-7 cursor-pointer items-center justify-center rounded-full bg-primary text-primary-foreground shadow-sm transition-opacity hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", children: _jsx(IconPlayerStopFilled, { className: "h-3 w-3" }) }) }), _jsx(TooltipContent, { children: t("agentChat.composer.stopResponse") })] })) : undefined })] })) }), showMissingKeySetup &&
|
|
4325
|
-
!showInlineMissingKeySetup ? (_jsx(PopoverContent, { side: missingApiKeySetupLayout === "sidebar"
|
|
4326
|
-
? "top"
|
|
4327
|
-
: "bottom", align: "center", sideOffset: 8, collisionPadding: 12, "data-agent-native-composer-popover": "true", className: "z-[260] box-border w-[min(calc(100vw-2rem),var(--radix-popover-content-available-width,26rem),26rem)] rounded-lg border-border p-3 shadow-lg", children: _jsx(BuilderSetupContent, { onConnected: handleBuilderConnected, layout: missingApiKeySetupLayout }) })) : null] })] })] }) }) }) }) }) }) }) }) }) }) }));
|
|
4277
|
+
: undefined, children: [_jsx(SelectionAttachedPill, {}), showPlanModeCallout && (_jsx(PlanModeCallout, { canImplementPlan: canImplementPlan, onImplementPlan: handleImplementPlan, onSwitchToAct: handleSwitchToAct })), showMissingKeySetup ? (_jsx(BuilderSetupCard, { fullWidth: true, attached: true, bouncePulse: missingKeyBouncePulse, layout: missingApiKeySetupLayout, onConnected: handleBuilderConnected })) : null, _jsx(AgentComposerFrame, { layoutVariant: composerLayoutVariant, className: cn(composerAreaClassName, showMissingKeySetup &&
|
|
4278
|
+
"agent-composer-area--attached-above", isComposerDisabled &&
|
|
4279
|
+
!showMissingKeySetup &&
|
|
4280
|
+
"opacity-70"), onClick: showMissingKeySetup
|
|
4281
|
+
? bounceMissingKeySetup
|
|
4282
|
+
: undefined, children: _jsxs(_Fragment, { children: [_jsx(ComposerAttachmentPreviewStrip, {}), _jsx(TiptapComposer, { focusRef: tiptapRef, initialText: initialComposerText ?? undefined, initialTextKey: composerDraftScope, onTextChange: isActiveComposer
|
|
4283
|
+
? handleComposerTextChange
|
|
4284
|
+
: undefined, disabled: isComposerDisabled || showMissingKeySetup, placeholder: showMissingKeySetup
|
|
4285
|
+
? t("agentChat.setup.connectPlaceholder")
|
|
4286
|
+
: engineSetupRequired
|
|
4287
|
+
? t("agentChat.setup.connectPlaceholder")
|
|
4288
|
+
: composerDisabled
|
|
4289
|
+
? (composerDisabledPlaceholder ??
|
|
4290
|
+
t("agentChat.composer.openDesktop"))
|
|
4291
|
+
: isRunning
|
|
4292
|
+
? queuedMessages.length > 0
|
|
4293
|
+
? t("agentChat.queue.followUpWithCount", {
|
|
4294
|
+
count: queuedMessages.length,
|
|
4295
|
+
})
|
|
4296
|
+
: t("agentChat.queue.followUp")
|
|
4297
|
+
: resolveAssistantChatComposerPlaceholder(composerPlaceholder), onSubmit: isRunning ||
|
|
4298
|
+
visibleComposerContextItems.length > 0
|
|
4299
|
+
? (text, references, attachments, options) => void addToQueue(text, undefined, references.length > 0
|
|
4300
|
+
? references
|
|
4301
|
+
: undefined, attachments, undefined, resolveAssistantChatSubmitIntent({
|
|
4302
|
+
isRunning,
|
|
4303
|
+
requestedIntent: options?.intent,
|
|
4304
|
+
}), undefined, true)
|
|
4305
|
+
: undefined, willQueue: engineSetupRequired || isRunning, onSlashCommand: onSlashCommand, execMode: execMode, onExecModeChange: onExecModeChange, planModeDisabled: planModeDisabled, planModeDisabledReason: planModeDisabledReason, selectedModel: selectedModel ?? defaultModel, selectedEffort: selectedEffort, availableModels: availableModels, availableAgents: availableAgents, selectedAgent: selectedAgent, hostedHarness: hostedHarness, modelListLoading: modelListLoading, onModelChange: shouldShowAssistantChatModelSelector(showModelSelector)
|
|
4306
|
+
? onModelChange
|
|
4307
|
+
: undefined, onEffortChange: onEffortChange, onAgentChange: onAgentChange, imageModelMenu: imageModelMenu, onConnectProvider: onConnectProvider, onConnectLocalRuntime: onConnectLocalRuntime, toolbarSlot: composerToolbarSlot, contextItems: visibleComposerContextItems, onRemoveContextItem: removeComposerContextItem, plusMenuMode: plusMenuMode, layoutVariant: composerLayoutVariant, providerConnectStatusEnabled: providerStatusChecksEnabled, voiceEnabled: true, draftScope: composerDraftScope, interceptBuildRequestsForBuilder: true, onAttachmentError: setComposerError, extraActionButton: composerExtraActionButton, stopButton: showRunningInUI ? (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", onClick: () => stopActiveRun({
|
|
4308
|
+
preserveQueuedMessages: true,
|
|
4309
|
+
}), "aria-label": t("agentChat.composer.stopResponse"), "data-agent-composer-slot": "stop-button", className: "shrink-0 flex h-7 w-7 cursor-pointer items-center justify-center rounded-full bg-primary text-primary-foreground shadow-sm transition-opacity hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", children: _jsx(IconPlayerStopFilled, { className: "h-3 w-3" }) }) }), _jsx(TooltipContent, { children: t("agentChat.composer.stopResponse") })] })) : undefined })] }) })] })] }) }) }) }) }) }) }) }) }) }) }));
|
|
4328
4310
|
});
|
|
4329
4311
|
export const AssistantChat = forwardRef(function AssistantChat({ apiUrl = agentNativePath("/_agent-native/agent-chat"), tabId, browserTabId, threadId, contextScope, contextNamespace, isActiveComposer, ...props }, ref) {
|
|
4330
4312
|
const modelRef = useRef(props.selectedModel);
|