@cmodjs/core 4.0.3 → 4.1.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.
Files changed (141) hide show
  1. package/docs/commands.md +43 -8
  2. package/docs/index.md +3 -3
  3. package/docs/install-steps.md +6 -6
  4. package/docs/jobs.md +2 -1
  5. package/docs/mod.md +1 -1
  6. package/docs/options.md +1 -1
  7. package/docs/permissions.md +1 -1
  8. package/docs/session.md +44 -13
  9. package/docs/testing.md +14 -10
  10. package/docs/ui.md +42 -10
  11. package/installer.d.ts +3 -2
  12. package/installer.d.ts.map +1 -1
  13. package/jobs/program.d.ts +1 -0
  14. package/jobs/program.d.ts.map +1 -1
  15. package/jobs/program.js +39 -23
  16. package/mod.d.ts +3 -2
  17. package/mod.d.ts.map +1 -1
  18. package/mod.js +1 -0
  19. package/package.json +1 -1
  20. package/records.d.ts +15 -1
  21. package/records.d.ts.map +1 -1
  22. package/records.js +39 -1
  23. package/register.d.ts.map +1 -1
  24. package/register.js +5 -2
  25. package/runtime/before.d.ts +6 -2
  26. package/runtime/before.d.ts.map +1 -1
  27. package/runtime/before.js +56 -18
  28. package/runtime/buttons.d.ts +10 -0
  29. package/runtime/buttons.d.ts.map +1 -0
  30. package/runtime/buttons.js +12 -0
  31. package/runtime/conversations.d.ts.map +1 -1
  32. package/runtime/conversations.js +134 -25
  33. package/runtime/engine.d.ts +2 -0
  34. package/runtime/engine.d.ts.map +1 -1
  35. package/runtime/grants.d.ts +1 -0
  36. package/runtime/grants.d.ts.map +1 -1
  37. package/runtime/grants.js +5 -2
  38. package/runtime/hotkeys.d.ts.map +1 -1
  39. package/runtime/hotkeys.js +7 -4
  40. package/runtime/installer.d.ts +9 -6
  41. package/runtime/installer.d.ts.map +1 -1
  42. package/runtime/installer.js +104 -47
  43. package/runtime/lifecycle.d.ts.map +1 -1
  44. package/runtime/lifecycle.js +121 -40
  45. package/runtime/pages.d.ts +3 -1
  46. package/runtime/pages.d.ts.map +1 -1
  47. package/runtime/pages.js +34 -10
  48. package/runtime/parts.d.ts +1 -0
  49. package/runtime/parts.d.ts.map +1 -1
  50. package/runtime/parts.js +6 -3
  51. package/runtime/programs.d.ts +1 -0
  52. package/runtime/programs.d.ts.map +1 -1
  53. package/runtime/programs.js +4 -1
  54. package/runtime/sessions.d.ts +2 -2
  55. package/runtime/sessions.d.ts.map +1 -1
  56. package/runtime/sessions.js +88 -58
  57. package/runtime/toaster.d.ts.map +1 -1
  58. package/runtime/toaster.js +23 -9
  59. package/runtime/ui.d.ts +21 -4
  60. package/runtime/ui.d.ts.map +1 -1
  61. package/runtime/ui.js +158 -58
  62. package/runtime/watches.d.ts.map +1 -1
  63. package/runtime/watches.js +18 -4
  64. package/runtime/widgets.d.ts +7 -5
  65. package/runtime/widgets.d.ts.map +1 -1
  66. package/runtime/widgets.js +29 -20
  67. package/session/context/turn/message/attached-file.d.ts +13 -0
  68. package/session/context/turn/message/attached-file.d.ts.map +1 -0
  69. package/session/context/turn/message/attached-file.js +21 -0
  70. package/session/context/turn/message/message.d.ts +3 -2
  71. package/session/context/turn/message/message.d.ts.map +1 -1
  72. package/session/context/turn/message/message.js +20 -6
  73. package/session/conversation.d.ts +14 -3
  74. package/session/conversation.d.ts.map +1 -1
  75. package/session/conversation.js +23 -4
  76. package/session/goal/task.d.ts +5 -0
  77. package/session/goal/task.d.ts.map +1 -1
  78. package/session/session.d.ts +35 -7
  79. package/session/session.d.ts.map +1 -1
  80. package/session/session.js +2 -1
  81. package/src/installer.ts +3 -2
  82. package/src/jobs/program.ts +41 -21
  83. package/src/mod.ts +3 -2
  84. package/src/records.ts +49 -1
  85. package/src/register.ts +9 -6
  86. package/src/runtime/before.ts +61 -22
  87. package/src/runtime/buttons.ts +22 -0
  88. package/src/runtime/conversations.ts +138 -32
  89. package/src/runtime/engine.ts +2 -0
  90. package/src/runtime/grants.ts +6 -2
  91. package/src/runtime/hotkeys.ts +7 -4
  92. package/src/runtime/installer.ts +144 -82
  93. package/src/runtime/lifecycle.ts +128 -43
  94. package/src/runtime/pages.ts +45 -8
  95. package/src/runtime/parts.ts +8 -4
  96. package/src/runtime/programs.ts +5 -1
  97. package/src/runtime/sessions.ts +83 -57
  98. package/src/runtime/toaster.ts +27 -9
  99. package/src/runtime/ui.ts +163 -61
  100. package/src/runtime/watches.ts +20 -5
  101. package/src/runtime/widgets.ts +34 -22
  102. package/src/session/context/turn/message/attached-file.ts +36 -0
  103. package/src/session/context/turn/message/message.ts +20 -7
  104. package/src/session/conversation.ts +30 -6
  105. package/src/session/goal/task.ts +6 -0
  106. package/src/session/session.ts +17 -7
  107. package/src/testing/fake-claude.ts +21 -6
  108. package/src/testing/fake-elements.ts +51 -8
  109. package/src/testing/fake-sessions.ts +60 -23
  110. package/src/testing.ts +86 -28
  111. package/src/ui/areas.ts +1 -0
  112. package/src/ui/components.ts +14 -8
  113. package/src/ui/elements.ts +6 -2
  114. package/src/ui/markdown.ts +3 -3
  115. package/src/ui/panes.ts +2 -2
  116. package/src/ui/styles.ts +6 -2
  117. package/testing/fake-claude.d.ts +3 -0
  118. package/testing/fake-claude.d.ts.map +1 -1
  119. package/testing/fake-claude.js +18 -5
  120. package/testing/fake-elements.d.ts.map +1 -1
  121. package/testing/fake-elements.js +46 -7
  122. package/testing/fake-sessions.d.ts +21 -10
  123. package/testing/fake-sessions.d.ts.map +1 -1
  124. package/testing/fake-sessions.js +58 -20
  125. package/testing.d.ts +8 -12
  126. package/testing.d.ts.map +1 -1
  127. package/testing.js +82 -21
  128. package/ui/areas.d.ts +4 -0
  129. package/ui/areas.d.ts.map +1 -1
  130. package/ui/components.d.ts +1 -0
  131. package/ui/components.d.ts.map +1 -1
  132. package/ui/components.js +11 -7
  133. package/ui/elements.d.ts +3 -0
  134. package/ui/elements.d.ts.map +1 -1
  135. package/ui/elements.js +6 -1
  136. package/ui/markdown.d.ts +2 -2
  137. package/ui/markdown.d.ts.map +1 -1
  138. package/ui/panes.d.ts +2 -1
  139. package/ui/panes.d.ts.map +1 -1
  140. package/ui/styles.d.ts +11 -2
  141. package/ui/styles.d.ts.map +1 -1
