ghostykit 0.3.2 → 0.4.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/README.md +44 -57
- package/dist/chunks/chunk-DXFWACUL.js +85 -0
- package/dist/chunks/chunk-HPTM2BCM.js +472 -0
- package/dist/core/index.d.ts +2 -3
- package/dist/core/index.js +13 -23
- package/dist/core/reducer.d.ts +27 -45
- package/dist/core/steps.d.ts +9 -1
- package/dist/core/types.d.ts +36 -160
- package/dist/react/Bubble.d.ts +2 -6
- package/dist/react/Composer.d.ts +6 -1
- package/dist/react/ConfirmCard.d.ts +3 -4
- package/dist/react/Piloto.d.ts +3 -10
- package/dist/react/Status.d.ts +1 -13
- package/dist/react/index.d.ts +6 -6
- package/dist/react/index.js +214 -408
- package/dist/react/strings.d.ts +2 -18
- package/dist/react/usePilot.d.ts +31 -43
- package/dist/styles.css +2 -103
- package/dist/transport/index.d.ts +40 -30
- package/dist/transport/index.js +3 -13
- package/package.json +16 -9
- package/dist/chunks/chunk-O6FCE4CS.js +0 -670
- package/dist/chunks/chunk-XZHX2NZT.js +0 -375
- package/dist/core/reconcile.d.ts +0 -7
- package/dist/react/connectingGate.d.ts +0 -17
- package/dist/transport/postStream.d.ts +0 -44
- package/dist/transport/types.d.ts +0 -47
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
// src/core/reducer.ts
|
|
2
|
+
function createPilotState(turns = []) {
|
|
3
|
+
return { turns, busy: false, error: null, notice: null, usage: null, permissions: [], turnStartedAt: 0, runId: null, effects: [] };
|
|
4
|
+
}
|
|
5
|
+
function patchAssistant(s, patch) {
|
|
6
|
+
const turns = [...s.turns];
|
|
7
|
+
const last = turns[turns.length - 1];
|
|
8
|
+
if (last?.role === "assistant") turns[turns.length - 1] = patch(last);
|
|
9
|
+
else turns.push(patch({ role: "assistant", text: "" }));
|
|
10
|
+
return { ...s, turns };
|
|
11
|
+
}
|
|
12
|
+
function argsDetail(json) {
|
|
13
|
+
if (!json) return void 0;
|
|
14
|
+
try {
|
|
15
|
+
const v = JSON.parse(json);
|
|
16
|
+
if (!v || typeof v !== "object") return String(v);
|
|
17
|
+
const parts = Object.entries(v).filter(([, x]) => x !== null && x !== void 0 && x !== "").map(([k, x]) => `${k}: ${typeof x === "object" ? JSON.stringify(x) : String(x)}`);
|
|
18
|
+
const s = parts.join(", ");
|
|
19
|
+
return s ? s.length > 80 ? `${s.slice(0, 79)}\u2026` : s : void 0;
|
|
20
|
+
} catch {
|
|
21
|
+
return void 0;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function upsertTool(s, id, patch) {
|
|
25
|
+
return patchAssistant(s, (t) => {
|
|
26
|
+
const tools = [...t.tools ?? []];
|
|
27
|
+
const i = tools.findIndex((x) => x.id === id);
|
|
28
|
+
let cuts = t.cuts;
|
|
29
|
+
if (i === -1) {
|
|
30
|
+
tools.push({ id, ...patch });
|
|
31
|
+
const len = t.text.length;
|
|
32
|
+
if (t.text.trim() && cuts?.[cuts.length - 1] !== len) cuts = [...cuts ?? [], len];
|
|
33
|
+
} else tools[i] = { ...tools[i], ...patch };
|
|
34
|
+
return { ...t, tools, cuts };
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
var APPROVE = { optionId: "approve", name: "Aprobar", kind: "allow_once" };
|
|
38
|
+
var REJECT = { optionId: "reject", name: "Rechazar", kind: "reject_once" };
|
|
39
|
+
function toPermission(i, turns) {
|
|
40
|
+
const meta = i.metadata ?? {};
|
|
41
|
+
const toolName2 = (typeof meta.tool === "string" ? meta.tool : void 0) ?? turns.flatMap((t) => t.tools ?? []).find((x) => x.id === i.toolCallId)?.title;
|
|
42
|
+
return {
|
|
43
|
+
id: i.id,
|
|
44
|
+
title: i.message ?? (toolName2 ? `${toolName2}` : i.reason),
|
|
45
|
+
...toolName2 ? { tool: toolName2 } : {},
|
|
46
|
+
...i.toolCallId ? { toolCallId: i.toolCallId } : {},
|
|
47
|
+
reason: i.reason,
|
|
48
|
+
options: [REJECT, APPROVE],
|
|
49
|
+
...i.expiresAt ? { expiresAt: i.expiresAt } : {}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function textOf(content) {
|
|
53
|
+
if (typeof content === "string") return content;
|
|
54
|
+
if (Array.isArray(content)) return content.map((p) => p && typeof p === "object" && p.type === "text" ? p.text ?? "" : "").join("");
|
|
55
|
+
return "";
|
|
56
|
+
}
|
|
57
|
+
function attachmentsOf(content) {
|
|
58
|
+
if (!Array.isArray(content)) return void 0;
|
|
59
|
+
const out = [];
|
|
60
|
+
for (const p of content) {
|
|
61
|
+
if (!p || p.type === "text" || !p.source) continue;
|
|
62
|
+
const mime = p.mimeType ?? p.source.mimeType ?? (p.type === "image" ? "image/*" : "application/octet-stream");
|
|
63
|
+
out.push(p.source.type === "data" ? { mimeType: mime, data: p.source.value, name: p.filename } : { mimeType: mime, url: p.source.value, name: p.filename });
|
|
64
|
+
}
|
|
65
|
+
return out.length ? out : void 0;
|
|
66
|
+
}
|
|
67
|
+
function messagesToTurns(messages) {
|
|
68
|
+
const turns = [];
|
|
69
|
+
let cur = null;
|
|
70
|
+
const assistant = () => {
|
|
71
|
+
if (!cur) {
|
|
72
|
+
cur = { role: "assistant", text: "" };
|
|
73
|
+
turns.push(cur);
|
|
74
|
+
}
|
|
75
|
+
return cur;
|
|
76
|
+
};
|
|
77
|
+
for (const m of messages) {
|
|
78
|
+
if (m.role === "user") {
|
|
79
|
+
cur = null;
|
|
80
|
+
turns.push({ role: "user", text: textOf(m.content), ...attachmentsOf(m.content) ? { attachments: attachmentsOf(m.content) } : {} });
|
|
81
|
+
} else if (m.role === "assistant") {
|
|
82
|
+
const t = assistant();
|
|
83
|
+
const text = m.content ?? "";
|
|
84
|
+
if (text) t.text = t.text ? `${t.text}
|
|
85
|
+
|
|
86
|
+
${text}` : text;
|
|
87
|
+
for (const c of m.toolCalls ?? []) {
|
|
88
|
+
t.tools = [...t.tools ?? [], { id: c.id, title: c.function.name, status: "pending", args: c.function.arguments, detail: argsDetail(c.function.arguments) }];
|
|
89
|
+
if (t.text.trim() && t.cuts?.[t.cuts.length - 1] !== t.text.length) t.cuts = [...t.cuts ?? [], t.text.length];
|
|
90
|
+
}
|
|
91
|
+
} else if (m.role === "tool") {
|
|
92
|
+
const t = assistant();
|
|
93
|
+
const failed = Boolean(m.error);
|
|
94
|
+
t.tools = (t.tools ?? []).map((x) => x.id === m.toolCallId ? { ...x, status: failed ? "failed" : "completed" } : x);
|
|
95
|
+
} else if (m.role === "reasoning") {
|
|
96
|
+
const t = assistant();
|
|
97
|
+
t.thought = (t.thought ?? "") + textOf(m.content);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return turns;
|
|
101
|
+
}
|
|
102
|
+
function applySnapshot(s, messages) {
|
|
103
|
+
const fresh = messagesToTurns(messages);
|
|
104
|
+
const old = s.turns;
|
|
105
|
+
const turns = fresh.map((t, i) => {
|
|
106
|
+
const o = old[i];
|
|
107
|
+
if (!o || o.role !== t.role) return t;
|
|
108
|
+
return {
|
|
109
|
+
...t,
|
|
110
|
+
...o.seconds !== void 0 ? { seconds: o.seconds } : {},
|
|
111
|
+
...o.usage ? { usage: o.usage } : {},
|
|
112
|
+
...o.decisions ? { decisions: o.decisions } : {},
|
|
113
|
+
...o.thought && !t.thought ? { thought: o.thought } : {},
|
|
114
|
+
...o.attachments && !t.attachments ? { attachments: o.attachments } : {},
|
|
115
|
+
...o.tools && t.tools ? { tools: t.tools.map((x) => ({ ...o.tools.find((y) => y.id === x.id), ...x })) } : {}
|
|
116
|
+
};
|
|
117
|
+
});
|
|
118
|
+
return { ...s, turns };
|
|
119
|
+
}
|
|
120
|
+
function reduceAgUiEvent(prev, e, now = Date.now()) {
|
|
121
|
+
const s = prev.effects.length ? { ...prev, effects: [] } : prev;
|
|
122
|
+
switch (e.type) {
|
|
123
|
+
case "RUN_STARTED":
|
|
124
|
+
return { ...s, busy: true, error: null, runId: e.runId ?? s.runId };
|
|
125
|
+
case "TEXT_MESSAGE_START":
|
|
126
|
+
if (e.role && e.role !== "assistant") return s;
|
|
127
|
+
return patchAssistant(s, (t) => ({ ...t, text: t.text && !t.text.endsWith("\n") ? `${t.text}
|
|
128
|
+
|
|
129
|
+
` : t.text }));
|
|
130
|
+
case "TEXT_MESSAGE_CONTENT":
|
|
131
|
+
case "TEXT_MESSAGE_CHUNK":
|
|
132
|
+
if (e.role && e.role !== "assistant") return s;
|
|
133
|
+
if (typeof e.delta !== "string" || !e.delta) return s;
|
|
134
|
+
return patchAssistant(s, (t) => ({ ...t, text: t.text + e.delta }));
|
|
135
|
+
case "TEXT_MESSAGE_END":
|
|
136
|
+
return s;
|
|
137
|
+
case "TOOL_CALL_START":
|
|
138
|
+
return upsertTool(s, e.toolCallId, { title: e.toolCallName, status: "in_progress" });
|
|
139
|
+
case "TOOL_CALL_CHUNK": {
|
|
140
|
+
const known = s.turns[s.turns.length - 1]?.tools?.find((x) => x.id === e.toolCallId);
|
|
141
|
+
let next = known ? s : upsertTool(s, e.toolCallId, { title: e.toolCallName, status: "in_progress" });
|
|
142
|
+
if (typeof e.delta === "string" && e.delta) next = upsertTool(next, e.toolCallId, { args: (known?.args ?? "") + e.delta });
|
|
143
|
+
return next;
|
|
144
|
+
}
|
|
145
|
+
case "TOOL_CALL_ARGS": {
|
|
146
|
+
const known = s.turns[s.turns.length - 1]?.tools?.find((x) => x.id === e.toolCallId);
|
|
147
|
+
return upsertTool(s, e.toolCallId, { args: (known?.args ?? "") + (e.delta ?? "") });
|
|
148
|
+
}
|
|
149
|
+
case "TOOL_CALL_END": {
|
|
150
|
+
const known = s.turns[s.turns.length - 1]?.tools?.find((x) => x.id === e.toolCallId);
|
|
151
|
+
return upsertTool(s, e.toolCallId, { detail: argsDetail(known?.args) });
|
|
152
|
+
}
|
|
153
|
+
case "TOOL_CALL_RESULT": {
|
|
154
|
+
const failed = Boolean(e.metadata?.error);
|
|
155
|
+
return upsertTool(s, e.toolCallId, { status: failed ? "failed" : "completed" });
|
|
156
|
+
}
|
|
157
|
+
case "REASONING_MESSAGE_CONTENT":
|
|
158
|
+
case "REASONING_MESSAGE_CHUNK":
|
|
159
|
+
if (typeof e.delta !== "string" || !e.delta) return s;
|
|
160
|
+
return patchAssistant(s, (t) => ({ ...t, thought: (t.thought ?? "") + e.delta }));
|
|
161
|
+
case "MESSAGES_SNAPSHOT":
|
|
162
|
+
return Array.isArray(e.messages) ? applySnapshot(s, e.messages) : s;
|
|
163
|
+
case "RUN_FINISHED":
|
|
164
|
+
return finish(s, e, now);
|
|
165
|
+
case "RUN_ERROR":
|
|
166
|
+
return finish({ ...s, error: typeof e.message === "string" ? e.message : "El agente fall\xF3." }, e, now, true);
|
|
167
|
+
case "CUSTOM":
|
|
168
|
+
if (e.name === "ghosty.title") return { ...s, effects: [{ type: "title", title: e.value?.title }] };
|
|
169
|
+
return s;
|
|
170
|
+
default:
|
|
171
|
+
return s;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
function finish(s0, e, now, failed = false) {
|
|
175
|
+
let s = { ...s0, busy: false };
|
|
176
|
+
const outcome = e.outcome;
|
|
177
|
+
const interrupted = outcome?.type === "interrupt" && Array.isArray(outcome.interrupts) && outcome.interrupts.length > 0;
|
|
178
|
+
s.permissions = interrupted ? outcome.interrupts.map((i) => toPermission(i, s.turns)) : [];
|
|
179
|
+
if (interrupted) {
|
|
180
|
+
const waiting = new Set(s.permissions.map((p) => p.toolCallId).filter(Boolean));
|
|
181
|
+
if (waiting.size) {
|
|
182
|
+
s = { ...s, turns: s.turns.map((t) => t.tools?.some((x) => waiting.has(x.id)) ? { ...t, tools: t.tools.map((x) => waiting.has(x.id) && x.status !== "completed" ? { ...x, status: "pending" } : x) } : t) };
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
const usage = Array.isArray(e.usage) && e.usage.length ? e.usage.reduce(
|
|
186
|
+
(a, u) => ({ ...a, used: a.used + (u.totalTokens ?? (u.inputTokens ?? 0) + (u.outputTokens ?? 0)), input: (a.input ?? 0) + (u.inputTokens ?? 0), output: (a.output ?? 0) + (u.outputTokens ?? 0) }),
|
|
187
|
+
{ used: 0, size: 0, cost: 0 }
|
|
188
|
+
) : null;
|
|
189
|
+
const t0 = s.turnStartedAt;
|
|
190
|
+
const seconds = t0 ? Math.max(1, Math.round((now - t0) / 1e3)) : void 0;
|
|
191
|
+
const turns = [...s.turns];
|
|
192
|
+
const last = turns[turns.length - 1];
|
|
193
|
+
if (last?.role === "assistant") turns[turns.length - 1] = { ...last, ...seconds !== void 0 ? { seconds } : {}, ...usage ? { usage } : {} };
|
|
194
|
+
s = { ...s, turns, turnStartedAt: 0, ...usage ? { usage } : {} };
|
|
195
|
+
if (!t0) return s;
|
|
196
|
+
const effect = {
|
|
197
|
+
type: "turn-done",
|
|
198
|
+
text: last?.role === "assistant" ? last.text : "",
|
|
199
|
+
runId: e.runId ?? s.runId ?? void 0,
|
|
200
|
+
...seconds !== void 0 ? { seconds } : {},
|
|
201
|
+
...failed ? { error: s.error ?? void 0 } : {},
|
|
202
|
+
...interrupted ? { interrupted: true } : {}
|
|
203
|
+
};
|
|
204
|
+
return { ...s, effects: [effect] };
|
|
205
|
+
}
|
|
206
|
+
function beginSend(prev, text, attachments = [], now = Date.now()) {
|
|
207
|
+
const turns = text || attachments.length ? [...prev.turns, { role: "user", text, ...attachments.length ? { attachments } : {} }] : prev.turns;
|
|
208
|
+
return { ...prev, turns, busy: true, error: null, notice: null, turnStartedAt: now, effects: [{ type: "turn-start" }] };
|
|
209
|
+
}
|
|
210
|
+
function sendFailed(prev, message) {
|
|
211
|
+
return { ...prev, busy: false, turnStartedAt: 0, error: message, effects: [] };
|
|
212
|
+
}
|
|
213
|
+
function stopTurn(prev) {
|
|
214
|
+
return { ...prev, busy: false, turnStartedAt: 0, effects: [] };
|
|
215
|
+
}
|
|
216
|
+
function dropLastExchange(prev) {
|
|
217
|
+
const turns = [...prev.turns];
|
|
218
|
+
while (turns.length && turns[turns.length - 1].role === "assistant") turns.pop();
|
|
219
|
+
return { ...prev, turns: turns.slice(0, -1), effects: [] };
|
|
220
|
+
}
|
|
221
|
+
function lastUserTurn(turns) {
|
|
222
|
+
for (let i = turns.length - 1; i >= 0; i--) if (turns[i].role === "user") return turns[i];
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
function resolvePermission(prev, id, status, asked) {
|
|
226
|
+
const req = prev.permissions.find((p) => p.id === id) ?? asked;
|
|
227
|
+
const s = { ...prev, effects: [], permissions: prev.permissions.filter((p) => p.id !== id) };
|
|
228
|
+
if (!req) return s;
|
|
229
|
+
const decision = { id, title: req.title, status };
|
|
230
|
+
const turns = [...s.turns];
|
|
231
|
+
let i = turns.length - 1;
|
|
232
|
+
while (i >= 0 && turns[i].role !== "assistant") i--;
|
|
233
|
+
if (i >= 0) turns[i] = { ...turns[i], decisions: [...turns[i].decisions ?? [], decision] };
|
|
234
|
+
return { ...s, turns };
|
|
235
|
+
}
|
|
236
|
+
function expiryMs(v) {
|
|
237
|
+
return typeof v === "number" ? v : Date.parse(v);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// src/core/narration.ts
|
|
241
|
+
var MARK = "\u2713";
|
|
242
|
+
var LINE = /^- ✓ (.+)$/;
|
|
243
|
+
function isNarration(chunk) {
|
|
244
|
+
const t = chunk.trim();
|
|
245
|
+
return t.length > 0 && t.length <= 300 && !/\n\s*\n/.test(t) && !t.includes("```");
|
|
246
|
+
}
|
|
247
|
+
function splitNarration(text, cuts) {
|
|
248
|
+
if (cuts?.length) {
|
|
249
|
+
const valid = cuts.filter((c) => c > 0 && c <= text.length);
|
|
250
|
+
if (valid.length) {
|
|
251
|
+
const chunks = [];
|
|
252
|
+
let from = 0;
|
|
253
|
+
for (const c of valid) {
|
|
254
|
+
const t = text.slice(from, c).trim();
|
|
255
|
+
if (t) chunks.push(t);
|
|
256
|
+
from = c;
|
|
257
|
+
}
|
|
258
|
+
if (chunks.every(isNarration)) {
|
|
259
|
+
return { steps: chunks.map((t) => t.replace(/\s*\n\s*/g, " ")), rest: text.slice(from).replace(/^\s+/, "") };
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return { steps: [], rest: text };
|
|
263
|
+
}
|
|
264
|
+
const lines = text.split("\n");
|
|
265
|
+
const steps = [];
|
|
266
|
+
let i = 0;
|
|
267
|
+
for (; i < lines.length; i++) {
|
|
268
|
+
const m = LINE.exec(lines[i]);
|
|
269
|
+
if (!m) break;
|
|
270
|
+
steps.push(m[1].trim());
|
|
271
|
+
}
|
|
272
|
+
if (!steps.length) return { steps, rest: text };
|
|
273
|
+
return { steps, rest: lines.slice(i).join("\n").replace(/^\s+/, "") };
|
|
274
|
+
}
|
|
275
|
+
function narrationToMarkdown(chunks, answer) {
|
|
276
|
+
const steps = chunks.map((t) => t.trim()).filter(Boolean);
|
|
277
|
+
if (!steps.length || !steps.every(isNarration)) return answer;
|
|
278
|
+
const list = steps.map((p) => `- ${MARK} ${p.replace(/\s*\n\s*/g, " ")}`).join("\n");
|
|
279
|
+
return answer.trim() ? `${list}
|
|
280
|
+
|
|
281
|
+
${answer.trim()}` : list;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// src/core/options.ts
|
|
285
|
+
var BULLET = /^\s*(?:[-*•+]|(\d{1,2})[.)])\s+(.+?)\s*$/;
|
|
286
|
+
var INVITES = /(puedo|quieres|elige|eliges|prefieres|opciones|caminos|can i|would you|want me|choose|prefer|options)/i;
|
|
287
|
+
function extractReplyOptions(text) {
|
|
288
|
+
const lines = text.replace(/\s+$/, "").split("\n");
|
|
289
|
+
const items = [];
|
|
290
|
+
let numbered = false;
|
|
291
|
+
let i = lines.length - 1;
|
|
292
|
+
for (; i >= 0; i--) {
|
|
293
|
+
const m = BULLET.exec(lines[i]);
|
|
294
|
+
if (!m) break;
|
|
295
|
+
if (m[1]) numbered = true;
|
|
296
|
+
items.unshift(m[2]);
|
|
297
|
+
}
|
|
298
|
+
if (items.length < 2 || items.length > 5) return null;
|
|
299
|
+
if (items.some((t) => t.length > 140 || /```|`|https?:\/\/|\]\(/.test(t))) return null;
|
|
300
|
+
let j = i;
|
|
301
|
+
while (j >= 0 && !lines[j].trim()) j--;
|
|
302
|
+
if (j < 0) return null;
|
|
303
|
+
const invitation = lines[j].trim();
|
|
304
|
+
if (!invitation.endsWith(":") && !INVITES.test(invitation)) return null;
|
|
305
|
+
return { before: lines.slice(0, i + 1).join("\n").trimEnd(), items, numbered };
|
|
306
|
+
}
|
|
307
|
+
function cleanReplyOption(t) {
|
|
308
|
+
let s = t.replace(/\*\*|__/g, "").trim();
|
|
309
|
+
s = s.replace(/^(?:o|y|u|or|and)\s+/i, "").replace(/[\s,.;]+$/, "").trim();
|
|
310
|
+
return s.charAt(0).toLocaleUpperCase() + s.slice(1);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// src/core/steps.ts
|
|
314
|
+
var isRunning = (t) => t.status !== "completed" && t.status !== "failed";
|
|
315
|
+
function toolNames(t) {
|
|
316
|
+
const title = t.title ?? "";
|
|
317
|
+
if (!title) return [];
|
|
318
|
+
const out = [title];
|
|
319
|
+
const double = /^mcp__.+?__(.+)$/i.exec(title);
|
|
320
|
+
if (double) out.push(double[1]);
|
|
321
|
+
else if (/^mcp_/i.test(title)) {
|
|
322
|
+
const parts = title.slice(4).split("_");
|
|
323
|
+
for (let i = 0; i < parts.length; i++) out.push(parts.slice(i).join("_"));
|
|
324
|
+
}
|
|
325
|
+
return [...new Set(out)];
|
|
326
|
+
}
|
|
327
|
+
function toolName(t) {
|
|
328
|
+
const names = toolNames(t);
|
|
329
|
+
return /^mcp__/i.test(names[0] ?? "") ? names[1] ?? names[0] : names[0] ?? null;
|
|
330
|
+
}
|
|
331
|
+
function namedVerb(t, verbs) {
|
|
332
|
+
for (const name of toolNames(t)) if (verbs[name]) return { key: name, verb: verbs[name] };
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
function stepLabel(t, verbs, fallback = "Paso") {
|
|
336
|
+
const named = namedVerb(t, verbs);
|
|
337
|
+
if (named) return isRunning(t) && named.verb.running ? named.verb.running : named.verb.one;
|
|
338
|
+
return t.title ?? t.kind ?? fallback;
|
|
339
|
+
}
|
|
340
|
+
function summarizeTools(tools, verbs) {
|
|
341
|
+
const groups = /* @__PURE__ */ new Map();
|
|
342
|
+
for (const t of tools) {
|
|
343
|
+
const named = namedVerb(t, verbs);
|
|
344
|
+
const key = named?.key ?? t.kind ?? "other";
|
|
345
|
+
const g = groups.get(key) ?? { n: 0, verb: named?.verb ?? verbs[key] };
|
|
346
|
+
g.n++;
|
|
347
|
+
groups.set(key, g);
|
|
348
|
+
}
|
|
349
|
+
const parts = [];
|
|
350
|
+
for (const [key, { n, verb }] of groups) {
|
|
351
|
+
if (verb) parts.push(n === 1 ? verb.one : verb.many.replace("%n", String(n)));
|
|
352
|
+
else parts.push(`${n} \xD7 ${key.replace(/[_-]/g, " ")}`);
|
|
353
|
+
}
|
|
354
|
+
return parts.join(" \xB7 ");
|
|
355
|
+
}
|
|
356
|
+
function groupSteps(tools, label) {
|
|
357
|
+
const groups = /* @__PURE__ */ new Map();
|
|
358
|
+
for (const t of tools) {
|
|
359
|
+
const k = `${label(t)}\0${t.detail ?? ""}`;
|
|
360
|
+
const g = groups.get(k);
|
|
361
|
+
if (!g) {
|
|
362
|
+
groups.set(k, { tool: t, times: 1 });
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
g.times++;
|
|
366
|
+
const wins = isRunning(t) || t.status === "failed" || !(isRunning(g.tool) || g.tool.status === "failed");
|
|
367
|
+
if (wins) g.tool = { ...t, id: g.tool.id };
|
|
368
|
+
}
|
|
369
|
+
return [...groups.values()];
|
|
370
|
+
}
|
|
371
|
+
var TOOL_VERBS_ES = {
|
|
372
|
+
read: { one: "Le\xED un archivo", many: "Le\xED %n archivos" },
|
|
373
|
+
read_file: { one: "Le\xED un archivo", many: "Le\xED %n archivos" },
|
|
374
|
+
write: { one: "Escrib\xED un archivo", many: "Escrib\xED %n archivos" },
|
|
375
|
+
write_file: { one: "Escrib\xED un archivo", many: "Escrib\xED %n archivos" },
|
|
376
|
+
edit: { one: "Edit\xE9 un archivo", many: "Edit\xE9 %n archivos" },
|
|
377
|
+
edit_file: { one: "Edit\xE9 un archivo", many: "Edit\xE9 %n archivos" },
|
|
378
|
+
bash: { one: "Corr\xED un comando", many: "Corr\xED %n comandos" },
|
|
379
|
+
shell: { one: "Corr\xED un comando", many: "Corr\xED %n comandos" },
|
|
380
|
+
terminal: { one: "Corr\xED un comando", many: "Corr\xED %n comandos" },
|
|
381
|
+
execute: { one: "Corr\xED un comando", many: "Corr\xED %n comandos" },
|
|
382
|
+
grep: { one: "Busqu\xE9 en el c\xF3digo", many: "Busqu\xE9 en el c\xF3digo" },
|
|
383
|
+
glob: { one: "Busqu\xE9 archivos", many: "Busqu\xE9 archivos" },
|
|
384
|
+
search: { one: "Busqu\xE9", many: "Busqu\xE9 %n veces" },
|
|
385
|
+
tool_search: { one: "Busqu\xE9 una herramienta", many: "Busqu\xE9 %n herramientas" },
|
|
386
|
+
web_search: { one: "Busqu\xE9 en la web", many: "Busqu\xE9 en la web" },
|
|
387
|
+
web_fetch: { one: "Abr\xED una p\xE1gina", many: "Abr\xED %n p\xE1ginas" },
|
|
388
|
+
fetch: { one: "Abr\xED una p\xE1gina", many: "Abr\xED %n p\xE1ginas" },
|
|
389
|
+
site_publish: { one: "Publiqu\xE9 una p\xE1gina", many: "Publiqu\xE9 %n p\xE1ginas" },
|
|
390
|
+
pdf: { one: "Gener\xE9 un PDF", many: "Gener\xE9 %n PDF" },
|
|
391
|
+
connector: { one: "Us\xE9 tu sistema", many: "Us\xE9 tu sistema %n veces" },
|
|
392
|
+
mcp: { one: "Us\xE9 una herramienta", many: "Us\xE9 %n herramientas" },
|
|
393
|
+
browser: { one: "Us\xE9 el navegador", many: "Us\xE9 el navegador" },
|
|
394
|
+
todo: { one: "Anot\xE9 el plan", many: "Anot\xE9 el plan" },
|
|
395
|
+
delete: { one: "Borr\xE9 un archivo", many: "Borr\xE9 %n archivos" },
|
|
396
|
+
move: { one: "Mov\xED un archivo", many: "Mov\xED %n archivos" },
|
|
397
|
+
think: { one: "Lo pens\xE9", many: "Lo pens\xE9" },
|
|
398
|
+
image: { one: "Cre\xE9 una imagen", many: "Cre\xE9 %n im\xE1genes" },
|
|
399
|
+
delegate: { one: "Delegu\xE9 una tarea", many: "Delegu\xE9 %n tareas" },
|
|
400
|
+
collect: { one: "Recog\xED un resultado", many: "Recog\xED %n resultados" },
|
|
401
|
+
status: { one: "Pregunt\xE9 c\xF3mo iba", many: "Pregunt\xE9 %n veces c\xF3mo iba" },
|
|
402
|
+
stop: { one: "Detuve una tarea", many: "Detuve %n tareas" },
|
|
403
|
+
guide: { one: "Le\xED una gu\xEDa", many: "Le\xED %n gu\xEDas" },
|
|
404
|
+
deliver: { one: "Entregu\xE9 un archivo", many: "Entregu\xE9 %n archivos" },
|
|
405
|
+
sheet: { one: "Consult\xE9 una hoja", many: "Consult\xE9 %n hojas" },
|
|
406
|
+
view: { one: "Arm\xE9 una vista", many: "Arm\xE9 %n vistas" }
|
|
407
|
+
};
|
|
408
|
+
var TOOL_VERBS_EN = {
|
|
409
|
+
read: { one: "Read a file", many: "Read %n files" },
|
|
410
|
+
read_file: { one: "Read a file", many: "Read %n files" },
|
|
411
|
+
write: { one: "Wrote a file", many: "Wrote %n files" },
|
|
412
|
+
write_file: { one: "Wrote a file", many: "Wrote %n files" },
|
|
413
|
+
edit: { one: "Edited a file", many: "Edited %n files" },
|
|
414
|
+
edit_file: { one: "Edited a file", many: "Edited %n files" },
|
|
415
|
+
bash: { one: "Ran a command", many: "Ran %n commands" },
|
|
416
|
+
shell: { one: "Ran a command", many: "Ran %n commands" },
|
|
417
|
+
terminal: { one: "Ran a command", many: "Ran %n commands" },
|
|
418
|
+
execute: { one: "Ran a command", many: "Ran %n commands" },
|
|
419
|
+
grep: { one: "Searched the code", many: "Searched the code" },
|
|
420
|
+
glob: { one: "Searched for files", many: "Searched for files" },
|
|
421
|
+
search: { one: "Searched", many: "Searched %n times" },
|
|
422
|
+
tool_search: { one: "Looked for a tool", many: "Looked for %n tools" },
|
|
423
|
+
web_search: { one: "Searched the web", many: "Searched the web" },
|
|
424
|
+
web_fetch: { one: "Opened a page", many: "Opened %n pages" },
|
|
425
|
+
fetch: { one: "Opened a page", many: "Opened %n pages" },
|
|
426
|
+
site_publish: { one: "Published a page", many: "Published %n pages" },
|
|
427
|
+
pdf: { one: "Generated a PDF", many: "Generated %n PDFs" },
|
|
428
|
+
connector: { one: "Used your system", many: "Used your system %n times" },
|
|
429
|
+
mcp: { one: "Used a tool", many: "Used %n tools" },
|
|
430
|
+
browser: { one: "Used the browser", many: "Used the browser" },
|
|
431
|
+
todo: { one: "Wrote down the plan", many: "Wrote down the plan" },
|
|
432
|
+
delete: { one: "Deleted a file", many: "Deleted %n files" },
|
|
433
|
+
move: { one: "Moved a file", many: "Moved %n files" },
|
|
434
|
+
think: { one: "Thought it through", many: "Thought it through" },
|
|
435
|
+
image: { one: "Created an image", many: "Created %n images" },
|
|
436
|
+
delegate: { one: "Delegated a task", many: "Delegated %n tasks" },
|
|
437
|
+
collect: { one: "Collected a result", many: "Collected %n results" },
|
|
438
|
+
status: { one: "Checked how it was going", many: "Checked %n times how it was going" },
|
|
439
|
+
stop: { one: "Stopped a task", many: "Stopped %n tasks" },
|
|
440
|
+
guide: { one: "Read a guide", many: "Read %n guides" },
|
|
441
|
+
deliver: { one: "Delivered a file", many: "Delivered %n files" },
|
|
442
|
+
sheet: { one: "Checked a sheet", many: "Checked %n sheets" },
|
|
443
|
+
view: { one: "Built a view", many: "Built %n views" }
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
export {
|
|
447
|
+
createPilotState,
|
|
448
|
+
argsDetail,
|
|
449
|
+
toPermission,
|
|
450
|
+
messagesToTurns,
|
|
451
|
+
reduceAgUiEvent,
|
|
452
|
+
beginSend,
|
|
453
|
+
sendFailed,
|
|
454
|
+
stopTurn,
|
|
455
|
+
dropLastExchange,
|
|
456
|
+
lastUserTurn,
|
|
457
|
+
resolvePermission,
|
|
458
|
+
expiryMs,
|
|
459
|
+
isNarration,
|
|
460
|
+
splitNarration,
|
|
461
|
+
narrationToMarkdown,
|
|
462
|
+
extractReplyOptions,
|
|
463
|
+
cleanReplyOption,
|
|
464
|
+
isRunning,
|
|
465
|
+
toolNames,
|
|
466
|
+
toolName,
|
|
467
|
+
stepLabel,
|
|
468
|
+
summarizeTools,
|
|
469
|
+
groupSteps,
|
|
470
|
+
TOOL_VERBS_ES,
|
|
471
|
+
TOOL_VERBS_EN
|
|
472
|
+
};
|
package/dist/core/index.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
export * from "./types";
|
|
2
|
-
export {
|
|
3
|
-
export { reconcileTurns } from "./reconcile";
|
|
2
|
+
export { argsDetail, beginSend, createPilotState, dropLastExchange, expiryMs, lastUserTurn, messagesToTurns, reduceAgUiEvent, resolvePermission, sendFailed, stopTurn, toPermission, } from "./reducer";
|
|
4
3
|
export { isNarration, narrationToMarkdown, splitNarration } from "./narration";
|
|
5
4
|
export { cleanReplyOption, extractReplyOptions, type ReplyOptions } from "./options";
|
|
6
|
-
export { TOOL_VERBS_EN, TOOL_VERBS_ES, groupSteps, isRunning, stepLabel, summarizeTools, toolName, type ToolVerb, type ToolVerbs, } from "./steps";
|
|
5
|
+
export { TOOL_VERBS_EN, TOOL_VERBS_ES, groupSteps, isRunning, stepLabel, summarizeTools, toolName, toolNames, type ToolVerb, type ToolVerbs, } from "./steps";
|
|
7
6
|
export { applyReferences, cleanUserText, formatDuration, systemNoteOf } from "./text";
|
package/dist/core/index.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
-
RESTART_NOTICE,
|
|
3
|
-
RESUME_TEXT,
|
|
4
2
|
TOOL_VERBS_EN,
|
|
5
3
|
TOOL_VERBS_ES,
|
|
6
|
-
|
|
7
|
-
applyHistory,
|
|
4
|
+
argsDetail,
|
|
8
5
|
beginSend,
|
|
9
6
|
cleanReplyOption,
|
|
10
7
|
createPilotState,
|
|
@@ -15,21 +12,19 @@ import {
|
|
|
15
12
|
isNarration,
|
|
16
13
|
isRunning,
|
|
17
14
|
lastUserTurn,
|
|
15
|
+
messagesToTurns,
|
|
18
16
|
narrationToMarkdown,
|
|
19
|
-
|
|
20
|
-
reconcileWith,
|
|
21
|
-
reducePilotEvent,
|
|
17
|
+
reduceAgUiEvent,
|
|
22
18
|
resolvePermission,
|
|
23
|
-
resumeMode,
|
|
24
|
-
sendAccepted,
|
|
25
19
|
sendFailed,
|
|
26
|
-
sendRejectedBusy,
|
|
27
20
|
splitNarration,
|
|
28
21
|
stepLabel,
|
|
29
22
|
stopTurn,
|
|
30
23
|
summarizeTools,
|
|
31
|
-
|
|
32
|
-
|
|
24
|
+
toPermission,
|
|
25
|
+
toolName,
|
|
26
|
+
toolNames
|
|
27
|
+
} from "../chunks/chunk-HPTM2BCM.js";
|
|
33
28
|
import {
|
|
34
29
|
applyReferences,
|
|
35
30
|
cleanUserText,
|
|
@@ -37,13 +32,10 @@ import {
|
|
|
37
32
|
systemNoteOf
|
|
38
33
|
} from "../chunks/chunk-6U545WJG.js";
|
|
39
34
|
export {
|
|
40
|
-
RESTART_NOTICE,
|
|
41
|
-
RESUME_TEXT,
|
|
42
35
|
TOOL_VERBS_EN,
|
|
43
36
|
TOOL_VERBS_ES,
|
|
44
|
-
appendAgentMessages,
|
|
45
|
-
applyHistory,
|
|
46
37
|
applyReferences,
|
|
38
|
+
argsDetail,
|
|
47
39
|
beginSend,
|
|
48
40
|
cleanReplyOption,
|
|
49
41
|
cleanUserText,
|
|
@@ -56,19 +48,17 @@ export {
|
|
|
56
48
|
isNarration,
|
|
57
49
|
isRunning,
|
|
58
50
|
lastUserTurn,
|
|
51
|
+
messagesToTurns,
|
|
59
52
|
narrationToMarkdown,
|
|
60
|
-
|
|
61
|
-
reconcileWith,
|
|
62
|
-
reducePilotEvent,
|
|
53
|
+
reduceAgUiEvent,
|
|
63
54
|
resolvePermission,
|
|
64
|
-
resumeMode,
|
|
65
|
-
sendAccepted,
|
|
66
55
|
sendFailed,
|
|
67
|
-
sendRejectedBusy,
|
|
68
56
|
splitNarration,
|
|
69
57
|
stepLabel,
|
|
70
58
|
stopTurn,
|
|
71
59
|
summarizeTools,
|
|
72
60
|
systemNoteOf,
|
|
73
|
-
|
|
61
|
+
toPermission,
|
|
62
|
+
toolName,
|
|
63
|
+
toolNames
|
|
74
64
|
};
|
package/dist/core/reducer.d.ts
CHANGED
|
@@ -1,54 +1,36 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
export declare const RESTART_NOTICE = "El servidor se reinici\u00F3 a media respuesta. Vuelve a enviar el mensaje.";
|
|
4
|
-
/** Lo que se ve en el hilo al retomar (el servidor manda al agente la continuación entera). */
|
|
5
|
-
export declare const RESUME_TEXT = "Contin\u00FAa donde te quedaste.";
|
|
1
|
+
import type { Interrupt, Message } from "@ag-ui/core";
|
|
2
|
+
import type { Attachment, Decision, PermissionRequest, PilotState, Turn } from "./types";
|
|
6
3
|
export declare function createPilotState(turns?: Turn[]): PilotState;
|
|
4
|
+
type Ev = {
|
|
5
|
+
type: string;
|
|
6
|
+
[k: string]: any;
|
|
7
|
+
};
|
|
8
|
+
/** Los argumentos de una tool en corto, para el renglón del paso: «job: purge-notifications». */
|
|
9
|
+
export declare function argsDetail(json?: string): string | undefined;
|
|
10
|
+
/** Un `Interrupt` de AG-UI como tarjeta: título, herramienta (por su toolCallId) y vencimiento. */
|
|
11
|
+
export declare function toPermission(i: Interrupt, turns: Turn[]): PermissionRequest;
|
|
7
12
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
13
|
+
* El hilo guardado (mensajes AG-UI) como turnos: cada mensaje del usuario abre uno; lo que sigue del
|
|
14
|
+
* asistente y de las herramientas se junta en un turno, con sus pasos y su narración.
|
|
10
15
|
*/
|
|
11
|
-
export declare function
|
|
12
|
-
/** `
|
|
13
|
-
export declare function
|
|
14
|
-
/**
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
/** El servidor aceptó el encargo. `inFlight` = había turno en vuelo al mandar. */
|
|
20
|
-
export declare function sendAccepted(prev: PilotState, r: SendResult, inFlight: boolean, now?: number): PilotState;
|
|
21
|
-
/**
|
|
22
|
-
* El servidor ya tiene un turno corriendo (409 `turn_busy`): el mensaje de este cliente NO entró.
|
|
23
|
-
* Se quita de la pantalla y se avisa; el transporte se re-engancha al turno que sí corre.
|
|
24
|
-
*/
|
|
25
|
-
export declare function sendRejectedBusy(prev: PilotState, message: string): PilotState;
|
|
26
|
-
/** El encargo fue rechazado (sin saldo, sin cuenta…): se DICE, para que no se quede pensando. */
|
|
27
|
-
export declare function sendFailed(prev: PilotState, message: string, needsLogin?: boolean): PilotState;
|
|
28
|
-
/** Detener el turno en curso (el POST de cancelar lo hace el transporte). */
|
|
16
|
+
export declare function messagesToTurns(messages: Message[]): Turn[];
|
|
17
|
+
/** Un evento AG-UI → estado nuevo. `now` se inyecta para que el reductor sea puro. */
|
|
18
|
+
export declare function reduceAgUiEvent(prev: PilotState, e: Ev, now?: number): PilotState;
|
|
19
|
+
/** El usuario manda un mensaje (o contesta una tarjeta: `text` vacío, sin burbuja). */
|
|
20
|
+
export declare function beginSend(prev: PilotState, text: string, attachments?: Attachment[], now?: number): PilotState;
|
|
21
|
+
/** El servidor no aceptó el encargo (HTTP): se DICE, para que no se quede pensando. */
|
|
22
|
+
export declare function sendFailed(prev: PilotState, message: string): PilotState;
|
|
23
|
+
/** Detener: el cliente cortó la conexión (el servidor corta el run). */
|
|
29
24
|
export declare function stopTurn(prev: PilotState): PilotState;
|
|
30
25
|
/** Quita el último intercambio (las respuestas del final y el último mensaje del usuario). */
|
|
31
26
|
export declare function dropLastExchange(prev: PilotState): PilotState;
|
|
32
|
-
/**
|
|
33
|
-
* El usuario contestó una tarjeta: sale de las abiertas y queda anotada en la última respuesta
|
|
34
|
-
* («Aprobaste: correr la purga»). `resolved: false` = ya no estaba abierta (venció o la contestó otro).
|
|
35
|
-
*/
|
|
36
|
-
export declare function resolvePermission(prev: PilotState, id: string, optionId: string, resolved: boolean, detail?: string,
|
|
37
|
-
/** La tarjeta como estaba al contestarla (el stream pudo retirarla mientras tanto). */
|
|
38
|
-
asked?: PermissionRequest): PilotState;
|
|
39
|
-
/**
|
|
40
|
-
* El historial guardado (al montar): el hilo, si la pantalla sigue vacía, y las tarjetas de permiso
|
|
41
|
-
* que siguen abiertas (sin duplicar las que el stream ya trajo).
|
|
42
|
-
*/
|
|
43
|
-
export declare function applyHistory(prev: PilotState, turns: Turn[], permissions?: PermissionRequest[]): PilotState;
|
|
44
|
-
/** Un mensaje del agente que entró fuera de un turno (el remate de los subagentes). */
|
|
45
|
-
export declare function appendAgentMessages(prev: PilotState, texts: string[]): PilotState;
|
|
46
|
-
/** La copia del hilo cambió: trae los mensajes que el usuario mandó desde OTRO cliente. Sólo en reposo. */
|
|
47
|
-
export declare function reconcileWith(prev: PilotState, fresh: Turn[]): PilotState;
|
|
48
|
-
/** El último mensaje del usuario (para reintentar o retomar). */
|
|
27
|
+
/** El último mensaje del usuario (para reintentar). */
|
|
49
28
|
export declare function lastUserTurn(turns: Turn[]): Turn | null;
|
|
50
29
|
/**
|
|
51
|
-
*
|
|
52
|
-
*
|
|
30
|
+
* El usuario contestó (o dejó vencer) una tarjeta: sale de las abiertas y queda anotada en la última
|
|
31
|
+
* respuesta («Aprobaste: correr la purga»).
|
|
53
32
|
*/
|
|
54
|
-
export declare function
|
|
33
|
+
export declare function resolvePermission(prev: PilotState, id: string, status: Decision["status"], asked?: PermissionRequest): PilotState;
|
|
34
|
+
/** `expiresAt` (ISO o epoch ms) → epoch ms. */
|
|
35
|
+
export declare function expiryMs(v: string | number): number;
|
|
36
|
+
export {};
|
package/dist/core/steps.d.ts
CHANGED
|
@@ -10,7 +10,15 @@ export interface ToolVerb {
|
|
|
10
10
|
export type ToolVerbs = Record<string, ToolVerb>;
|
|
11
11
|
/** ¿Sigue corriendo? `pending` también: es el instante entre pedirla y arrancarla. */
|
|
12
12
|
export declare const isRunning: (t: ToolEntry) => boolean;
|
|
13
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* Los nombres con los que se puede buscar el verbo/ícono de una herramienta, del más específico al
|
|
15
|
+
* menos: el título tal cual, y sin el prefijo de MCP. Los MCP la nombran `mcp__servidor__tool` (dos
|
|
16
|
+
* guiones bajos) o `mcp_servidor_tool` (uno, visto en EasyBits); como el servidor y la tool también
|
|
17
|
+
* llevan guiones bajos, con uno solo se prueban todos los cortes: `mcp_easy_bits_list_crons` →
|
|
18
|
+
* `easy_bits_list_crons`, `bits_list_crons`, `list_crons`, `crons`.
|
|
19
|
+
*/
|
|
20
|
+
export declare function toolNames(t: ToolEntry): string[];
|
|
21
|
+
/** El nombre «corto» de la herramienta (sin el prefijo de MCP de dos guiones bajos). */
|
|
14
22
|
export declare function toolName(t: ToolEntry): string | null;
|
|
15
23
|
/** El rótulo de un paso: el verbo de su nombre, o el título que manda el servidor, o su clase. */
|
|
16
24
|
export declare function stepLabel(t: ToolEntry, verbs: ToolVerbs, fallback?: string): string;
|