agent-portal-2 0.1.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/.continue/agents/new-config.yaml +22 -0
- package/AGENT_STEERING.md +36 -0
- package/ARCHITECTURE.md +13 -0
- package/CHANGELOG.md +97 -0
- package/CLI.md +38 -0
- package/CONTRIBUTING.md +55 -0
- package/INSTALLATION.md +58 -0
- package/LICENSE +60 -0
- package/PLUGIN_SYSTEM.md +33 -0
- package/PYTHON_SDK.md +22 -0
- package/QUICKSTART.md +19 -0
- package/README.md +385 -0
- package/RELEASE_NOTES_v0.1.0.md +281 -0
- package/ROADMAP.md +3 -0
- package/RUNTIME.md +44 -0
- package/SAFETY_MODEL.md +24 -0
- package/TESTING.md +35 -0
- package/TROUBLESHOOTING.md +30 -0
- package/UPGRADE_GUIDE.md +288 -0
- package/VS_CODE_EXTENSION.md +47 -0
- package/agent-portal.config.json +20 -0
- package/apps/desktop/agent-portal-desktop.zip +0 -0
- package/apps/desktop/fixtures/local-workflow.html +151 -0
- package/apps/desktop/package.json +18 -0
- package/apps/desktop/src/main.ts +117 -0
- package/apps/desktop/tsconfig.json +8 -0
- package/apps/vscode-extension/LICENSE +60 -0
- package/apps/vscode-extension/README.md +20 -0
- package/apps/vscode-extension/media/agent-portal-logo.png +0 -0
- package/apps/vscode-extension/package.json +149 -0
- package/apps/vscode-extension/src/extension.ts +614 -0
- package/apps/vscode-extension/tsconfig.json +12 -0
- package/assets/branding/agent-portal-logo.png +0 -0
- package/connectors/chatgpt-tools/README.md +9 -0
- package/connectors/claude-mcp-server/README.md +9 -0
- package/connectors/gemini-connector/README.md +9 -0
- package/connectors/rest-websocket-api/README.md +9 -0
- package/docs/MCP_SERVER.md +68 -0
- package/docs/architecture.md +214 -0
- package/docs/roadmap.md +125 -0
- package/package.json +21 -0
- package/packages/agent-portal-mcp/README.md +12 -0
- package/packages/agent-portal-mcp/agent_portal_mcp/__init__.py +3 -0
- package/packages/agent-portal-mcp/agent_portal_mcp/bridge/__init__.py +1 -0
- package/packages/agent-portal-mcp/agent_portal_mcp/bridge/runtime_client.py +180 -0
- package/packages/agent-portal-mcp/agent_portal_mcp/cli.py +32 -0
- package/packages/agent-portal-mcp/agent_portal_mcp/doctor.py +71 -0
- package/packages/agent-portal-mcp/agent_portal_mcp/schemas/__init__.py +1 -0
- package/packages/agent-portal-mcp/agent_portal_mcp/schemas/actions.py +17 -0
- package/packages/agent-portal-mcp/agent_portal_mcp/schemas/results.py +24 -0
- package/packages/agent-portal-mcp/agent_portal_mcp/schemas/risk.py +20 -0
- package/packages/agent-portal-mcp/agent_portal_mcp/security/__init__.py +1 -0
- package/packages/agent-portal-mcp/agent_portal_mcp/security/policy.py +27 -0
- package/packages/agent-portal-mcp/agent_portal_mcp/server.py +148 -0
- package/packages/agent-portal-mcp/agent_portal_mcp/tool_registry.py +58 -0
- package/packages/agent-portal-mcp/agent_portal_mcp/tools/__init__.py +1 -0
- package/packages/agent-portal-mcp/agent_portal_mcp/tools/browser.py +89 -0
- package/packages/agent-portal-mcp/agent_portal_mcp/tools/common.py +98 -0
- package/packages/agent-portal-mcp/agent_portal_mcp/tools/inspection.py +93 -0
- package/packages/agent-portal-mcp/agent_portal_mcp/tools/navigation.py +93 -0
- package/packages/agent-portal-mcp/agent_portal_mcp/tools/reports.py +34 -0
- package/packages/agent-portal-mcp/agent_portal_mcp/tools/steering.py +93 -0
- package/packages/agent-portal-mcp/pyproject.toml +20 -0
- package/packages/agent-portal-mcp/tests/test_doctor.py +20 -0
- package/packages/agent-portal-mcp/tests/test_mcp_server.py +161 -0
- package/packages/core/package.json +15 -0
- package/packages/core/src/index.ts +1842 -0
- package/packages/core/tsconfig.json +8 -0
- package/packages/mcp-server/package.json +15 -0
- package/packages/mcp-server/src/index.ts +73 -0
- package/packages/mcp-server/tsconfig.json +8 -0
- package/packages/sdk/package.json +15 -0
- package/packages/sdk/src/index.ts +544 -0
- package/packages/sdk/tsconfig.json +8 -0
- package/plugins/README.md +16 -0
- package/plugins/agent-portal-browser/plugin.json +19 -0
- package/plugins/agent-portal-python/plugin.json +16 -0
- package/plugins/agent-portal-skills/plugin.json +19 -0
- package/plugins/agent-portal-vscode/plugin.json +27 -0
- package/plugins/example-runtime-plugin/README.md +3 -0
- package/plugins/example-runtime-plugin/plugin.json +20 -0
- package/plugins/plugin.schema.json +53 -0
- package/python/README.md +18 -0
- package/python/agent_portal/__init__.py +5 -0
- package/python/agent_portal/__main__.py +5 -0
- package/python/agent_portal/browser.py +393 -0
- package/python/agent_portal/cli.py +164 -0
- package/python/agent_portal/config.py +31 -0
- package/python/agent_portal/doctor.py +165 -0
- package/python/agent_portal/exceptions.py +39 -0
- package/python/agent_portal/logging_utils.py +33 -0
- package/python/agent_portal/metrics.py +309 -0
- package/python/agent_portal/models.py +160 -0
- package/python/agent_portal/plugin_system.py +42 -0
- package/python/agent_portal/rate_limit.py +253 -0
- package/python/agent_portal/runtime.py +739 -0
- package/python/agent_portal/server.py +351 -0
- package/python/agent_portal/validation.py +299 -0
- package/python/pyproject.toml +29 -0
- package/python/tests/test_config.py +24 -0
- package/python/tests/test_doctor.py +19 -0
- package/python/tests/test_metrics.py +180 -0
- package/python/tests/test_rate_limit.py +237 -0
- package/python/tests/test_runtime.py +122 -0
- package/python/tests/test_server.py +53 -0
- package/python/tests/test_validation.py +170 -0
- package/releases/desktop/agent-portal-desktop/README.md +378 -0
- package/releases/desktop/agent-portal-desktop/RELEASE_NOTES.md +14 -0
- package/releases/desktop/agent-portal-desktop/assets/branding/agent-portal-logo.png +0 -0
- package/releases/desktop/agent-portal-desktop/fixtures/local-workflow.html +151 -0
- package/releases/desktop/agent-portal-desktop/launch-agent-portal.bat +4 -0
- package/releases/desktop/agent-portal-desktop.zip +0 -0
- package/releases/python/agent_portal-0.0.2-py3-none-any.whl +0 -0
- package/releases/python/agent_portal-0.0.2.tar.gz +0 -0
- package/scripts/package_desktop.mjs +117 -0
- package/scripts/release_python.py +46 -0
- package/tests/plugin-manifest.test.mjs +26 -0
- package/tests/runtime.test.mjs +41 -0
- package/tests/vscode-extension.test.mjs +22 -0
- package/tsconfig.base.json +16 -0
|
@@ -0,0 +1,614 @@
|
|
|
1
|
+
import * as net from "node:net";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import * as vscode from "vscode";
|
|
4
|
+
|
|
5
|
+
type ExtensionStatus =
|
|
6
|
+
| "thinking"
|
|
7
|
+
| "waiting-approval"
|
|
8
|
+
| "acting"
|
|
9
|
+
| "paused"
|
|
10
|
+
| "blocked"
|
|
11
|
+
| "finished"
|
|
12
|
+
| "failed"
|
|
13
|
+
| "stopped"
|
|
14
|
+
| "idle";
|
|
15
|
+
|
|
16
|
+
interface ActionQueueItem {
|
|
17
|
+
action_id: string;
|
|
18
|
+
action_type: string;
|
|
19
|
+
target?: string;
|
|
20
|
+
reason: string;
|
|
21
|
+
risk_level: string;
|
|
22
|
+
status: string;
|
|
23
|
+
created_at: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface RuntimeStatusResponse {
|
|
27
|
+
session: {
|
|
28
|
+
runtime_status: string;
|
|
29
|
+
current_goal?: string | null;
|
|
30
|
+
pending_actions: ActionQueueItem[];
|
|
31
|
+
};
|
|
32
|
+
browser: {
|
|
33
|
+
connected: boolean;
|
|
34
|
+
current_url?: string | null;
|
|
35
|
+
page_title?: string | null;
|
|
36
|
+
last_error?: string | null;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface ExtensionState {
|
|
41
|
+
runtimeStatus: ExtensionStatus;
|
|
42
|
+
browserStatus: string;
|
|
43
|
+
currentGoal: string;
|
|
44
|
+
pendingActions: ActionQueueItem[];
|
|
45
|
+
latestReportPath?: string;
|
|
46
|
+
currentUrl?: string;
|
|
47
|
+
pageTitle?: string;
|
|
48
|
+
runtimeUrl: string;
|
|
49
|
+
logs: string[];
|
|
50
|
+
detectedServers: string[];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
class AgentPortalSidebarProvider implements vscode.WebviewViewProvider {
|
|
54
|
+
public static readonly viewType = "agentPortal.sidebar";
|
|
55
|
+
private view?: vscode.WebviewView;
|
|
56
|
+
|
|
57
|
+
constructor(
|
|
58
|
+
private readonly context: vscode.ExtensionContext,
|
|
59
|
+
private readonly store: AgentPortalExtensionStore
|
|
60
|
+
) {}
|
|
61
|
+
|
|
62
|
+
resolveWebviewView(webviewView: vscode.WebviewView): void {
|
|
63
|
+
this.view = webviewView;
|
|
64
|
+
webviewView.webview.options = {
|
|
65
|
+
enableScripts: true
|
|
66
|
+
};
|
|
67
|
+
webviewView.webview.onDidReceiveMessage(async (message: { command: string }) => {
|
|
68
|
+
await vscode.commands.executeCommand(message.command);
|
|
69
|
+
});
|
|
70
|
+
this.render();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
render(): void {
|
|
74
|
+
if (!this.view) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const state = this.store.snapshot();
|
|
79
|
+
const webview = this.view.webview;
|
|
80
|
+
this.view.title = "Control Center";
|
|
81
|
+
webview.html = getSidebarHtml(webview, this.context, state);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
class AgentPortalExtensionStore {
|
|
86
|
+
private terminal?: vscode.Terminal;
|
|
87
|
+
private state: ExtensionState = {
|
|
88
|
+
runtimeStatus: "idle",
|
|
89
|
+
browserStatus: "idle",
|
|
90
|
+
currentGoal: "No active goal",
|
|
91
|
+
pendingActions: [],
|
|
92
|
+
logs: [],
|
|
93
|
+
detectedServers: [],
|
|
94
|
+
runtimeUrl: "http://127.0.0.1:8765"
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
snapshot(): ExtensionState {
|
|
98
|
+
return {
|
|
99
|
+
...this.state,
|
|
100
|
+
pendingActions: [...this.state.pendingActions],
|
|
101
|
+
logs: [...this.state.logs],
|
|
102
|
+
detectedServers: [...this.state.detectedServers]
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
update(partial: Partial<ExtensionState>): void {
|
|
107
|
+
this.state = {
|
|
108
|
+
...this.state,
|
|
109
|
+
...partial
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
log(message: string): void {
|
|
114
|
+
this.state.logs = [message, ...this.state.logs].slice(0, 8);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
setTerminal(terminal?: vscode.Terminal): void {
|
|
118
|
+
this.terminal = terminal;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
getTerminal(): vscode.Terminal | undefined {
|
|
122
|
+
return this.terminal;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function activate(context: vscode.ExtensionContext): void {
|
|
127
|
+
const store = new AgentPortalExtensionStore();
|
|
128
|
+
const sidebar = new AgentPortalSidebarProvider(context, store);
|
|
129
|
+
|
|
130
|
+
const refreshSidebar = async (): Promise<void> => {
|
|
131
|
+
const runtimeUrl = getRuntimeUrl();
|
|
132
|
+
const runtimeStatus = await fetchRuntimeStatus(runtimeUrl);
|
|
133
|
+
const detectedServers = await detectLocalDevelopmentServers();
|
|
134
|
+
|
|
135
|
+
store.update({
|
|
136
|
+
runtimeUrl,
|
|
137
|
+
detectedServers
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
if (runtimeStatus) {
|
|
141
|
+
store.update({
|
|
142
|
+
runtimeStatus: normalizeRuntimeStatus(runtimeStatus.session.runtime_status),
|
|
143
|
+
browserStatus: runtimeStatus.browser.connected ? "connected" : "idle",
|
|
144
|
+
currentGoal: runtimeStatus.session.current_goal ?? "No active goal",
|
|
145
|
+
pendingActions: runtimeStatus.session.pending_actions,
|
|
146
|
+
currentUrl: runtimeStatus.browser.current_url ?? undefined,
|
|
147
|
+
pageTitle: runtimeStatus.browser.page_title ?? undefined
|
|
148
|
+
});
|
|
149
|
+
} else {
|
|
150
|
+
store.update({
|
|
151
|
+
runtimeStatus: "stopped",
|
|
152
|
+
browserStatus: "disconnected",
|
|
153
|
+
pendingActions: []
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
sidebar.render();
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
context.subscriptions.push(
|
|
161
|
+
vscode.window.registerWebviewViewProvider(
|
|
162
|
+
AgentPortalSidebarProvider.viewType,
|
|
163
|
+
sidebar
|
|
164
|
+
),
|
|
165
|
+
vscode.workspace.onDidChangeConfiguration((event) => {
|
|
166
|
+
if (
|
|
167
|
+
event.affectsConfiguration("agentPortal.runtimeUrl") ||
|
|
168
|
+
event.affectsConfiguration("agentPortal.preferredLocalDevPort")
|
|
169
|
+
) {
|
|
170
|
+
void refreshSidebar();
|
|
171
|
+
}
|
|
172
|
+
}),
|
|
173
|
+
vscode.commands.registerCommand("agentPortal.startRuntime", async () => {
|
|
174
|
+
const workspaceRoot = getWorkspaceRoot();
|
|
175
|
+
if (!workspaceRoot) {
|
|
176
|
+
void vscode.window.showErrorMessage(
|
|
177
|
+
"Open the Agent Portal workspace before starting the runtime."
|
|
178
|
+
);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const runtimeUrl = getRuntimeUrl();
|
|
183
|
+
const { host, port } = parseRuntimeUrl(runtimeUrl);
|
|
184
|
+
const terminal =
|
|
185
|
+
store.getTerminal() ??
|
|
186
|
+
vscode.window.createTerminal({
|
|
187
|
+
name: "Agent Portal Runtime",
|
|
188
|
+
cwd: workspaceRoot
|
|
189
|
+
});
|
|
190
|
+
store.setTerminal(terminal);
|
|
191
|
+
terminal.show();
|
|
192
|
+
terminal.sendText(`python -m agent_portal --host ${host} --port ${port} start`);
|
|
193
|
+
store.log(`Starting runtime on ${runtimeUrl}.`);
|
|
194
|
+
await refreshSidebar();
|
|
195
|
+
}),
|
|
196
|
+
vscode.commands.registerCommand("agentPortal.stopRuntime", async () => {
|
|
197
|
+
const response = await postRuntimeCommand("/control/stop");
|
|
198
|
+
if (!response) {
|
|
199
|
+
void vscode.window.showErrorMessage("Runtime is not available to stop.");
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
store.setTerminal(undefined);
|
|
203
|
+
store.log("Stopped runtime.");
|
|
204
|
+
await refreshSidebar();
|
|
205
|
+
}),
|
|
206
|
+
vscode.commands.registerCommand("agentPortal.restartRuntime", async () => {
|
|
207
|
+
const response = await postRuntimeCommand("/control/restart");
|
|
208
|
+
if (!response) {
|
|
209
|
+
void vscode.window.showErrorMessage("Runtime is not available to restart.");
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
store.log("Restarted runtime.");
|
|
213
|
+
await refreshSidebar();
|
|
214
|
+
}),
|
|
215
|
+
vscode.commands.registerCommand("agentPortal.openCurrentProject", async () => {
|
|
216
|
+
const snapshot = store.snapshot();
|
|
217
|
+
const preferredPort =
|
|
218
|
+
vscode.workspace
|
|
219
|
+
.getConfiguration("agentPortal")
|
|
220
|
+
.get<number>("preferredLocalDevPort", 3000) ?? 3000;
|
|
221
|
+
const preferredServer = snapshot.detectedServers.find((server) =>
|
|
222
|
+
server.endsWith(`:${preferredPort}`)
|
|
223
|
+
);
|
|
224
|
+
const target = preferredServer ?? snapshot.currentUrl ?? snapshot.detectedServers[0];
|
|
225
|
+
|
|
226
|
+
if (!target) {
|
|
227
|
+
void vscode.window.showInformationMessage(
|
|
228
|
+
"No local development server or runtime page is available yet."
|
|
229
|
+
);
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
await vscode.env.openExternal(vscode.Uri.parse(target));
|
|
234
|
+
store.log(`Opened ${target}.`);
|
|
235
|
+
sidebar.render();
|
|
236
|
+
}),
|
|
237
|
+
vscode.commands.registerCommand("agentPortal.runAgent", async () => {
|
|
238
|
+
const response = await postRuntimeCommand("/control/resume");
|
|
239
|
+
if (!response) {
|
|
240
|
+
void vscode.window.showErrorMessage("Runtime is not available.");
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
store.log("Resumed agent execution.");
|
|
244
|
+
await refreshSidebar();
|
|
245
|
+
}),
|
|
246
|
+
vscode.commands.registerCommand("agentPortal.pauseAgent", async () => {
|
|
247
|
+
const response = await postRuntimeCommand("/control/pause");
|
|
248
|
+
if (!response) {
|
|
249
|
+
void vscode.window.showErrorMessage("Runtime is not available.");
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
store.log("Paused agent execution.");
|
|
253
|
+
await refreshSidebar();
|
|
254
|
+
}),
|
|
255
|
+
vscode.commands.registerCommand("agentPortal.resumeAgent", async () => {
|
|
256
|
+
const response = await postRuntimeCommand("/control/resume");
|
|
257
|
+
if (!response) {
|
|
258
|
+
void vscode.window.showErrorMessage("Runtime is not available.");
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
store.log("Resumed agent execution.");
|
|
262
|
+
await refreshSidebar();
|
|
263
|
+
}),
|
|
264
|
+
vscode.commands.registerCommand("agentPortal.stopAgent", async () => {
|
|
265
|
+
const response = await postRuntimeCommand("/control/pause");
|
|
266
|
+
if (!response) {
|
|
267
|
+
void vscode.window.showErrorMessage("Runtime is not available.");
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
store.log("Stopped agent execution and left runtime paused.");
|
|
271
|
+
await refreshSidebar();
|
|
272
|
+
}),
|
|
273
|
+
vscode.commands.registerCommand("agentPortal.approveNextAction", async () => {
|
|
274
|
+
const response = await postRuntimeCommand("/control/approve-next");
|
|
275
|
+
if (!response) {
|
|
276
|
+
void vscode.window.showErrorMessage("Runtime is not available.");
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
store.log("Approved next action.");
|
|
280
|
+
await refreshSidebar();
|
|
281
|
+
}),
|
|
282
|
+
vscode.commands.registerCommand("agentPortal.rejectNextAction", async () => {
|
|
283
|
+
const response = await postRuntimeCommand("/control/reject-next", {
|
|
284
|
+
reason: "Rejected from VS Code"
|
|
285
|
+
});
|
|
286
|
+
if (!response) {
|
|
287
|
+
void vscode.window.showErrorMessage("Runtime is not available.");
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
store.log("Rejected next action.");
|
|
291
|
+
await refreshSidebar();
|
|
292
|
+
}),
|
|
293
|
+
vscode.commands.registerCommand("agentPortal.generateReport", async () => {
|
|
294
|
+
const report = await fetchRuntimeReport();
|
|
295
|
+
if (!report?.reportPath) {
|
|
296
|
+
void vscode.window.showInformationMessage(
|
|
297
|
+
"The runtime is not available to generate a report."
|
|
298
|
+
);
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const document = await vscode.workspace.openTextDocument(report.reportPath);
|
|
303
|
+
await vscode.window.showTextDocument(document);
|
|
304
|
+
store.update({ latestReportPath: report.reportPath });
|
|
305
|
+
store.log(`Opened report ${report.reportPath}.`);
|
|
306
|
+
await refreshSidebar();
|
|
307
|
+
}),
|
|
308
|
+
vscode.commands.registerCommand("agentPortal.openDocumentation", async () => {
|
|
309
|
+
const workspaceRoot = getWorkspaceRoot();
|
|
310
|
+
if (!workspaceRoot) {
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const quickstart = path.join(workspaceRoot, "QUICKSTART.md");
|
|
315
|
+
const document = await vscode.workspace.openTextDocument(quickstart);
|
|
316
|
+
await vscode.window.showTextDocument(document);
|
|
317
|
+
store.log("Opened quickstart documentation.");
|
|
318
|
+
sidebar.render();
|
|
319
|
+
}),
|
|
320
|
+
vscode.commands.registerCommand("agentPortal.connectRuntime", async () => {
|
|
321
|
+
await refreshSidebar();
|
|
322
|
+
store.log("Connected to runtime status endpoint.");
|
|
323
|
+
sidebar.render();
|
|
324
|
+
})
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
const interval = setInterval(() => {
|
|
328
|
+
void refreshSidebar();
|
|
329
|
+
}, 5_000);
|
|
330
|
+
context.subscriptions.push({
|
|
331
|
+
dispose: () => clearInterval(interval)
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
void refreshSidebar();
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
export function deactivate(): void {}
|
|
338
|
+
|
|
339
|
+
function getWorkspaceRoot(): string | undefined {
|
|
340
|
+
return vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function getRuntimeUrl(): string {
|
|
344
|
+
return (
|
|
345
|
+
vscode.workspace
|
|
346
|
+
.getConfiguration("agentPortal")
|
|
347
|
+
.get<string>("runtimeUrl", "http://127.0.0.1:8765") ?? "http://127.0.0.1:8765"
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function parseRuntimeUrl(runtimeUrl: string): { host: string; port: number } {
|
|
352
|
+
const parsed = new URL(runtimeUrl);
|
|
353
|
+
return {
|
|
354
|
+
host: parsed.hostname,
|
|
355
|
+
port: parsed.port ? Number(parsed.port) : 8765
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
async function fetchRuntimeStatus(
|
|
360
|
+
runtimeUrl: string
|
|
361
|
+
): Promise<RuntimeStatusResponse | undefined> {
|
|
362
|
+
return fetchJson<RuntimeStatusResponse>(`${runtimeUrl}/status`);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
async function fetchRuntimeReport(): Promise<
|
|
366
|
+
{ reportPath?: string; report?: unknown } | undefined
|
|
367
|
+
> {
|
|
368
|
+
return fetchJson(`${getRuntimeUrl()}/report/latest`);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
async function postRuntimeCommand(
|
|
372
|
+
pathName: string,
|
|
373
|
+
payload?: Record<string, unknown>
|
|
374
|
+
): Promise<unknown | undefined> {
|
|
375
|
+
const runtimeUrl = getRuntimeUrl();
|
|
376
|
+
return fetchJson(`${runtimeUrl}${pathName}`, {
|
|
377
|
+
method: "POST",
|
|
378
|
+
headers: {
|
|
379
|
+
"Content-Type": "application/json"
|
|
380
|
+
},
|
|
381
|
+
body: JSON.stringify(payload ?? {})
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
async function fetchJson<T>(
|
|
386
|
+
url: string,
|
|
387
|
+
init?: RequestInit
|
|
388
|
+
): Promise<T | undefined> {
|
|
389
|
+
try {
|
|
390
|
+
const response = await fetch(url, {
|
|
391
|
+
...init,
|
|
392
|
+
signal: AbortSignal.timeout(2_500)
|
|
393
|
+
});
|
|
394
|
+
if (!response.ok) {
|
|
395
|
+
return undefined;
|
|
396
|
+
}
|
|
397
|
+
return (await response.json()) as T;
|
|
398
|
+
} catch {
|
|
399
|
+
return undefined;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
async function detectLocalDevelopmentServers(): Promise<string[]> {
|
|
404
|
+
const ports = [3000, 5173, 8080, 8000, 5000];
|
|
405
|
+
const checks = await Promise.all(ports.map((port) => isPortOpen(port)));
|
|
406
|
+
|
|
407
|
+
return checks
|
|
408
|
+
.filter((entry): entry is { port: number; open: true } => entry.open)
|
|
409
|
+
.map((entry) => `http://localhost:${entry.port}`);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
async function isPortOpen(
|
|
413
|
+
port: number
|
|
414
|
+
): Promise<{ port: number; open: boolean }> {
|
|
415
|
+
return new Promise((resolve) => {
|
|
416
|
+
const socket = new net.Socket();
|
|
417
|
+
const finish = (open: boolean): void => {
|
|
418
|
+
socket.destroy();
|
|
419
|
+
resolve({ port, open });
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
socket.setTimeout(250);
|
|
423
|
+
socket.once("connect", () => finish(true));
|
|
424
|
+
socket.once("timeout", () => finish(false));
|
|
425
|
+
socket.once("error", () => finish(false));
|
|
426
|
+
socket.connect(port, "127.0.0.1");
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function normalizeRuntimeStatus(status: string): ExtensionStatus {
|
|
431
|
+
switch (status) {
|
|
432
|
+
case "thinking":
|
|
433
|
+
case "waiting-approval":
|
|
434
|
+
case "acting":
|
|
435
|
+
case "paused":
|
|
436
|
+
case "blocked":
|
|
437
|
+
case "finished":
|
|
438
|
+
case "failed":
|
|
439
|
+
case "stopped":
|
|
440
|
+
return status;
|
|
441
|
+
default:
|
|
442
|
+
return "idle";
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
function getSidebarHtml(
|
|
447
|
+
webview: vscode.Webview,
|
|
448
|
+
context: vscode.ExtensionContext,
|
|
449
|
+
state: ExtensionState
|
|
450
|
+
): string {
|
|
451
|
+
const logoUri = webview.asWebviewUri(
|
|
452
|
+
vscode.Uri.joinPath(context.extensionUri, "media", "agent-portal-logo.png")
|
|
453
|
+
);
|
|
454
|
+
const pendingCards =
|
|
455
|
+
state.pendingActions.length > 0
|
|
456
|
+
? state.pendingActions
|
|
457
|
+
.map(
|
|
458
|
+
(action) => `
|
|
459
|
+
<article class="card action">
|
|
460
|
+
<div class="label">${action.action_type}</div>
|
|
461
|
+
<strong>${escapeHtml(action.target ?? "No target")}</strong>
|
|
462
|
+
<p>${escapeHtml(action.reason)}</p>
|
|
463
|
+
<span class="badge risk-${action.risk_level}">${action.risk_level}</span>
|
|
464
|
+
</article>
|
|
465
|
+
`
|
|
466
|
+
)
|
|
467
|
+
.join("")
|
|
468
|
+
: `<article class="card empty"><strong>No pending actions</strong><p>The queue is currently clear.</p></article>`;
|
|
469
|
+
|
|
470
|
+
const logs = state.logs.map((log) => `<li>${escapeHtml(log)}</li>`).join("");
|
|
471
|
+
const servers = state.detectedServers
|
|
472
|
+
.map((server) => `<li>${escapeHtml(server)}</li>`)
|
|
473
|
+
.join("");
|
|
474
|
+
|
|
475
|
+
return `<!DOCTYPE html>
|
|
476
|
+
<html lang="en">
|
|
477
|
+
<head>
|
|
478
|
+
<meta charset="UTF-8" />
|
|
479
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
480
|
+
<style>
|
|
481
|
+
body {
|
|
482
|
+
font-family: "Segoe UI", sans-serif;
|
|
483
|
+
padding: 12px;
|
|
484
|
+
color: var(--vscode-foreground);
|
|
485
|
+
}
|
|
486
|
+
.hero {
|
|
487
|
+
display: grid;
|
|
488
|
+
gap: 10px;
|
|
489
|
+
margin-bottom: 16px;
|
|
490
|
+
}
|
|
491
|
+
.hero img {
|
|
492
|
+
width: 100%;
|
|
493
|
+
border-radius: 16px;
|
|
494
|
+
}
|
|
495
|
+
.status-row {
|
|
496
|
+
display: grid;
|
|
497
|
+
grid-template-columns: 1fr 1fr;
|
|
498
|
+
gap: 10px;
|
|
499
|
+
}
|
|
500
|
+
.card {
|
|
501
|
+
border: 1px solid var(--vscode-panel-border);
|
|
502
|
+
border-radius: 12px;
|
|
503
|
+
padding: 12px;
|
|
504
|
+
background: color-mix(in srgb, var(--vscode-editor-background) 92%, white 8%);
|
|
505
|
+
}
|
|
506
|
+
.action {
|
|
507
|
+
margin-bottom: 10px;
|
|
508
|
+
}
|
|
509
|
+
.label {
|
|
510
|
+
font-size: 11px;
|
|
511
|
+
text-transform: uppercase;
|
|
512
|
+
letter-spacing: 0.08em;
|
|
513
|
+
opacity: 0.7;
|
|
514
|
+
}
|
|
515
|
+
.badge {
|
|
516
|
+
display: inline-block;
|
|
517
|
+
margin-top: 8px;
|
|
518
|
+
padding: 4px 8px;
|
|
519
|
+
border-radius: 999px;
|
|
520
|
+
font-size: 11px;
|
|
521
|
+
text-transform: uppercase;
|
|
522
|
+
}
|
|
523
|
+
.risk-safe, .risk-low {
|
|
524
|
+
background: rgba(60, 200, 140, 0.2);
|
|
525
|
+
}
|
|
526
|
+
.risk-medium {
|
|
527
|
+
background: rgba(255, 194, 64, 0.24);
|
|
528
|
+
}
|
|
529
|
+
.risk-high, .risk-blocked {
|
|
530
|
+
background: rgba(255, 90, 90, 0.24);
|
|
531
|
+
}
|
|
532
|
+
.controls {
|
|
533
|
+
display: grid;
|
|
534
|
+
grid-template-columns: 1fr 1fr;
|
|
535
|
+
gap: 8px;
|
|
536
|
+
}
|
|
537
|
+
button {
|
|
538
|
+
border: 0;
|
|
539
|
+
border-radius: 10px;
|
|
540
|
+
padding: 10px;
|
|
541
|
+
color: white;
|
|
542
|
+
cursor: pointer;
|
|
543
|
+
background: linear-gradient(90deg, #179cff, #ff9b21);
|
|
544
|
+
}
|
|
545
|
+
ul {
|
|
546
|
+
margin: 8px 0 0;
|
|
547
|
+
padding-left: 18px;
|
|
548
|
+
}
|
|
549
|
+
</style>
|
|
550
|
+
</head>
|
|
551
|
+
<body>
|
|
552
|
+
<section class="hero">
|
|
553
|
+
<img src="${logoUri}" alt="Agent Portal" />
|
|
554
|
+
<div class="card">
|
|
555
|
+
<div class="label">Current Goal</div>
|
|
556
|
+
<strong>${escapeHtml(state.currentGoal)}</strong>
|
|
557
|
+
<p>${escapeHtml(state.runtimeUrl)}</p>
|
|
558
|
+
</div>
|
|
559
|
+
</section>
|
|
560
|
+
<section class="status-row">
|
|
561
|
+
<div class="card">
|
|
562
|
+
<div class="label">Runtime</div>
|
|
563
|
+
<strong>${escapeHtml(state.runtimeStatus)}</strong>
|
|
564
|
+
</div>
|
|
565
|
+
<div class="card">
|
|
566
|
+
<div class="label">Browser</div>
|
|
567
|
+
<strong>${escapeHtml(state.browserStatus)}</strong>
|
|
568
|
+
</div>
|
|
569
|
+
</section>
|
|
570
|
+
<section class="card">
|
|
571
|
+
<div class="label">Current Page</div>
|
|
572
|
+
<strong>${escapeHtml(state.pageTitle ?? "No page open")}</strong>
|
|
573
|
+
<p>${escapeHtml(state.currentUrl ?? "No active browser session")}</p>
|
|
574
|
+
</section>
|
|
575
|
+
<section class="card">
|
|
576
|
+
<div class="label">Pending Action Queue</div>
|
|
577
|
+
${pendingCards}
|
|
578
|
+
</section>
|
|
579
|
+
<section class="controls">
|
|
580
|
+
<button onclick="post('agentPortal.startRuntime')">Start Runtime</button>
|
|
581
|
+
<button onclick="post('agentPortal.connectRuntime')">Connect Runtime</button>
|
|
582
|
+
<button onclick="post('agentPortal.pauseAgent')">Pause Agent</button>
|
|
583
|
+
<button onclick="post('agentPortal.resumeAgent')">Resume Agent</button>
|
|
584
|
+
<button onclick="post('agentPortal.approveNextAction')">Approve Next</button>
|
|
585
|
+
<button onclick="post('agentPortal.rejectNextAction')">Reject Next</button>
|
|
586
|
+
<button onclick="post('agentPortal.generateReport')">Open Report</button>
|
|
587
|
+
<button onclick="post('agentPortal.restartRuntime')">Restart Runtime</button>
|
|
588
|
+
</section>
|
|
589
|
+
<section class="card">
|
|
590
|
+
<div class="label">Detected Local Servers</div>
|
|
591
|
+
<ul>${servers || "<li>None detected</li>"}</ul>
|
|
592
|
+
</section>
|
|
593
|
+
<section class="card">
|
|
594
|
+
<div class="label">Recent Logs</div>
|
|
595
|
+
<ul>${logs || "<li>No recent extension activity</li>"}</ul>
|
|
596
|
+
</section>
|
|
597
|
+
<script>
|
|
598
|
+
const vscode = acquireVsCodeApi();
|
|
599
|
+
function post(command) {
|
|
600
|
+
vscode.postMessage({ command });
|
|
601
|
+
}
|
|
602
|
+
</script>
|
|
603
|
+
</body>
|
|
604
|
+
</html>`;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
function escapeHtml(value: string): string {
|
|
608
|
+
return value
|
|
609
|
+
.replaceAll("&", "&")
|
|
610
|
+
.replaceAll("<", "<")
|
|
611
|
+
.replaceAll(">", ">")
|
|
612
|
+
.replaceAll('"', """)
|
|
613
|
+
.replaceAll("'", "'");
|
|
614
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# ChatGPT Tools Connector
|
|
2
|
+
|
|
3
|
+
This connector is intended to expose Agent Portal runtime controls to ChatGPT-style tool environments.
|
|
4
|
+
|
|
5
|
+
## Planned Responsibilities
|
|
6
|
+
|
|
7
|
+
- map tool calls to runtime actions
|
|
8
|
+
- expose queue, report, and steering endpoints
|
|
9
|
+
- surface approval-required actions cleanly
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Claude MCP Server Connector
|
|
2
|
+
|
|
3
|
+
This connector is intended to provide an MCP server wrapper around Agent Portal.
|
|
4
|
+
|
|
5
|
+
## Planned Responsibilities
|
|
6
|
+
|
|
7
|
+
- expose runtime tools over MCP
|
|
8
|
+
- surface reports and memory records
|
|
9
|
+
- bridge agent steering controls to Claude workflows
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# REST And WebSocket API
|
|
2
|
+
|
|
3
|
+
This connector represents the generic transport layer for Agent Portal.
|
|
4
|
+
|
|
5
|
+
## Planned Responsibilities
|
|
6
|
+
|
|
7
|
+
- REST endpoints for runtime state and reports
|
|
8
|
+
- WebSocket streaming for queue, browser, and steering updates
|
|
9
|
+
- stable integration surface for external agents and tools
|