@amodalai/runtime 0.3.49 → 0.3.51

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 (161) hide show
  1. package/dist/src/agent/completion-tools.integration.test.d.ts +6 -0
  2. package/dist/src/agent/completion-tools.integration.test.js +263 -0
  3. package/dist/src/agent/completion-tools.integration.test.js.map +1 -0
  4. package/dist/src/agent/load-template-plan.integration.test.d.ts +6 -0
  5. package/dist/src/agent/load-template-plan.integration.test.js +152 -0
  6. package/dist/src/agent/load-template-plan.integration.test.js.map +1 -0
  7. package/dist/src/agent/local-server.js +64 -9
  8. package/dist/src/agent/local-server.js.map +1 -1
  9. package/dist/src/agent/loop-types.d.ts +12 -2
  10. package/dist/src/agent/loop.test.js +5 -2
  11. package/dist/src/agent/loop.test.js.map +1 -1
  12. package/dist/src/agent/propose-plan.integration.test.d.ts +6 -0
  13. package/dist/src/agent/propose-plan.integration.test.js +186 -0
  14. package/dist/src/agent/propose-plan.integration.test.js.map +1 -0
  15. package/dist/src/agent/routes/package-updates.d.ts +42 -0
  16. package/dist/src/agent/routes/package-updates.js +207 -0
  17. package/dist/src/agent/routes/package-updates.js.map +1 -0
  18. package/dist/src/agent/routes/package-updates.test.d.ts +6 -0
  19. package/dist/src/agent/routes/package-updates.test.js +25 -0
  20. package/dist/src/agent/routes/package-updates.test.js.map +1 -0
  21. package/dist/src/agent/setup-state.integration.test.d.ts +6 -0
  22. package/dist/src/agent/setup-state.integration.test.js +182 -0
  23. package/dist/src/agent/setup-state.integration.test.js.map +1 -0
  24. package/dist/src/agent/snapshot-server.js +1 -0
  25. package/dist/src/agent/snapshot-server.js.map +1 -1
  26. package/dist/src/agent/states/executing.d.ts +6 -0
  27. package/dist/src/agent/states/executing.js +63 -27
  28. package/dist/src/agent/states/executing.js.map +1 -1
  29. package/dist/src/agent/states/streaming.js +18 -2
  30. package/dist/src/agent/states/streaming.js.map +1 -1
  31. package/dist/src/agent/tool-executor-local.js +11 -2
  32. package/dist/src/agent/tool-executor-local.js.map +1 -1
  33. package/dist/src/agent/validate-connection.integration.test.d.ts +6 -0
  34. package/dist/src/agent/validate-connection.integration.test.js +160 -0
  35. package/dist/src/agent/validate-connection.integration.test.js.map +1 -0
  36. package/dist/src/api/create-agent.js +1 -0
  37. package/dist/src/api/create-agent.js.map +1 -1
  38. package/dist/src/index.d.ts +2 -0
  39. package/dist/src/index.js +9 -0
  40. package/dist/src/index.js.map +1 -1
  41. package/dist/src/intent/executor.d.ts +48 -0
  42. package/dist/src/intent/executor.js +420 -0
  43. package/dist/src/intent/executor.js.map +1 -0
  44. package/dist/src/intent/executor.test.d.ts +6 -0
  45. package/dist/src/intent/executor.test.js +543 -0
  46. package/dist/src/intent/executor.test.js.map +1 -0
  47. package/dist/src/intent/index.d.ts +10 -0
  48. package/dist/src/intent/index.js +9 -0
  49. package/dist/src/intent/index.js.map +1 -0
  50. package/dist/src/intent/loader.d.ts +16 -0
  51. package/dist/src/intent/loader.js +112 -0
  52. package/dist/src/intent/loader.js.map +1 -0
  53. package/dist/src/intent/loader.test.d.ts +6 -0
  54. package/dist/src/intent/loader.test.js +86 -0
  55. package/dist/src/intent/loader.test.js.map +1 -0
  56. package/dist/src/intent/matcher.d.ts +26 -0
  57. package/dist/src/intent/matcher.js +29 -0
  58. package/dist/src/intent/matcher.js.map +1 -0
  59. package/dist/src/intent/matcher.test.d.ts +6 -0
  60. package/dist/src/intent/matcher.test.js +53 -0
  61. package/dist/src/intent/matcher.test.js.map +1 -0
  62. package/dist/src/intent/onboarding.e2e.test.d.ts +6 -0
  63. package/dist/src/intent/onboarding.e2e.test.js +394 -0
  64. package/dist/src/intent/onboarding.e2e.test.js.map +1 -0
  65. package/dist/src/routes/ai-stream.js +96 -3
  66. package/dist/src/routes/ai-stream.js.map +1 -1
  67. package/dist/src/routes/session-resolver.js +16 -0
  68. package/dist/src/routes/session-resolver.js.map +1 -1
  69. package/dist/src/session/credential-scrubber.d.ts +35 -0
  70. package/dist/src/session/credential-scrubber.js +150 -0
  71. package/dist/src/session/credential-scrubber.js.map +1 -0
  72. package/dist/src/session/credential-scrubber.test.d.ts +6 -0
  73. package/dist/src/session/credential-scrubber.test.js +192 -0
  74. package/dist/src/session/credential-scrubber.test.js.map +1 -0
  75. package/dist/src/session/manager.intent.test.d.ts +6 -0
  76. package/dist/src/session/manager.intent.test.js +197 -0
  77. package/dist/src/session/manager.intent.test.js.map +1 -0
  78. package/dist/src/session/manager.js +114 -0
  79. package/dist/src/session/manager.js.map +1 -1
  80. package/dist/src/session/session-builder.d.ts +16 -1
  81. package/dist/src/session/session-builder.js +209 -41
  82. package/dist/src/session/session-builder.js.map +1 -1
  83. package/dist/src/session/store.js +48 -2
  84. package/dist/src/session/store.js.map +1 -1
  85. package/dist/src/session/tool-context-factory.js +12 -0
  86. package/dist/src/session/tool-context-factory.js.map +1 -1
  87. package/dist/src/session/types.d.ts +12 -1
  88. package/dist/src/setup/commit-setup.d.ts +94 -0
  89. package/dist/src/setup/commit-setup.js +154 -0
  90. package/dist/src/setup/commit-setup.js.map +1 -0
  91. package/dist/src/setup/commit-setup.test.d.ts +6 -0
  92. package/dist/src/setup/commit-setup.test.js +310 -0
  93. package/dist/src/setup/commit-setup.test.js.map +1 -0
  94. package/dist/src/tools/README.md +270 -0
  95. package/dist/src/tools/admin-tools.d.ts +27 -0
  96. package/dist/src/tools/admin-tools.js +734 -0
  97. package/dist/src/tools/admin-tools.js.map +1 -0
  98. package/dist/src/tools/agent-package-discovery.test.d.ts +6 -0
  99. package/dist/src/tools/agent-package-discovery.test.js +90 -0
  100. package/dist/src/tools/agent-package-discovery.test.js.map +1 -0
  101. package/dist/src/tools/builtin/ask-choice.d.ts +8 -0
  102. package/dist/src/tools/builtin/ask-choice.js +54 -0
  103. package/dist/src/tools/builtin/ask-choice.js.map +1 -0
  104. package/dist/src/tools/context.d.ts +154 -0
  105. package/dist/src/tools/context.js +30 -0
  106. package/dist/src/tools/context.js.map +1 -0
  107. package/dist/src/tools/custom-tool-adapter.d.ts +33 -2
  108. package/dist/src/tools/custom-tool-adapter.js +38 -1
  109. package/dist/src/tools/custom-tool-adapter.js.map +1 -1
  110. package/dist/src/tools/custom-tool-adapter.test.js +48 -0
  111. package/dist/src/tools/custom-tool-adapter.test.js.map +1 -1
  112. package/dist/src/tools/fetch-url-tool.js +2 -0
  113. package/dist/src/tools/fetch-url-tool.js.map +1 -1
  114. package/dist/src/tools/file-tools.js +16 -0
  115. package/dist/src/tools/file-tools.js.map +1 -1
  116. package/dist/src/tools/fs/local.test.d.ts +6 -0
  117. package/dist/src/tools/fs/local.test.js +126 -0
  118. package/dist/src/tools/fs/local.test.js.map +1 -0
  119. package/dist/src/tools/index.d.ts +35 -0
  120. package/dist/src/tools/index.js +11 -0
  121. package/dist/src/tools/index.js.map +1 -0
  122. package/dist/src/tools/mcp-tool-adapter.js +2 -0
  123. package/dist/src/tools/mcp-tool-adapter.js.map +1 -1
  124. package/dist/src/tools/memory-tool.js +23 -1
  125. package/dist/src/tools/memory-tool.js.map +1 -1
  126. package/dist/src/tools/permissions.d.ts +36 -0
  127. package/dist/src/tools/permissions.js +97 -0
  128. package/dist/src/tools/permissions.js.map +1 -0
  129. package/dist/src/tools/permissions.test.d.ts +6 -0
  130. package/dist/src/tools/permissions.test.js +62 -0
  131. package/dist/src/tools/permissions.test.js.map +1 -0
  132. package/dist/src/tools/request-tool.js +2 -0
  133. package/dist/src/tools/request-tool.js.map +1 -1
  134. package/dist/src/tools/sdk-context.d.ts +43 -0
  135. package/dist/src/tools/sdk-context.js +94 -0
  136. package/dist/src/tools/sdk-context.js.map +1 -0
  137. package/dist/src/tools/sdk-context.test.d.ts +6 -0
  138. package/dist/src/tools/sdk-context.test.js +134 -0
  139. package/dist/src/tools/sdk-context.test.js.map +1 -0
  140. package/dist/src/tools/store-tools.js +6 -0
  141. package/dist/src/tools/store-tools.js.map +1 -1
  142. package/dist/src/tools/types.d.ts +53 -14
  143. package/dist/src/tools/web-search-tool.js +2 -0
  144. package/dist/src/tools/web-search-tool.js.map +1 -1
  145. package/dist/src/types.d.ts +164 -28
  146. package/dist/src/types.js +9 -3
  147. package/dist/src/types.js.map +1 -1
  148. package/dist/tsconfig.tsbuildinfo +1 -1
  149. package/package.json +16 -4
  150. package/dist/src/tools/agent-config-tool.d.ts +0 -7
  151. package/dist/src/tools/agent-config-tool.js +0 -78
  152. package/dist/src/tools/agent-config-tool.js.map +0 -1
  153. package/dist/src/tools/collect-secret-tool.d.ts +0 -7
  154. package/dist/src/tools/collect-secret-tool.js +0 -47
  155. package/dist/src/tools/collect-secret-tool.js.map +0 -1
  156. package/dist/src/tools/fs/http.d.ts +0 -37
  157. package/dist/src/tools/fs/http.js +0 -88
  158. package/dist/src/tools/fs/http.js.map +0 -1
  159. package/dist/src/tools/http-file-tools.d.ts +0 -13
  160. package/dist/src/tools/http-file-tools.js +0 -146
  161. package/dist/src/tools/http-file-tools.js.map +0 -1
