@alisio/core 0.1.0-alpha.17 → 0.1.0-alpha.19
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/agents/active.d.ts +66 -0
- package/dist/agents/active.js +113 -0
- package/dist/application.d.ts +12 -3
- package/dist/application.js +25 -5
- package/dist/commands/builtins.d.ts +10 -0
- package/dist/commands/builtins.js +122 -0
- package/dist/commands/catalog.d.ts +86 -0
- package/dist/commands/catalog.js +292 -0
- package/dist/config.d.ts +7 -0
- package/dist/config.js +27 -0
- package/dist/core/contracts.d.ts +109 -2
- package/dist/core/runner.d.ts +21 -0
- package/dist/core/runner.js +73 -12
- package/dist/index.d.ts +9 -4
- package/dist/index.js +8 -3
- package/dist/mcp/rich.js +68 -0
- package/dist/plugins/host.d.ts +4 -0
- package/dist/plugins/host.js +22 -8
- package/dist/providers/settings.d.ts +15 -0
- package/dist/providers/settings.js +38 -0
- package/dist/runtime/blobs.d.ts +29 -0
- package/dist/runtime/blobs.js +94 -0
- package/dist/runtime/diff.d.ts +23 -0
- package/dist/runtime/diff.js +154 -0
- package/dist/runtime/store.d.ts +75 -3
- package/dist/runtime/store.js +309 -11
- package/dist/sessions/side-questions.d.ts +57 -0
- package/dist/sessions/side-questions.js +161 -0
- package/dist/tools/standard.js +59 -9
- package/package.json +2 -2
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import { agentCatalogFromState, resolveActiveAgent } from "../agents/active.js";
|
|
2
|
+
import { formatSideQuestion, SIDE_QUESTION_DESCRIPTION, SIDE_QUESTION_USAGE, } from "../sessions/side-questions.js";
|
|
3
|
+
import { BUILTIN_COMMANDS } from "./builtins.js";
|
|
4
|
+
export { BUILTIN_COMMANDS };
|
|
5
|
+
const builtinDescriptor = (c) => ({
|
|
6
|
+
name: c.name,
|
|
7
|
+
description: c.description,
|
|
8
|
+
...(c.aliases ? { aliases: [...c.aliases] } : {}),
|
|
9
|
+
...(c.argumentHint ? { argumentHint: c.argumentHint } : {}),
|
|
10
|
+
source: "builtin",
|
|
11
|
+
surfaces: [...c.surfaces],
|
|
12
|
+
execution: c.execution,
|
|
13
|
+
});
|
|
14
|
+
const ALL = ["tui", "web", "api"];
|
|
15
|
+
/** Root sessions of this workspace bound to the current provider, newest first (as the TUI). */
|
|
16
|
+
const workspaceSessions = (host) => host.store
|
|
17
|
+
.list()
|
|
18
|
+
.filter((s) => s.workspace === host.workspace && s.provider === host.provider.id);
|
|
19
|
+
const firstPrompt = (host, id) => {
|
|
20
|
+
const first = host.store.messages(id).find((m) => m.role === "user" && !m.summary);
|
|
21
|
+
return first?.role === "user" ? first.text.replace(/\s+/g, " ").slice(0, 60) : "";
|
|
22
|
+
};
|
|
23
|
+
const HANDLERS = {
|
|
24
|
+
async tools(_args, _ctx, host) {
|
|
25
|
+
const policy = host.runner.policy;
|
|
26
|
+
const rows = host.registry
|
|
27
|
+
.list()
|
|
28
|
+
.map((t) => {
|
|
29
|
+
const effect = t.effect ?? "external";
|
|
30
|
+
const state = effect === "read" || effect === "internal" || policy[effect]
|
|
31
|
+
? "enabled"
|
|
32
|
+
: host.runner.approvals && (effect === "write" || effect === "process")
|
|
33
|
+
? "ask"
|
|
34
|
+
: "disabled";
|
|
35
|
+
return `| \`${t.name}\` | ${effect} | ${state} |`;
|
|
36
|
+
})
|
|
37
|
+
.join("\n");
|
|
38
|
+
return {
|
|
39
|
+
text: `**Tools**\n\n| Tool | Effect | State |\n| --- | --- | --- |\n${rows}\n\n\`ask\` prompts before running (allow once / session / deny). Use --allow-write / --allow-process to pre-allow; --read-only disables them. Paths outside the workspace ask per directory; pre-allow with --add-dir or the \`additionalDirectories\` config key. \`internal\` tools (built-in plugins) only write Alisio's own state.`,
|
|
40
|
+
};
|
|
41
|
+
},
|
|
42
|
+
async sessions(_args, ctx, host) {
|
|
43
|
+
const list = workspaceSessions(host).slice(0, 20);
|
|
44
|
+
if (!list.length)
|
|
45
|
+
return { text: "No sessions in this workspace", tone: "notice" };
|
|
46
|
+
return {
|
|
47
|
+
text: [
|
|
48
|
+
"**Recent sessions** (use `/resume <id-prefix>`)",
|
|
49
|
+
"",
|
|
50
|
+
...list.map((s) => `- \`${s.id}\` ${s.id === ctx.sessionId ? "**(current)** " : ""}${s.model} · ${host.store.messages(s.id).length} msgs · ${firstPrompt(host, s.id) || "_empty_"}`),
|
|
51
|
+
].join("\n"),
|
|
52
|
+
data: list,
|
|
53
|
+
};
|
|
54
|
+
},
|
|
55
|
+
async resume(args, _ctx, host) {
|
|
56
|
+
const target = args.trim();
|
|
57
|
+
if (!target)
|
|
58
|
+
throw new Error("Usage: /resume <id>");
|
|
59
|
+
const matches = workspaceSessions(host).filter((s) => s.id.startsWith(target));
|
|
60
|
+
if (matches.length !== 1)
|
|
61
|
+
throw new Error(matches.length
|
|
62
|
+
? `Ambiguous session prefix: ${target}`
|
|
63
|
+
: `No session in this workspace matches ${target}`);
|
|
64
|
+
const found = matches[0];
|
|
65
|
+
return { text: `Resumed session ${found.id}`, tone: "notice", sessionId: found.id };
|
|
66
|
+
},
|
|
67
|
+
async clear(_args, ctx, host) {
|
|
68
|
+
const model = host.store.get(ctx.sessionId).model;
|
|
69
|
+
const created = host.store.create(host.workspace, host.provider.id, model);
|
|
70
|
+
return { text: `New session ${created.id}`, tone: "notice", sessionId: created.id };
|
|
71
|
+
},
|
|
72
|
+
async compact(args, ctx, host) {
|
|
73
|
+
const result = await host.runner.compact(ctx.sessionId, {
|
|
74
|
+
...(args.trim() ? { focus: args.trim() } : {}),
|
|
75
|
+
...(ctx.signal ? { signal: ctx.signal } : {}),
|
|
76
|
+
});
|
|
77
|
+
return result
|
|
78
|
+
? {
|
|
79
|
+
text: `Compacted ${result.replaced} messages: ~${result.before} → ~${result.after} tokens`,
|
|
80
|
+
tone: "notice",
|
|
81
|
+
data: result,
|
|
82
|
+
}
|
|
83
|
+
: { text: "Not enough history to compact", tone: "notice" };
|
|
84
|
+
},
|
|
85
|
+
async model(args, ctx, host) {
|
|
86
|
+
const model = args.trim();
|
|
87
|
+
if (!model)
|
|
88
|
+
throw new Error("Usage: /model <model id>");
|
|
89
|
+
host.runner.setModel(ctx.sessionId, model);
|
|
90
|
+
return { text: `Model set to \`${model}\``, tone: "notice", data: { model } };
|
|
91
|
+
},
|
|
92
|
+
async effort(args, _ctx, host) {
|
|
93
|
+
const level = args.trim();
|
|
94
|
+
if (!level)
|
|
95
|
+
return {
|
|
96
|
+
text: `Reasoning effort: ${host.config?.agents.effort ?? "model default"}`,
|
|
97
|
+
tone: "notice",
|
|
98
|
+
};
|
|
99
|
+
if (!host.updateSetting)
|
|
100
|
+
throw new Error("Effort changes are unavailable here");
|
|
101
|
+
const value = level === "!clear" ? undefined : level;
|
|
102
|
+
await host.updateSetting("agents.effort", value);
|
|
103
|
+
return {
|
|
104
|
+
text: value ? `Reasoning effort set to ${value}` : "Reasoning effort cleared",
|
|
105
|
+
tone: "notice",
|
|
106
|
+
data: { effort: value },
|
|
107
|
+
};
|
|
108
|
+
},
|
|
109
|
+
async stats(_args, ctx, host) {
|
|
110
|
+
const session = host.store.get(ctx.sessionId);
|
|
111
|
+
const runs = host.store.runs?.(ctx.sessionId) ?? [];
|
|
112
|
+
const tokens = runs.reduce((sum, r) => ({
|
|
113
|
+
input: sum.input + (r.usage?.input ?? 0),
|
|
114
|
+
output: sum.output + (r.usage?.output ?? 0),
|
|
115
|
+
}), { input: 0, output: 0 });
|
|
116
|
+
return {
|
|
117
|
+
text: [
|
|
118
|
+
"**Session statistics**",
|
|
119
|
+
"",
|
|
120
|
+
`- Session: \`${session.id}\``,
|
|
121
|
+
`- Model: \`${session.model}\``,
|
|
122
|
+
`- Messages: ${host.store.messages(session.id).length}`,
|
|
123
|
+
`- Runs: ${runs.length}`,
|
|
124
|
+
`- Tokens: in ${tokens.input} · out ${tokens.output}`,
|
|
125
|
+
].join("\n"),
|
|
126
|
+
data: { session, runs: runs.length, tokens },
|
|
127
|
+
};
|
|
128
|
+
},
|
|
129
|
+
async skills(_args, _ctx, host) {
|
|
130
|
+
const skills = host.skillCatalog?.() ?? [];
|
|
131
|
+
if (!skills.length)
|
|
132
|
+
return { text: "No skills found", tone: "notice" };
|
|
133
|
+
const state = (s) => s.effective ? "enabled" : s.shadowedBy ? `shadowed by ${s.shadowedBy}` : "disabled";
|
|
134
|
+
return {
|
|
135
|
+
text: `**Skills**\n\n| Skill | Scope | State |\n| --- | --- | --- |\n${skills
|
|
136
|
+
.map((s) => `| \`${s.displayId}\` | ${s.scope} | ${state(s)} |`)
|
|
137
|
+
.join("\n")}`,
|
|
138
|
+
data: skills,
|
|
139
|
+
};
|
|
140
|
+
},
|
|
141
|
+
async plugins(_args, _ctx, host) {
|
|
142
|
+
const plugins = host.pluginCatalog?.() ?? [];
|
|
143
|
+
if (!plugins.length)
|
|
144
|
+
return { text: "No plugins loaded", tone: "notice" };
|
|
145
|
+
return {
|
|
146
|
+
text: `**Plugins**\n\n| Plugin | Status | Enabled |\n| --- | --- | --- |\n${plugins
|
|
147
|
+
.map((p) => `| \`${p.id}\` | ${p.status} | ${p.enabled ? "yes" : "no"} |`)
|
|
148
|
+
.join("\n")}`,
|
|
149
|
+
data: plugins,
|
|
150
|
+
};
|
|
151
|
+
},
|
|
152
|
+
async mcp(_args, _ctx, host) {
|
|
153
|
+
const servers = host.mcp?.list() ?? [];
|
|
154
|
+
if (!servers.length)
|
|
155
|
+
return { text: "No MCP servers configured", tone: "notice" };
|
|
156
|
+
return {
|
|
157
|
+
text: `**MCP servers**\n\n| Server | Status | Tools |\n| --- | --- | --- |\n${servers
|
|
158
|
+
.map((s) => `| \`${s.name}\` | ${s.status} | ${s.counts.tools} |`)
|
|
159
|
+
.join("\n")}`,
|
|
160
|
+
data: servers,
|
|
161
|
+
};
|
|
162
|
+
},
|
|
163
|
+
async btw(args, ctx, host) {
|
|
164
|
+
const question = args.trim();
|
|
165
|
+
const side = host.sideQuestions;
|
|
166
|
+
if (!question) {
|
|
167
|
+
const history = side?.history(ctx.sessionId) ?? [];
|
|
168
|
+
const latest = history.at(-1);
|
|
169
|
+
if (!latest)
|
|
170
|
+
return {
|
|
171
|
+
text: `${SIDE_QUESTION_USAGE}\n${SIDE_QUESTION_DESCRIPTION}`,
|
|
172
|
+
tone: "notice",
|
|
173
|
+
data: { history },
|
|
174
|
+
};
|
|
175
|
+
return {
|
|
176
|
+
text: formatSideQuestion(latest, history.length, history.length),
|
|
177
|
+
data: { history, entry: latest },
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
if (!side)
|
|
181
|
+
throw new Error("Side questions are unavailable here");
|
|
182
|
+
const entry = await side.ask(ctx.sessionId, question, ctx.signal ? { signal: ctx.signal } : {});
|
|
183
|
+
const total = side.history(ctx.sessionId).length;
|
|
184
|
+
return { text: formatSideQuestion(entry, total, total), data: { entry } };
|
|
185
|
+
},
|
|
186
|
+
async agents(args, ctx, host) {
|
|
187
|
+
// With arguments, `/agents <verb>` belongs to the subagents plugin's own command (as in the
|
|
188
|
+
// TUI); without them it lists the active-agent catalog.
|
|
189
|
+
const plugin = args.trim() ? host.plugins?.commands.get("agents") : undefined;
|
|
190
|
+
if (plugin)
|
|
191
|
+
return { text: await plugin(args.trim(), { sessionId: ctx.sessionId }) };
|
|
192
|
+
let state;
|
|
193
|
+
try {
|
|
194
|
+
state = host.plugins?.pluginState?.("subagents", "mainAgents");
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
/* agent contributions are best-effort */
|
|
198
|
+
}
|
|
199
|
+
const agents = agentCatalogFromState(state);
|
|
200
|
+
const active = resolveActiveAgent(agents, host.config?.agents.active);
|
|
201
|
+
return {
|
|
202
|
+
text: [
|
|
203
|
+
"**Agents**",
|
|
204
|
+
"",
|
|
205
|
+
...agents.map((a) => `- **${a.name}**${a.id === active.id ? " (current)" : ""}${a.readOnly ? " · read-only" : ""} — ${a.description}`),
|
|
206
|
+
].join("\n"),
|
|
207
|
+
data: { agents, active: active.id },
|
|
208
|
+
};
|
|
209
|
+
},
|
|
210
|
+
};
|
|
211
|
+
export class CommandCatalog {
|
|
212
|
+
host;
|
|
213
|
+
constructor(host) {
|
|
214
|
+
this.host = host;
|
|
215
|
+
}
|
|
216
|
+
builtins() {
|
|
217
|
+
return BUILTIN_COMMANDS.map(builtinDescriptor);
|
|
218
|
+
}
|
|
219
|
+
/** Every descriptor (optionally for one surface). Precedence: builtin > plugin > prompt > skill. */
|
|
220
|
+
list(surface) {
|
|
221
|
+
const out = this.builtins();
|
|
222
|
+
const taken = new Set(out.flatMap((c) => [c.name, ...(c.aliases ?? [])]));
|
|
223
|
+
const push = (descriptor) => {
|
|
224
|
+
if (taken.has(descriptor.name))
|
|
225
|
+
return;
|
|
226
|
+
taken.add(descriptor.name);
|
|
227
|
+
out.push(descriptor);
|
|
228
|
+
};
|
|
229
|
+
const host = this.host;
|
|
230
|
+
if (host?.plugins)
|
|
231
|
+
for (const [name, info] of host.plugins.commandInfo)
|
|
232
|
+
push({
|
|
233
|
+
name,
|
|
234
|
+
description: info.description ?? `plugin ${info.plugin}`,
|
|
235
|
+
...(info.argumentHint ? { argumentHint: info.argumentHint } : {}),
|
|
236
|
+
source: "plugin",
|
|
237
|
+
owner: info.plugin,
|
|
238
|
+
surfaces: [...ALL],
|
|
239
|
+
execution: "core",
|
|
240
|
+
});
|
|
241
|
+
if (host?.prompts)
|
|
242
|
+
for (const template of host.prompts.templates.values())
|
|
243
|
+
push({
|
|
244
|
+
name: template.name,
|
|
245
|
+
description: template.description,
|
|
246
|
+
...(template.argumentHint ? { argumentHint: template.argumentHint } : {}),
|
|
247
|
+
source: "prompt",
|
|
248
|
+
owner: template.source,
|
|
249
|
+
surfaces: [...ALL],
|
|
250
|
+
execution: "surface",
|
|
251
|
+
});
|
|
252
|
+
for (const skill of host?.skillCatalog?.() ?? [])
|
|
253
|
+
if (skill.effective && skill.enabled)
|
|
254
|
+
push({
|
|
255
|
+
name: `skill:${skill.id}`,
|
|
256
|
+
description: skill.description,
|
|
257
|
+
source: "skill",
|
|
258
|
+
...(skill.owner ? { owner: skill.owner.id } : {}),
|
|
259
|
+
surfaces: [...ALL],
|
|
260
|
+
execution: "surface",
|
|
261
|
+
});
|
|
262
|
+
return surface ? out.filter((c) => c.surfaces.includes(surface)) : out;
|
|
263
|
+
}
|
|
264
|
+
/** A command by name or alias (built-in names and aliases are case-insensitive). */
|
|
265
|
+
resolve(name) {
|
|
266
|
+
const lower = name.toLowerCase();
|
|
267
|
+
const all = this.list();
|
|
268
|
+
return (all.find((c) => c.source === "builtin" && (c.name === lower || c.aliases?.includes(lower))) ??
|
|
269
|
+
all.find((c) => c.source !== "builtin" && c.name === name));
|
|
270
|
+
}
|
|
271
|
+
/** Runs an `execution: "core"` command. Surface commands and unknown names throw. */
|
|
272
|
+
async execute(name, args, ctx) {
|
|
273
|
+
const descriptor = this.resolve(name);
|
|
274
|
+
if (!descriptor)
|
|
275
|
+
throw new Error(`Unknown command /${name}. Type /help.`);
|
|
276
|
+
if (descriptor.execution !== "core")
|
|
277
|
+
throw new Error(`/${descriptor.name} is handled by each surface, not by the core catalog`);
|
|
278
|
+
const host = this.host;
|
|
279
|
+
if (!host)
|
|
280
|
+
throw new Error(`/${descriptor.name} needs an application host`);
|
|
281
|
+
if (descriptor.source === "plugin") {
|
|
282
|
+
const handler = host.plugins?.commands.get(descriptor.name);
|
|
283
|
+
if (!handler)
|
|
284
|
+
throw new Error(`Unknown plugin command: ${descriptor.name}`);
|
|
285
|
+
return { text: await handler(args, { sessionId: ctx.sessionId }) };
|
|
286
|
+
}
|
|
287
|
+
const handler = HANDLERS[descriptor.name];
|
|
288
|
+
if (!handler)
|
|
289
|
+
throw new Error(`/${descriptor.name} has no core handler`);
|
|
290
|
+
return handler(args, ctx, host);
|
|
291
|
+
}
|
|
292
|
+
}
|
package/dist/config.d.ts
CHANGED
|
@@ -476,6 +476,13 @@ declare const SETTABLE_KEYS: {
|
|
|
476
476
|
};
|
|
477
477
|
export type SettableSettingKey = keyof typeof SETTABLE_KEYS;
|
|
478
478
|
export declare function isSettableSettingKey(key: string): key is SettableSettingKey;
|
|
479
|
+
export interface SettableSettingInfo {
|
|
480
|
+
key: SettableSettingKey;
|
|
481
|
+
kind: "boolean" | "number" | "string" | "enum";
|
|
482
|
+
options?: string[];
|
|
483
|
+
}
|
|
484
|
+
/** Every settable key with its value kind (and enum options), for settings editors. */
|
|
485
|
+
export declare function settableSettings(): SettableSettingInfo[];
|
|
479
486
|
/**
|
|
480
487
|
* Atomically sets one user-facing setting in the global config file (`<config home>/config.json`)
|
|
481
488
|
* while preserving every unrelated JSON field. The value is validated against the same zod leaf
|
package/dist/config.js
CHANGED
|
@@ -551,6 +551,33 @@ const SETTABLE_KEYS = {
|
|
|
551
551
|
export function isSettableSettingKey(key) {
|
|
552
552
|
return Object.prototype.hasOwnProperty.call(SETTABLE_KEYS, key);
|
|
553
553
|
}
|
|
554
|
+
/** Unwraps optional/default/nullable wrappers down to the value schema. */
|
|
555
|
+
function settingLeaf(schema) {
|
|
556
|
+
let current = schema;
|
|
557
|
+
for (let depth = 0; depth < 8; depth++) {
|
|
558
|
+
const type = current.def?.type;
|
|
559
|
+
if ((type === "optional" || type === "default" || type === "nullable") &&
|
|
560
|
+
current.def?.innerType)
|
|
561
|
+
current = current.def.innerType;
|
|
562
|
+
else
|
|
563
|
+
break;
|
|
564
|
+
}
|
|
565
|
+
return current;
|
|
566
|
+
}
|
|
567
|
+
/** Every settable key with its value kind (and enum options), for settings editors. */
|
|
568
|
+
export function settableSettings() {
|
|
569
|
+
return Object.keys(SETTABLE_KEYS).map((key) => {
|
|
570
|
+
const leaf = settingLeaf(SETTABLE_KEYS[key]);
|
|
571
|
+
const type = leaf.def?.type;
|
|
572
|
+
if (type === "enum")
|
|
573
|
+
return { key, kind: "enum", options: (leaf.options ?? []).map((o) => String(o)) };
|
|
574
|
+
if (type === "boolean")
|
|
575
|
+
return { key, kind: "boolean" };
|
|
576
|
+
if (type === "number" || type === "int")
|
|
577
|
+
return { key, kind: "number" };
|
|
578
|
+
return { key, kind: "string" };
|
|
579
|
+
});
|
|
580
|
+
}
|
|
554
581
|
/**
|
|
555
582
|
* Atomically sets one user-facing setting in the global config file (`<config home>/config.json`)
|
|
556
583
|
* while preserving every unrelated JSON field. The value is validated against the same zod leaf
|
package/dist/core/contracts.d.ts
CHANGED
|
@@ -18,6 +18,10 @@ export interface Session {
|
|
|
18
18
|
options?: Record<string, unknown>;
|
|
19
19
|
createdAt?: number;
|
|
20
20
|
updatedAt?: number;
|
|
21
|
+
/** Root sessions pinned in web clients (v4). */
|
|
22
|
+
pinned?: boolean;
|
|
23
|
+
/** When a root session was archived in web clients (v4); absent when not archived. */
|
|
24
|
+
archivedAt?: number;
|
|
21
25
|
}
|
|
22
26
|
export interface ChildSessionRecord {
|
|
23
27
|
id?: string;
|
|
@@ -30,6 +34,82 @@ export interface ChildSessionRecord {
|
|
|
30
34
|
depth: number;
|
|
31
35
|
options: Record<string, unknown>;
|
|
32
36
|
}
|
|
37
|
+
/** `queued` and `running` are the only non-terminal run states. */
|
|
38
|
+
export type RunStatus = "queued" | "running" | "completed" | "turns_exceeded" | "failed" | "cancelled" | "interrupted";
|
|
39
|
+
export type TerminalRunStatus = Exclude<RunStatus, "queued" | "running">;
|
|
40
|
+
/** One `AgentRunner.run` execution (v4 `runs` table). `id` equals `RunEvent.runId`. */
|
|
41
|
+
export interface RunRecord {
|
|
42
|
+
id: string;
|
|
43
|
+
session: string;
|
|
44
|
+
status: RunStatus;
|
|
45
|
+
/** Client idempotency key; absent for TUI/headless runs. */
|
|
46
|
+
requestId?: string;
|
|
47
|
+
correlationId?: string;
|
|
48
|
+
/** Process that executes (or executed) the run, for startup reconciliation. */
|
|
49
|
+
ownerPid?: number;
|
|
50
|
+
model?: string;
|
|
51
|
+
createdAt: number;
|
|
52
|
+
startedAt?: number;
|
|
53
|
+
endedAt?: number;
|
|
54
|
+
error?: string;
|
|
55
|
+
usage?: {
|
|
56
|
+
input: number;
|
|
57
|
+
output: number;
|
|
58
|
+
cachedInput?: number;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export interface BeginRunInput {
|
|
62
|
+
id: string;
|
|
63
|
+
session: string;
|
|
64
|
+
/** `queued` for scheduled runs, `running` (default) when execution starts now. */
|
|
65
|
+
status?: "queued" | "running";
|
|
66
|
+
requestId?: string;
|
|
67
|
+
correlationId?: string;
|
|
68
|
+
model?: string;
|
|
69
|
+
}
|
|
70
|
+
export interface EndRunInput {
|
|
71
|
+
status: TerminalRunStatus;
|
|
72
|
+
error?: string;
|
|
73
|
+
usage?: {
|
|
74
|
+
input: number;
|
|
75
|
+
output: number;
|
|
76
|
+
cachedInput?: number;
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
export interface PageOptions {
|
|
80
|
+
/** Only rows with a sequence greater than this (ascending from there). */
|
|
81
|
+
after?: number;
|
|
82
|
+
/** Only rows with a sequence lower than this (the newest `limit` of them). */
|
|
83
|
+
before?: number;
|
|
84
|
+
limit?: number;
|
|
85
|
+
}
|
|
86
|
+
export interface MessagePage {
|
|
87
|
+
items: Array<{
|
|
88
|
+
seq: number;
|
|
89
|
+
message: Message;
|
|
90
|
+
compacted: boolean;
|
|
91
|
+
}>;
|
|
92
|
+
/** More rows exist beyond the page in the paging direction. */
|
|
93
|
+
hasMore: boolean;
|
|
94
|
+
}
|
|
95
|
+
export interface StoredEvent {
|
|
96
|
+
/** Global `events.seq` as a string (same value as `RunEvent.eventId`). */
|
|
97
|
+
eventId: string;
|
|
98
|
+
runId: string;
|
|
99
|
+
type: string;
|
|
100
|
+
data: unknown;
|
|
101
|
+
createdAt?: number;
|
|
102
|
+
correlationId?: string;
|
|
103
|
+
}
|
|
104
|
+
export interface EventPage {
|
|
105
|
+
items: StoredEvent[];
|
|
106
|
+
hasMore: boolean;
|
|
107
|
+
}
|
|
108
|
+
/** Optional tool-call metadata (v4 columns); stores may ignore it. */
|
|
109
|
+
export interface ToolCallMeta {
|
|
110
|
+
runId?: string;
|
|
111
|
+
effect?: string;
|
|
112
|
+
}
|
|
33
113
|
export interface SessionStore {
|
|
34
114
|
create(workspace: string, provider: string, model: string): Session;
|
|
35
115
|
get(id: string): Session;
|
|
@@ -64,12 +144,39 @@ export interface SessionStore {
|
|
|
64
144
|
overwrite(id: string, messages: Message[]): void;
|
|
65
145
|
acquire(id: string): void;
|
|
66
146
|
release(id: string): void;
|
|
67
|
-
beginCall(session: string, call: ToolCall): void;
|
|
147
|
+
beginCall(session: string, call: ToolCall, meta?: ToolCallMeta): void;
|
|
68
148
|
endCall(session: string, call: ToolCall, result: ToolResult): void;
|
|
69
149
|
/** The persisted result of a completed tool call, for replay of rich (ui/image) parts. */
|
|
70
150
|
callResult(session: string, callId: string): ToolResult | undefined;
|
|
71
151
|
reconcile(id: string, acknowledge?: boolean): void;
|
|
72
|
-
|
|
152
|
+
/**
|
|
153
|
+
* Persist a durable run event. Stores that assign a global, monotonic sequence return it; the
|
|
154
|
+
* runner exposes it as `RunEvent.eventId`. Returning nothing is allowed (no `eventId`).
|
|
155
|
+
*/
|
|
156
|
+
event(id: string, runId: string, type: string, data: unknown, meta?: {
|
|
157
|
+
correlationId?: string;
|
|
158
|
+
}): number | void;
|
|
159
|
+
/**
|
|
160
|
+
* Records a run, or moves an existing `queued` run with the same id to `running`. A run whose
|
|
161
|
+
* `(session, requestId)` already exists is returned as is (`created: false`): idempotency.
|
|
162
|
+
*/
|
|
163
|
+
beginRun?(input: BeginRunInput): {
|
|
164
|
+
run: RunRecord;
|
|
165
|
+
created: boolean;
|
|
166
|
+
};
|
|
167
|
+
/** Moves a non-terminal run to a terminal state; terminal runs are never overwritten. */
|
|
168
|
+
endRun?(id: string, input: EndRunInput): void;
|
|
169
|
+
runByRequest?(session: string, requestId: string): RunRecord | undefined;
|
|
170
|
+
/** Runs of a session, newest first. */
|
|
171
|
+
runs?(session: string, options?: {
|
|
172
|
+
limit?: number;
|
|
173
|
+
}): RunRecord[];
|
|
174
|
+
messagesPage?(session: string, options?: PageOptions & {
|
|
175
|
+
compacted?: boolean;
|
|
176
|
+
}): MessagePage;
|
|
177
|
+
eventsPage?(session: string, options?: PageOptions): EventPage;
|
|
178
|
+
/** Marks queued/running runs whose owner process is not alive as interrupted. */
|
|
179
|
+
interruptRuns?(): number;
|
|
73
180
|
}
|
|
74
181
|
export interface ContextSource {
|
|
75
182
|
/** System instructions; `sessionId` scopes lazily attached nested instructions. */
|
package/dist/core/runner.d.ts
CHANGED
|
@@ -93,6 +93,10 @@ export interface RunOptions {
|
|
|
93
93
|
timeoutMs?: number;
|
|
94
94
|
/** Reasoning effort level for this run; beats the runner-level default when set. */
|
|
95
95
|
reasoningEffort?: string;
|
|
96
|
+
/** Run id to use for this run's events (e.g. preassigned by an embedder); a UUID otherwise. */
|
|
97
|
+
runId?: string;
|
|
98
|
+
/** Copied to every event of this run as `RunEvent.correlationId` (e.g. an HTTP request id). */
|
|
99
|
+
correlationId?: string;
|
|
96
100
|
}
|
|
97
101
|
/**
|
|
98
102
|
* Default cumulative token budget for one run: several context windows (each turn re-sends the
|
|
@@ -153,6 +157,23 @@ export declare class AgentRunner {
|
|
|
153
157
|
basis: "window" | "unknown";
|
|
154
158
|
compactionAt: number;
|
|
155
159
|
};
|
|
160
|
+
/**
|
|
161
|
+
* What a tool-less side call about a session needs (for example a `/btw` side question): the
|
|
162
|
+
* session's provider and model, its system instructions, its ACTIVE history and the current
|
|
163
|
+
* limits. Claims no lock and writes nothing, so it is safe while a run is active.
|
|
164
|
+
*/
|
|
165
|
+
sideContext(sessionId: string): Promise<{
|
|
166
|
+
provider: ModelProvider;
|
|
167
|
+
model: string;
|
|
168
|
+
instructions: string;
|
|
169
|
+
messages: Message[];
|
|
170
|
+
maxContextChars: number;
|
|
171
|
+
maxOutputTokens: number;
|
|
172
|
+
timeoutMs: number;
|
|
173
|
+
/** Context window of the model in tokens, when known and trusted. */
|
|
174
|
+
contextWindow?: number;
|
|
175
|
+
reasoningEffort?: string;
|
|
176
|
+
}>;
|
|
156
177
|
/** Change the model used by subsequent turns of a session; recorded in the store. */
|
|
157
178
|
setModel(sessionId: string, model: string): void;
|
|
158
179
|
/** Manually compact a session outside a run. */
|