@cmodjs/core 2.0.0 → 2.0.1
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 +3 -3
- package/docs/dependencies.md +1 -1
- 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 +5 -4
- package/runtime/before.d.ts +6 -3
- package/runtime/before.d.ts.map +1 -1
- package/runtime/before.js +59 -37
- package/runtime/conversations.d.ts.map +1 -1
- package/runtime/conversations.js +38 -13
- package/runtime/hooks.d.ts +9 -22
- package/runtime/hooks.d.ts.map +1 -1
- package/runtime/hooks.js +4 -58
- package/runtime/lifecycle.d.ts +2 -1
- package/runtime/lifecycle.d.ts.map +1 -1
- package/runtime/lifecycle.js +34 -17
- package/runtime/permission-check.d.ts +15 -0
- package/runtime/permission-check.d.ts.map +1 -0
- package/runtime/permission-check.js +37 -0
- package/runtime/sessions.d.ts +12 -8
- package/runtime/sessions.d.ts.map +1 -1
- package/runtime/sessions.js +41 -80
- package/runtime/state.d.ts.map +1 -1
- package/runtime/state.js +14 -11
- package/session/context/turn/message/message.d.ts +1 -0
- package/session/context/turn/message/message.d.ts.map +1 -1
- package/session/context/turn/message/message.js +5 -2
- package/src/records.ts +1 -1
- package/src/register.ts +5 -4
- package/src/runtime/before.ts +60 -39
- package/src/runtime/conversations.ts +35 -13
- package/src/runtime/hooks.ts +13 -84
- package/src/runtime/lifecycle.ts +49 -31
- package/src/runtime/permission-check.ts +47 -0
- package/src/runtime/sessions.ts +51 -84
- package/src/runtime/state.ts +15 -8
- package/src/session/context/turn/message/message.ts +6 -2
- package/src/testing.ts +4 -3
- package/testing.d.ts.map +1 -1
- package/testing.js +4 -3
package/runtime/lifecycle.js
CHANGED
|
@@ -28,6 +28,7 @@ const installId = 'cmod:install';
|
|
|
28
28
|
const installRetrySeconds = [2, 4, 8, 16, 32, 64, 128];
|
|
29
29
|
const cmodCheckMs = 1000;
|
|
30
30
|
const cmodWaitMs = 60_000;
|
|
31
|
+
const cmodSlowCheckMs = 10_000;
|
|
31
32
|
const startHoldMs = 10_000 - 1_000;
|
|
32
33
|
const dependencyCallMs = 30_000;
|
|
33
34
|
const cannotStart = /failed to start: /;
|
|
@@ -92,7 +93,7 @@ async function cmodVersion(claude) {
|
|
|
92
93
|
throw new Error(`cmod --version ${formatExit(result.exitCode, lastLineOf(result.stderr) ?? '')}`);
|
|
93
94
|
return result.stdout.trim().split(/\s+/).at(-1);
|
|
94
95
|
}
|
|
95
|
-
export function createLifecycle(definition,
|
|
96
|
+
export function createLifecycle(definition, permissionRoutes = () => undefined, options = {}, asksPerson = true) {
|
|
96
97
|
let router = createRouter();
|
|
97
98
|
let phase = 'starting';
|
|
98
99
|
let runtime;
|
|
@@ -250,7 +251,7 @@ export function createLifecycle(definition, checksPermissions = () => true, opti
|
|
|
250
251
|
if (runtime === undefined || plugin === undefined)
|
|
251
252
|
return;
|
|
252
253
|
const activeRouter = createRouter();
|
|
253
|
-
const active = await createMod(definition, { ...runtime, router: activeRouter, dataFolder: dataFolder(plugin.store, plugin.name), store: plugin.store, steps: plugin.steps, granted, refreshGrants,
|
|
254
|
+
const active = await createMod(definition, { ...runtime, router: activeRouter, dataFolder: dataFolder(plugin.store, plugin.name), store: plugin.store, steps: plugin.steps, granted, refreshGrants, permissionRoutes, options: pluginOptions }).catch(async (error) => {
|
|
254
255
|
if (error instanceof MissingOptions && (await askOptions(error)))
|
|
255
256
|
return undefined;
|
|
256
257
|
fail(messageOf(error), error instanceof MissingOptions ? `Set ${error.keys.length === 1 ? 'it' : 'them'} in /config.` : undefined, begin);
|
|
@@ -268,12 +269,19 @@ export function createLifecycle(definition, checksPermissions = () => true, opti
|
|
|
268
269
|
void runSteps(active.mod).catch((error) => toastFailure({ id: 'cmod:steps', message: 'Its installer stopped', error }));
|
|
269
270
|
const missed = missedSessionStart;
|
|
270
271
|
missedSessionStart = undefined;
|
|
271
|
-
const
|
|
272
|
+
const replay = (e) => (definition.name === cmodPluginName ? activeRouter.dispatch('classic.SessionStart', e, async () => ({})) : checkedRoute('classic.SessionStart', e, async () => ({})));
|
|
273
|
+
const lateAnswer = missed === undefined ? {} : await replay(missed).catch((error) => {
|
|
272
274
|
toastFailure({ id: 'classic.SessionStart', message: 'The SessionStart hook failed', error });
|
|
273
275
|
return {};
|
|
274
276
|
});
|
|
275
|
-
|
|
276
|
-
|
|
277
|
+
const { additionalContext, ...unread } = lateAnswer;
|
|
278
|
+
for (const text of additionalContext ?? []) {
|
|
279
|
+
const added = await runtime.claude.session.append({ message: { type: 'user', content: [{ type: 'text', text }] } });
|
|
280
|
+
if (added.deny !== undefined)
|
|
281
|
+
runtime.claude.ui.log(`${definition.name}: Claude Code refused its SessionStart note: ${added.deny}`, { to: 'debug' });
|
|
282
|
+
}
|
|
283
|
+
if (Object.keys(unread).length > 0) {
|
|
284
|
+
runtime.claude.ui.log(`${definition.name} started after Claude Code's SessionStart, so Claude Code did not read ${Object.keys(unread).join(', ')} from its SessionStart hooks.`, { to: 'debug' });
|
|
277
285
|
}
|
|
278
286
|
const version = plugin.version ?? '';
|
|
279
287
|
const announced = announcedOf(await runtime.claude.store.get(announcedKey));
|
|
@@ -359,7 +367,7 @@ export function createLifecycle(definition, checksPermissions = () => true, opti
|
|
|
359
367
|
timer.cancel();
|
|
360
368
|
longWait.cancel();
|
|
361
369
|
};
|
|
362
|
-
const
|
|
370
|
+
const poll = (ms) => claude().clock.every(ms, () => {
|
|
363
371
|
check ??= cmodVersion(claude())
|
|
364
372
|
.then((version) => {
|
|
365
373
|
check = undefined;
|
|
@@ -374,9 +382,14 @@ export function createLifecycle(definition, checksPermissions = () => true, opti
|
|
|
374
382
|
report(error);
|
|
375
383
|
});
|
|
376
384
|
});
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
385
|
+
let timer = poll(cmodCheckMs);
|
|
386
|
+
const longWait = claude().clock.after(cmodWaitMs, () => {
|
|
387
|
+
timer.cancel();
|
|
388
|
+
timer = poll(cmodSlowCheckMs);
|
|
389
|
+
installing(latest === undefined
|
|
390
|
+
? "Still waiting for cmod to download cmod. See cmod's own line."
|
|
391
|
+
: `PATH finds cmod ${latest}, and ${definition.name} needs cmod ${oldestCmodFor(plugin?.steps ?? {})} or later. Run npm i -g @cmodjs/cli, or put ~/.local/bin ahead of the old cmod on PATH.`);
|
|
392
|
+
});
|
|
380
393
|
};
|
|
381
394
|
const bootstrap = async (root) => {
|
|
382
395
|
installing();
|
|
@@ -544,8 +557,10 @@ export function createLifecycle(definition, checksPermissions = () => true, opti
|
|
|
544
557
|
if (event === 'cmod.call' && !isCallToThisMod)
|
|
545
558
|
return next(e);
|
|
546
559
|
await (event === 'classic.SessionStart' || isCallToThisMod ? holdUntilStarted() : whenActive());
|
|
547
|
-
if (event === 'classic.SessionStart' && phase !== 'active')
|
|
560
|
+
if (event === 'classic.SessionStart' && phase !== 'active') {
|
|
548
561
|
missedSessionStart = e;
|
|
562
|
+
return next(e);
|
|
563
|
+
}
|
|
549
564
|
if (isCallToThisMod && phase !== 'active') {
|
|
550
565
|
const isStopped = phase === 'declined' || phase === 'failed';
|
|
551
566
|
return { deny: isStopped ? `${definition.name} is not set up. Its toast says why.` : `${definition.name} is installing. Try again when it's ready.` };
|
|
@@ -646,7 +661,7 @@ async function createMod(definition, runtime) {
|
|
|
646
661
|
}));
|
|
647
662
|
const modState = createState({ name: definition.name, initial: definition.state ?? {}, session, root, claude, report, changed: area.changed, forkParent });
|
|
648
663
|
const modOptions = createOptions({ name: definition.name, declared: definition.options ?? {}, fromClaude: runtime.options, claude, report, changed: modState.changed });
|
|
649
|
-
const hookOn = (hook, listener) => router.add(`classic.${hook}`, classicHook(
|
|
664
|
+
const hookOn = (hook, listener) => router.add(`classic.${hook}`, classicHook(hook, listener, report));
|
|
650
665
|
const expand = (path) => (homePrefix.test(path) ? resolvedPath(path, places) : path);
|
|
651
666
|
const settingsFile = (source) => ({
|
|
652
667
|
read: () => checked.settings.read({ source }),
|
|
@@ -662,6 +677,7 @@ async function createMod(definition, runtime) {
|
|
|
662
677
|
isGranted: (permission) => flag(permission),
|
|
663
678
|
hookOn: (hook, listener) => hookOn(hook, listener),
|
|
664
679
|
routeOn: (event, hook) => router.add(event, hook),
|
|
680
|
+
permissionRoutes: runtime.permissionRoutes,
|
|
665
681
|
});
|
|
666
682
|
let followed;
|
|
667
683
|
let isSetUp = false;
|
|
@@ -706,8 +722,8 @@ async function createMod(definition, runtime) {
|
|
|
706
722
|
const change = { source: source === 'skills' ? 'skills' : source.replace('_settings', ''), ...(file_path === undefined ? {} : { path: file_path }) };
|
|
707
723
|
if (matcher !== undefined && !matches(change, matcher))
|
|
708
724
|
return undefined;
|
|
709
|
-
const
|
|
710
|
-
return
|
|
725
|
+
const refuse = (await listener(change))?.refuse;
|
|
726
|
+
return typeof refuse === 'string' ? { block: refuse } : undefined;
|
|
711
727
|
});
|
|
712
728
|
},
|
|
713
729
|
},
|
|
@@ -729,11 +745,12 @@ async function createMod(definition, runtime) {
|
|
|
729
745
|
const watch = { paths, listener };
|
|
730
746
|
watches.add(watch);
|
|
731
747
|
if (watches.size === 1) {
|
|
732
|
-
hookOn('SessionStart', () => ({
|
|
748
|
+
hookOn('SessionStart', () => ({ watchPaths: [...new Set([...watches].flatMap((each) => each.paths))] }));
|
|
733
749
|
hookOn('FileChanged', ({ file_path, event }) => {
|
|
734
750
|
for (const each of watches)
|
|
735
751
|
if (each.paths.some((watched) => relativePath(watched, file_path) !== undefined))
|
|
736
752
|
void Promise.resolve(each.listener({ path: file_path, type: event })).catch((error) => report({ id: 'fs:onChange', message: 'The fs.onChange listener failed', error }));
|
|
753
|
+
return undefined;
|
|
737
754
|
});
|
|
738
755
|
}
|
|
739
756
|
return { dispose: () => void watches.delete(watch) };
|
|
@@ -933,7 +950,7 @@ async function createMod(definition, runtime) {
|
|
|
933
950
|
await failsAs('its setup function threw', () => definition.setup(mod));
|
|
934
951
|
isSetUp = true;
|
|
935
952
|
const permissionHooks = permissionEvents.filter((event) => router.has(event));
|
|
936
|
-
if (permissionHooks.length > 0 &&
|
|
953
|
+
if (permissionHooks.length > 0 && runtime.permissionRoutes() === undefined) {
|
|
937
954
|
throw new Error(`it decides permissions on ${listed(permissionHooks)}, so hooks/register.ts must call registerPermissionCheck(addHook) after registerMod`);
|
|
938
955
|
}
|
|
939
956
|
await failsAs('its open panes did not load', () => area.restorePanes());
|
|
@@ -943,7 +960,7 @@ function checkingKeys(claude, toast, keys) {
|
|
|
943
960
|
const bound = Object.entries(keys);
|
|
944
961
|
if (bound.length === 0)
|
|
945
962
|
return claude;
|
|
946
|
-
const
|
|
963
|
+
const addCommand = (command) => {
|
|
947
964
|
for (const [key, commandName] of bound) {
|
|
948
965
|
if (commandName === command.name && command.immediate !== true) {
|
|
949
966
|
toast.warning(`${key} runs /${command.name}, which waits for Claude's turn to end`, { id: `key:${key}`, description: `It adds a row to the conversation. Give /${command.name} immediate: true.`, duration: Infinity });
|
|
@@ -951,7 +968,7 @@ function checkingKeys(claude, toast, keys) {
|
|
|
951
968
|
}
|
|
952
969
|
return claude.command.register(command);
|
|
953
970
|
};
|
|
954
|
-
return { ...claude, command: { ...claude.command, register } };
|
|
971
|
+
return { ...claude, command: { ...claude.command, register: addCommand } };
|
|
955
972
|
}
|
|
956
973
|
const longestTimerMs = 2_147_483_647;
|
|
957
974
|
function checkedMs(name, subject, ms) {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Disposable } from '../mod.js';
|
|
2
|
+
import type { HookFields } from './hooks.js';
|
|
3
|
+
import { type HookOn, type RouteOn } from './sessions.js';
|
|
4
|
+
export type PermissionUse = {
|
|
5
|
+
readonly raise: (method: string, payload: HookFields) => Promise<HookFields | undefined>;
|
|
6
|
+
readonly hookOn: HookOn;
|
|
7
|
+
readonly routeOn: RouteOn;
|
|
8
|
+
readonly current: () => {
|
|
9
|
+
readonly id: string;
|
|
10
|
+
readonly cwd: string;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
export type PermissionRoutes = (method: string, use: PermissionUse) => Disposable;
|
|
14
|
+
export declare const permissionRoutes: PermissionRoutes;
|
|
15
|
+
//# sourceMappingURL=permission-check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permission-check.d.ts","sourceRoot":"","sources":["../src/runtime/permission-check.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAC5C,OAAO,EAAuC,KAAK,MAAM,EAAE,KAAK,OAAO,EAAkB,MAAM,eAAe,CAAA;AAE9G,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,KAAK,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAAA;IACxF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;CACtE,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,KAAK,UAAU,CAAA;AAMjF,eAAO,MAAM,gBAAgB,EAAE,gBA6B9B,CAAA"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { defined, processPayloadOf, recordOf } from './sessions.js';
|
|
2
|
+
const verdictName = (decision) => (decision === 'deny' ? 'refuse' : decision);
|
|
3
|
+
export const permissionRoutes = (method, { raise, hookOn, routeOn, current }) => {
|
|
4
|
+
const isBefore = method.startsWith('before');
|
|
5
|
+
if (method.endsWith('PermissionCheck')) {
|
|
6
|
+
return routeOn('tool.check', async (e, next) => {
|
|
7
|
+
const below = (await next(e));
|
|
8
|
+
const { id, cwd } = current();
|
|
9
|
+
const payload = { session: id, 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) };
|
|
10
|
+
if (!isBefore) {
|
|
11
|
+
void raise(method, payload);
|
|
12
|
+
return below;
|
|
13
|
+
}
|
|
14
|
+
const answer = await raise(method, payload);
|
|
15
|
+
if (answer === undefined || below.decision === 'deny')
|
|
16
|
+
return below;
|
|
17
|
+
if (typeof answer['refuse'] === 'string')
|
|
18
|
+
return { decision: 'deny', reason: answer['refuse'] };
|
|
19
|
+
if (typeof answer['ask'] === 'string')
|
|
20
|
+
return { decision: 'ask', reason: answer['ask'] };
|
|
21
|
+
return answer['allow'] === true ? { decision: 'allow' } : below;
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
return hookOn('PermissionRequest', async (input) => {
|
|
25
|
+
const payload = processPayloadOf('PermissionRequest', input);
|
|
26
|
+
if (!isBefore) {
|
|
27
|
+
void raise(method, payload);
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
const answer = await raise(method, payload);
|
|
31
|
+
if (answer === undefined)
|
|
32
|
+
return undefined;
|
|
33
|
+
if (typeof answer['refuse'] === 'string')
|
|
34
|
+
return { decision: defined({ behavior: 'deny', message: answer['refuse'], interrupt: answer['interrupt'] }) };
|
|
35
|
+
return answer['allow'] === true ? { decision: defined({ behavior: 'allow', updatedInput: answer['input'], updatedPermissions: answer['updates'] }) } : undefined;
|
|
36
|
+
});
|
|
37
|
+
};
|
package/runtime/sessions.d.ts
CHANGED
|
@@ -2,11 +2,12 @@ import type { Args, CommandRunResult } from 'claude-code';
|
|
|
2
2
|
import type { Disposable } from '../mod.js';
|
|
3
3
|
import { ToolResult } from '../session/context/turn/message/tool-result.js';
|
|
4
4
|
import { type SessionChange, type SessionData, type TaskData } from '../session/conversation.js';
|
|
5
|
-
import {
|
|
5
|
+
import { type BeforeEvent, type ProcessName, type SessionEvent, type Sessions, type TaskStartArgs, type ToolCallArgs, type Usage } from '../session/session.js';
|
|
6
6
|
import { type SentMatcher } from '../utils/matcher.js';
|
|
7
7
|
import { type Place } from './before.js';
|
|
8
8
|
import type { Claude } from './claude.js';
|
|
9
|
-
import { type
|
|
9
|
+
import { type HookFields } from './hooks.js';
|
|
10
|
+
import type { PermissionRoutes } from './permission-check.js';
|
|
10
11
|
export type Subscription = {
|
|
11
12
|
readonly event: SessionEvent;
|
|
12
13
|
readonly matcher: SentMatcher | undefined;
|
|
@@ -54,16 +55,17 @@ export declare function usageOf(claude: Pick<Claude, 'session'>): Promise<Usage>
|
|
|
54
55
|
export declare function payloadOf(change: SessionChange): unknown;
|
|
55
56
|
export declare function isHeardBy(event: SessionEvent, matcher: unknown, change: SessionChange): boolean;
|
|
56
57
|
export declare function isInScope(scope: Scope, session: string, change: SessionChange, isUnder: (task: string, ancestor: string) => boolean): boolean;
|
|
57
|
-
export
|
|
58
|
+
export type HookOn = (hook: string, listener: (input: unknown) => HookFields | undefined | Promise<HookFields | undefined>) => Disposable;
|
|
59
|
+
export declare function createSessionsClient({ name, claude, report, isGranted, hookOn, routeOn, permissionRoutes }: {
|
|
58
60
|
readonly name: string;
|
|
59
61
|
readonly claude: Claude;
|
|
60
62
|
readonly report: (id: string, message: string, error: unknown) => void;
|
|
61
63
|
readonly isGranted: (permission: string) => boolean;
|
|
62
|
-
readonly hookOn?:
|
|
64
|
+
readonly hookOn?: HookOn;
|
|
63
65
|
readonly routeOn?: RouteOn;
|
|
66
|
+
readonly permissionRoutes?: () => PermissionRoutes | undefined;
|
|
64
67
|
}): SessionsClient;
|
|
65
|
-
type
|
|
66
|
-
type ToolCheck = {
|
|
68
|
+
export type ToolCheck = {
|
|
67
69
|
readonly tool: string;
|
|
68
70
|
readonly input: unknown;
|
|
69
71
|
readonly tool_use_id?: string;
|
|
@@ -74,9 +76,11 @@ export type RouteOn = {
|
|
|
74
76
|
(event: 'tool.check', hook: (e: ToolCheck, next: (e: ToolCheck) => Promise<unknown>) => Promise<unknown>): Disposable;
|
|
75
77
|
(event: 'command.run', hook: (e: Args<'command.run'>, next: (e: Args<'command.run'>) => Promise<CommandRunResult>) => Promise<CommandRunResult>): Disposable;
|
|
76
78
|
};
|
|
79
|
+
export declare const recordOf: (value: unknown) => Readonly<Record<string, unknown>>;
|
|
80
|
+
export declare const defined: (fields: HookFields) => HookFields;
|
|
81
|
+
type ProcessFieldsName = Exclude<ProcessName, 'PermissionCheck'>;
|
|
77
82
|
export declare function spawnTask(claude: Pick<Claude, 'agent'>, task: TaskStartArgs): Promise<TaskData>;
|
|
78
|
-
export declare function processPayloadOf(named:
|
|
79
|
-
export declare function hookAnswerOf(named: string, hook: string, answer: HookFields): HookAnswer | undefined;
|
|
83
|
+
export declare function processPayloadOf(named: ProcessFieldsName, input: unknown): HookFields;
|
|
80
84
|
export declare function resultOf(where: Pick<Place, 'session' | 'context' | 'turn'>, call: ToolCallArgs & {
|
|
81
85
|
readonly cwd: string;
|
|
82
86
|
}, answered: Awaited<ReturnType<Claude['tool']['call']>>): ToolResult;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../src/runtime/sessions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AACzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAM3C,OAAO,EAAE,UAAU,EAAE,MAAM,gDAAgD,CAAA;AAE3E,OAAO,EAAgD,KAAK,aAAa,EAAE,KAAK,WAAW,EAAE,KAAK,QAAQ,EAAiB,MAAM,4BAA4B,CAAA;AAG7J,OAAO,
|
|
1
|
+
{"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../src/runtime/sessions.ts"],"names":[],"mappings":"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"}
|
package/runtime/sessions.js
CHANGED
|
@@ -10,7 +10,7 @@ import { GoalChange } from '../session/goal/goal.js';
|
|
|
10
10
|
import { TaskEnd, TaskStart } from '../session/goal/task.js';
|
|
11
11
|
import { beforeEvents, processEvents, Session, sessionEvents } from '../session/session.js';
|
|
12
12
|
import { matches, sentMatcher } from '../utils/matcher.js';
|
|
13
|
-
import { foldAnswers } from './before.js';
|
|
13
|
+
import { beforeAnswerKeys, foldAnswers, foldBefore, methodOf, takenAnswer } from './before.js';
|
|
14
14
|
import { cmodPluginName } from './dependencies.js';
|
|
15
15
|
import { messageOf } from '../utils/text.js';
|
|
16
16
|
import { listenerOf, PassedTimeout, withinTime } from './deadline.js';
|
|
@@ -36,7 +36,6 @@ const actPermissions = { send: 'prompt', startTask: 'tasks', usage: 'conversatio
|
|
|
36
36
|
const startsTurns = ['CronCreate'];
|
|
37
37
|
const ownSchedule = ['CronDelete', 'CronList'];
|
|
38
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}`;
|
|
40
39
|
export const isAct = (action) => action === 'callTool' || action in actPermissions;
|
|
41
40
|
export function toolPermission(tool) {
|
|
42
41
|
return startsTurns.includes(tool) ? 'prompt' : ownSchedule.includes(tool) ? undefined : 'tools';
|
|
@@ -113,7 +112,7 @@ export function isInScope(scope, session, change, isUnder) {
|
|
|
113
112
|
return change.message.turn === scope.turn;
|
|
114
113
|
return (change.type === TurnStart || change.type === TurnEnd) && change.turn.prompt.id === scope.turn;
|
|
115
114
|
}
|
|
116
|
-
export function createSessionsClient({ name, claude, report, isGranted, hookOn, routeOn }) {
|
|
115
|
+
export function createSessionsClient({ name, claude, report, isGranted, hookOn, routeOn, permissionRoutes }) {
|
|
117
116
|
const call = (method, input) => claude.cmod.call({ to: cmodPluginName, method, input });
|
|
118
117
|
const listening = new Map();
|
|
119
118
|
const deciding = new Map();
|
|
@@ -176,6 +175,12 @@ export function createSessionsClient({ name, claude, report, isGranted, hookOn,
|
|
|
176
175
|
},
|
|
177
176
|
};
|
|
178
177
|
};
|
|
178
|
+
const invoke = (method, options, run, takes = []) => withinTime(claude.clock, method, options, run).catch((error) => {
|
|
179
|
+
if (error instanceof PassedTimeout && !method.startsWith('before'))
|
|
180
|
+
return undefined;
|
|
181
|
+
report(`session:${method}`, `The ${method} listener failed`, error);
|
|
182
|
+
return options.onFailure === 'refuse' && takes.includes('refuse') ? { refuse: `${name}: ${messageOf(error)}` } : undefined;
|
|
183
|
+
});
|
|
179
184
|
const combined = async (session, event, change, isInProject) => {
|
|
180
185
|
const askers = [...(deciding.get(session) ?? []), ...(isInProject ? (deciding.get(everySession) ?? []) : [])]
|
|
181
186
|
.filter((entry) => entry.event === event)
|
|
@@ -185,17 +190,12 @@ export function createSessionsClient({ name, claude, report, isGranted, hookOn,
|
|
|
185
190
|
if (entry.matcher !== undefined && !matches(payloadOf(latest), entry.matcher))
|
|
186
191
|
return undefined;
|
|
187
192
|
const payload = await payloadFor(session, latest);
|
|
188
|
-
const answer = await
|
|
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
|
-
});
|
|
193
|
+
const answer = await invoke(entry.method, entry.options, async (time) => takenAnswer(entry.method, beforeAnswerKeys[event], await entry.listener(payload, time)), beforeAnswerKeys[event]);
|
|
194
194
|
if (answer === undefined || answer === null || (typeof answer === 'object' && 'refuse' in answer) || isGranted(beforePermission(event)))
|
|
195
195
|
return answer;
|
|
196
196
|
throw new Error(`Its ${entry.method} answer changes nothing until ${beforePermission(event)} is turned on for ${name} in /mods.`);
|
|
197
197
|
});
|
|
198
|
-
return
|
|
198
|
+
return foldBefore(event, change, askers, (error) => report(`session:${methodOf('before', event)}`, `The ${methodOf('before', event)} listener failed`, error));
|
|
199
199
|
};
|
|
200
200
|
const eventMethods = (scopeOf, hasBefore, ended) => {
|
|
201
201
|
const method = (into, named, event) => (...args) => {
|
|
@@ -208,10 +208,12 @@ export function createSessionsClient({ name, claude, report, isGranted, hookOn,
|
|
|
208
208
|
...(hasBefore ? beforeEvents.map((named) => [`before${named}`, method(deciding, `before${named}`, sessionEvents[named])]) : []),
|
|
209
209
|
]);
|
|
210
210
|
};
|
|
211
|
+
let folders;
|
|
211
212
|
const withFiles = async (message) => {
|
|
212
213
|
if (message.type !== ToolCall && message.type !== ToolResult)
|
|
213
214
|
return message;
|
|
214
|
-
|
|
215
|
+
folders ??= Promise.all([claude.env.home(), claude.env.configHome()]).then(([home, configRoot]) => ({ home, configRoot }));
|
|
216
|
+
return { ...message, files: await callFiles(name, claude, folders, { tool: message.name, input: message.input }, message.cwd) };
|
|
215
217
|
};
|
|
216
218
|
const messagesFrom = async (method, input) => Promise.all((await call(method, input)).map(withFiles));
|
|
217
219
|
const searched = (request, matcher) => ({
|
|
@@ -405,39 +407,16 @@ export function createSessionsClient({ name, claude, report, isGranted, hookOn,
|
|
|
405
407
|
};
|
|
406
408
|
const processListeners = new Map();
|
|
407
409
|
const raise = async (method, payload) => {
|
|
408
|
-
const isBefore = method.startsWith('before');
|
|
409
410
|
const heard = [...(processListeners.get(method) ?? [])].filter(({ matcher }) => payload['task'] === undefined && (matcher === undefined || matches(payload, matcher)));
|
|
410
|
-
if (!
|
|
411
|
-
await Promise.all(heard.map(({ listener, options }) =>
|
|
412
|
-
if (!(error instanceof PassedTimeout))
|
|
413
|
-
report(`session:${method}`, `The ${method} listener failed`, error);
|
|
414
|
-
})));
|
|
411
|
+
if (!method.startsWith('before')) {
|
|
412
|
+
await Promise.all(heard.map(({ listener, options }) => invoke(method, options, (time) => listener(payload, time))));
|
|
415
413
|
return undefined;
|
|
416
414
|
}
|
|
415
|
+
const failing = method === 'beforePermissionRequest' ? { onFailure: 'refuse' } : {};
|
|
417
416
|
const takes = answerKeys[method.slice('before'.length)] ?? [];
|
|
418
|
-
|
|
419
|
-
|
|
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;
|
|
417
|
+
const askers = heard.map(({ listener, options }) => () => invoke(method, { ...options, ...failing }, async (time) => takenAnswer(method, takes, await listener(payload, time)), takes));
|
|
418
|
+
return foldAnswers(method, takes, ['refuse'], undefined, askers, (error) => report(`session:${method}`, `The ${method} listener failed`, error));
|
|
439
419
|
};
|
|
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
420
|
const commandRun = async (e) => {
|
|
442
421
|
const source = (await claude.command.list()).find((command) => command.name === e.command)?.source;
|
|
443
422
|
return { session: current.id, cwd: current.cwd, name: e.command, args: e.args, ...defined({ source }), from: senderOf(e.origin, false) };
|
|
@@ -466,22 +445,11 @@ export function createSessionsClient({ name, claude, report, isGranted, hookOn,
|
|
|
466
445
|
if (hookOn === undefined || routeOn === undefined)
|
|
467
446
|
throw new Error(`${name}: ${method} works only in a mod's own session.`);
|
|
468
447
|
const isBefore = method.startsWith('before');
|
|
469
|
-
if (named === 'PermissionCheck') {
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
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
|
-
});
|
|
448
|
+
if (named === 'PermissionCheck' || named === 'PermissionRequest') {
|
|
449
|
+
const routes = permissionRoutes?.();
|
|
450
|
+
if (routes !== undefined)
|
|
451
|
+
return routes(method, { raise, hookOn, routeOn, current: () => current });
|
|
452
|
+
return named === 'PermissionCheck' ? routeOn('tool.check', (e, next) => next(e)) : hookOn('PermissionRequest', () => undefined);
|
|
485
453
|
}
|
|
486
454
|
const event = processEvents[named];
|
|
487
455
|
const hook = (isBefore ? event.before : undefined) ?? event.on;
|
|
@@ -493,7 +461,7 @@ export function createSessionsClient({ name, claude, report, isGranted, hookOn,
|
|
|
493
461
|
return undefined;
|
|
494
462
|
}
|
|
495
463
|
const answer = await raise(method, payload);
|
|
496
|
-
return answer === undefined ? undefined : hookAnswerOf(named,
|
|
464
|
+
return answer === undefined ? undefined : hookAnswerOf(named, answer);
|
|
497
465
|
});
|
|
498
466
|
};
|
|
499
467
|
const listen = (method, args) => {
|
|
@@ -529,16 +497,15 @@ export function createSessionsClient({ name, claude, report, isGranted, hookOn,
|
|
|
529
497
|
},
|
|
530
498
|
changed(session, change, isInProject) {
|
|
531
499
|
remember(session, change);
|
|
500
|
+
let payload;
|
|
532
501
|
for (const key of isInProject ? [session, everySession] : [session]) {
|
|
533
502
|
for (const { scope, event, method, matcher, listener, options } of listening.get(key) ?? []) {
|
|
534
503
|
if (!isHeardBy(event, matcher, change) || !isInScope(scope, session, change, isUnderIn(session)))
|
|
535
504
|
continue;
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
.
|
|
539
|
-
|
|
540
|
-
report(`session:${method}`, `The ${method} listener failed`, error);
|
|
541
|
-
});
|
|
505
|
+
payload ??= payloadFor(session, change);
|
|
506
|
+
void payload
|
|
507
|
+
.then((heard) => invoke(method, options, (time) => listener(heard, time)))
|
|
508
|
+
.catch((error) => report(`session:${method}`, `The ${method} listener failed`, error));
|
|
542
509
|
}
|
|
543
510
|
}
|
|
544
511
|
},
|
|
@@ -567,9 +534,8 @@ export function createSessionsClient({ name, claude, report, isGranted, hookOn,
|
|
|
567
534
|
},
|
|
568
535
|
};
|
|
569
536
|
}
|
|
570
|
-
const
|
|
571
|
-
const
|
|
572
|
-
const defined = (fields) => Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== undefined));
|
|
537
|
+
export const recordOf = (value) => (typeof value === 'object' && value !== null && !Array.isArray(value) ? value : {});
|
|
538
|
+
export const defined = (fields) => Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== undefined));
|
|
573
539
|
const placementOf = (input) => defined({ session: input['session_id'], task: input['agent_id'], agentType: input['agent_type'], cwd: input['cwd'], permissionMode: input['permission_mode'] });
|
|
574
540
|
const mcpServerOf = (server) => (server === undefined ? undefined : { name: String(recordOf(server)['name']), source: String(recordOf(server)['source']) });
|
|
575
541
|
const modelChangeOf = (input) => ({
|
|
@@ -653,30 +619,25 @@ export function processPayloadOf(named, input) {
|
|
|
653
619
|
const fields = recordOf(input);
|
|
654
620
|
return { ...placementOf(fields), ...defined(processFields[named](fields)) };
|
|
655
621
|
}
|
|
656
|
-
|
|
657
|
-
const output = (fields) => ({ hookSpecificOutput: { hookEventName: hook, ...defined(fields) } });
|
|
622
|
+
function hookAnswerOf(named, answer) {
|
|
658
623
|
const refuse = typeof answer['refuse'] === 'string' ? answer['refuse'] : undefined;
|
|
624
|
+
const note = typeof answer['note'] === 'string' ? [answer['note']] : undefined;
|
|
659
625
|
if (named === 'SessionStart')
|
|
660
|
-
return
|
|
626
|
+
return defined({ additionalContext: note, sessionTitle: answer['title'], reloadSkills: answer['reloadSkills'] === true ? true : undefined, initialUserMessage: answer['firstPrompt'] });
|
|
661
627
|
if (named === 'ModelChange') {
|
|
662
628
|
if (refuse !== undefined)
|
|
663
|
-
return
|
|
664
|
-
return typeof answer['ask'] === 'string' ?
|
|
629
|
+
return { permissionDecision: 'deny', permissionDecisionReason: refuse };
|
|
630
|
+
return typeof answer['ask'] === 'string' ? { permissionDecision: 'ask', permissionDecisionReason: answer['ask'] } : undefined;
|
|
665
631
|
}
|
|
666
632
|
if (named === 'WorktreeCreate')
|
|
667
|
-
return
|
|
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
|
-
}
|
|
633
|
+
return defined({ worktreePath: answer['result'] });
|
|
673
634
|
if (named === 'PermissionDenial')
|
|
674
|
-
return
|
|
635
|
+
return answer['retry'] === true ? { retry: true } : undefined;
|
|
675
636
|
if (named === 'ToolBatch')
|
|
676
|
-
return
|
|
637
|
+
return defined({ additionalContext: note });
|
|
677
638
|
if (named === 'ReplyDisplay')
|
|
678
|
-
return
|
|
679
|
-
return refuse === undefined ? undefined : {
|
|
639
|
+
return defined({ displayContent: answer['text'] });
|
|
640
|
+
return refuse === undefined ? undefined : { block: refuse };
|
|
680
641
|
}
|
|
681
642
|
export function resultOf(where, call, answered) {
|
|
682
643
|
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 };
|
package/runtime/state.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/runtime/state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AACpD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAQ3C,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IAC3C,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAA;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;CACvD,CAAA;AAED,MAAM,MAAM,QAAQ,CAAC,KAAK,SAAS,MAAM,IAAI;IAC3C,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,OAAO,IAAI,IAAI,CAAA;IACf,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACrB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACnC,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,CAAC,cAAc,CAAC,CAAC,QAAQ,CAAC,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACvH,CAAA;AAQD,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAIvE;AAED,wBAAsB,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAO9H;AAED,wBAAgB,WAAW,CAAC,KAAK,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,YAAY,GAAG,QAAQ,CAAC,KAAK,CAAC,
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/runtime/state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AACpD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAQ3C,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IAC3C,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAA;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;CACvD,CAAA;AAED,MAAM,MAAM,QAAQ,CAAC,KAAK,SAAS,MAAM,IAAI;IAC3C,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,OAAO,IAAI,IAAI,CAAA;IACf,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACrB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACnC,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,CAAC,cAAc,CAAC,CAAC,QAAQ,CAAC,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACvH,CAAA;AAQD,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAIvE;AAED,wBAAsB,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAO9H;AAED,wBAAgB,WAAW,CAAC,KAAK,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,YAAY,GAAG,QAAQ,CAAC,KAAK,CAAC,CAoH9J"}
|
package/runtime/state.js
CHANGED
|
@@ -46,17 +46,18 @@ export function createState({ name, initial, session, root, claude, report, chan
|
|
|
46
46
|
const transcriptsKey = `${name}.$transcripts.`;
|
|
47
47
|
const forgetEndedSessions = async (current) => {
|
|
48
48
|
const stored = await claude.store.keys();
|
|
49
|
-
|
|
50
|
-
const owner = recorded.slice(transcriptsKey.length);
|
|
51
|
-
if (owner === current)
|
|
52
|
-
continue;
|
|
49
|
+
const ended = async (recorded) => {
|
|
53
50
|
const transcript = await claude.store.get(recorded);
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
51
|
+
return typeof transcript !== 'string' || !(await claude.fs.exists(transcript));
|
|
52
|
+
};
|
|
53
|
+
const owners = stored.filter((key) => key.startsWith(transcriptsKey) && key !== `${transcriptsKey}${current}`);
|
|
54
|
+
await Promise.all(owners.map(async (recorded) => {
|
|
55
|
+
if (!(await ended(recorded)))
|
|
56
|
+
return;
|
|
57
|
+
const owner = recorded.slice(transcriptsKey.length);
|
|
58
|
+
await Promise.all(stored.filter((key) => key.startsWith(`${name}.`) && key.endsWith(`.sessions.${owner}`)).map((value) => claude.store.delete(value)));
|
|
58
59
|
await claude.store.delete(recorded);
|
|
59
|
-
}
|
|
60
|
+
}));
|
|
60
61
|
};
|
|
61
62
|
const loadGroups = async (only, next) => {
|
|
62
63
|
const loaded = [];
|
|
@@ -114,8 +115,10 @@ export function createState({ name, initial, session, root, claude, report, chan
|
|
|
114
115
|
}),
|
|
115
116
|
moveTo: (nextRoot) => inTurn(async () => (nextRoot === projectRoot ? undefined : loadGroups(['project'], { root: nextRoot, session: sessionId }))),
|
|
116
117
|
switchSession: (nextSession, source, transcript) => inTurn(async () => {
|
|
117
|
-
|
|
118
|
-
|
|
118
|
+
if (Object.keys(declared.session).length > 0) {
|
|
119
|
+
await claude.store.set(`${transcriptsKey}${nextSession}`, transcript);
|
|
120
|
+
void forgetEndedSessions(nextSession).catch((error) => report({ id: 'state:sessions', message: 'Could not forget the session values of ended sessions', error }));
|
|
121
|
+
}
|
|
119
122
|
if (nextSession === sessionId)
|
|
120
123
|
return;
|
|
121
124
|
if (source === 'fork')
|
|
@@ -35,6 +35,7 @@ export type Row = {
|
|
|
35
35
|
};
|
|
36
36
|
export declare const messageTypes: readonly MessageType[];
|
|
37
37
|
export declare const skillPrefix = "Base directory for this skill:";
|
|
38
|
+
export declare function instructionPathOf(text: string): string;
|
|
38
39
|
export declare function rowOfLine(row: Readonly<Record<string, unknown>>): Row | undefined;
|
|
39
40
|
export declare function rowOfAppend(append: {
|
|
40
41
|
readonly uuid: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message.d.ts","sourceRoot":"","sources":["../../../../src/session/context/turn/message/message.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"message.d.ts","sourceRoot":"","sources":["../../../../src/session/context/turn/message/message.ts"],"names":[],"mappings":"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"}
|