@cmodjs/core 1.6.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/docs/commands.md +7 -7
  2. package/docs/dependencies.md +1 -1
  3. package/docs/index.md +12 -11
  4. package/docs/jobs.md +12 -16
  5. package/docs/mod.md +111 -36
  6. package/docs/permissions.md +14 -18
  7. package/docs/session.md +171 -82
  8. package/docs/state.md +5 -5
  9. package/docs/testing.md +57 -41
  10. package/docs/ui.md +9 -9
  11. package/jobs/permissions/decide-permission.d.ts.map +1 -1
  12. package/jobs/permissions/decide-permission.js +1 -1
  13. package/jobs/prompt.d.ts +1 -3
  14. package/jobs/prompt.d.ts.map +1 -1
  15. package/jobs/prompt.js +2 -21
  16. package/jobs/slash-command.d.ts +1 -1
  17. package/jobs/slash-command.d.ts.map +1 -1
  18. package/jobs/slash-command.js +1 -1
  19. package/jobs/status-line.d.ts +0 -1
  20. package/jobs/status-line.d.ts.map +1 -1
  21. package/jobs/status-line.js +3 -4
  22. package/mod.d.ts +53 -105
  23. package/mod.d.ts.map +1 -1
  24. package/mod.js +18 -80
  25. package/package.json +1 -1
  26. package/records.d.ts +1 -1
  27. package/records.js +1 -1
  28. package/register.d.ts.map +1 -1
  29. package/register.js +8 -5
  30. package/runtime/before.d.ts +11 -4
  31. package/runtime/before.d.ts.map +1 -1
  32. package/runtime/before.js +131 -67
  33. package/runtime/claude.d.ts +6 -1
  34. package/runtime/claude.d.ts.map +1 -1
  35. package/runtime/conversations.d.ts.map +1 -1
  36. package/runtime/conversations.js +82 -38
  37. package/runtime/deadline.d.ts +17 -0
  38. package/runtime/deadline.d.ts.map +1 -1
  39. package/runtime/deadline.js +34 -0
  40. package/runtime/dependencies.d.ts +2 -0
  41. package/runtime/dependencies.d.ts.map +1 -1
  42. package/runtime/dependencies.js +12 -0
  43. package/runtime/grants.d.ts.map +1 -1
  44. package/runtime/grants.js +13 -8
  45. package/runtime/hooks.d.ts +15 -11
  46. package/runtime/hooks.d.ts.map +1 -1
  47. package/runtime/hooks.js +8 -165
  48. package/runtime/lifecycle.d.ts +4 -2
  49. package/runtime/lifecycle.d.ts.map +1 -1
  50. package/runtime/lifecycle.js +96 -62
  51. package/runtime/permission-check.d.ts +15 -0
  52. package/runtime/permission-check.d.ts.map +1 -0
  53. package/runtime/permission-check.js +37 -0
  54. package/runtime/sessions.d.ts +35 -5
  55. package/runtime/sessions.d.ts.map +1 -1
  56. package/runtime/sessions.js +304 -82
  57. package/runtime/state.d.ts.map +1 -1
  58. package/runtime/state.js +14 -11
  59. package/runtime/tool-calls.d.ts +0 -1
  60. package/runtime/tool-calls.d.ts.map +1 -1
  61. package/runtime/tool-calls.js +0 -19
  62. package/session/context/turn/message/instruction-file.d.ts +9 -0
  63. package/session/context/turn/message/instruction-file.d.ts.map +1 -0
  64. package/session/context/turn/message/instruction-file.js +4 -0
  65. package/session/context/turn/message/message.d.ts +6 -4
  66. package/session/context/turn/message/message.d.ts.map +1 -1
  67. package/session/context/turn/message/message.js +12 -8
  68. package/session/context/turn/message/sender.d.ts +2 -1
  69. package/session/context/turn/message/sender.d.ts.map +1 -1
  70. package/session/context/turn/message/sender.js +4 -1
  71. package/session/context/turn/message/tool-call.d.ts +5 -0
  72. package/session/context/turn/message/tool-call.d.ts.map +1 -1
  73. package/session/context/turn/message/tool-result.d.ts +6 -1
  74. package/session/context/turn/message/tool-result.d.ts.map +1 -1
  75. package/session/context/turn/turn.d.ts +7 -0
  76. package/session/context/turn/turn.d.ts.map +1 -1
  77. package/session/conversation.d.ts +10 -3
  78. package/session/conversation.d.ts.map +1 -1
  79. package/session/conversation.js +14 -5
  80. package/session/goal/goal.d.ts +1 -1
  81. package/session/goal/goal.d.ts.map +1 -1
  82. package/session/goal/task.d.ts +2 -2
  83. package/session/goal/task.d.ts.map +1 -1
  84. package/session/session.d.ts +361 -26
  85. package/session/session.d.ts.map +1 -1
  86. package/session/session.js +40 -0
  87. package/src/jobs/permissions/decide-permission.ts +2 -1
  88. package/src/jobs/prompt.ts +3 -20
  89. package/src/jobs/slash-command.ts +2 -2
  90. package/src/jobs/status-line.ts +4 -7
  91. package/src/mod.ts +52 -147
  92. package/src/records.ts +1 -1
  93. package/src/register.ts +8 -5
  94. package/src/runtime/before.ts +140 -66
  95. package/src/runtime/claude.ts +5 -1
  96. package/src/runtime/conversations.ts +75 -37
  97. package/src/runtime/deadline.ts +38 -0
  98. package/src/runtime/dependencies.ts +12 -0
  99. package/src/runtime/grants.ts +11 -7
  100. package/src/runtime/hooks.ts +24 -163
  101. package/src/runtime/lifecycle.ts +110 -77
  102. package/src/runtime/permission-check.ts +47 -0
  103. package/src/runtime/sessions.ts +334 -91
  104. package/src/runtime/state.ts +15 -8
  105. package/src/runtime/tool-calls.ts +0 -19
  106. package/src/session/context/turn/message/instruction-file.ts +13 -0
  107. package/src/session/context/turn/message/message.ts +17 -11
  108. package/src/session/context/turn/message/sender.ts +5 -2
  109. package/src/session/context/turn/message/tool-call.ts +3 -0
  110. package/src/session/context/turn/message/tool-result.ts +6 -1
  111. package/src/session/context/turn/turn.ts +4 -0
  112. package/src/session/conversation.ts +21 -8
  113. package/src/session/goal/goal.ts +1 -1
  114. package/src/session/goal/task.ts +2 -2
  115. package/src/session/session.ts +227 -24
  116. package/src/testing/claude-code.ts +23 -0
  117. package/src/testing/fake-claude.ts +5 -1
  118. package/src/testing/fake-sessions.ts +38 -24
  119. package/src/testing.ts +88 -19
  120. package/testing/claude-code.d.ts +12 -0
  121. package/testing/claude-code.d.ts.map +1 -0
  122. package/testing/claude-code.js +10 -0
  123. package/testing/fake-claude.d.ts +4 -0
  124. package/testing/fake-claude.d.ts.map +1 -1
  125. package/testing/fake-claude.js +4 -1
  126. package/testing/fake-sessions.d.ts +8 -6
  127. package/testing/fake-sessions.d.ts.map +1 -1
  128. package/testing/fake-sessions.js +35 -20
  129. package/testing.d.ts +38 -9
  130. package/testing.d.ts.map +1 -1
  131. package/testing.js +58 -8
  132. package/docs/hooks.md +0 -259
  133. package/session/context/turn/message/document.d.ts +0 -7
  134. package/session/context/turn/message/document.d.ts.map +0 -1
  135. package/session/context/turn/message/document.js +0 -1
  136. package/src/session/context/turn/message/document.ts +0 -8
