exempclaw 0.4.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 (201) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +306 -0
  3. package/dist/agent/agent.d.ts +91 -0
  4. package/dist/agent/agent.js +258 -0
  5. package/dist/agent/agent.js.map +1 -0
  6. package/dist/agent/config.d.ts +49 -0
  7. package/dist/agent/config.js +58 -0
  8. package/dist/agent/config.js.map +1 -0
  9. package/dist/agent/persona.d.ts +39 -0
  10. package/dist/agent/persona.js +81 -0
  11. package/dist/agent/persona.js.map +1 -0
  12. package/dist/agents/registry.d.ts +21 -0
  13. package/dist/agents/registry.js +51 -0
  14. package/dist/agents/registry.js.map +1 -0
  15. package/dist/cli/approve.d.ts +17 -0
  16. package/dist/cli/approve.js +50 -0
  17. package/dist/cli/approve.js.map +1 -0
  18. package/dist/cli/chat.d.ts +16 -0
  19. package/dist/cli/chat.js +148 -0
  20. package/dist/cli/chat.js.map +1 -0
  21. package/dist/cli/demo.d.ts +7 -0
  22. package/dist/cli/demo.js +82 -0
  23. package/dist/cli/demo.js.map +1 -0
  24. package/dist/cli/init.d.ts +17 -0
  25. package/dist/cli/init.js +89 -0
  26. package/dist/cli/init.js.map +1 -0
  27. package/dist/cli/live.d.ts +10 -0
  28. package/dist/cli/live.js +109 -0
  29. package/dist/cli/live.js.map +1 -0
  30. package/dist/cli/offline.d.ts +23 -0
  31. package/dist/cli/offline.js +236 -0
  32. package/dist/cli/offline.js.map +1 -0
  33. package/dist/cli/probe.d.ts +23 -0
  34. package/dist/cli/probe.js +140 -0
  35. package/dist/cli/probe.js.map +1 -0
  36. package/dist/cli/render.d.ts +15 -0
  37. package/dist/cli/render.js +50 -0
  38. package/dist/cli/render.js.map +1 -0
  39. package/dist/cli/tui.d.ts +101 -0
  40. package/dist/cli/tui.js +334 -0
  41. package/dist/cli/tui.js.map +1 -0
  42. package/dist/config/index.d.ts +33 -0
  43. package/dist/config/index.js +48 -0
  44. package/dist/config/index.js.map +1 -0
  45. package/dist/connectors/connector.d.ts +58 -0
  46. package/dist/connectors/connector.js +30 -0
  47. package/dist/connectors/connector.js.map +1 -0
  48. package/dist/connectors/email/email-connector.d.ts +43 -0
  49. package/dist/connectors/email/email-connector.js +364 -0
  50. package/dist/connectors/email/email-connector.js.map +1 -0
  51. package/dist/connectors/github/github-connector.d.ts +52 -0
  52. package/dist/connectors/github/github-connector.js +271 -0
  53. package/dist/connectors/github/github-connector.js.map +1 -0
  54. package/dist/connectors/http.d.ts +34 -0
  55. package/dist/connectors/http.js +78 -0
  56. package/dist/connectors/http.js.map +1 -0
  57. package/dist/connectors/index.d.ts +34 -0
  58. package/dist/connectors/index.js +86 -0
  59. package/dist/connectors/index.js.map +1 -0
  60. package/dist/connectors/notion/notion-connector.d.ts +45 -0
  61. package/dist/connectors/notion/notion-connector.js +222 -0
  62. package/dist/connectors/notion/notion-connector.js.map +1 -0
  63. package/dist/connectors/slack/slack-connector.d.ts +43 -0
  64. package/dist/connectors/slack/slack-connector.js +291 -0
  65. package/dist/connectors/slack/slack-connector.js.map +1 -0
  66. package/dist/core/errors.d.ts +36 -0
  67. package/dist/core/errors.js +40 -0
  68. package/dist/core/errors.js.map +1 -0
  69. package/dist/core/logger.d.ts +14 -0
  70. package/dist/core/logger.js +44 -0
  71. package/dist/core/logger.js.map +1 -0
  72. package/dist/core/run-log.d.ts +37 -0
  73. package/dist/core/run-log.js +37 -0
  74. package/dist/core/run-log.js.map +1 -0
  75. package/dist/core/usage.d.ts +22 -0
  76. package/dist/core/usage.js +58 -0
  77. package/dist/core/usage.js.map +1 -0
  78. package/dist/dashboard/data.d.ts +62 -0
  79. package/dist/dashboard/data.js +84 -0
  80. package/dist/dashboard/data.js.map +1 -0
  81. package/dist/dashboard/page.d.ts +9 -0
  82. package/dist/dashboard/page.js +421 -0
  83. package/dist/dashboard/page.js.map +1 -0
  84. package/dist/dashboard/server.d.ts +19 -0
  85. package/dist/dashboard/server.js +44 -0
  86. package/dist/dashboard/server.js.map +1 -0
  87. package/dist/demo/bootstrap.d.ts +25 -0
  88. package/dist/demo/bootstrap.js +60 -0
  89. package/dist/demo/bootstrap.js.map +1 -0
  90. package/dist/demo/claude.d.ts +31 -0
  91. package/dist/demo/claude.js +230 -0
  92. package/dist/demo/claude.js.map +1 -0
  93. package/dist/demo/demo-connector.d.ts +19 -0
  94. package/dist/demo/demo-connector.js +168 -0
  95. package/dist/demo/demo-connector.js.map +1 -0
  96. package/dist/demo/world.d.ts +60 -0
  97. package/dist/demo/world.js +117 -0
  98. package/dist/demo/world.js.map +1 -0
  99. package/dist/index.d.ts +2 -0
  100. package/dist/index.js +396 -0
  101. package/dist/index.js.map +1 -0
  102. package/dist/ingest/ingest.d.ts +63 -0
  103. package/dist/ingest/ingest.js +258 -0
  104. package/dist/ingest/ingest.js.map +1 -0
  105. package/dist/llm/claude.d.ts +97 -0
  106. package/dist/llm/claude.js +163 -0
  107. package/dist/llm/claude.js.map +1 -0
  108. package/dist/memory/compaction.d.ts +22 -0
  109. package/dist/memory/compaction.js +79 -0
  110. package/dist/memory/compaction.js.map +1 -0
  111. package/dist/memory/file-store.d.ts +28 -0
  112. package/dist/memory/file-store.js +110 -0
  113. package/dist/memory/file-store.js.map +1 -0
  114. package/dist/memory/store.d.ts +32 -0
  115. package/dist/memory/store.js +2 -0
  116. package/dist/memory/store.js.map +1 -0
  117. package/dist/orchestrator/orchestrator.d.ts +63 -0
  118. package/dist/orchestrator/orchestrator.js +181 -0
  119. package/dist/orchestrator/orchestrator.js.map +1 -0
  120. package/dist/orchestrator/scheduler.d.ts +33 -0
  121. package/dist/orchestrator/scheduler.js +67 -0
  122. package/dist/orchestrator/scheduler.js.map +1 -0
  123. package/dist/orchestrator/seen-events.d.ts +21 -0
  124. package/dist/orchestrator/seen-events.js +71 -0
  125. package/dist/orchestrator/seen-events.js.map +1 -0
  126. package/dist/plugins/apply.d.ts +9 -0
  127. package/dist/plugins/apply.js +17 -0
  128. package/dist/plugins/apply.js.map +1 -0
  129. package/dist/plugins/define.d.ts +29 -0
  130. package/dist/plugins/define.js +30 -0
  131. package/dist/plugins/define.js.map +1 -0
  132. package/dist/plugins/loader.d.ts +31 -0
  133. package/dist/plugins/loader.js +61 -0
  134. package/dist/plugins/loader.js.map +1 -0
  135. package/dist/plugins/scaffold.d.ts +5 -0
  136. package/dist/plugins/scaffold.js +72 -0
  137. package/dist/plugins/scaffold.js.map +1 -0
  138. package/dist/tools/builtin.d.ts +8 -0
  139. package/dist/tools/builtin.js +63 -0
  140. package/dist/tools/builtin.js.map +1 -0
  141. package/dist/tools/tool.d.ts +84 -0
  142. package/dist/tools/tool.js +70 -0
  143. package/dist/tools/tool.js.map +1 -0
  144. package/dist/ui/agent-view.test.d.ts +1 -0
  145. package/dist/ui/agent-view.test.js +54 -0
  146. package/dist/ui/agent-view.test.js.map +1 -0
  147. package/dist/ui/agents-data.d.ts +7 -0
  148. package/dist/ui/agents-data.js +25 -0
  149. package/dist/ui/agents-data.js.map +1 -0
  150. package/dist/ui/app.d.ts +24 -0
  151. package/dist/ui/app.js +59 -0
  152. package/dist/ui/app.js.map +1 -0
  153. package/dist/ui/app.test.d.ts +1 -0
  154. package/dist/ui/app.test.js +47 -0
  155. package/dist/ui/app.test.js.map +1 -0
  156. package/dist/ui/components/key-hints.d.ts +4 -0
  157. package/dist/ui/components/key-hints.js +6 -0
  158. package/dist/ui/components/key-hints.js.map +1 -0
  159. package/dist/ui/components/menu.d.ts +11 -0
  160. package/dist/ui/components/menu.js +20 -0
  161. package/dist/ui/components/menu.js.map +1 -0
  162. package/dist/ui/create-wizard.test.d.ts +1 -0
  163. package/dist/ui/create-wizard.test.js +58 -0
  164. package/dist/ui/create-wizard.test.js.map +1 -0
  165. package/dist/ui/doctor-data.d.ts +6 -0
  166. package/dist/ui/doctor-data.js +29 -0
  167. package/dist/ui/doctor-data.js.map +1 -0
  168. package/dist/ui/history-data.d.ts +2 -0
  169. package/dist/ui/history-data.js +18 -0
  170. package/dist/ui/history-data.js.map +1 -0
  171. package/dist/ui/screens/agent.d.ts +8 -0
  172. package/dist/ui/screens/agent.js +95 -0
  173. package/dist/ui/screens/agent.js.map +1 -0
  174. package/dist/ui/screens/agents.d.ts +7 -0
  175. package/dist/ui/screens/agents.js +47 -0
  176. package/dist/ui/screens/agents.js.map +1 -0
  177. package/dist/ui/screens/create.d.ts +7 -0
  178. package/dist/ui/screens/create.js +141 -0
  179. package/dist/ui/screens/create.js.map +1 -0
  180. package/dist/ui/screens/doctor.d.ts +5 -0
  181. package/dist/ui/screens/doctor.js +13 -0
  182. package/dist/ui/screens/doctor.js.map +1 -0
  183. package/dist/ui/screens/history.d.ts +7 -0
  184. package/dist/ui/screens/history.js +50 -0
  185. package/dist/ui/screens/history.js.map +1 -0
  186. package/dist/ui/screens/home.d.ts +8 -0
  187. package/dist/ui/screens/home.js +35 -0
  188. package/dist/ui/screens/home.js.map +1 -0
  189. package/dist/ui/screens/plugins.d.ts +7 -0
  190. package/dist/ui/screens/plugins.js +40 -0
  191. package/dist/ui/screens/plugins.js.map +1 -0
  192. package/dist/ui/services.d.ts +33 -0
  193. package/dist/ui/services.js +67 -0
  194. package/dist/ui/services.js.map +1 -0
  195. package/dist/ui/start.d.ts +1 -0
  196. package/dist/ui/start.js +16 -0
  197. package/dist/ui/start.js.map +1 -0
  198. package/dist/ui/theme.d.ts +6 -0
  199. package/dist/ui/theme.js +26 -0
  200. package/dist/ui/theme.js.map +1 -0
  201. package/package.json +69 -0
