wtagent 0.1.0-alpha.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/LICENSE +21 -0
- package/README.md +83 -0
- package/docs/technical-design.md +838 -0
- package/package.json +55 -0
- package/src/browser/cdp-browser.js +170 -0
- package/src/browser/chatgpt-web-adapter.js +717 -0
- package/src/browser/fake-web-model-adapter.js +86 -0
- package/src/browser/mode-selection.js +179 -0
- package/src/browser/native-login.js +49 -0
- package/src/cli/at-files.js +93 -0
- package/src/cli/main.js +629 -0
- package/src/cli/render-events.js +367 -0
- package/src/platform/chrome-discovery.js +94 -0
- package/src/platform/paths.js +73 -0
- package/src/policy/path-guard.js +114 -0
- package/src/policy/policy-engine.js +277 -0
- package/src/protocol/markers.js +64 -0
- package/src/protocol/prompt-builder.js +152 -0
- package/src/protocol/xml-protocol.js +333 -0
- package/src/runtime/agent-runtime.js +636 -0
- package/src/session/agent-session.js +569 -0
- package/src/session/canonical-transcript.js +145 -0
- package/src/session/session-export.js +202 -0
- package/src/session/task-session.js +5 -0
- package/src/shared/errors.js +52 -0
- package/src/shared/limits.js +31 -0
- package/src/tools/default-tools.js +555 -0
- package/src/tools/process-manager.js +116 -0
- package/src/tools/process-utils.js +31 -0
- package/src/tools/registry.js +129 -0
- package/src/tools/safe-env.js +67 -0
- package/src/tools/terminal-exec.js +118 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
// Session exporters: canonical transcript -> Codex rollout JSONL and Claude
|
|
2
|
+
// Code project JSONL.
|
|
3
|
+
//
|
|
4
|
+
// The canonical transcript is already Codex-shaped, so the Codex exporter is a
|
|
5
|
+
// thin framing layer. The Claude Code exporter maps Responses-style items onto
|
|
6
|
+
// Anthropic Messages blocks (tool_use / tool_result) with the uuid/parentUuid
|
|
7
|
+
// linkage that Claude Code's project JSONL uses.
|
|
8
|
+
//
|
|
9
|
+
// Both exporters strip WTAgent-specific scaffolding: `developer` messages
|
|
10
|
+
// (protocol + tool catalog) and any legacy/current protocol or reminder blocks
|
|
11
|
+
// in user text. The WTAgent XML protocol and its tool set must never surface
|
|
12
|
+
// in a Codex or Claude Code session — those runtimes drive their own tools.
|
|
13
|
+
|
|
14
|
+
import { randomUUID } from "node:crypto";
|
|
15
|
+
import { stripSystemMarkers } from "../protocol/markers.js";
|
|
16
|
+
import { isDeveloper, messageText } from "./canonical-transcript.js";
|
|
17
|
+
|
|
18
|
+
function jsonl(records) {
|
|
19
|
+
return records.map((record) => JSON.stringify(record)).join("\n") + "\n";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function firstTimestamp(items, fallback) {
|
|
23
|
+
return items.find((item) => item.timestamp)?.timestamp ?? fallback;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// The uploaded bytes of an @file attachment live only in the ChatGPT
|
|
27
|
+
// conversation, not in the portable transcript. So exports append a short,
|
|
28
|
+
// self-describing note naming the attached files, keeping the exported session
|
|
29
|
+
// honest about what the user actually provided.
|
|
30
|
+
function withAttachmentNote(text, item) {
|
|
31
|
+
const attachments = Array.isArray(item?.attachments) ? item.attachments : [];
|
|
32
|
+
if (attachments.length === 0) {
|
|
33
|
+
return text;
|
|
34
|
+
}
|
|
35
|
+
const names = attachments.map((file) => file.name ?? file.path).filter(Boolean);
|
|
36
|
+
if (names.length === 0) {
|
|
37
|
+
return text;
|
|
38
|
+
}
|
|
39
|
+
const note = `[attached: ${names.join(", ")}]`;
|
|
40
|
+
return text ? `${text}\n\n${note}` : note;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
// Codex rollout export
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
// Produces `{timestamp, type, payload}` records: a leading `session_meta`, then
|
|
47
|
+
// one `response_item` per transcript item. WTAgent's transport-only developer
|
|
48
|
+
// scaffold is omitted entirely; it must not become Codex instructions.
|
|
49
|
+
export function toCodexRollout(transcript, { now } = {}) {
|
|
50
|
+
const meta = transcript.meta ?? {};
|
|
51
|
+
const items = transcript.items ?? [];
|
|
52
|
+
const stamp = now ?? firstTimestamp(items, meta.createdAt) ?? null;
|
|
53
|
+
const sessionId = meta.sessionId ?? randomUUID();
|
|
54
|
+
|
|
55
|
+
const records = [];
|
|
56
|
+
records.push({
|
|
57
|
+
timestamp: stamp,
|
|
58
|
+
type: "session_meta",
|
|
59
|
+
payload: {
|
|
60
|
+
id: sessionId,
|
|
61
|
+
session_id: sessionId,
|
|
62
|
+
timestamp: meta.createdAt ?? stamp,
|
|
63
|
+
cwd: meta.cwd ?? null,
|
|
64
|
+
originator: "wtagent",
|
|
65
|
+
source: "wtagent",
|
|
66
|
+
base_instructions: meta.baseInstructions ?? null,
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
for (const entry of items) {
|
|
71
|
+
const item = entry.item;
|
|
72
|
+
if (isDeveloper(item)) {
|
|
73
|
+
// This is the XML/tool transport scaffold used only on ChatGPT Web.
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
let payload = item;
|
|
78
|
+
if (item.type === "message" && item.role === "user") {
|
|
79
|
+
const cleaned = withAttachmentNote(stripSystemMarkers(messageText(item)), item);
|
|
80
|
+
if (!cleaned) {
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
payload = {
|
|
84
|
+
type: "message",
|
|
85
|
+
role: "user",
|
|
86
|
+
content: [{ type: "input_text", text: cleaned }],
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
records.push({
|
|
91
|
+
timestamp: entry.timestamp ?? stamp,
|
|
92
|
+
type: "response_item",
|
|
93
|
+
payload,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return jsonl(records);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
// Claude Code project export
|
|
102
|
+
// ---------------------------------------------------------------------------
|
|
103
|
+
// Produces uuid/parentUuid-linked records with Anthropic Messages content.
|
|
104
|
+
// function_call -> assistant message with a tool_use block.
|
|
105
|
+
// function_call_output -> user message with a tool_result block.
|
|
106
|
+
export function toClaudeCodeSession(transcript, { sessionId, now } = {}) {
|
|
107
|
+
const meta = transcript.meta ?? {};
|
|
108
|
+
const items = transcript.items ?? [];
|
|
109
|
+
const session = sessionId ?? meta.sessionId ?? randomUUID();
|
|
110
|
+
const cwd = meta.cwd ?? null;
|
|
111
|
+
const stamp = now ?? firstTimestamp(items, meta.createdAt) ?? null;
|
|
112
|
+
|
|
113
|
+
const records = [];
|
|
114
|
+
let parentUuid = null;
|
|
115
|
+
|
|
116
|
+
const push = (partial, timestamp) => {
|
|
117
|
+
const uuid = randomUUID();
|
|
118
|
+
records.push({
|
|
119
|
+
parentUuid,
|
|
120
|
+
isSidechain: false,
|
|
121
|
+
type: partial.type,
|
|
122
|
+
message: partial.message,
|
|
123
|
+
uuid,
|
|
124
|
+
timestamp: timestamp ?? stamp,
|
|
125
|
+
cwd,
|
|
126
|
+
sessionId: session,
|
|
127
|
+
version: "wtagent-export",
|
|
128
|
+
...(partial.extra ?? {}),
|
|
129
|
+
});
|
|
130
|
+
parentUuid = uuid;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
for (const entry of items) {
|
|
134
|
+
const item = entry.item;
|
|
135
|
+
const ts = entry.timestamp ?? stamp;
|
|
136
|
+
|
|
137
|
+
if (isDeveloper(item)) {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (item.type === "message" && item.role === "user") {
|
|
142
|
+
const cleaned = withAttachmentNote(stripSystemMarkers(messageText(item)), item);
|
|
143
|
+
if (!cleaned) {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
push({
|
|
147
|
+
type: "user",
|
|
148
|
+
message: { role: "user", content: cleaned },
|
|
149
|
+
}, ts);
|
|
150
|
+
} else if (item.type === "message" && item.role === "assistant") {
|
|
151
|
+
const text = messageText(item);
|
|
152
|
+
if (!text.trim()) {
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
push({
|
|
156
|
+
type: "assistant",
|
|
157
|
+
message: {
|
|
158
|
+
role: "assistant",
|
|
159
|
+
content: [{ type: "text", text }],
|
|
160
|
+
},
|
|
161
|
+
}, ts);
|
|
162
|
+
} else if (item.type === "function_call") {
|
|
163
|
+
let input = {};
|
|
164
|
+
try {
|
|
165
|
+
input = JSON.parse(item.arguments || "{}");
|
|
166
|
+
} catch {
|
|
167
|
+
input = { _raw: item.arguments };
|
|
168
|
+
}
|
|
169
|
+
push({
|
|
170
|
+
type: "assistant",
|
|
171
|
+
message: {
|
|
172
|
+
role: "assistant",
|
|
173
|
+
content: [{
|
|
174
|
+
type: "tool_use",
|
|
175
|
+
id: item.call_id,
|
|
176
|
+
name: item.name,
|
|
177
|
+
input,
|
|
178
|
+
}],
|
|
179
|
+
},
|
|
180
|
+
}, ts);
|
|
181
|
+
} else if (item.type === "function_call_output") {
|
|
182
|
+
push({
|
|
183
|
+
type: "user",
|
|
184
|
+
message: {
|
|
185
|
+
role: "user",
|
|
186
|
+
content: [{
|
|
187
|
+
type: "tool_result",
|
|
188
|
+
tool_use_id: item.call_id,
|
|
189
|
+
content: item.output,
|
|
190
|
+
}],
|
|
191
|
+
},
|
|
192
|
+
}, ts);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return jsonl(records);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export const EXPORTERS = {
|
|
200
|
+
codex: toCodexRollout,
|
|
201
|
+
"claude-code": toClaudeCodeSession,
|
|
202
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export class WTAgentError extends Error {
|
|
2
|
+
constructor(message, options = {}) {
|
|
3
|
+
super(message, options);
|
|
4
|
+
this.name = this.constructor.name;
|
|
5
|
+
this.code = options.code ?? "WTAGENT_ERROR";
|
|
6
|
+
this.recoverable = options.recoverable ?? false;
|
|
7
|
+
this.details = options.details;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class ProtocolError extends WTAgentError {
|
|
12
|
+
constructor(message, options = {}) {
|
|
13
|
+
super(message, {
|
|
14
|
+
code: "PROTOCOL_ERROR",
|
|
15
|
+
recoverable: true,
|
|
16
|
+
...options,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class BrowserAdapterError extends WTAgentError {
|
|
22
|
+
constructor(message, options = {}) {
|
|
23
|
+
super(message, {
|
|
24
|
+
code: "BROWSER_ADAPTER_ERROR",
|
|
25
|
+
recoverable: true,
|
|
26
|
+
...options,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export class ToolValidationError extends WTAgentError {
|
|
32
|
+
constructor(message, options = {}) {
|
|
33
|
+
super(message, {
|
|
34
|
+
code: "TOOL_VALIDATION_ERROR",
|
|
35
|
+
recoverable: true,
|
|
36
|
+
...options,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export class PolicyDeniedError extends WTAgentError {
|
|
42
|
+
constructor(message, options = {}) {
|
|
43
|
+
super(message, {
|
|
44
|
+
code: "POLICY_DENIED",
|
|
45
|
+
recoverable: true,
|
|
46
|
+
...options,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Compatibility alias for integrations written before the product rename.
|
|
52
|
+
export { WTAgentError as WebAgentError };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export const DEFAULT_LIMITS = Object.freeze({
|
|
2
|
+
maxTurns: 36,
|
|
3
|
+
maxProtocolErrors: 3,
|
|
4
|
+
modelTurnTimeoutMs: 10 * 60_000,
|
|
5
|
+
modelStableWindowMs: 1_500,
|
|
6
|
+
loginTimeoutMs: 15 * 60_000,
|
|
7
|
+
toolTimeoutMs: 2 * 60_000,
|
|
8
|
+
maxToolOutputBytes: 64 * 1024,
|
|
9
|
+
maxFileReadBytes: 128 * 1024,
|
|
10
|
+
maxDirectoryEntries: 500,
|
|
11
|
+
maxSearchResults: 200,
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export function resolveLimits({ modelTurnTimeoutMs } = {}) {
|
|
15
|
+
if (modelTurnTimeoutMs == null || modelTurnTimeoutMs === "") {
|
|
16
|
+
return DEFAULT_LIMITS;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const parsed = Number(modelTurnTimeoutMs);
|
|
20
|
+
if (!Number.isSafeInteger(parsed) || parsed <= 0) {
|
|
21
|
+
throw new TypeError(
|
|
22
|
+
"Model turn timeout must be a positive integer number of milliseconds.",
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return Object.freeze({
|
|
27
|
+
...DEFAULT_LIMITS,
|
|
28
|
+
modelTurnTimeoutMs: parsed,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|