@cmodjs/core 1.6.0 → 2.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/docs/commands.md +4 -4
- package/docs/index.md +12 -11
- package/docs/jobs.md +12 -16
- package/docs/mod.md +111 -36
- package/docs/permissions.md +14 -18
- package/docs/session.md +171 -82
- package/docs/state.md +5 -5
- package/docs/testing.md +57 -41
- package/docs/ui.md +9 -9
- package/jobs/permissions/decide-permission.d.ts.map +1 -1
- package/jobs/permissions/decide-permission.js +1 -1
- package/jobs/prompt.d.ts +1 -3
- package/jobs/prompt.d.ts.map +1 -1
- package/jobs/prompt.js +2 -21
- package/jobs/slash-command.d.ts +1 -1
- package/jobs/slash-command.d.ts.map +1 -1
- package/jobs/slash-command.js +1 -1
- package/jobs/status-line.d.ts +0 -1
- package/jobs/status-line.d.ts.map +1 -1
- package/jobs/status-line.js +3 -4
- package/mod.d.ts +53 -105
- package/mod.d.ts.map +1 -1
- package/mod.js +18 -80
- package/package.json +1 -1
- package/records.d.ts +1 -1
- package/records.js +1 -1
- package/register.d.ts.map +1 -1
- package/register.js +3 -1
- package/runtime/before.d.ts +5 -1
- package/runtime/before.d.ts.map +1 -1
- package/runtime/before.js +81 -39
- package/runtime/claude.d.ts +6 -1
- package/runtime/claude.d.ts.map +1 -1
- package/runtime/conversations.d.ts.map +1 -1
- package/runtime/conversations.js +45 -26
- package/runtime/deadline.d.ts +17 -0
- package/runtime/deadline.d.ts.map +1 -1
- package/runtime/deadline.js +34 -0
- package/runtime/dependencies.d.ts +2 -0
- package/runtime/dependencies.d.ts.map +1 -1
- package/runtime/dependencies.js +12 -0
- package/runtime/grants.d.ts.map +1 -1
- package/runtime/grants.js +13 -8
- package/runtime/hooks.d.ts +28 -11
- package/runtime/hooks.d.ts.map +1 -1
- package/runtime/hooks.js +20 -123
- package/runtime/lifecycle.d.ts +2 -1
- package/runtime/lifecycle.d.ts.map +1 -1
- package/runtime/lifecycle.js +66 -49
- package/runtime/sessions.d.ts +31 -5
- package/runtime/sessions.d.ts.map +1 -1
- package/runtime/sessions.js +342 -81
- package/runtime/tool-calls.d.ts +0 -1
- package/runtime/tool-calls.d.ts.map +1 -1
- package/runtime/tool-calls.js +0 -19
- package/session/context/turn/message/instruction-file.d.ts +9 -0
- package/session/context/turn/message/instruction-file.d.ts.map +1 -0
- package/session/context/turn/message/instruction-file.js +4 -0
- package/session/context/turn/message/message.d.ts +5 -4
- package/session/context/turn/message/message.d.ts.map +1 -1
- package/session/context/turn/message/message.js +7 -6
- package/session/context/turn/message/sender.d.ts +2 -1
- package/session/context/turn/message/sender.d.ts.map +1 -1
- package/session/context/turn/message/sender.js +4 -1
- package/session/context/turn/message/tool-call.d.ts +5 -0
- package/session/context/turn/message/tool-call.d.ts.map +1 -1
- package/session/context/turn/message/tool-result.d.ts +6 -1
- package/session/context/turn/message/tool-result.d.ts.map +1 -1
- package/session/context/turn/turn.d.ts +7 -0
- package/session/context/turn/turn.d.ts.map +1 -1
- package/session/conversation.d.ts +10 -3
- package/session/conversation.d.ts.map +1 -1
- package/session/conversation.js +14 -5
- package/session/goal/goal.d.ts +1 -1
- package/session/goal/goal.d.ts.map +1 -1
- package/session/goal/task.d.ts +2 -2
- package/session/goal/task.d.ts.map +1 -1
- package/session/session.d.ts +361 -26
- package/session/session.d.ts.map +1 -1
- package/session/session.js +40 -0
- package/src/jobs/permissions/decide-permission.ts +2 -1
- package/src/jobs/prompt.ts +3 -20
- package/src/jobs/slash-command.ts +2 -2
- package/src/jobs/status-line.ts +4 -7
- package/src/mod.ts +52 -147
- package/src/records.ts +1 -1
- package/src/register.ts +3 -1
- package/src/runtime/before.ts +90 -37
- package/src/runtime/claude.ts +5 -1
- package/src/runtime/conversations.ts +41 -25
- package/src/runtime/deadline.ts +38 -0
- package/src/runtime/dependencies.ts +12 -0
- package/src/runtime/grants.ts +11 -7
- package/src/runtime/hooks.ts +48 -116
- package/src/runtime/lifecycle.ts +65 -50
- package/src/runtime/sessions.ts +366 -90
- package/src/runtime/tool-calls.ts +0 -19
- package/src/session/context/turn/message/instruction-file.ts +13 -0
- package/src/session/context/turn/message/message.ts +11 -9
- package/src/session/context/turn/message/sender.ts +5 -2
- package/src/session/context/turn/message/tool-call.ts +3 -0
- package/src/session/context/turn/message/tool-result.ts +6 -1
- package/src/session/context/turn/turn.ts +4 -0
- package/src/session/conversation.ts +21 -8
- package/src/session/goal/goal.ts +1 -1
- package/src/session/goal/task.ts +2 -2
- package/src/session/session.ts +227 -24
- package/src/testing/claude-code.ts +23 -0
- package/src/testing/fake-claude.ts +5 -1
- package/src/testing/fake-sessions.ts +38 -24
- package/src/testing.ts +84 -16
- package/testing/claude-code.d.ts +12 -0
- package/testing/claude-code.d.ts.map +1 -0
- package/testing/claude-code.js +10 -0
- package/testing/fake-claude.d.ts +4 -0
- package/testing/fake-claude.d.ts.map +1 -1
- package/testing/fake-claude.js +4 -1
- package/testing/fake-sessions.d.ts +8 -6
- package/testing/fake-sessions.d.ts.map +1 -1
- package/testing/fake-sessions.js +35 -20
- package/testing.d.ts +38 -9
- package/testing.d.ts.map +1 -1
- package/testing.js +54 -5
- package/docs/hooks.md +0 -259
- package/session/context/turn/message/document.d.ts +0 -7
- package/session/context/turn/message/document.d.ts.map +0 -1
- package/session/context/turn/message/document.js +0 -1
- package/src/session/context/turn/message/document.ts +0 -8
package/runtime/sessions.js
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import { ContextEnd, ContextStart } from '../session/context/context.js';
|
|
2
|
-
import { Message } from '../session/context/turn/message/message.js';
|
|
2
|
+
import { Message, noFiles } from '../session/context/turn/message/message.js';
|
|
3
3
|
import { Note } from '../session/context/turn/message/note.js';
|
|
4
|
-
import { Person } from '../session/context/turn/message/sender.js';
|
|
4
|
+
import { Person, senderOf } from '../session/context/turn/message/sender.js';
|
|
5
|
+
import { ToolCall } from '../session/context/turn/message/tool-call.js';
|
|
5
6
|
import { ToolResult } from '../session/context/turn/message/tool-result.js';
|
|
6
7
|
import { TurnEnd, TurnStart } from '../session/context/turn/turn.js';
|
|
8
|
+
import { searchLimit } from '../session/conversation.js';
|
|
7
9
|
import { GoalChange } from '../session/goal/goal.js';
|
|
8
10
|
import { TaskEnd, TaskStart } from '../session/goal/task.js';
|
|
9
|
-
import { Session } from '../session/session.js';
|
|
11
|
+
import { beforeEvents, processEvents, Session, sessionEvents } from '../session/session.js';
|
|
10
12
|
import { matches, sentMatcher } from '../utils/matcher.js';
|
|
11
13
|
import { foldAnswers } from './before.js';
|
|
12
14
|
import { cmodPluginName } from './dependencies.js';
|
|
15
|
+
import { messageOf } from '../utils/text.js';
|
|
16
|
+
import { listenerOf, PassedTimeout, withinTime } from './deadline.js';
|
|
17
|
+
import { callFiles } from './hooks.js';
|
|
13
18
|
export const everySession = '*';
|
|
14
19
|
export const conversationMethods = {
|
|
15
20
|
sessions: 'cmod:sessions',
|
|
@@ -27,10 +32,11 @@ export const conversationMethods = {
|
|
|
27
32
|
changed: 'cmod:changed',
|
|
28
33
|
before: 'cmod:before',
|
|
29
34
|
};
|
|
30
|
-
const actPermissions = { send: 'prompt', startTask: 'tasks', usage: 'conversation',
|
|
35
|
+
const actPermissions = { send: 'prompt', startTask: 'tasks', usage: 'conversation', surfaces: 'conversation', abort: 'prompt' };
|
|
31
36
|
const startsTurns = ['CronCreate'];
|
|
32
37
|
const ownSchedule = ['CronDelete', 'CronList'];
|
|
33
|
-
export const beforePermission = (event) => (event === 'toolCall' ? 'tools' : event === TaskStart
|
|
38
|
+
export const beforePermission = (event) => (event === 'toolCall' ? 'tools' : event === TaskStart ? 'tasks' : 'prompt');
|
|
39
|
+
const methodOf = (prefix, event) => `${prefix}${Object.entries(sessionEvents).find(([, each]) => each === event)?.[0] ?? event}`;
|
|
34
40
|
export const isAct = (action) => action === 'callTool' || action in actPermissions;
|
|
35
41
|
export function toolPermission(tool) {
|
|
36
42
|
return startsTurns.includes(tool) ? 'prompt' : ownSchedule.includes(tool) ? undefined : 'tools';
|
|
@@ -38,11 +44,10 @@ export function toolPermission(tool) {
|
|
|
38
44
|
export function actPermission(action, input) {
|
|
39
45
|
return action === 'callTool' ? (toolPermission(String(input?.name)) ?? 'tools') : actPermissions[action];
|
|
40
46
|
}
|
|
41
|
-
const asked = (action) => `cmod: ${action} runs only on a session's own main loop, and Claude Code gives a subagent's tasks no way to start a task or run a tool.`;
|
|
42
47
|
export async function usageOf(claude) {
|
|
43
48
|
const [model, usage] = await Promise.all([claude.session.model(), claude.session.usage()]);
|
|
44
49
|
const { tokens, window, percent } = usage.context;
|
|
45
|
-
return { model,
|
|
50
|
+
return { model, contextWindow: { ...(tokens === undefined ? {} : { tokenCount: tokens }), size: window, ...(percent === undefined ? {} : { percent }) }, ...(usage.cost === undefined ? {} : { cost: { usd: usage.cost.usd } }) };
|
|
46
51
|
}
|
|
47
52
|
export function payloadOf(change) {
|
|
48
53
|
if (change.type === Message)
|
|
@@ -96,10 +101,10 @@ export function isInScope(scope, session, change, isUnder) {
|
|
|
96
101
|
if (scope.session !== undefined && scope.session !== session)
|
|
97
102
|
return false;
|
|
98
103
|
const task = taskOfChange(change);
|
|
104
|
+
if (scope.isAgent === true)
|
|
105
|
+
return (change.type === TaskStart || change.type === TaskEnd ? change.task.parent : task) === scope.task;
|
|
99
106
|
if (scope.task !== undefined && (task === undefined || !isUnder(task, scope.task)))
|
|
100
107
|
return false;
|
|
101
|
-
if (scope.isGoal === true && change.type !== GoalChange && change.type !== TaskStart && (task === undefined || task === scope.task))
|
|
102
|
-
return false;
|
|
103
108
|
if (scope.context !== undefined && (contextOfChange(change) !== scope.context || ownerOfChange(change) !== scope.task))
|
|
104
109
|
return false;
|
|
105
110
|
if (scope.turn === undefined)
|
|
@@ -108,7 +113,7 @@ export function isInScope(scope, session, change, isUnder) {
|
|
|
108
113
|
return change.message.turn === scope.turn;
|
|
109
114
|
return (change.type === TurnStart || change.type === TurnEnd) && change.turn.prompt.id === scope.turn;
|
|
110
115
|
}
|
|
111
|
-
export function createSessionsClient({ name, claude, report, isGranted }) {
|
|
116
|
+
export function createSessionsClient({ name, claude, report, isGranted, hookOn, routeOn }) {
|
|
112
117
|
const call = (method, input) => claude.cmod.call({ to: cmodPluginName, method, input });
|
|
113
118
|
const listening = new Map();
|
|
114
119
|
const deciding = new Map();
|
|
@@ -149,11 +154,9 @@ export function createSessionsClient({ name, claude, report, isGranted }) {
|
|
|
149
154
|
if (change.type === TaskStart)
|
|
150
155
|
parents.get(session)?.set(change.task.id, change.task.parent);
|
|
151
156
|
};
|
|
152
|
-
const add = (into, scope, event,
|
|
153
|
-
const listener = (maybeListener ?? matcherOrListener);
|
|
154
|
-
const matcher = maybeListener === undefined ? undefined : matcherOrListener;
|
|
157
|
+
const add = (into, scope, event, method, args, onChange) => {
|
|
155
158
|
const key = keyOf(scope);
|
|
156
|
-
const entry = { scope, event,
|
|
159
|
+
const entry = { scope, event, method, ...listenerOf(name, method, args) };
|
|
157
160
|
const set = into.get(key) ?? new Set();
|
|
158
161
|
set.add(entry);
|
|
159
162
|
into.set(key, set);
|
|
@@ -181,31 +184,74 @@ export function createSessionsClient({ name, claude, report, isGranted }) {
|
|
|
181
184
|
return undefined;
|
|
182
185
|
if (entry.matcher !== undefined && !matches(payloadOf(latest), entry.matcher))
|
|
183
186
|
return undefined;
|
|
184
|
-
const
|
|
185
|
-
|
|
187
|
+
const payload = await payloadFor(session, latest);
|
|
188
|
+
const answer = await withinTime(claude.clock, entry.method, entry.options, (time) => entry.listener(payload, time)).catch((error) => {
|
|
189
|
+
if (entry.options.onFailure !== 'refuse')
|
|
190
|
+
throw error;
|
|
191
|
+
report(`session:${entry.method}`, `The ${entry.method} listener failed`, error);
|
|
192
|
+
return { refuse: `${name}: ${messageOf(error)}` };
|
|
193
|
+
});
|
|
194
|
+
if (answer === undefined || answer === null || (typeof answer === 'object' && 'refuse' in answer) || isGranted(beforePermission(event)))
|
|
186
195
|
return answer;
|
|
187
|
-
throw new Error(`Its
|
|
196
|
+
throw new Error(`Its ${entry.method} answer changes nothing until ${beforePermission(event)} is turned on for ${name} in /mods.`);
|
|
188
197
|
});
|
|
189
|
-
return foldAnswers(event, change, askers, (error) => report(`
|
|
198
|
+
return foldAnswers(event, change, askers, (error) => report(`session:${methodOf('before', event)}`, `The ${methodOf('before', event)} listener failed`, error));
|
|
199
|
+
};
|
|
200
|
+
const eventMethods = (scopeOf, hasBefore, ended) => {
|
|
201
|
+
const method = (into, named, event) => (...args) => {
|
|
202
|
+
if (ended !== undefined)
|
|
203
|
+
throw new Error(`${name}: ${ended} has ended, so its ${named} never fires. Listen on its Session.`);
|
|
204
|
+
return add(into, scopeOf(), event, named, args, subscribe);
|
|
205
|
+
};
|
|
206
|
+
return Object.fromEntries([
|
|
207
|
+
...Object.entries(sessionEvents).map(([named, event]) => [`on${named}`, method(listening, `on${named}`, event)]),
|
|
208
|
+
...(hasBefore ? beforeEvents.map((named) => [`before${named}`, method(deciding, `before${named}`, sessionEvents[named])]) : []),
|
|
209
|
+
]);
|
|
190
210
|
};
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
211
|
+
const withFiles = async (message) => {
|
|
212
|
+
if (message.type !== ToolCall && message.type !== ToolResult)
|
|
213
|
+
return message;
|
|
214
|
+
return { ...message, files: await callFiles(name, claude, { tool: message.name, input: message.input }, message.cwd) };
|
|
215
|
+
};
|
|
216
|
+
const messagesFrom = async (method, input) => Promise.all((await call(method, input)).map(withFiles));
|
|
217
|
+
const searched = (request, matcher) => ({
|
|
218
|
+
async *[Symbol.asyncIterator]() {
|
|
219
|
+
let before = request['before'];
|
|
220
|
+
for (;;) {
|
|
221
|
+
const page = await messagesFrom(conversationMethods.search, { ...request, ...(before === undefined ? {} : { before }) });
|
|
222
|
+
for (const message of page)
|
|
223
|
+
if (matcher === undefined || matches(message, matcher))
|
|
224
|
+
yield message;
|
|
225
|
+
const last = page.at(-1);
|
|
226
|
+
if (last === undefined || page.length < searchLimit)
|
|
227
|
+
return;
|
|
228
|
+
before = { session: last.session, id: last.id };
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
const recordPart = (scope, ended) => ({
|
|
233
|
+
search: (text, matcher) => searched({ text, ...scope }, matcher),
|
|
234
|
+
...eventMethods(() => scope, true, ended),
|
|
195
235
|
});
|
|
196
236
|
const turnOf = (scope, data) => {
|
|
197
237
|
const task = data.prompt.task ?? scope.task;
|
|
198
238
|
return {
|
|
199
239
|
...data,
|
|
200
|
-
...recordPart({ ...scope, ...(task === undefined ? {} : { task }), turn: data.prompt.id }),
|
|
201
|
-
messages: () =>
|
|
240
|
+
...recordPart({ ...scope, ...(task === undefined ? {} : { task }), turn: data.prompt.id }, data.closedAt === undefined ? undefined : `turn ${data.prompt.id}`),
|
|
241
|
+
messages: () => messagesFrom(conversationMethods.messages, { session: scope.session, turn: data.prompt.id, ...(task === undefined ? {} : { task }) }),
|
|
242
|
+
abort: async () => {
|
|
243
|
+
if (data.closedAt !== undefined)
|
|
244
|
+
throw new Error(`${name}: turn ${data.prompt.id} has ended, so there is nothing to abort.`);
|
|
245
|
+
const session = scope.session ?? current.id;
|
|
246
|
+
await act(session, 'abort', { turn: data.prompt.id }, task === undefined ? undefined : { session, task });
|
|
247
|
+
},
|
|
202
248
|
};
|
|
203
249
|
};
|
|
204
250
|
const contextOf = (scope, data) => {
|
|
205
251
|
const owned = { ...(scope.session === undefined ? {} : { session: scope.session }), ...(data.task === undefined ? {} : { task: data.task }) };
|
|
206
252
|
return {
|
|
207
253
|
...data,
|
|
208
|
-
...recordPart({ ...owned, context: data.number }),
|
|
254
|
+
...recordPart({ ...owned, context: data.number }, data.closedAt === undefined ? undefined : `context ${data.number}`),
|
|
209
255
|
turns: async () => (await call(conversationMethods.turns, { ...owned, context: data.number })).map((turn) => turnOf(owned, turn)),
|
|
210
256
|
};
|
|
211
257
|
};
|
|
@@ -218,22 +264,27 @@ export function createSessionsClient({ name, claude, report, isGranted }) {
|
|
|
218
264
|
owner: ownerOf(session, data),
|
|
219
265
|
...recordPart({ session, task: data.id }),
|
|
220
266
|
tasks: async () => (await call(conversationMethods.tasks, { session, task: data.id })).map((task) => taskOf(session, task)),
|
|
221
|
-
messages: () =>
|
|
267
|
+
messages: () => messagesFrom(conversationMethods.messages, { session, task: data.id }),
|
|
222
268
|
});
|
|
223
|
-
const goalOf = (
|
|
224
|
-
...
|
|
225
|
-
...
|
|
226
|
-
|
|
269
|
+
const goalOf = (where) => ({
|
|
270
|
+
search: (text, matcher) => searched({ text, ...where() }, matcher),
|
|
271
|
+
...eventMethods(() => ({ ...where(), isGoal: true }), true),
|
|
272
|
+
text: async () => (await call(conversationMethods.goal, where())).text,
|
|
273
|
+
tasks: async () => {
|
|
274
|
+
const { session = current.id, task } = where();
|
|
275
|
+
return (await call(conversationMethods.tasks, { session, ...(task === undefined ? {} : { task }) })).map((each) => taskOf(session, each));
|
|
276
|
+
},
|
|
227
277
|
update: async ({ text }) => {
|
|
228
278
|
if (text.trim() === '')
|
|
229
279
|
throw new Error(`${name}: a goal needs text.`);
|
|
230
|
-
|
|
280
|
+
const { session = current.id, task } = where();
|
|
281
|
+
await call(conversationMethods.updateGoal, { session, ...(task === undefined ? {} : { task }), text });
|
|
231
282
|
},
|
|
232
283
|
});
|
|
233
|
-
const payloadFor = (session, change) => {
|
|
284
|
+
const payloadFor = async (session, change) => {
|
|
234
285
|
const scope = { session };
|
|
235
286
|
if (change.type === Message)
|
|
236
|
-
return change.message;
|
|
287
|
+
return withFiles(change.message);
|
|
237
288
|
if ('turn' in change)
|
|
238
289
|
return turnOf(scope, change.turn);
|
|
239
290
|
if ('context' in change)
|
|
@@ -242,7 +293,8 @@ export function createSessionsClient({ name, claude, report, isGranted }) {
|
|
|
242
293
|
return taskOf(session, change.task);
|
|
243
294
|
if ('usage' in change)
|
|
244
295
|
return change.usage;
|
|
245
|
-
|
|
296
|
+
const { task } = change.goal;
|
|
297
|
+
return goalOf(() => ({ session, ...(task === undefined ? {} : { task }) }));
|
|
246
298
|
};
|
|
247
299
|
const act = (session, action, input, owned) => call(conversationMethods.act, { session: owned?.session ?? session, action, input, ...(owned === undefined ? {} : { task: owned.task }) });
|
|
248
300
|
const sendHere = async ({ type, text, as }) => {
|
|
@@ -258,26 +310,15 @@ export function createSessionsClient({ name, claude, report, isGranted }) {
|
|
|
258
310
|
if ('drop' in submitted && typeof submitted.drop === 'string')
|
|
259
311
|
throw new Error(`${name}: Claude Code dropped the prompt: ${submitted.drop}`);
|
|
260
312
|
};
|
|
261
|
-
const startHere = async (task) => {
|
|
262
|
-
|
|
263
|
-
prompt: task.prompt,
|
|
264
|
-
...(task.name === undefined ? {} : { description: task.name, name: task.name }),
|
|
265
|
-
...(task.agentType === undefined ? {} : { subagentType: task.agentType }),
|
|
266
|
-
...(task.model === undefined ? {} : { model: task.model }),
|
|
267
|
-
...(task.cwd === undefined ? {} : { cwd: task.cwd }),
|
|
268
|
-
});
|
|
269
|
-
if (started.deny !== undefined)
|
|
270
|
-
throw new Error(`${name}: Claude Code refused the task: ${started.deny}`);
|
|
271
|
-
return taskOf(current.id, { id: started.agentId ?? '', title: task.name ?? '', agentType: task.agentType ?? 'general-purpose', model: started.model, openedBy: '', openedAt: new Date().toISOString() });
|
|
272
|
-
};
|
|
273
|
-
const clientsHere = async () => [...(await claude.session.surfaces())];
|
|
313
|
+
const startHere = async (task) => taskOf(current.id, await spawnTask(claude, task).catch((error) => Promise.reject(new Error(`${name}: ${messageOf(error)}`))));
|
|
314
|
+
const surfacesHere = async () => [...(await claude.session.surfaces())];
|
|
274
315
|
const sessionOf = (read, owned) => {
|
|
275
316
|
const id = () => read().id;
|
|
276
317
|
const isHere = () => owned === undefined && id() === current.id;
|
|
277
|
-
const scope = () => (owned === undefined ? { session: id() } : { session: owned.session, task: owned.task });
|
|
318
|
+
const scope = () => (owned === undefined ? { session: id(), isAgent: true } : { session: owned.session, task: owned.task, isAgent: true });
|
|
278
319
|
const where = () => (owned === undefined ? { session: id() } : { session: owned.session, task: owned.task });
|
|
279
|
-
const turnsOf = async (options = {}) => (await call(conversationMethods.turns, { ...where(), ...options })).map((turn) => turnOf(
|
|
280
|
-
const contextsOf = async () => (await call(conversationMethods.contexts, where())).map((context) => contextOf(
|
|
320
|
+
const turnsOf = async (options = {}) => (await call(conversationMethods.turns, { ...where(), ...options })).map((turn) => turnOf(where(), turn));
|
|
321
|
+
const contextsOf = async () => (await call(conversationMethods.contexts, where())).map((context) => contextOf(where(), context));
|
|
281
322
|
const session = {
|
|
282
323
|
get id() {
|
|
283
324
|
return read().id;
|
|
@@ -309,10 +350,9 @@ export function createSessionsClient({ name, claude, report, isGranted }) {
|
|
|
309
350
|
get forkedFrom() {
|
|
310
351
|
return read().forkedFrom;
|
|
311
352
|
},
|
|
312
|
-
search: (text,
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
goal: async () => goalOf(scope(), await call(conversationMethods.goal, where())),
|
|
353
|
+
search: (text, matcher) => recordPart(scope()).search(text, matcher),
|
|
354
|
+
...eventMethods(scope, true),
|
|
355
|
+
goal: goalOf(where),
|
|
316
356
|
context: async () => {
|
|
317
357
|
const all = await contextsOf();
|
|
318
358
|
const last = all.at(-1);
|
|
@@ -326,9 +366,9 @@ export function createSessionsClient({ name, claude, report, isGranted }) {
|
|
|
326
366
|
return last?.closedAt === undefined ? last : undefined;
|
|
327
367
|
},
|
|
328
368
|
turns: turnsOf,
|
|
329
|
-
messages: (options = {}) =>
|
|
369
|
+
messages: (options = {}) => messagesFrom(conversationMethods.messages, { ...where(), ...options }),
|
|
330
370
|
usage: async () => (isHere() ? usageOf(claude) : act(id(), 'usage', null, owned)),
|
|
331
|
-
|
|
371
|
+
surfaces: async () => (isHere() ? surfacesHere() : act(id(), 'surfaces', null, owned)),
|
|
332
372
|
async send(message) {
|
|
333
373
|
if (message.text.trim() === '')
|
|
334
374
|
throw new Error(`${name}: send needs text.`);
|
|
@@ -336,21 +376,18 @@ export function createSessionsClient({ name, claude, report, isGranted }) {
|
|
|
336
376
|
return sendHere(message);
|
|
337
377
|
await act(id(), 'send', message, owned);
|
|
338
378
|
},
|
|
379
|
+
};
|
|
380
|
+
if (owned !== undefined)
|
|
381
|
+
return session;
|
|
382
|
+
return Object.assign(session, {
|
|
339
383
|
async startTask(task) {
|
|
340
|
-
if (owned !== undefined)
|
|
341
|
-
throw new Error(asked('startTask'));
|
|
342
384
|
if (isHere())
|
|
343
385
|
return startHere(task);
|
|
344
386
|
const started = await act(id(), 'startTask', task);
|
|
345
387
|
return taskOf(id(), started);
|
|
346
388
|
},
|
|
347
|
-
async
|
|
348
|
-
|
|
349
|
-
throw new Error(asked('callTool'));
|
|
350
|
-
return act(id(), 'callTool', toolCall);
|
|
351
|
-
},
|
|
352
|
-
};
|
|
353
|
-
return session;
|
|
389
|
+
callTool: async (toolCall) => (await withFiles(await act(id(), 'callTool', toolCall))),
|
|
390
|
+
});
|
|
354
391
|
};
|
|
355
392
|
const ownerOf = (session, data) => {
|
|
356
393
|
const parent = session === current.id ? current : known.get(session);
|
|
@@ -366,9 +403,113 @@ export function createSessionsClient({ name, claude, report, isGranted }) {
|
|
|
366
403
|
lastAt: data.closedAt ?? data.openedAt,
|
|
367
404
|
}), { session, task: data.id });
|
|
368
405
|
};
|
|
369
|
-
const
|
|
370
|
-
const
|
|
371
|
-
|
|
406
|
+
const processListeners = new Map();
|
|
407
|
+
const raise = async (method, payload) => {
|
|
408
|
+
const isBefore = method.startsWith('before');
|
|
409
|
+
const heard = [...(processListeners.get(method) ?? [])].filter(({ matcher }) => payload['task'] === undefined && (matcher === undefined || matches(payload, matcher)));
|
|
410
|
+
if (!isBefore) {
|
|
411
|
+
await Promise.all(heard.map(({ listener, options }) => withinTime(claude.clock, method, options, (time) => listener(payload, time)).catch((error) => {
|
|
412
|
+
if (!(error instanceof PassedTimeout))
|
|
413
|
+
report(`session:${method}`, `The ${method} listener failed`, error);
|
|
414
|
+
})));
|
|
415
|
+
return undefined;
|
|
416
|
+
}
|
|
417
|
+
const takes = answerKeys[method.slice('before'.length)] ?? [];
|
|
418
|
+
let merged;
|
|
419
|
+
for (const { listener, options } of heard) {
|
|
420
|
+
try {
|
|
421
|
+
const answer = (await withinTime(claude.clock, method, options, (time) => listener(payload, time)));
|
|
422
|
+
if (answer === undefined || answer === null)
|
|
423
|
+
continue;
|
|
424
|
+
const unread = Object.keys(answer).filter((key) => !takes.includes(key));
|
|
425
|
+
if (unread.length > 0)
|
|
426
|
+
throw new Error(`it answered ${unread.join(' and ')}, which ${method} does not take. It takes ${takes.map((key) => `{ ${key} }`).join(', ') || 'no answer'}.`);
|
|
427
|
+
const notes = [merged?.['note'], answer['note']].filter((note) => typeof note === 'string');
|
|
428
|
+
merged = { ...merged, ...answer, ...(notes.length === 0 ? {} : { note: notes.join('\n\n') }) };
|
|
429
|
+
if (typeof answer['refuse'] === 'string')
|
|
430
|
+
return merged;
|
|
431
|
+
}
|
|
432
|
+
catch (error) {
|
|
433
|
+
report(`session:${method}`, `The ${method} listener failed`, error);
|
|
434
|
+
if (options.onFailure === 'refuse' || method === 'beforePermissionRequest')
|
|
435
|
+
return { refuse: `${name}: ${messageOf(error)}` };
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
return merged;
|
|
439
|
+
};
|
|
440
|
+
const permissionCheck = (e, below) => ({ session: current.id, cwd: current.cwd, ...defined({ task: e.agentId, callId: e.tool_use_id, reason: below.reason, rule: below.rule, ceiling: e.ceiling === undefined ? undefined : verdictName(e.ceiling) }), name: e.tool, input: recordOf(e.input), decision: verdictName(below.decision) });
|
|
441
|
+
const commandRun = async (e) => {
|
|
442
|
+
const source = (await claude.command.list()).find((command) => command.name === e.command)?.source;
|
|
443
|
+
return { session: current.id, cwd: current.cwd, name: e.command, args: e.args, ...defined({ source }), from: senderOf(e.origin, false) };
|
|
444
|
+
};
|
|
445
|
+
const commandListeners = { beforeRun: new Set(), onRun: new Set() };
|
|
446
|
+
for (const [method, held] of Object.entries(commandListeners))
|
|
447
|
+
processListeners.set(method, held);
|
|
448
|
+
routeOn?.('command.run', async (e, next) => {
|
|
449
|
+
if (commandListeners.beforeRun.size === 0 && commandListeners.onRun.size === 0)
|
|
450
|
+
return next(e);
|
|
451
|
+
const run = await commandRun(e);
|
|
452
|
+
const answer = commandListeners.beforeRun.size === 0 ? undefined : await raise('beforeRun', run);
|
|
453
|
+
if (typeof answer?.['refuse'] === 'string')
|
|
454
|
+
return { text: answer['refuse'] };
|
|
455
|
+
const changes = answer !== undefined && isGranted('prompt') ? answer : undefined;
|
|
456
|
+
if (answer !== undefined && changes === undefined)
|
|
457
|
+
report('session:beforeRun', 'The beforeRun listener failed', new Error(`${name}: its answer changes a command, which needs the prompt permission.`));
|
|
458
|
+
if (typeof changes?.['result'] === 'string')
|
|
459
|
+
return { text: changes['result'], ...(typeof changes['exitCode'] === 'number' ? { exitCode: changes['exitCode'] } : {}) };
|
|
460
|
+
const args = typeof changes?.['args'] === 'string' ? changes['args'] : e.args;
|
|
461
|
+
const ran = await next(args === e.args ? e : { ...e, args });
|
|
462
|
+
void raise('onRun', { ...run, args });
|
|
463
|
+
return typeof changes?.['note'] === 'string' ? { ...ran, context: [...(ran.context ?? []), changes['note']] } : ran;
|
|
464
|
+
});
|
|
465
|
+
const listenTo = (method, named) => {
|
|
466
|
+
if (hookOn === undefined || routeOn === undefined)
|
|
467
|
+
throw new Error(`${name}: ${method} works only in a mod's own session.`);
|
|
468
|
+
const isBefore = method.startsWith('before');
|
|
469
|
+
if (named === 'PermissionCheck') {
|
|
470
|
+
return routeOn('tool.check', async (e, next) => {
|
|
471
|
+
const below = (await next(e));
|
|
472
|
+
if (!isBefore) {
|
|
473
|
+
void raise(method, permissionCheck(e, below));
|
|
474
|
+
return below;
|
|
475
|
+
}
|
|
476
|
+
const answer = await raise(method, permissionCheck(e, below));
|
|
477
|
+
if (answer === undefined || below.decision === 'deny')
|
|
478
|
+
return below;
|
|
479
|
+
if (typeof answer['refuse'] === 'string')
|
|
480
|
+
return { decision: 'deny', reason: answer['refuse'] };
|
|
481
|
+
if (typeof answer['ask'] === 'string')
|
|
482
|
+
return { decision: 'ask', reason: answer['ask'] };
|
|
483
|
+
return answer['allow'] === true ? { decision: 'allow' } : below;
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
const event = processEvents[named];
|
|
487
|
+
const hook = (isBefore ? event.before : undefined) ?? event.on;
|
|
488
|
+
return hookOn(hook, async (input) => {
|
|
489
|
+
const payload = processPayloadOf(named, input);
|
|
490
|
+
if (!isBefore) {
|
|
491
|
+
if (named !== 'SessionStart' || payload['source'] !== 'compact')
|
|
492
|
+
void raise(method, payload);
|
|
493
|
+
return undefined;
|
|
494
|
+
}
|
|
495
|
+
const answer = await raise(method, payload);
|
|
496
|
+
return answer === undefined ? undefined : hookAnswerOf(named, hook, answer);
|
|
497
|
+
});
|
|
498
|
+
};
|
|
499
|
+
const listen = (method, args) => {
|
|
500
|
+
const entry = listenerOf(name, method, args);
|
|
501
|
+
const held = processListeners.get(method) ?? new Set();
|
|
502
|
+
if (!processListeners.has(method)) {
|
|
503
|
+
listenTo(method, method.replace(/^(on|before)/, ''));
|
|
504
|
+
processListeners.set(method, held);
|
|
505
|
+
}
|
|
506
|
+
held.add(entry);
|
|
507
|
+
return { dispose: () => void held.delete(entry) };
|
|
508
|
+
};
|
|
509
|
+
const processMethods = () => Object.fromEntries(Object.entries(processEvents).flatMap(([named, event]) => [`on${named}`, ...('before' in event ? [`before${named}`] : [])]).map((method) => [method, (...args) => listen(method, args)]));
|
|
510
|
+
const currentSession = Object.assign(sessionOf(() => current), processMethods());
|
|
511
|
+
const every = eventMethods(() => ({}), false);
|
|
512
|
+
const answerBefore = async (session, event, change, isInProject) => (session === current.id || isGranted('conversation') ? combined(session, event, change, isInProject) : undefined);
|
|
372
513
|
return {
|
|
373
514
|
sessions: {
|
|
374
515
|
get current() {
|
|
@@ -383,23 +524,27 @@ export function createSessionsClient({ name, claude, report, isGranted }) {
|
|
|
383
524
|
current = { ...current, title: data.title };
|
|
384
525
|
return currentSession;
|
|
385
526
|
}),
|
|
386
|
-
search: (text, options = {}) =>
|
|
387
|
-
|
|
388
|
-
before: every.before,
|
|
527
|
+
search: (text, matcher, options = {}) => searched({ text, ...searchOptionsOf(options) }, matcher),
|
|
528
|
+
...every,
|
|
389
529
|
},
|
|
390
530
|
changed(session, change, isInProject) {
|
|
391
531
|
remember(session, change);
|
|
392
532
|
for (const key of isInProject ? [session, everySession] : [session]) {
|
|
393
|
-
for (const { scope, event, matcher, listener } of listening.get(key) ?? []) {
|
|
533
|
+
for (const { scope, event, method, matcher, listener, options } of listening.get(key) ?? []) {
|
|
394
534
|
if (!isHeardBy(event, matcher, change) || !isInScope(scope, session, change, isUnderIn(session)))
|
|
395
535
|
continue;
|
|
396
|
-
void
|
|
397
|
-
.then(() => listener(
|
|
398
|
-
.catch((error) =>
|
|
536
|
+
void payloadFor(session, change)
|
|
537
|
+
.then((payload) => withinTime(claude.clock, method, options, (time) => listener(payload, time)))
|
|
538
|
+
.catch((error) => {
|
|
539
|
+
if (!(error instanceof PassedTimeout))
|
|
540
|
+
report(`session:${method}`, `The ${method} listener failed`, error);
|
|
541
|
+
});
|
|
399
542
|
}
|
|
400
543
|
}
|
|
401
544
|
},
|
|
402
545
|
answerBefore,
|
|
546
|
+
raise,
|
|
547
|
+
listen,
|
|
403
548
|
follow(next) {
|
|
404
549
|
const moved = next.id !== current.id && current.id !== '';
|
|
405
550
|
const previous = current.id;
|
|
@@ -422,12 +567,128 @@ export function createSessionsClient({ name, claude, report, isGranted }) {
|
|
|
422
567
|
},
|
|
423
568
|
};
|
|
424
569
|
}
|
|
425
|
-
|
|
426
|
-
|
|
570
|
+
const verdictName = (decision) => (decision === 'deny' ? 'refuse' : decision);
|
|
571
|
+
const recordOf = (value) => (typeof value === 'object' && value !== null && !Array.isArray(value) ? value : {});
|
|
572
|
+
const defined = (fields) => Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== undefined));
|
|
573
|
+
const placementOf = (input) => defined({ session: input['session_id'], task: input['agent_id'], agentType: input['agent_type'], cwd: input['cwd'], permissionMode: input['permission_mode'] });
|
|
574
|
+
const mcpServerOf = (server) => (server === undefined ? undefined : { name: String(recordOf(server)['name']), source: String(recordOf(server)['source']) });
|
|
575
|
+
const modelChangeOf = (input) => ({
|
|
576
|
+
model: input['to_model'],
|
|
577
|
+
previous: input['from_model'],
|
|
578
|
+
requested: input['requested_model'] ?? undefined,
|
|
579
|
+
source: input['source'],
|
|
580
|
+
contextTokenCount: input['context_tokens'],
|
|
581
|
+
isCacheWarm: input['prompt_cache_warm'],
|
|
582
|
+
cacheTtl: input['cache_ttl'],
|
|
583
|
+
cacheWriteUsd: input['estimated_cache_write_usd'],
|
|
584
|
+
pricing: input['pricing'],
|
|
585
|
+
});
|
|
586
|
+
const answerKeys = {
|
|
587
|
+
Run: ['args', 'note', 'result', 'exitCode', 'refuse'],
|
|
588
|
+
ProjectSetup: [],
|
|
589
|
+
SessionStart: ['note', 'title', 'reloadSkills', 'firstPrompt'],
|
|
590
|
+
ModelChange: ['ask', 'refuse'],
|
|
591
|
+
WorktreeCreate: ['result'],
|
|
592
|
+
Elicitation: ['refuse'],
|
|
593
|
+
ElicitationResponse: ['refuse'],
|
|
594
|
+
PermissionCheck: ['allow', 'ask', 'refuse'],
|
|
595
|
+
PermissionRequest: ['allow', 'input', 'updates', 'refuse', 'interrupt'],
|
|
596
|
+
PermissionDenial: ['retry'],
|
|
597
|
+
ToolBatch: ['note'],
|
|
598
|
+
TodoCreate: ['refuse'],
|
|
599
|
+
TodoComplete: ['refuse'],
|
|
600
|
+
ReplyDisplay: ['text'],
|
|
601
|
+
};
|
|
602
|
+
const todoOf = (input) => ({ id: input['task_id'], subject: input['task_subject'], description: input['task_description'], teammate: input['teammate_name'] });
|
|
603
|
+
const processFields = {
|
|
604
|
+
ProjectSetup: (input) => ({ trigger: input['trigger'] }),
|
|
605
|
+
SessionStart: (input) => ({
|
|
606
|
+
source: input['source'],
|
|
607
|
+
model: input['model'],
|
|
608
|
+
title: input['session_title'] || undefined,
|
|
609
|
+
idleSeconds: input['seconds_since_last_response'],
|
|
610
|
+
contextTokenCount: input['context_tokens'],
|
|
611
|
+
isCacheWarm: input['prompt_cache_likely_expired'] === undefined ? undefined : input['prompt_cache_likely_expired'] !== true,
|
|
612
|
+
cacheWriteUsd: input['estimated_cache_write_usd'],
|
|
613
|
+
}),
|
|
614
|
+
SessionEnd: (input) => ({ reason: input['reason'] === 'prompt_input_exit' ? 'exit' : input['reason'] }),
|
|
615
|
+
ModelChange: modelChangeOf,
|
|
616
|
+
CwdChange: (input) => ({ cwd: input['new_cwd'] ?? input['cwd'], previous: input['old_cwd'] }),
|
|
617
|
+
DirectoryAdd: (input) => ({ directory: input['directory'], source: input['source'] === 'slash_command' ? 'command' : 'sdk' }),
|
|
618
|
+
WorktreeCreate: (input) => ({ name: input['name'] }),
|
|
619
|
+
WorktreeRemove: (input) => ({ path: input['worktree_path'] }),
|
|
620
|
+
Notification: (input) => ({ text: input['message'], title: input['title'], type: input['notification_type'] }),
|
|
621
|
+
Elicitation: (input) => ({ id: input['elicitation_id'], server: input['mcp_server_name'], text: input['message'], mode: input['mode'], url: input['url'], schema: input['requested_schema'] }),
|
|
622
|
+
ElicitationResponse: (input) => ({ id: input['elicitation_id'], server: input['mcp_server_name'], mode: input['mode'], action: input['action'], content: input['content'] }),
|
|
623
|
+
PermissionRequest: (input) => ({ name: input['tool_name'], input: recordOf(input['tool_input']), suggestions: input['permission_suggestions'] ?? [], mcpServer: mcpServerOf(input['mcp_server']) }),
|
|
624
|
+
PermissionDenial: (input) => ({ name: input['tool_name'], input: recordOf(input['tool_input']), callId: input['tool_use_id'], reason: input['reason'], mcpServer: mcpServerOf(input['mcp_server']) }),
|
|
625
|
+
ToolBatch: (input) => ({
|
|
626
|
+
calls: (Array.isArray(input['tool_calls']) ? input['tool_calls'] : []).map((call) => {
|
|
627
|
+
const fields = recordOf(call);
|
|
628
|
+
return defined({ callId: fields['tool_use_id'], name: fields['tool_name'], input: recordOf(fields['tool_input']), output: fields['tool_response'] });
|
|
629
|
+
}),
|
|
630
|
+
}),
|
|
631
|
+
TodoCreate: todoOf,
|
|
632
|
+
TodoComplete: todoOf,
|
|
633
|
+
ReplyDisplay: (input) => ({ replyId: input['message_id'], index: input['index'], isLast: input['final'], text: input['delta'] }),
|
|
634
|
+
};
|
|
635
|
+
export async function spawnTask(claude, task) {
|
|
636
|
+
if (task.title.trim() === '')
|
|
637
|
+
throw new Error('a task needs a title.');
|
|
638
|
+
const started = await claude.agent.spawn({
|
|
639
|
+
prompt: task.prompt,
|
|
640
|
+
description: task.title,
|
|
641
|
+
...(task.name === undefined ? {} : { name: task.name }),
|
|
642
|
+
...(task.agentType === undefined ? {} : { subagentType: task.agentType }),
|
|
643
|
+
...(task.model === undefined ? {} : { model: task.model }),
|
|
644
|
+
...(task.cwd === undefined ? {} : { cwd: task.cwd }),
|
|
645
|
+
});
|
|
646
|
+
if (started.deny !== undefined)
|
|
647
|
+
throw new Error(`Claude Code refused the task: ${started.deny}`);
|
|
648
|
+
if (started.agentId === undefined)
|
|
649
|
+
throw new Error('Claude Code answered without starting a task.');
|
|
650
|
+
return { id: started.agentId, title: task.title, agentType: task.agentType ?? 'general-purpose', model: started.model, openedBy: '', openedAt: new Date().toISOString() };
|
|
651
|
+
}
|
|
652
|
+
export function processPayloadOf(named, input) {
|
|
653
|
+
const fields = recordOf(input);
|
|
654
|
+
return { ...placementOf(fields), ...defined(processFields[named](fields)) };
|
|
655
|
+
}
|
|
656
|
+
export function hookAnswerOf(named, hook, answer) {
|
|
657
|
+
const output = (fields) => ({ hookSpecificOutput: { hookEventName: hook, ...defined(fields) } });
|
|
658
|
+
const refuse = typeof answer['refuse'] === 'string' ? answer['refuse'] : undefined;
|
|
659
|
+
if (named === 'SessionStart')
|
|
660
|
+
return output({ additionalContext: answer['note'], sessionTitle: answer['title'], reloadSkills: answer['reloadSkills'], initialUserMessage: answer['firstPrompt'] });
|
|
661
|
+
if (named === 'ModelChange') {
|
|
662
|
+
if (refuse !== undefined)
|
|
663
|
+
return output({ permissionDecision: 'deny', permissionDecisionReason: refuse });
|
|
664
|
+
return typeof answer['ask'] === 'string' ? output({ permissionDecision: 'ask', permissionDecisionReason: answer['ask'] }) : undefined;
|
|
665
|
+
}
|
|
666
|
+
if (named === 'WorktreeCreate')
|
|
667
|
+
return output({ worktreePath: answer['result'] });
|
|
668
|
+
if (named === 'PermissionRequest') {
|
|
669
|
+
if (refuse !== undefined)
|
|
670
|
+
return output({ decision: defined({ behavior: 'deny', message: refuse, interrupt: answer['interrupt'] }) });
|
|
671
|
+
return answer['allow'] === true ? output({ decision: defined({ behavior: 'allow', updatedInput: answer['input'], updatedPermissions: answer['updates'] }) }) : undefined;
|
|
672
|
+
}
|
|
673
|
+
if (named === 'PermissionDenial')
|
|
674
|
+
return output({ retry: answer['retry'] });
|
|
675
|
+
if (named === 'ToolBatch')
|
|
676
|
+
return output({ additionalContext: answer['note'] });
|
|
677
|
+
if (named === 'ReplyDisplay')
|
|
678
|
+
return output({ displayContent: answer['text'] });
|
|
679
|
+
return refuse === undefined ? undefined : { decision: 'block', reason: refuse };
|
|
680
|
+
}
|
|
681
|
+
export function resultOf(where, call, answered) {
|
|
682
|
+
const base = { id: '', type: ToolResult, at: new Date().toISOString(), session: where.session, context: where.context, ...(where.turn === undefined ? {} : { turn: where.turn.prompt.id }), callId: '', name: call.name, input: call.input, cwd: call.cwd, files: noFiles };
|
|
427
683
|
if (answered.deny !== undefined)
|
|
428
|
-
return { ...base,
|
|
429
|
-
return { ...base,
|
|
684
|
+
return { ...base, output: answered.deny, isError: true };
|
|
685
|
+
return { ...base, output: answered.result ?? answered.text, isError: answered.isError === true };
|
|
430
686
|
}
|
|
431
|
-
function
|
|
432
|
-
return {
|
|
687
|
+
function searchOptionsOf({ running, since, everyProject, before }) {
|
|
688
|
+
return {
|
|
689
|
+
...(running === undefined ? {} : { running }),
|
|
690
|
+
...(since === undefined ? {} : { since }),
|
|
691
|
+
...(everyProject === undefined ? {} : { everyProject }),
|
|
692
|
+
...(before === undefined ? {} : { before: { session: before.session, id: before.id } }),
|
|
693
|
+
};
|
|
433
694
|
}
|
package/runtime/tool-calls.d.ts
CHANGED
|
@@ -6,7 +6,6 @@ export type Agent = {
|
|
|
6
6
|
};
|
|
7
7
|
export type ToolCalls = {
|
|
8
8
|
agentOf(toolUseId: string | undefined): Promise<Agent>;
|
|
9
|
-
cwdOf(toolUseId: string): string | undefined;
|
|
10
9
|
};
|
|
11
10
|
export declare const reservedKeys: readonly string[];
|
|
12
11
|
export declare function toolInputOf(envelope: object): Record<string, unknown>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-calls.d.ts","sourceRoot":"","sources":["../src/runtime/tool-calls.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC,MAAM,MAAM,KAAK,GAAG;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAE5D,MAAM,MAAM,SAAS,GAAG;IACtB,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"tool-calls.d.ts","sourceRoot":"","sources":["../src/runtime/tool-calls.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC,MAAM,MAAM,KAAK,GAAG;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAE5D,MAAM,MAAM,SAAS,GAAG;IACtB,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,CAAA;CACvD,CAAA;AAED,eAAO,MAAM,YAAY,EAAE,SAAS,MAAM,EAAkD,CAAA;AAE5F,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAErE;AAID,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,CA8BnE"}
|