@cmodjs/core 2.1.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/commands.md +1 -1
- package/docs/mod.md +2 -0
- package/docs/session.md +9 -1
- package/docs/testing.md +4 -2
- package/jobs/context.d.ts.map +1 -1
- package/jobs/context.js +2 -1
- package/jobs/permissions.d.ts.map +1 -1
- package/jobs/permissions.js +1 -1
- package/jobs/prompt.d.ts.map +1 -1
- package/jobs/prompt.js +2 -1
- package/jobs/slash-command.d.ts.map +1 -1
- package/jobs/slash-command.js +4 -3
- 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 +58 -51
- package/runtime/before.d.ts +1 -1
- package/runtime/before.d.ts.map +1 -1
- package/runtime/before.js +6 -5
- package/runtime/conversations.d.ts.map +1 -1
- package/runtime/conversations.js +46 -19
- package/runtime/hooks.d.ts +2 -1
- package/runtime/hooks.d.ts.map +1 -1
- package/runtime/hooks.js +7 -0
- package/runtime/lifecycle.d.ts +2 -1
- package/runtime/lifecycle.d.ts.map +1 -1
- package/runtime/lifecycle.js +31 -9
- package/runtime/permission-check.d.ts +3 -0
- package/runtime/permission-check.d.ts.map +1 -1
- package/runtime/permission-check.js +19 -6
- package/runtime/router.d.ts +3 -1
- package/runtime/router.d.ts.map +1 -1
- package/runtime/router.js +19 -11
- package/runtime/sessions.d.ts +13 -4
- package/runtime/sessions.d.ts.map +1 -1
- package/runtime/sessions.js +67 -26
- package/runtime/toaster.d.ts.map +1 -1
- package/runtime/toaster.js +3 -0
- package/session/context/turn/message/message.d.ts.map +1 -1
- package/session/context/turn/message/message.js +30 -2
- package/session/context/turn/message/note.d.ts +7 -0
- package/session/context/turn/message/note.d.ts.map +1 -1
- package/session/context/turn/message/note.js +12 -0
- package/session/session.d.ts +3 -1
- package/session/session.d.ts.map +1 -1
- package/src/jobs/context.ts +2 -1
- package/src/jobs/permissions.ts +8 -4
- package/src/jobs/prompt.ts +2 -1
- package/src/jobs/slash-command.ts +4 -3
- package/src/records.ts +1 -1
- package/src/register.ts +63 -52
- package/src/runtime/before.ts +7 -6
- package/src/runtime/conversations.ts +54 -27
- package/src/runtime/hooks.ts +9 -1
- package/src/runtime/lifecycle.ts +37 -13
- package/src/runtime/permission-check.ts +44 -22
- package/src/runtime/router.ts +23 -13
- package/src/runtime/sessions.ts +91 -37
- package/src/runtime/toaster.ts +2 -0
- package/src/session/context/turn/message/message.ts +27 -2
- package/src/session/context/turn/message/note.ts +16 -0
- package/src/session/session.ts +1 -2
- package/src/testing/fake-claude.ts +4 -3
- package/src/testing.ts +6 -5
- package/testing/fake-claude.d.ts +1 -0
- package/testing/fake-claude.d.ts.map +1 -1
- package/testing/fake-claude.js +3 -2
- package/testing.d.ts.map +1 -1
- package/testing.js +5 -4
package/runtime/hooks.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ClassicHookEvent, ClassicHookInputs } from 'claude-code';
|
|
2
2
|
import type { Claude } from './claude.js';
|
|
3
|
-
import type { RoutedHook } from './router.js';
|
|
3
|
+
import type { RoutedFailure, RoutedHook } from './router.js';
|
|
4
4
|
import type { Failure } from './toaster.js';
|
|
5
5
|
import { type CallBase } from '../utils/call-effects.js';
|
|
6
6
|
export type Hook = Exclude<ClassicHookEvent, 'PreToolUse'>;
|
|
@@ -12,6 +12,7 @@ type CallFiles = {
|
|
|
12
12
|
readonly changed: string[];
|
|
13
13
|
};
|
|
14
14
|
export declare function classicHook<H extends Hook>(event: H, hook: HookListener<H>, report: (failure: Failure) => void): RoutedHook<RoutedEvent>;
|
|
15
|
+
export declare function classicFailure<H extends Hook>(failed: (input: ClassicHookInputs[H], error: Error) => HookFields | undefined | Promise<HookFields | undefined>): RoutedFailure<RoutedEvent>;
|
|
15
16
|
export declare function userSkillHook(claude: Claude): RoutedHook<'skill.prompt'>;
|
|
16
17
|
export type HomeFolders = {
|
|
17
18
|
readonly home: string | undefined;
|
package/runtime/hooks.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/runtime/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAEtE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/runtime/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAEtE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAC5D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAC3C,OAAO,EAAe,KAAK,QAAQ,EAAmB,MAAM,0BAA0B,CAAA;AAKtF,MAAM,MAAM,IAAI,GAAG,OAAO,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAAA;AAE1D,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;AAE1D,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,IAAI,IAAI,CAAC,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAAC,KAAK,UAAU,GAAG,SAAS,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAAA;AAEpI,MAAM,MAAM,WAAW,GACnB,WAAW,IAAI,EAAE,GACjB,YAAY,GACZ,WAAW,GACX,eAAe,GACf,gBAAgB,GAChB,mBAAmB,GACnB,aAAa,GACb,iBAAiB,GACjB,cAAc,GACd,WAAW,GACX,UAAU,GACV,WAAW,GACX,UAAU,GACV,WAAW,GACX,gBAAgB,GAChB,iBAAiB,GACjB,iBAAiB,GACjB,aAAa,GACb,YAAY,CAAA;AAEhB,KAAK,SAAS,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAAA;AAExE,wBAAgB,WAAW,CAAC,CAAC,SAAS,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,GAAG,UAAU,CAAC,WAAW,CAAC,CAYxI;AAED,wBAAgB,cAAc,CAAC,CAAC,SAAS,IAAI,EAAE,MAAM,EAAE,CAAC,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,KAAK,UAAU,GAAG,SAAS,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,GAAG,aAAa,CAAC,WAAW,CAAC,CAM1L;AAMD,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAAC,cAAc,CAAC,CAexE;AAED,MAAM,MAAM,WAAW,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAA;AAExG,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAgB3I"}
|
package/runtime/hooks.js
CHANGED
|
@@ -17,6 +17,13 @@ export function classicHook(event, hook, report) {
|
|
|
17
17
|
};
|
|
18
18
|
return routed;
|
|
19
19
|
}
|
|
20
|
+
export function classicFailure(failed) {
|
|
21
|
+
const routed = async (e, error, next) => {
|
|
22
|
+
const ours = await failed(e, error);
|
|
23
|
+
return ours === undefined ? undefined : mergeClassic((await next(e)), ours);
|
|
24
|
+
};
|
|
25
|
+
return routed;
|
|
26
|
+
}
|
|
20
27
|
const frontmatter = /^---\r?\n[\s\S]*?\r?\n---[ \t]*(?:\r?\n|$)/;
|
|
21
28
|
const baseDirectoryLine = /^Base directory for this skill: [^\n]*\n\n/;
|
|
22
29
|
export function userSkillHook(claude) {
|
package/runtime/lifecycle.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Args, EventResult, Frozen, PluginOptions } from 'claude-code';
|
|
1
|
+
import type { Args, EventResult, Frozen, HookFailure, PluginOptions } from 'claude-code';
|
|
2
2
|
import type { Mod, ModDefinition } from '../mod.js';
|
|
3
3
|
import type { Options } from '../options.js';
|
|
4
4
|
import { type Steps } from '../records.js';
|
|
@@ -25,6 +25,7 @@ export type Lifecycle<State extends object> = {
|
|
|
25
25
|
readonly toasts: readonly ShownToast[];
|
|
26
26
|
start(claude: Claude, read: (claude: Claude) => Promise<Plugin>): Promise<void>;
|
|
27
27
|
route<N extends RoutedEvent>(event: N, e: Frozen<Args<N>>, next: RouterNext<N>): Promise<EventResult<N>>;
|
|
28
|
+
caught<N extends RoutedEvent>(event: N, e: Frozen<Args<N>>, next: RouterNext<N>, failure: HookFailure): Promise<EventResult<N>>;
|
|
28
29
|
raise(method: string, payload: Readonly<Record<string, unknown>>): Promise<unknown>;
|
|
29
30
|
};
|
|
30
31
|
export declare function readPlugin(claude: Claude): Promise<Plugin>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../src/runtime/lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,
|
|
1
|
+
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../src/runtime/lifecycle.ts"],"names":[],"mappings":"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"}
|
package/runtime/lifecycle.js
CHANGED
|
@@ -7,7 +7,7 @@ import { bind } from './bound.js';
|
|
|
7
7
|
import { serveConversations } from './conversations.js';
|
|
8
8
|
import { answerCall, answeringOwnCalls, cmodPluginName, dependencyCalls, withBareRefusals } from './dependencies.js';
|
|
9
9
|
import { keepEngine } from './engine.js';
|
|
10
|
-
import { classicHook, userSkillHook } from './hooks.js';
|
|
10
|
+
import { classicFailure, classicHook, userSkillHook } from './hooks.js';
|
|
11
11
|
import { checkedAnswer, checkingGrants, checkSettings, checksAnswers, checkWrite, isCovered, itemOf, passedDown, programAddresses } from './grants.js';
|
|
12
12
|
import { cmodError, lastLineOf, locatingPrograms, readLines } from './programs.js';
|
|
13
13
|
import { createModFiles, resolvedPath } from './metadata.js';
|
|
@@ -29,7 +29,8 @@ const installRetrySeconds = [2, 4, 8, 16, 32, 64, 128];
|
|
|
29
29
|
const cmodCheckMs = 1000;
|
|
30
30
|
const cmodWaitMs = 60_000;
|
|
31
31
|
const cmodSlowCheckMs = 10_000;
|
|
32
|
-
const
|
|
32
|
+
const hookMs = 10_000;
|
|
33
|
+
const startHoldMs = hookMs - 1_000;
|
|
33
34
|
const dependencyCallMs = 30_000;
|
|
34
35
|
const cannotStart = /failed to start: /;
|
|
35
36
|
const shellTools = ['Bash', 'PowerShell'];
|
|
@@ -582,6 +583,17 @@ export function createLifecycle(definition, permissionRoutes = () => undefined,
|
|
|
582
583
|
return answer;
|
|
583
584
|
return runtime.toaster.draw((await answer), runtime.claude.ui.resolve(render), render.props, render.requestId);
|
|
584
585
|
},
|
|
586
|
+
async caught(event, e, next, { kind, message }) {
|
|
587
|
+
const call = e;
|
|
588
|
+
const isAsk = event === 'cmod.call' && call.to === definition.name && beforeAskOf(call) !== undefined;
|
|
589
|
+
const reasons = { timeout: `it ran past the ${hookMs / 1000} s Claude Code gives a hook`, throw: message ?? 'it threw' };
|
|
590
|
+
const reason = reasons[kind];
|
|
591
|
+
if (reason === undefined)
|
|
592
|
+
return isAsk ? { value: null } : next(e);
|
|
593
|
+
const error = new Error(reason);
|
|
594
|
+
toastFailure({ id: `stopped:${event}`, message: `Claude Code stopped its ${event.replace(/^classic\./, '')} hook`, error });
|
|
595
|
+
return router.dispatchFailure(event, e, error, next);
|
|
596
|
+
},
|
|
585
597
|
};
|
|
586
598
|
}
|
|
587
599
|
async function createMod(definition, runtime) {
|
|
@@ -661,7 +673,7 @@ async function createMod(definition, runtime) {
|
|
|
661
673
|
}));
|
|
662
674
|
const modState = createState({ name: definition.name, initial: definition.state ?? {}, session, root, claude, report, changed: area.changed, forkParent });
|
|
663
675
|
const modOptions = createOptions({ name: definition.name, declared: definition.options ?? {}, fromClaude: runtime.options, claude, report, changed: modState.changed });
|
|
664
|
-
const hookOn = (hook, listener) => router.add(`classic.${hook}`, classicHook(hook, listener, report));
|
|
676
|
+
const hookOn = (hook, listener, failed) => router.add(`classic.${hook}`, classicHook(hook, listener, report), failed === undefined ? undefined : classicFailure(failed));
|
|
665
677
|
const expand = (path) => (homePrefix.test(path) ? resolvedPath(path, places) : path);
|
|
666
678
|
const settingsFile = (source) => ({
|
|
667
679
|
read: () => checked.settings.read({ source }),
|
|
@@ -675,8 +687,8 @@ async function createMod(definition, runtime) {
|
|
|
675
687
|
claude: checked,
|
|
676
688
|
report: (id, message, error) => report({ id, message, error }),
|
|
677
689
|
isGranted: (permission) => flag(permission),
|
|
678
|
-
hookOn: (hook, listener) => hookOn(hook, listener),
|
|
679
|
-
routeOn: (event, hook) => router.add(event, hook),
|
|
690
|
+
hookOn: (hook, listener, failed) => hookOn(hook, listener, failed),
|
|
691
|
+
routeOn: (event, hook, failed) => router.add(event, hook, failed),
|
|
680
692
|
permissionRoutes: runtime.permissionRoutes,
|
|
681
693
|
});
|
|
682
694
|
let followed;
|
|
@@ -918,10 +930,9 @@ async function createMod(definition, runtime) {
|
|
|
918
930
|
client.changed(changedSession, change, isInProject !== false);
|
|
919
931
|
return { value: null };
|
|
920
932
|
}
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
return { value: (await client.answerBefore(
|
|
924
|
-
}
|
|
933
|
+
const ask = beforeAskOf(call);
|
|
934
|
+
if (ask !== undefined)
|
|
935
|
+
return { value: (await client.answerBefore(ask.session, ask.event, ask.change, ask.isInProject)) ?? null };
|
|
925
936
|
if (e.method === settingsPagesMethod)
|
|
926
937
|
return { value: [...pages].map(([id, { title }]) => ({ id, title })) };
|
|
927
938
|
if (e.method === openPageMethod) {
|
|
@@ -930,6 +941,11 @@ async function createMod(definition, runtime) {
|
|
|
930
941
|
}
|
|
931
942
|
const served = await conversations?.answer(call);
|
|
932
943
|
return served ?? answerCall(definition.name, api, e);
|
|
944
|
+
}, (e, error) => {
|
|
945
|
+
if (e.to !== definition.name)
|
|
946
|
+
return undefined;
|
|
947
|
+
const ask = beforeAskOf(e);
|
|
948
|
+
return ask === undefined ? { deny: `${definition.name}: ${error.message}` } : { value: client.failedBefore(ask.session, ask.event, ask.change, ask.isInProject, error) ?? null };
|
|
933
949
|
});
|
|
934
950
|
const agents = toolCalls(claude, router);
|
|
935
951
|
keepEngine(mod, { claude: checked, router, toolCalls: agents, report, hasPermissionCheck: () => runtime.permissionRoutes() !== undefined });
|
|
@@ -972,6 +988,12 @@ function checkedMs(name, subject, ms) {
|
|
|
972
988
|
throw new Error(`${name}: ${subject} is ${ms}. Give a whole number of milliseconds above 0 and at most ${longestTimerMs}.`);
|
|
973
989
|
return ms;
|
|
974
990
|
}
|
|
991
|
+
function beforeAskOf(call) {
|
|
992
|
+
if (call.method !== conversationMethods.before || call.caller !== cmodPluginName)
|
|
993
|
+
return undefined;
|
|
994
|
+
const { session, event, change, isInProject } = call.input;
|
|
995
|
+
return { session, event, change, isInProject: isInProject !== false };
|
|
996
|
+
}
|
|
975
997
|
async function failsAs(subject, step) {
|
|
976
998
|
try {
|
|
977
999
|
await step();
|
|
@@ -3,6 +3,9 @@ import type { HookFields } from './hooks.js';
|
|
|
3
3
|
import { type HookOn, type RouteOn } from './sessions.js';
|
|
4
4
|
export type PermissionUse = {
|
|
5
5
|
readonly raise: (method: string, payload: HookFields) => Promise<HookFields | undefined>;
|
|
6
|
+
readonly refusalFor: (method: string, payload: HookFields, error: Error) => {
|
|
7
|
+
readonly refuse: string;
|
|
8
|
+
} | undefined;
|
|
6
9
|
readonly hookOn: HookOn;
|
|
7
10
|
readonly routeOn: RouteOn;
|
|
8
11
|
readonly current: () => {
|
|
@@ -1 +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,
|
|
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,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,KAAK;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAA;IACnH,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,gBAkD9B,CAAA"}
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import { defined, processPayloadOf, recordOf } from './sessions.js';
|
|
2
2
|
const verdictName = (decision) => (decision === 'deny' ? 'refuse' : decision);
|
|
3
|
-
export const permissionRoutes = (method, { raise, hookOn, routeOn, current }) => {
|
|
3
|
+
export const permissionRoutes = (method, { raise, refusalFor, hookOn, routeOn, current }) => {
|
|
4
4
|
const isBefore = method.startsWith('before');
|
|
5
5
|
if (method.endsWith('PermissionCheck')) {
|
|
6
|
+
const payloadOf = (e, below) => {
|
|
7
|
+
const { id, cwd } = current();
|
|
8
|
+
return { 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) };
|
|
9
|
+
};
|
|
10
|
+
const failed = async (e, error, next) => {
|
|
11
|
+
const refusal = refusalFor(method, payloadOf(e, (await next(e))), error);
|
|
12
|
+
return refusal === undefined ? undefined : { decision: 'deny', reason: refusal.refuse };
|
|
13
|
+
};
|
|
6
14
|
return routeOn('tool.check', async (e, next) => {
|
|
7
15
|
const below = (await next(e));
|
|
8
|
-
const
|
|
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) };
|
|
16
|
+
const payload = payloadOf(e, below);
|
|
10
17
|
if (!isBefore) {
|
|
11
18
|
void raise(method, payload);
|
|
12
19
|
return below;
|
|
@@ -19,8 +26,9 @@ export const permissionRoutes = (method, { raise, hookOn, routeOn, current }) =>
|
|
|
19
26
|
if (typeof answer['ask'] === 'string')
|
|
20
27
|
return { decision: 'ask', reason: answer['ask'] };
|
|
21
28
|
return answer['allow'] === true ? { decision: 'allow' } : below;
|
|
22
|
-
});
|
|
29
|
+
}, isBefore ? failed : undefined);
|
|
23
30
|
}
|
|
31
|
+
const refused = (refuse, interrupt) => ({ decision: defined({ behavior: 'deny', message: refuse, interrupt }) });
|
|
24
32
|
return hookOn('PermissionRequest', async (input) => {
|
|
25
33
|
const payload = processPayloadOf('PermissionRequest', input);
|
|
26
34
|
if (!isBefore) {
|
|
@@ -31,7 +39,12 @@ export const permissionRoutes = (method, { raise, hookOn, routeOn, current }) =>
|
|
|
31
39
|
if (answer === undefined)
|
|
32
40
|
return undefined;
|
|
33
41
|
if (typeof answer['refuse'] === 'string')
|
|
34
|
-
return
|
|
42
|
+
return refused(answer['refuse'], answer['interrupt']);
|
|
35
43
|
return answer['allow'] === true ? { decision: defined({ behavior: 'allow', updatedInput: answer['input'], updatedPermissions: answer['updates'] }) } : undefined;
|
|
36
|
-
}
|
|
44
|
+
}, isBefore
|
|
45
|
+
? (input, error) => {
|
|
46
|
+
const refusal = refusalFor(method, processPayloadOf('PermissionRequest', input), error);
|
|
47
|
+
return refusal === undefined ? undefined : refused(refusal.refuse);
|
|
48
|
+
}
|
|
49
|
+
: undefined);
|
|
37
50
|
};
|
package/runtime/router.d.ts
CHANGED
|
@@ -3,9 +3,11 @@ import type { Disposable } from '../mod.js';
|
|
|
3
3
|
import type { RoutedEvent } from './hooks.js';
|
|
4
4
|
export type RouterNext<N extends RoutedEvent> = (e: Frozen<Args<N>>) => Promise<EventResult<N>>;
|
|
5
5
|
export type RoutedHook<N extends RoutedEvent> = (e: Frozen<Args<N>>, next: RouterNext<N>) => EventResult<N> | Promise<EventResult<N>>;
|
|
6
|
+
export type RoutedFailure<N extends RoutedEvent> = (e: Frozen<Args<N>>, error: Error, next: RouterNext<N>) => EventResult<N> | undefined | Promise<EventResult<N> | undefined>;
|
|
6
7
|
export type Router = {
|
|
7
|
-
add<N extends RoutedEvent>(event: N, hook: RoutedHook<N>): Disposable;
|
|
8
|
+
add<N extends RoutedEvent>(event: N, hook: RoutedHook<N>, failed?: RoutedFailure<N>): Disposable;
|
|
8
9
|
dispatch<N extends RoutedEvent>(event: N, e: Frozen<Args<N>>, next: RouterNext<N>): Promise<EventResult<N>>;
|
|
10
|
+
dispatchFailure<N extends RoutedEvent>(event: N, e: Frozen<Args<N>>, error: Error, next: RouterNext<N>): Promise<EventResult<N>>;
|
|
9
11
|
};
|
|
10
12
|
export declare function createRouter(): Router;
|
|
11
13
|
//# sourceMappingURL=router.d.ts.map
|
package/runtime/router.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/runtime/router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAE7C,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,WAAW,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAA;AAE/F,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,WAAW,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,WAAW,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAA;AAErI,MAAM,MAAM,MAAM,GAAG;IACnB,GAAG,CAAC,CAAC,SAAS,WAAW,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/runtime/router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAE7C,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,WAAW,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAA;AAE/F,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,WAAW,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,WAAW,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAA;AAErI,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,WAAW,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,WAAW,CAAC,CAAC,CAAC,GAAG,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAA;AAE9K,MAAM,MAAM,MAAM,GAAG;IACnB,GAAG,CAAC,CAAC,SAAS,WAAW,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,UAAU,CAAA;IAChG,QAAQ,CAAC,CAAC,SAAS,WAAW,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAA;IAC3G,eAAe,CAAC,CAAC,SAAS,WAAW,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAA;CACjI,CAAA;AAID,wBAAgB,YAAY,IAAI,MAAM,CAgCrC"}
|
package/runtime/router.js
CHANGED
|
@@ -1,28 +1,36 @@
|
|
|
1
1
|
export function createRouter() {
|
|
2
|
-
const
|
|
2
|
+
const routes = new Map();
|
|
3
3
|
return {
|
|
4
|
-
add(event, hook) {
|
|
5
|
-
const
|
|
6
|
-
|
|
4
|
+
add(event, hook, failed) {
|
|
5
|
+
const route = { hook, ...(failed === undefined ? {} : { failed }) };
|
|
6
|
+
routes.set(event, [...(routes.get(event) ?? []), route]);
|
|
7
7
|
return {
|
|
8
8
|
dispose() {
|
|
9
|
-
const chain = (
|
|
9
|
+
const chain = (routes.get(event) ?? []).filter((each) => each !== route);
|
|
10
10
|
if (chain.length === 0)
|
|
11
|
-
|
|
11
|
+
routes.delete(event);
|
|
12
12
|
else
|
|
13
|
-
|
|
13
|
+
routes.set(event, chain);
|
|
14
14
|
},
|
|
15
15
|
};
|
|
16
16
|
},
|
|
17
17
|
dispatch(event, e, next) {
|
|
18
|
-
const chain =
|
|
18
|
+
const chain = routes.get(event);
|
|
19
19
|
const call = async (index, current) => {
|
|
20
|
-
const
|
|
21
|
-
if (
|
|
20
|
+
const route = chain?.[index];
|
|
21
|
+
if (route === undefined)
|
|
22
22
|
return next(current);
|
|
23
|
-
return hook(current, (passed) => call(index + 1, passed));
|
|
23
|
+
return route.hook(current, (passed) => call(index + 1, passed));
|
|
24
24
|
};
|
|
25
25
|
return call(0, e);
|
|
26
26
|
},
|
|
27
|
+
async dispatchFailure(event, e, error, next) {
|
|
28
|
+
for (const route of routes.get(event) ?? []) {
|
|
29
|
+
const answer = await route.failed?.(e, error, next);
|
|
30
|
+
if (answer !== undefined)
|
|
31
|
+
return answer;
|
|
32
|
+
}
|
|
33
|
+
return next(e);
|
|
34
|
+
},
|
|
27
35
|
};
|
|
28
36
|
}
|
package/runtime/sessions.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Args, CommandRunResult } from 'claude-code';
|
|
1
|
+
import type { Args, CommandRunResult, SessionCompactResult } 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';
|
|
@@ -11,6 +11,8 @@ import type { PermissionRoutes } from './permission-check.js';
|
|
|
11
11
|
export type Subscription = {
|
|
12
12
|
readonly event: SessionEvent;
|
|
13
13
|
readonly matcher: SentMatcher | undefined;
|
|
14
|
+
readonly onFailure?: 'refuse';
|
|
15
|
+
readonly scope?: Scope;
|
|
14
16
|
};
|
|
15
17
|
export declare const everySession = "*";
|
|
16
18
|
export declare const conversationMethods: {
|
|
@@ -46,6 +48,9 @@ export type SessionsClient = {
|
|
|
46
48
|
readonly sessions: Sessions;
|
|
47
49
|
changed(session: string, change: SessionChange, isInProject: boolean): void;
|
|
48
50
|
answerBefore(session: string, event: BeforeEvent, change: SessionChange, isInProject: boolean): Promise<unknown>;
|
|
51
|
+
failedBefore(session: string, event: BeforeEvent, change: SessionChange, isInProject: boolean, error: Error): {
|
|
52
|
+
readonly refuse: string;
|
|
53
|
+
} | undefined;
|
|
49
54
|
raise(method: string, payload: Readonly<Record<string, unknown>>): Promise<unknown>;
|
|
50
55
|
listen(method: string, args: readonly unknown[]): Disposable;
|
|
51
56
|
/** An empty title keeps the title the session already has. */
|
|
@@ -56,7 +61,7 @@ export declare function payloadOf(change: SessionChange): unknown;
|
|
|
56
61
|
export declare function isHeardBy(event: SessionEvent, matcher: unknown, change: SessionChange): boolean;
|
|
57
62
|
export declare function isInScope(scope: Scope, session: string, change: SessionChange, isUnder: (task: string, ancestor: string) => boolean): boolean;
|
|
58
63
|
export declare function withoutPermissionCheck(name: string, hearer: string): string;
|
|
59
|
-
export type HookOn = (hook: string, listener: (input: unknown) => HookFields | undefined | Promise<HookFields | undefined
|
|
64
|
+
export type HookOn = (hook: string, listener: (input: unknown) => HookFields | undefined | Promise<HookFields | undefined>, failed?: (input: unknown, error: Error) => HookFields | undefined) => Disposable;
|
|
60
65
|
export declare function createSessionsClient({ name, claude, report, isGranted, hookOn, routeOn, permissionRoutes }: {
|
|
61
66
|
readonly name: string;
|
|
62
67
|
readonly claude: Claude;
|
|
@@ -74,8 +79,12 @@ export type ToolCheck = {
|
|
|
74
79
|
readonly ceiling?: 'allow' | 'ask' | 'deny';
|
|
75
80
|
};
|
|
76
81
|
export type RouteOn = {
|
|
77
|
-
(event: 'tool.check', hook: (e: ToolCheck, next: (e: ToolCheck) => Promise<unknown>) => Promise<unknown>)
|
|
78
|
-
|
|
82
|
+
(event: 'tool.check', hook: (e: ToolCheck, next: (e: ToolCheck) => Promise<unknown>) => Promise<unknown>, failed?: (e: ToolCheck, error: Error, next: (e: ToolCheck) => Promise<unknown>) => Promise<{
|
|
83
|
+
readonly decision: 'deny';
|
|
84
|
+
readonly reason: string;
|
|
85
|
+
} | undefined>): Disposable;
|
|
86
|
+
(event: 'command.run', hook: (e: Args<'command.run'>, next: (e: Args<'command.run'>) => Promise<CommandRunResult>) => Promise<CommandRunResult>, failed?: (e: Args<'command.run'>, error: Error) => Promise<CommandRunResult | undefined>): Disposable;
|
|
87
|
+
(event: 'session.compact', hook: (e: Args<'session.compact'>, next: (e: Args<'session.compact'>) => Promise<SessionCompactResult>) => Promise<SessionCompactResult>): Disposable;
|
|
79
88
|
};
|
|
80
89
|
export declare const recordOf: (value: unknown) => Readonly<Record<string, unknown>>;
|
|
81
90
|
export declare const defined: (fields: HookFields) => HookFields;
|
|
@@ -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;
|
|
1
|
+
{"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../src/runtime/sessions.ts"],"names":[],"mappings":"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"}
|
package/runtime/sessions.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ContextEnd, ContextStart } from '../session/context/context.js';
|
|
2
2
|
import { Message, noFiles } from '../session/context/turn/message/message.js';
|
|
3
|
-
import { Note } from '../session/context/turn/message/note.js';
|
|
3
|
+
import { modNote, Note } from '../session/context/turn/message/note.js';
|
|
4
4
|
import { Person, senderOf } from '../session/context/turn/message/sender.js';
|
|
5
5
|
import { ToolCall } from '../session/context/turn/message/tool-call.js';
|
|
6
6
|
import { ToolResult } from '../session/context/turn/message/tool-result.js';
|
|
@@ -130,7 +130,7 @@ export function createSessionsClient({ name, claude, report, isGranted, hookOn,
|
|
|
130
130
|
queued.add(key);
|
|
131
131
|
void Promise.resolve().then(async () => {
|
|
132
132
|
queued.delete(key);
|
|
133
|
-
const heard = (from) => [...(from.get(key) ?? [])].map(({ event, matcher }) => ({ event, matcher: matcher === undefined ? undefined : sentMatcher(matcher) }));
|
|
133
|
+
const heard = (from) => [...(from.get(key) ?? [])].map(({ event, matcher, options, scope }) => ({ event, matcher: matcher === undefined ? undefined : sentMatcher(matcher), ...(options.onFailure === 'refuse' ? { onFailure: 'refuse', scope } : {}) }));
|
|
134
134
|
const tracksTasks = [...(listening.get(key) ?? []), ...(deciding.get(key) ?? [])].some(({ scope }) => scope.task !== undefined || scope.isGoal === true);
|
|
135
135
|
const subscriptions = [...heard(listening), ...(tracksTasks ? [{ event: TaskStart }] : [])];
|
|
136
136
|
await call(conversationMethods.subscribe, { session: key, subscriptions }).catch((error) => report(`sessions:${key}`, `Hears nothing from ${key === everySession ? 'the other sessions' : `session ${key}`}`, error));
|
|
@@ -178,19 +178,20 @@ export function createSessionsClient({ name, claude, report, isGranted, hookOn,
|
|
|
178
178
|
},
|
|
179
179
|
};
|
|
180
180
|
};
|
|
181
|
+
const refusalOf = (options, takes, error) => (options.onFailure === 'refuse' && takes.includes('refuse') ? { refuse: `${name}: ${messageOf(error)}` } : undefined);
|
|
181
182
|
const invoke = (method, options, run, takes = []) => withinTime(claude.clock, method, options, run).catch((error) => {
|
|
182
183
|
if (error instanceof PassedTimeout && !method.startsWith('before'))
|
|
183
184
|
return undefined;
|
|
184
185
|
report(`session:${method}`, `The ${method} listener failed`, error);
|
|
185
|
-
return options
|
|
186
|
+
return refusalOf(options, takes, error);
|
|
186
187
|
});
|
|
188
|
+
const decidersOf = (session, event, isInProject) => [...(deciding.get(session) ?? []), ...(isInProject ? (deciding.get(everySession) ?? []) : [])].filter((entry) => entry.event === event);
|
|
189
|
+
const decides = (entry, session, change) => isInScope(entry.scope, session, change, isUnderIn(session)) && (entry.matcher === undefined || matches(payloadOf(change), entry.matcher));
|
|
190
|
+
const mayDecide = (session) => session === current.id || isGranted('conversation');
|
|
187
191
|
const combined = async (session, event, change, isInProject) => {
|
|
188
|
-
const askers =
|
|
189
|
-
.filter((entry) => entry.event === event)
|
|
192
|
+
const askers = decidersOf(session, event, isInProject)
|
|
190
193
|
.map((entry) => async (latest) => {
|
|
191
|
-
if (!
|
|
192
|
-
return undefined;
|
|
193
|
-
if (entry.matcher !== undefined && !matches(payloadOf(latest), entry.matcher))
|
|
194
|
+
if (!decides(entry, session, latest))
|
|
194
195
|
return undefined;
|
|
195
196
|
const payload = await payloadFor(session, latest);
|
|
196
197
|
const answer = await invoke(entry.method, entry.options, async (time) => takenAnswer(entry.method, beforeAnswerKeys[event], await entry.listener(payload, time)), beforeAnswerKeys[event]);
|
|
@@ -198,7 +199,8 @@ export function createSessionsClient({ name, claude, report, isGranted, hookOn,
|
|
|
198
199
|
return answer;
|
|
199
200
|
throw new Error(`Its ${entry.method} answer changes nothing until ${beforePermission(event)} is turned on for ${name} in /mods.`);
|
|
200
201
|
});
|
|
201
|
-
|
|
202
|
+
const answer = await foldBefore(event, change, askers, (error) => report(`session:${methodOf('before', event)}`, `The ${methodOf('before', event)} listener failed`, error));
|
|
203
|
+
return answer?.note === undefined ? answer : { ...answer, note: modNote(name, answer.note) };
|
|
202
204
|
};
|
|
203
205
|
const eventMethods = (scopeOf, hasBefore, ended) => {
|
|
204
206
|
const method = (into, named, event) => (...args) => {
|
|
@@ -306,7 +308,7 @@ export function createSessionsClient({ name, claude, report, isGranted, hookOn,
|
|
|
306
308
|
if (type === Note) {
|
|
307
309
|
if (as !== undefined)
|
|
308
310
|
throw new Error(`${name}: a Note is always from this mod, so it takes no as.`);
|
|
309
|
-
const added = await claude.session.append({ message: { type: 'user', content: [{ type: 'text', text }] } });
|
|
311
|
+
const added = await claude.session.append({ message: { type: 'user', content: [{ type: 'text', text: modNote(name, text) }] } });
|
|
310
312
|
if (added.deny !== undefined)
|
|
311
313
|
throw new Error(`${name}: Claude Code refused the note: ${added.deny}`);
|
|
312
314
|
return;
|
|
@@ -409,17 +411,19 @@ export function createSessionsClient({ name, claude, report, isGranted, hookOn,
|
|
|
409
411
|
}), { session, task: data.id });
|
|
410
412
|
};
|
|
411
413
|
const processListeners = new Map();
|
|
414
|
+
const processHearers = (method, payload) => [...(processListeners.get(method) ?? [])].filter(({ matcher, isEveryAgent }) => (payload['task'] === undefined || isEveryAgent) && (matcher === undefined || matches(payload, matcher)));
|
|
412
415
|
const raise = async (method, payload) => {
|
|
413
|
-
const heard =
|
|
416
|
+
const heard = processHearers(method, payload);
|
|
414
417
|
if (!method.startsWith('before')) {
|
|
415
418
|
await Promise.all(heard.map(({ listener, options }) => invoke(method, options, (time) => listener(payload, time))));
|
|
416
419
|
return undefined;
|
|
417
420
|
}
|
|
418
|
-
const
|
|
419
|
-
const
|
|
420
|
-
const askers = heard.map(({ listener, options }) => () => invoke(method, { ...options, ...failing }, async (time) => takenAnswer(method, takes, await listener(payload, time)), takes));
|
|
421
|
+
const takes = takesOf(method);
|
|
422
|
+
const askers = heard.map(({ listener, options }) => () => invoke(method, options, async (time) => takenAnswer(method, takes, await listener(payload, time)), takes));
|
|
421
423
|
return foldAnswers(method, takes, ['refuse'], undefined, askers, (error) => report(`session:${method}`, `The ${method} listener failed`, error));
|
|
422
424
|
};
|
|
425
|
+
const takesOf = (method) => answerKeys[method.slice('before'.length)] ?? [];
|
|
426
|
+
const refusalFor = (method, payload, error) => processHearers(method, payload).map((entry) => refusalOf(entry.options, takesOf(method), error)).find((answer) => answer !== undefined);
|
|
423
427
|
const commandRun = async (e) => {
|
|
424
428
|
const source = (await claude.command.list()).find((command) => command.name === e.command)?.source;
|
|
425
429
|
return { session: current.id, cwd: current.cwd, name: e.command, args: e.args, ...defined({ source }), from: senderOf(e.origin, false) };
|
|
@@ -442,7 +446,10 @@ export function createSessionsClient({ name, claude, report, isGranted, hookOn,
|
|
|
442
446
|
const args = typeof changes?.['args'] === 'string' ? changes['args'] : e.args;
|
|
443
447
|
const ran = await next(args === e.args ? e : { ...e, args });
|
|
444
448
|
void raise('onRun', { ...run, args });
|
|
445
|
-
return typeof changes?.['note'] === 'string' ? { ...ran, context: [...(ran.context ?? []), changes['note']] } : ran;
|
|
449
|
+
return typeof changes?.['note'] === 'string' ? { ...ran, context: [...(ran.context ?? []), modNote(name, changes['note'])] } : ran;
|
|
450
|
+
}, async (e, error) => {
|
|
451
|
+
const refusal = commandListeners.beforeRun.size === 0 ? undefined : refusalFor('beforeRun', await commandRun(e), error);
|
|
452
|
+
return refusal === undefined ? undefined : { text: refusal.refuse };
|
|
446
453
|
});
|
|
447
454
|
const listenTo = (method, named) => {
|
|
448
455
|
if (hookOn === undefined || routeOn === undefined)
|
|
@@ -452,23 +459,51 @@ export function createSessionsClient({ name, claude, report, isGranted, hookOn,
|
|
|
452
459
|
const routes = permissionRoutes?.();
|
|
453
460
|
if (routes === undefined)
|
|
454
461
|
throw new Error(withoutPermissionCheck(name, method));
|
|
455
|
-
return routes(method, { raise, hookOn, routeOn, current: () => current });
|
|
462
|
+
return routes(method, { raise, refusalFor, hookOn, routeOn, current: () => current });
|
|
456
463
|
}
|
|
457
464
|
const event = processEvents[named];
|
|
458
465
|
const hook = (isBefore ? event.before : undefined) ?? event.on;
|
|
466
|
+
if (method === 'beforeSessionStart')
|
|
467
|
+
routeOn('session.compact', compactionNote);
|
|
468
|
+
const failed = (input, error) => {
|
|
469
|
+
const refusal = refusalFor(method, processPayloadOf(named, input), error);
|
|
470
|
+
return refusal === undefined ? undefined : hookAnswerOf(name, named, refusal);
|
|
471
|
+
};
|
|
459
472
|
return hookOn(hook, async (input) => {
|
|
460
473
|
const payload = processPayloadOf(named, input);
|
|
474
|
+
if (named === 'SessionStart' && payload['source'] === 'compact') {
|
|
475
|
+
const started = isBefore ? compactionStarts.shift() : undefined;
|
|
476
|
+
return started === undefined ? undefined : hookAnswerOf(name, named, started);
|
|
477
|
+
}
|
|
461
478
|
if (!isBefore) {
|
|
462
|
-
|
|
463
|
-
void raise(method, payload);
|
|
479
|
+
void raise(method, payload);
|
|
464
480
|
return undefined;
|
|
465
481
|
}
|
|
466
482
|
const answer = await raise(method, payload);
|
|
467
|
-
return answer === undefined ? undefined : hookAnswerOf(named, answer);
|
|
468
|
-
});
|
|
483
|
+
return answer === undefined ? undefined : hookAnswerOf(name, named, answer);
|
|
484
|
+
}, isBefore ? failed : undefined);
|
|
469
485
|
};
|
|
470
|
-
const
|
|
471
|
-
|
|
486
|
+
const compactionStarts = [];
|
|
487
|
+
const compactionNote = async (e, next) => {
|
|
488
|
+
const compacted = await next(e);
|
|
489
|
+
if (e.trigger === 'precompute' || compacted.skip !== undefined)
|
|
490
|
+
return compacted;
|
|
491
|
+
const model = e.agentId === undefined ? await claude.session.model() : undefined;
|
|
492
|
+
const answer = await raise('beforeSessionStart', { session: current.id, cwd: current.cwd, ...defined({ task: e.agentId, model, title: current.title || undefined, contextTokenCount: compacted.tokensAfter }), source: 'compact' });
|
|
493
|
+
const { note, ...started } = answer ?? {};
|
|
494
|
+
if (Object.keys(started).length > 0)
|
|
495
|
+
compactionStarts.push(started);
|
|
496
|
+
if (typeof note !== 'string')
|
|
497
|
+
return compacted;
|
|
498
|
+
if (!isGranted('prompt')) {
|
|
499
|
+
report('session:beforeSessionStart', 'The beforeSessionStart listener failed', new Error(`${name}: its note adds text Claude reads, which needs the prompt permission.`));
|
|
500
|
+
return compacted;
|
|
501
|
+
}
|
|
502
|
+
return { ...compacted, messages: [...compacted.messages, { role: 'user', text: modNote(name, note), toolUses: [] }] };
|
|
503
|
+
};
|
|
504
|
+
const listen = (method, args, isEveryAgent = false) => {
|
|
505
|
+
const heard = listenerOf(name, method, args);
|
|
506
|
+
const entry = { ...heard, options: method === 'beforePermissionRequest' ? { ...heard.options, onFailure: 'refuse' } : heard.options, isEveryAgent };
|
|
472
507
|
const held = processListeners.get(method) ?? new Set();
|
|
473
508
|
if (!processListeners.has(method)) {
|
|
474
509
|
listenTo(method, method.replace(/^(on|before)/, ''));
|
|
@@ -478,9 +513,14 @@ export function createSessionsClient({ name, claude, report, isGranted, hookOn,
|
|
|
478
513
|
return { dispose: () => void held.delete(entry) };
|
|
479
514
|
};
|
|
480
515
|
const processMethods = () => Object.fromEntries(Object.entries(processEvents).flatMap(([named, event]) => [`on${named}`, ...('before' in event ? [`before${named}`] : [])]).map((method) => [method, (...args) => listen(method, args)]));
|
|
481
|
-
const
|
|
516
|
+
const here = sessionOf(() => current);
|
|
517
|
+
const currentSession = Object.assign(here, processMethods(), { goal: Object.assign(here.goal, { beforeSessionStart: (...args) => listen('beforeSessionStart', args, true) }) });
|
|
482
518
|
const every = eventMethods(() => ({}), false);
|
|
483
|
-
const answerBefore = async (session, event, change, isInProject) => (session
|
|
519
|
+
const answerBefore = async (session, event, change, isInProject) => (mayDecide(session) ? combined(session, event, change, isInProject) : undefined);
|
|
520
|
+
const failedBefore = (session, event, change, isInProject, error) => {
|
|
521
|
+
const asked = mayDecide(session) ? decidersOf(session, event, isInProject).filter((entry) => decides(entry, session, change)) : [];
|
|
522
|
+
return asked.map((entry) => refusalOf(entry.options, beforeAnswerKeys[event], error)).find((answer) => answer !== undefined);
|
|
523
|
+
};
|
|
484
524
|
return {
|
|
485
525
|
sessions: {
|
|
486
526
|
get current() {
|
|
@@ -513,6 +553,7 @@ export function createSessionsClient({ name, claude, report, isGranted, hookOn,
|
|
|
513
553
|
}
|
|
514
554
|
},
|
|
515
555
|
answerBefore,
|
|
556
|
+
failedBefore,
|
|
516
557
|
raise,
|
|
517
558
|
listen,
|
|
518
559
|
follow(next) {
|
|
@@ -622,9 +663,9 @@ export function processPayloadOf(named, input) {
|
|
|
622
663
|
const fields = recordOf(input);
|
|
623
664
|
return { ...placementOf(fields), ...defined(processFields[named](fields)) };
|
|
624
665
|
}
|
|
625
|
-
function hookAnswerOf(named, answer) {
|
|
666
|
+
function hookAnswerOf(name, named, answer) {
|
|
626
667
|
const refuse = typeof answer['refuse'] === 'string' ? answer['refuse'] : undefined;
|
|
627
|
-
const note = typeof answer['note'] === 'string' ? [answer['note']] : undefined;
|
|
668
|
+
const note = typeof answer['note'] === 'string' ? [modNote(name, answer['note'])] : undefined;
|
|
628
669
|
if (named === 'SessionStart')
|
|
629
670
|
return defined({ additionalContext: note, sessionTitle: answer['title'], reloadSkills: answer['reloadSkills'] === true ? true : undefined, initialUserMessage: answer['firstPrompt'] });
|
|
630
671
|
if (named === 'ModelChange') {
|
package/runtime/toaster.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toaster.d.ts","sourceRoot":"","sources":["../src/runtime/toaster.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAS,MAAM,aAAa,CAAA;AACpF,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAgB,SAAS,EAAE,MAAM,WAAW,CAAA;AAIxE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC,MAAM,MAAM,UAAU,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAE9G,MAAM,MAAM,OAAO,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,OAAO,CAAA;CAAE,CAAA;AAEhK,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;IACrB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACjC,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAA;IAC/B,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAA;IACrC,IAAI,CAAC,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,CAAC,aAAa,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,aAAa,CAAA;CACnH,CAAA;AAuCD,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,MAAM,GAAG,SAAS,GAAG,OAAO,
|
|
1
|
+
{"version":3,"file":"toaster.d.ts","sourceRoot":"","sources":["../src/runtime/toaster.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAS,MAAM,aAAa,CAAA;AACpF,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAgB,SAAS,EAAE,MAAM,WAAW,CAAA;AAIxE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC,MAAM,MAAM,UAAU,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAE9G,MAAM,MAAM,OAAO,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,OAAO,CAAA;CAAE,CAAA;AAEhK,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;IACrB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACjC,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAA;IAC/B,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAA;IACrC,IAAI,CAAC,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,CAAC,aAAa,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,aAAa,CAAA;CACnH,CAAA;AAuCD,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,MAAM,GAAG,SAAS,GAAG,OAAO,CA+PtG"}
|
package/runtime/toaster.js
CHANGED
|
@@ -133,6 +133,9 @@ export function createToaster(claude, name, version) {
|
|
|
133
133
|
if (hidden.get(id) === hides)
|
|
134
134
|
return id;
|
|
135
135
|
hidden.delete(id);
|
|
136
|
+
const shown = entries.get(id);
|
|
137
|
+
if (shown?.hides === hides && shown.trying === undefined && shown.options.description === description)
|
|
138
|
+
return id;
|
|
136
139
|
const stack = error instanceof Error && error.stack !== undefined ? `\n\n${error.stack}` : '';
|
|
137
140
|
const details = async () => {
|
|
138
141
|
const modVersion = await claude.fs
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message.d.ts","sourceRoot":"","sources":["../../../../src/session/context/turn/message/message.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAc,MAAM,uBAAuB,CAAA;AACnE,OAAO,
|
|
1
|
+
{"version":3,"file":"message.d.ts","sourceRoot":"","sources":["../../../../src/session/context/turn/message/message.ts"],"names":[],"mappings":"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"}
|