@@ -0,0 +1,112 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Amodal Labs, Inc.
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ /**
7
+ * Load intents from `<repoPath>/intents/<id>/intent.ts`. Each subdir is
8
+ * one intent; its `intent.ts` default-exports an `IntentDefinition`.
9
+ *
10
+ * Compiles each entry with esbuild (same approach as `tool-executor-local`
11
+ * uses for tool handlers) so authors can split helpers across sibling
12
+ * `.ts` files inside the intent's directory. Output goes into a
13
+ * sibling `.build/` directory so the source tree stays clean.
14
+ *
15
+ * Skipped silently when:
16
+ * - `<repoPath>/intents/` doesn't exist (most agents don't have intents)
17
+ * - A subdir has no `intent.ts` (probably a future siblings dir)
18
+ *
19
+ * Throws when:
20
+ * - `intent.ts` exists but the default export isn't a valid
21
+ * IntentDefinition shape (caller surface, fail fast)
22
+ */
23
+ import { existsSync, mkdirSync } from 'node:fs';
24
+ import { readdir, stat } from 'node:fs/promises';
25
+ import * as path from 'node:path';
26
+ import { pathToFileURL } from 'node:url';
27
+ /**
28
+ * Read the intents directory and return a sorted list of loaded
29
+ * IntentDefinitions. Empty array when the directory is missing.
30
+ *
31
+ * Sort order is alphabetical by directory name. `matchIntent` walks
32
+ * the list in registration order, so the directory name controls
33
+ * which regex is tested first when two intents could match the same
34
+ * input. Author intent ids accordingly (the id IS the directory name).
35
+ */
36
+ export async function loadIntents(repoPath) {
37
+ const intentsDir = path.join(repoPath, 'intents');
38
+ if (!existsSync(intentsDir))
39
+ return [];
40
+ let dirEntries;
41
+ try {
42
+ dirEntries = await readdir(intentsDir);
43
+ }
44
+ catch {
45
+ return [];
46
+ }
47
+ dirEntries.sort();
48
+ const loaded = [];
49
+ for (const name of dirEntries) {
50
+ if (name.startsWith('.') || name.startsWith('_'))
51
+ continue;
52
+ const subdirPath = path.join(intentsDir, name);
53
+ let stats;
54
+ try {
55
+ stats = await stat(subdirPath);
56
+ }
57
+ catch {
58
+ continue;
59
+ }
60
+ if (!stats.isDirectory())
61
+ continue;
62
+ const intentFile = path.join(subdirPath, 'intent.ts');
63
+ if (!existsSync(intentFile))
64
+ continue;
65
+ const def = await loadIntentModule(name, intentFile);
66
+ loaded.push(def);
67
+ }
68
+ return loaded;
69
+ }
70
+ /**
71
+ * Compile + import a single intent.ts. esbuild runs with
72
+ * `bundle: true, packages: 'external'` so sibling .ts helpers get
73
+ * inlined while npm + node:* imports stay external for normal
74
+ * Node resolution.
75
+ */
76
+ async function loadIntentModule(dirName, intentFilePath) {
77
+ const buildDir = path.join(path.dirname(intentFilePath), '.build');
78
+ const outFile = path.join(buildDir, 'intent.mjs');
79
+ mkdirSync(buildDir, { recursive: true });
80
+ const { build } = await import('esbuild');
81
+ await build({
82
+ entryPoints: [intentFilePath],
83
+ outfile: outFile,
84
+ bundle: true,
85
+ packages: 'external',
86
+ format: 'esm',
87
+ platform: 'node',
88
+ target: 'node20',
89
+ logLevel: 'warning',
90
+ });
91
+ const moduleUrl = pathToFileURL(outFile).href;
92
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- dynamic import boundary; validated below
93
+ const mod = (await import(moduleUrl));
94
+ const def = mod.default;
95
+ if (!isValidIntentDefinition(def)) {
96
+ throw new Error(`Intent "${dirName}" at ${intentFilePath} must default-export an IntentDefinition with {id: string, regex: RegExp, handle: function}.`);
97
+ }
98
+ if (def.id !== dirName) {
99
+ throw new Error(`Intent at ${intentFilePath} has id "${def.id}" but lives in directory "${dirName}". The id must match the directory name.`);
100
+ }
101
+ return def;
102
+ }
103
+ function isValidIntentDefinition(value) {
104
+ if (typeof value !== 'object' || value === null)
105
+ return false;
106
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- discriminating shape check
107
+ const v = value;
108
+ return (typeof v['id'] === 'string' &&
109
+ v['regex'] instanceof RegExp &&
110
+ typeof v['handle'] === 'function');
111
+ }
112
+ //# sourceMappingURL=loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../../../src/intent/loader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Amodal Labs, Inc.
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ export {};
@@ -0,0 +1,86 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Amodal Labs, Inc.
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
7
+ import { mkdtemp, mkdir, writeFile, rm } from 'node:fs/promises';
8
+ import { tmpdir } from 'node:os';
9
+ import * as path from 'node:path';
10
+ import { loadIntents } from './loader.js';
11
+ describe('loadIntents', () => {
12
+ let repoPath;
13
+ beforeEach(async () => {
14
+ repoPath = await mkdtemp(path.join(tmpdir(), 'intent-loader-test-'));
15
+ });
16
+ afterEach(async () => {
17
+ await rm(repoPath, { recursive: true, force: true });
18
+ });
19
+ it('returns empty array when intents/ does not exist', async () => {
20
+ const intents = await loadIntents(repoPath);
21
+ expect(intents).toEqual([]);
22
+ });
23
+ it('loads a single valid intent', async () => {
24
+ const intentDir = path.join(repoPath, 'intents', 'echo');
25
+ await mkdir(intentDir, { recursive: true });
26
+ await writeFile(path.join(intentDir, 'intent.ts'), `export default {
27
+ id: 'echo',
28
+ regex: /^echo (.+)$/,
29
+ async handle(ctx) { return {}; },
30
+ };`);
31
+ const intents = await loadIntents(repoPath);
32
+ expect(intents).toHaveLength(1);
33
+ expect(intents[0].id).toBe('echo');
34
+ expect(intents[0].regex.source).toBe('^echo (.+)$');
35
+ });
36
+ it('loads multiple intents alphabetically sorted', async () => {
37
+ for (const id of ['banana', 'apple', 'cherry']) {
38
+ const intentDir = path.join(repoPath, 'intents', id);
39
+ await mkdir(intentDir, { recursive: true });
40
+ await writeFile(path.join(intentDir, 'intent.ts'), `export default { id: '${id}', regex: /^${id}$/, async handle() { return {}; } };`);
41
+ }
42
+ const intents = await loadIntents(repoPath);
43
+ expect(intents.map((i) => i.id)).toEqual(['apple', 'banana', 'cherry']);
44
+ });
45
+ it('skips subdirs without intent.ts', async () => {
46
+ await mkdir(path.join(repoPath, 'intents', 'just-a-folder'), { recursive: true });
47
+ const intents = await loadIntents(repoPath);
48
+ expect(intents).toEqual([]);
49
+ });
50
+ it('skips dot- and underscore-prefixed entries', async () => {
51
+ for (const name of ['.hidden', '_private', '.build']) {
52
+ const dir = path.join(repoPath, 'intents', name);
53
+ await mkdir(dir, { recursive: true });
54
+ await writeFile(path.join(dir, 'intent.ts'), `export default { id: 'shouldnotload', regex: /^x$/, async handle() { return {}; } };`);
55
+ }
56
+ const intents = await loadIntents(repoPath);
57
+ expect(intents).toEqual([]);
58
+ });
59
+ it('rejects malformed default export', async () => {
60
+ const intentDir = path.join(repoPath, 'intents', 'bad');
61
+ await mkdir(intentDir, { recursive: true });
62
+ await writeFile(path.join(intentDir, 'intent.ts'), `export default { id: 'bad', regex: 'not-a-regex', handle: 42 };`);
63
+ await expect(loadIntents(repoPath)).rejects.toThrow(/IntentDefinition/);
64
+ });
65
+ it('rejects id mismatch with directory name', async () => {
66
+ const intentDir = path.join(repoPath, 'intents', 'directory-name');
67
+ await mkdir(intentDir, { recursive: true });
68
+ await writeFile(path.join(intentDir, 'intent.ts'), `export default { id: 'different-id', regex: /^x$/, async handle() { return {}; } };`);
69
+ await expect(loadIntents(repoPath)).rejects.toThrow(/must match the directory name/);
70
+ });
71
+ it('inlines sibling .ts helpers via esbuild bundle', async () => {
72
+ const intentDir = path.join(repoPath, 'intents', 'with-helper');
73
+ await mkdir(intentDir, { recursive: true });
74
+ await writeFile(path.join(intentDir, 'helper.ts'), `export const greeting = 'hi';`);
75
+ await writeFile(path.join(intentDir, 'intent.ts'), `import {greeting} from './helper.js';
76
+ export default {
77
+ id: 'with-helper',
78
+ regex: new RegExp('^' + greeting + '$'),
79
+ async handle() { return {}; },
80
+ };`);
81
+ const intents = await loadIntents(repoPath);
82
+ expect(intents).toHaveLength(1);
83
+ expect(intents[0].regex.test('hi')).toBe(true);
84
+ });
85
+ });
86
+ //# sourceMappingURL=loader.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.test.js","sourceRoot":"","sources":["../../../src/intent/loader.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Amodal Labs, Inc.
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import type { IntentDefinition } from '@amodalai/types';
7
+ /**
8
+ * Result of a successful intent match. The runtime hands this to the
9
+ * bypass executor (`runIntent`) which builds the IntentContext, calls
10
+ * the handler, and emits the SSE event sequence.
11
+ */
12
+ export interface IntentMatch {
13
+ intent: IntentDefinition;
14
+ match: RegExpExecArray;
15
+ }
16
+ /**
17
+ * Walk the intent list in registration order and return the first
18
+ * regex match against `input`. Returns null when nothing matches —
19
+ * the caller falls through to the agent loop.
20
+ *
21
+ * "First match wins" is intentional: there's no priority field and no
22
+ * tie-breaking. If two intents would match the same input, the one
23
+ * loaded earlier wins. Intent authors keep regexes precise + anchored
24
+ * (`^...$`) to avoid surprises.
25
+ */
26
+ export declare function matchIntent(intents: readonly IntentDefinition[], input: string): IntentMatch | null;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Amodal Labs, Inc.
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ /**
7
+ * Walk the intent list in registration order and return the first
8
+ * regex match against `input`. Returns null when nothing matches —
9
+ * the caller falls through to the agent loop.
10
+ *
11
+ * "First match wins" is intentional: there's no priority field and no
12
+ * tie-breaking. If two intents would match the same input, the one
13
+ * loaded earlier wins. Intent authors keep regexes precise + anchored
14
+ * (`^...$`) to avoid surprises.
15
+ */
16
+ export function matchIntent(intents, input) {
17
+ for (const intent of intents) {
18
+ // Reset lastIndex defensively in case the regex was authored
19
+ // with the /g flag (which would make exec() stateful and
20
+ // produce the wrong results across multiple matches).
21
+ intent.regex.lastIndex = 0;
22
+ const match = intent.regex.exec(input);
23
+ if (match) {
24
+ return { intent, match };
25
+ }
26
+ }
27
+ return null;
28
+ }
29
+ //# sourceMappingURL=matcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matcher.js","sourceRoot":"","sources":["../../../src/intent/matcher.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAcH;;;;;;;;;GASG;AACH,MAAM,UAAU,WAAW,CACzB,OAAoC,EACpC,KAAa;IAEb,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,6DAA6D;QAC7D,yDAAyD;QACzD,sDAAsD;QACtD,MAAM,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC;QAC3B,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACvC,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,EAAC,MAAM,EAAE,KAAK,EAAC,CAAC;QACzB,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Amodal Labs, Inc.
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ export {};
@@ -0,0 +1,53 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Amodal Labs, Inc.
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import { describe, it, expect } from 'vitest';
7
+ import { matchIntent } from './matcher.js';
8
+ const noopHandle = async () => null;
9
+ function intent(id, regex) {
10
+ return { id, regex, handle: noopHandle };
11
+ }
12
+ describe('matchIntent', () => {
13
+ it('returns null when no intent matches', () => {
14
+ const intents = [intent('a', /^hello$/), intent('b', /^world$/)];
15
+ expect(matchIntent(intents, 'unrelated')).toBeNull();
16
+ });
17
+ it('matches and exposes capture groups', () => {
18
+ const intents = [intent('install', /^Set up template '(.+)'\.?$/)];
19
+ const result = matchIntent(intents, "Set up template 'marketing-digest'.");
20
+ expect(result).not.toBeNull();
21
+ expect(result?.intent.id).toBe('install');
22
+ expect(result?.match[1]).toBe('marketing-digest');
23
+ });
24
+ it('first match wins when multiple intents would match', () => {
25
+ const intents = [
26
+ intent('first', /^hello (.+)$/),
27
+ intent('second', /^hello world$/),
28
+ ];
29
+ const result = matchIntent(intents, 'hello world');
30
+ expect(result?.intent.id).toBe('first');
31
+ expect(result?.match[1]).toBe('world');
32
+ });
33
+ it('treats unanchored regexes literally — author responsibility', () => {
34
+ // "matchIntent" itself doesn't enforce anchoring, but unanchored
35
+ // regexes are surprising. This test documents the behavior:
36
+ // "say hello to me" matches /hello/ even though probably not
37
+ // what the author wanted.
38
+ const intents = [intent('hello', /hello/)];
39
+ const result = matchIntent(intents, 'say hello to me');
40
+ expect(result?.intent.id).toBe('hello');
41
+ });
42
+ it('resets lastIndex on /g-flagged regexes so repeated calls work', () => {
43
+ // Defensive: a global regex with state would otherwise produce
44
+ // null on the second call.
45
+ const intents = [intent('global', /^hello$/g)];
46
+ expect(matchIntent(intents, 'hello')?.intent.id).toBe('global');
47
+ expect(matchIntent(intents, 'hello')?.intent.id).toBe('global');
48
+ });
49
+ it('handles an empty intent list', () => {
50
+ expect(matchIntent([], 'anything')).toBeNull();
51
+ });
52
+ });
53
+ //# sourceMappingURL=matcher.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matcher.test.js","sourceRoot":"","sources":["../../../src/intent/matcher.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Amodal Labs, Inc.
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ export {};