@cmodjs/core 1.0.0 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/commands.md +22 -11
- package/docs/dependencies.md +6 -4
- package/docs/hooks.md +26 -3
- package/docs/index.md +1 -1
- package/docs/install-steps.md +5 -3
- package/docs/mod.md +29 -3
- package/docs/permissions.md +4 -4
- package/docs/session.md +126 -63
- package/docs/testing.md +7 -5
- package/docs/ui.md +9 -1
- package/jobs/program.d.ts.map +1 -1
- package/jobs/program.js +7 -2
- package/mod.d.ts +30 -6
- package/mod.d.ts.map +1 -1
- package/mod.js +34 -1
- package/package.json +1 -1
- package/records.d.ts +4 -1
- package/records.d.ts.map +1 -1
- package/records.js +6 -1
- package/register.d.ts.map +1 -1
- package/register.js +17 -0
- package/runtime/announce.d.ts.map +1 -1
- package/runtime/announce.js +2 -0
- package/runtime/before.d.ts +32 -0
- package/runtime/before.d.ts.map +1 -0
- package/runtime/before.js +214 -0
- package/runtime/claude.d.ts +2 -1
- package/runtime/claude.d.ts.map +1 -1
- package/runtime/conversations.d.ts +1 -0
- package/runtime/conversations.d.ts.map +1 -1
- package/runtime/conversations.js +333 -61
- package/runtime/dependencies.js +1 -1
- package/runtime/engine.d.ts +2 -6
- package/runtime/engine.d.ts.map +1 -1
- package/runtime/grants.d.ts.map +1 -1
- package/runtime/grants.js +8 -8
- package/runtime/hooks.d.ts +1 -1
- package/runtime/hooks.d.ts.map +1 -1
- package/runtime/hooks.js +19 -4
- package/runtime/installer.d.ts +1 -1
- package/runtime/installer.d.ts.map +1 -1
- package/runtime/installer.js +6 -5
- package/runtime/lifecycle.d.ts.map +1 -1
- package/runtime/lifecycle.js +130 -59
- package/runtime/metadata.d.ts.map +1 -1
- package/runtime/metadata.js +5 -4
- package/runtime/sessions.d.ts +26 -4
- package/runtime/sessions.d.ts.map +1 -1
- package/runtime/sessions.js +315 -117
- package/runtime/toaster.d.ts +5 -12
- package/runtime/toaster.d.ts.map +1 -1
- package/runtime/toaster.js +158 -25
- package/runtime/ui.d.ts.map +1 -1
- package/runtime/ui.js +26 -6
- package/session/context/context.d.ts +3 -1
- package/session/context/context.d.ts.map +1 -1
- package/session/context/turn/message/message.d.ts +1 -0
- package/session/context/turn/message/message.d.ts.map +1 -1
- package/session/context/turn/message/message.js +42 -1
- package/session/context/turn/turn.d.ts +2 -1
- package/session/context/turn/turn.d.ts.map +1 -1
- package/session/conversation.d.ts +20 -11
- package/session/conversation.d.ts.map +1 -1
- package/session/conversation.js +50 -27
- package/session/goal/goal.d.ts +3 -2
- package/session/goal/goal.d.ts.map +1 -1
- package/session/goal/task.d.ts +4 -4
- package/session/goal/task.d.ts.map +1 -1
- package/session/session.d.ts +80 -21
- package/session/session.d.ts.map +1 -1
- package/session/session.js +1 -0
- package/src/jobs/program.ts +7 -2
- package/src/mod.ts +48 -5
- package/src/records.ts +6 -1
- package/src/register.ts +17 -0
- package/src/runtime/announce.ts +2 -0
- package/src/runtime/before.ts +200 -0
- package/src/runtime/claude.ts +3 -0
- package/src/runtime/conversations.ts +337 -60
- package/src/runtime/dependencies.ts +1 -1
- package/src/runtime/engine.ts +2 -1
- package/src/runtime/grants.ts +8 -10
- package/src/runtime/hooks.ts +20 -4
- package/src/runtime/installer.ts +6 -5
- package/src/runtime/lifecycle.ts +136 -56
- package/src/runtime/metadata.ts +5 -4
- package/src/runtime/sessions.ts +316 -111
- package/src/runtime/toaster.ts +183 -45
- package/src/runtime/ui.ts +23 -4
- package/src/session/context/context.ts +3 -1
- package/src/session/context/turn/message/message.ts +39 -1
- package/src/session/context/turn/turn.ts +2 -1
- package/src/session/conversation.ts +56 -31
- package/src/session/goal/goal.ts +3 -2
- package/src/session/goal/task.ts +4 -4
- package/src/session/session.ts +62 -15
- package/src/testing/fake-claude.ts +4 -2
- package/src/testing/fake-sessions.ts +94 -20
- package/src/testing.ts +27 -3
- package/src/utils/call-effects.ts +2 -2
- package/src/utils/disposable.ts +3 -0
- package/src/utils/matcher.ts +1 -1
- package/src/utils/paths.ts +6 -3
- package/testing/fake-claude.d.ts +1 -0
- package/testing/fake-claude.d.ts.map +1 -1
- package/testing/fake-claude.js +3 -1
- package/testing/fake-sessions.d.ts +8 -1
- package/testing/fake-sessions.d.ts.map +1 -1
- package/testing/fake-sessions.js +97 -18
- package/testing.d.ts +1 -0
- package/testing.d.ts.map +1 -1
- package/testing.js +23 -2
- package/utils/call-effects.d.ts +1 -0
- package/utils/call-effects.d.ts.map +1 -1
- package/utils/call-effects.js +1 -1
- package/utils/disposable.d.ts +4 -0
- package/utils/disposable.d.ts.map +1 -0
- package/utils/disposable.js +1 -0
- package/utils/matcher.js +1 -1
- package/utils/paths.d.ts +2 -1
- package/utils/paths.d.ts.map +1 -1
- package/utils/paths.js +6 -3
package/src/testing.ts
CHANGED
|
@@ -23,7 +23,9 @@ 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 { foldAnswers, installBefore } from './runtime/before.js'
|
|
26
27
|
import type { RoutedEvent } from './runtime/hooks.js'
|
|
28
|
+
import { createRouter } from './runtime/router.js'
|
|
27
29
|
import { toolInputOf } from './runtime/tool-calls.js'
|
|
28
30
|
import { createLifecycle } from './runtime/lifecycle.js'
|
|
29
31
|
import { MissingOptions } from './runtime/options.js'
|
|
@@ -33,11 +35,13 @@ import type { Message } from './session/context/turn/message/message.js'
|
|
|
33
35
|
import { Note } from './session/context/turn/message/note.js'
|
|
34
36
|
import { Prompt } from './session/context/turn/message/prompt.js'
|
|
35
37
|
import { Mod, Person } from './session/context/turn/message/sender.js'
|
|
38
|
+
import type { SessionChange } from './session/conversation.js'
|
|
39
|
+
import type { BeforeEvent } from './session/session.js'
|
|
36
40
|
import { calledAnswer, fakeClaude, type Fakes, type Shown as ShownByClaude, type TestCall } from './testing/fake-claude.js'
|
|
37
41
|
import { elements, findElement, rowsOf } from './testing/fake-elements.js'
|
|
38
42
|
import { fakeFiles } from './testing/fake-files.js'
|
|
39
43
|
import { fakeSessions, type FakeSession, type FakeSessions, type MessageDraft } from './testing/fake-sessions.js'
|
|
40
|
-
import { listed } from './utils/text.js'
|
|
44
|
+
import { listed, messageOf } from './utils/text.js'
|
|
41
45
|
import { componentOf, type ComponentPlace, type PlaceProps } from './ui/places.js'
|
|
42
46
|
|
|
43
47
|
export type { Fakes, TestCall } from './testing/fake-claude.js'
|
|
@@ -72,7 +76,7 @@ export type TestedMod<State extends object> = {
|
|
|
72
76
|
add(message: MessageDraft, options?: { readonly task?: string }): Promise<Message[]>
|
|
73
77
|
endTurn(): Promise<void>
|
|
74
78
|
compact(): Promise<void>
|
|
75
|
-
startTask(task: { readonly title: string; readonly agentType?: string; readonly model?: string; readonly openedBy?: string }): Promise<string>
|
|
79
|
+
startTask(task: { readonly title: string; readonly agentType?: string; readonly model?: string; readonly openedBy?: string; readonly parent?: string }): Promise<string>
|
|
76
80
|
lines(paneId: string): Promise<string[]>
|
|
77
81
|
lines<P extends ComponentPlace>(place: P, props: Omit<PlaceProps<P>, 'Default'>, requestId?: string): Promise<string[]>
|
|
78
82
|
type(line: string): Promise<CommandReply>
|
|
@@ -111,6 +115,7 @@ export function testMod<State extends object, Declared extends Options = Options
|
|
|
111
115
|
fake.claude.session.root = async () => projectRoot
|
|
112
116
|
fake.claude.session.cwd = async () => cwd
|
|
113
117
|
const conversations = fakeSessions({
|
|
118
|
+
callTool: (args: Parameters<Claude["tool"]["call"]>[0]) => fake.claude.tool.call(args),
|
|
114
119
|
project: () => projectRoot,
|
|
115
120
|
given: options.sessions ?? [],
|
|
116
121
|
push: (mod, session, change) => {
|
|
@@ -171,9 +176,24 @@ export function testMod<State extends object, Declared extends Options = Options
|
|
|
171
176
|
if (lifecycle.phase !== 'active') throw failure ?? new Error(`${name} did not start: the lifecycle is ${lifecycle.phase}.`)
|
|
172
177
|
}
|
|
173
178
|
|
|
179
|
+
const cmodPlugin = createRouter()
|
|
180
|
+
const askMod = async (event: BeforeEvent, change: SessionChange) => {
|
|
181
|
+
const asked = { to: name, method: conversationMethods.before, input: { session: sessionId, event, change }, caller: cmodPluginName } as unknown as Frozen<Args<'cmod.call'>>
|
|
182
|
+
return calledAnswer(cmodPluginName, await lifecycle.route('cmod.call', asked, async () => ({ deny: notInstalled(name) })))
|
|
183
|
+
}
|
|
184
|
+
installBefore({
|
|
185
|
+
claude: fake.claude,
|
|
186
|
+
router: cmodPlugin,
|
|
187
|
+
hooks: {
|
|
188
|
+
place: async () => conversations.place(),
|
|
189
|
+
wants: async (event) => conversations.wants(name, event),
|
|
190
|
+
decide: (event, change) => foldAnswers(event, change, [(latest) => askMod(event, latest)], (error) => fake.claude.ui.log(`cmod: passed the ${event} on without ${name}'s answer: ${messageOf(error)}`, { to: 'debug' })),
|
|
191
|
+
},
|
|
192
|
+
})
|
|
193
|
+
|
|
174
194
|
const route = async <N extends RoutedEvent>(event: N, input: unknown, below: unknown): Promise<EventResult<N>> => {
|
|
175
195
|
await start()
|
|
176
|
-
return
|
|
196
|
+
return cmodPlugin.dispatch(event, input as Frozen<Args<N>>, (passed) => lifecycle.route(event, passed, async (e) => (below ?? claudeCodeAnswer(event, e)) as EventResult<N>))
|
|
177
197
|
}
|
|
178
198
|
|
|
179
199
|
const drawPane = async (paneId: string) => {
|
|
@@ -360,6 +380,10 @@ function fakeDependencies(caller: string, dependencies: NonNullable<TestOptions<
|
|
|
360
380
|
}
|
|
361
381
|
|
|
362
382
|
function claudeCodeAnswer(event: RoutedEvent, e: unknown): unknown {
|
|
383
|
+
if (event === 'agent.spawn') {
|
|
384
|
+
const { model, parentModel } = e as Args<'agent.spawn'>
|
|
385
|
+
return { agentId: 'agent-1', model: model ?? parentModel }
|
|
386
|
+
}
|
|
363
387
|
if (event !== 'prompt.submit') return {}
|
|
364
388
|
const { text, context, origin } = e as Args<'prompt.submit'>
|
|
365
389
|
return { text, ...(context === undefined ? {} : { context }), origin }
|
|
@@ -18,9 +18,9 @@ export type CallEffects = {
|
|
|
18
18
|
subagent: string | undefined
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
export function callEffects(use: CallBase, workspace: { cwd: string; home: string; fs: FileSystem }): CallEffects {
|
|
21
|
+
export function callEffects(use: CallBase, workspace: { cwd: string; home: string; configRoot?: string; fs: FileSystem }): CallEffects {
|
|
22
22
|
const { fs } = workspace
|
|
23
|
-
const absolute = (path: string, folder = workspace.cwd) => resolve(folder, expandHome(path, workspace.home))
|
|
23
|
+
const absolute = (path: string, folder = workspace.cwd) => resolve(folder, expandHome(path, workspace.home, workspace.configRoot))
|
|
24
24
|
const access = (path: string): FileAccess => ({ path: absolute(path), contents: undefined })
|
|
25
25
|
const effects: CallEffects = { shell: undefined, reads: [], writes: [], urls: [], subagent: undefined }
|
|
26
26
|
if (use.tool === 'Bash') {
|
package/src/utils/matcher.ts
CHANGED
|
@@ -13,7 +13,7 @@ export type Matcher<Value> = Value extends string
|
|
|
13
13
|
export type SentMatcher = string | number | boolean | null | { readonly regexp: string; readonly flags: string } | { readonly [key: string]: SentMatcher } | readonly SentMatcher[]
|
|
14
14
|
|
|
15
15
|
export function matches(value: unknown, matcher: unknown): boolean {
|
|
16
|
-
if (matcher instanceof RegExp) return matcher.test(String(value))
|
|
16
|
+
if (matcher instanceof RegExp) return value !== undefined && value !== null && matcher.test(String(value))
|
|
17
17
|
if (Array.isArray(matcher)) return matcher.some((each) => matches(value, each))
|
|
18
18
|
if (typeof matcher !== 'object' || matcher === null) return value === matcher
|
|
19
19
|
if (Array.isArray(value)) return value.some((each) => matches(each, matcher))
|
package/src/utils/paths.ts
CHANGED
|
@@ -20,9 +20,12 @@ export type FileSystem = {
|
|
|
20
20
|
exists: (path: string) => Promise<boolean>
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
export
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
export const homePrefix = /^(~|\$HOME|\$CLAUDE_CONFIG_DIR)(?=\/|$)/
|
|
24
|
+
|
|
25
|
+
export function expandHome(path: string, home: string, configRoot = `${home}/.claude`): string {
|
|
26
|
+
const prefix = homePrefix.exec(path)?.[0]
|
|
27
|
+
if (prefix === undefined) return path
|
|
28
|
+
return `${prefix === '$CLAUDE_CONFIG_DIR' ? configRoot : home}${path.slice(prefix.length)}`
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
export function relativePath(base: string, path: string): string | undefined {
|
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;AAIlD,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,SAAS,OAAO,EAAE,CAAA;CAClC,CAAA;AAED,MAAM,MAAM,KAAK,GAAG;IAClB,OAAO,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,CAAA;KAAE,CAAA;IAC/E,EAAE,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAA;KAAE,CAAA;IAC7J,IAAI,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAA;KAAE,CAAA;IACzC,QAAQ,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAA;KAAE,CAAA;IAC/C,MAAM,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAA;KAAE,CAAA;IAC3C,EAAE,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAA;KAAE,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
|
@@ -71,6 +71,7 @@ export function fakeClaude(plugin) {
|
|
|
71
71
|
calls.push({ call: 'ui.open', args: [pane] });
|
|
72
72
|
const answer = fakes.ui.open === undefined ? { isPlaced: true } : await fakes.ui.open(pane);
|
|
73
73
|
if (answer.isPlaced) {
|
|
74
|
+
shown.openPanes.delete(pane.id);
|
|
74
75
|
shown.openPanes.add(pane.id);
|
|
75
76
|
unplacedPanes.delete(pane.id);
|
|
76
77
|
if (pane.focus === true)
|
|
@@ -89,11 +90,12 @@ export function fakeClaude(plugin) {
|
|
|
89
90
|
focusedPane = undefined;
|
|
90
91
|
},
|
|
91
92
|
panes: async () => [
|
|
92
|
-
...[...shown.openPanes].map((id) => ({ id, title: id, isShown:
|
|
93
|
+
...[...shown.openPanes].map((id) => ({ id, title: id, isShown: id === [...shown.openPanes].at(-1), isFocused: id === focusedPane, isPlaced: true })),
|
|
93
94
|
...[...unplacedPanes].map((id) => ({ id, title: id, isShown: false, isFocused: false, isPlaced: false })),
|
|
94
95
|
],
|
|
95
96
|
scroll: rejected('ui.scroll', () => fakes.ui.scroll ?? (async () => ({}))),
|
|
96
97
|
copy: rejected('ui.copy', () => fakes.ui.copy ?? (async () => ({ isCopied: true }))),
|
|
98
|
+
focus: rejected('ui.focus', () => fakes.ui.focus ?? (async () => ({}))),
|
|
97
99
|
resolve: () => elements,
|
|
98
100
|
},
|
|
99
101
|
process: {
|
|
@@ -9,6 +9,8 @@ import { Skill } from '../session/context/turn/message/skill.js';
|
|
|
9
9
|
import { ToolCall } from '../session/context/turn/message/tool-call.js';
|
|
10
10
|
import { ToolResult } from '../session/context/turn/message/tool-result.js';
|
|
11
11
|
import { type SessionChange, type SessionData } from '../session/conversation.js';
|
|
12
|
+
import type { Place } from '../runtime/before.js';
|
|
13
|
+
import type { Claude } from '../runtime/claude.js';
|
|
12
14
|
export type MessageDraft = {
|
|
13
15
|
readonly type: typeof Prompt;
|
|
14
16
|
readonly text: string;
|
|
@@ -50,6 +52,8 @@ export type FakeSession = Partial<Omit<SessionData, 'id'>> & {
|
|
|
50
52
|
export type FakeSessions = {
|
|
51
53
|
answer(caller: string, method: string, input: unknown): unknown;
|
|
52
54
|
add(draft: MessageDraft, task?: string): Message[];
|
|
55
|
+
place(): Place;
|
|
56
|
+
wants(mod: string, event: string): boolean;
|
|
53
57
|
endTurn(): void;
|
|
54
58
|
compact(): void;
|
|
55
59
|
startTask(task: {
|
|
@@ -57,18 +61,21 @@ export type FakeSessions = {
|
|
|
57
61
|
readonly agentType?: string;
|
|
58
62
|
readonly model?: string;
|
|
59
63
|
readonly openedBy?: string;
|
|
64
|
+
readonly parent?: string;
|
|
60
65
|
}): string;
|
|
61
66
|
start(id: string): void;
|
|
62
67
|
readonly sent: {
|
|
63
68
|
readonly session: string;
|
|
69
|
+
readonly task?: string;
|
|
64
70
|
readonly type: string;
|
|
65
71
|
readonly text: string;
|
|
66
72
|
readonly as?: string;
|
|
67
73
|
}[];
|
|
68
74
|
};
|
|
69
|
-
export declare function fakeSessions({ project, given, push }: {
|
|
75
|
+
export declare function fakeSessions({ project, given, push, callTool }: {
|
|
70
76
|
readonly project: () => string;
|
|
71
77
|
readonly given: readonly FakeSession[];
|
|
72
78
|
readonly push: (mod: string, session: string, change: SessionChange) => void;
|
|
79
|
+
readonly callTool: Claude['tool']['call'];
|
|
73
80
|
}): FakeSessions;
|
|
74
81
|
//# sourceMappingURL=fake-sessions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fake-sessions.d.ts","sourceRoot":"","sources":["../src/testing/fake-sessions.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,6CAA6C,CAAA;AACtE,OAAO,EAAE,OAAO,EAAqC,MAAM,4CAA4C,CAAA;AACvG,OAAO,EAAE,IAAI,EAAE,MAAM,yCAAyC,CAAA;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAA;AAClE,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAA;AAClE,OAAO,EAAE,KAAK,EAAE,MAAM,0CAA0C,CAAA;AAChE,OAAO,EAAyB,KAAK,MAAM,EAAE,MAAM,2CAA2C,CAAA;
|
|
1
|
+
{"version":3,"file":"fake-sessions.d.ts","sourceRoot":"","sources":["../src/testing/fake-sessions.ts"],"names":[],"mappings":"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"}
|
package/testing/fake-sessions.js
CHANGED
|
@@ -6,17 +6,20 @@ import { Notice } from '../session/context/turn/message/notice.js';
|
|
|
6
6
|
import { Prompt } from '../session/context/turn/message/prompt.js';
|
|
7
7
|
import { Reply } from '../session/context/turn/message/reply.js';
|
|
8
8
|
import { Mod, Person, Schedule } from '../session/context/turn/message/sender.js';
|
|
9
|
+
import { GoalChange } from '../session/goal/goal.js';
|
|
9
10
|
import { Task } from '../session/goal/task.js';
|
|
10
11
|
import { Skill } from '../session/context/turn/message/skill.js';
|
|
11
12
|
import { ToolCall } from '../session/context/turn/message/tool-call.js';
|
|
12
13
|
import { ToolResult } from '../session/context/turn/message/tool-result.js';
|
|
13
14
|
import { createConversation, emptyConversation, searchLimit } from '../session/conversation.js';
|
|
14
|
-
import { conversationMethods, isHeardBy } from '../runtime/sessions.js';
|
|
15
|
+
import { conversationMethods, everySession, isHeardBy, resultOf } from '../runtime/sessions.js';
|
|
15
16
|
import { receivedMatcher } from '../utils/matcher.js';
|
|
16
17
|
import { firstLine, messageOf } from '../utils/text.js';
|
|
17
|
-
export function fakeSessions({ project, given, push }) {
|
|
18
|
+
export function fakeSessions({ project, given, push, callTool }) {
|
|
18
19
|
const kept = new Map();
|
|
19
20
|
const heard = new Map();
|
|
21
|
+
const watches = new Map();
|
|
22
|
+
const goals = new Map();
|
|
20
23
|
const sent = [];
|
|
21
24
|
let current = '';
|
|
22
25
|
let clock = Date.parse('2026-01-01T00:00:00.000Z');
|
|
@@ -39,7 +42,7 @@ export function fakeSessions({ project, given, push }) {
|
|
|
39
42
|
session.tasks.set(change.task.id, change.task);
|
|
40
43
|
session.data = { ...session.data, lastAt: change.type === Message ? change.message.at : session.data.lastAt, ...(session.data.title === '' && change.type === Message && change.message.type === Prompt && change.message.from.type === Person ? { title: firstLine(change.message.text) } : {}) };
|
|
41
44
|
for (const [mod, sessions] of heard) {
|
|
42
|
-
if ((sessions.get(session.data.id) ?? []).some(({ event, matcher }) => isHeardBy(event, matcher, change)))
|
|
45
|
+
if ([...(sessions.get(session.data.id) ?? []), ...(sessions.get(everySession) ?? [])].some(({ event, matcher }) => isHeardBy(event, matcher, change)))
|
|
43
46
|
push(mod, session.data.id, change);
|
|
44
47
|
}
|
|
45
48
|
}
|
|
@@ -63,6 +66,23 @@ export function fakeSessions({ project, given, push }) {
|
|
|
63
66
|
return session;
|
|
64
67
|
};
|
|
65
68
|
const currentSession = () => sessionOf(current);
|
|
69
|
+
const place = () => {
|
|
70
|
+
const { session, context, contextOpenedAt, turn, tasks, taskTurns } = currentSession().conversation.state;
|
|
71
|
+
return { session, context, contextOpenedAt, ...(turn === undefined ? {} : { turn }), tasks, taskTurns };
|
|
72
|
+
};
|
|
73
|
+
const taskOf = (session, id) => {
|
|
74
|
+
const task = session.tasks.get(id);
|
|
75
|
+
if (task === undefined)
|
|
76
|
+
throw new Error(`Session ${session.data.id} has no task ${id}. Start it with tested.startTask.`);
|
|
77
|
+
return task;
|
|
78
|
+
};
|
|
79
|
+
const turnsOf = (session, task) => [...session.turns.values()].filter((turn) => turn.prompt.task === (typeof task === 'string' ? task : undefined));
|
|
80
|
+
const isUnder = (session, task, ancestor) => {
|
|
81
|
+
for (let at = task, steps = 0; at !== undefined && steps < 64; at = session?.tasks.get(at)?.parent, steps += 1)
|
|
82
|
+
if (at === ancestor)
|
|
83
|
+
return true;
|
|
84
|
+
return false;
|
|
85
|
+
};
|
|
66
86
|
const lastOf = (items, last) => (typeof last === 'number' ? items.slice(-last) : [...items]);
|
|
67
87
|
const isInScope = (session, request) => (request['everyProject'] === true || session.data.project === project()) && session.data.lastAt >= (typeof request['since'] === 'string' ? request['since'] : '') && (request['running'] !== true || session.data.isRunning);
|
|
68
88
|
const methods = {
|
|
@@ -74,10 +94,21 @@ export function fakeSessions({ project, given, push }) {
|
|
|
74
94
|
const text = String(request['text']);
|
|
75
95
|
if ([...text].length < 3)
|
|
76
96
|
throw new Error('cmod history search takes at least 3 characters.');
|
|
97
|
+
const inRecord = (message) => {
|
|
98
|
+
const session = kept.get(message.session);
|
|
99
|
+
const task = request['task'];
|
|
100
|
+
if (request['session'] !== undefined && message.session !== request['session'])
|
|
101
|
+
return false;
|
|
102
|
+
if (typeof task === 'string' && (message.task === undefined || !isUnder(session, message.task, task) || (request['isGoal'] === true && message.task === task)))
|
|
103
|
+
return false;
|
|
104
|
+
if (task === undefined && request['isGoal'] === true && message.task === undefined)
|
|
105
|
+
return false;
|
|
106
|
+
return (request['turn'] === undefined || message.turn === request['turn']) && (request['context'] === undefined || message.context === request['context']);
|
|
107
|
+
};
|
|
77
108
|
const found = [...kept.values()]
|
|
78
|
-
.filter((session) => isInScope(session, request))
|
|
109
|
+
.filter((session) => request['session'] !== undefined || isInScope(session, request))
|
|
79
110
|
.flatMap((session) => session.messages)
|
|
80
|
-
.filter((message) => searchText(message).toLowerCase().includes(text.toLowerCase()))
|
|
111
|
+
.filter((message) => inRecord(message) && searchText(message).toLowerCase().includes(text.toLowerCase()))
|
|
81
112
|
.toSorted((first, second) => second.at.localeCompare(first.at));
|
|
82
113
|
const before = isObject(request['before']) ? request['before'] : undefined;
|
|
83
114
|
const start = before === undefined ? 0 : found.findIndex((message) => message.session === before['session'] && message.id === before['id']) + 1;
|
|
@@ -89,15 +120,66 @@ export function fakeSessions({ project, given, push }) {
|
|
|
89
120
|
const after = typeof request['after'] === 'string' ? fits.findIndex((message) => message.id === request['after']) + 1 : 0;
|
|
90
121
|
return lastOf(fits.slice(after), request['last']);
|
|
91
122
|
},
|
|
92
|
-
[conversationMethods.turns]: (request) => lastOf(
|
|
93
|
-
[conversationMethods.contexts]
|
|
94
|
-
|
|
95
|
-
|
|
123
|
+
[conversationMethods.turns]: (request) => lastOf(turnsOf(sessionOf(String(request['session'])), request['task']).filter((turn) => request['context'] === undefined || turn.context === request['context']), request['last']),
|
|
124
|
+
[conversationMethods.contexts](request) {
|
|
125
|
+
const session = sessionOf(String(request['session']));
|
|
126
|
+
if (typeof request['task'] !== 'string')
|
|
127
|
+
return [...session.contexts.values()];
|
|
128
|
+
const task = taskOf(session, request['task']);
|
|
129
|
+
return [{ number: session.conversation.state.tasks[task.id]?.context ?? 1, openedAt: task.openedAt, ...(task.closedAt === undefined ? {} : { closedAt: task.closedAt }) }];
|
|
130
|
+
},
|
|
131
|
+
[conversationMethods.tasks]: (request) => [...sessionOf(String(request['session'])).tasks.values()].filter((task) => request['all'] === true || task.parent === request['task']),
|
|
132
|
+
[conversationMethods.goal](request) {
|
|
133
|
+
const session = sessionOf(String(request['session']));
|
|
134
|
+
const task = request['task'];
|
|
135
|
+
if (typeof task !== 'string')
|
|
136
|
+
return { text: session.conversation.state.goal ?? '' };
|
|
137
|
+
return { text: goals.get(task) ?? turnsOf(session, task)[0]?.prompt.text ?? '' };
|
|
138
|
+
},
|
|
96
139
|
[conversationMethods.updateGoal](request) {
|
|
97
140
|
const session = sessionOf(String(request['session']));
|
|
98
|
-
|
|
141
|
+
const task = typeof request['task'] === 'string' ? request['task'] : undefined;
|
|
142
|
+
const text = String(request['text']);
|
|
143
|
+
if (task === undefined)
|
|
144
|
+
record(session, session.conversation.changeGoal(text));
|
|
145
|
+
else {
|
|
146
|
+
goals.set(task, text);
|
|
147
|
+
record(session, [{ type: GoalChange, goal: { text, task } }]);
|
|
148
|
+
}
|
|
99
149
|
return null;
|
|
100
150
|
},
|
|
151
|
+
[conversationMethods.watchBefore](request, caller) {
|
|
152
|
+
const targets = watches.get(caller) ?? new Map();
|
|
153
|
+
targets.set(String(request['session']), (Array.isArray(request['subscriptions']) ? request['subscriptions'] : []).map(({ event }) => event));
|
|
154
|
+
watches.set(caller, targets);
|
|
155
|
+
return null;
|
|
156
|
+
},
|
|
157
|
+
async [conversationMethods.act](request) {
|
|
158
|
+
const session = sessionOf(String(request['session']));
|
|
159
|
+
const action = String(request['action']);
|
|
160
|
+
const input = isObject(request['input']) ? request['input'] : {};
|
|
161
|
+
const task = typeof request['task'] === 'string' ? request['task'] : undefined;
|
|
162
|
+
const isLive = task === undefined ? session.data.isRunning : session.data.isRunning && taskOf(session, task).closedAt === undefined;
|
|
163
|
+
if (action === 'usage')
|
|
164
|
+
return { model: task === undefined ? session.data.model : (taskOf(session, task).model ?? session.data.model), context: {} };
|
|
165
|
+
if (action === 'clients')
|
|
166
|
+
return isLive ? ['terminal'] : [];
|
|
167
|
+
if (!isLive)
|
|
168
|
+
throw new Error(`session ${task ?? session.data.id} has ended`);
|
|
169
|
+
if (action === 'send') {
|
|
170
|
+
sent.push({ session: session.data.id, ...(task === undefined ? {} : { task }), type: String(input['type']), text: String(input['text']), ...(typeof input['as'] === 'string' ? { as: input['as'] } : {}) });
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
if (action === 'startTask') {
|
|
174
|
+
const id = `task-${(ids += 1)}`;
|
|
175
|
+
const data = { id, title: typeof input['name'] === 'string' ? input['name'] : '', agentType: typeof input['agentType'] === 'string' ? input['agentType'] : 'general-purpose', openedBy: '', openedAt: now(), ...(typeof input['model'] === 'string' ? { model: input['model'] } : {}) };
|
|
176
|
+
record(session, session.conversation.startTask(data));
|
|
177
|
+
return data;
|
|
178
|
+
}
|
|
179
|
+
if (session.data.id === current)
|
|
180
|
+
return resultOf(place(), await callTool({ ...input['input'], tool: String(input['name']) }));
|
|
181
|
+
return { id: '', type: ToolResult, at: now(), session: session.data.id, context: session.conversation.state.context, callId: '', text: '', isError: false };
|
|
182
|
+
},
|
|
101
183
|
[conversationMethods.subscribe](request, caller) {
|
|
102
184
|
const list = Array.isArray(request['subscriptions']) ? request['subscriptions'] : [];
|
|
103
185
|
const sessions = heard.get(caller) ?? new Map();
|
|
@@ -105,10 +187,6 @@ export function fakeSessions({ project, given, push }) {
|
|
|
105
187
|
heard.set(caller, sessions);
|
|
106
188
|
return null;
|
|
107
189
|
},
|
|
108
|
-
[conversationMethods.send](request) {
|
|
109
|
-
sent.push({ session: String(request['session']), type: String(request['type']), text: String(request['text']), ...(typeof request['as'] === 'string' ? { as: request['as'] } : {}) });
|
|
110
|
-
return null;
|
|
111
|
-
},
|
|
112
190
|
};
|
|
113
191
|
return {
|
|
114
192
|
answer(caller, method, input) {
|
|
@@ -123,19 +201,20 @@ export function fakeSessions({ project, given, push }) {
|
|
|
123
201
|
}
|
|
124
202
|
},
|
|
125
203
|
add: (draft, task) => add(currentSession(), draft, task),
|
|
204
|
+
place,
|
|
205
|
+
wants: (mod, event) => [current, everySession].some((target) => (watches.get(mod)?.get(target) ?? []).includes(event)),
|
|
126
206
|
endTurn() {
|
|
127
207
|
const session = currentSession();
|
|
128
208
|
record(session, session.conversation.add({ id: `turn-end-${(ids += 1)}`, at: now(), type: 'system', name: 'turn_duration', isMeta: false, isSummary: false, content: [] }));
|
|
129
209
|
},
|
|
130
210
|
compact() {
|
|
131
211
|
const session = currentSession();
|
|
132
|
-
|
|
133
|
-
record(session, [...session.conversation.endContext(at), ...session.conversation.startContext(at)]);
|
|
212
|
+
record(session, session.conversation.add({ id: `compact-${(ids += 1)}`, at: now(), type: 'system', name: 'compact_boundary', isMeta: false, isSummary: false, content: [] }));
|
|
134
213
|
},
|
|
135
|
-
startTask({ title, agentType = 'general-purpose', model, openedBy = '' }) {
|
|
214
|
+
startTask({ title, agentType = 'general-purpose', model, openedBy = '', parent }) {
|
|
136
215
|
const session = currentSession();
|
|
137
216
|
const id = `task-${(ids += 1)}`;
|
|
138
|
-
record(session, session.conversation.startTask({ id, title, agentType, openedBy, openedAt: now(), ...(model === undefined ? {} : { model }) }));
|
|
217
|
+
record(session, session.conversation.startTask({ id, title, agentType, openedBy, openedAt: now(), ...(model === undefined ? {} : { model }), ...(parent === undefined ? {} : { parent }) }));
|
|
139
218
|
return id;
|
|
140
219
|
},
|
|
141
220
|
start(id) {
|
package/testing.d.ts
CHANGED
|
@@ -49,6 +49,7 @@ export type TestedMod<State extends object> = {
|
|
|
49
49
|
readonly agentType?: string;
|
|
50
50
|
readonly model?: string;
|
|
51
51
|
readonly openedBy?: string;
|
|
52
|
+
readonly parent?: string;
|
|
52
53
|
}): Promise<string>;
|
|
53
54
|
lines(paneId: string): Promise<string[]>;
|
|
54
55
|
lines<P extends ComponentPlace>(place: P, props: Omit<PlaceProps<P>, 'Default'>, requestId?: string): Promise<string[]>;
|
package/testing.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["src/testing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,IAAI,EACJ,iBAAiB,EACjB,gBAAgB,EAGhB,WAAW,EASX,eAAe,EAGhB,MAAM,aAAa,CAAA;AACpB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAC3D,OAAO,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACnD,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["src/testing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,IAAI,EACJ,iBAAiB,EACjB,gBAAgB,EAGhB,WAAW,EASX,eAAe,EAGhB,MAAM,aAAa,CAAA;AACpB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAC3D,OAAO,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACnD,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAKzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAMrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,2CAA2C,CAAA;AAMxE,OAAO,EAA4B,KAAK,KAAK,EAAE,KAAK,KAAK,IAAI,aAAa,EAAE,KAAK,QAAQ,EAAE,MAAM,0BAA0B,CAAA;AAG3H,OAAO,EAAgB,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAEjH,OAAO,EAAe,KAAK,cAAc,EAAE,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAElF,YAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAA;AAC/D,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAC3E,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACrD,YAAY,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAEtD,MAAM,MAAM,KAAK,GAAG,aAAa,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC,CAAA;CAAE,CAAA;AAEnH,MAAM,MAAM,WAAW,CAAC,KAAK,SAAS,MAAM,EAAE,QAAQ,SAAS,OAAO,GAAG,OAAO,IAAI;IAClF,QAAQ,CAAC,KAAK,CAAC,EAAE;QAAE,QAAQ,EAAE,QAAQ,IAAI,MAAM,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;KAAE,CAAA;IAClF,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAA;IAClD,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACxC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACnC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,YAAY,CAAC,EAAE;QAAE,QAAQ,EAAE,IAAI,IAAI,MAAM,gBAAgB,CAAC,CAAC,EAAE,gBAAgB,CAAC,IAAI,CAAC;KAAE,CAAA;IAC9F,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACjD,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACjD,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,WAAW,EAAE,CAAA;CAC3C,CAAA;AAED,KAAK,WAAW,GAAG,YAAY,GAAG,iBAAiB,GAAG,KAAK,GAAG,iBAAiB,GAAG,aAAa,CAAA;AAE/F,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,IAAI,IAAI,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,WAAW,GAAG,MAAM,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AAE/J,MAAM,MAAM,SAAS,CAAC,KAAK,SAAS,MAAM,IAAI;IAC5C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACtB,IAAI,CAAC,CAAC,SAAS,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,WAAW,CAAC,WAAW,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAA;IAC7H,IAAI,CAAC,CAAC,SAAS,WAAW,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAA;IACtG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACvB,GAAG,CAAC,OAAO,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAA;IACpF,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACxB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACxB,SAAS,CAAC,IAAI,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IACxK,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;IACxC,KAAK,CAAC,CAAC,SAAS,cAAc,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;IACvH,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,CAAA;IACzF,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAChE,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,eAAe,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/F,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACjE,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACxD,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC/B,QAAQ,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,CAAA;IACnC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;CACtB,CAAA;AAUD,wBAAgB,OAAO,CAAC,KAAK,SAAS,MAAM,EAAE,QAAQ,SAAS,OAAO,GAAG,OAAO,EAAE,UAAU,EAAE,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAE,WAAW,CAAC,KAAK,EAAE,QAAQ,CAAM,GAAG,SAAS,CAAC,KAAK,CAAC,CA2QlM"}
|
package/testing.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { readSteps, storeFolder } from './records.js';
|
|
2
2
|
import { answerCall, cmodPluginName, notInstalled } from './runtime/dependencies.js';
|
|
3
|
+
import { foldAnswers, installBefore } from './runtime/before.js';
|
|
4
|
+
import { createRouter } from './runtime/router.js';
|
|
3
5
|
import { toolInputOf } from './runtime/tool-calls.js';
|
|
4
6
|
import { createLifecycle } from './runtime/lifecycle.js';
|
|
5
7
|
import { MissingOptions } from './runtime/options.js';
|
|
@@ -11,7 +13,7 @@ import { calledAnswer, fakeClaude } from './testing/fake-claude.js';
|
|
|
11
13
|
import { elements, findElement, rowsOf } from './testing/fake-elements.js';
|
|
12
14
|
import { fakeFiles } from './testing/fake-files.js';
|
|
13
15
|
import { fakeSessions } from './testing/fake-sessions.js';
|
|
14
|
-
import { listed } from './utils/text.js';
|
|
16
|
+
import { listed, messageOf } from './utils/text.js';
|
|
15
17
|
import { componentOf } from './ui/places.js';
|
|
16
18
|
const defaultColumns = 80;
|
|
17
19
|
const defaultRows = 24;
|
|
@@ -33,6 +35,7 @@ export function testMod(definition, options = {}) {
|
|
|
33
35
|
fake.claude.session.root = async () => projectRoot;
|
|
34
36
|
fake.claude.session.cwd = async () => cwd;
|
|
35
37
|
const conversations = fakeSessions({
|
|
38
|
+
callTool: (args) => fake.claude.tool.call(args),
|
|
36
39
|
project: () => projectRoot,
|
|
37
40
|
given: options.sessions ?? [],
|
|
38
41
|
push: (mod, session, change) => {
|
|
@@ -95,9 +98,23 @@ export function testMod(definition, options = {}) {
|
|
|
95
98
|
if (lifecycle.phase !== 'active')
|
|
96
99
|
throw failure ?? new Error(`${name} did not start: the lifecycle is ${lifecycle.phase}.`);
|
|
97
100
|
};
|
|
101
|
+
const cmodPlugin = createRouter();
|
|
102
|
+
const askMod = async (event, change) => {
|
|
103
|
+
const asked = { to: name, method: conversationMethods.before, input: { session: sessionId, event, change }, caller: cmodPluginName };
|
|
104
|
+
return calledAnswer(cmodPluginName, await lifecycle.route('cmod.call', asked, async () => ({ deny: notInstalled(name) })));
|
|
105
|
+
};
|
|
106
|
+
installBefore({
|
|
107
|
+
claude: fake.claude,
|
|
108
|
+
router: cmodPlugin,
|
|
109
|
+
hooks: {
|
|
110
|
+
place: async () => conversations.place(),
|
|
111
|
+
wants: async (event) => conversations.wants(name, event),
|
|
112
|
+
decide: (event, change) => foldAnswers(event, change, [(latest) => askMod(event, latest)], (error) => fake.claude.ui.log(`cmod: passed the ${event} on without ${name}'s answer: ${messageOf(error)}`, { to: 'debug' })),
|
|
113
|
+
},
|
|
114
|
+
});
|
|
98
115
|
const route = async (event, input, below) => {
|
|
99
116
|
await start();
|
|
100
|
-
return lifecycle.route(event,
|
|
117
|
+
return cmodPlugin.dispatch(event, input, (passed) => lifecycle.route(event, passed, async (e) => (below ?? claudeCodeAnswer(event, e))));
|
|
101
118
|
};
|
|
102
119
|
const drawPane = async (paneId) => {
|
|
103
120
|
await start();
|
|
@@ -292,6 +309,10 @@ function fakeDependencies(caller, dependencies, conversations) {
|
|
|
292
309
|
};
|
|
293
310
|
}
|
|
294
311
|
function claudeCodeAnswer(event, e) {
|
|
312
|
+
if (event === 'agent.spawn') {
|
|
313
|
+
const { model, parentModel } = e;
|
|
314
|
+
return { agentId: 'agent-1', model: model ?? parentModel };
|
|
315
|
+
}
|
|
295
316
|
if (event !== 'prompt.submit')
|
|
296
317
|
return {};
|
|
297
318
|
const { text, context, origin } = e;
|
package/utils/call-effects.d.ts
CHANGED
|
@@ -41,6 +41,7 @@ export type CallEffects = {
|
|
|
41
41
|
export declare function callEffects(use: CallBase, workspace: {
|
|
42
42
|
cwd: string;
|
|
43
43
|
home: string;
|
|
44
|
+
configRoot?: string;
|
|
44
45
|
fs: FileSystem;
|
|
45
46
|
}): CallEffects;
|
|
46
47
|
export declare function inputOf(value: unknown): Readonly<Record<string, unknown>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"call-effects.d.ts","sourceRoot":"","sources":["../src/utils/call-effects.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAChE,OAAO,EAAyB,KAAK,UAAU,EAAE,MAAM,YAAY,CAAA;AAGnE,MAAM,MAAM,QAAQ,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AACvH,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG;IAAE,QAAQ,EAAE,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,EAAE,CAAC;IAAC,aAAa,EAAE,OAAO,CAAA;CAAE,CAAA;AAClG,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAC9F,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,CAAA;AAClD,MAAM,MAAM,IAAI,GAAG,WAAW,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAA;AAEhE,MAAM,MAAM,QAAQ,GAAG;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AACrE,MAAM,MAAM,UAAU,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,CAAC,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC,GAAG,SAAS,CAAA;CAAE,CAAA;AAC1F,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,YAAY,EAAE,CAAC;QAAC,aAAa,EAAE,OAAO,CAAA;KAAE,GAAG,SAAS,CAAA;IACrF,KAAK,EAAE,UAAU,EAAE,CAAA;IACnB,MAAM,EAAE,UAAU,EAAE,CAAA;IACpB,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAA;CAC7B,CAAA;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,QAAQ,EAAE,SAAS,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,UAAU,CAAA;CAAE,GAAG,WAAW,
|
|
1
|
+
{"version":3,"file":"call-effects.d.ts","sourceRoot":"","sources":["../src/utils/call-effects.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAChE,OAAO,EAAyB,KAAK,UAAU,EAAE,MAAM,YAAY,CAAA;AAGnE,MAAM,MAAM,QAAQ,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AACvH,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG;IAAE,QAAQ,EAAE,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,EAAE,CAAC;IAAC,aAAa,EAAE,OAAO,CAAA;CAAE,CAAA;AAClG,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAC9F,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,CAAA;AAClD,MAAM,MAAM,IAAI,GAAG,WAAW,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAA;AAEhE,MAAM,MAAM,QAAQ,GAAG;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AACrE,MAAM,MAAM,UAAU,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,CAAC,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC,GAAG,SAAS,CAAA;CAAE,CAAA;AAC1F,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,YAAY,EAAE,CAAC;QAAC,aAAa,EAAE,OAAO,CAAA;KAAE,GAAG,SAAS,CAAA;IACrF,KAAK,EAAE,UAAU,EAAE,CAAA;IACnB,MAAM,EAAE,UAAU,EAAE,CAAA;IACpB,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAA;CAC7B,CAAA;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,QAAQ,EAAE,SAAS,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,UAAU,CAAA;CAAE,GAAG,WAAW,CAsCrI;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAEzE;AAED,wBAAgB,UAAU,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,QAAQ,GAAG,QAAQ,CAElF"}
|
package/utils/call-effects.js
CHANGED
|
@@ -3,7 +3,7 @@ import { expandHome, ifMissing } from './paths.js';
|
|
|
3
3
|
import { resolve } from '../vendor.js';
|
|
4
4
|
export function callEffects(use, workspace) {
|
|
5
5
|
const { fs } = workspace;
|
|
6
|
-
const absolute = (path, folder = workspace.cwd) => resolve(folder, expandHome(path, workspace.home));
|
|
6
|
+
const absolute = (path, folder = workspace.cwd) => resolve(folder, expandHome(path, workspace.home, workspace.configRoot));
|
|
7
7
|
const access = (path) => ({ path: absolute(path), contents: undefined });
|
|
8
8
|
const effects = { shell: undefined, reads: [], writes: [], urls: [], subagent: undefined };
|
|
9
9
|
if (use.tool === 'Bash') {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"disposable.d.ts","sourceRoot":"","sources":["../src/utils/disposable.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG;IACvB,OAAO,IAAI,IAAI,CAAA;CAChB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/utils/matcher.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export function matches(value, matcher) {
|
|
2
2
|
if (matcher instanceof RegExp)
|
|
3
|
-
return matcher.test(String(value));
|
|
3
|
+
return value !== undefined && value !== null && matcher.test(String(value));
|
|
4
4
|
if (Array.isArray(matcher))
|
|
5
5
|
return matcher.some((each) => matches(value, each));
|
|
6
6
|
if (typeof matcher !== 'object' || matcher === null)
|
package/utils/paths.d.ts
CHANGED
|
@@ -8,7 +8,8 @@ export type FileSystem = {
|
|
|
8
8
|
}>;
|
|
9
9
|
exists: (path: string) => Promise<boolean>;
|
|
10
10
|
};
|
|
11
|
-
export declare
|
|
11
|
+
export declare const homePrefix: RegExp;
|
|
12
|
+
export declare function expandHome(path: string, home: string, configRoot?: string): string;
|
|
12
13
|
export declare function relativePath(base: string, path: string): string | undefined;
|
|
13
14
|
export declare function realPathOf(path: string, fs: FileSystem): Promise<string | undefined>;
|
|
14
15
|
//# sourceMappingURL=paths.d.ts.map
|
package/utils/paths.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/utils/paths.ts"],"names":[],"mappings":"AAKA,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAEjD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,GAAG,CAAC,KAAK,EAAE,OAAO,KAAK,KAAK,CAKxE;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IACvC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,GAAG,KAAK,GAAG,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACtF,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;CAC3C,CAAA;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/utils/paths.ts"],"names":[],"mappings":"AAKA,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAEjD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,GAAG,CAAC,KAAK,EAAE,OAAO,KAAK,KAAK,CAKxE;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IACvC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,GAAG,KAAK,GAAG,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACtF,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;CAC3C,CAAA;AAED,eAAO,MAAM,UAAU,QAA4C,CAAA;AAEnE,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,SAAoB,GAAG,MAAM,CAI7F;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAI3E;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAK1F"}
|
package/utils/paths.js
CHANGED
|
@@ -11,9 +11,12 @@ export function ifMissing(value) {
|
|
|
11
11
|
throw error;
|
|
12
12
|
};
|
|
13
13
|
}
|
|
14
|
-
export
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
export const homePrefix = /^(~|\$HOME|\$CLAUDE_CONFIG_DIR)(?=\/|$)/;
|
|
15
|
+
export function expandHome(path, home, configRoot = `${home}/.claude`) {
|
|
16
|
+
const prefix = homePrefix.exec(path)?.[0];
|
|
17
|
+
if (prefix === undefined)
|
|
18
|
+
return path;
|
|
19
|
+
return `${prefix === '$CLAUDE_CONFIG_DIR' ? configRoot : home}${path.slice(prefix.length)}`;
|
|
17
20
|
}
|
|
18
21
|
export function relativePath(base, path) {
|
|
19
22
|
if (path === base)
|