@amodalai/runtime 0.3.49 → 0.3.50

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,310 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Amodal Labs, Inc.
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ /**
7
+ * Phase E.2 integration test for `commitSetup`. Runs against a real
8
+ * Postgres + a real `LocalFsBackend` over a temp repo so the
9
+ * file-then-DB ordering, idempotency, and validation gating all get
10
+ * exercised end-to-end.
11
+ *
12
+ * Skips when DATABASE_URL is not set (same pattern as the Phase B
13
+ * setup-state.test.ts).
14
+ */
15
+ import { randomUUID } from 'node:crypto';
16
+ import { mkdtemp, readFile, rm } from 'node:fs/promises';
17
+ import { tmpdir } from 'node:os';
18
+ import * as path from 'node:path';
19
+ import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest';
20
+ import { closeDb, ensureSchema, getDb, upsertSetupState, } from '@amodalai/db';
21
+ import { LocalFsBackend } from '../tools/fs/local.js';
22
+ import { commitSetup, composeAmodalJson } from './commit-setup.js';
23
+ const HAS_DB = Boolean(process.env['DATABASE_URL']);
24
+ const describeWhenDb = HAS_DB ? describe : describe.skip;
25
+ function makePlan(overrides) {
26
+ return {
27
+ templatePackage: '@amodalai/test-template',
28
+ slots: [],
29
+ config: [],
30
+ completion: {
31
+ title: 'Test Agent',
32
+ suggestions: [],
33
+ automationTitle: null,
34
+ },
35
+ ...overrides,
36
+ };
37
+ }
38
+ const slackSlot = {
39
+ label: 'Slack',
40
+ description: 'Where the digest gets posted.',
41
+ required: true,
42
+ multi: false,
43
+ options: [
44
+ {
45
+ packageName: '@amodalai/connection-slack',
46
+ displayName: 'Slack',
47
+ authType: 'bearer',
48
+ oauthScopes: [],
49
+ },
50
+ ],
51
+ };
52
+ describeWhenDb('commitSetup (Phase E.2 integration)', () => {
53
+ let db;
54
+ let repoRoot;
55
+ let fs;
56
+ let agentId;
57
+ const scopeId = '';
58
+ beforeAll(async () => {
59
+ db = getDb();
60
+ await ensureSchema(db);
61
+ });
62
+ afterAll(async () => {
63
+ await closeDb();
64
+ });
65
+ beforeEach(async () => {
66
+ repoRoot = await mkdtemp(path.join(tmpdir(), 'commit-setup-'));
67
+ fs = new LocalFsBackend({ repoRoot });
68
+ agentId = `agent_${randomUUID()}`;
69
+ });
70
+ afterEach(async () => {
71
+ // Clean DB row + temp repo so reruns stay isolated.
72
+ try {
73
+ const { deleteSetupState } = await import('@amodalai/db');
74
+ await deleteSetupState(db, agentId, scopeId);
75
+ }
76
+ catch {
77
+ // Ignore — best-effort cleanup.
78
+ }
79
+ await rm(repoRoot, { recursive: true, force: true });
80
+ });
81
+ it('returns no_state when no setup_state row exists', async () => {
82
+ const result = await commitSetup({ db, fs, agentId, scopeId });
83
+ expect(result).toMatchObject({ ok: false, reason: 'no_state' });
84
+ });
85
+ it('returns not_ready when a required slot is missing', async () => {
86
+ await upsertSetupState(db, agentId, scopeId, {
87
+ phase: 'configuring',
88
+ plan: makePlan({ slots: [slackSlot] }),
89
+ });
90
+ const result = await commitSetup({ db, fs, agentId, scopeId });
91
+ expect(result.ok).toBe(false);
92
+ if (result.ok)
93
+ throw new Error('unreachable');
94
+ expect(result.reason).toBe('not_ready');
95
+ if (result.reason !== 'not_ready')
96
+ throw new Error('unreachable');
97
+ expect(result.warnings[0]?.kind).toBe('missing_required_slot');
98
+ });
99
+ it('commits when required slots are satisfied — writes amodal.json + marks completed_at', async () => {
100
+ await upsertSetupState(db, agentId, scopeId, {
101
+ phase: 'configuring',
102
+ plan: makePlan({
103
+ slots: [slackSlot],
104
+ completion: { title: 'Marketing Digest', suggestions: [], automationTitle: 'Weekly digest' },
105
+ }),
106
+ appendCompleted: [
107
+ {
108
+ slotLabel: 'Slack',
109
+ packageName: '@amodalai/connection-slack',
110
+ connectedAt: new Date().toISOString(),
111
+ validatedAt: new Date().toISOString(),
112
+ validationFormatted: 'Found 12 channels',
113
+ },
114
+ ],
115
+ });
116
+ const result = await commitSetup({ db, fs, agentId, scopeId });
117
+ expect(result.ok).toBe(true);
118
+ if (!result.ok)
119
+ throw new Error('unreachable');
120
+ expect(result.alreadyComplete).toBe(false);
121
+ expect(result.completedAt).toBeInstanceOf(Date);
122
+ // amodal.json was written with the right content.
123
+ const written = await readFile(path.join(repoRoot, 'amodal.json'), 'utf-8');
124
+ const parsed = JSON.parse(written);
125
+ expect(parsed.name).toBe('marketing-digest');
126
+ expect(parsed.version).toBe('1.0.0');
127
+ expect(parsed.packages).toContain('@amodalai/test-template');
128
+ expect(parsed.packages).toContain('@amodalai/connection-slack');
129
+ });
130
+ it('is idempotent — second commit returns alreadyComplete: true with the same timestamp', async () => {
131
+ await upsertSetupState(db, agentId, scopeId, {
132
+ phase: 'configuring',
133
+ plan: makePlan(),
134
+ });
135
+ const r1 = await commitSetup({ db, fs, agentId, scopeId });
136
+ expect(r1.ok).toBe(true);
137
+ if (!r1.ok)
138
+ throw new Error('unreachable');
139
+ const r2 = await commitSetup({ db, fs, agentId, scopeId });
140
+ expect(r2.ok).toBe(true);
141
+ if (!r2.ok)
142
+ throw new Error('unreachable');
143
+ expect(r2.alreadyComplete).toBe(true);
144
+ expect(r2.completedAt.getTime()).toBe(r1.completedAt.getTime());
145
+ });
146
+ it('force: true commits even with missing required slots', async () => {
147
+ await upsertSetupState(db, agentId, scopeId, {
148
+ phase: 'configuring',
149
+ plan: makePlan({ slots: [slackSlot] }),
150
+ });
151
+ const result = await commitSetup({ db, fs, agentId, scopeId, force: true });
152
+ expect(result.ok).toBe(true);
153
+ if (!result.ok)
154
+ throw new Error('unreachable');
155
+ // amodal.json was still written even without the Slack connection
156
+ // — this is the "user said skip Slack and finish anyway" path.
157
+ const written = await readFile(path.join(repoRoot, 'amodal.json'), 'utf-8');
158
+ expect(written.length).toBeGreaterThan(0);
159
+ });
160
+ it('returns not_ready with a clear warning when no Plan is attached', async () => {
161
+ // Edge case: row exists (from update_setup_state seeding planning
162
+ // phase) but no Plan ever got attached.
163
+ await upsertSetupState(db, agentId, scopeId, { phase: 'planning' });
164
+ const result = await commitSetup({ db, fs, agentId, scopeId });
165
+ expect(result.ok).toBe(false);
166
+ if (result.ok)
167
+ throw new Error('unreachable');
168
+ expect(result.reason).toBe('not_ready');
169
+ });
170
+ it('does not write amodal.json when readiness fails', async () => {
171
+ await upsertSetupState(db, agentId, scopeId, {
172
+ phase: 'configuring',
173
+ plan: makePlan({ slots: [slackSlot] }),
174
+ });
175
+ const result = await commitSetup({ db, fs, agentId, scopeId });
176
+ expect(result.ok).toBe(false);
177
+ // The temp repo is fresh — no amodal.json should have been
178
+ // written when readiness blocked the commit.
179
+ await expect(readFile(path.join(repoRoot, 'amodal.json'), 'utf-8')).rejects.toThrow();
180
+ });
181
+ it('survives a concurrent double-commit: first wins, second sees alreadyComplete', async () => {
182
+ // Phase E.2 risk from the build plan: "Agent fires
183
+ // request_complete_setup at the same instant the user clicks
184
+ // Finish setup. Both reach commit_setup; first acquires the
185
+ // row lock and writes; second sees completed_at IS NOT NULL
186
+ // and returns alreadyComplete: true. Test this explicitly
187
+ // with two parallel HTTP calls."
188
+ await upsertSetupState(db, agentId, scopeId, {
189
+ phase: 'configuring',
190
+ plan: makePlan(),
191
+ });
192
+ const [r1, r2] = await Promise.all([
193
+ commitSetup({ db, fs, agentId, scopeId }),
194
+ commitSetup({ db, fs, agentId, scopeId }),
195
+ ]);
196
+ // Both succeed.
197
+ expect(r1.ok).toBe(true);
198
+ expect(r2.ok).toBe(true);
199
+ if (!r1.ok || !r2.ok)
200
+ throw new Error('unreachable');
201
+ // Exactly one was the canonical write; the other reports
202
+ // alreadyComplete: true. Both report the same timestamp
203
+ // (the second is reading the row the first wrote).
204
+ const canonical = r1.alreadyComplete ? r2 : r1;
205
+ const followUp = r1.alreadyComplete ? r1 : r2;
206
+ expect(canonical.alreadyComplete).toBe(false);
207
+ expect(followUp.alreadyComplete).toBe(true);
208
+ expect(canonical.completedAt.getTime()).toBe(followUp.completedAt.getTime());
209
+ // The amodal.json that landed is well-formed (only one writer
210
+ // — the second branch early-returned before writing).
211
+ const written = await readFile(path.join(repoRoot, 'amodal.json'), 'utf-8');
212
+ const parsed = JSON.parse(written);
213
+ expect(parsed.name).toBeTruthy();
214
+ expect(parsed.version).toBeTruthy();
215
+ });
216
+ it('concurrent commits with one force=true still resolve idempotently', async () => {
217
+ // Edge case: the agent calls request_complete_setup (force:
218
+ // false) at the same time the user clicks Finish anyway
219
+ // (force: true). Whichever lands first wins; the other
220
+ // returns alreadyComplete: true. Force flag is irrelevant
221
+ // post-completion since completedAt is already set.
222
+ await upsertSetupState(db, agentId, scopeId, {
223
+ phase: 'configuring',
224
+ plan: makePlan(),
225
+ });
226
+ const [r1, r2] = await Promise.all([
227
+ commitSetup({ db, fs, agentId, scopeId, force: false }),
228
+ commitSetup({ db, fs, agentId, scopeId, force: true }),
229
+ ]);
230
+ expect(r1.ok).toBe(true);
231
+ expect(r2.ok).toBe(true);
232
+ if (!r1.ok || !r2.ok)
233
+ throw new Error('unreachable');
234
+ expect(r1.completedAt.getTime()).toBe(r2.completedAt.getTime());
235
+ });
236
+ });
237
+ describe('composeAmodalJson (unit)', () => {
238
+ it('builds a config from name + packages with template first', () => {
239
+ const config = composeAmodalJson({
240
+ phase: 'configuring',
241
+ currentStep: null,
242
+ completed: [
243
+ {
244
+ slotLabel: 'Slack',
245
+ packageName: '@amodalai/connection-slack',
246
+ connectedAt: '2026-04-30T10:00:00Z',
247
+ validatedAt: '2026-04-30T10:00:05Z',
248
+ validationFormatted: 'Found 12',
249
+ },
250
+ ],
251
+ skipped: [],
252
+ configAnswers: {},
253
+ deferredRequests: [],
254
+ providedContext: {},
255
+ plan: makePlan({
256
+ templatePackage: '@amodalai/marketing-ops',
257
+ completion: { title: 'Marketing Ops', suggestions: [], automationTitle: null },
258
+ }),
259
+ }, makePlan({
260
+ templatePackage: '@amodalai/marketing-ops',
261
+ completion: { title: 'Marketing Ops', suggestions: [], automationTitle: null },
262
+ }));
263
+ expect(config.name).toBe('marketing-ops');
264
+ expect(config.packages).toEqual(['@amodalai/marketing-ops', '@amodalai/connection-slack']);
265
+ });
266
+ it('falls back to "agent" when the completion title is empty', () => {
267
+ const config = composeAmodalJson({
268
+ phase: 'configuring',
269
+ currentStep: null,
270
+ completed: [],
271
+ skipped: [],
272
+ configAnswers: {},
273
+ deferredRequests: [],
274
+ providedContext: {},
275
+ plan: null,
276
+ }, null);
277
+ expect(config.name).toBe('agent');
278
+ expect(config.packages).toBeUndefined();
279
+ });
280
+ it('dedupes packages across the template + completed list', () => {
281
+ const config = composeAmodalJson({
282
+ phase: 'configuring',
283
+ currentStep: null,
284
+ completed: [
285
+ {
286
+ slotLabel: 'Slack',
287
+ packageName: '@amodalai/connection-slack',
288
+ connectedAt: 'x',
289
+ validatedAt: null,
290
+ validationFormatted: null,
291
+ },
292
+ {
293
+ slotLabel: 'Slack DM',
294
+ packageName: '@amodalai/connection-slack',
295
+ connectedAt: 'x',
296
+ validatedAt: null,
297
+ validationFormatted: null,
298
+ },
299
+ ],
300
+ skipped: [],
301
+ configAnswers: {},
302
+ deferredRequests: [],
303
+ providedContext: {},
304
+ plan: makePlan({ templatePackage: '@amodalai/connection-slack' }),
305
+ }, makePlan({ templatePackage: '@amodalai/connection-slack' }));
306
+ // Single occurrence even though it appeared 3 times across template + completed.
307
+ expect(config.packages).toEqual(['@amodalai/connection-slack']);
308
+ });
309
+ });
310
+ //# sourceMappingURL=commit-setup.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commit-setup.test.js","sourceRoot":"","sources":["../../../src/setup/commit-setup.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,270 @@
1
+ <!--
2
+ Copyright 2026 Amodal Labs, Inc.
3
+ SPDX-License-Identifier: MIT
4
+ -->
5
+
6
+ # Custom-tool SDK
7
+
8
+ Public API for tools shipped inside agent packages — `agent-admin`,
9
+ `connection-*`, `template-*`, and any third-party agent the registry
10
+ points to. Every tool that wants to do more than return a string
11
+ (emit inline UI, read or write files, run a query, make an HTTP call)
12
+ goes through this surface.
13
+
14
+ The SDK is maintained as **additive-only** post-launch. Existing
15
+ fields and block types do not change shape — older tools keep working
16
+ when the runtime upgrades, and the registry doesn't have to track
17
+ per-package SDK versions.
18
+
19
+ ## Where tools live
20
+
21
+ ```
22
+ <agent-package>/
23
+ package.json ← amodal.permissions declares capabilities
24
+ tools/
25
+ my_tool/
26
+ tool.json ← description + JSON Schema parameters
27
+ handler.ts ← export default async (params, ctx) => …
28
+ ```
29
+
30
+ The runtime walks `<agent-package>/tools/*/{tool.json, handler.ts}`
31
+ on agent load. Every directory whose name matches `^[a-z][a-z0-9_]*$`
32
+ becomes a tool the LLM can call. There is no separate registration
33
+ step.
34
+
35
+ ## Writing a handler
36
+
37
+ ```ts
38
+ // tools/show_preview/handler.ts
39
+ import type { ToolContext } from "@amodalai/runtime/tools";
40
+
41
+ export default async function showPreview(
42
+ params: { title: string; tagline: string },
43
+ ctx: ToolContext,
44
+ ): Promise<{ ok: true }> {
45
+ ctx.emit({
46
+ type: "block",
47
+ block: {
48
+ type: "agent_card_preview",
49
+ card: {
50
+ title: params.title,
51
+ tagline: params.tagline,
52
+ platforms: [],
53
+ thumbnailConversation: [],
54
+ },
55
+ },
56
+ });
57
+ return { ok: true };
58
+ }
59
+ ```
60
+
61
+ The first argument is the parameter object (validated against
62
+ `tool.json#parameters` before the handler runs). The second is a
63
+ [`ToolContext`](./context.ts) — the SDK surface this README documents.
64
+
65
+ Pure-JS handlers work too — agent packages with no build step can ship
66
+ a plain function and skip the `@amodalai/runtime` import. The runtime
67
+ compiles `handler.ts` with esbuild on first call regardless.
68
+
69
+ ## `ToolContext` reference
70
+
71
+ ```ts
72
+ interface ToolContext {
73
+ // Identity
74
+ agentId: string;
75
+ scopeId: string; // empty string = agent-level
76
+ scopeContext?: Record<string, string>;
77
+ sessionId: string;
78
+
79
+ // Side effects (always available)
80
+ emit(event: EmitEvent): void; // structured: text | block | error
81
+ log(message: string): void; // sugar for emit({type:'text', …})
82
+
83
+ // Capabilities (permission-gated)
84
+ fs: FsBackend;
85
+ db: ToolDbHandle;
86
+ fetch: typeof globalThis.fetch;
87
+
88
+ // Cancellation
89
+ signal: AbortSignal;
90
+ }
91
+ ```
92
+
93
+ ### `ctx.emit(event)`
94
+
95
+ Three event variants:
96
+
97
+ - `{type: 'text', text}` — appended as agent prose alongside the LLM's stream.
98
+ - `{type: 'block', block}` — dispatched into the inline-block list and rendered
99
+ by the widget (or by Studio's `inlineBlockRenderers` for Studio-only block
100
+ types like `connection_panel`).
101
+ - `{type: 'error', message}` — surfaces an inline error notice and is logged
102
+ server-side.
103
+
104
+ Block types are typed in [`@amodalai/types/blocks`](../../../types/src/blocks.ts)
105
+ as a discriminated union — `text`, `ask_choice`, `agent_card_preview`,
106
+ `connection_panel`, `proposal`, `update_plan`. Add a new variant by
107
+ extending the union; the widget reducer keys off the `type` field.
108
+
109
+ ### `ctx.fs` — repo file access
110
+
111
+ ```ts
112
+ ctx.fs.readRepoFile("amodal.json");
113
+ ctx.fs.writeRepoFile("skills/digest/SKILL.md", body);
114
+ ctx.fs.readManyRepoFiles(["a.json", "b.json"]);
115
+ ctx.fs.listRepoFiles("skills/");
116
+ ctx.fs.deleteRepoFile("skills/old-skill/SKILL.md");
117
+ ```
118
+
119
+ All paths are repo-relative. Absolute paths and `..` traversal are
120
+ rejected — the backend throws `FsSandboxError`. The same handler
121
+ works against either backend the runtime selects via
122
+ `AMODAL_REPO_MODE`:
123
+
124
+ - `local` (default, `amodal dev`) — direct `fs/promises`, sub-ms.
125
+ - `cloud` — proxies through `cloud-phase-4/platform-api`'s
126
+ `/api/repo/files/*` routes (Phase 0G); ~20-50ms per write.
127
+
128
+ Permission gates: `readRepoFile` / `readManyRepoFiles` / `listRepoFiles`
129
+ require `fs.read`. `writeRepoFile` / `deleteRepoFile` require `fs.write`.
130
+
131
+ ### `ctx.db` — Drizzle handle
132
+
133
+ ```ts
134
+ const rows = await ctx.db.execute({
135
+ sql: "SELECT * FROM setup_state WHERE agent_id = $1",
136
+ params: [ctx.agentId],
137
+ });
138
+ ```
139
+
140
+ Scoped to the agent's session. Phase B introduces per-domain query
141
+ modules at `@amodalai/db/queries/<domain>.ts` (the Midday pattern); use
142
+ those over raw SQL when one exists.
143
+
144
+ Permission gate: any call to `ctx.db.execute` requires both `db.read`
145
+ and `db.write` — the SDK can't reliably parse SQL to know which is
146
+ which, so the conservative gate is "declare both if you touch the DB
147
+ at all." Phase B may split into `query()` / `mutate()` once we see how
148
+ db tools shake out in practice.
149
+
150
+ ### `ctx.fetch` — outbound HTTP
151
+
152
+ Same signature as `globalThis.fetch`. The runtime injects `ctx.signal`
153
+ when the caller doesn't pass one, so outbound requests cancel with the
154
+ tool invocation.
155
+
156
+ Permission gate: `net.fetch`.
157
+
158
+ ## Declaring permissions
159
+
160
+ In the agent package's `package.json`:
161
+
162
+ ```json
163
+ {
164
+ "name": "@amodalai/agent-admin",
165
+ "amodal": {
166
+ "permissions": ["fs.read", "fs.write", "db.read", "db.write"]
167
+ }
168
+ }
169
+ ```
170
+
171
+ Default-deny: a package with no `amodal.permissions` block gets an
172
+ empty list, and any tool inside it that reaches for a privileged
173
+ `ctx.*` capability throws `PermissionError` at the boundary, naming
174
+ the tool, the missing permission, and the package.
175
+
176
+ Available tiers:
177
+
178
+ - `fs.read` / `fs.write` — repo file access.
179
+ - `db.read` / `db.write` — Drizzle `ctx.db.execute`.
180
+ - `net.fetch` — outbound HTTP.
181
+
182
+ Notably **not present**: `secrets.*`. Credentials enter the system in
183
+ exactly two places — the Configure modal (`POST /api/secrets/:name`)
184
+ and the OAuth callback (`/api/oauth/callback`). The SDK has no
185
+ `ctx.saveSecret` and never will. Tools never see tokens in args, in
186
+ chat history, or in the LLM's context window. If your tool needs a
187
+ secret to talk to a third party, read it from `process.env` at runtime
188
+ through the connection package's auth surface — never accept it as a
189
+ parameter from the LLM.
190
+
191
+ ## Common patterns
192
+
193
+ ### Emit a block from a handler
194
+
195
+ ```ts
196
+ ctx.emit({
197
+ type: "block",
198
+ block: {
199
+ type: "ask_choice",
200
+ askId: `choice_${ctx.sessionId}_${Date.now().toString(36)}`,
201
+ question: "When should the digest run?",
202
+ options: [
203
+ { label: "Monday 8 AM", value: "monday-8am" },
204
+ { label: "Friday 4 PM", value: "friday-4pm" },
205
+ ],
206
+ },
207
+ });
208
+ ```
209
+
210
+ ### Read setup state from the DB (Phase B onward)
211
+
212
+ ```ts
213
+ const rows = await ctx.db.execute({
214
+ sql: "SELECT * FROM setup_state WHERE agent_id = $1 AND scope_id = $2",
215
+ params: [ctx.agentId, ctx.scopeId],
216
+ });
217
+ ```
218
+
219
+ ### Write a config file
220
+
221
+ ```ts
222
+ await ctx.fs.writeRepoFile(
223
+ "amodal.json",
224
+ JSON.stringify({ ...config, packages: [...config.packages, name] }, null, 2) +
225
+ "\n",
226
+ );
227
+ ```
228
+
229
+ ### Make an outbound HTTP call
230
+
231
+ ```ts
232
+ const res = await ctx.fetch("https://api.example.com/probe", {
233
+ headers: { Authorization: `Bearer ${process.env.MY_TOKEN}` },
234
+ });
235
+ const data = (await res.json()) as { count: number };
236
+ ```
237
+
238
+ ## Errors
239
+
240
+ - `PermissionError` — a `ctx.*` method was called without the matching
241
+ permission declared. Message names tool / package / permission so
242
+ the package author can fix the manifest in one place.
243
+ - `FsSandboxError` — a path resolved outside the repo root. Callers
244
+ should treat this as a bug, not a recoverable failure.
245
+
246
+ Both extend `Error` and are exported from `@amodalai/runtime/tools`.
247
+
248
+ ## Versioning
249
+
250
+ The SDK ships in `@amodalai/runtime`. Subpath import:
251
+
252
+ ```ts
253
+ import type { ToolContext } from "@amodalai/runtime/tools";
254
+ import {
255
+ PermissionError,
256
+ FsSandboxError,
257
+ LocalFsBackend,
258
+ } from "@amodalai/runtime/tools";
259
+ ```
260
+
261
+ Treat the block-type union and the `ToolContext` interface as
262
+ **additive-only**. New phases extend; they do not rename or remove.
263
+
264
+ ## Related
265
+
266
+ - **Connection validation probes** (Phase A) — the contract a connection
267
+ package's `validate.js` follows so the admin agent's
268
+ `validate_connection` tool can surface a real-data sanity check after
269
+ Connect. Documented in
270
+ [`@amodalai/core/cards/README.md`](../../../core/src/cards/README.md#connection-validation-probes).
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Amodal Labs, Inc.
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import type { ToolRegistry } from './types.js';
7
+ import type { Logger } from '../logger.js';
8
+ export interface AdminToolsOptions {
9
+ /** Path to the agent repo — `install_package` writes amodal.json here. */
10
+ repoRoot: string;
11
+ /** npm registry base URL. Defaults to npmjs.org; tests override. */
12
+ registryUrl?: string;
13
+ logger: Logger;
14
+ }
15
+ export declare function registerAdminTools(registry: ToolRegistry, opts: AdminToolsOptions): void;
16
+ interface SearchHit {
17
+ name: string;
18
+ version: string;
19
+ description: string;
20
+ keywords: string[];
21
+ }
22
+ /**
23
+ * Pull the relevant fields from npm's `/-/v1/search` response. The response
24
+ * shape is `{ objects: [{ package: { name, version, description, keywords } }] }`.
25
+ */
26
+ export declare function extractSearchHits(raw: unknown): SearchHit[];
27
+ export {};