@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/src/runtime/sessions.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
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 { ContextEnd, ContextStart, type Context } from '../session/context/context.js'
|
|
4
4
|
import { Message, noFiles } from '../session/context/turn/message/message.js'
|
|
5
|
-
import { Note } from '../session/context/turn/message/note.js'
|
|
5
|
+
import { modNote, Note } from '../session/context/turn/message/note.js'
|
|
6
6
|
import { Person, senderOf } from '../session/context/turn/message/sender.js'
|
|
7
7
|
import { ToolCall } from '../session/context/turn/message/tool-call.js'
|
|
8
8
|
import { ToolResult } from '../session/context/turn/message/tool-result.js'
|
|
@@ -20,7 +20,7 @@ import { listenerOf, PassedTimeout, withinTime, type ListenOptions, type ListenT
|
|
|
20
20
|
import { callFiles, type HomeFolders, type HookFields } from './hooks.js'
|
|
21
21
|
import type { PermissionRoutes } from './permission-check.js'
|
|
22
22
|
|
|
23
|
-
export type Subscription = { readonly event: SessionEvent; readonly matcher: SentMatcher | undefined }
|
|
23
|
+
export type Subscription = { readonly event: SessionEvent; readonly matcher: SentMatcher | undefined; readonly onFailure?: 'refuse'; readonly scope?: Scope }
|
|
24
24
|
|
|
25
25
|
export const everySession = '*'
|
|
26
26
|
|
|
@@ -67,6 +67,7 @@ export type SessionsClient = {
|
|
|
67
67
|
readonly sessions: Sessions
|
|
68
68
|
changed(session: string, change: SessionChange, isInProject: boolean): void
|
|
69
69
|
answerBefore(session: string, event: BeforeEvent, change: SessionChange, isInProject: boolean): Promise<unknown>
|
|
70
|
+
failedBefore(session: string, event: BeforeEvent, change: SessionChange, isInProject: boolean, error: Error): { readonly refuse: string } | undefined
|
|
70
71
|
raise(method: string, payload: Readonly<Record<string, unknown>>): Promise<unknown>
|
|
71
72
|
listen(method: string, args: readonly unknown[]): Disposable
|
|
72
73
|
/** An empty title keeps the title the session already has. */
|
|
@@ -137,7 +138,7 @@ export function withoutPermissionCheck(name: string, hearer: string): string {
|
|
|
137
138
|
return `${name}: ${hearer} hears Claude Code's permission check, so hooks/register.ts must call registerPermissionCheck(addHook) after registerMod.`
|
|
138
139
|
}
|
|
139
140
|
|
|
140
|
-
export type HookOn = (hook: string, listener: (input: unknown) => HookFields | undefined | Promise<HookFields | undefined
|
|
141
|
+
export type HookOn = (hook: string, listener: (input: unknown) => HookFields | undefined | Promise<HookFields | undefined>, failed?: (input: unknown, error: Error) => HookFields | undefined) => Disposable
|
|
141
142
|
|
|
142
143
|
export function createSessionsClient({ name, claude, report, isGranted, hookOn, routeOn, permissionRoutes }: { readonly name: string; readonly claude: Claude; readonly report: (id: string, message: string, error: unknown) => void; readonly isGranted: (permission: string) => boolean; readonly hookOn?: HookOn; readonly routeOn?: RouteOn; readonly permissionRoutes?: () => PermissionRoutes | undefined }): SessionsClient {
|
|
143
144
|
const call = <Answer>(method: string, input: unknown) => claude.cmod.call({ to: cmodPluginName, method, input }) as Promise<Answer>
|
|
@@ -155,7 +156,8 @@ export function createSessionsClient({ name, claude, report, isGranted, hookOn,
|
|
|
155
156
|
queued.add(key)
|
|
156
157
|
void Promise.resolve().then(async () => {
|
|
157
158
|
queued.delete(key)
|
|
158
|
-
const heard = (from: Map<string, Set<Listening>>): Subscription[] =>
|
|
159
|
+
const heard = (from: Map<string, Set<Listening>>): Subscription[] =>
|
|
160
|
+
[...(from.get(key) ?? [])].map(({ event, matcher, options, scope }) => ({ event, matcher: matcher === undefined ? undefined : sentMatcher(matcher), ...(options.onFailure === 'refuse' ? { onFailure: 'refuse' as const, scope } : {}) }))
|
|
159
161
|
const tracksTasks = [...(listening.get(key) ?? []), ...(deciding.get(key) ?? [])].some(({ scope }) => scope.task !== undefined || scope.isGoal === true)
|
|
160
162
|
const subscriptions = [...heard(listening), ...(tracksTasks ? [{ event: TaskStart }] : [])]
|
|
161
163
|
await call(conversationMethods.subscribe, { session: key, subscriptions }).catch((error: unknown) => report(`sessions:${key}`, `Hears nothing from ${key === everySession ? 'the other sessions' : `session ${key}`}`, error))
|
|
@@ -200,27 +202,32 @@ export function createSessionsClient({ name, claude, report, isGranted, hookOn,
|
|
|
200
202
|
}
|
|
201
203
|
}
|
|
202
204
|
|
|
203
|
-
|
|
205
|
+
const refusalOf = (options: ListenOptions, takes: readonly string[], error: unknown) => (options.onFailure === 'refuse' && takes.includes('refuse') ? { refuse: `${name}: ${messageOf(error)}` } : undefined)
|
|
204
206
|
|
|
205
207
|
const invoke = (method: string, options: ListenOptions, run: (time: ListenTime) => unknown, takes: readonly string[] = []): Promise<unknown> =>
|
|
206
208
|
withinTime(claude.clock, method, options, run).catch((error: unknown) => {
|
|
207
209
|
if (error instanceof PassedTimeout && !method.startsWith('before')) return undefined
|
|
208
210
|
report(`session:${method}`, `The ${method} listener failed`, error)
|
|
209
|
-
return options
|
|
211
|
+
return refusalOf(options, takes, error)
|
|
210
212
|
})
|
|
211
213
|
|
|
214
|
+
const decidersOf = (session: string, event: BeforeEvent, isInProject: boolean) => [...(deciding.get(session) ?? []), ...(isInProject ? (deciding.get(everySession) ?? []) : [])].filter((entry) => entry.event === event)
|
|
215
|
+
|
|
216
|
+
const decides = (entry: Listening, session: string, change: SessionChange) => isInScope(entry.scope, session, change, isUnderIn(session)) && (entry.matcher === undefined || matches(payloadOf(change), entry.matcher))
|
|
217
|
+
|
|
218
|
+
const mayDecide = (session: string) => session === current.id || isGranted('conversation')
|
|
219
|
+
|
|
212
220
|
const combined = async (session: string, event: BeforeEvent, change: SessionChange, isInProject: boolean): Promise<Partial<Answers> | undefined> => {
|
|
213
|
-
const askers =
|
|
214
|
-
.filter((entry) => entry.event === event)
|
|
221
|
+
const askers = decidersOf(session, event, isInProject)
|
|
215
222
|
.map((entry) => async (latest: SessionChange) => {
|
|
216
|
-
if (!
|
|
217
|
-
if (entry.matcher !== undefined && !matches(payloadOf(latest), entry.matcher)) return undefined
|
|
223
|
+
if (!decides(entry, session, latest)) return undefined
|
|
218
224
|
const payload = await payloadFor(session, latest)
|
|
219
225
|
const answer = await invoke(entry.method, entry.options, async (time) => takenAnswer(entry.method, beforeAnswerKeys[event], await entry.listener(payload as never, time)), beforeAnswerKeys[event])
|
|
220
226
|
if (answer === undefined || answer === null || (typeof answer === 'object' && 'refuse' in answer) || isGranted(beforePermission(event))) return answer
|
|
221
227
|
throw new Error(`Its ${entry.method} answer changes nothing until ${beforePermission(event)} is turned on for ${name} in /mods.`)
|
|
222
228
|
})
|
|
223
|
-
|
|
229
|
+
const answer = await foldBefore(event, change, askers, (error) => report(`session:${methodOf('before', event)}`, `The ${methodOf('before', event)} listener failed`, error))
|
|
230
|
+
return answer?.note === undefined ? answer : { ...answer, note: modNote(name, answer.note) }
|
|
224
231
|
}
|
|
225
232
|
|
|
226
233
|
const eventMethods = (scopeOf: () => Scope, hasBefore: boolean, ended?: string): OnMethods & Partial<BeforeMethods> => {
|
|
@@ -329,7 +336,7 @@ export function createSessionsClient({ name, claude, report, isGranted, hookOn,
|
|
|
329
336
|
const sendHere = async ({ type, text, as }: SessionSend) => {
|
|
330
337
|
if (type === Note) {
|
|
331
338
|
if (as !== undefined) throw new Error(`${name}: a Note is always from this mod, so it takes no as.`)
|
|
332
|
-
const added = await claude.session.append({ message: { type: 'user', content: [{ type: 'text', text }] } })
|
|
339
|
+
const added = await claude.session.append({ message: { type: 'user', content: [{ type: 'text', text: modNote(name, text) }] } })
|
|
333
340
|
if (added.deny !== undefined) throw new Error(`${name}: Claude Code refused the note: ${added.deny}`)
|
|
334
341
|
return
|
|
335
342
|
}
|
|
@@ -432,21 +439,26 @@ export function createSessionsClient({ name, claude, report, isGranted, hookOn,
|
|
|
432
439
|
)
|
|
433
440
|
}
|
|
434
441
|
|
|
435
|
-
type ProcessListener = { readonly matcher: unknown; readonly listener: (payload: HookFields, time: ListenTime) => unknown; readonly options: ListenOptions }
|
|
442
|
+
type ProcessListener = { readonly matcher: unknown; readonly listener: (payload: HookFields, time: ListenTime) => unknown; readonly options: ListenOptions; readonly isEveryAgent: boolean }
|
|
436
443
|
const processListeners = new Map<string, Set<ProcessListener>>()
|
|
437
444
|
|
|
445
|
+
const processHearers = (method: string, payload: HookFields) => [...(processListeners.get(method) ?? [])].filter(({ matcher, isEveryAgent }) => (payload['task'] === undefined || isEveryAgent) && (matcher === undefined || matches(payload, matcher)))
|
|
446
|
+
|
|
438
447
|
const raise = async (method: string, payload: HookFields): Promise<HookFields | undefined> => {
|
|
439
|
-
const heard =
|
|
448
|
+
const heard = processHearers(method, payload)
|
|
440
449
|
if (!method.startsWith('before')) {
|
|
441
450
|
await Promise.all(heard.map(({ listener, options }) => invoke(method, options, (time) => listener(payload, time))))
|
|
442
451
|
return undefined
|
|
443
452
|
}
|
|
444
|
-
const
|
|
445
|
-
const
|
|
446
|
-
const askers = heard.map(({ listener, options }) => () => invoke(method, { ...options, ...failing }, async (time) => takenAnswer(method, takes, await listener(payload, time)), takes))
|
|
453
|
+
const takes = takesOf(method)
|
|
454
|
+
const askers = heard.map(({ listener, options }) => () => invoke(method, options, async (time) => takenAnswer(method, takes, await listener(payload, time)), takes))
|
|
447
455
|
return foldAnswers<undefined, HookFields>(method, takes, ['refuse'], undefined, askers, (error) => report(`session:${method}`, `The ${method} listener failed`, error))
|
|
448
456
|
}
|
|
449
457
|
|
|
458
|
+
const takesOf = (method: string) => answerKeys[method.slice('before'.length)] ?? []
|
|
459
|
+
|
|
460
|
+
const refusalFor = (method: string, payload: HookFields, error: Error) => processHearers(method, payload).map((entry) => refusalOf(entry.options, takesOf(method), error)).find((answer) => answer !== undefined)
|
|
461
|
+
|
|
450
462
|
const commandRun = async (e: Args<'command.run'>): Promise<HookFields> => {
|
|
451
463
|
const source = (await claude.command.list()).find((command) => command.name === e.command)?.source
|
|
452
464
|
return { session: current.id, cwd: current.cwd, name: e.command, args: e.args, ...defined({ source }), from: senderOf(e.origin, false) }
|
|
@@ -465,7 +477,10 @@ export function createSessionsClient({ name, claude, report, isGranted, hookOn,
|
|
|
465
477
|
const args = typeof changes?.['args'] === 'string' ? changes['args'] : e.args
|
|
466
478
|
const ran = await next(args === e.args ? e : { ...e, args })
|
|
467
479
|
void raise('onRun', { ...run, args })
|
|
468
|
-
return typeof changes?.['note'] === 'string' ? { ...ran, context: [...(ran.context ?? []), changes['note']] } : ran
|
|
480
|
+
return typeof changes?.['note'] === 'string' ? { ...ran, context: [...(ran.context ?? []), modNote(name, changes['note'])] } : ran
|
|
481
|
+
}, async (e, error) => {
|
|
482
|
+
const refusal = commandListeners.beforeRun.size === 0 ? undefined : refusalFor('beforeRun', await commandRun(e), error)
|
|
483
|
+
return refusal === undefined ? undefined : { text: refusal.refuse }
|
|
469
484
|
})
|
|
470
485
|
|
|
471
486
|
const listenTo = (method: string, named: string) => {
|
|
@@ -474,23 +489,54 @@ export function createSessionsClient({ name, claude, report, isGranted, hookOn,
|
|
|
474
489
|
if (named === 'PermissionCheck' || named === 'PermissionRequest') {
|
|
475
490
|
const routes = permissionRoutes?.()
|
|
476
491
|
if (routes === undefined) throw new Error(withoutPermissionCheck(name, method))
|
|
477
|
-
return routes(method, { raise, hookOn, routeOn, current: () => current })
|
|
492
|
+
return routes(method, { raise, refusalFor, hookOn, routeOn, current: () => current })
|
|
478
493
|
}
|
|
479
494
|
const event: { readonly on: string; readonly before?: string } = processEvents[named as ProcessName]
|
|
480
495
|
const hook = (isBefore ? event.before : undefined) ?? event.on
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
496
|
+
if (method === 'beforeSessionStart') routeOn('session.compact', compactionNote)
|
|
497
|
+
const failed = (input: unknown, error: Error) => {
|
|
498
|
+
const refusal = refusalFor(method, processPayloadOf(named as ProcessFieldsName, input), error)
|
|
499
|
+
return refusal === undefined ? undefined : hookAnswerOf(name, named, refusal)
|
|
500
|
+
}
|
|
501
|
+
return hookOn(
|
|
502
|
+
hook,
|
|
503
|
+
async (input: unknown) => {
|
|
504
|
+
const payload = processPayloadOf(named as ProcessFieldsName, input)
|
|
505
|
+
if (named === 'SessionStart' && payload['source'] === 'compact') {
|
|
506
|
+
const started = isBefore ? compactionStarts.shift() : undefined
|
|
507
|
+
return started === undefined ? undefined : hookAnswerOf(name, named, started)
|
|
508
|
+
}
|
|
509
|
+
if (!isBefore) {
|
|
510
|
+
void raise(method, payload)
|
|
511
|
+
return undefined
|
|
512
|
+
}
|
|
513
|
+
const answer = await raise(method, payload)
|
|
514
|
+
return answer === undefined ? undefined : hookAnswerOf(name, named, answer)
|
|
515
|
+
},
|
|
516
|
+
isBefore ? failed : undefined,
|
|
517
|
+
)
|
|
490
518
|
}
|
|
491
519
|
|
|
492
|
-
const
|
|
493
|
-
|
|
520
|
+
const compactionStarts: HookFields[] = []
|
|
521
|
+
|
|
522
|
+
const compactionNote = async (e: Args<'session.compact'>, next: (e: Args<'session.compact'>) => Promise<SessionCompactResult>): Promise<SessionCompactResult> => {
|
|
523
|
+
const compacted = await next(e)
|
|
524
|
+
if (e.trigger === 'precompute' || compacted.skip !== undefined) return compacted
|
|
525
|
+
const model = e.agentId === undefined ? await claude.session.model() : undefined
|
|
526
|
+
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' })
|
|
527
|
+
const { note, ...started } = answer ?? {}
|
|
528
|
+
if (Object.keys(started).length > 0) compactionStarts.push(started)
|
|
529
|
+
if (typeof note !== 'string') return compacted
|
|
530
|
+
if (!isGranted('prompt')) {
|
|
531
|
+
report('session:beforeSessionStart', 'The beforeSessionStart listener failed', new Error(`${name}: its note adds text Claude reads, which needs the prompt permission.`))
|
|
532
|
+
return compacted
|
|
533
|
+
}
|
|
534
|
+
return { ...compacted, messages: [...compacted.messages, { role: 'user', text: modNote(name, note), toolUses: [] }] }
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
const listen = (method: string, args: readonly unknown[], isEveryAgent = false): Disposable => {
|
|
538
|
+
const heard = listenerOf(name, method, args)
|
|
539
|
+
const entry = { ...heard, options: method === 'beforePermissionRequest' ? { ...heard.options, onFailure: 'refuse' } : heard.options, isEveryAgent } as ProcessListener
|
|
494
540
|
const held = processListeners.get(method) ?? new Set()
|
|
495
541
|
if (!processListeners.has(method)) {
|
|
496
542
|
listenTo(method, method.replace(/^(on|before)/, ''))
|
|
@@ -505,10 +551,16 @@ export function createSessionsClient({ name, claude, report, isGranted, hookOn,
|
|
|
505
551
|
Object.entries(processEvents).flatMap(([named, event]) => [`on${named}`, ...('before' in event ? [`before${named}`] : [])]).map((method) => [method, (...args: unknown[]) => listen(method, args)]),
|
|
506
552
|
) as ProcessOnMethods & ProcessBeforeMethods
|
|
507
553
|
|
|
508
|
-
const
|
|
554
|
+
const here = sessionOf(() => current)
|
|
555
|
+
const currentSession = Object.assign(here, processMethods(), { goal: Object.assign(here.goal, { beforeSessionStart: (...args: unknown[]) => listen('beforeSessionStart', args, true) }) })
|
|
509
556
|
const every = eventMethods(() => ({}), false)
|
|
510
557
|
|
|
511
|
-
const answerBefore = async (session: string, event: BeforeEvent, change: SessionChange, isInProject: boolean) => (session
|
|
558
|
+
const answerBefore = async (session: string, event: BeforeEvent, change: SessionChange, isInProject: boolean) => (mayDecide(session) ? combined(session, event, change, isInProject) : undefined)
|
|
559
|
+
|
|
560
|
+
const failedBefore = (session: string, event: BeforeEvent, change: SessionChange, isInProject: boolean, error: Error) => {
|
|
561
|
+
const asked = mayDecide(session) ? decidersOf(session, event, isInProject).filter((entry) => decides(entry, session, change)) : []
|
|
562
|
+
return asked.map((entry) => refusalOf(entry.options, beforeAnswerKeys[event], error)).find((answer) => answer !== undefined)
|
|
563
|
+
}
|
|
512
564
|
|
|
513
565
|
return {
|
|
514
566
|
sessions: {
|
|
@@ -541,6 +593,7 @@ export function createSessionsClient({ name, claude, report, isGranted, hookOn,
|
|
|
541
593
|
}
|
|
542
594
|
},
|
|
543
595
|
answerBefore,
|
|
596
|
+
failedBefore,
|
|
544
597
|
raise,
|
|
545
598
|
listen,
|
|
546
599
|
follow(next) {
|
|
@@ -565,8 +618,9 @@ export function createSessionsClient({ name, claude, report, isGranted, hookOn,
|
|
|
565
618
|
export type ToolCheck = { readonly tool: string; readonly input: unknown; readonly tool_use_id?: string; readonly agentId?: string; readonly ceiling?: 'allow' | 'ask' | 'deny' }
|
|
566
619
|
|
|
567
620
|
export type RouteOn = {
|
|
568
|
-
(event: 'tool.check', hook: (e: ToolCheck, next: (e: ToolCheck) => Promise<unknown>) => Promise<unknown>): Disposable
|
|
569
|
-
(event: 'command.run', hook: (e: Args<'command.run'>, next: (e: Args<'command.run'>) => Promise<CommandRunResult>) => Promise<CommandRunResult>): Disposable
|
|
621
|
+
(event: 'tool.check', hook: (e: ToolCheck, next: (e: ToolCheck) => Promise<unknown>) => Promise<unknown>, failed?: (e: ToolCheck, error: Error, next: (e: ToolCheck) => Promise<unknown>) => Promise<{ readonly decision: 'deny'; readonly reason: string } | undefined>): Disposable
|
|
622
|
+
(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
|
|
623
|
+
(event: 'session.compact', hook: (e: Args<'session.compact'>, next: (e: Args<'session.compact'>) => Promise<SessionCompactResult>) => Promise<SessionCompactResult>): Disposable
|
|
570
624
|
}
|
|
571
625
|
|
|
572
626
|
export const recordOf = (value: unknown): Readonly<Record<string, unknown>> => (typeof value === 'object' && value !== null && !Array.isArray(value) ? (value as Record<string, unknown>) : {})
|
|
@@ -663,9 +717,9 @@ export function processPayloadOf(named: ProcessFieldsName, input: unknown): Hook
|
|
|
663
717
|
return { ...placementOf(fields), ...defined(processFields[named](fields)) }
|
|
664
718
|
}
|
|
665
719
|
|
|
666
|
-
function hookAnswerOf(named: string, answer: HookFields): HookFields | undefined {
|
|
720
|
+
function hookAnswerOf(name: string, named: string, answer: HookFields): HookFields | undefined {
|
|
667
721
|
const refuse = typeof answer['refuse'] === 'string' ? answer['refuse'] : undefined
|
|
668
|
-
const note = typeof answer['note'] === 'string' ? [answer['note']] : undefined
|
|
722
|
+
const note = typeof answer['note'] === 'string' ? [modNote(name, answer['note'])] : undefined
|
|
669
723
|
if (named === 'SessionStart') return defined({ additionalContext: note, sessionTitle: answer['title'], reloadSkills: answer['reloadSkills'] === true ? true : undefined, initialUserMessage: answer['firstPrompt'] })
|
|
670
724
|
if (named === 'ModelChange') {
|
|
671
725
|
if (refuse !== undefined) return { permissionDecision: 'deny', permissionDecisionReason: refuse }
|
package/src/runtime/toaster.ts
CHANGED
|
@@ -180,6 +180,8 @@ export function createToaster(claude: Claude, name: string, version: () => strin
|
|
|
180
180
|
const hides = `${version() ?? ''}\n${message}\n${error === undefined ? '' : describe(error)}`
|
|
181
181
|
if (hidden.get(id) === hides) return id
|
|
182
182
|
hidden.delete(id)
|
|
183
|
+
const shown = entries.get(id)
|
|
184
|
+
if (shown?.hides === hides && shown.trying === undefined && shown.options.description === description) return id
|
|
183
185
|
const stack = error instanceof Error && error.stack !== undefined ? `\n\n${error.stack}` : ''
|
|
184
186
|
const details = async () => {
|
|
185
187
|
const modVersion = await claude.fs
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { isObject } from '../../../../records.js'
|
|
2
2
|
import { Task } from '../../../goal/task.js'
|
|
3
3
|
import { InstructionFile, isRulePath } from './instruction-file.js'
|
|
4
|
-
import { Note } from './note.js'
|
|
4
|
+
import { modNote, modNoteBlock, modNotesIn, Note } from './note.js'
|
|
5
5
|
import { Notice } from './notice.js'
|
|
6
6
|
import { Prompt } from './prompt.js'
|
|
7
7
|
import { Reply } from './reply.js'
|
|
8
|
-
import { senderOf } from './sender.js'
|
|
8
|
+
import { Mod, senderOf } from './sender.js'
|
|
9
9
|
import { Skill } from './skill.js'
|
|
10
10
|
import { ToolCall, type CallFiles } from './tool-call.js'
|
|
11
11
|
import { ToolResult } from './tool-result.js'
|
|
@@ -108,8 +108,11 @@ export function messagesOf(row: Row, isInTask: boolean): Unplaced[] {
|
|
|
108
108
|
function unplacedOf(row: Row, isInTask: boolean): Unplaced[] {
|
|
109
109
|
const base = { id: row.id, at: row.at }
|
|
110
110
|
const text = textOf(row.content).trim()
|
|
111
|
+
const notesFrom = (each: readonly { readonly mod: string; readonly text: string }[]): Unplaced[] => each.map((note) => ({ ...base, type: Note, text: note.text, from: { type: Mod, name: note.mod } }))
|
|
111
112
|
if (row.type === 'system') return row.name === 'turn_duration' || row.name === 'compact_boundary' || text === '' ? [] : [{ ...base, type: Notice, text }]
|
|
113
|
+
if (row.type === 'attachment' && row.name === 'hook_additional_context') return notesFrom(row.content.flatMap((entry) => modNotesIn(typeof entry === 'string' ? entry : textOf([entry]))))
|
|
112
114
|
if (row.type === 'attachment') return row.name === 'nested_memory' ? [{ ...base, type: InstructionFile, path: row.path ?? '', text, isRule: isRulePath(row.path ?? '') }] : []
|
|
115
|
+
if (isModNotesRow(row)) return notesFrom(modNotesIn(text))
|
|
113
116
|
if (row.type === 'assistant') {
|
|
114
117
|
return row.content.filter(isObject).flatMap((block): Unplaced[] => {
|
|
115
118
|
if (block['type'] === 'text' && typeof block['text'] === 'string' && block['text'].trim() !== '') return [{ ...base, type: Reply, text: block['text'].trim() }]
|
|
@@ -128,7 +131,29 @@ function unplacedOf(row: Row, isInTask: boolean): Unplaced[] {
|
|
|
128
131
|
return prompt === undefined ? [] : [{ ...base, type: Prompt, text: prompt, from: senderOf(row.origin, isInTask) }]
|
|
129
132
|
}
|
|
130
133
|
|
|
134
|
+
function isModNotesRow(row: Row): boolean {
|
|
135
|
+
const text = textOf(row.content)
|
|
136
|
+
return row.type === 'user' && !row.content.some(isToolResultBlock) && modNotesIn(text).length > 0 && text.replace(modNoteBlock, '').trim() === ''
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function withNoteTexts(entries: readonly unknown[], texts: ReadonlyMap<number, string>): unknown[] {
|
|
140
|
+
let index = -1
|
|
141
|
+
const replaced = (text: string) =>
|
|
142
|
+
text.replace(modNoteBlock, (whole, mod: string) => {
|
|
143
|
+
index += 1
|
|
144
|
+
const changed = texts.get(index)
|
|
145
|
+
if (changed === undefined) return whole
|
|
146
|
+
return changed === '' ? '' : modNote(mod, changed)
|
|
147
|
+
})
|
|
148
|
+
return entries.map((entry) => {
|
|
149
|
+
if (typeof entry === 'string') return replaced(entry)
|
|
150
|
+
return isObject(entry) && entry['type'] === 'text' && typeof entry['text'] === 'string' ? { ...entry, text: replaced(entry['text']) } : entry
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
|
|
131
154
|
export function withTexts(row: Row, texts: ReadonlyMap<number, string>): readonly unknown[] {
|
|
155
|
+
if (row.type === 'attachment' && row.name === 'hook_additional_context') return withNoteTexts(row.content, texts)
|
|
156
|
+
if (isModNotesRow(row)) return keepingMedia(row.content, withNoteTexts([textOf(row.content)], texts)[0] as string)
|
|
132
157
|
if (row.type === 'assistant') {
|
|
133
158
|
let index = -1
|
|
134
159
|
return row.content.map((block) => {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { pluginName } from '../../../../records.js'
|
|
1
2
|
import type { MessageOf } from './message.js'
|
|
2
3
|
import type { Sender } from './sender.js'
|
|
3
4
|
|
|
@@ -7,3 +8,18 @@ export type Note = MessageOf<typeof Note> & {
|
|
|
7
8
|
readonly text: string
|
|
8
9
|
readonly from: Sender
|
|
9
10
|
}
|
|
11
|
+
|
|
12
|
+
export const modNoteBlock = new RegExp(`<(${pluginName.source.slice(1, -1)})_mod>\\n([\\s\\S]*?)\\n</\\1_mod>`, 'g')
|
|
13
|
+
|
|
14
|
+
export function modNote(mod: string, text: string): string {
|
|
15
|
+
return `<${mod}_mod>\n${text}\n</${mod}_mod>`
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function modNotesIn(text: string): { readonly mod: string; readonly text: string }[] {
|
|
19
|
+
return [...text.matchAll(modNoteBlock)].map((match) => ({ mod: match[1] as string, text: match[2] as string }))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function untagged(text: string): string {
|
|
23
|
+
const notes = modNotesIn(text)
|
|
24
|
+
return notes.length === 0 ? text : notes.map((note) => note.text).join('\n\n')
|
|
25
|
+
}
|
package/src/session/session.ts
CHANGED
|
@@ -120,7 +120,6 @@ export type OnMethods = { readonly [Name in EventName as `on${Name}`]: OnMethod<
|
|
|
120
120
|
|
|
121
121
|
export type BeforeMethods = { readonly [Name in BeforeName as `before${Name}`]: BeforeMethod<PayloadOf<Name>, AnswerOf<Name>> }
|
|
122
122
|
|
|
123
|
-
|
|
124
123
|
export type RecordPart = OnMethods &
|
|
125
124
|
BeforeMethods & {
|
|
126
125
|
search(text: string, matcher?: Matcher<Message>): AsyncIterable<Message>
|
|
@@ -327,7 +326,7 @@ export type ProcessOnMethods = { readonly [Name in ProcessName as `on${Name}`]:
|
|
|
327
326
|
|
|
328
327
|
export type ProcessBeforeMethods = { readonly [Name in keyof ProcessAnswer as `before${Name}`]: BeforeMethod<ProcessAsked[Name], ProcessAnswer[Name]> }
|
|
329
328
|
|
|
330
|
-
export type CurrentSession = Session & ProcessOnMethods & ProcessBeforeMethods
|
|
329
|
+
export type CurrentSession = Omit<Session, 'goal'> & ProcessOnMethods & ProcessBeforeMethods & { readonly goal: Goal & Pick<ProcessBeforeMethods, 'beforeSessionStart'> }
|
|
331
330
|
|
|
332
331
|
export type SessionScope = {
|
|
333
332
|
readonly running?: boolean
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { EventResult, Timer } from 'claude-code'
|
|
2
2
|
import { isObject } from '../records.js'
|
|
3
3
|
import type { Claude } from '../runtime/claude.js'
|
|
4
|
+
import { untagged } from '../session/context/turn/message/note.js'
|
|
4
5
|
import { elements } from './fake-elements.js'
|
|
5
6
|
import { fakeFiles } from './fake-files.js'
|
|
6
7
|
|
|
@@ -21,7 +22,7 @@ export type Fakes = {
|
|
|
21
22
|
prompt: { submit?: Claude['prompt']['submit'] }
|
|
22
23
|
model: { complete?: Claude['model']['complete'] }
|
|
23
24
|
agent: { list?: Claude['agent']['list']; spawn?: Claude['agent']['spawn'] }
|
|
24
|
-
clock: { after?: Claude['clock']['after']; every?: Claude['clock']['every'] }
|
|
25
|
+
clock: { now?: Claude['clock']['now']; after?: Claude['clock']['after']; every?: Claude['clock']['every'] }
|
|
25
26
|
cmod: { call?: Claude['cmod']['call'] }
|
|
26
27
|
tool: { call?: Claude['tool']['call'] }
|
|
27
28
|
}
|
|
@@ -79,7 +80,7 @@ export function fakeClaude(plugin: { readonly name: string; readonly root: strin
|
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
const appendNote: Claude['session']['append'] = async ({ message }) => {
|
|
82
|
-
shown.notes.push(message.content.map((block) => (typeof block['text'] === 'string' ? block['text'] : '')).join(''))
|
|
83
|
+
shown.notes.push(untagged(message.content.map((block) => (typeof block['text'] === 'string' ? block['text'] : '')).join('')))
|
|
83
84
|
return { message, uuid: `note-${shown.notes.length}` }
|
|
84
85
|
}
|
|
85
86
|
const submitPrompt: Claude['prompt']['submit'] = async ({ text }) => {
|
|
@@ -175,7 +176,7 @@ export function fakeClaude(plugin: { readonly name: string; readonly root: strin
|
|
|
175
176
|
keys: async () => [...store.keys()],
|
|
176
177
|
},
|
|
177
178
|
clock: {
|
|
178
|
-
now:
|
|
179
|
+
now: () => (fakes.clock.now === undefined ? Promise.resolve(Date.now()) : fakes.clock.now()),
|
|
179
180
|
after: faked('clock.after', () => fakes.clock.after ?? realTimer),
|
|
180
181
|
every: faked('clock.every', () => fakes.clock.every ?? idleInterval),
|
|
181
182
|
},
|
package/src/testing.ts
CHANGED
|
@@ -23,7 +23,7 @@ import type { Options, OptionValues } from './options.js'
|
|
|
23
23
|
import { readSteps, storeFolder, type Steps } from './records.js'
|
|
24
24
|
import type { Claude } from './runtime/claude.js'
|
|
25
25
|
import { answerCall, cmodPluginName, notInstalled } from './runtime/dependencies.js'
|
|
26
|
-
import { closingContext, foldBefore, installBefore, withAnswer } from './runtime/before.js'
|
|
26
|
+
import { closingContext, foldBefore, installBefore, withAnswer, type Answers } from './runtime/before.js'
|
|
27
27
|
import { permissionRoutes } from './runtime/permission-check.js'
|
|
28
28
|
import type { RoutedEvent } from './runtime/hooks.js'
|
|
29
29
|
import { keepClaudeCode, type ClaudeCode } from './testing/claude-code.js'
|
|
@@ -34,7 +34,7 @@ import { MissingOptions } from './runtime/options.js'
|
|
|
34
34
|
import { conversationMethods } from './runtime/sessions.js'
|
|
35
35
|
import type { ShownToast } from './runtime/toaster.js'
|
|
36
36
|
import { Message } from './session/context/turn/message/message.js'
|
|
37
|
-
import { Note } from './session/context/turn/message/note.js'
|
|
37
|
+
import { Note, untagged } from './session/context/turn/message/note.js'
|
|
38
38
|
import { Prompt } from './session/context/turn/message/prompt.js'
|
|
39
39
|
import { Mod, Person } from './session/context/turn/message/sender.js'
|
|
40
40
|
import type { SessionChange } from './session/conversation.js'
|
|
@@ -215,7 +215,7 @@ export function testMod<State extends object, Declared extends Options = Options
|
|
|
215
215
|
hooks: {
|
|
216
216
|
place: async () => conversations.place(),
|
|
217
217
|
wants: async (event) => conversations.wants(name, event),
|
|
218
|
-
|
|
218
|
+
fold: () => (event, change) => foldBefore(event, change, [(latest) => askMod(event, latest)], (error) => fake.claude.ui.log(`cmod: passed the ${event} on without ${name}'s answer: ${messageOf(error)}`, { to: 'debug' })),
|
|
219
219
|
},
|
|
220
220
|
})
|
|
221
221
|
|
|
@@ -273,7 +273,8 @@ export function testMod<State extends object, Declared extends Options = Options
|
|
|
273
273
|
|
|
274
274
|
const asked = async (event: BeforeEvent, change: SessionChange) => {
|
|
275
275
|
await start()
|
|
276
|
-
|
|
276
|
+
const answer = ((await askMod(event, change)) as Partial<Answers> | null | undefined) ?? undefined
|
|
277
|
+
return answer?.note === undefined ? answer : { ...answer, note: untagged(answer.note) }
|
|
277
278
|
}
|
|
278
279
|
|
|
279
280
|
const raise = Object.fromEntries([
|
|
@@ -470,7 +471,7 @@ function plainOutput({ component, props }: Args<'ui.render'>): RenderElement {
|
|
|
470
471
|
}
|
|
471
472
|
|
|
472
473
|
function replyOf({ text, context }: CommandRunResult): CommandReply {
|
|
473
|
-
return { ...(text === undefined ? {} : { text }), ...(context === undefined ? {} : { note: context.join('\n') }) }
|
|
474
|
+
return { ...(text === undefined ? {} : { text }), ...(context === undefined ? {} : { note: context.map(untagged).join('\n') }) }
|
|
474
475
|
}
|
|
475
476
|
|
|
476
477
|
async function modSteps(): Promise<Steps> {
|
package/testing/fake-claude.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fake-claude.d.ts","sourceRoot":"","sources":["../src/testing/fake-claude.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAS,MAAM,aAAa,CAAA;AAErD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"fake-claude.d.ts","sourceRoot":"","sources":["../src/testing/fake-claude.ts"],"names":[],"mappings":"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"}
|
package/testing/fake-claude.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isObject } from '../records.js';
|
|
2
|
+
import { untagged } from '../session/context/turn/message/note.js';
|
|
2
3
|
import { elements } from './fake-elements.js';
|
|
3
4
|
import { fakeFiles } from './fake-files.js';
|
|
4
5
|
export function calledAnswer(caller, answer) {
|
|
@@ -29,7 +30,7 @@ export function fakeClaude(plugin) {
|
|
|
29
30
|
return async (...args) => run(...args);
|
|
30
31
|
};
|
|
31
32
|
const appendNote = async ({ message }) => {
|
|
32
|
-
shown.notes.push(message.content.map((block) => (typeof block['text'] === 'string' ? block['text'] : '')).join(''));
|
|
33
|
+
shown.notes.push(untagged(message.content.map((block) => (typeof block['text'] === 'string' ? block['text'] : '')).join('')));
|
|
33
34
|
return { message, uuid: `note-${shown.notes.length}` };
|
|
34
35
|
};
|
|
35
36
|
const submitPrompt = async ({ text }) => {
|
|
@@ -129,7 +130,7 @@ export function fakeClaude(plugin) {
|
|
|
129
130
|
keys: async () => [...store.keys()],
|
|
130
131
|
},
|
|
131
132
|
clock: {
|
|
132
|
-
now:
|
|
133
|
+
now: () => (fakes.clock.now === undefined ? Promise.resolve(Date.now()) : fakes.clock.now()),
|
|
133
134
|
after: faked('clock.after', () => fakes.clock.after ?? realTimer),
|
|
134
135
|
every: faked('clock.every', () => fakes.clock.every ?? idleInterval),
|
|
135
136
|
},
|
package/testing.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["src/testing.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["src/testing.ts"],"names":[],"mappings":"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"}
|
package/testing.js
CHANGED
|
@@ -9,7 +9,7 @@ import { createLifecycle } from './runtime/lifecycle.js';
|
|
|
9
9
|
import { MissingOptions } from './runtime/options.js';
|
|
10
10
|
import { conversationMethods } from './runtime/sessions.js';
|
|
11
11
|
import { Message } from './session/context/turn/message/message.js';
|
|
12
|
-
import { Note } from './session/context/turn/message/note.js';
|
|
12
|
+
import { Note, untagged } from './session/context/turn/message/note.js';
|
|
13
13
|
import { Prompt } from './session/context/turn/message/prompt.js';
|
|
14
14
|
import { Mod, Person } from './session/context/turn/message/sender.js';
|
|
15
15
|
import { TurnEnd } from './session/context/turn/turn.js';
|
|
@@ -117,7 +117,7 @@ export function testMod(definition, options = {}) {
|
|
|
117
117
|
hooks: {
|
|
118
118
|
place: async () => conversations.place(),
|
|
119
119
|
wants: async (event) => conversations.wants(name, event),
|
|
120
|
-
|
|
120
|
+
fold: () => (event, change) => foldBefore(event, change, [(latest) => askMod(event, latest)], (error) => fake.claude.ui.log(`cmod: passed the ${event} on without ${name}'s answer: ${messageOf(error)}`, { to: 'debug' })),
|
|
121
121
|
},
|
|
122
122
|
});
|
|
123
123
|
const route = async (event, input, below) => {
|
|
@@ -172,7 +172,8 @@ export function testMod(definition, options = {}) {
|
|
|
172
172
|
});
|
|
173
173
|
const asked = async (event, change) => {
|
|
174
174
|
await start();
|
|
175
|
-
|
|
175
|
+
const answer = (await askMod(event, change)) ?? undefined;
|
|
176
|
+
return answer?.note === undefined ? answer : { ...answer, note: untagged(answer.note) };
|
|
176
177
|
};
|
|
177
178
|
const raise = Object.fromEntries([
|
|
178
179
|
...beforeEvents
|
|
@@ -378,7 +379,7 @@ function plainOutput({ component, props }) {
|
|
|
378
379
|
return elements.Box({ flexDirection: 'column', children: [elements.Text({ children: component }), elements.Box({ flexDirection: 'column', paddingLeft: 2, children: rows })] });
|
|
379
380
|
}
|
|
380
381
|
function replyOf({ text, context }) {
|
|
381
|
-
return { ...(text === undefined ? {} : { text }), ...(context === undefined ? {} : { note: context.join('\n') }) };
|
|
382
|
+
return { ...(text === undefined ? {} : { text }), ...(context === undefined ? {} : { note: context.map(untagged).join('\n') }) };
|
|
382
383
|
}
|
|
383
384
|
async function modSteps() {
|
|
384
385
|
const manifest = '../../../package.json';
|