u-foo 2.5.14 → 3.0.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/package.json +1 -1
- package/src/agents/prompts/native/environment.js +20 -8
- package/src/code/agent.js +517 -112
- package/src/code/commands.js +77 -0
- package/src/code/context/artifactGc.js +292 -0
- package/src/code/context/artifactIndex.js +161 -0
- package/src/code/context/artifacts.js +183 -0
- package/src/code/context/assembler.js +703 -0
- package/src/code/context/executionSegment.js +292 -0
- package/src/code/context/index.js +28 -0
- package/src/code/context/planGraph.js +1410 -0
- package/src/code/context/planGraphService.js +857 -0
- package/src/code/context/planMode.js +398 -0
- package/src/code/context/planProjection.js +432 -0
- package/src/code/context/projectSnapshot.js +201 -0
- package/src/code/context/promptLayers.js +175 -0
- package/src/code/context/reducers.js +328 -0
- package/src/code/context/stableJson.js +29 -0
- package/src/code/context/stateCommit.js +414 -0
- package/src/code/context/toolRuntime.js +172 -0
- package/src/code/context/transcript.js +182 -0
- package/src/code/context/transcriptSync.js +106 -0
- package/src/code/context/userInteraction.js +457 -0
- package/src/code/context/userNudge.js +116 -0
- package/src/code/context/workingSet.js +323 -0
- package/src/code/dispatch.js +20 -1
- package/src/code/index.js +8 -0
- package/src/code/modelCommand.js +87 -0
- package/src/code/nativeRunner.js +625 -34
- package/src/code/repl.js +196 -50
- package/src/code/runtime/agentWakeup.js +58 -0
- package/src/code/runtime/graphOwner.js +41 -0
- package/src/code/runtime/graphYieldRouter.js +42 -0
- package/src/code/runtime/index.js +15 -0
- package/src/code/runtime/loopMailbox.js +124 -0
- package/src/code/runtime/runtimeEvents.js +39 -0
- package/src/code/runtime/taskControl.js +565 -0
- package/src/code/runtime/taskFocus.js +165 -0
- package/src/code/runtime/taskLoop.js +383 -0
- package/src/code/runtime/taskRun.js +187 -0
- package/src/code/runtime/toolProvenance.js +70 -0
- package/src/code/runtime/workspaceLease.js +208 -0
- package/src/code/sessionStore.js +217 -15
- package/src/code/skills/index.js +10 -0
- package/src/code/skills/injection.js +66 -3
- package/src/code/skills/loader.js +21 -0
- package/src/code/skills/manifest.js +87 -0
- package/src/code/skills/render.js +15 -1
- package/src/code/taskDecomposer.js +56 -2
- package/src/code/tools/artifactRead.js +40 -0
- package/src/code/tools/askUser.js +11 -0
- package/src/code/tools/planGraph.js +29 -0
- package/src/code/tui.js +2 -0
- package/src/code/usageStore.js +15 -0
- package/src/ui/format/index.js +285 -45
- package/src/ui/format/markdownRenderer.js +436 -71
- package/src/ui/ink/ChatApp.js +39 -8
- package/src/ui/ink/UcodeApp.js +592 -43
- package/src/ui/ink/chatLogModel.js +102 -21
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const { randomUUID } = require("crypto");
|
|
6
|
+
|
|
7
|
+
function getTranscriptsDir(workspaceRoot = process.cwd()) {
|
|
8
|
+
const root = path.resolve(workspaceRoot || process.cwd());
|
|
9
|
+
return path.join(root, ".ufoo", "agent", "ucode", "transcripts");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function getTranscriptFilePath(workspaceRoot = process.cwd(), sessionId = "") {
|
|
13
|
+
const id = String(sessionId || "").trim();
|
|
14
|
+
if (!id) return "";
|
|
15
|
+
return path.join(getTranscriptsDir(workspaceRoot), `${id}.jsonl`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function createTranscriptEventId() {
|
|
19
|
+
return `msg_${Date.now().toString(36)}_${randomUUID().slice(0, 8)}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function normalizeTranscriptEvent(input = {}) {
|
|
23
|
+
const source = input && typeof input === "object" ? input : {};
|
|
24
|
+
return {
|
|
25
|
+
id: String(source.id || createTranscriptEventId()).trim(),
|
|
26
|
+
role: String(source.role || "").trim(),
|
|
27
|
+
content: source.content,
|
|
28
|
+
toolCalls: Array.isArray(source.toolCalls) ? source.toolCalls : undefined,
|
|
29
|
+
toolCallId: source.toolCallId ? String(source.toolCallId) : undefined,
|
|
30
|
+
artifactId: source.artifactId ? String(source.artifactId) : undefined,
|
|
31
|
+
preview: source.preview ? String(source.preview) : undefined,
|
|
32
|
+
segmentId: source.segmentId ? String(source.segmentId) : undefined,
|
|
33
|
+
createdAt: String(source.createdAt || new Date().toISOString()),
|
|
34
|
+
rawMessage: source.rawMessage && typeof source.rawMessage === "object"
|
|
35
|
+
? source.rawMessage
|
|
36
|
+
: undefined,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function messageToTranscriptEvent(message = {}, extra = {}) {
|
|
41
|
+
if (!message || typeof message !== "object") return null;
|
|
42
|
+
const role = String(message.role || "").trim();
|
|
43
|
+
if (!role) return null;
|
|
44
|
+
const event = {
|
|
45
|
+
id: createTranscriptEventId(),
|
|
46
|
+
role,
|
|
47
|
+
content: message.content,
|
|
48
|
+
createdAt: new Date().toISOString(),
|
|
49
|
+
rawMessage: message,
|
|
50
|
+
...extra,
|
|
51
|
+
};
|
|
52
|
+
if (message.tool_calls) event.toolCalls = message.tool_calls;
|
|
53
|
+
if (message.tool_call_id) event.toolCallId = message.tool_call_id;
|
|
54
|
+
return normalizeTranscriptEvent(event);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function readTranscriptFile(filePath = "") {
|
|
58
|
+
try {
|
|
59
|
+
if (!filePath || !fs.existsSync(filePath)) return [];
|
|
60
|
+
const raw = fs.readFileSync(filePath, "utf8");
|
|
61
|
+
if (!raw.trim()) return [];
|
|
62
|
+
const events = [];
|
|
63
|
+
for (const line of raw.split(/\r?\n/).map((item) => item.trim()).filter(Boolean)) {
|
|
64
|
+
try {
|
|
65
|
+
events.push(normalizeTranscriptEvent(JSON.parse(line)));
|
|
66
|
+
} catch {
|
|
67
|
+
// ignore malformed line
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return events;
|
|
71
|
+
} catch {
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function loadTranscript(workspaceRoot = process.cwd(), sessionId = "") {
|
|
77
|
+
const filePath = getTranscriptFilePath(workspaceRoot, sessionId);
|
|
78
|
+
return {
|
|
79
|
+
filePath,
|
|
80
|
+
events: readTranscriptFile(filePath),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function appendTranscriptEvent(workspaceRoot = process.cwd(), sessionId = "", event = {}) {
|
|
85
|
+
const filePath = getTranscriptFilePath(workspaceRoot, sessionId);
|
|
86
|
+
if (!filePath) {
|
|
87
|
+
return { ok: false, error: "invalid session id", event: null };
|
|
88
|
+
}
|
|
89
|
+
const normalized = normalizeTranscriptEvent(event);
|
|
90
|
+
try {
|
|
91
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
92
|
+
fs.appendFileSync(filePath, `${JSON.stringify(normalized)}\n`, "utf8");
|
|
93
|
+
return { ok: true, error: "", event: normalized, filePath };
|
|
94
|
+
} catch (err) {
|
|
95
|
+
return {
|
|
96
|
+
ok: false,
|
|
97
|
+
error: err && err.message ? err.message : "failed to append transcript",
|
|
98
|
+
event: normalized,
|
|
99
|
+
filePath,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function appendTranscriptMessages(workspaceRoot = process.cwd(), sessionId = "", messages = [], extra = {}) {
|
|
105
|
+
const list = Array.isArray(messages) ? messages : [];
|
|
106
|
+
const appended = [];
|
|
107
|
+
for (const message of list) {
|
|
108
|
+
const event = messageToTranscriptEvent(message, extra);
|
|
109
|
+
if (!event) continue;
|
|
110
|
+
const result = appendTranscriptEvent(workspaceRoot, sessionId, event);
|
|
111
|
+
if (result.ok) appended.push(result.event);
|
|
112
|
+
}
|
|
113
|
+
return appended;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function transcriptEventsToMessages(events = [], options = {}) {
|
|
117
|
+
const preferArtifact = options.preferArtifact !== false;
|
|
118
|
+
const list = Array.isArray(events) ? events : [];
|
|
119
|
+
const messages = [];
|
|
120
|
+
for (const event of list) {
|
|
121
|
+
if (preferArtifact && event.artifactId) {
|
|
122
|
+
messages.push({
|
|
123
|
+
role: event.role || "tool",
|
|
124
|
+
content: JSON.stringify({
|
|
125
|
+
artifactId: event.artifactId,
|
|
126
|
+
preview: event.preview || "",
|
|
127
|
+
}),
|
|
128
|
+
tool_call_id: event.toolCallId,
|
|
129
|
+
});
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (!preferArtifact && event.rawMessage && typeof event.rawMessage === "object") {
|
|
133
|
+
messages.push(event.rawMessage);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
const message = { role: event.role };
|
|
137
|
+
if (event.content !== undefined) message.content = event.content;
|
|
138
|
+
if (event.toolCalls) message.tool_calls = event.toolCalls;
|
|
139
|
+
if (event.toolCallId) message.tool_call_id = event.toolCallId;
|
|
140
|
+
messages.push(message);
|
|
141
|
+
}
|
|
142
|
+
return messages;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function migrateNlMessagesToTranscript(workspaceRoot = process.cwd(), sessionId = "", nlMessages = []) {
|
|
146
|
+
const filePath = getTranscriptFilePath(workspaceRoot, sessionId);
|
|
147
|
+
if (filePath && fs.existsSync(filePath)) {
|
|
148
|
+
return loadTranscript(workspaceRoot, sessionId).events;
|
|
149
|
+
}
|
|
150
|
+
const messages = Array.isArray(nlMessages) ? nlMessages : [];
|
|
151
|
+
if (messages.length === 0) return [];
|
|
152
|
+
appendTranscriptMessages(workspaceRoot, sessionId, messages, { migrated: true });
|
|
153
|
+
return loadTranscript(workspaceRoot, sessionId).events;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function deleteTranscript(workspaceRoot = process.cwd(), sessionId = "") {
|
|
157
|
+
const filePath = getTranscriptFilePath(workspaceRoot, sessionId);
|
|
158
|
+
if (!filePath || !fs.existsSync(filePath)) return { ok: true, error: "" };
|
|
159
|
+
try {
|
|
160
|
+
fs.unlinkSync(filePath);
|
|
161
|
+
return { ok: true, error: "" };
|
|
162
|
+
} catch (err) {
|
|
163
|
+
return {
|
|
164
|
+
ok: false,
|
|
165
|
+
error: err && err.message ? err.message : "failed to delete transcript",
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
module.exports = {
|
|
171
|
+
getTranscriptsDir,
|
|
172
|
+
getTranscriptFilePath,
|
|
173
|
+
createTranscriptEventId,
|
|
174
|
+
normalizeTranscriptEvent,
|
|
175
|
+
messageToTranscriptEvent,
|
|
176
|
+
loadTranscript,
|
|
177
|
+
appendTranscriptEvent,
|
|
178
|
+
appendTranscriptMessages,
|
|
179
|
+
transcriptEventsToMessages,
|
|
180
|
+
migrateNlMessagesToTranscript,
|
|
181
|
+
deleteTranscript,
|
|
182
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
normalizeTranscriptEvent,
|
|
5
|
+
createTranscriptEventId,
|
|
6
|
+
appendTranscriptEvent,
|
|
7
|
+
} = require("./transcript");
|
|
8
|
+
|
|
9
|
+
function messageRole(message = {}) {
|
|
10
|
+
return String(message && message.role || "").trim().toLowerCase();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function isToolRoleMessage(message = {}) {
|
|
14
|
+
const role = messageRole(message);
|
|
15
|
+
if (role === "tool") return true;
|
|
16
|
+
if (Array.isArray(message.content)) {
|
|
17
|
+
return message.content.some((block) => block && block.type === "tool_result");
|
|
18
|
+
}
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function parseToolArtifactContent(content = "") {
|
|
23
|
+
if (typeof content !== "string" || !content.trim()) return null;
|
|
24
|
+
try {
|
|
25
|
+
const parsed = JSON.parse(content);
|
|
26
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
27
|
+
const artifactId = String(parsed.artifactId || "").trim();
|
|
28
|
+
if (!artifactId) return null;
|
|
29
|
+
return {
|
|
30
|
+
artifactId,
|
|
31
|
+
preview: String(parsed.preview || "").trim(),
|
|
32
|
+
};
|
|
33
|
+
} catch {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function messageToTranscriptEventForStorage(message = {}, extra = {}) {
|
|
39
|
+
if (!message || typeof message !== "object") return null;
|
|
40
|
+
const role = messageRole(message);
|
|
41
|
+
if (!role) return null;
|
|
42
|
+
|
|
43
|
+
const base = {
|
|
44
|
+
id: createTranscriptEventId(),
|
|
45
|
+
role,
|
|
46
|
+
createdAt: new Date().toISOString(),
|
|
47
|
+
...extra,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
if (isToolRoleMessage(message)) {
|
|
51
|
+
const artifact = parseToolArtifactContent(
|
|
52
|
+
typeof message.content === "string" ? message.content : JSON.stringify(message.content || ""),
|
|
53
|
+
);
|
|
54
|
+
if (artifact) {
|
|
55
|
+
return normalizeTranscriptEvent({
|
|
56
|
+
...base,
|
|
57
|
+
role: "tool",
|
|
58
|
+
artifactId: artifact.artifactId,
|
|
59
|
+
preview: artifact.preview,
|
|
60
|
+
toolCallId: message.tool_call_id ? String(message.tool_call_id) : undefined,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
const preview = typeof message.content === "string"
|
|
64
|
+
? message.content.slice(0, 600)
|
|
65
|
+
: JSON.stringify(message.content).slice(0, 600);
|
|
66
|
+
return normalizeTranscriptEvent({
|
|
67
|
+
...base,
|
|
68
|
+
role: "tool",
|
|
69
|
+
preview,
|
|
70
|
+
toolCallId: message.tool_call_id ? String(message.tool_call_id) : undefined,
|
|
71
|
+
content: preview,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0) {
|
|
76
|
+
return normalizeTranscriptEvent({
|
|
77
|
+
...base,
|
|
78
|
+
content: message.content,
|
|
79
|
+
toolCalls: message.tool_calls,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return normalizeTranscriptEvent({
|
|
84
|
+
...base,
|
|
85
|
+
content: message.content,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function appendTranscriptMessagesForStorage(workspaceRoot = process.cwd(), sessionId = "", messages = [], extra = {}) {
|
|
90
|
+
const list = Array.isArray(messages) ? messages : [];
|
|
91
|
+
const appended = [];
|
|
92
|
+
for (const message of list) {
|
|
93
|
+
const event = messageToTranscriptEventForStorage(message, extra);
|
|
94
|
+
if (!event) continue;
|
|
95
|
+
const result = appendTranscriptEvent(workspaceRoot, sessionId, event);
|
|
96
|
+
if (result.ok) appended.push(result.event);
|
|
97
|
+
}
|
|
98
|
+
return appended;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
module.exports = {
|
|
102
|
+
parseToolArtifactContent,
|
|
103
|
+
messageToTranscriptEventForStorage,
|
|
104
|
+
appendTranscriptMessagesForStorage,
|
|
105
|
+
isToolRoleMessage,
|
|
106
|
+
};
|