@zhanguym/chatroom 1.0.0 → 1.1.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app/application.d.ts +0 -1
- package/dist/app/application.js +8 -7
- package/dist/app/event-bus.d.ts +13 -10
- package/dist/app/git-access.d.ts +50 -0
- package/dist/app/git-access.js +2 -0
- package/dist/app/service-registry.d.ts +11 -0
- package/dist/app/service-registry.js +17 -0
- package/dist/app/workspace-access.d.ts +24 -0
- package/dist/app/workspace-access.js +2 -0
- package/dist/auth/ingress-policy.d.ts +2 -0
- package/dist/auth/ingress-policy.js +14 -2
- package/dist/auth/passkey-service.js +1 -1
- package/dist/cli/run-cli.js +1 -1
- package/dist/core/fs/safe-directory.d.ts +9 -0
- package/dist/core/fs/safe-directory.js +129 -0
- package/dist/core/operations/repository.d.ts +2 -0
- package/dist/core/runtime/line-process.d.ts +19 -0
- package/dist/core/runtime/line-process.js +51 -0
- package/dist/infrastructure/database/operation-repository.js +8 -2
- package/dist/infrastructure/database/schema.d.ts +1 -1
- package/dist/infrastructure/database/schema.js +62 -0
- package/dist/infrastructure/http/http-server.js +1 -1
- package/dist/native/linux/chatroom-computer-helper +0 -0
- package/dist/native/macos/ChatRoomComputerHelper.app/Contents/MacOS/chatroom-computer-helper +0 -0
- package/dist/native/windows/chatroom-computer-helper.exe +0 -0
- package/dist/operations/operation-log.js +16 -4
- package/dist/plugins/agent/async-queue.d.ts +10 -0
- package/dist/plugins/agent/async-queue.js +49 -0
- package/dist/plugins/agent/mcp.d.ts +3 -0
- package/dist/plugins/agent/mcp.js +502 -0
- package/dist/plugins/agent/plugin.d.ts +4 -0
- package/dist/plugins/agent/plugin.js +35 -0
- package/dist/plugins/agent/provider.d.ts +132 -0
- package/dist/plugins/agent/providers/codex/mapping.d.ts +56 -0
- package/dist/plugins/agent/providers/codex/mapping.js +427 -0
- package/dist/plugins/agent/providers/codex/provider.d.ts +84 -0
- package/dist/plugins/agent/providers/codex/provider.js +828 -0
- package/dist/plugins/agent/providers/codex/rpc.d.ts +40 -0
- package/dist/plugins/agent/providers/codex/rpc.js +227 -0
- package/dist/plugins/agent/providers/common.d.ts +11 -0
- package/dist/plugins/agent/providers/common.js +75 -0
- package/dist/plugins/agent/providers/index.d.ts +3 -0
- package/dist/plugins/agent/providers/index.js +4 -0
- package/dist/plugins/agent/repository.d.ts +21 -0
- package/dist/plugins/agent/repository.js +226 -0
- package/dist/plugins/agent/runtime-config.d.ts +5 -0
- package/dist/plugins/agent/runtime-config.js +60 -0
- package/dist/plugins/agent/runtime-files.d.ts +8 -0
- package/dist/plugins/agent/runtime-files.js +170 -0
- package/dist/plugins/agent/runtime-state.d.ts +17 -0
- package/dist/plugins/agent/runtime-state.js +133 -0
- package/dist/plugins/agent/runtime.d.ts +60 -0
- package/dist/plugins/agent/runtime.js +928 -0
- package/dist/plugins/agent/store.d.ts +17 -0
- package/dist/plugins/agent/store.js +1 -0
- package/dist/plugins/agent/types.d.ts +320 -0
- package/dist/plugins/agent/types.js +13 -0
- package/dist/plugins/cloud/plugin.d.ts +1 -1
- package/dist/plugins/cloud/plugin.js +3 -3
- package/dist/plugins/cloud/tunnel-client.js +6 -7
- package/dist/plugins/computer/computer-schemas.d.ts +7 -7
- package/dist/plugins/computer/computer-service.d.ts +3 -0
- package/dist/plugins/computer/computer-service.js +33 -1
- package/dist/plugins/computer/plugin.d.ts +1 -1
- package/dist/plugins/computer/plugin.js +1 -1
- package/dist/plugins/git/git-service.d.ts +3 -2
- package/dist/plugins/git/git-service.js +37 -1
- package/dist/plugins/git/plugin.d.ts +0 -2
- package/dist/plugins/git/plugin.js +3 -3
- package/dist/plugins/process/plugin.d.ts +1 -1
- package/dist/plugins/process/plugin.js +3 -3
- package/dist/plugins/process/process-supervisor.d.ts +1 -0
- package/dist/plugins/process/process-supervisor.js +15 -4
- package/dist/plugins/types.d.ts +1 -11
- package/dist/plugins/types.js +1 -17
- package/dist/plugins/web/api-types.d.ts +20 -5
- package/dist/plugins/web/http/agent-api-router.d.ts +3 -0
- package/dist/plugins/web/http/agent-api-router.js +254 -0
- package/dist/plugins/web/http/api-router.js +9 -0
- package/dist/plugins/web/http/computer-api-router.js +21 -6
- package/dist/plugins/web/http/process-api-router.js +3 -0
- package/dist/plugins/web/http/workspace-api-router.js +79 -6
- package/dist/plugins/web/plugin.d.ts +1 -1
- package/dist/plugins/web/plugin.js +5 -4
- package/dist/plugins/web/runtime.d.ts +6 -3
- package/dist/plugins/web/runtime.js +6 -1
- package/dist/plugins/workspace/mcp.js +16 -0
- package/dist/plugins/workspace/metadata.d.ts +1 -1
- package/dist/plugins/workspace/plugin.d.ts +1 -1
- package/dist/plugins/workspace/plugin.js +5 -2
- package/dist/plugins/workspace/types.d.ts +1 -18
- package/dist/plugins/workspace/workspace-fs.d.ts +9 -3
- package/dist/plugins/workspace/workspace-fs.js +78 -125
- package/dist/plugins/workspace/workspace-root-policy.d.ts +9 -0
- package/dist/plugins/workspace/workspace-root-policy.js +56 -0
- package/dist/plugins/workspace/workspace-service.d.ts +11 -6
- package/dist/plugins/workspace/workspace-service.js +39 -49
- package/dist/web/assets/AgentView-DKuspiki.js +4 -0
- package/dist/web/assets/AgentView-DYJeMJLX.css +1 -0
- package/dist/web/assets/{CloudView-CHjVbp-5.css → CloudView-DVVvwuWW.css} +1 -1
- package/dist/web/assets/CloudView-PbREbJgz.js +1 -0
- package/dist/web/assets/CodeViewer.vue_vue_type_script_setup_true_lang-CiFNEpWo.js +3 -0
- package/dist/web/assets/ComputerView-BM0u3wSF.css +1 -0
- package/dist/web/assets/ComputerView-BRb2sE0Z.js +1 -0
- package/dist/web/assets/McpToolsView-B8NSFdf4.js +1 -0
- package/dist/web/assets/McpToolsView-C22ZbSuV.css +1 -0
- package/dist/web/assets/OperationsView-BjTY2lIh.css +1 -0
- package/dist/web/assets/OperationsView-Cavi-6SZ.js +4 -0
- package/dist/web/assets/ProcessesView-Ce6iVwjA.css +1 -0
- package/dist/web/assets/ProcessesView-UO48Nh2q.js +4 -0
- package/dist/web/assets/StateChip.vue_vue_type_script_setup_true_lang-Bvc9wRyA.js +1 -0
- package/dist/web/assets/SystemLogsView-BXmOrmu5.js +1 -0
- package/dist/web/assets/SystemLogsView-CofP4Jvi.css +1 -0
- package/dist/web/assets/VSelect-BPRXx8KM.js +1 -0
- package/dist/web/assets/VSpacer-Cl-QiwaM.js +1 -0
- package/dist/web/assets/VSwitch-C0XNj1lR.js +1 -0
- package/dist/web/assets/{VTabs-qBcuuXh3.js → VTabs-CHj39Ket.js} +1 -1
- package/dist/web/assets/VTextarea-BIcUWOU6.js +16 -0
- package/dist/web/assets/VTextarea-DG1zE1as.css +1 -0
- package/dist/web/assets/WorkspacesView-DPCt6kYy.css +1 -0
- package/dist/web/assets/WorkspacesView-rY6RXeiR.js +2 -0
- package/dist/web/assets/entity-refresh-queue-wQii8Dl5.js +1 -0
- package/dist/web/assets/{index-CkjqXRSj.css → index-BPpl3-sP.css} +1 -1
- package/dist/web/assets/index-Dt-pWiW1.js +19 -0
- package/dist/web/assets/{syntax-highlight-B5ArnyrC.js → syntax-highlight-C_fuMcwC.js} +1 -1
- package/dist/web/assets/useMasterDetailFocus-D0j-KcXv.js +1 -0
- package/dist/web/assets/useOperationLabels-DR8r6Vrl.js +1 -0
- package/dist/web/index.html +2 -2
- package/package.json +4 -2
- package/dist/plugins/git/types.d.ts +0 -32
- package/dist/web/assets/CloudView-CvX0dkgu.js +0 -1
- package/dist/web/assets/CodeViewer.vue_vue_type_script_setup_true_lang-B5TxGkUW.js +0 -3
- package/dist/web/assets/ComputerView-91Z9Cefw.css +0 -1
- package/dist/web/assets/ComputerView-OOyEqWml.js +0 -1
- package/dist/web/assets/McpToolsView-BTnolyS6.css +0 -1
- package/dist/web/assets/McpToolsView-DjXWTKrZ.js +0 -1
- package/dist/web/assets/OperationsView-DqTY51Cr.js +0 -4
- package/dist/web/assets/ProcessesView-C13BykIa.js +0 -4
- package/dist/web/assets/StateChip.vue_vue_type_script_setup_true_lang-DyOhJmR6.js +0 -1
- package/dist/web/assets/SystemLogsView-Ci0u4BTb.js +0 -1
- package/dist/web/assets/VSelect-DrRpKyYI.js +0 -1
- package/dist/web/assets/VSpacer-C3wAY3F0.js +0 -1
- package/dist/web/assets/VSwitch-CxusR4iO.js +0 -1
- package/dist/web/assets/WorkspacesView-BzQ0aH74.css +0 -1
- package/dist/web/assets/WorkspacesView-CijIvfsp.js +0 -2
- package/dist/web/assets/index-BegKQ3Yd.js +0 -19
- package/dist/web/assets/useOperationLabels-BDjzGDkT.js +0 -1
- /package/dist/{plugins/process → core/runtime}/head-tail-buffer.d.ts +0 -0
- /package/dist/{plugins/process → core/runtime}/head-tail-buffer.js +0 -0
- /package/dist/plugins/{git/types.js → agent/provider.js} +0 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { ChatRoomError } from "#core/errors/chatroom-error";
|
|
2
|
+
export function emptyAgentSessionConfig() {
|
|
3
|
+
return {
|
|
4
|
+
model: null,
|
|
5
|
+
reasoningEffort: null,
|
|
6
|
+
reasoningSummary: null,
|
|
7
|
+
serviceTier: null,
|
|
8
|
+
approvalPolicy: null,
|
|
9
|
+
approvalsReviewer: null,
|
|
10
|
+
permissionMode: null,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export function copyAgentSessionConfig(config) {
|
|
14
|
+
return {
|
|
15
|
+
model: config.model,
|
|
16
|
+
reasoningEffort: config.reasoningEffort,
|
|
17
|
+
reasoningSummary: config.reasoningSummary,
|
|
18
|
+
serviceTier: config.serviceTier,
|
|
19
|
+
approvalPolicy: config.approvalPolicy,
|
|
20
|
+
approvalsReviewer: config.approvalsReviewer,
|
|
21
|
+
permissionMode: config.permissionMode,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export function approvalPolicySupported(value, setting) {
|
|
25
|
+
if (value === null)
|
|
26
|
+
return false;
|
|
27
|
+
if (typeof value === "string")
|
|
28
|
+
return setting.options.some((option) => option.id === value);
|
|
29
|
+
if (!setting.options.some((option) => option.id === "granular"))
|
|
30
|
+
return false;
|
|
31
|
+
const granular = value.granular;
|
|
32
|
+
return (typeof granular.sandbox_approval === "boolean" &&
|
|
33
|
+
typeof granular.rules === "boolean" &&
|
|
34
|
+
typeof granular.skill_approval === "boolean" &&
|
|
35
|
+
typeof granular.request_permissions === "boolean" &&
|
|
36
|
+
typeof granular.mcp_elicitations === "boolean");
|
|
37
|
+
}
|
|
38
|
+
export function validateReviewTarget(target) {
|
|
39
|
+
if (!target || typeof target !== "object")
|
|
40
|
+
throw new ChatRoomError("INVALID_INPUT", "Invalid review target");
|
|
41
|
+
switch (target.type) {
|
|
42
|
+
case "uncommittedChanges":
|
|
43
|
+
return;
|
|
44
|
+
case "baseBranch":
|
|
45
|
+
if (typeof target.branch === "string" && target.branch.trim())
|
|
46
|
+
return;
|
|
47
|
+
break;
|
|
48
|
+
case "commit":
|
|
49
|
+
if (typeof target.sha === "string" &&
|
|
50
|
+
target.sha.trim() &&
|
|
51
|
+
(target.title === null || typeof target.title === "string"))
|
|
52
|
+
return;
|
|
53
|
+
break;
|
|
54
|
+
case "custom":
|
|
55
|
+
if (typeof target.instructions === "string" && target.instructions.trim())
|
|
56
|
+
return;
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
throw new ChatRoomError("INVALID_INPUT", "Invalid review target");
|
|
60
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ProviderItem } from "./provider.js";
|
|
2
|
+
import type { AgentStore } from "./store.js";
|
|
3
|
+
import type { AgentHistory, AgentSession, AgentTurnInput } from "./types.js";
|
|
4
|
+
export declare function sessionAttachmentPrefix(sessionId: string): string;
|
|
5
|
+
export declare function cleanupSessionFiles(store: AgentStore, history: AgentHistory): Promise<void>;
|
|
6
|
+
export declare function cleanupOrphanAttachments(store: AgentStore, maxAgeMs: number): Promise<void>;
|
|
7
|
+
export declare function prepareProviderItem(session: AgentSession, item: ProviderItem): Promise<ProviderItem>;
|
|
8
|
+
export declare function validateAttachments(session: AgentSession, attachments: AgentTurnInput["attachments"]): Promise<AgentTurnInput["attachments"]>;
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { readdir, readFile, stat } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { ChatRoomError } from "#core/errors/chatroom-error";
|
|
5
|
+
import { removeFileWithin, resolveReadableFileWithin, writeBytesAtomicWithin, } from "#core/fs/safe-directory";
|
|
6
|
+
import { runtimeErrorMessage } from "./runtime-state.js";
|
|
7
|
+
export function sessionAttachmentPrefix(sessionId) {
|
|
8
|
+
return ".chatroom/agent-attachments/" + sessionId + "/";
|
|
9
|
+
}
|
|
10
|
+
export async function cleanupSessionFiles(store, history) {
|
|
11
|
+
const workspaceRoot = history.session.workspaceRoot;
|
|
12
|
+
const retained = referencedManagedFiles(store, workspaceRoot, history.session.id);
|
|
13
|
+
const attachmentPrefix = sessionAttachmentPrefix(history.session.id);
|
|
14
|
+
const attachmentDirectory = path.join(workspaceRoot, attachmentPrefix);
|
|
15
|
+
try {
|
|
16
|
+
for (const file of await readdir(attachmentDirectory)) {
|
|
17
|
+
const filePath = attachmentPrefix + file;
|
|
18
|
+
if (retained.has(filePath))
|
|
19
|
+
continue;
|
|
20
|
+
await removeFileWithin(workspaceRoot, filePath).catch(() => undefined);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// The session may not have uploaded local attachments.
|
|
25
|
+
}
|
|
26
|
+
for (const filePath of sessionArtifactFiles(history)) {
|
|
27
|
+
if (retained.has(filePath))
|
|
28
|
+
continue;
|
|
29
|
+
await removeFileWithin(workspaceRoot, filePath).catch(() => undefined);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export async function cleanupOrphanAttachments(store, maxAgeMs) {
|
|
33
|
+
const now = Date.now();
|
|
34
|
+
const workspaceRoots = new Set(store.listSessions().map((session) => session.workspaceRoot));
|
|
35
|
+
for (const workspaceRoot of workspaceRoots) {
|
|
36
|
+
await removeUnreferencedAttachmentFiles(workspaceRoot, referencedManagedFiles(store, workspaceRoot), now, maxAgeMs);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export async function prepareProviderItem(session, item) {
|
|
40
|
+
if (item.type !== "artifact")
|
|
41
|
+
return item;
|
|
42
|
+
try {
|
|
43
|
+
if (!path.isAbsolute(item.artifact.path)) {
|
|
44
|
+
return {
|
|
45
|
+
...item,
|
|
46
|
+
artifact: {
|
|
47
|
+
...item.artifact,
|
|
48
|
+
path: (await resolveReadableFileWithin(session.workspaceRoot, item.artifact.path)).relative,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
if (item.artifact.kind !== "image")
|
|
53
|
+
throw new ChatRoomError("FORBIDDEN", "External Agent artifacts must be imported explicitly");
|
|
54
|
+
const info = await stat(item.artifact.path);
|
|
55
|
+
if (!info.isFile())
|
|
56
|
+
throw new ChatRoomError("INVALID_INPUT", "Agent artifact is not a file");
|
|
57
|
+
if (info.size > 32 * 1024 * 1024)
|
|
58
|
+
throw new ChatRoomError("INVALID_INPUT", "Agent artifact is too large");
|
|
59
|
+
const extension = path.extname(item.artifact.name).slice(0, 16);
|
|
60
|
+
const target = ".chatroom/agent-artifacts/" + randomUUID() + extension.toLowerCase();
|
|
61
|
+
const content = await readFile(item.artifact.path);
|
|
62
|
+
if (content.byteLength > 32 * 1024 * 1024)
|
|
63
|
+
throw new ChatRoomError("INVALID_INPUT", "Agent artifact is too large");
|
|
64
|
+
await writeBytesAtomicWithin(session.workspaceRoot, target, content);
|
|
65
|
+
return {
|
|
66
|
+
...item,
|
|
67
|
+
artifact: {
|
|
68
|
+
...item.artifact,
|
|
69
|
+
path: target,
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
return {
|
|
75
|
+
type: "notice",
|
|
76
|
+
level: "warning",
|
|
77
|
+
text: "Agent artifact could not be imported: " + runtimeErrorMessage(error),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
export async function validateAttachments(session, attachments) {
|
|
82
|
+
return Promise.all(attachments.map(async (attachment) => {
|
|
83
|
+
const resolved = await resolveReadableFileWithin(session.workspaceRoot, attachment.path);
|
|
84
|
+
if (resolved.relative.startsWith(".chatroom/agent-attachments/") &&
|
|
85
|
+
!resolved.relative.startsWith(sessionAttachmentPrefix(session.id)))
|
|
86
|
+
throw new ChatRoomError("FORBIDDEN", "Agent attachment belongs to another session");
|
|
87
|
+
return {
|
|
88
|
+
...attachment,
|
|
89
|
+
path: resolved.relative,
|
|
90
|
+
};
|
|
91
|
+
}));
|
|
92
|
+
}
|
|
93
|
+
function referencedManagedFiles(store, workspaceRoot, excludedSessionId) {
|
|
94
|
+
const paths = new Set();
|
|
95
|
+
for (const session of store.listSessions()) {
|
|
96
|
+
if (session.id === excludedSessionId)
|
|
97
|
+
continue;
|
|
98
|
+
if (session.workspaceRoot !== workspaceRoot)
|
|
99
|
+
continue;
|
|
100
|
+
const history = store.history(session.id);
|
|
101
|
+
if (!history)
|
|
102
|
+
continue;
|
|
103
|
+
for (const filePath of managedFilesInHistory(history))
|
|
104
|
+
paths.add(filePath);
|
|
105
|
+
}
|
|
106
|
+
return paths;
|
|
107
|
+
}
|
|
108
|
+
async function removeUnreferencedAttachmentFiles(workspaceRoot, referenced, now, maxAgeMs) {
|
|
109
|
+
const root = path.join(workspaceRoot, ".chatroom/agent-attachments");
|
|
110
|
+
let sessions;
|
|
111
|
+
try {
|
|
112
|
+
sessions = await readdir(root);
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
for (const sessionId of sessions) {
|
|
118
|
+
const dir = path.join(root, sessionId);
|
|
119
|
+
let files;
|
|
120
|
+
try {
|
|
121
|
+
files = await readdir(dir);
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
for (const file of files) {
|
|
127
|
+
const relative = sessionAttachmentPrefix(sessionId) + file;
|
|
128
|
+
if (referenced.has(relative))
|
|
129
|
+
continue;
|
|
130
|
+
try {
|
|
131
|
+
const info = await stat(path.join(dir, file));
|
|
132
|
+
if (info.isFile() && now - info.mtimeMs > maxAgeMs)
|
|
133
|
+
await removeFileWithin(workspaceRoot, relative);
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
// Ignore races with uploads/deletions.
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function managedFilesInHistory(history) {
|
|
142
|
+
const paths = new Set();
|
|
143
|
+
for (const entry of history.turns) {
|
|
144
|
+
for (const item of entry.items) {
|
|
145
|
+
if (item.type === "user_message") {
|
|
146
|
+
for (const attachment of item.attachments) {
|
|
147
|
+
if (attachment.path.startsWith(".chatroom/agent-attachments/") ||
|
|
148
|
+
attachment.path.startsWith(".chatroom/agent-artifacts/"))
|
|
149
|
+
paths.add(attachment.path);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
else if (item.type === "artifact" &&
|
|
153
|
+
item.artifact.path.startsWith(".chatroom/agent-artifacts/")) {
|
|
154
|
+
paths.add(item.artifact.path);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return paths;
|
|
159
|
+
}
|
|
160
|
+
function sessionArtifactFiles(history) {
|
|
161
|
+
const paths = new Set();
|
|
162
|
+
for (const entry of history.turns) {
|
|
163
|
+
for (const item of entry.items) {
|
|
164
|
+
if (item.type === "artifact" &&
|
|
165
|
+
item.artifact.path.startsWith(".chatroom/agent-artifacts/"))
|
|
166
|
+
paths.add(item.artifact.path);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return paths;
|
|
170
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ProviderItem } from "./provider.js";
|
|
2
|
+
import type { AgentError, AgentHistory, AgentInputQuestion, AgentInteractionOption, AgentItem } from "./types.js";
|
|
3
|
+
export declare function nextSessionUpdatedAt(current: string): string;
|
|
4
|
+
export declare function materializeProviderItem(turnId: string, providerItemId: string, sequence: number, status: AgentItem["status"], item: ProviderItem, existing?: AgentItem): AgentItem;
|
|
5
|
+
export declare function materializeInteraction(turnId: string, providerItemId: string, sequence: number, interaction: {
|
|
6
|
+
id: string;
|
|
7
|
+
kind: "approval" | "input";
|
|
8
|
+
title: string;
|
|
9
|
+
description: string | null;
|
|
10
|
+
questions: AgentInputQuestion[];
|
|
11
|
+
approvalOptions: AgentInteractionOption[];
|
|
12
|
+
}): AgentItem;
|
|
13
|
+
export declare function applyItemDelta(item: AgentItem, field: "text" | "output", delta: string): AgentItem;
|
|
14
|
+
export declare function buildContinuityDelta(history: AgentHistory, afterTurnId: string | null): string;
|
|
15
|
+
export declare function mergeContinuityContext(existing: string, delta: string): string;
|
|
16
|
+
export declare function runtimeErrorMessage(error: unknown): string;
|
|
17
|
+
export declare function structuredError(error: unknown): AgentError;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
const CONTINUITY_CONTEXT_LIMIT = 64 * 1024;
|
|
3
|
+
export function nextSessionUpdatedAt(current) {
|
|
4
|
+
const currentMs = Date.parse(current);
|
|
5
|
+
const nextMs = Math.max(Date.now(), Number.isFinite(currentMs) ? currentMs + 1 : 0);
|
|
6
|
+
return new Date(nextMs).toISOString();
|
|
7
|
+
}
|
|
8
|
+
export function materializeProviderItem(turnId, providerItemId, sequence, status, item, existing) {
|
|
9
|
+
const now = new Date().toISOString();
|
|
10
|
+
return {
|
|
11
|
+
id: existing?.id ?? "item_" + randomUUID(),
|
|
12
|
+
turnId,
|
|
13
|
+
providerItemId,
|
|
14
|
+
sequence,
|
|
15
|
+
status,
|
|
16
|
+
createdAt: existing?.createdAt ?? now,
|
|
17
|
+
updatedAt: now,
|
|
18
|
+
...item,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export function materializeInteraction(turnId, providerItemId, sequence, interaction) {
|
|
22
|
+
const now = new Date().toISOString();
|
|
23
|
+
return {
|
|
24
|
+
id: "item_" + randomUUID(),
|
|
25
|
+
turnId,
|
|
26
|
+
providerItemId,
|
|
27
|
+
sequence,
|
|
28
|
+
status: "waiting",
|
|
29
|
+
createdAt: now,
|
|
30
|
+
updatedAt: now,
|
|
31
|
+
type: "interaction",
|
|
32
|
+
interaction: {
|
|
33
|
+
kind: interaction.kind,
|
|
34
|
+
title: interaction.title,
|
|
35
|
+
description: interaction.description,
|
|
36
|
+
questions: interaction.questions,
|
|
37
|
+
approvalOptions: interaction.approvalOptions,
|
|
38
|
+
response: null,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export function applyItemDelta(item, field, delta) {
|
|
43
|
+
const updatedAt = new Date().toISOString();
|
|
44
|
+
if (field === "text") {
|
|
45
|
+
if (item.type === "assistant_message" ||
|
|
46
|
+
item.type === "plan" ||
|
|
47
|
+
item.type === "reasoning_summary")
|
|
48
|
+
return { ...item, text: item.text + delta, updatedAt };
|
|
49
|
+
if (item.type === "notice")
|
|
50
|
+
return { ...item, text: item.text + delta, updatedAt };
|
|
51
|
+
}
|
|
52
|
+
if (field === "output" && item.type === "command")
|
|
53
|
+
return { ...item, output: item.output + delta, updatedAt };
|
|
54
|
+
return { ...item, updatedAt };
|
|
55
|
+
}
|
|
56
|
+
export function buildContinuityDelta(history, afterTurnId) {
|
|
57
|
+
let startIndex = 0;
|
|
58
|
+
if (afterTurnId) {
|
|
59
|
+
const index = history.turns.findIndex((entry) => entry.turn.id === afterTurnId);
|
|
60
|
+
if (index >= 0)
|
|
61
|
+
startIndex = index + 1;
|
|
62
|
+
}
|
|
63
|
+
const lines = [];
|
|
64
|
+
for (const entry of history.turns.slice(startIndex)) {
|
|
65
|
+
for (const item of entry.items) {
|
|
66
|
+
const line = continuityLine(item, entry.turn.source);
|
|
67
|
+
if (line)
|
|
68
|
+
lines.push(line);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return lines.join("\n\n");
|
|
72
|
+
}
|
|
73
|
+
export function mergeContinuityContext(existing, delta) {
|
|
74
|
+
if (!delta)
|
|
75
|
+
return existing;
|
|
76
|
+
let merged = existing ? existing + "\n\n" + delta : delta;
|
|
77
|
+
if (merged.length > CONTINUITY_CONTEXT_LIMIT)
|
|
78
|
+
merged =
|
|
79
|
+
"[Earlier cross-provider context omitted]\n\n" +
|
|
80
|
+
merged.slice(merged.length - CONTINUITY_CONTEXT_LIMIT);
|
|
81
|
+
return merged;
|
|
82
|
+
}
|
|
83
|
+
function continuityLine(item, source) {
|
|
84
|
+
if (item.type === "user_message") {
|
|
85
|
+
const attachments = item.attachments.length
|
|
86
|
+
? "\nAttachments: " +
|
|
87
|
+
item.attachments
|
|
88
|
+
.map((attachment) => `${attachment.name} (${attachment.path})`)
|
|
89
|
+
.join(", ")
|
|
90
|
+
: "";
|
|
91
|
+
return `User [${item.source ?? source}]: ${item.text}${attachments}`;
|
|
92
|
+
}
|
|
93
|
+
if (item.type === "assistant_message")
|
|
94
|
+
return "Assistant: " + item.text;
|
|
95
|
+
if (item.type === "plan")
|
|
96
|
+
return ("Plan: " +
|
|
97
|
+
item.text +
|
|
98
|
+
"\n" +
|
|
99
|
+
item.steps.map((step) => `[${step.status}] ${step.step}`).join("\n"));
|
|
100
|
+
if (item.type === "command")
|
|
101
|
+
return ("Command: " +
|
|
102
|
+
item.command +
|
|
103
|
+
(item.exitCode === null ? "" : " (exit " + item.exitCode + ")") +
|
|
104
|
+
(item.output ? "\n" + item.output.slice(-2000) : ""));
|
|
105
|
+
if (item.type === "file_change")
|
|
106
|
+
return ("Files changed: " +
|
|
107
|
+
item.changes.map((change) => change.kind + " " + change.path).join(", "));
|
|
108
|
+
if (item.type === "tool")
|
|
109
|
+
return "Tool: " + (item.server ? item.server + "/" : "") + item.name;
|
|
110
|
+
if (item.type === "artifact")
|
|
111
|
+
return "Artifact: " + item.artifact.path;
|
|
112
|
+
if (item.type === "interaction" && item.interaction.response)
|
|
113
|
+
return ("Interaction: " +
|
|
114
|
+
item.interaction.title +
|
|
115
|
+
"\nResponse: " +
|
|
116
|
+
JSON.stringify(item.interaction.response));
|
|
117
|
+
if (item.type === "notice" && item.level !== "info")
|
|
118
|
+
return "Notice: " + item.text;
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
export function runtimeErrorMessage(error) {
|
|
122
|
+
return error instanceof Error ? error.message : String(error);
|
|
123
|
+
}
|
|
124
|
+
export function structuredError(error) {
|
|
125
|
+
const details = error && typeof error === "object" ? error : null;
|
|
126
|
+
return {
|
|
127
|
+
message: typeof details?.message === "string"
|
|
128
|
+
? details.message
|
|
129
|
+
: runtimeErrorMessage(error),
|
|
130
|
+
details: typeof details?.details === "string" ? details.details : null,
|
|
131
|
+
providerData: details?.providerData ?? null,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { RuntimeEventBus } from "#app/event-bus";
|
|
2
|
+
import type { WorkspaceAccess } from "#app/workspace-access";
|
|
3
|
+
import type { AgentProvider } from "./provider.js";
|
|
4
|
+
import type { AgentStore } from "./store.js";
|
|
5
|
+
import type { AgentSessionConfig, AgentHistory, AgentHistoryUpdate, AgentInteractionResponse, AgentItem, AgentModel, AgentProviderId, AgentProviderStatus, AgentProviderDetails, AgentReviewTarget, AgentSession, AgentTurn, AgentTurnInput, AgentTurnSource, CreateAgentSessionInput } from "./types.js";
|
|
6
|
+
export declare class AgentRuntime {
|
|
7
|
+
private readonly store;
|
|
8
|
+
private readonly workspaces;
|
|
9
|
+
private readonly events;
|
|
10
|
+
private readonly providerSessions;
|
|
11
|
+
private readonly activeTurns;
|
|
12
|
+
private readonly sessionMutations;
|
|
13
|
+
private readonly deltaInvalidations;
|
|
14
|
+
private closed;
|
|
15
|
+
private readonly providers;
|
|
16
|
+
constructor(store: AgentStore, workspaces: WorkspaceAccess, events: RuntimeEventBus, providers: AgentProvider[]);
|
|
17
|
+
providerStatuses(): Promise<AgentProviderStatus[]>;
|
|
18
|
+
providerDetails(providerId: AgentProviderId): Promise<AgentProviderDetails>;
|
|
19
|
+
models(providerId: AgentProviderId): Promise<AgentModel[]>;
|
|
20
|
+
listSessions(): AgentSession[];
|
|
21
|
+
getSession(sessionId: string): AgentSession;
|
|
22
|
+
history(sessionId: string): AgentHistory;
|
|
23
|
+
historyUpdate(sessionId: string, turnIds: readonly string[], itemIds: readonly string[]): AgentHistoryUpdate;
|
|
24
|
+
createSession(input: CreateAgentSessionInput): Promise<AgentSession>;
|
|
25
|
+
startTurn(sessionId: string, input: AgentTurnInput, source: AgentTurnSource): Promise<AgentTurn>;
|
|
26
|
+
private startTurnUnlocked;
|
|
27
|
+
startReview(sessionId: string, target: AgentReviewTarget, source: AgentTurnSource): Promise<AgentTurn>;
|
|
28
|
+
steer(sessionId: string, input: Pick<AgentTurnInput, "text" | "attachments">, source: AgentTurnSource): Promise<void>;
|
|
29
|
+
private beginTurn;
|
|
30
|
+
configureSession(sessionId: string, patch: Partial<AgentSessionConfig>): Promise<AgentSession>;
|
|
31
|
+
private configureSessionUnlocked;
|
|
32
|
+
private validateConfig;
|
|
33
|
+
interrupt(sessionId: string): Promise<void>;
|
|
34
|
+
respondInteraction(sessionId: string, itemId: string, response: AgentInteractionResponse): Promise<AgentItem>;
|
|
35
|
+
deleteSession(sessionId: string): Promise<void>;
|
|
36
|
+
private deleteSessionUnlocked;
|
|
37
|
+
switchProvider(sessionId: string, targetProvider: AgentProviderId, patch?: Partial<AgentSessionConfig>): Promise<AgentSession>;
|
|
38
|
+
private switchProviderUnlocked;
|
|
39
|
+
close(): Promise<void>;
|
|
40
|
+
private executeTurn;
|
|
41
|
+
private handleProviderEvent;
|
|
42
|
+
private finishTurn;
|
|
43
|
+
private providerSession;
|
|
44
|
+
private requireProviderState;
|
|
45
|
+
private updateSessionStatus;
|
|
46
|
+
private requireProvider;
|
|
47
|
+
private requireSession;
|
|
48
|
+
cleanupOrphanAttachments(maxAgeMs?: number): Promise<void>;
|
|
49
|
+
private withSessionMutation;
|
|
50
|
+
private validateInputModalities;
|
|
51
|
+
private scheduleDeltaInvalidation;
|
|
52
|
+
private flushDeltaInvalidation;
|
|
53
|
+
private persistItem;
|
|
54
|
+
private persistPendingSession;
|
|
55
|
+
private failActiveTurn;
|
|
56
|
+
private persistPendingItems;
|
|
57
|
+
private boundCommandOutput;
|
|
58
|
+
private appendCommandOutput;
|
|
59
|
+
private invalidateSession;
|
|
60
|
+
}
|