@@ -0,0 +1,9 @@
1
+ import type { Tool } from "../tools/tool.js";
2
+ import type { FailedPlugin, PluginLoadResult } from "./loader.js";
3
+ export interface AppliedPlugins {
4
+ /** Plugin tools, registered into every agent's ToolRegistry by the orchestrator. */
5
+ extraTools: Tool[];
6
+ /** Loader failures plus apply-time failures (e.g. connector id collisions). */
7
+ failures: FailedPlugin[];
8
+ }
9
+ export declare function applyPlugins(result: PluginLoadResult): AppliedPlugins;
@@ -0,0 +1,17 @@
1
+ import { registerConnector } from "../connectors/index.js";
2
+ export function applyPlugins(result) {
3
+ const extraTools = [];
4
+ const failures = [...result.failures];
5
+ for (const plugin of result.plugins) {
6
+ try {
7
+ for (const connector of plugin.spec.connectors ?? [])
8
+ registerConnector(connector);
9
+ extraTools.push(...(plugin.spec.tools ?? []));
10
+ }
11
+ catch (err) {
12
+ failures.push({ dir: plugin.dir, name: plugin.manifest.name, error: err instanceof Error ? err.message : String(err) });
13
+ }
14
+ }
15
+ return { extraTools, failures };
16
+ }
17
+ //# sourceMappingURL=apply.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply.js","sourceRoot":"","sources":["../../src/plugins/apply.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAU3D,MAAM,UAAU,YAAY,CAAC,MAAwB;IACnD,MAAM,UAAU,GAAW,EAAE,CAAC;IAC9B,MAAM,QAAQ,GAAG,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IACtC,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACpC,IAAI,CAAC;YACH,KAAK,MAAM,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE;gBAAE,iBAAiB,CAAC,SAAS,CAAC,CAAC;YACnF,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC;QAChD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,QAAQ,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC1H,CAAC;IACH,CAAC;IACD,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC;AAClC,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { z } from "zod";
2
+ import { defineTool, type Tool } from "../tools/tool.js";
3
+ import type { Connector } from "../connectors/connector.js";
4
+ import type { ConnectorEnvKey } from "../connectors/index.js";
5
+ /**
6
+ * Public plugin API. Plugin authors either:
7
+ * - default-export `definePlugin({...})` (requires `npm i exempclaw zod` in the
8
+ * plugin folder so imports resolve), or
9
+ * - default-export a factory `(api: PluginApi) => PluginSpec` — zero installs,
10
+ * the loader passes this module in. The scaffold uses the factory form.
11
+ */
12
+ export interface PluginConnector {
13
+ id: string;
14
+ description: string;
15
+ envKeys: ConnectorEnvKey[];
16
+ make: () => Connector;
17
+ }
18
+ export interface PluginSpec {
19
+ name: string;
20
+ tools?: Tool[];
21
+ connectors?: PluginConnector[];
22
+ }
23
+ export declare function definePlugin(spec: PluginSpec): PluginSpec;
24
+ export declare const pluginApi: {
25
+ z: typeof z;
26
+ defineTool: typeof defineTool;
27
+ definePlugin: typeof definePlugin;
28
+ };
29
+ export type PluginApi = typeof pluginApi;
@@ -0,0 +1,30 @@
1
+ import { z } from "zod";
2
+ import { defineTool } from "../tools/tool.js";
3
+ export function definePlugin(spec) {
4
+ if (!spec.name || typeof spec.name !== "string") {
5
+ throw new Error("plugin name is required (non-empty string)");
6
+ }
7
+ const seen = new Set();
8
+ for (const tool of spec.tools ?? []) {
9
+ if (seen.has(tool.name))
10
+ throw new Error(`duplicate tool in plugin "${spec.name}": ${tool.name}`);
11
+ seen.add(tool.name);
12
+ }
13
+ const seenConnectors = new Set();
14
+ for (const connector of spec.connectors ?? []) {
15
+ if (!connector.id || typeof connector.make !== "function") {
16
+ throw new Error(`invalid connector in plugin "${spec.name}": needs id and make()`);
17
+ }
18
+ if (seenConnectors.has(connector.id)) {
19
+ throw new Error(`duplicate connector id in plugin "${spec.name}": ${connector.id}`);
20
+ }
21
+ seenConnectors.add(connector.id);
22
+ }
23
+ return {
24
+ name: spec.name,
25
+ tools: spec.tools ? [...spec.tools] : undefined,
26
+ connectors: spec.connectors ? [...spec.connectors] : undefined,
27
+ };
28
+ }
29
+ export const pluginApi = { z, defineTool, definePlugin };
30
+ //# sourceMappingURL=define.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define.js","sourceRoot":"","sources":["../../src/plugins/define.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,UAAU,EAAa,MAAM,kBAAkB,CAAC;AAwBzD,MAAM,UAAU,YAAY,CAAC,IAAgB;IAC3C,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;IAChE,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;QACpC,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,6BAA6B,IAAI,CAAC,IAAI,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAClG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtB,CAAC;IACD,MAAM,cAAc,GAAG,IAAI,GAAG,EAAU,CAAC;IACzC,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,UAAU,IAAI,EAAE,EAAE,CAAC;QAC9C,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,OAAO,SAAS,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YAC1D,MAAM,IAAI,KAAK,CAAC,gCAAgC,IAAI,CAAC,IAAI,wBAAwB,CAAC,CAAC;QACrF,CAAC;QACD,IAAI,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC;YACrC,MAAM,IAAI,KAAK,CAAC,qCAAqC,IAAI,CAAC,IAAI,MAAM,SAAS,CAAC,EAAE,EAAE,CAAC,CAAC;QACtF,CAAC;QACD,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IACnC,CAAC;IACD,OAAO;QACL,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS;QAC/C,UAAU,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS;KAC/D,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,SAAS,GAAG,EAAE,CAAC,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC"}
@@ -0,0 +1,31 @@
1
+ import { z } from "zod";
2
+ import { type PluginSpec } from "./define.js";
3
+ declare const ManifestSchema: z.ZodObject<{
4
+ name: z.ZodString;
5
+ version: z.ZodString;
6
+ description: z.ZodDefault<z.ZodString>;
7
+ entry: z.ZodDefault<z.ZodString>;
8
+ }, z.core.$strip>;
9
+ export type PluginManifest = z.infer<typeof ManifestSchema>;
10
+ export interface LoadedPlugin {
11
+ manifest: PluginManifest;
12
+ dir: string;
13
+ spec: PluginSpec;
14
+ }
15
+ export interface FailedPlugin {
16
+ dir: string;
17
+ /** Manifest name when known, else the folder name. */
18
+ name: string;
19
+ error: string;
20
+ }
21
+ export interface PluginLoadResult {
22
+ plugins: LoadedPlugin[];
23
+ failures: FailedPlugin[];
24
+ }
25
+ export declare function defaultPluginsDir(env?: NodeJS.ProcessEnv): string;
26
+ /**
27
+ * Discovers and imports every plugin folder. A broken plugin becomes a
28
+ * `failures` entry — it must never throw out of this function.
29
+ */
30
+ export declare function loadPlugins(dir?: string): Promise<PluginLoadResult>;
31
+ export {};
@@ -0,0 +1,61 @@
1
+ import { readdir, readFile, stat } from "node:fs/promises";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { pathToFileURL } from "node:url";
5
+ import { z } from "zod";
6
+ import { pluginApi } from "./define.js";
7
+ const ManifestSchema = z.object({
8
+ name: z.string().min(1),
9
+ version: z.string().min(1),
10
+ description: z.string().default(""),
11
+ entry: z.string().default("./index.js"),
12
+ });
13
+ export function defaultPluginsDir(env = process.env) {
14
+ return env.EXEMPCLAW_PLUGINS_DIR ?? join(homedir(), ".exempclaw", "plugins");
15
+ }
16
+ /**
17
+ * Discovers and imports every plugin folder. A broken plugin becomes a
18
+ * `failures` entry — it must never throw out of this function.
19
+ */
20
+ export async function loadPlugins(dir = defaultPluginsDir()) {
21
+ let entries;
22
+ try {
23
+ entries = await readdir(dir);
24
+ }
25
+ catch {
26
+ return { plugins: [], failures: [] };
27
+ }
28
+ const plugins = [];
29
+ const failures = [];
30
+ for (const entry of entries.sort()) {
31
+ const pluginDir = join(dir, entry);
32
+ try {
33
+ if (!(await stat(pluginDir)).isDirectory())
34
+ continue;
35
+ }
36
+ catch {
37
+ continue;
38
+ }
39
+ let manifest;
40
+ try {
41
+ const manifestRaw = await readFile(join(pluginDir, "exempclaw.plugin.json"), "utf8");
42
+ manifest = ManifestSchema.parse(JSON.parse(manifestRaw));
43
+ const entryUrl = pathToFileURL(join(pluginDir, manifest.entry)).href;
44
+ const mod = (await import(entryUrl));
45
+ const exported = mod.default;
46
+ const rawSpec = typeof exported === "function"
47
+ ? exported(pluginApi)
48
+ : exported;
49
+ const spec = rawSpec instanceof Promise ? await rawSpec : rawSpec;
50
+ if (!spec || typeof spec !== "object" || typeof spec.name !== "string") {
51
+ throw new Error("entry default export must be a PluginSpec or a factory returning one");
52
+ }
53
+ plugins.push({ manifest, dir: pluginDir, spec: spec });
54
+ }
55
+ catch (err) {
56
+ failures.push({ dir: pluginDir, name: manifest?.name ?? entry, error: err instanceof Error ? err.message : String(err) });
57
+ }
58
+ }
59
+ return { plugins, failures };
60
+ }
61
+ //# sourceMappingURL=loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/plugins/loader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Writes a working, zero-install plugin template: the entry default-exports a
3
+ * factory receiving the plugin API, so nothing needs `npm install` to run.
4
+ */
5
+ export declare function scaffoldPlugin(pluginsDir: string, name: string): Promise<string>;
@@ -0,0 +1,72 @@
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
+ import { existsSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ /**
5
+ * Writes a working, zero-install plugin template: the entry default-exports a
6
+ * factory receiving the plugin API, so nothing needs `npm install` to run.
7
+ */
8
+ export async function scaffoldPlugin(pluginsDir, name) {
9
+ if (!/^[a-z][a-z0-9-]*$/.test(name)) {
10
+ throw new Error(`plugin name must be lowercase letters/digits/dashes, got "${name}"`);
11
+ }
12
+ const dir = join(pluginsDir, name);
13
+ if (existsSync(dir))
14
+ throw new Error(`${dir} already exists`);
15
+ await mkdir(dir, { recursive: true });
16
+ await writeFile(join(dir, "exempclaw.plugin.json"), `${JSON.stringify({ name, version: "0.1.0", description: `${name} plugin`, entry: "./index.js" }, null, 2)}\n`, "utf8");
17
+ await writeFile(join(dir, "index.js"), `/**
18
+ * Exempclaw plugin: ${name}
19
+ *
20
+ * The default export is a factory that receives Exempclaw's plugin API
21
+ * ({ z, defineTool, definePlugin }), so this file works with zero installs.
22
+ * See PLUGIN.md for the full interface, including connectors.
23
+ */
24
+ export default function ({ z, defineTool, definePlugin }) {
25
+ return definePlugin({
26
+ name: "${name}",
27
+ tools: [
28
+ defineTool({
29
+ name: "${name}_hello",
30
+ description: "Example tool — replace with your own. Tools that act on the outside world must set outward: true so the approval gate engages.",
31
+ schema: z.object({ who: z.string().describe("Who to greet") }),
32
+ execute: async ({ who }) => ({ content: \`Hello, \${who}! (from the ${name} plugin)\` }),
33
+ }),
34
+ ],
35
+ });
36
+ }
37
+ `, "utf8");
38
+ await writeFile(join(dir, "PLUGIN.md"), `# ${name} — an Exempclaw plugin
39
+
40
+ Exempclaw discovers plugins in this folder's parent directory at startup
41
+ (\`~/.exempclaw/plugins\` by default, override with \`EXEMPCLAW_PLUGINS_DIR\`).
42
+
43
+ ## Anatomy
44
+
45
+ - \`exempclaw.plugin.json\` — name, version, description, entry (path to the module).
46
+ - the entry module — default-exports either:
47
+ 1. **a factory** \`(api) => spec\` (this template): \`api\` is \`{ z, defineTool, definePlugin }\`.
48
+ Zero installs needed.
49
+ 2. **a spec object** built with \`import { definePlugin } from "exempclaw/plugin"\` —
50
+ for TypeScript authors; run \`npm i exempclaw zod\` in this folder and compile to JS.
51
+
52
+ ## What a plugin can provide
53
+
54
+ - **tools**: capabilities every agent can call. Same shape as built-ins:
55
+ \`defineTool({ name, description, schema, outward, execute })\`. Set \`outward: true\`
56
+ for anything that touches the outside world — that engages the human-approval gate.
57
+ - **connectors**: full integrations (tools + inbound events). Provide
58
+ \`{ id, description, envKeys, make }\` where \`make()\` returns an object implementing
59
+ the \`Connector\` interface (\`init\`, \`tools\`, optional \`listen\`/\`shutdown\`).
60
+ Agents opt in by listing your connector id in their config's \`connectors\` array.
61
+
62
+ ## Rules
63
+
64
+ - A broken plugin never crashes Exempclaw — it shows up on the Plugins screen with the error.
65
+ - Plugins cannot change the LLM. Exempclaw runs on the Claude API only.
66
+
67
+ Check your work: run \`exempclaw\` and open **Plugins** — your plugin should be listed
68
+ with its tools, or shown with a load error to fix.
69
+ `, "utf8");
70
+ return dir;
71
+ }
72
+ //# sourceMappingURL=scaffold.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../src/plugins/scaffold.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,UAAkB,EAAE,IAAY;IACnE,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,6DAA6D,IAAI,GAAG,CAAC,CAAC;IACxF,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IACnC,IAAI,UAAU,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,GAAG,iBAAiB,CAAC,CAAC;IAC9D,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAEtC,MAAM,SAAS,CACb,IAAI,CAAC,GAAG,EAAE,uBAAuB,CAAC,EAClC,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,IAAI,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAC9G,MAAM,CACP,CAAC;IAEF,MAAM,SAAS,CACb,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,EACrB;uBACmB,IAAI;;;;;;;;aAQd,IAAI;;;iBAGA,IAAI;;;8EAGyD,IAAI;;;;;CAKjF,EACG,MAAM,CACP,CAAC;IAEF,MAAM,SAAS,CACb,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,EACtB,KAAK,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+BZ,EACG,MAAM,CACP,CAAC;IAEF,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { type Tool } from "./tool.js";
2
+ import type { MemoryStore } from "../memory/store.js";
3
+ /**
4
+ * Tools every agent gets regardless of connectors: durable memory, a clock,
5
+ * and a status channel to the operator's terminal. These are inward (no
6
+ * approval needed) — they never touch the outside world.
7
+ */
8
+ export declare function builtinTools(memory: MemoryStore): Tool[];
@@ -0,0 +1,63 @@
1
+ import { z } from "zod";
2
+ import { AGENT_ACTIVITIES, defineTool } from "./tool.js";
3
+ /**
4
+ * Tools every agent gets regardless of connectors: durable memory, a clock,
5
+ * and a status channel to the operator's terminal. These are inward (no
6
+ * approval needed) — they never touch the outside world.
7
+ */
8
+ export function builtinTools(memory) {
9
+ const remember = defineTool({
10
+ name: "remember",
11
+ description: "Store a durable fact, lesson, or piece of role context so future sessions retain it. Use for anything you'd want to know next time but that isn't already obvious from the conversation.",
12
+ schema: z.object({
13
+ text: z.string().describe("The fact or lesson to remember, stated concisely."),
14
+ source: z.string().default("self").describe("Where this came from, e.g. email, slack, onboarding."),
15
+ tags: z.array(z.string()).default([]).describe("Optional tags for later retrieval."),
16
+ }),
17
+ async execute(input) {
18
+ const entry = await memory.addMemory({ text: input.text, source: input.source, tags: input.tags });
19
+ return { content: `Stored memory ${entry.id}.` };
20
+ },
21
+ });
22
+ const recall = defineTool({
23
+ name: "recall",
24
+ description: "Search your durable memory for relevant context by keyword.",
25
+ schema: z.object({
26
+ query: z.string().describe("What to search for."),
27
+ limit: z.number().int().min(1).max(50).default(10),
28
+ }),
29
+ async execute(input) {
30
+ const hits = await memory.searchMemory(input.query, input.limit);
31
+ if (hits.length === 0)
32
+ return { content: "No matching memories." };
33
+ return { content: hits.map((m) => `- (${m.source}) ${m.text}`).join("\n") };
34
+ },
35
+ });
36
+ const now = defineTool({
37
+ name: "current_time",
38
+ description: "Get the current date and time in ISO-8601 (UTC).",
39
+ schema: z.object({}),
40
+ async execute() {
41
+ return { content: new Date().toISOString() };
42
+ },
43
+ });
44
+ const displayStatus = defineTool({
45
+ name: "display_status",
46
+ description: "Show the human operator a live animated status line in their terminal describing what you are doing right now. Use it when you start a distinct phase of multi-step work (searching a mailbox, reading a long thread, drafting a reply, waiting on something) so the operator can follow along — at most once per phase, and skip it for quick single-tool answers. Use \"celebrating\" once when a meaningful task fully completes, and \"alert\" only for something the operator should look at.",
47
+ schema: z.object({
48
+ activity: z.enum(AGENT_ACTIVITIES).describe("The kind of activity animation to play."),
49
+ message: z.string().min(1).max(100).describe("Short present-tense label, e.g. \"combing through Ana's last thread\"."),
50
+ }),
51
+ async execute(input, ctx) {
52
+ if (ctx.emit) {
53
+ ctx.emit({ kind: "status", activity: input.activity, message: input.message });
54
+ }
55
+ else {
56
+ ctx.log.info("agent status", { activity: input.activity, message: input.message });
57
+ }
58
+ return { content: "Status shown to the operator. Continue with the work itself." };
59
+ },
60
+ });
61
+ return [remember, recall, now, displayStatus];
62
+ }
63
+ //# sourceMappingURL=builtin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builtin.js","sourceRoot":"","sources":["../../src/tools/builtin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,84 @@
1
+ import { z } from "zod";
2
+ import type Anthropic from "@anthropic-ai/sdk";
3
+ import type { Logger } from "../core/logger.js";
4
+ import type { ActionPolicy } from "../config/index.js";
5
+ /** Activities the agent can signal to the operator's terminal. */
6
+ export declare const AGENT_ACTIVITIES: readonly ["thinking", "searching", "reading", "writing", "sending", "waiting", "celebrating", "alert"];
7
+ export type AgentActivity = (typeof AGENT_ACTIVITIES)[number];
8
+ /** A presentational signal from a tool to whatever UI is attached. */
9
+ export interface AgentSignal {
10
+ kind: "status";
11
+ activity: AgentActivity;
12
+ message: string;
13
+ }
14
+ /**
15
+ * Context handed to every tool at execution time. Tools never read global
16
+ * state directly — everything they need comes through here, which keeps them
17
+ * unit-testable and lets the orchestrator scope resources per agent.
18
+ */
19
+ export interface ToolContext {
20
+ agentId: string;
21
+ log: Logger;
22
+ /** Resolve an outward-action approval. The runtime wires this to the policy + UI. */
23
+ requestApproval(action: ApprovalRequest): Promise<boolean>;
24
+ /** Shared signal for cooperative cancellation. */
25
+ signal: AbortSignal;
26
+ /** Present a signal to the attached UI (animated status line in the TUI). */
27
+ emit?(signal: AgentSignal): void;
28
+ }
29
+ export interface ApprovalRequest {
30
+ /** Short human label, e.g. "Send email to jane@acme.com". */
31
+ summary: string;
32
+ /** Full detail shown to the approver (the email body, the Slack message, …). */
33
+ detail: string;
34
+ tool: string;
35
+ }
36
+ /**
37
+ * A capability the agent can invoke. `input` is validated with a Zod schema,
38
+ * which doubles as the JSON Schema sent to Claude. Mark `outward: true` for
39
+ * anything that affects the world outside Exempclaw (sending, posting, writing
40
+ * to external systems) so the approval gate engages.
41
+ */
42
+ export interface Tool<I = unknown> {
43
+ readonly name: string;
44
+ readonly description: string;
45
+ readonly schema: z.ZodType<I>;
46
+ /** True if executing this tool acts on the outside world (needs approval). */
47
+ readonly outward: boolean;
48
+ execute(input: I, ctx: ToolContext): Promise<ToolResult>;
49
+ }
50
+ export interface ToolResult {
51
+ /** Text returned to the model as the tool_result content. */
52
+ content: string;
53
+ /** Marks the result as an error so the model knows the call failed. */
54
+ isError?: boolean;
55
+ }
56
+ /** Helper to declare a tool with full type inference from its Zod schema. */
57
+ export declare function defineTool<I>(spec: {
58
+ name: string;
59
+ description: string;
60
+ schema: z.ZodType<I>;
61
+ outward?: boolean;
62
+ execute: (input: I, ctx: ToolContext) => Promise<ToolResult>;
63
+ }): Tool<I>;
64
+ /** Registry of the tools available to a single agent. */
65
+ export declare class ToolRegistry {
66
+ private readonly tools;
67
+ register(tool: Tool): void;
68
+ get(name: string): Tool | undefined;
69
+ /** Renders the registered tools into the Anthropic tool-definition format. */
70
+ toAnthropicTools(): Anthropic.Tool[];
71
+ get size(): number;
72
+ }
73
+ /**
74
+ * Decides whether an outward action proceeds, given the deployment policy.
75
+ * "deny" blocks unconditionally, "auto" allows, "ask" defers to the supplied
76
+ * interactive approver (terminal prompt, web UI, Slack button, …).
77
+ */
78
+ export declare function evaluatePolicy(policy: ActionPolicy, request: ApprovalRequest, interactiveApprover: (req: ApprovalRequest) => Promise<boolean>): Promise<boolean>;
79
+ /**
80
+ * Resolves the effective policy for one tool: an exact per-tool override wins,
81
+ * then a "*" override, then the deployment-wide default. Lets an agent config
82
+ * say e.g. auto-approve Slack thread replies while still gating email sends.
83
+ */
84
+ export declare function resolvePolicy(globalPolicy: ActionPolicy, overrides: Record<string, ActionPolicy> | undefined, toolName: string): ActionPolicy;
@@ -0,0 +1,70 @@
1
+ import { z } from "zod";
2
+ /** Activities the agent can signal to the operator's terminal. */
3
+ export const AGENT_ACTIVITIES = [
4
+ "thinking",
5
+ "searching",
6
+ "reading",
7
+ "writing",
8
+ "sending",
9
+ "waiting",
10
+ "celebrating",
11
+ "alert",
12
+ ];
13
+ /** Helper to declare a tool with full type inference from its Zod schema. */
14
+ export function defineTool(spec) {
15
+ return {
16
+ name: spec.name,
17
+ description: spec.description,
18
+ schema: spec.schema,
19
+ outward: spec.outward ?? false,
20
+ execute: spec.execute,
21
+ };
22
+ }
23
+ /** Registry of the tools available to a single agent. */
24
+ export class ToolRegistry {
25
+ tools = new Map();
26
+ register(tool) {
27
+ if (this.tools.has(tool.name)) {
28
+ throw new Error(`duplicate tool registered: ${tool.name}`);
29
+ }
30
+ this.tools.set(tool.name, tool);
31
+ }
32
+ get(name) {
33
+ return this.tools.get(name);
34
+ }
35
+ /** Renders the registered tools into the Anthropic tool-definition format. */
36
+ toAnthropicTools() {
37
+ return [...this.tools.values()].map((tool) => ({
38
+ name: tool.name,
39
+ description: tool.description,
40
+ input_schema: z.toJSONSchema(tool.schema, { target: "draft-7" }),
41
+ }));
42
+ }
43
+ get size() {
44
+ return this.tools.size;
45
+ }
46
+ }
47
+ /**
48
+ * Decides whether an outward action proceeds, given the deployment policy.
49
+ * "deny" blocks unconditionally, "auto" allows, "ask" defers to the supplied
50
+ * interactive approver (terminal prompt, web UI, Slack button, …).
51
+ */
52
+ export async function evaluatePolicy(policy, request, interactiveApprover) {
53
+ switch (policy) {
54
+ case "auto":
55
+ return true;
56
+ case "deny":
57
+ return false;
58
+ case "ask":
59
+ return interactiveApprover(request);
60
+ }
61
+ }
62
+ /**
63
+ * Resolves the effective policy for one tool: an exact per-tool override wins,
64
+ * then a "*" override, then the deployment-wide default. Lets an agent config
65
+ * say e.g. auto-approve Slack thread replies while still gating email sends.
66
+ */
67
+ export function resolvePolicy(globalPolicy, overrides, toolName) {
68
+ return overrides?.[toolName] ?? overrides?.["*"] ?? globalPolicy;
69
+ }
70
+ //# sourceMappingURL=tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool.js","sourceRoot":"","sources":["../../src/tools/tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,kEAAkE;AAClE,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,UAAU;IACV,WAAW;IACX,SAAS;IACT,SAAS;IACT,SAAS;IACT,SAAS;IACT,aAAa;IACb,OAAO;CACC,CAAC;AAwDX,6EAA6E;AAC7E,MAAM,UAAU,UAAU,CAAI,IAM7B;IACC,OAAO;QACL,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,KAAK;QAC9B,OAAO,EAAE,IAAI,CAAC,OAAO;KACtB,CAAC;AACJ,CAAC;AAED,yDAAyD;AACzD,MAAM,OAAO,YAAY;IACN,KAAK,GAAG,IAAI,GAAG,EAAgB,CAAC;IAEjD,QAAQ,CAAC,IAAU;QACjB,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC,8BAA8B,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAC7D,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAY,CAAC,CAAC;IAC1C,CAAC;IAED,GAAG,CAAC,IAAY;QACd,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;IAED,8EAA8E;IAC9E,gBAAgB;QACd,OAAO,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAC7C,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,YAAY,EAAE,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,CAA+B;SAC/F,CAAC,CAAC,CAAC;IACN,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IACzB,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,MAAoB,EACpB,OAAwB,EACxB,mBAA+D;IAE/D,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,MAAM;YACT,OAAO,IAAI,CAAC;QACd,KAAK,MAAM;YACT,OAAO,KAAK,CAAC;QACf,KAAK,KAAK;YACR,OAAO,mBAAmB,CAAC,OAAO,CAAC,CAAC;IACxC,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAC3B,YAA0B,EAC1B,SAAmD,EACnD,QAAgB;IAEhB,OAAO,SAAS,EAAE,CAAC,QAAQ,CAAC,IAAI,SAAS,EAAE,CAAC,GAAG,CAAC,IAAI,YAAY,CAAC;AACnE,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,54 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // src/ui/agent-view.test.tsx
3
+ import { test } from "node:test";
4
+ import assert from "node:assert/strict";
5
+ import { render } from "ink-testing-library";
6
+ import { AgentScreen } from "./screens/agent.js";
7
+ import { ApprovalBridge } from "./services.js";
8
+ const tick = () => new Promise((r) => setTimeout(r, 50));
9
+ function servicesWith(dispatch) {
10
+ const approvals = new ApprovalBridge();
11
+ const orchestrator = {
12
+ dispatch: (_id, input, opts) => dispatch(input, opts.hooks),
13
+ };
14
+ return {
15
+ config: { defaultModel: "m", dataDir: "/tmp/x", logLevel: "error", actionPolicy: "ask", contextBudgetTokens: 200_000 },
16
+ agentsDir: "/tmp/agents",
17
+ plugins: { plugins: [], failures: [] },
18
+ applied: { extraTools: [], failures: [] },
19
+ approvals,
20
+ listAgents: async () => ({ agents: [], broken: [] }),
21
+ getOrchestrator: async () => orchestrator,
22
+ shutdown: async () => undefined,
23
+ };
24
+ }
25
+ test("chat streams text and tool lines", async () => {
26
+ const services = servicesWith(async (_input, hooks) => {
27
+ hooks.onToolStart("recall", {});
28
+ hooks.onToolEnd("recall", true);
29
+ hooks.onText("Hello from Sam.");
30
+ return { text: "Hello from Sam.", usage: {}, iterations: 1, stopReason: "end_turn" };
31
+ });
32
+ const { lastFrame, stdin } = render(_jsx(AgentScreen, { services: services, agentId: "sam", onNavigate: () => undefined }));
33
+ await tick();
34
+ stdin.write("hi");
35
+ await tick();
36
+ stdin.write("\r");
37
+ await tick();
38
+ assert.match(lastFrame(), /you ▸ hi/);
39
+ assert.match(lastFrame(), /recall/);
40
+ assert.match(lastFrame(), /Hello from Sam\./);
41
+ });
42
+ test("approval dialog renders and resolves on y", async () => {
43
+ const services = servicesWith(async () => new Promise(() => undefined)); // never settles
44
+ const { lastFrame, stdin } = render(_jsx(AgentScreen, { services: services, agentId: "sam", onNavigate: () => undefined }));
45
+ await tick();
46
+ await tick();
47
+ const decision = services.approvals.approve({ tool: "email_send", summary: "Send email", detail: "body" });
48
+ await tick();
49
+ await tick();
50
+ assert.match(lastFrame(), /Approval required — email_send/);
51
+ stdin.write("y");
52
+ assert.equal(await decision, true);
53
+ });
54
+ //# sourceMappingURL=agent-view.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-view.test.js","sourceRoot":"","sources":["../../src/ui/agent-view.test.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export interface AgentActivity {
2
+ runs: number;
3
+ lastRunAt: string | undefined;
4
+ totalCostUsd: number;
5
+ }
6
+ export declare function agentActivity(dataDir: string, agentId: string): Promise<AgentActivity>;
7
+ export declare function timeAgo(iso: string | undefined, now?: () => number): string;
@@ -0,0 +1,25 @@
1
+ import { RunLog } from "../core/run-log.js";
2
+ export async function agentActivity(dataDir, agentId) {
3
+ const records = await new RunLog(dataDir, agentId).readAll();
4
+ let lastRunAt;
5
+ let totalCostUsd = 0;
6
+ for (const record of records) {
7
+ if (!lastRunAt || record.startedAt > lastRunAt)
8
+ lastRunAt = record.startedAt;
9
+ totalCostUsd += record.costUsd ?? 0;
10
+ }
11
+ return { runs: records.length, lastRunAt, totalCostUsd };
12
+ }
13
+ export function timeAgo(iso, now = Date.now) {
14
+ if (!iso)
15
+ return "never";
16
+ const seconds = Math.max(0, Math.floor((now() - Date.parse(iso)) / 1000));
17
+ if (seconds < 60)
18
+ return `${seconds}s ago`;
19
+ if (seconds < 3600)
20
+ return `${Math.floor(seconds / 60)}m ago`;
21
+ if (seconds < 86400)
22
+ return `${Math.floor(seconds / 3600)}h ago`;
23
+ return `${Math.floor(seconds / 86400)}d ago`;
24
+ }
25
+ //# sourceMappingURL=agents-data.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agents-data.js","sourceRoot":"","sources":["../../src/ui/agents-data.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAQ5C,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAAe,EAAE,OAAe;IAClE,MAAM,OAAO,GAAG,MAAM,IAAI,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC;IAC7D,IAAI,SAA6B,CAAC;IAClC,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,CAAC,SAAS,IAAI,MAAM,CAAC,SAAS,GAAG,SAAS;YAAE,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;QAC7E,YAAY,IAAI,MAAM,CAAC,OAAO,IAAI,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,GAAuB,EAAE,MAAoB,IAAI,CAAC,GAAG;IAC3E,IAAI,CAAC,GAAG;QAAE,OAAO,OAAO,CAAC;IACzB,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;IAC1E,IAAI,OAAO,GAAG,EAAE;QAAE,OAAO,GAAG,OAAO,OAAO,CAAC;IAC3C,IAAI,OAAO,GAAG,IAAI;QAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC;IAC9D,IAAI,OAAO,GAAG,KAAK;QAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;IACjE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;AAC/C,CAAC"}