package/runtime/pages.js CHANGED
@@ -1,13 +1,16 @@
1
1
  import { isObject } from '../records.js';
2
+ import { Text } from '../ui/elements.js';
2
3
  import { messageOf } from '../utils/text.js';
4
+ import { cmodPluginName } from './dependencies.js';
3
5
  import { partMethods } from './ui.js';
4
6
  const askedWaitMs = 1000;
5
7
  export function pagesOf(input) {
6
8
  const listed = isObject(input) && Array.isArray(input['pages']) ? input['pages'] : [];
7
9
  return listed.filter(isObject).flatMap((each) => (typeof each['id'] === 'string' && typeof each['title'] === 'string' && (each['place'] === 'sidebar' || each['place'] === 'settings') ? [{ id: each['id'], title: each['title'], place: each['place'] }] : []));
8
10
  }
9
- export function servePages({ claude, router, report, widgets, parts }) {
11
+ export function servePages({ claude, router, report, widgets, parts, tellStylers, }) {
10
12
  const declared = new Map();
13
+ const stylers = new Set();
11
14
  const asking = new Map();
12
15
  const pressing = new Set();
13
16
  let waiting = [];
@@ -20,16 +23,18 @@ export function servePages({ claude, router, report, widgets, parts }) {
20
23
  for (const { mod, requests } of taken)
21
24
  await run(mod, requests);
22
25
  };
23
- const settle = async () => {
24
- while (pressing.size > 0)
25
- await Promise.allSettled(pressing);
26
+ const settle = async (earlier) => {
27
+ const own = () => [...pressing].filter((pending) => !earlier.has(pending));
28
+ for (let pending = own(); pending.length > 0; pending = own())
29
+ await Promise.allSettled(pending);
26
30
  await takeWaiting();
27
31
  };
28
32
  const wrapped = async (next) => {
29
33
  inputDepth += 1;
34
+ const earlier = new Set(pressing);
30
35
  try {
31
36
  const answer = await next();
32
- await settle();
37
+ await settle(earlier);
33
38
  return answer;
34
39
  }
35
40
  finally {
@@ -47,11 +52,27 @@ export function servePages({ claude, router, report, widgets, parts }) {
47
52
  asking.set(mod, pending);
48
53
  return pending;
49
54
  };
55
+ const restyle = (mod, isStyler) => {
56
+ if (!isStyler && !stylers.delete(mod))
57
+ return;
58
+ if (isStyler)
59
+ stylers.add(mod);
60
+ parts.forget(mod);
61
+ const listed = [...stylers];
62
+ tellStylers(listed);
63
+ for (const told of declared.keys()) {
64
+ if (told !== cmodPluginName)
65
+ void claude.cmod.call({ to: told, method: partMethods.stylers, input: listed }).catch((error) => claude.ui.log(`cmod: ${told} did not hear which mods style markdown: ${messageOf(error)}`, { to: 'debug' }));
66
+ }
67
+ };
50
68
  const update = (mod, { parts: listing, redraw, requests, isAsked }) => {
51
69
  if (listing !== undefined)
52
70
  declared.set(mod, pagesOf(listing));
53
- if (listing !== undefined || redraw === true)
54
- widgets.update(mod, { ...(listing === undefined ? {} : { parts: listing }), ...(redraw === undefined ? {} : { redraw }) });
71
+ if (listing !== undefined)
72
+ restyle(mod, isObject(listing) && listing['stylesMarkdown'] === true);
73
+ const isRedrawn = redraw === true || requests?.some((request) => 'open' in request) === true;
74
+ if (listing !== undefined || isRedrawn)
75
+ widgets.update(mod, { ...(listing === undefined ? {} : { parts: listing }), ...(isRedrawn ? { redraw: true } : {}) });
55
76
  if (requests === undefined || requests.length === 0)
56
77
  return false;
57
78
  if (isAsked !== true)
@@ -76,20 +97,23 @@ export function servePages({ claude, router, report, widgets, parts }) {
76
97
  void pending.then(takeWaiting);
77
98
  };
78
99
  const cacheKey = (page, params, columns, isFocused) => `${page}\u0000${JSON.stringify(params ?? null)}\u0000${columns}\u0000${isFocused}`;
100
+ const plain = (data) => JSON.parse(JSON.stringify(data));
79
101
  return {
80
102
  declared: () => [...declared].map(([mod, pages]) => ({ mod, pages })),
103
+ stylers: () => [...stylers],
81
104
  element(mod, page, params, columns, isFocused) {
82
105
  if (!declared.has(mod))
83
106
  void ask(mod);
84
107
  const cached = cacheKey(page, params, columns, isFocused);
85
108
  const name = `The ${page} page of ${mod}`;
86
- void parts.fill(mod, cached, { page, params: params ?? null, columns, isFocused }, name)?.then(() => claude.ui.invalidate('ui.render'));
87
- return parts.element(mod, cached, `${mod}/${page}:`, name, `page:${mod}/${page}`);
109
+ void parts.fill(mod, cached, { page, params: plain(params ?? null), columns, isFocused }, name)?.then(() => claude.ui.invalidate('ui.render'));
110
+ const failure = parts.failure(mod, cached);
111
+ return parts.element(mod, cached, `${mod}/${page}:`, name, `page:${mod}/${page}`) ?? (failure === undefined ? undefined : Text({ color: 'error', children: `${name} could not draw: ${failure}` }));
88
112
  },
89
113
  press,
90
114
  update,
91
115
  tell(mod, shown) {
92
- void claude.cmod.call({ to: mod, method: partMethods.shown, input: shown }).catch((error) => claude.ui.log(`cmod: ${mod} did not hear which of its pages show: ${messageOf(error)}`, { to: 'debug' }));
116
+ void claude.cmod.call({ to: mod, method: partMethods.shown, input: plain(shown) }).catch((error) => claude.ui.log(`cmod: ${mod} did not hear which of its pages show: ${messageOf(error)}`, { to: 'debug' }));
93
117
  },
94
118
  apply(listener) {
95
119
  applyTo = listener;
@@ -4,6 +4,7 @@ import { type PartPress } from './ui.js';
4
4
  export type DrawnParts = {
5
5
  fill(mod: string, cached: string, input: Readonly<Record<string, unknown>>, name: string): Promise<void> | undefined;
6
6
  element(mod: string, cached: string, keyPrefix: string, name: string, reportId: string): RenderElement | undefined;
7
+ failure(mod: string, cached: string): string | undefined;
7
8
  forget(mod: string): void;
8
9
  };
9
10
  export declare function drawnParts({ claude, report, press }: {
@@ -1 +1 @@
1
- {"version":3,"file":"parts.d.ts","sourceRoot":"","sources":["../src/runtime/parts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAGhD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAA+B,KAAK,SAAS,EAAE,MAAM,SAAS,CAAA;AAErE,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,SAAS,CAAA;IACpH,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAAA;IAClH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B,CAAA;AAMD,wBAAgB,UAAU,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,KAAK,IAAI,CAAA;CAAE,GAAG,UAAU,CAqD9N"}
1
+ {"version":3,"file":"parts.d.ts","sourceRoot":"","sources":["../src/runtime/parts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAGhD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAA+B,KAAK,SAAS,EAAE,MAAM,SAAS,CAAA;AAErE,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,SAAS,CAAA;IACpH,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAAA;IAClH,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;IACxD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B,CAAA;AAMD,wBAAgB,UAAU,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,KAAK,IAAI,CAAA;CAAE,GAAG,UAAU,CAwD9N"}
package/runtime/parts.js CHANGED
@@ -14,9 +14,9 @@ export function drawnParts({ claude, report, press }) {
14
14
  const generation = generationOf(mod);
15
15
  if (drawn.get(key)?.generation === generation)
16
16
  return undefined;
17
- const keep = (data) => {
17
+ const keep = (data, failure) => {
18
18
  drawn.delete(key);
19
- drawn.set(key, { data, generation });
19
+ drawn.set(key, { data, ...(failure === undefined ? {} : { failure }), generation });
20
20
  const [oldest] = drawn.keys();
21
21
  if (drawn.size > keptDrawings && oldest !== undefined)
22
22
  drawn.delete(oldest);
@@ -28,7 +28,7 @@ export function drawnParts({ claude, report, press }) {
28
28
  .call({ to: mod, method: partMethods.draw, input })
29
29
  .then((data) => keep((data ?? null)), (error) => {
30
30
  claude.ui.log(`cmod: ${name} drew nothing: ${messageOf(error)}`, { to: 'debug' });
31
- keep(null);
31
+ keep(drawn.get(key)?.data ?? null, messageOf(error));
32
32
  })
33
33
  .finally(() => drawing.delete(key));
34
34
  drawing.set(key, pending);
@@ -52,6 +52,9 @@ export function drawnParts({ claude, report, press }) {
52
52
  return undefined;
53
53
  }
54
54
  },
55
+ failure(mod, cached) {
56
+ return drawn.get(keyOf(mod, cached))?.failure;
57
+ },
55
58
  forget(mod) {
56
59
  generations.set(mod, generationOf(mod) + 1);
57
60
  },
@@ -10,6 +10,7 @@ export declare function readLines(stream: HookStream<ProcessSpawnChunk, ProcessS
10
10
  code: number | null;
11
11
  lastError: string;
12
12
  }>;
13
+ export declare function spawnCmod(claude: Claude, args: readonly string[], session: string, env?: Readonly<Record<string, string>>): HookStream<ProcessSpawnChunk, ProcessSpawnResult>;
13
14
  export declare function runCmod(claude: Claude, args: readonly string[], session: string): Promise<string>;
14
15
  export declare function locatingPrograms(claude: Claude): Programs;
15
16
  //# sourceMappingURL=programs.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"programs.d.ts","sourceRoot":"","sources":["../src/runtime/programs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAIpF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC,MAAM,MAAM,QAAQ,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAA;AAE5E,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAG3D;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAI7E;AAED,wBAAsB,SAAS,CAAC,MAAM,EAAE,UAAU,CAAC,iBAAiB,EAAE,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAgB9K;AAED,wBAAsB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAKvG;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAsBzD"}
1
+ {"version":3,"file":"programs.d.ts","sourceRoot":"","sources":["../src/runtime/programs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAIpF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC,MAAM,MAAM,QAAQ,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAA;AAE5E,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAG3D;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAI7E;AAED,wBAAsB,SAAS,CAAC,MAAM,EAAE,UAAU,CAAC,iBAAiB,EAAE,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAgB9K;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM,GAAG,UAAU,CAAC,iBAAiB,EAAE,kBAAkB,CAAC,CAEjL;AAED,wBAAsB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAKvG;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAsBzD"}
@@ -30,9 +30,12 @@ export async function readLines(stream, onLine) {
30
30
  onLine(pending);
31
31
  return { code: piece.value.code, lastError };
32
32
  }
33
+ export function spawnCmod(claude, args, session, env = {}) {
34
+ return claude.process.spawn({ argv: ['cmod', ...args], env: { ...env, CLAUDE_CODE_SESSION_ID: session } });
35
+ }
33
36
  export async function runCmod(claude, args, session) {
34
37
  const lines = [];
35
- const { code, lastError } = await readLines(claude.process.spawn({ argv: ['cmod', ...args], env: { CLAUDE_CODE_SESSION_ID: session } }), (line) => void lines.push(line));
38
+ const { code, lastError } = await readLines(spawnCmod(claude, args, session), (line) => void lines.push(line));
36
39
  if (code !== 0)
37
40
  throw new Error(cmodError(lastError, args[0]) || `cmod ${args.slice(0, 2).join(' ')} ${formatExit(code, '')}.`);
38
41
  return lines.join('\n');
@@ -31,7 +31,7 @@ export declare const conversationMethods: {
31
31
  readonly changed: 'cmod:changed';
32
32
  readonly before: 'cmod:before';
33
33
  };
34
- export type Act = 'send' | 'startTask' | 'callTool' | 'usage' | 'surfaces' | 'abort';
34
+ export type Act = 'send' | 'startTask' | 'fork' | 'clone' | 'startBackgroundTask' | 'callTool' | 'usage' | 'surfaces' | 'abort';
35
35
  export declare const beforePermission: (event: BeforeEvent) => string;
36
36
  export declare const isAct: (action: string) => action is Act;
37
37
  export declare function toolPermission(tool: string): string | undefined;
@@ -90,7 +90,7 @@ export type RouteOn = {
90
90
  export declare const recordOf: (value: unknown) => Readonly<Record<string, unknown>>;
91
91
  export declare const defined: (fields: HookFields) => HookFields;
92
92
  type ProcessFieldsName = Exclude<ProcessName, 'PermissionCheck'>;
93
- export declare function spawnTask(claude: Pick<Claude, 'agent'>, task: TaskStartArgs): Promise<TaskData>;
93
+ export declare function spawnTask(claude: Pick<Claude, 'agent'>, task: TaskStartArgs, forkedFrom?: string): Promise<TaskData>;
94
94
  export declare function processPayloadOf(named: ProcessFieldsName, input: unknown): HookFields;
95
95
  export declare function resultOf(where: Pick<Place, 'session' | 'context' | 'turn'>, call: ToolCallArgs & {
96
96
  readonly cwd: string;
@@ -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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"}
@@ -5,7 +5,7 @@ import { Person, senderOf } from '../session/context/turn/message/sender.js';
5
5
  import { ToolCall } from '../session/context/turn/message/tool-call.js';
6
6
  import { ToolResult } from '../session/context/turn/message/tool-result.js';
7
7
  import { TurnEnd, TurnStart } from '../session/context/turn/turn.js';
8
- import { searchLimit } from '../session/conversation.js';
8
+ import { forkAgentType, searchLimit } from '../session/conversation.js';
9
9
  import { GoalChange } from '../session/goal/goal.js';
10
10
  import { TaskEnd, TaskStart } from '../session/goal/task.js';
11
11
  import { beforeEvents, processEvents, Session, sessionEvents } from '../session/session.js';
@@ -32,10 +32,14 @@ export const conversationMethods = {
32
32
  changed: 'cmod:changed',
33
33
  before: 'cmod:before',
34
34
  };
35
- const actPermissions = { send: 'prompt', startTask: 'tasks', usage: 'conversation', surfaces: 'conversation', abort: 'prompt' };
35
+ const actPermissions = { send: 'prompt', startTask: 'tasks', fork: 'tasks', clone: 'tasks', startBackgroundTask: 'tasks', usage: 'conversation', surfaces: 'conversation', abort: 'prompt' };
36
36
  const startsTurns = ['CronCreate'];
37
37
  const ownSchedule = ['CronDelete', 'CronList'];
38
38
  export const beforePermission = (event) => (event === 'toolCall' ? 'tools' : event === TaskStart ? 'tasks' : 'prompt');
39
+ const answerPermissions = (event, answer) => [
40
+ ...(Object.keys(answer).some((key) => key !== 'note') ? [beforePermission(event)] : []),
41
+ ...(answer.note === undefined ? [] : ['prompt']),
42
+ ];
39
43
  export const isAct = (action) => action === 'callTool' || action in actPermissions;
40
44
  export function toolPermission(tool) {
41
45
  return startsTurns.includes(tool) ? 'prompt' : ownSchedule.includes(tool) ? undefined : 'tools';
@@ -46,7 +50,8 @@ export function actPermission(action, input) {
46
50
  export async function usageOf(claude) {
47
51
  const [model, usage] = await Promise.all([claude.session.model(), claude.session.usage()]);
48
52
  const { tokens, window, percent } = usage.context;
49
- return { model, contextWindow: { ...(tokens === undefined ? {} : { tokenCount: tokens }), size: window, ...(percent === undefined ? {} : { percent }) }, ...(usage.cost === undefined ? {} : { cost: { usd: usage.cost.usd } }) };
53
+ const rateLimits = usage.rateLimits.map(({ kind, percentUsed, resetsAt }) => ({ type: kind, percent: percentUsed, ...(resetsAt === undefined ? {} : { resetsAt }) }));
54
+ return { model, contextWindow: { ...(tokens === undefined ? {} : { tokenCount: tokens }), size: window, ...(percent === undefined ? {} : { percent }) }, rateLimits, ...(usage.cost === undefined ? {} : { cost: { usd: usage.cost.usd } }) };
50
55
  }
51
56
  export function payloadOf(change) {
52
57
  if (change.type === Message)
@@ -56,8 +61,9 @@ export function payloadOf(change) {
56
61
  if ('context' in change)
57
62
  return change.context;
58
63
  if ('task' in change) {
59
- const { id, title, agentType, model, openedBy, openedAt, closedAt } = change.task;
60
- return { id, title, openedBy, openedAt, ...(closedAt === undefined ? {} : { closedAt }), owner: { id, title, agentType, ...(model === undefined ? {} : { model }), startedAt: openedAt } };
64
+ const { id, title, agentType, model, openedBy, openedAt, closedAt, isBackground, forkedFrom } = change.task;
65
+ const owner = { id, title, ...(agentType === undefined ? {} : { agentType }), ...(model === undefined ? {} : { model }), ...(forkedFrom === undefined ? {} : { forkedFrom }), startedAt: openedAt };
66
+ return { id, title, openedBy, openedAt, ...(closedAt === undefined ? {} : { closedAt }), ...(isBackground === true ? { isBackground } : {}), owner };
61
67
  }
62
68
  if ('usage' in change)
63
69
  return change.usage;
@@ -195,9 +201,12 @@ export function createSessionsClient({ name, claude, report, isGranted, hookOn,
195
201
  return undefined;
196
202
  const payload = await payloadFor(session, latest);
197
203
  const answer = await invoke(entry.method, entry.options, async (time) => takenAnswer(entry.method, beforeAnswerKeys[event], await entry.listener(payload, time)), beforeAnswerKeys[event]);
198
- if (answer === undefined || answer === null || (typeof answer === 'object' && 'refuse' in answer) || isGranted(beforePermission(event)))
204
+ if (answer === undefined || answer === null || (typeof answer === 'object' && 'refuse' in answer))
199
205
  return answer;
200
- throw new Error(`Its ${entry.method} answer changes nothing until you grant ${name} "${permissionWords(beforePermission(event))}". ${grantFix}`);
206
+ const missing = answerPermissions(event, answer).find((permission) => !isGranted(permission));
207
+ if (missing === undefined)
208
+ return answer;
209
+ throw new Error(`Its ${entry.method} answer changes nothing until you grant ${name} "${permissionWords(missing)}". ${grantFix}`);
201
210
  });
202
211
  const answer = await foldBefore(event, change, askers, (error) => report(`session:${methodOf('before', event)}`, `The ${methodOf('before', event)} listener failed`, error));
203
212
  return answer?.note === undefined ? answer : { ...answer, note: modNote(name, answer.note) };
@@ -262,17 +271,25 @@ export function createSessionsClient({ name, claude, report, isGranted, hookOn,
262
271
  turns: async () => (await call(conversationMethods.turns, { ...owned, context: data.number })).map((turn) => turnOf(owned, turn)),
263
272
  };
264
273
  };
265
- const taskOf = (session, data) => ({
266
- id: data.id,
267
- title: data.title,
268
- openedBy: data.openedBy,
269
- openedAt: data.openedAt,
270
- ...(data.closedAt === undefined ? {} : { closedAt: data.closedAt }),
271
- owner: ownerOf(session, data),
272
- ...recordPart({ session, task: data.id }),
273
- tasks: async () => (await call(conversationMethods.tasks, { session, task: data.id })).map((task) => taskOf(session, task)),
274
- messages: () => messagesFrom(conversationMethods.messages, { session, task: data.id }),
275
- });
274
+ const taskOf = (session, data) => {
275
+ const record = data.isBackground === true ? { session: data.id } : { session, task: data.id };
276
+ return {
277
+ id: data.id,
278
+ title: data.title,
279
+ openedBy: data.openedBy,
280
+ openedAt: data.openedAt,
281
+ ...(data.closedAt === undefined ? {} : { closedAt: data.closedAt }),
282
+ ...(data.status === undefined ? {} : { status: data.status }),
283
+ ...(data.name === undefined ? {} : { name: data.name }),
284
+ ...(data.spawnedBy === undefined ? {} : { spawnedBy: data.spawnedBy }),
285
+ ...(data.isBackground === true ? { isBackground: true } : {}),
286
+ owner: ownerOf(session, data),
287
+ ...recordPart({ session, task: data.id }),
288
+ search: (text, matcher) => searched({ text, ...record }, matcher),
289
+ tasks: async () => (await call(conversationMethods.tasks, record)).map((task) => taskOf(record.session, task)),
290
+ messages: () => messagesFrom(conversationMethods.messages, record),
291
+ };
292
+ };
276
293
  const goalOf = (where) => ({
277
294
  search: (text, matcher) => searched({ text, ...where() }, matcher),
278
295
  ...eventMethods(() => ({ ...where(), isGoal: true }), true),
@@ -317,9 +334,8 @@ export function createSessionsClient({ name, claude, report, isGranted, hookOn,
317
334
  if ('drop' in submitted && typeof submitted.drop === 'string')
318
335
  throw new Error(`${name}: Claude Code dropped the prompt: ${submitted.drop}`);
319
336
  };
320
- const startHere = async (task) => taskOf(current.id, await spawnTask(claude, task).catch((error) => Promise.reject(new Error(`${name}: ${messageOf(error)}`))));
321
337
  const surfacesHere = async () => [...(await claude.session.surfaces())];
322
- const sessionOf = (read, owned) => {
338
+ const sessionOf = (read, owned, isOwner = owned !== undefined) => {
323
339
  const id = () => read().id;
324
340
  const isHere = () => owned === undefined && id() === current.id;
325
341
  const scope = () => (owned === undefined ? { session: id(), isAgent: true } : { session: owned.session, task: owned.task, isAgent: true });
@@ -384,31 +400,31 @@ export function createSessionsClient({ name, claude, report, isGranted, hookOn,
384
400
  await act(id(), 'send', message, owned);
385
401
  },
386
402
  };
387
- if (owned !== undefined)
403
+ if (isOwner)
388
404
  return session;
405
+ const started = async (starting) => taskOf(id(), await starting.catch((error) => Promise.reject(new Error(`${name}: ${messageOf(error)}`))));
389
406
  return Object.assign(session, {
390
- async startTask(task) {
391
- if (isHere())
392
- return startHere(task);
393
- const started = await act(id(), 'startTask', task);
394
- return taskOf(id(), started);
395
- },
407
+ startTask: (task) => started(task.isBackground === true ? act(id(), 'startBackgroundTask', task) : isHere() ? spawnTask(claude, task) : act(id(), 'startTask', task)),
408
+ fork: (task) => started(isHere() ? spawnTask(claude, task, current.id) : act(id(), 'fork', task)),
409
+ clone: (task) => started(act(id(), 'clone', task)),
396
410
  callTool: async (toolCall) => (await withFiles(await act(id(), 'callTool', toolCall))),
397
411
  });
398
412
  };
399
413
  const ownerOf = (session, data) => {
400
414
  const parent = session === current.id ? current : known.get(session);
415
+ const isBackground = data.isBackground === true;
401
416
  return sessionOf(() => ({
402
417
  id: data.id,
403
418
  title: data.title,
404
419
  project: parent?.project ?? '',
405
- agentType: data.agentType,
420
+ ...(data.agentType === undefined ? {} : { agentType: data.agentType }),
406
421
  model: data.model ?? '',
407
422
  cwd: parent?.cwd ?? '',
408
- isRunning: data.closedAt === undefined && (parent?.isRunning ?? true),
423
+ isRunning: data.closedAt === undefined && (isBackground || (parent?.isRunning ?? true)),
409
424
  startedAt: data.openedAt,
410
425
  lastAt: data.closedAt ?? data.openedAt,
411
- }), { session, task: data.id });
426
+ ...(data.forkedFrom === undefined ? {} : { forkedFrom: data.forkedFrom }),
427
+ }), isBackground ? undefined : { session, task: data.id }, true);
412
428
  };
413
429
  const processListeners = new Map();
414
430
  const processHearers = (method, payload) => [...(processListeners.get(method) ?? [])].filter(({ matcher, isEveryAgent }) => (payload['task'] === undefined || isEveryAgent) && (matcher === undefined || matches(payload, matcher)));
@@ -464,7 +480,12 @@ export function createSessionsClient({ name, claude, report, isGranted, hookOn,
464
480
  const event = processEvents[named];
465
481
  const hook = (isBefore ? event.before : undefined) ?? event.on;
466
482
  if (method === 'beforeSessionStart')
467
- routeOn('session.compact', compactionNote);
483
+ routeOn('session.compact', trackCompaction);
484
+ if (method === 'beforeModelChange')
485
+ hookOn(event.on, () => {
486
+ const notes = modelNotes.splice(0);
487
+ return notes.length === 0 ? undefined : { additionalContext: notes };
488
+ });
468
489
  const failed = (input, error) => {
469
490
  const refusal = refusalFor(method, processPayloadOf(named, input), error);
470
491
  return refusal === undefined ? undefined : hookAnswerOf(name, named, refusal);
@@ -472,34 +493,40 @@ export function createSessionsClient({ name, claude, report, isGranted, hookOn,
472
493
  return hookOn(hook, async (input) => {
473
494
  const payload = processPayloadOf(named, input);
474
495
  if (named === 'SessionStart' && payload['source'] === 'compact') {
475
- const started = isBefore ? compactionStarts.shift() : undefined;
476
- return started === undefined ? undefined : hookAnswerOf(name, named, started);
496
+ if (!isBefore)
497
+ return undefined;
498
+ const task = compacting?.agentId;
499
+ const model = task === undefined ? await claude.session.model() : undefined;
500
+ const answer = await raise(method, { ...payload, ...defined({ task, model, title: current.title || undefined }) });
501
+ if (answer === undefined)
502
+ return undefined;
503
+ return hookAnswerOf(name, named, task === undefined ? answer : defined({ note: answer['note'] }));
477
504
  }
478
505
  if (!isBefore) {
479
506
  void raise(method, payload);
480
507
  return undefined;
481
508
  }
509
+ if (named === 'ModelChange')
510
+ modelNotes.splice(0);
482
511
  const answer = await raise(method, payload);
483
- return answer === undefined ? undefined : hookAnswerOf(name, named, answer);
512
+ if (answer === undefined)
513
+ return undefined;
514
+ if (named === 'ModelChange' && typeof answer['note'] === 'string' && answer['refuse'] === undefined)
515
+ modelNotes.push(modNote(name, answer['note']));
516
+ return hookAnswerOf(name, named, answer);
484
517
  }, isBefore ? failed : undefined);
485
518
  };
486
- const compactionStarts = [];
487
- const compactionNote = async (e, next) => {
488
- const compacted = await next(e);
489
- if (e.trigger === 'precompute' || compacted.skip !== undefined)
490
- return compacted;
491
- const model = e.agentId === undefined ? await claude.session.model() : undefined;
492
- const answer = await raise('beforeSessionStart', { session: current.id, cwd: current.cwd, ...defined({ task: e.agentId, model, title: current.title || undefined, contextTokenCount: compacted.tokensAfter }), source: 'compact' });
493
- const { note, ...started } = answer ?? {};
494
- if (Object.keys(started).length > 0)
495
- compactionStarts.push(started);
496
- if (typeof note !== 'string')
497
- return compacted;
498
- if (!isGranted('prompt')) {
499
- report('session:beforeSessionStart', 'The beforeSessionStart listener failed', new Error(`${name}: its note adds text Claude reads, which needs the prompt permission.`));
500
- return compacted;
519
+ const modelNotes = [];
520
+ let compacting;
521
+ const trackCompaction = async (e, next) => {
522
+ compacting = e;
523
+ try {
524
+ return await next(e);
525
+ }
526
+ finally {
527
+ if (compacting === e)
528
+ compacting = undefined;
501
529
  }
502
- return { ...compacted, messages: [...compacted.messages, { role: 'user', text: modNote(name, note), toolUses: [] }] };
503
530
  };
504
531
  const listen = (method, args, isEveryAgent = false) => {
505
532
  const heard = listenerOf(name, method, args);
@@ -604,9 +631,9 @@ const modelChangeOf = (input) => ({
604
631
  });
605
632
  const answerKeys = {
606
633
  Run: ['args', 'note', 'result', 'exitCode', 'refuse'],
607
- ProjectSetup: [],
634
+ ProjectSetup: ['note'],
608
635
  SessionStart: ['note', 'title', 'reloadSkills', 'firstPrompt'],
609
- ModelChange: ['ask', 'refuse'],
636
+ ModelChange: ['ask', 'refuse', 'note'],
610
637
  WorktreeCreate: ['result'],
611
638
  Elicitation: ['refuse'],
612
639
  ElicitationResponse: ['refuse'],
@@ -651,22 +678,23 @@ const processFields = {
651
678
  TodoComplete: todoOf,
652
679
  ReplyDisplay: (input) => ({ replyId: input['message_id'], index: input['index'], isLast: input['final'], text: input['delta'] }),
653
680
  };
654
- export async function spawnTask(claude, task) {
681
+ export async function spawnTask(claude, task, forkedFrom) {
655
682
  if (task.title.trim() === '')
656
683
  throw new Error('a task needs a title.');
684
+ const { name, agentType, model, cwd } = forkedFrom === undefined ? task : { prompt: task.prompt, title: task.title, agentType: forkAgentType };
657
685
  const started = await claude.agent.spawn({
658
686
  prompt: task.prompt,
659
687
  description: task.title,
660
- ...(task.name === undefined ? {} : { name: task.name }),
661
- ...(task.agentType === undefined ? {} : { subagentType: task.agentType }),
662
- ...(task.model === undefined ? {} : { model: task.model }),
663
- ...(task.cwd === undefined ? {} : { cwd: task.cwd }),
688
+ ...(name === undefined ? {} : { name }),
689
+ ...(agentType === undefined ? {} : { subagentType: agentType }),
690
+ ...(model === undefined ? {} : { model }),
691
+ ...(cwd === undefined ? {} : { cwd }),
664
692
  });
665
693
  if (started.deny !== undefined)
666
694
  throw new Error(`Claude Code refused the task: ${started.deny}`);
667
695
  if (started.agentId === undefined)
668
696
  throw new Error('Claude Code answered without starting a task.');
669
- return { id: started.agentId, title: task.title, agentType: task.agentType ?? 'general-purpose', model: started.model, openedBy: '', openedAt: new Date().toISOString() };
697
+ return { id: started.agentId, title: task.title, ...(forkedFrom === undefined ? { agentType: agentType ?? 'general-purpose' } : { forkedFrom }), model: started.model, openedBy: '', openedAt: new Date().toISOString() };
670
698
  }
671
699
  export function processPayloadOf(named, input) {
672
700
  const fields = recordOf(input);
@@ -682,6 +710,8 @@ function hookAnswerOf(name, named, answer) {
682
710
  return { permissionDecision: 'deny', permissionDecisionReason: refuse };
683
711
  return typeof answer['ask'] === 'string' ? { permissionDecision: 'ask', permissionDecisionReason: answer['ask'] } : undefined;
684
712
  }
713
+ if (named === 'ProjectSetup')
714
+ return defined({ additionalContext: note });
685
715
  if (named === 'WorktreeCreate')
686
716
  return defined({ worktreePath: answer['result'] });
687
717
  if (named === 'PermissionDenial')
@@ -1 +1 @@
1
- {"version":3,"file":"toaster.d.ts","sourceRoot":"","sources":["../src/runtime/toaster.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAS,MAAM,aAAa,CAAA;AACpF,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAgB,SAAS,EAAE,MAAM,WAAW,CAAA;AAIxE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC,MAAM,MAAM,UAAU,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAE9G,MAAM,MAAM,OAAO,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,OAAO,CAAA;CAAE,CAAA;AAEhK,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;IACrB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACjC,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAA;IAC/B,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAA;IACrC,IAAI,CAAC,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,CAAC,aAAa,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,aAAa,CAAA;CACnH,CAAA;AAuCD,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,MAAM,GAAG,SAAS,EAAE,eAAe,EAAE,MAAM,OAAO,GAAG,OAAO,CAqQtI"}
1
+ {"version":3,"file":"toaster.d.ts","sourceRoot":"","sources":["../src/runtime/toaster.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAS,MAAM,aAAa,CAAA;AACpF,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAgB,SAAS,EAAE,MAAM,WAAW,CAAA;AAIxE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC,MAAM,MAAM,UAAU,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAE9G,MAAM,MAAM,OAAO,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,OAAO,CAAA;CAAE,CAAA;AAEhK,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;IACrB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACjC,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAA;IAC/B,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAA;IACrC,IAAI,CAAC,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,CAAC,aAAa,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,aAAa,CAAA;CACnH,CAAA;AA2CD,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,MAAM,GAAG,SAAS,EAAE,eAAe,EAAE,MAAM,OAAO,GAAG,OAAO,CA+QtI"}
@@ -8,6 +8,8 @@ const defaultDuration = 4000;
8
8
  const visibleToasts = 3;
9
9
  const spinner = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
10
10
  const spinnerMs = 100;
11
+ const spinFrames = 10_000 / spinnerMs;
12
+ const still = '…';
11
13
  const icons = {
12
14
  default: { glyph: ' ' },
13
15
  success: { glyph: '✓', color: 'success' },
@@ -31,15 +33,19 @@ export function createToaster(claude, name, version, openPermissions) {
31
33
  let isHeld = false;
32
34
  let showsAll = false;
33
35
  let bandId;
36
+ const isLoading = (entry) => entry.type === 'loading' || entry.trying !== undefined;
37
+ const isSpinning = (entry) => entry.spinsFrom !== undefined && frame - entry.spinsFrom < spinFrames;
34
38
  const changed = () => {
35
- const isLoading = [...entries.values()].some((entry) => entry.type === 'loading' || entry.trying !== undefined);
36
- if (isLoading && spinning === undefined) {
39
+ for (const entry of entries.values())
40
+ entry.spinsFrom = isLoading(entry) ? (entry.spinsFrom ?? frame) : undefined;
41
+ const spins = [...entries.values()].some(isSpinning);
42
+ if (spins && spinning === undefined) {
37
43
  spinning = claude.clock.every(spinnerMs, () => {
38
44
  frame += 1;
39
- claude.ui.invalidate('ui.render');
45
+ changed();
40
46
  });
41
47
  }
42
- if (!isLoading && spinning !== undefined) {
48
+ if (!spins && spinning !== undefined) {
43
49
  spinning.cancel();
44
50
  spinning = undefined;
45
51
  }
@@ -83,7 +89,7 @@ export function createToaster(claude, name, version, openPermissions) {
83
89
  if (previous !== undefined)
84
90
  disarm(previous);
85
91
  const reporting = hides !== undefined && previous?.hides === hides ? previous.reporting : undefined;
86
- const entry = { id, type, message, options, actions, ...(hides === undefined ? {} : { hides }), isOpen: previous?.isOpen ?? false, reporting, timer: undefined };
92
+ const entry = { id, type, message, options, actions, ...(hides === undefined ? {} : { hides }), at: new Date(), isOpen: previous?.isOpen ?? false, reporting, timer: undefined };
87
93
  if (type === 'error' && previous?.message !== message)
88
94
  claude.ui.log(`${name}: ${message}${typeof options.description === 'string' ? `: ${options.description}` : ''}`, { to: 'debug' });
89
95
  entries.set(id, entry);
@@ -134,15 +140,20 @@ export function createToaster(claude, name, version, openPermissions) {
134
140
  return id;
135
141
  hidden.delete(id);
136
142
  const shown = entries.get(id);
137
- if (shown?.hides === hides && shown.trying === undefined && shown.options.description === description)
143
+ if (shown?.hides === hides && shown.trying === undefined && shown.options.description === description) {
144
+ const previous = clockTime(shown.at);
145
+ shown.at = new Date();
146
+ if (clockTime(shown.at) !== previous)
147
+ changed();
138
148
  return id;
149
+ }
139
150
  const stack = error instanceof Error && error.stack !== undefined ? `\n\n${error.stack}` : '';
140
151
  const details = async () => {
141
152
  const modVersion = await claude.fs
142
153
  .read(`${claude.plugin.root}/.claude-plugin/plugin.json`)
143
154
  .then((text) => String(JSON.parse(text).version))
144
155
  .catch((failure) => `unknown (${messageOf(failure)})`);
145
- return `${name} ${modVersion}, @cmodjs/core ${coreVersion}\n${message}${description === undefined ? '' : `\n${description}`}${stack}`;
156
+ return `${name} ${modVersion}, @cmodjs/core ${coreVersion}, at ${(entries.get(id)?.at ?? new Date()).toISOString()}\n${message}${description === undefined ? '' : `\n${description}`}${stack}`;
146
157
  };
147
158
  const startReporting = async () => {
148
159
  const shown = entries.get(id);
@@ -190,13 +201,13 @@ export function createToaster(claude, name, version, openPermissions) {
190
201
  const row = (entry) => {
191
202
  const { description } = entry.options;
192
203
  const isText = typeof description === 'string';
193
- const icon = entry.type === 'loading' || entry.trying !== undefined ? { glyph: spinner[frame % spinner.length], color: 'claude' } : icons[entry.type];
204
+ const icon = entry.type === 'loading' || entry.trying !== undefined ? { glyph: isSpinning(entry) ? spinner[frame % spinner.length] : still, color: 'claude' } : icons[entry.type];
194
205
  const message = isText ? Button({ plain: true, key: `toast:${entry.id}:open`, label: entry.message, onPress: toggle(entry) }) : Text({ children: entry.message });
195
206
  const details = !isText ? description : Text({ dimColor: true, wrap: entry.isOpen ? 'wrap' : 'truncate-end', children: entry.isOpen ? description : firstLine(description) });
196
207
  const fixed = (child) => Box({ flexShrink: 0, children: child });
197
208
  const close = Button({ plain: true, dimColor: true, key: `toast:${entry.id}:close`, label: entry.hides === undefined ? '✕' : 'Hide', onPress: () => hide(entry) });
198
209
  const actions = entry.actions.map((action, index) => Button({ plain: true, key: `toast:${entry.id}:${index === 0 ? 'action' : `action-${index}`}`, label: action.label, onPress: press(entry, action.onClick) }));
199
- const buttons = Box({ gap: 2, flexWrap: 'wrap', children: [...actions, close] });
210
+ const buttons = Box({ gap: 2, flexWrap: 'wrap', children: [...actions, close, Text({ dimColor: true, children: clockTime(entry.at) })].map(fixed) });
200
211
  const trying = entry.trying === undefined ? undefined : Box({ gap: 1, children: [Text({ dimColor: true, children: 'Trying again:' }), typeof entry.trying === 'string' ? Text({ dimColor: true, wrap: 'truncate-end', children: entry.trying }) : entry.trying] });
201
212
  const text = details === undefined ? [message] : [message, trying ?? details];
202
213
  const under = trying !== undefined && details === undefined ? [trying] : entry.reporting === undefined ? [buttons] : reportChoices(entry, entry.reporting);
@@ -316,3 +327,6 @@ function reportPrompt(name, contact, details) {
316
327
  : `Email it to ${contact.address}.`;
317
328
  return `The ${name} mod failed. Report it to its author. ${where} First check whether it is reported already, and add to that report instead of filing a new one. Show me the report before you post it.\n\n${details}`;
318
329
  }
330
+ function clockTime(at) {
331
+ return `${String(at.getHours()).padStart(2, '0')}:${String(at.getMinutes()).padStart(2, '0')}`;
332
+ }