@cmodjs/core 0.3.0 → 1.5.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 +61 -10
- package/docs/dependencies.md +16 -14
- package/docs/hooks.md +64 -29
- package/docs/index.md +29 -25
- package/docs/install-steps.md +13 -9
- package/docs/jobs.md +59 -111
- package/docs/mod.md +227 -175
- package/docs/options.md +2 -2
- package/docs/permissions.md +27 -16
- package/docs/session.md +311 -0
- package/docs/state.md +11 -7
- package/docs/testing.md +68 -23
- package/docs/ui.md +116 -76
- package/jobs/check.d.ts.map +1 -1
- package/jobs/check.js +5 -2
- package/jobs/context.d.ts +7 -8
- package/jobs/context.d.ts.map +1 -1
- package/jobs/context.js +23 -73
- package/jobs/permissions/decide-permission.d.ts +5 -3
- package/jobs/permissions/decide-permission.d.ts.map +1 -1
- package/jobs/permissions/decide-permission.js +6 -4
- package/jobs/permissions/find-project-scope.d.ts +1 -1
- package/jobs/permissions/find-project-scope.d.ts.map +1 -1
- package/jobs/permissions/find-project-scope.js +8 -11
- package/jobs/permissions/match-target.d.ts +2 -2
- package/jobs/permissions/match-target.d.ts.map +1 -1
- package/jobs/permissions.d.ts +1 -1
- package/jobs/permissions.d.ts.map +1 -1
- package/jobs/permissions.js +6 -4
- package/jobs/program.d.ts.map +1 -1
- package/jobs/program.js +14 -4
- package/jobs/prompt.d.ts +4 -2
- package/jobs/prompt.d.ts.map +1 -1
- package/jobs/prompt.js +16 -15
- package/jobs/schedule.d.ts +2 -2
- package/jobs/schedule.d.ts.map +1 -1
- package/jobs/schedule.js +9 -9
- package/jobs/slash-command.d.ts +4 -4
- package/jobs/slash-command.d.ts.map +1 -1
- package/jobs/slash-command.js +14 -9
- package/jobs/status-line.d.ts +1 -11
- package/jobs/status-line.d.ts.map +1 -1
- package/jobs/status-line.js +8 -12
- package/jobs/tool.d.ts +2 -2
- package/jobs/tool.d.ts.map +1 -1
- package/jobs/tool.js +15 -9
- package/mod.d.ts +178 -75
- package/mod.d.ts.map +1 -1
- package/mod.js +127 -3
- package/options.d.ts +2 -2
- package/options.js +19 -19
- package/package.json +3 -3
- package/records.d.ts +13 -8
- package/records.d.ts.map +1 -1
- package/records.js +33 -17
- package/register.d.ts.map +1 -1
- package/register.js +34 -5
- package/runtime/announce.d.ts +18 -0
- package/runtime/announce.d.ts.map +1 -0
- package/runtime/announce.js +68 -0
- package/runtime/before.d.ts +32 -0
- package/runtime/before.d.ts.map +1 -0
- package/runtime/before.js +214 -0
- package/runtime/bound.d.ts +4 -0
- package/runtime/bound.d.ts.map +1 -0
- package/runtime/bound.js +9 -0
- package/runtime/claude.d.ts +4 -2
- package/runtime/claude.d.ts.map +1 -1
- package/runtime/conversations.d.ts +17 -0
- package/runtime/conversations.d.ts.map +1 -0
- package/runtime/conversations.js +563 -0
- package/runtime/deadline.d.ts +0 -1
- package/runtime/deadline.d.ts.map +1 -1
- package/runtime/deadline.js +1 -1
- package/runtime/dependencies.d.ts +2 -0
- package/runtime/dependencies.d.ts.map +1 -1
- package/runtime/dependencies.js +10 -1
- package/runtime/engine.d.ts +13 -0
- package/runtime/engine.d.ts.map +1 -0
- package/runtime/engine.js +10 -0
- package/runtime/grants.d.ts +4 -0
- package/runtime/grants.d.ts.map +1 -1
- package/runtime/grants.js +68 -31
- package/runtime/hooks.d.ts +5 -4
- package/runtime/hooks.d.ts.map +1 -1
- package/runtime/hooks.js +29 -9
- package/runtime/installer.d.ts +1 -1
- package/runtime/installer.d.ts.map +1 -1
- package/runtime/installer.js +17 -14
- package/runtime/lifecycle.d.ts +3 -1
- package/runtime/lifecycle.d.ts.map +1 -1
- package/runtime/lifecycle.js +414 -212
- package/runtime/metadata.d.ts +3 -1
- package/runtime/metadata.d.ts.map +1 -1
- package/runtime/metadata.js +25 -14
- package/runtime/options.d.ts +3 -1
- package/runtime/options.d.ts.map +1 -1
- package/runtime/options.js +4 -4
- package/runtime/programs.d.ts +8 -0
- package/runtime/programs.d.ts.map +1 -1
- package/runtime/programs.js +39 -1
- package/runtime/router.d.ts +2 -1
- package/runtime/router.d.ts.map +1 -1
- package/runtime/router.js +11 -3
- package/runtime/sessions.d.ts +58 -0
- package/runtime/sessions.d.ts.map +1 -0
- package/runtime/sessions.js +433 -0
- package/runtime/state.d.ts +6 -2
- package/runtime/state.d.ts.map +1 -1
- package/runtime/state.js +46 -13
- package/runtime/toaster.d.ts +24 -0
- package/runtime/toaster.d.ts.map +1 -0
- package/runtime/toaster.js +286 -0
- package/runtime/ui.d.ts +6 -17
- package/runtime/ui.d.ts.map +1 -1
- package/runtime/ui.js +62 -118
- package/session/context/context.d.ts +12 -0
- package/session/context/context.d.ts.map +1 -0
- package/session/context/context.js +2 -0
- package/session/context/turn/message/document.d.ts +7 -0
- package/session/context/turn/message/document.d.ts.map +1 -0
- package/session/context/turn/message/document.js +1 -0
- package/session/context/turn/message/message.d.ts +55 -0
- package/session/context/turn/message/message.d.ts.map +1 -0
- package/session/context/turn/message/message.js +178 -0
- package/session/context/turn/message/note.d.ts +8 -0
- package/session/context/turn/message/note.d.ts.map +1 -0
- package/session/context/turn/message/note.js +1 -0
- package/session/context/turn/message/notice.d.ts +6 -0
- package/session/context/turn/message/notice.d.ts.map +1 -0
- package/session/context/turn/message/notice.js +1 -0
- package/session/context/turn/message/prompt.d.ts +8 -0
- package/session/context/turn/message/prompt.d.ts.map +1 -0
- package/session/context/turn/message/prompt.js +1 -0
- package/session/context/turn/message/reply.d.ts +6 -0
- package/session/context/turn/message/reply.d.ts.map +1 -0
- package/session/context/turn/message/reply.js +1 -0
- package/session/context/turn/message/sender.d.ts +14 -0
- package/session/context/turn/message/sender.d.ts.map +1 -0
- package/session/context/turn/message/sender.js +30 -0
- package/session/context/turn/message/skill.d.ts +7 -0
- package/session/context/turn/message/skill.d.ts.map +1 -0
- package/session/context/turn/message/skill.js +1 -0
- package/session/context/turn/message/tool-call.d.ts +9 -0
- package/session/context/turn/message/tool-call.d.ts.map +1 -0
- package/session/context/turn/message/tool-call.js +1 -0
- package/session/context/turn/message/tool-result.d.ts +8 -0
- package/session/context/turn/message/tool-result.d.ts.map +1 -0
- package/session/context/turn/message/tool-result.js +1 -0
- package/session/context/turn/turn.d.ts +13 -0
- package/session/context/turn/turn.d.ts.map +1 -0
- package/session/context/turn/turn.js +2 -0
- package/session/conversation.d.ts +73 -0
- package/session/conversation.d.ts.map +1 -0
- package/session/conversation.js +146 -0
- package/session/goal/goal.d.ts +11 -0
- package/session/goal/goal.d.ts.map +1 -0
- package/session/goal/goal.js +1 -0
- package/session/goal/task.d.ts +16 -0
- package/session/goal/task.d.ts.map +1 -0
- package/session/goal/task.js +3 -0
- package/session/session.d.ts +158 -0
- package/session/session.d.ts.map +1 -0
- package/session/session.js +2 -0
- package/src/jobs/check.ts +7 -4
- package/src/jobs/context.ts +24 -82
- package/src/jobs/permissions/decide-permission.ts +10 -8
- package/src/jobs/permissions/find-project-scope.ts +8 -14
- package/src/jobs/permissions/match-target.ts +3 -3
- package/src/jobs/permissions.ts +8 -6
- package/src/jobs/program.ts +13 -4
- package/src/jobs/prompt.ts +21 -24
- package/src/jobs/schedule.ts +14 -14
- package/src/jobs/slash-command.ts +20 -14
- package/src/jobs/status-line.ts +9 -21
- package/src/jobs/tool.ts +17 -11
- package/src/mod.ts +252 -97
- package/src/options.ts +22 -22
- package/src/records.ts +40 -23
- package/src/register.ts +34 -5
- package/src/runtime/announce.ts +76 -0
- package/src/runtime/before.ts +200 -0
- package/src/runtime/bound.ts +12 -0
- package/src/runtime/claude.ts +7 -2
- package/src/runtime/conversations.ts +579 -0
- package/src/runtime/deadline.ts +2 -2
- package/src/runtime/dependencies.ts +13 -1
- package/src/runtime/engine.ts +23 -0
- package/src/runtime/grants.ts +69 -29
- package/src/runtime/hooks.ts +39 -14
- package/src/runtime/installer.ts +16 -14
- package/src/runtime/lifecycle.ts +413 -201
- package/src/runtime/metadata.ts +37 -14
- package/src/runtime/options.ts +19 -4
- package/src/runtime/programs.ts +40 -1
- package/src/runtime/router.ts +11 -4
- package/src/runtime/sessions.ts +447 -0
- package/src/runtime/state.ts +50 -14
- package/src/runtime/toaster.ts +334 -0
- package/src/runtime/ui.ts +65 -143
- package/src/session/context/context.ts +14 -0
- package/src/session/context/turn/message/document.ts +8 -0
- package/src/session/context/turn/message/message.ts +203 -0
- package/src/session/context/turn/message/note.ts +9 -0
- package/src/session/context/turn/message/notice.ts +7 -0
- package/src/session/context/turn/message/prompt.ts +9 -0
- package/src/session/context/turn/message/reply.ts +7 -0
- package/src/session/context/turn/message/sender.ts +38 -0
- package/src/session/context/turn/message/skill.ts +8 -0
- package/src/session/context/turn/message/tool-call.ts +10 -0
- package/src/session/context/turn/message/tool-result.ts +9 -0
- package/src/session/context/turn/turn.ts +15 -0
- package/src/session/conversation.ts +194 -0
- package/src/session/goal/goal.ts +10 -0
- package/src/session/goal/task.ts +19 -0
- package/src/session/session.ts +141 -0
- package/src/testing/fake-claude.ts +25 -17
- package/src/testing/fake-elements.ts +3 -2
- package/src/testing/fake-files.ts +1 -1
- package/src/testing/fake-sessions.ts +277 -0
- package/src/testing.ts +138 -35
- package/src/ui/components.ts +3 -3
- package/src/ui/elements.ts +22 -8
- package/src/ui/markdown.ts +22 -26
- package/src/ui/places.ts +49 -0
- package/src/utils/call-effects.ts +5 -5
- package/src/utils/changelog.ts +32 -0
- package/src/utils/disposable.ts +3 -0
- package/src/utils/matcher.ts +37 -0
- package/src/utils/metadata.ts +17 -15
- package/src/utils/parse-shell.ts +9 -8
- package/src/utils/paths.ts +21 -4
- package/src/utils/text.ts +4 -0
- package/testing/fake-claude.d.ts +5 -1
- package/testing/fake-claude.d.ts.map +1 -1
- package/testing/fake-claude.js +21 -12
- package/testing/fake-elements.js +4 -2
- package/testing/fake-files.js +1 -1
- package/testing/fake-sessions.d.ts +81 -0
- package/testing/fake-sessions.d.ts.map +1 -0
- package/testing/fake-sessions.js +249 -0
- package/testing.d.ts +34 -13
- package/testing.d.ts.map +1 -1
- package/testing.js +115 -23
- package/ui/components.d.ts +2 -3
- package/ui/components.d.ts.map +1 -1
- package/ui/components.js +3 -3
- package/ui/elements.d.ts.map +1 -1
- package/ui/elements.js +19 -9
- package/ui/markdown.d.ts +35 -33
- package/ui/markdown.d.ts.map +1 -1
- package/ui/markdown.js +14 -16
- package/ui/places.d.ts +43 -0
- package/ui/places.d.ts.map +1 -0
- package/ui/places.js +31 -0
- package/utils/call-effects.d.ts +2 -1
- package/utils/call-effects.d.ts.map +1 -1
- package/utils/call-effects.js +3 -3
- package/utils/changelog.d.ts +9 -0
- package/utils/changelog.d.ts.map +1 -0
- package/utils/changelog.js +29 -0
- package/utils/disposable.d.ts +4 -0
- package/utils/disposable.d.ts.map +1 -0
- package/utils/disposable.js +1 -0
- package/utils/matcher.d.ts +13 -0
- package/utils/matcher.d.ts.map +1 -0
- package/utils/matcher.js +32 -0
- package/utils/metadata.d.ts +3 -3
- package/utils/metadata.js +18 -14
- package/utils/parse-shell.js +9 -7
- package/utils/paths.d.ts +4 -1
- package/utils/paths.d.ts.map +1 -1
- package/utils/paths.js +19 -4
- package/utils/text.d.ts +1 -0
- package/utils/text.d.ts.map +1 -1
- package/utils/text.js +3 -0
- package/src/ui/slots.ts +0 -36
- package/ui/slots.d.ts +0 -208
- package/ui/slots.d.ts.map +0 -1
- package/ui/slots.js +0 -19
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { matchesGlob } from '../../utils/matches-glob.js'
|
|
2
2
|
import { parseShell } from '../../utils/parse-shell.js'
|
|
3
|
-
import { callBaseOf, type CallBase, type CallEffects, type CommandCall, type FetchCall, type FileAccess, type FileCall, type
|
|
3
|
+
import { callBaseOf, type CallBase, type CallEffects, type CommandCall, type FetchCall, type FileAccess, type FileCall, type Call } from '../../utils/call-effects.js'
|
|
4
4
|
import type { Workspace } from './find-project-scope.js'
|
|
5
5
|
import { realPathOf, relativePath } from '../../utils/paths.js'
|
|
6
6
|
import { basename } from '../../vendor.js'
|
|
@@ -9,7 +9,7 @@ export type TargetCalls = { command: CommandCall; read: FileCall; write: FileCal
|
|
|
9
9
|
export type TargetKey = keyof TargetCalls
|
|
10
10
|
export type TargetOf<Key extends TargetKey> = Record<Key, string | string[]> & Partial<Record<Exclude<TargetKey, Key>, never>>
|
|
11
11
|
export type Target = { [Key in TargetKey]: TargetOf<Key> }[TargetKey]
|
|
12
|
-
export type MatchedCall = { call:
|
|
12
|
+
export type MatchedCall = { call: Call; access: FileAccess | undefined; folder: string }
|
|
13
13
|
|
|
14
14
|
const targetKeys: readonly TargetKey[] = ['command', 'read', 'write', 'fetch', 'subagent', 'tool']
|
|
15
15
|
|
|
@@ -29,7 +29,7 @@ export function targetOf(target: Target): { key: TargetKey; patterns: string[];
|
|
|
29
29
|
export async function matchTarget(target: Target, call: CallBase, effects: CallEffects, workspace: Workspace): Promise<MatchedCall[]> {
|
|
30
30
|
const { key, patterns, exclusions } = targetOf(target)
|
|
31
31
|
const base = callBaseOf(call)
|
|
32
|
-
const only = (matched:
|
|
32
|
+
const only = (matched: Call, folder = workspace.cwd): MatchedCall[] => [{ call: matched, access: undefined, folder }]
|
|
33
33
|
const isNamed = (value: string) => matchesAny(value, patterns) && !matchesAny(value, exclusions)
|
|
34
34
|
if (key === 'read' || key === 'write') {
|
|
35
35
|
const matched: MatchedCall[] = []
|
package/src/jobs/permissions.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import type { Args, Frozen } from 'claude-code'
|
|
2
2
|
import type { Job, Mod } from '../mod.js'
|
|
3
3
|
import type { Deadline } from '../runtime/deadline.js'
|
|
4
|
+
import { engineOf } from '../runtime/engine.js'
|
|
4
5
|
import { inputOf } from '../utils/call-effects.js'
|
|
5
6
|
import { decidePermission, stricterVerdict, type PermissionRules as RulesFor, type Rule as RuleFor, type Verdict } from './permissions/decide-permission.js'
|
|
6
7
|
import { targetOf } from './permissions/match-target.js'
|
|
7
8
|
import { messageOf } from '../utils/text.js'
|
|
8
|
-
import {
|
|
9
|
+
import { withinDeadline, workspaceReader } from './context.js'
|
|
9
10
|
|
|
10
|
-
export type { CommandCall, FetchCall, FileCall,
|
|
11
|
+
export type { CommandCall, FetchCall, FileCall, Call } from '../utils/call-effects.js'
|
|
11
12
|
export type { Target } from './permissions/match-target.js'
|
|
12
13
|
export type Rule<State extends object = Record<never, never>> = RuleFor<Mod<State>>
|
|
13
14
|
export type PermissionRules<State extends object = Record<never, never>> = RulesFor<Mod<State>>
|
|
@@ -19,25 +20,26 @@ export function permissions<State extends object = Record<never, never>>(rules:
|
|
|
19
20
|
if (count === 0) throw new Error('permissions: give it a deny or an ask rule, or remove it from setup.')
|
|
20
21
|
|
|
21
22
|
return (job) => {
|
|
23
|
+
const { router, toolCalls } = engineOf(job.mod)
|
|
22
24
|
const readWorkspace = workspaceReader(job)
|
|
23
25
|
|
|
24
26
|
const verdictOf = async (e: Frozen<Args<'tool.check'>>): Promise<Verdict | undefined> => {
|
|
25
27
|
let current: PermissionRules<State>
|
|
26
28
|
try {
|
|
27
|
-
current = typeof rules === 'function' ? rules(job.mod.state
|
|
29
|
+
current = typeof rules === 'function' ? rules(job.mod.state) : rules
|
|
28
30
|
checkedCount(current)
|
|
29
31
|
} catch (error) {
|
|
30
32
|
return { decision: 'deny', reason: `The permission rules of ${job.mod.name} failed, so it denies the call: ${messageOf(error)}` }
|
|
31
33
|
}
|
|
32
34
|
try {
|
|
33
|
-
const [agent, workspace] = await Promise.all([
|
|
34
|
-
return await decidePermission(current, { tool: e.tool, input: inputOf(e.input), ...agent }, workspace,
|
|
35
|
+
const [agent, workspace] = await Promise.all([toolCalls.agentOf(e.tool_use_id), readWorkspace()])
|
|
36
|
+
return await decidePermission(current, { tool: e.tool, input: inputOf(e.input), ...agent }, workspace, job.mod, (run) => withinDeadline(job.mod, deadline, 'it', run))
|
|
35
37
|
} catch (error) {
|
|
36
38
|
return { decision: 'deny', reason: `The permissions job could not read the session, so it denies the call: ${messageOf(error)}` }
|
|
37
39
|
}
|
|
38
40
|
}
|
|
39
41
|
|
|
40
|
-
|
|
42
|
+
router.add('tool.check', async (e, next) => {
|
|
41
43
|
const [below, ours] = await Promise.all([next(e), verdictOf(e)])
|
|
42
44
|
return ours === undefined ? below : stricterVerdict<Verdict>(below, ours)
|
|
43
45
|
})
|
package/src/jobs/program.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Job } from '../mod.js'
|
|
2
|
-
import { PermissionRefused } from '../runtime/grants.js'
|
|
2
|
+
import { PermissionRefused, programAddresses } from '../runtime/grants.js'
|
|
3
|
+
import { engineOf } from '../runtime/engine.js'
|
|
3
4
|
import { messageOf } from '../utils/text.js'
|
|
4
5
|
|
|
5
6
|
export type Program = {
|
|
@@ -14,7 +15,7 @@ const backoffSeconds = [1, 2, 4, 8, 16] as const
|
|
|
14
15
|
export function program(options: { readonly command: readonly string[]; readonly environment?: Record<string, string> }): Job<Program> {
|
|
15
16
|
const { command, environment } = options
|
|
16
17
|
return ({ mod, announce }) => {
|
|
17
|
-
const { claude } = mod
|
|
18
|
+
const { claude, report } = engineOf(mod)
|
|
18
19
|
const [executable] = command
|
|
19
20
|
if (executable === undefined) throw new Error(`${mod.name}: the program has no command. Name the program on PATH and its arguments, such as ['preview-server', '--port', '0'].`)
|
|
20
21
|
let state: Program['state'] = 'stopped'
|
|
@@ -22,11 +23,17 @@ export function program(options: { readonly command: readonly string[]; readonly
|
|
|
22
23
|
let failure: Error | undefined
|
|
23
24
|
let exits = 0
|
|
24
25
|
const waiting: { resolve: (address: Address) => void; reject: (error: Error) => void }[] = []
|
|
26
|
+
const ownAddresses = programAddresses(mod)
|
|
25
27
|
|
|
26
28
|
const stop = (reason: string) => {
|
|
27
29
|
state = 'fatal'
|
|
28
30
|
failure = new Error(`${mod.name}: the ${executable} program stopped: ${reason}`)
|
|
29
|
-
|
|
31
|
+
const retry = () => {
|
|
32
|
+
exits = 0
|
|
33
|
+
failure = undefined
|
|
34
|
+
void start()
|
|
35
|
+
}
|
|
36
|
+
report({ id: `program:${executable}`, message: `The ${executable} program stopped`, description: reason, retry })
|
|
30
37
|
for (const waiter of waiting.splice(0)) waiter.reject(failure)
|
|
31
38
|
}
|
|
32
39
|
|
|
@@ -52,17 +59,19 @@ export function program(options: { readonly command: readonly string[]; readonly
|
|
|
52
59
|
}
|
|
53
60
|
state = 'running'
|
|
54
61
|
exits = 0
|
|
62
|
+
ownAddresses.add(address.socketPath ?? new URL(address.url).host)
|
|
55
63
|
for (const waiter of waiting.splice(0)) waiter.resolve(address)
|
|
56
64
|
}
|
|
57
65
|
} catch (error) {
|
|
58
66
|
if (error instanceof PermissionRefused) return stop(`${messageOf(error)} Add it, then run /reload-plugins.`)
|
|
59
67
|
lastError = messageOf(error)
|
|
60
68
|
}
|
|
69
|
+
if (address !== undefined) ownAddresses.delete(address.socketPath ?? new URL(address.url).host)
|
|
61
70
|
address = undefined
|
|
62
71
|
exits += 1
|
|
63
72
|
const wait = backoffSeconds[exits - 1]
|
|
64
73
|
if (wait === undefined) {
|
|
65
|
-
stop(`it did not start again after ${backoffSeconds.length} tries: ${lastError === '' ? 'no standard error' : lastError}
|
|
74
|
+
stop(`it did not start again after ${backoffSeconds.length} tries: ${lastError === '' ? 'no standard error' : lastError}.`)
|
|
66
75
|
return
|
|
67
76
|
}
|
|
68
77
|
state = 'backoff'
|
package/src/jobs/prompt.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import type { Job, JobContext, Mod } from '../mod.js'
|
|
2
|
-
import {
|
|
2
|
+
import type { Deadline } from '../runtime/deadline.js'
|
|
3
|
+
import { engineOf } from '../runtime/engine.js'
|
|
3
4
|
import { onStateChange } from '../runtime/state.js'
|
|
4
5
|
import { listed, messageOf } from '../utils/text.js'
|
|
5
|
-
import { callEffects, type
|
|
6
|
+
import { callEffects, type Call } from '../utils/call-effects.js'
|
|
6
7
|
import { matchTarget, targetOf, type Target } from './permissions/match-target.js'
|
|
7
|
-
import { afterCall,
|
|
8
|
+
import { afterCall, inFolder, targetWords, withinDeadline } from './context.js'
|
|
8
9
|
|
|
9
|
-
type PromptInput = { userPrompt?: string; call?:
|
|
10
|
+
type PromptInput = { userPrompt?: string; call?: Call & { readonly folder: string } }
|
|
10
11
|
|
|
11
12
|
type PromptOptions<State extends object> = {
|
|
12
13
|
name: string
|
|
@@ -15,7 +16,7 @@ type PromptOptions<State extends object> = {
|
|
|
15
16
|
after?: Target | Target[]
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
const deadline: Deadline = { ms: 5000,
|
|
19
|
+
const deadline: Deadline = { ms: 5000, job: 'prompt' }
|
|
19
20
|
|
|
20
21
|
export function prompt<State extends object = Record<never, never>>({ name, prompt: text, when, after }: PromptOptions<State>): Job<void, State> {
|
|
21
22
|
if (name.trim() === '') throw new Error('prompt: give the block a name. Claude reads the text under it.')
|
|
@@ -26,10 +27,10 @@ export function prompt<State extends object = Record<never, never>>({ name, prom
|
|
|
26
27
|
return (job) => {
|
|
27
28
|
job.reserveName('prompt', name, `prompt: ${job.mod.name} adds the name "${name}" two times. Give each prompt its own name.`)
|
|
28
29
|
|
|
29
|
-
const log = (reason: string) => job.mod.
|
|
30
|
-
const textFor = async (input: PromptInput
|
|
30
|
+
const log = (reason: string) => job.mod.log(`prompt "${name}" added nothing: ${reason}`)
|
|
31
|
+
const textFor = async (input: PromptInput) => {
|
|
31
32
|
try {
|
|
32
|
-
const value = typeof text === 'string' ? text : await text(input, mod)
|
|
33
|
+
const value = typeof text === 'string' ? text : await withinDeadline(job.mod, deadline, `the ${name} prompt`, () => text(input, job.mod))
|
|
33
34
|
return value === '' ? undefined : value
|
|
34
35
|
} catch (error) {
|
|
35
36
|
log(messageOf(error))
|
|
@@ -37,36 +38,37 @@ export function prompt<State extends object = Record<never, never>>({ name, prom
|
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
40
|
|
|
40
|
-
const headedText = async (input: PromptInput
|
|
41
|
-
const value = await textFor(input
|
|
41
|
+
const headedText = async (input: PromptInput) => {
|
|
42
|
+
const value = await textFor(input)
|
|
42
43
|
return value === undefined ? undefined : `# ${name}\n${value}`
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
if (when !== undefined) {
|
|
46
|
-
addAfterUserPrompts(job, when, (userPrompt) => headedText({ userPrompt }
|
|
47
|
+
addAfterUserPrompts(job, when, (userPrompt) => headedText({ userPrompt }), log)
|
|
47
48
|
job.announce(`the ${name} prompt after matching user prompts`)
|
|
48
49
|
} else if (targets.length > 0) {
|
|
49
50
|
addAfterCalls(job, targets, headedText, log)
|
|
50
51
|
job.announce(`the ${name} prompt after ${listed(targets.map(targetWords))}`)
|
|
51
52
|
} else {
|
|
52
|
-
addKeptCurrent(job, name, () => textFor({}
|
|
53
|
+
addKeptCurrent(job, name, () => textFor({}), log)
|
|
53
54
|
job.announce(`the ${name} prompt`)
|
|
54
55
|
}
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
function addKeptCurrent<State extends object>(job: JobContext<State>, name: string, textFor: () => Promise<string | undefined>, log: (reason: string) => void): void {
|
|
60
|
+
const { claude, router } = engineOf(job.mod)
|
|
59
61
|
let given: string | undefined
|
|
60
62
|
let hasStarted = false
|
|
61
63
|
let isQueued = false
|
|
62
64
|
let updates = Promise.resolve()
|
|
63
65
|
|
|
64
|
-
|
|
66
|
+
router.add('classic.SessionStart', (e, next) => {
|
|
65
67
|
hasStarted = e.source === 'resume' || e.source === 'compact'
|
|
66
68
|
if (!hasStarted) given = undefined
|
|
67
69
|
return next(e)
|
|
68
70
|
})
|
|
69
|
-
|
|
71
|
+
router.add('prompt.context', async (e, next) => {
|
|
70
72
|
const below = await next(e)
|
|
71
73
|
hasStarted = true
|
|
72
74
|
if (below.blocks.some((block) => block.name === name)) {
|
|
@@ -83,7 +85,7 @@ function addKeptCurrent<State extends object>(job: JobContext<State>, name: stri
|
|
|
83
85
|
const value = await textFor()
|
|
84
86
|
if (value === undefined || value === given) return
|
|
85
87
|
given = value
|
|
86
|
-
await
|
|
88
|
+
await claude.session.append({ message: { type: 'user', content: [{ type: 'text', text: `# ${name}\n${value}` }] } }).then((added) => {
|
|
87
89
|
if (added.deny !== undefined) log(`Claude Code refused the new text: ${added.deny}`)
|
|
88
90
|
})
|
|
89
91
|
}
|
|
@@ -94,8 +96,8 @@ function addKeptCurrent<State extends object>(job: JobContext<State>, name: stri
|
|
|
94
96
|
})
|
|
95
97
|
}
|
|
96
98
|
|
|
97
|
-
function addAfterUserPrompts(job: JobContext
|
|
98
|
-
job.mod.
|
|
99
|
+
function addAfterUserPrompts<State extends object>(job: JobContext<State>, when: RegExp | ((userPrompt: string) => boolean), textFor: (userPrompt: string) => Promise<string | undefined>, log: (reason: string) => void): void {
|
|
100
|
+
engineOf(job.mod).router.add('prompt.submit', async (e, next) => {
|
|
99
101
|
let value: string | undefined
|
|
100
102
|
try {
|
|
101
103
|
if (typeof when === 'function' ? when(e.text) : e.text.search(when) >= 0) value = await textFor(e.text)
|
|
@@ -106,12 +108,7 @@ function addAfterUserPrompts(job: JobContext, when: RegExp | ((userPrompt: strin
|
|
|
106
108
|
})
|
|
107
109
|
}
|
|
108
110
|
|
|
109
|
-
function addAfterCalls<State extends object>(
|
|
110
|
-
job: JobContext<State>,
|
|
111
|
-
targets: Target[],
|
|
112
|
-
textFor: (input: PromptInput, mod: Mod<State>) => Promise<string | undefined>,
|
|
113
|
-
log: (reason: string) => void,
|
|
114
|
-
): void {
|
|
111
|
+
function addAfterCalls<State extends object>(job: JobContext<State>, targets: Target[], textFor: (input: PromptInput) => Promise<string | undefined>, log: (reason: string) => void): void {
|
|
115
112
|
afterCall(
|
|
116
113
|
job,
|
|
117
114
|
async (use, workspace) => {
|
|
@@ -119,7 +116,7 @@ function addAfterCalls<State extends object>(
|
|
|
119
116
|
for (const target of targets) {
|
|
120
117
|
const [matched] = await matchTarget(target, use, effects, workspace)
|
|
121
118
|
if (matched === undefined) continue
|
|
122
|
-
const value = await textFor({ call: matched.call
|
|
119
|
+
const value = await textFor({ call: await inFolder(matched.call, matched.folder, workspace) })
|
|
123
120
|
return value === undefined ? [] : [value]
|
|
124
121
|
}
|
|
125
122
|
return []
|
package/src/jobs/schedule.ts
CHANGED
|
@@ -1,37 +1,37 @@
|
|
|
1
1
|
import type { Job } from '../mod.js'
|
|
2
2
|
import { onStateChange } from '../runtime/state.js'
|
|
3
|
-
import {
|
|
3
|
+
import { engineOf } from '../runtime/engine.js'
|
|
4
4
|
|
|
5
|
-
export type
|
|
5
|
+
export type ScheduledPrompt = { readonly cron: string; readonly prompt: string }
|
|
6
6
|
|
|
7
7
|
const scheduleCounts = new WeakMap<object, number>()
|
|
8
8
|
|
|
9
|
-
export function schedule<State extends object = Record<never, never>>(entry:
|
|
9
|
+
export function schedule<State extends object = Record<never, never>>(entry: ScheduledPrompt | ((state: State) => ScheduledPrompt | undefined)): Job<void, State> {
|
|
10
10
|
if (typeof entry !== 'function') checked(entry)
|
|
11
11
|
|
|
12
12
|
return (job) => {
|
|
13
13
|
const { mod } = job
|
|
14
|
-
const { claude } = mod
|
|
14
|
+
const { claude, report: reportFailure } = engineOf(mod)
|
|
15
15
|
const order = scheduleCounts.get(mod) ?? 0
|
|
16
16
|
scheduleCounts.set(mod, order + 1)
|
|
17
17
|
const storeKey = `cmod:schedule:${order}`
|
|
18
|
-
const
|
|
18
|
+
const report = (message: string, error: unknown) => reportFailure({ id: storeKey, message: `The schedule ${message}`, error })
|
|
19
19
|
let current: { readonly id: string; readonly wanted: string } | undefined
|
|
20
20
|
let isQueued = false
|
|
21
21
|
|
|
22
22
|
const remove = async (id: string) => {
|
|
23
|
-
await claude.tool.call({ tool: 'CronDelete', id }).catch(() =>
|
|
23
|
+
await claude.tool.call({ tool: 'CronDelete', id }).catch((error: unknown) => report(`could not delete its last cron ${id}`, error))
|
|
24
24
|
await claude.store.delete(storeKey)
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
const update = async () => {
|
|
28
28
|
isQueued = false
|
|
29
|
-
let next:
|
|
29
|
+
let next: ScheduledPrompt | undefined
|
|
30
30
|
try {
|
|
31
|
-
next = typeof entry === 'function' ? entry(mod.state
|
|
31
|
+
next = typeof entry === 'function' ? entry(mod.state) : entry
|
|
32
32
|
if (next !== undefined) checked(next)
|
|
33
33
|
} catch (error) {
|
|
34
|
-
|
|
34
|
+
report('kept its last cron', error)
|
|
35
35
|
return
|
|
36
36
|
}
|
|
37
37
|
const wanted = next === undefined ? undefined : JSON.stringify([next.cron, next.prompt])
|
|
@@ -40,8 +40,8 @@ export function schedule<State extends object = Record<never, never>>(entry: Sch
|
|
|
40
40
|
current = undefined
|
|
41
41
|
if (next === undefined) return
|
|
42
42
|
const answer = await claude.tool.call({ tool: 'CronCreate', cron: next.cron, prompt: next.prompt, recurring: true })
|
|
43
|
-
if (answer.deny !== undefined) return
|
|
44
|
-
if (answer.isError === true) return
|
|
43
|
+
if (answer.deny !== undefined) return report('was refused', answer.deny)
|
|
44
|
+
if (answer.isError === true) return report('failed', answer.text ?? 'CronCreate answered an error')
|
|
45
45
|
current = { id: answer.result.id, wanted: wanted as string }
|
|
46
46
|
await claude.store.set(storeKey, current.id)
|
|
47
47
|
}
|
|
@@ -50,17 +50,17 @@ export function schedule<State extends object = Record<never, never>>(entry: Sch
|
|
|
50
50
|
.get(storeKey)
|
|
51
51
|
.then((left) => (typeof left === 'string' ? remove(left) : undefined))
|
|
52
52
|
.then(update)
|
|
53
|
-
.catch((error: unknown) =>
|
|
53
|
+
.catch((error: unknown) => report('failed', error))
|
|
54
54
|
onStateChange(mod.state, () => {
|
|
55
55
|
if (isQueued) return
|
|
56
56
|
isQueued = true
|
|
57
|
-
updates = updates.then(update).catch((error: unknown) =>
|
|
57
|
+
updates = updates.then(update).catch((error: unknown) => report('failed', error))
|
|
58
58
|
})
|
|
59
59
|
job.announce(typeof entry === 'function' ? 'a schedule from its state' : `a schedule at ${entry.cron}`)
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
function checked(entry:
|
|
63
|
+
function checked(entry: ScheduledPrompt): void {
|
|
64
64
|
if (entry.cron.trim() === '') throw new Error('schedule: give it a cron, such as "*/10 * * * *" for every 10 minutes.')
|
|
65
65
|
if (entry.prompt.trim() === '') throw new Error('schedule: give it the prompt Claude gets at each fire.')
|
|
66
66
|
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import type { CommandRunResult } from 'claude-code'
|
|
2
|
-
import type { Job, Mod } from '../mod.js'
|
|
2
|
+
import type { Disposable, Job, Mod } from '../mod.js'
|
|
3
3
|
import { parse } from '../vendor.js'
|
|
4
|
-
import {
|
|
4
|
+
import type { Deadline } from '../runtime/deadline.js'
|
|
5
|
+
import { engineOf } from '../runtime/engine.js'
|
|
5
6
|
import { messageOf } from '../utils/text.js'
|
|
6
|
-
import {
|
|
7
|
+
import { withinDeadline } from './context.js'
|
|
7
8
|
|
|
8
|
-
export type
|
|
9
|
+
export type CommandReply = string | { text?: string; context?: string } | undefined
|
|
9
10
|
|
|
10
|
-
const deadline: Deadline = { ms: 30000,
|
|
11
|
+
const deadline: Deadline = { ms: 30000, job: 'slashCommand' }
|
|
11
12
|
|
|
12
13
|
const commandName = /^[A-Za-z0-9_-]{1,64}$/
|
|
13
14
|
|
|
@@ -16,14 +17,14 @@ export function slashCommand<State extends object = Record<never, never>>(option
|
|
|
16
17
|
readonly description: string
|
|
17
18
|
readonly argumentHint?: string
|
|
18
19
|
readonly immediate?: true
|
|
19
|
-
readonly reply: (input: { args: string; positionals: string[] }, mod: Mod<State>) =>
|
|
20
|
-
}): Job<
|
|
20
|
+
readonly reply: (input: { args: string; positionals: string[] }, mod: Mod<State>) => CommandReply | void | Promise<CommandReply | void>
|
|
21
|
+
}): Job<Disposable, State> {
|
|
21
22
|
const { name, description, argumentHint, immediate, reply } = options
|
|
22
23
|
return (job) => {
|
|
23
24
|
const { mod, announce, reserveName } = job
|
|
24
|
-
const { claude } = mod
|
|
25
|
+
const { claude, router, report } = engineOf(mod)
|
|
25
26
|
if (!commandName.test(name)) throw new Error(`${mod.name}: "${name}" is not a slash command name. Use 1 to 64 letters, digits, "_", or "-", without the slash.`)
|
|
26
|
-
reserveName('slashCommand', name, `${mod.name}: the slash command /${name} is already added. Give each slashCommand its own name.`)
|
|
27
|
+
const reserved = reserveName('slashCommand', name, `${mod.name}: the slash command /${name} is already added. Give each slashCommand its own name.`)
|
|
27
28
|
|
|
28
29
|
let registered: string | undefined
|
|
29
30
|
claude.command.register({ name, description, ...(argumentHint === undefined ? {} : { argumentHint }), ...(immediate === undefined ? {} : { immediate }) }).then(
|
|
@@ -31,24 +32,29 @@ export function slashCommand<State extends object = Record<never, never>>(option
|
|
|
31
32
|
registered = command
|
|
32
33
|
},
|
|
33
34
|
(error: unknown) => {
|
|
34
|
-
|
|
35
|
+
report({ id: `command:${name}`, message: `/${name} is not added`, error })
|
|
35
36
|
},
|
|
36
37
|
)
|
|
37
38
|
announce(`/${name}`)
|
|
38
39
|
|
|
39
|
-
const
|
|
40
|
-
claude.on('command.run', async (e, next) => {
|
|
40
|
+
const answering = router.add('command.run', async (e, next) => {
|
|
41
41
|
if (e.command !== registered) return next(e)
|
|
42
42
|
try {
|
|
43
|
-
return answerOf(await reply({ args: e.args, positionals: splitWords(e.args) },
|
|
43
|
+
return answerOf(await withinDeadline(mod, deadline, `/${name}`, () => reply({ args: e.args, positionals: splitWords(e.args) }, mod)))
|
|
44
44
|
} catch (error) {
|
|
45
45
|
return { text: `/${name} failed: ${oneLine(error)}` }
|
|
46
46
|
}
|
|
47
47
|
})
|
|
48
|
+
return {
|
|
49
|
+
dispose() {
|
|
50
|
+
answering.dispose()
|
|
51
|
+
reserved.dispose()
|
|
52
|
+
},
|
|
53
|
+
}
|
|
48
54
|
}
|
|
49
55
|
}
|
|
50
56
|
|
|
51
|
-
function answerOf(reply:
|
|
57
|
+
function answerOf(reply: CommandReply | void): CommandRunResult {
|
|
52
58
|
if (reply === undefined) return {}
|
|
53
59
|
if (typeof reply === 'string') return { text: reply }
|
|
54
60
|
return { ...(reply.text === undefined ? {} : { text: reply.text }), ...(reply.context === undefined ? {} : { context: [reply.context] }) }
|
package/src/jobs/status-line.ts
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import type { Job, Mod } from '../mod.js'
|
|
2
2
|
import type { Deadline } from '../runtime/deadline.js'
|
|
3
|
+
import { engineOf } from '../runtime/engine.js'
|
|
4
|
+
import { usageOf } from '../runtime/sessions.js'
|
|
5
|
+
import type { Usage } from '../session/session.js'
|
|
3
6
|
import { messageOf } from '../utils/text.js'
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
export type Usage = {
|
|
7
|
-
model: string
|
|
8
|
-
context: { tokens?: number; window: number; percent?: number }
|
|
9
|
-
cost?: { usd: number }
|
|
10
|
-
}
|
|
7
|
+
import { withinDeadline } from './context.js'
|
|
11
8
|
|
|
12
9
|
const deadline: Deadline = { ms: 2000, job: 'statusLine' }
|
|
13
10
|
|
|
@@ -18,30 +15,21 @@ export function statusLine<State extends object = Record<never, never>>(options:
|
|
|
18
15
|
const { text, interval = 10000 } = options
|
|
19
16
|
return (job) => {
|
|
20
17
|
const { mod, announce, reserveName } = job
|
|
21
|
-
const { claude } = mod
|
|
18
|
+
const { claude, router } = engineOf(mod)
|
|
22
19
|
if (!(interval >= 1)) throw new Error(`${mod.name}: the status line interval is ${interval}. Give it in milliseconds, 1 or more.`)
|
|
23
20
|
reserveName('statusLine', '', `${mod.name}: a status line is already added. A mod has one status line: join the texts in one statusLine.`)
|
|
24
21
|
|
|
25
|
-
const textMod = modWithin(job, deadline)
|
|
26
22
|
let shown: string | undefined
|
|
27
23
|
let queue = Promise.resolve()
|
|
28
24
|
const update = async () => {
|
|
29
25
|
try {
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
const next = await text(
|
|
33
|
-
{
|
|
34
|
-
model,
|
|
35
|
-
context: { window, ...(tokens === undefined ? {} : { tokens }), ...(percent === undefined ? {} : { percent }) },
|
|
36
|
-
...(usage.cost === undefined ? {} : { cost: { usd: usage.cost.usd } }),
|
|
37
|
-
},
|
|
38
|
-
textMod,
|
|
39
|
-
)
|
|
26
|
+
const usage = await usageOf(claude)
|
|
27
|
+
const next = await withinDeadline(mod, deadline, 'the status line', () => text(usage, mod))
|
|
40
28
|
if (next === shown) return
|
|
41
29
|
shown = next
|
|
42
30
|
claude.ui.status(next)
|
|
43
31
|
} catch (error) {
|
|
44
|
-
|
|
32
|
+
mod.log(`the status line kept its last text: ${messageOf(error)}`)
|
|
45
33
|
}
|
|
46
34
|
}
|
|
47
35
|
const refresh = () => {
|
|
@@ -49,7 +37,7 @@ export function statusLine<State extends object = Record<never, never>>(options:
|
|
|
49
37
|
}
|
|
50
38
|
|
|
51
39
|
announce('a status line')
|
|
52
|
-
|
|
40
|
+
router.add('session.measure', (e, next) => {
|
|
53
41
|
refresh()
|
|
54
42
|
return next(e)
|
|
55
43
|
})
|
package/src/jobs/tool.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { FromSchema, JSONSchema } from 'json-schema-to-ts'
|
|
2
|
-
import type { Job, Mod } from '../mod.js'
|
|
2
|
+
import type { Disposable, Job, Mod } from '../mod.js'
|
|
3
3
|
import { Validator } from '../vendor.js'
|
|
4
4
|
import { toolDeadline } from '../runtime/deadline.js'
|
|
5
|
+
import { engineOf } from '../runtime/engine.js'
|
|
5
6
|
import { reservedKeys, toolInputOf } from '../runtime/tool-calls.js'
|
|
6
7
|
import { messageOf } from '../utils/text.js'
|
|
7
|
-
import {
|
|
8
|
+
import { withinDeadline } from './context.js'
|
|
8
9
|
|
|
9
10
|
const toolName = /^[A-Za-z0-9_-]{1,64}$/
|
|
10
11
|
|
|
@@ -13,42 +14,47 @@ export function tool<const Schema extends object = { type: 'object' }, State ext
|
|
|
13
14
|
readonly description: string
|
|
14
15
|
readonly inputSchema?: Schema
|
|
15
16
|
readonly execute: (input: Schema extends JSONSchema ? FromSchema<Schema> : never, mod: Mod<State>) => unknown
|
|
16
|
-
}): Job<{ readonly name: string }, State> {
|
|
17
|
+
}): Job<Disposable & { readonly name: string }, State> {
|
|
17
18
|
const { name, description, inputSchema, execute } = options
|
|
18
19
|
return (job) => {
|
|
19
20
|
const { mod, announce, reserveName } = job
|
|
20
|
-
const { claude } = mod
|
|
21
|
+
const { claude, router, report } = engineOf(mod)
|
|
21
22
|
if (!toolName.test(name)) throw new Error(`${mod.name}: "${name}" is not a tool name. Use 1 to 64 letters, digits, "_", or "-".`)
|
|
22
23
|
const properties = (inputSchema as { properties?: object } | undefined)?.properties ?? {}
|
|
23
24
|
const reserved = reservedKeys.filter((key) => Object.hasOwn(properties, key))
|
|
24
25
|
if (reserved.length > 0) throw new Error(`${mod.name}: the ${name} tool's inputSchema has the property ${reserved.join(', ')}, which Claude Code keeps for itself. Rename it.`)
|
|
25
|
-
reserveName('tool', name, `${mod.name}: the tool ${name} is already added. Give each tool its own name.`)
|
|
26
|
+
const named = reserveName('tool', name, `${mod.name}: the tool ${name} is already added. Give each tool its own name.`)
|
|
26
27
|
|
|
27
|
-
const validator = new Validator(
|
|
28
|
+
const validator = new Validator(inputSchema ?? { type: 'object' }, '2020-12', false)
|
|
28
29
|
let registered: string | undefined
|
|
29
30
|
claude.tool.register({ name, description, ...(inputSchema === undefined ? {} : { inputSchema: inputSchema as Record<string, unknown> }) }).then(
|
|
30
31
|
(answer) => {
|
|
31
32
|
registered = answer.tool
|
|
32
33
|
},
|
|
33
34
|
(error: unknown) => {
|
|
34
|
-
|
|
35
|
+
report({ id: `tool:${name}`, message: `The ${name} tool is not added`, error })
|
|
35
36
|
},
|
|
36
37
|
)
|
|
37
38
|
announce(`the ${name} tool`)
|
|
38
39
|
|
|
39
|
-
const
|
|
40
|
-
claude.on('tool.call', async (e, next) => {
|
|
40
|
+
const answering = router.add('tool.call', async (e, next) => {
|
|
41
41
|
if (e.tool !== registered) return next(e)
|
|
42
42
|
const input = toolInputOf(e)
|
|
43
43
|
const { valid, errors } = validator.validate(input)
|
|
44
44
|
if (!valid) return { deny: `The ${name} tool input is not valid:\n${errors.map((error) => `- ${error.instanceLocation}: ${error.error}`).join('\n')}` }
|
|
45
45
|
try {
|
|
46
|
-
return { result: resultOf(await execute(input as Parameters<typeof execute>[0],
|
|
46
|
+
return { result: resultOf(await withinDeadline(mod, toolDeadline, `the ${name} tool`, () => execute(input as Parameters<typeof execute>[0], mod))) }
|
|
47
47
|
} catch (error) {
|
|
48
48
|
return { deny: `The ${name} tool failed: ${messageOf(error)}` }
|
|
49
49
|
}
|
|
50
50
|
})
|
|
51
|
-
return {
|
|
51
|
+
return {
|
|
52
|
+
name: `mcp__${claude.plugin.name}__${name}`,
|
|
53
|
+
dispose() {
|
|
54
|
+
answering.dispose()
|
|
55
|
+
named.dispose()
|
|
56
|
+
},
|
|
57
|
+
}
|
|
52
58
|
}
|
|
53
59
|
}
|
|
54
60
|
|