@@ -1,4 +1,3 @@
1
- import { hooks, PreToolUse } from '../mod.js';
2
1
  import { changelogPage, consentPath, dataFolder, finishStepsMethod, isAtLeast, marketplaceOf, oldestCmodFor, openPageMethod, parseConsent, pendingStepsMethod, parseEvent, permissionWords, readRecord, readSteps, recordPath, scriptsSha256, settingsPagesMethod, storeFolder, updatesToTurnOn } from '../records.js';
3
2
  import { homePrefix, ifMissing, relativePath } from '../utils/paths.js';
4
3
  import { firstLine, formatExit, listed, messageOf } from '../utils/text.js';
@@ -6,9 +5,9 @@ import { beforeDeadline } from './deadline.js';
6
5
  import { announcedOf, announcementMs, announcementOf } from './announce.js';
7
6
  import { bind } from './bound.js';
8
7
  import { serveConversations } from './conversations.js';
9
- import { answerCall, cmodPluginName, dependencyCalls, withBareRefusals } from './dependencies.js';
8
+ import { answerCall, answeringOwnCalls, cmodPluginName, dependencyCalls, withBareRefusals } from './dependencies.js';
10
9
  import { keepEngine } from './engine.js';
11
- import { classicHook, heldDecisionHook, permissionEvents, preToolUseHook, userSkillHook } from './hooks.js';
10
+ import { classicHook, permissionEvents, userSkillHook } from './hooks.js';
12
11
  import { checkedAnswer, checkingGrants, checkSettings, checksAnswers, checkWrite, isCovered, itemOf, passedDown, programAddresses } from './grants.js';
13
12
  import { cmodError, lastLineOf, locatingPrograms, readLines } from './programs.js';
14
13
  import { createModFiles, resolvedPath } from './metadata.js';
@@ -29,6 +28,7 @@ const installId = 'cmod:install';
29
28
  const installRetrySeconds = [2, 4, 8, 16, 32, 64, 128];
30
29
  const cmodCheckMs = 1000;
31
30
  const cmodWaitMs = 60_000;
31
+ const cmodSlowCheckMs = 10_000;
32
32
  const startHoldMs = 10_000 - 1_000;
33
33
  const dependencyCallMs = 30_000;
34
34
  const cannotStart = /failed to start: /;
@@ -93,7 +93,7 @@ async function cmodVersion(claude) {
93
93
  throw new Error(`cmod --version ${formatExit(result.exitCode, lastLineOf(result.stderr) ?? '')}`);
94
94
  return result.stdout.trim().split(/\s+/).at(-1);
95
95
  }
96
- export function createLifecycle(definition, checksPermissions = () => true, options = {}, asksPerson = true) {
96
+ export function createLifecycle(definition, permissionRoutes = () => undefined, options = {}, asksPerson = true) {
97
97
  let router = createRouter();
98
98
  let phase = 'starting';
99
99
  let runtime;
@@ -103,6 +103,7 @@ export function createLifecycle(definition, checksPermissions = () => true, opti
103
103
  let activation;
104
104
  let mod;
105
105
  let imports;
106
+ let raise;
106
107
  let failure;
107
108
  let pluginOptions = options;
108
109
  const installer = createInstaller(definition.name, () => claude());
@@ -250,7 +251,7 @@ export function createLifecycle(definition, checksPermissions = () => true, opti
250
251
  if (runtime === undefined || plugin === undefined)
251
252
  return;
252
253
  const activeRouter = createRouter();
253
- const active = await createMod(definition, { ...runtime, router: activeRouter, dataFolder: dataFolder(plugin.store, plugin.name), store: plugin.store, steps: plugin.steps, granted, refreshGrants, checksPermissions, options: pluginOptions }).catch(async (error) => {
254
+ const active = await createMod(definition, { ...runtime, router: activeRouter, dataFolder: dataFolder(plugin.store, plugin.name), store: plugin.store, steps: plugin.steps, granted, refreshGrants, permissionRoutes, options: pluginOptions }).catch(async (error) => {
254
255
  if (error instanceof MissingOptions && (await askOptions(error)))
255
256
  return undefined;
256
257
  fail(messageOf(error), error instanceof MissingOptions ? `Set ${error.keys.length === 1 ? 'it' : 'them'} in /config.` : undefined, begin);
@@ -261,18 +262,26 @@ export function createLifecycle(definition, checksPermissions = () => true, opti
261
262
  const { added } = active;
262
263
  mod = active.mod;
263
264
  imports = active.imports;
265
+ raise = active.raise;
264
266
  router = activeRouter;
265
267
  phase = 'active';
266
268
  runtime.claude.ui.invalidate('ui.render');
267
269
  void runSteps(active.mod).catch((error) => toastFailure({ id: 'cmod:steps', message: 'Its installer stopped', error }));
268
270
  const missed = missedSessionStart;
269
271
  missedSessionStart = undefined;
270
- const lateAnswer = missed === undefined ? {} : await activeRouter.dispatch('classic.SessionStart', missed, async () => ({})).catch((error) => {
272
+ const replay = (e) => (definition.name === cmodPluginName ? activeRouter.dispatch('classic.SessionStart', e, async () => ({})) : checkedRoute('classic.SessionStart', e, async () => ({})));
273
+ const lateAnswer = missed === undefined ? {} : await replay(missed).catch((error) => {
271
274
  toastFailure({ id: 'classic.SessionStart', message: 'The SessionStart hook failed', error });
272
275
  return {};
273
276
  });
274
- if (Object.keys(lateAnswer).length > 0) {
275
- runtime.claude.ui.log(`${definition.name} started after Claude Code's SessionStart, so Claude Code did not read the answer of its SessionStart hooks.`, { to: 'debug' });
277
+ const { additionalContext, ...unread } = lateAnswer;
278
+ for (const text of additionalContext ?? []) {
279
+ const added = await runtime.claude.session.append({ message: { type: 'user', content: [{ type: 'text', text }] } });
280
+ if (added.deny !== undefined)
281
+ runtime.claude.ui.log(`${definition.name}: Claude Code refused its SessionStart note: ${added.deny}`, { to: 'debug' });
282
+ }
283
+ if (Object.keys(unread).length > 0) {
284
+ runtime.claude.ui.log(`${definition.name} started after Claude Code's SessionStart, so Claude Code did not read ${Object.keys(unread).join(', ')} from its SessionStart hooks.`, { to: 'debug' });
276
285
  }
277
286
  const version = plugin.version ?? '';
278
287
  const announced = announcedOf(await runtime.claude.store.get(announcedKey));
@@ -358,7 +367,7 @@ export function createLifecycle(definition, checksPermissions = () => true, opti
358
367
  timer.cancel();
359
368
  longWait.cancel();
360
369
  };
361
- const timer = claude().clock.every(cmodCheckMs, () => {
370
+ const poll = (ms) => claude().clock.every(ms, () => {
362
371
  check ??= cmodVersion(claude())
363
372
  .then((version) => {
364
373
  check = undefined;
@@ -373,9 +382,14 @@ export function createLifecycle(definition, checksPermissions = () => true, opti
373
382
  report(error);
374
383
  });
375
384
  });
376
- const longWait = claude().clock.after(cmodWaitMs, () => installing(latest === undefined
377
- ? "Still waiting for cmod to download cmod. See cmod's own line."
378
- : `PATH finds cmod ${latest}, and ${definition.name} needs cmod ${oldestCmodFor(plugin?.steps ?? {})} or later. Run npm i -g @cmodjs/cli, or put ~/.local/bin ahead of the old cmod on PATH.`));
385
+ let timer = poll(cmodCheckMs);
386
+ const longWait = claude().clock.after(cmodWaitMs, () => {
387
+ timer.cancel();
388
+ timer = poll(cmodSlowCheckMs);
389
+ installing(latest === undefined
390
+ ? "Still waiting for cmod to download cmod. See cmod's own line."
391
+ : `PATH finds cmod ${latest}, and ${definition.name} needs cmod ${oldestCmodFor(plugin?.steps ?? {})} or later. Run npm i -g @cmodjs/cli, or put ~/.local/bin ahead of the old cmod on PATH.`);
392
+ });
379
393
  };
380
394
  const bootstrap = async (root) => {
381
395
  installing();
@@ -502,6 +516,12 @@ export function createLifecycle(definition, checksPermissions = () => true, opti
502
516
  get toasts() {
503
517
  return runtime?.toaster.shown ?? [];
504
518
  },
519
+ async raise(method, payload) {
520
+ if (raise === undefined || imports === undefined)
521
+ throw new Error(`${definition.name} has not started, so ${method} has no listener yet.`);
522
+ bind(imports);
523
+ return raise(method, payload);
524
+ },
505
525
  async start(claudeCalls, read) {
506
526
  if (runtime !== undefined)
507
527
  return;
@@ -537,8 +557,10 @@ export function createLifecycle(definition, checksPermissions = () => true, opti
537
557
  if (event === 'cmod.call' && !isCallToThisMod)
538
558
  return next(e);
539
559
  await (event === 'classic.SessionStart' || isCallToThisMod ? holdUntilStarted() : whenActive());
540
- if (event === 'classic.SessionStart' && phase !== 'active')
560
+ if (event === 'classic.SessionStart' && phase !== 'active') {
541
561
  missedSessionStart = e;
562
+ return next(e);
563
+ }
542
564
  if (isCallToThisMod && phase !== 'active') {
543
565
  const isStopped = phase === 'declined' || phase === 'failed';
544
566
  return { deny: isStopped ? `${definition.name} is not set up. Its toast says why.` : `${definition.name} is installing. Try again when it's ready.` };
@@ -565,9 +587,8 @@ export function createLifecycle(definition, checksPermissions = () => true, opti
565
587
  async function createMod(definition, runtime) {
566
588
  const { router, toaster } = runtime;
567
589
  const report = (failure) => void toaster.fail(failure);
568
- const claude = withBareRefusals(checkingKeys(runtime.claude, toaster.toast, runtime.steps.keys ?? {}));
590
+ const claude = answeringOwnCalls(withBareRefusals(checkingKeys(runtime.claude, toaster.toast, runtime.steps.keys ?? {})), router);
569
591
  const added = [];
570
- const hooksAdded = [];
571
592
  const announce = (feature) => {
572
593
  added.push(feature);
573
594
  };
@@ -640,7 +661,7 @@ async function createMod(definition, runtime) {
640
661
  }));
641
662
  const modState = createState({ name: definition.name, initial: definition.state ?? {}, session, root, claude, report, changed: area.changed, forkParent });
642
663
  const modOptions = createOptions({ name: definition.name, declared: definition.options ?? {}, fromClaude: runtime.options, claude, report, changed: modState.changed });
643
- const held = runtime.checksPermissions() ? new Map() : undefined;
664
+ const hookOn = (hook, listener) => router.add(`classic.${hook}`, classicHook(hook, listener, report));
644
665
  const expand = (path) => (homePrefix.test(path) ? resolvedPath(path, places) : path);
645
666
  const settingsFile = (source) => ({
646
667
  read: () => checked.settings.read({ source }),
@@ -649,8 +670,18 @@ async function createMod(definition, runtime) {
649
670
  await claude.cmod.call({ to: cmodPluginName, method: 'updateSettings', input: { mod: definition.name, source, project: modState.root, values } });
650
671
  },
651
672
  });
652
- const client = createSessionsClient({ name: definition.name, claude: checked, report: (id, message, error) => report({ id, message, error }), isGranted: (permission) => flag(permission) });
673
+ const client = createSessionsClient({
674
+ name: definition.name,
675
+ claude: checked,
676
+ report: (id, message, error) => report({ id, message, error }),
677
+ isGranted: (permission) => flag(permission),
678
+ hookOn: (hook, listener) => hookOn(hook, listener),
679
+ routeOn: (event, hook) => router.add(event, hook),
680
+ permissionRoutes: runtime.permissionRoutes,
681
+ });
653
682
  let followed;
683
+ let isSetUp = false;
684
+ const watches = new Set();
654
685
  const followFolders = (project, folder) => {
655
686
  if (followed === undefined)
656
687
  return;
@@ -659,7 +690,16 @@ async function createMod(definition, runtime) {
659
690
  };
660
691
  const imports = {
661
692
  sessions: client.sessions,
662
- settings: {
693
+ commands: {
694
+ list: async () => (await checked.command.list()).map(({ name, description, source, plugin }) => ({ name, description, source, ...(plugin === undefined ? {} : { plugin }) })),
695
+ run: async (command, args) => {
696
+ const { text, context } = await checked.command.run({ command, ...(args === undefined ? {} : { args }) });
697
+ return { ...(text === undefined ? {} : { text }), ...(context === undefined || context.length === 0 ? {} : { note: context.join('\n') }) };
698
+ },
699
+ beforeRun: (...args) => client.listen('beforeRun', args),
700
+ onRun: (...args) => client.listen('onRun', args),
701
+ },
702
+ config: {
663
703
  list: async () => (await checked.config.list()).map(({ key, label, description, kind, value, options, provider, isLocked }) => ({
664
704
  key,
665
705
  label,
@@ -670,9 +710,22 @@ async function createMod(definition, runtime) {
670
710
  ...(options === undefined ? {} : { options }),
671
711
  ...(provider.plugin === 'engine' ? {} : { plugin: provider.plugin }),
672
712
  })),
713
+ },
714
+ settings: {
673
715
  user: settingsFile('user'),
674
716
  project: settingsFile('project'),
675
717
  local: settingsFile('local'),
718
+ beforeFileChange: (matcherOrListener, maybeListener) => {
719
+ const listener = (maybeListener ?? matcherOrListener);
720
+ const matcher = maybeListener === undefined ? undefined : matcherOrListener;
721
+ return hookOn('ConfigChange', async ({ source, file_path }) => {
722
+ const change = { source: source === 'skills' ? 'skills' : source.replace('_settings', ''), ...(file_path === undefined ? {} : { path: file_path }) };
723
+ if (matcher !== undefined && !matches(change, matcher))
724
+ return undefined;
725
+ const refuse = (await listener(change))?.refuse;
726
+ return typeof refuse === 'string' ? { block: refuse } : undefined;
727
+ });
728
+ },
676
729
  },
677
730
  model: { complete: (completion, options) => checked.model.complete(completion, options) },
678
731
  fs: {
@@ -685,6 +738,23 @@ async function createMod(definition, runtime) {
685
738
  const { kind, size, mtimeMs, isLink, realPath } = await checked.fs.stat(expand(path), options);
686
739
  return { isFile: kind === 'file', isFolder: kind === 'dir', isLink, size, modifiedAt: mtimeMs, ...(realPath === undefined ? {} : { realPath }) };
687
740
  },
741
+ onChange({ path }, listener) {
742
+ if (isSetUp)
743
+ throw new Error(`${definition.name}: fs.onChange works only in setup, because Claude Code reads the paths it watches when a session starts.`);
744
+ const paths = [path].flat().map((each) => resolvedPath(each, places));
745
+ const watch = { paths, listener };
746
+ watches.add(watch);
747
+ if (watches.size === 1) {
748
+ hookOn('SessionStart', () => ({ watchPaths: [...new Set([...watches].flatMap((each) => each.paths))] }));
749
+ hookOn('FileChanged', ({ file_path, event }) => {
750
+ for (const each of watches)
751
+ if (each.paths.some((watched) => relativePath(watched, file_path) !== undefined))
752
+ void Promise.resolve(each.listener({ path: file_path, type: event })).catch((error) => report({ id: 'fs:onChange', message: 'The fs.onChange listener failed', error }));
753
+ return undefined;
754
+ });
755
+ }
756
+ return { dispose: () => void watches.delete(watch) };
757
+ },
688
758
  },
689
759
  metadata: {
690
760
  read: (path) => files.read(path),
@@ -748,26 +818,10 @@ async function createMod(definition, runtime) {
748
818
  files: allowed('files'),
749
819
  config: allowed('config'),
750
820
  },
751
- on(hook, matcherOrListener, listenerOrOptions, maybeOptions) {
752
- const hasMatcher = typeof listenerOrOptions === 'function';
753
- const listener = (hasMatcher ? listenerOrOptions : matcherOrListener);
754
- const matcher = hasMatcher ? matcherOrListener : undefined;
755
- const options = (hasMatcher ? maybeOptions : listenerOrOptions);
756
- if (!hooks.includes(hook))
757
- throw new Error(`${definition.name}: mod.on takes one of Claude Code's hooks, such as PreToolUse, not ${String(hook)}.`);
758
- const matched = matcher === undefined ? listener : (input) => (matches(input, matcher) ? listener(input) : undefined);
759
- const bounded = options?.timeoutMs === undefined ? matched : timedHook(hook, matched, checkedMs(definition.name, `the ${hook} hook's timeoutMs`, options.timeoutMs), claude, toaster.toast);
760
- if (!hooksAdded.includes(hook))
761
- hooksAdded.push(hook);
762
- if (hook === PreToolUse)
763
- return router.add('tool.call', preToolUseHook(definition.name, bounded, claude, agents, held));
764
- const classic = hook;
765
- return router.add(`classic.${classic}`, classicHook(definition.name, classic, bounded, claude, agents, report));
766
- },
767
821
  every: (ms, listener) => timer(ms, 'every', claude.clock.every, listener),
768
822
  after: (ms, listener) => timer(ms, 'after', claude.clock.after, listener),
769
823
  use: (job) => job({ mod, announce, reserveName }),
770
- log: (text) => claude.ui.log(`${definition.name}: ${text}`, { to: 'debug' }),
824
+ log: (text, options) => claude.ui.log(`${definition.name}: ${text}`, { to: options?.to ?? 'debug' }),
771
825
  ui: { ...area.ui, notice: (callId, text) => claude.ui.notice(callId, text) },
772
826
  settings: settingsPages,
773
827
  dependencies: dependencyCalls(claude, (call, task) => beforeDeadline(claude, { ms: dependencyCallMs }, call, task)),
@@ -858,9 +912,9 @@ async function createMod(definition, runtime) {
858
912
  return next(e);
859
913
  const call = e;
860
914
  if (e.method === conversationMethods.changed && call.caller === cmodPluginName) {
861
- if (!flag('conversation'))
862
- return { value: null };
863
915
  const { session: changedSession, change, isInProject } = e.input;
916
+ if (changedSession !== client.sessions.current.id && !flag('conversation'))
917
+ return { value: null };
864
918
  client.changed(changedSession, change, isInProject !== false);
865
919
  return { value: null };
866
920
  }
@@ -894,22 +948,19 @@ async function createMod(definition, runtime) {
894
948
  throw new MissingOptions(missing, missing.map((key) => definition.options?.[key]?.title ?? key));
895
949
  await failsAs('the details of its session did not load', () => followCurrent(undefined));
896
950
  await failsAs('its setup function threw', () => definition.setup(mod));
951
+ isSetUp = true;
897
952
  const permissionHooks = permissionEvents.filter((event) => router.has(event));
898
- if (permissionHooks.length > 0 && !runtime.checksPermissions()) {
953
+ if (permissionHooks.length > 0 && runtime.permissionRoutes() === undefined) {
899
954
  throw new Error(`it decides permissions on ${listed(permissionHooks)}, so hooks/register.ts must call registerPermissionCheck(addHook) after registerMod`);
900
955
  }
901
- if (held !== undefined && hooksAdded.includes(PreToolUse))
902
- router.add('tool.check', heldDecisionHook(held));
903
956
  await failsAs('its open panes did not load', () => area.restorePanes());
904
- if (hooksAdded.length > 0)
905
- added.push(`${hooksAdded.length === 1 ? 'a hook' : 'hooks'} on ${listed(hooksAdded)}`);
906
- return { mod, added, imports };
957
+ return { mod, added, imports, raise: client.raise };
907
958
  }
908
959
  function checkingKeys(claude, toast, keys) {
909
960
  const bound = Object.entries(keys);
910
961
  if (bound.length === 0)
911
962
  return claude;
912
- const register = (command) => {
963
+ const addCommand = (command) => {
913
964
  for (const [key, commandName] of bound) {
914
965
  if (commandName === command.name && command.immediate !== true) {
915
966
  toast.warning(`${key} runs /${command.name}, which waits for Claude's turn to end`, { id: `key:${key}`, description: `It adds a row to the conversation. Give /${command.name} immediate: true.`, duration: Infinity });
@@ -917,7 +968,7 @@ function checkingKeys(claude, toast, keys) {
917
968
  }
918
969
  return claude.command.register(command);
919
970
  };
920
- return { ...claude, command: { ...claude.command, register } };
971
+ return { ...claude, command: { ...claude.command, register: addCommand } };
921
972
  }
922
973
  const longestTimerMs = 2_147_483_647;
923
974
  function checkedMs(name, subject, ms) {
@@ -925,23 +976,6 @@ function checkedMs(name, subject, ms) {
925
976
  throw new Error(`${name}: ${subject} is ${ms}. Give a whole number of milliseconds above 0 and at most ${longestTimerMs}.`);
926
977
  return ms;
927
978
  }
928
- function timedHook(event, hook, ms, claude, toast) {
929
- return (input) => new Promise((resolve, reject) => {
930
- const timer = claude.clock.after(ms, () => {
931
- toast.warning(`The ${event} hook passed its ${ms / 1000} s timeout`, { id: `timeout:${event}` });
932
- resolve(undefined);
933
- });
934
- Promise.resolve()
935
- .then(() => hook(input))
936
- .then((answer) => {
937
- timer.cancel();
938
- resolve(answer);
939
- }, (error) => {
940
- timer.cancel();
941
- reject(error);
942
- });
943
- });
944
- }
945
979
  async function failsAs(subject, step) {
946
980
  try {
947
981
  await step();
@@ -0,0 +1,15 @@
1
+ import type { Disposable } from '../mod.js';
2
+ import type { HookFields } from './hooks.js';
3
+ import { type HookOn, type RouteOn } from './sessions.js';
4
+ export type PermissionUse = {
5
+ readonly raise: (method: string, payload: HookFields) => Promise<HookFields | undefined>;
6
+ readonly hookOn: HookOn;
7
+ readonly routeOn: RouteOn;
8
+ readonly current: () => {
9
+ readonly id: string;
10
+ readonly cwd: string;
11
+ };
12
+ };
13
+ export type PermissionRoutes = (method: string, use: PermissionUse) => Disposable;
14
+ export declare const permissionRoutes: PermissionRoutes;
15
+ //# sourceMappingURL=permission-check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permission-check.d.ts","sourceRoot":"","sources":["../src/runtime/permission-check.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAC5C,OAAO,EAAuC,KAAK,MAAM,EAAE,KAAK,OAAO,EAAkB,MAAM,eAAe,CAAA;AAE9G,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,KAAK,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAAA;IACxF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;CACtE,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,KAAK,UAAU,CAAA;AAMjF,eAAO,MAAM,gBAAgB,EAAE,gBA6B9B,CAAA"}
@@ -0,0 +1,37 @@
1
+ import { defined, processPayloadOf, recordOf } from './sessions.js';
2
+ const verdictName = (decision) => (decision === 'deny' ? 'refuse' : decision);
3
+ export const permissionRoutes = (method, { raise, hookOn, routeOn, current }) => {
4
+ const isBefore = method.startsWith('before');
5
+ if (method.endsWith('PermissionCheck')) {
6
+ return routeOn('tool.check', async (e, next) => {
7
+ const below = (await next(e));
8
+ const { id, cwd } = current();
9
+ const payload = { session: id, cwd, ...defined({ task: e.agentId, callId: e.tool_use_id, reason: below.reason, rule: below.rule, ceiling: e.ceiling === undefined ? undefined : verdictName(e.ceiling) }), name: e.tool, input: recordOf(e.input), decision: verdictName(below.decision) };
10
+ if (!isBefore) {
11
+ void raise(method, payload);
12
+ return below;
13
+ }
14
+ const answer = await raise(method, payload);
15
+ if (answer === undefined || below.decision === 'deny')
16
+ return below;
17
+ if (typeof answer['refuse'] === 'string')
18
+ return { decision: 'deny', reason: answer['refuse'] };
19
+ if (typeof answer['ask'] === 'string')
20
+ return { decision: 'ask', reason: answer['ask'] };
21
+ return answer['allow'] === true ? { decision: 'allow' } : below;
22
+ });
23
+ }
24
+ return hookOn('PermissionRequest', async (input) => {
25
+ const payload = processPayloadOf('PermissionRequest', input);
26
+ if (!isBefore) {
27
+ void raise(method, payload);
28
+ return undefined;
29
+ }
30
+ const answer = await raise(method, payload);
31
+ if (answer === undefined)
32
+ return undefined;
33
+ if (typeof answer['refuse'] === 'string')
34
+ return { decision: defined({ behavior: 'deny', message: answer['refuse'], interrupt: answer['interrupt'] }) };
35
+ return answer['allow'] === true ? { decision: defined({ behavior: 'allow', updatedInput: answer['input'], updatedPermissions: answer['updates'] }) } : undefined;
36
+ });
37
+ };
@@ -1,9 +1,13 @@
1
+ import type { Args, CommandRunResult } from 'claude-code';
2
+ import type { Disposable } from '../mod.js';
1
3
  import { ToolResult } from '../session/context/turn/message/tool-result.js';
2
- import type { SessionChange, SessionData } from '../session/conversation.js';
3
- import { type BeforeEvent, type SessionEvent, type Sessions, type Usage } from '../session/session.js';
4
+ import { type SessionChange, type SessionData, type TaskData } from '../session/conversation.js';
5
+ import { type BeforeEvent, type ProcessName, type SessionEvent, type Sessions, type TaskStartArgs, type ToolCallArgs, type Usage } from '../session/session.js';
4
6
  import { type SentMatcher } from '../utils/matcher.js';
5
7
  import { type Place } from './before.js';
6
8
  import type { Claude } from './claude.js';
9
+ import { type HookFields } from './hooks.js';
10
+ import type { PermissionRoutes } from './permission-check.js';
7
11
  export type Subscription = {
8
12
  readonly event: SessionEvent;
9
13
  readonly matcher: SentMatcher | undefined;
@@ -25,7 +29,7 @@ export declare const conversationMethods: {
25
29
  readonly changed: 'cmod:changed';
26
30
  readonly before: 'cmod:before';
27
31
  };
28
- export type Act = 'send' | 'startTask' | 'callTool' | 'usage' | 'clients';
32
+ export type Act = 'send' | 'startTask' | 'callTool' | 'usage' | 'surfaces' | 'abort';
29
33
  export declare const beforePermission: (event: BeforeEvent) => string;
30
34
  export declare const isAct: (action: string) => action is Act;
31
35
  export declare function toolPermission(tool: string): string | undefined;
@@ -36,11 +40,14 @@ export type Scope = {
36
40
  readonly turn?: string;
37
41
  readonly task?: string;
38
42
  readonly isGoal?: true;
43
+ readonly isAgent?: true;
39
44
  };
40
45
  export type SessionsClient = {
41
46
  readonly sessions: Sessions;
42
47
  changed(session: string, change: SessionChange, isInProject: boolean): void;
43
48
  answerBefore(session: string, event: BeforeEvent, change: SessionChange, isInProject: boolean): Promise<unknown>;
49
+ raise(method: string, payload: Readonly<Record<string, unknown>>): Promise<unknown>;
50
+ listen(method: string, args: readonly unknown[]): Disposable;
44
51
  /** An empty title keeps the title the session already has. */
45
52
  follow(current: Omit<SessionData, 'isRunning' | 'lastAt'>): void;
46
53
  };
@@ -48,11 +55,34 @@ export declare function usageOf(claude: Pick<Claude, 'session'>): Promise<Usage>
48
55
  export declare function payloadOf(change: SessionChange): unknown;
49
56
  export declare function isHeardBy(event: SessionEvent, matcher: unknown, change: SessionChange): boolean;
50
57
  export declare function isInScope(scope: Scope, session: string, change: SessionChange, isUnder: (task: string, ancestor: string) => boolean): boolean;
51
- export declare function createSessionsClient({ name, claude, report, isGranted }: {
58
+ export type HookOn = (hook: string, listener: (input: unknown) => HookFields | undefined | Promise<HookFields | undefined>) => Disposable;
59
+ export declare function createSessionsClient({ name, claude, report, isGranted, hookOn, routeOn, permissionRoutes }: {
52
60
  readonly name: string;
53
61
  readonly claude: Claude;
54
62
  readonly report: (id: string, message: string, error: unknown) => void;
55
63
  readonly isGranted: (permission: string) => boolean;
64
+ readonly hookOn?: HookOn;
65
+ readonly routeOn?: RouteOn;
66
+ readonly permissionRoutes?: () => PermissionRoutes | undefined;
56
67
  }): SessionsClient;
57
- export declare function resultOf(where: Pick<Place, 'session' | 'context' | 'turn'>, answered: Awaited<ReturnType<Claude['tool']['call']>>): ToolResult;
68
+ export type ToolCheck = {
69
+ readonly tool: string;
70
+ readonly input: unknown;
71
+ readonly tool_use_id?: string;
72
+ readonly agentId?: string;
73
+ readonly ceiling?: 'allow' | 'ask' | 'deny';
74
+ };
75
+ export type RouteOn = {
76
+ (event: 'tool.check', hook: (e: ToolCheck, next: (e: ToolCheck) => Promise<unknown>) => Promise<unknown>): Disposable;
77
+ (event: 'command.run', hook: (e: Args<'command.run'>, next: (e: Args<'command.run'>) => Promise<CommandRunResult>) => Promise<CommandRunResult>): Disposable;
78
+ };
79
+ export declare const recordOf: (value: unknown) => Readonly<Record<string, unknown>>;
80
+ export declare const defined: (fields: HookFields) => HookFields;
81
+ type ProcessFieldsName = Exclude<ProcessName, 'PermissionCheck'>;
82
+ export declare function spawnTask(claude: Pick<Claude, 'agent'>, task: TaskStartArgs): Promise<TaskData>;
83
+ export declare function processPayloadOf(named: ProcessFieldsName, input: unknown): HookFields;
84
+ export declare function resultOf(where: Pick<Place, 'session' | 'context' | 'turn'>, call: ToolCallArgs & {
85
+ readonly cwd: string;
86
+ }, answered: Awaited<ReturnType<Claude['tool']['call']>>): ToolResult;
87
+ export {};
58
88
  //# sourceMappingURL=sessions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../src/runtime/sessions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../src/runtime/sessions.ts"],"names":[],"mappings":"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"}