@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,6 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Amodal Labs, Inc.
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ export {};
@@ -0,0 +1,263 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Amodal Labs, Inc.
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ /**
7
+ * Phase E.3 + E.4 + E.10 integration test. Pins the agent-admin
8
+ * completion tools end-to-end through the actual runtime path:
9
+ * LocalToolExecutor compiles each handler.ts via esbuild, dynamic-imports
10
+ * the resulting .mjs, and runs it with a CustomToolContext whose
11
+ * completion ops are backed by the real commitSetup primitive +
12
+ * deleteSetupState query (against a temp Postgres + temp repo).
13
+ *
14
+ * Skips when DATABASE_URL or the agent-admin cache isn't populated.
15
+ */
16
+ import { existsSync } from 'node:fs';
17
+ import { randomUUID } from 'node:crypto';
18
+ import { mkdtemp, readFile, rm } from 'node:fs/promises';
19
+ import { homedir, tmpdir } from 'node:os';
20
+ import * as path from 'node:path';
21
+ import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest';
22
+ import { and, eq } from 'drizzle-orm';
23
+ import { closeDb, deleteSetupState, ensureSchema, getDb, setupState, upsertSetupState, } from '@amodalai/db';
24
+ import { LocalFsBackend } from '../tools/fs/local.js';
25
+ import { commitSetup } from '../setup/commit-setup.js';
26
+ import { LocalToolExecutor } from './tool-executor-local.js';
27
+ const ADMIN_AGENT_PATH = path.join(homedir(), '.amodal', 'admin-agent', 'latest');
28
+ const REQUEST_HANDLER = path.join(ADMIN_AGENT_PATH, 'tools', 'request_complete_setup', 'handler.ts');
29
+ const FORCE_HANDLER = path.join(ADMIN_AGENT_PATH, 'tools', 'force_complete_setup', 'handler.ts');
30
+ const CANCEL_HANDLER = path.join(ADMIN_AGENT_PATH, 'tools', 'cancel_setup', 'handler.ts');
31
+ const HAS_DEPS = Boolean(process.env['DATABASE_URL']) &&
32
+ existsSync(REQUEST_HANDLER) &&
33
+ existsSync(FORCE_HANDLER) &&
34
+ existsSync(CANCEL_HANDLER);
35
+ const describeWhenReady = HAS_DEPS ? describe : describe.skip;
36
+ function makePlan(overrides) {
37
+ return {
38
+ templatePackage: '@amodalai/test-template',
39
+ slots: [],
40
+ config: [],
41
+ completion: { title: 'Test Agent', suggestions: [], automationTitle: null },
42
+ ...overrides,
43
+ };
44
+ }
45
+ const slackSlot = {
46
+ label: 'Slack',
47
+ description: 'Where the digest gets posted.',
48
+ required: true,
49
+ multi: false,
50
+ options: [
51
+ {
52
+ packageName: '@amodalai/connection-slack',
53
+ displayName: 'Slack',
54
+ authType: 'bearer',
55
+ oauthScopes: [],
56
+ },
57
+ ],
58
+ };
59
+ describeWhenReady('completion tools (Phase E.3 / E.4 / E.10 integration)', () => {
60
+ let db;
61
+ let repoRoot;
62
+ let fs;
63
+ let executor;
64
+ let agentId;
65
+ const scopeId = '';
66
+ beforeAll(async () => {
67
+ db = getDb();
68
+ await ensureSchema(db);
69
+ });
70
+ afterAll(async () => {
71
+ await closeDb();
72
+ });
73
+ beforeEach(async () => {
74
+ repoRoot = await mkdtemp(path.join(tmpdir(), 'completion-'));
75
+ fs = new LocalFsBackend({ repoRoot });
76
+ executor = new LocalToolExecutor();
77
+ agentId = `agent_${randomUUID()}`;
78
+ });
79
+ afterEach(async () => {
80
+ executor.dispose();
81
+ try {
82
+ await deleteSetupState(db, agentId, scopeId);
83
+ }
84
+ catch {
85
+ // Best-effort.
86
+ }
87
+ await rm(repoRoot, { recursive: true, force: true });
88
+ });
89
+ function loadedTool(handlerPath, name) {
90
+ return {
91
+ name,
92
+ description: `${name} test`,
93
+ parameters: {},
94
+ confirm: false,
95
+ timeout: 10_000,
96
+ env: [],
97
+ handlerPath,
98
+ location: path.dirname(handlerPath),
99
+ hasPackageJson: false,
100
+ hasSetupScript: false,
101
+ hasRequirementsTxt: false,
102
+ hasDockerfile: false,
103
+ sandboxLanguage: 'typescript',
104
+ };
105
+ }
106
+ function buildCompletionOps() {
107
+ return {
108
+ async commit(opts) {
109
+ const result = await commitSetup({
110
+ db,
111
+ fs,
112
+ agentId,
113
+ scopeId,
114
+ force: opts?.force ?? false,
115
+ });
116
+ if (result.ok) {
117
+ return {
118
+ ok: true,
119
+ alreadyComplete: result.alreadyComplete,
120
+ completedAt: result.completedAt.toISOString(),
121
+ };
122
+ }
123
+ if (result.reason === 'not_ready') {
124
+ return { ok: false, reason: 'not_ready', warnings: result.warnings };
125
+ }
126
+ return { ok: false, reason: 'no_state', message: result.message };
127
+ },
128
+ async cancel() {
129
+ const deleted = await deleteSetupState(db, agentId, scopeId);
130
+ return { ok: true, deleted };
131
+ },
132
+ };
133
+ }
134
+ function buildCtx(opts) {
135
+ const completion = opts && Object.prototype.hasOwnProperty.call(opts, 'completion')
136
+ ? opts.completion
137
+ : buildCompletionOps();
138
+ return {
139
+ async request() {
140
+ throw new Error('not used');
141
+ },
142
+ async exec() {
143
+ throw new Error('not used');
144
+ },
145
+ async store() {
146
+ throw new Error('not used');
147
+ },
148
+ env: () => undefined,
149
+ log: () => undefined,
150
+ signal: AbortSignal.timeout(30_000),
151
+ agentId,
152
+ scopeId,
153
+ sessionId: 'sess_test',
154
+ ...(completion ? { completion } : {}),
155
+ ...(opts?.emitCapture
156
+ ? {
157
+ emit: (event) => {
158
+ opts.emitCapture?.push(event);
159
+ },
160
+ }
161
+ : {}),
162
+ };
163
+ }
164
+ // -------------------------------------------------------------------
165
+ // request_complete_setup (E.3)
166
+ // -------------------------------------------------------------------
167
+ it('request_complete_setup returns not_ready with warnings when slots are missing', async () => {
168
+ await upsertSetupState(db, agentId, scopeId, {
169
+ phase: 'configuring',
170
+ plan: makePlan({ slots: [slackSlot] }),
171
+ });
172
+ const result = await executor.execute(loadedTool(REQUEST_HANDLER, 'request_complete_setup'), {}, buildCtx());
173
+ expect(result).toMatchObject({ ok: false, reason: 'not_ready' });
174
+ const r = result;
175
+ expect(r.warnings[0]?.target).toBe('Slack');
176
+ // amodal.json was NOT written.
177
+ await expect(readFile(path.join(repoRoot, 'amodal.json'), 'utf-8')).rejects.toThrow();
178
+ });
179
+ it('request_complete_setup commits when validation passes and writes amodal.json', async () => {
180
+ await upsertSetupState(db, agentId, scopeId, {
181
+ phase: 'configuring',
182
+ plan: makePlan({
183
+ slots: [slackSlot],
184
+ completion: { title: 'Slackbot', suggestions: [], automationTitle: null },
185
+ }),
186
+ appendCompleted: [
187
+ {
188
+ slotLabel: 'Slack',
189
+ packageName: '@amodalai/connection-slack',
190
+ connectedAt: new Date().toISOString(),
191
+ validatedAt: new Date().toISOString(),
192
+ validationFormatted: 'Found 12 channels',
193
+ },
194
+ ],
195
+ });
196
+ const result = await executor.execute(loadedTool(REQUEST_HANDLER, 'request_complete_setup'), {}, buildCtx());
197
+ expect(result).toMatchObject({ ok: true, alreadyComplete: false });
198
+ const written = JSON.parse(await readFile(path.join(repoRoot, 'amodal.json'), 'utf-8'));
199
+ expect(written.name).toBe('slackbot');
200
+ expect(written.packages).toContain('@amodalai/connection-slack');
201
+ });
202
+ it('request_complete_setup is idempotent — second call returns alreadyComplete:true', async () => {
203
+ await upsertSetupState(db, agentId, scopeId, {
204
+ phase: 'configuring',
205
+ plan: makePlan(),
206
+ });
207
+ const r1 = await executor.execute(loadedTool(REQUEST_HANDLER, 'request_complete_setup'), {}, buildCtx());
208
+ const r2 = await executor.execute(loadedTool(REQUEST_HANDLER, 'request_complete_setup'), {}, buildCtx());
209
+ expect(r1).toMatchObject({ ok: true });
210
+ expect(r2).toMatchObject({ ok: true, alreadyComplete: true });
211
+ });
212
+ it('request_complete_setup returns no_ops when ctx.completion is absent', async () => {
213
+ const result = await executor.execute(loadedTool(REQUEST_HANDLER, 'request_complete_setup'), {}, buildCtx({ completion: null }));
214
+ expect(result).toMatchObject({ ok: false, reason: 'no_ops' });
215
+ });
216
+ // -------------------------------------------------------------------
217
+ // force_complete_setup (E.4)
218
+ // -------------------------------------------------------------------
219
+ it('force_complete_setup commits even with missing required slots', async () => {
220
+ await upsertSetupState(db, agentId, scopeId, {
221
+ phase: 'configuring',
222
+ plan: makePlan({ slots: [slackSlot] }),
223
+ });
224
+ const result = await executor.execute(loadedTool(FORCE_HANDLER, 'force_complete_setup'), {}, buildCtx());
225
+ expect(result).toMatchObject({ ok: true, alreadyComplete: false });
226
+ const written = await readFile(path.join(repoRoot, 'amodal.json'), 'utf-8');
227
+ expect(written.length).toBeGreaterThan(0);
228
+ });
229
+ // -------------------------------------------------------------------
230
+ // cancel_setup (E.10)
231
+ // -------------------------------------------------------------------
232
+ it('cancel_setup deletes the row + emits a setup_cancelled SSE event', async () => {
233
+ await upsertSetupState(db, agentId, scopeId, { phase: 'planning' });
234
+ const captured = [];
235
+ const result = await executor.execute(loadedTool(CANCEL_HANDLER, 'cancel_setup'), { reason: 'wants a different template' }, buildCtx({ emitCapture: captured }));
236
+ expect(result).toMatchObject({ ok: true, deleted: true });
237
+ expect(captured).toHaveLength(1);
238
+ const event = captured[0];
239
+ if (event.type !== 'setup_cancelled')
240
+ throw new Error('expected setup_cancelled');
241
+ expect(event.reason).toBe('wants a different template');
242
+ // Row is gone.
243
+ const fresh = await db
244
+ .select()
245
+ .from(setupState)
246
+ .where(and(eq(setupState.agentId, agentId), eq(setupState.scopeId, scopeId)));
247
+ expect(fresh).toHaveLength(0);
248
+ });
249
+ it('cancel_setup returns deleted: false when no row existed', async () => {
250
+ const captured = [];
251
+ const result = await executor.execute(loadedTool(CANCEL_HANDLER, 'cancel_setup'), {}, buildCtx({ emitCapture: captured }));
252
+ expect(result).toMatchObject({ ok: true, deleted: false });
253
+ // The setup_cancelled event still fires — the agent emitting it
254
+ // is signaling intent, not confirming a row delete; Studio
255
+ // should flip to picker either way.
256
+ expect(captured).toHaveLength(1);
257
+ });
258
+ it('cancel_setup returns no_ops when ctx.completion is absent', async () => {
259
+ const result = await executor.execute(loadedTool(CANCEL_HANDLER, 'cancel_setup'), {}, buildCtx({ completion: null }));
260
+ expect(result).toMatchObject({ ok: false, reason: 'no_ops' });
261
+ });
262
+ });
263
+ //# sourceMappingURL=completion-tools.integration.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"completion-tools.integration.test.js","sourceRoot":"","sources":["../../../src/agent/completion-tools.integration.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 {};
@@ -0,0 +1,152 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Amodal Labs, Inc.
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ /**
7
+ * Phase C integration test for `load_template_plan`. Pins the
8
+ * end-to-end runtime path: LocalToolExecutor compiles the
9
+ * agent-admin handler via esbuild, dynamic-imports the resulting
10
+ * .mjs, and runs it with a CustomToolContext carrying a real
11
+ * `ctx.plan.compose` ops object backed by `composePlan` from
12
+ * `@amodalai/core`.
13
+ *
14
+ * The test sets up a synthetic repo with a fake template package +
15
+ * connection packages so the entire compose chain runs against real
16
+ * disk reads. Skips when the agent-admin cache isn't populated.
17
+ */
18
+ import { existsSync } from 'node:fs';
19
+ import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
20
+ import { homedir, tmpdir } from 'node:os';
21
+ import * as path from 'node:path';
22
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
23
+ import { composePlan } from '@amodalai/core';
24
+ import { LocalToolExecutor } from './tool-executor-local.js';
25
+ const ADMIN_AGENT_PATH = path.join(homedir(), '.amodal', 'admin-agent', 'latest');
26
+ const HANDLER = path.join(ADMIN_AGENT_PATH, 'tools', 'load_template_plan', 'handler.ts');
27
+ const HAS_HANDLER = existsSync(HANDLER);
28
+ const describeWhenReady = HAS_HANDLER ? describe : describe.skip;
29
+ describeWhenReady('load_template_plan (Phase C integration)', () => {
30
+ let repoRoot;
31
+ let executor;
32
+ beforeEach(async () => {
33
+ repoRoot = await mkdtemp(path.join(tmpdir(), 'phase-c-'));
34
+ executor = new LocalToolExecutor();
35
+ });
36
+ afterEach(async () => {
37
+ executor.dispose();
38
+ await rm(repoRoot, { recursive: true, force: true });
39
+ });
40
+ function loadedTool() {
41
+ return {
42
+ name: 'load_template_plan',
43
+ description: 'load_template_plan test',
44
+ parameters: {},
45
+ confirm: false,
46
+ timeout: 10_000,
47
+ env: [],
48
+ handlerPath: HANDLER,
49
+ location: path.dirname(HANDLER),
50
+ hasPackageJson: false,
51
+ hasSetupScript: false,
52
+ hasRequirementsTxt: false,
53
+ hasDockerfile: false,
54
+ sandboxLanguage: 'typescript',
55
+ };
56
+ }
57
+ function buildPlanOps() {
58
+ return {
59
+ async compose(templatePackageName) {
60
+ try {
61
+ const composed = await composePlan({ repoPath: repoRoot, templatePackage: templatePackageName });
62
+ return { ok: true, plan: composed };
63
+ }
64
+ catch (err) {
65
+ if (err !== null &&
66
+ typeof err === 'object' &&
67
+ 'code' in err &&
68
+ err.code === 'CONFIG_NOT_FOUND') {
69
+ return {
70
+ ok: false,
71
+ reason: 'not_installed',
72
+ message: `Template "${templatePackageName}" is not installed.`,
73
+ };
74
+ }
75
+ return {
76
+ ok: false,
77
+ reason: 'malformed',
78
+ message: err instanceof Error ? err.message : String(err),
79
+ };
80
+ }
81
+ },
82
+ };
83
+ }
84
+ function buildCtx(opts) {
85
+ // null = explicitly omit ctx.plan; undefined / missing = use the default real ops.
86
+ const planOps = opts && Object.prototype.hasOwnProperty.call(opts, 'planOps')
87
+ ? opts.planOps
88
+ : buildPlanOps();
89
+ return {
90
+ async request() {
91
+ throw new Error('not used');
92
+ },
93
+ async exec() {
94
+ throw new Error('not used');
95
+ },
96
+ async store() {
97
+ throw new Error('not used');
98
+ },
99
+ env: () => undefined,
100
+ log: () => undefined,
101
+ signal: AbortSignal.timeout(30_000),
102
+ agentId: 'agent_test',
103
+ scopeId: '',
104
+ sessionId: 'sess_test',
105
+ ...(planOps ? { plan: planOps } : {}),
106
+ };
107
+ }
108
+ async function writeJson(rel, value) {
109
+ const full = path.join(repoRoot, rel);
110
+ await mkdir(path.dirname(full), { recursive: true });
111
+ await writeFile(full, JSON.stringify(value), 'utf-8');
112
+ }
113
+ it('returns a SetupPlan composed from the template + connection packages', async () => {
114
+ await writeJson('node_modules/@amodalai/test-template/template.json', {
115
+ connections: [
116
+ {
117
+ label: 'Slack',
118
+ description: 'Where the digest gets posted.',
119
+ options: ['@amodalai/connection-slack'],
120
+ required: true,
121
+ },
122
+ ],
123
+ });
124
+ await writeJson('node_modules/@amodalai/connection-slack/package.json', {
125
+ name: '@amodalai/connection-slack',
126
+ amodal: {
127
+ displayName: 'Slack',
128
+ auth: { type: 'bearer' },
129
+ oauth: { scopes: ['chat:write'] },
130
+ },
131
+ });
132
+ const result = await executor.execute(loadedTool(), { packageName: '@amodalai/test-template' }, buildCtx());
133
+ const r = result;
134
+ expect(r.ok).toBe(true);
135
+ expect(r.plan.slots).toHaveLength(1);
136
+ expect(r.plan.slots[0].label).toBe('Slack');
137
+ expect(r.plan.slots[0].options[0].displayName).toBe('Slack');
138
+ });
139
+ it('surfaces not_installed soft-fail when the template is missing', async () => {
140
+ const result = await executor.execute(loadedTool(), { packageName: '@amodalai/never-installed' }, buildCtx());
141
+ expect(result).toMatchObject({ ok: false, reason: 'not_installed' });
142
+ });
143
+ it('rejects path-traversal package names without filesystem access', async () => {
144
+ const result = await executor.execute(loadedTool(), { packageName: '../../etc/passwd' }, buildCtx());
145
+ expect(result).toMatchObject({ ok: false, reason: 'error' });
146
+ });
147
+ it('returns no_ops when ctx.plan is absent', async () => {
148
+ const result = await executor.execute(loadedTool(), { packageName: '@amodalai/test' }, buildCtx({ planOps: null }));
149
+ expect(result).toMatchObject({ ok: false, reason: 'no_ops' });
150
+ });
151
+ });
152
+ //# sourceMappingURL=load-template-plan.integration.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-template-plan.integration.test.js","sourceRoot":"","sources":["../../../src/agent/load-template-plan.integration.test.ts"],"names":[],"mappings":"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"}
@@ -11,7 +11,7 @@
11
11
  * for config changes (hot reload).
12
12
  */
13
13
  import express from 'express';
14
- import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'node:fs';
14
+ import { existsSync, readFileSync, mkdirSync, writeFileSync, watch } from 'node:fs';
15
15
  import path from 'node:path';
16
16
  import { fileURLToPath } from 'node:url';
17
17
  import { randomUUID } from 'node:crypto';
@@ -67,6 +67,7 @@ import { createChatStreamRouter } from '../routes/chat-stream.js';
67
67
  import { createChatRouter } from '../routes/chat.js';
68
68
  import { createTaskRouter } from './routes/task.js';
69
69
  import { createInspectRouter } from './routes/inspect.js';
70
+ import { createPackageUpdatesRouter } from './routes/package-updates.js';
70
71
  import { createFeedbackRouter } from './routes/feedback.js';
71
72
  import { FeedbackStore } from './feedback-store.js';
72
73
  import { createStoresRouter } from './routes/stores.js';
@@ -326,6 +327,7 @@ export async function createLocalServer(config) {
326
327
  systemPrompt: components.systemPrompt,
327
328
  toolContextFactory: components.toolContextFactory,
328
329
  appId,
330
+ intents: components.intents,
329
331
  });
330
332
  return { session, toolContextFactory: components.toolContextFactory };
331
333
  };
@@ -510,6 +512,19 @@ export async function createLocalServer(config) {
510
512
  }
511
513
  return out;
512
514
  }
515
+ // ---------------------------------------------------------------------------
516
+ // /api/oauth/start + /api/oauth/callback — DEPRECATED
517
+ //
518
+ // OAuth start/callback moved to Studio (packages/studio/src/server/routes/oauth.ts).
519
+ // Studio is the boot surface — it's up before `amodal.json` exists, so the
520
+ // setup-time Configure flow can run before this runtime even starts. The
521
+ // runtime now watches `<repoPath>/.amodal/secrets.env` for changes and
522
+ // reloads `process.env` automatically when Studio writes a new credential.
523
+ //
524
+ // These handlers stay in for backward compat (any older Studio build or
525
+ // direct caller) but are no longer the canonical path. Plan: remove after
526
+ // one release.
527
+ // ---------------------------------------------------------------------------
513
528
  app.get('/api/oauth/start', (req, res) => {
514
529
  void (async () => {
515
530
  reapExpiredOauthStates();
@@ -628,13 +643,31 @@ export async function createLocalServer(config) {
628
643
  res.status(500).json({ error: msg });
629
644
  });
630
645
  });
631
- // Load any previously-persisted OAuth secrets into process.env on startup.
632
- // (Idempotent overwrites any existing value with the persisted one,
633
- // which is what users want after a runtime restart.)
646
+ // Load any previously-persisted secrets into process.env on startup,
647
+ // then watch the file for changes Studio writes to this file when
648
+ // it completes an OAuth dance or saves a paste-field, so the runtime
649
+ // needs to pick up new credentials without a restart.
650
+ //
651
+ // Path resolution: the admin agent is spawned with `cwd: <admin
652
+ // agent dir>` but `REPO_PATH=<user's repo>` in env. We want to
653
+ // watch the USER's secrets.env in both cases (main runtime and
654
+ // admin agent), so prefer REPO_PATH when set. Falls back to
655
+ // config.repoPath, which is the right path for the main runtime.
634
656
  {
635
- const file = path.join(config.repoPath, '.amodal', 'secrets.env');
636
- if (existsSync(file)) {
637
- const content = readFileSync(file, 'utf-8');
657
+ const secretsRepoPath = process.env['REPO_PATH'] ?? config.repoPath;
658
+ const dir = path.join(secretsRepoPath, '.amodal');
659
+ const file = path.join(dir, 'secrets.env');
660
+ function loadSecrets(reason) {
661
+ if (!existsSync(file))
662
+ return;
663
+ let content;
664
+ try {
665
+ content = readFileSync(file, 'utf-8');
666
+ }
667
+ catch (err) {
668
+ log.warn('secrets_read_failed', { file, error: err instanceof Error ? err.message : String(err) });
669
+ return;
670
+ }
638
671
  let count = 0;
639
672
  for (const line of content.split('\n')) {
640
673
  const eq = line.indexOf('=');
@@ -647,8 +680,28 @@ export async function createLocalServer(config) {
647
680
  count++;
648
681
  }
649
682
  }
650
- if (count > 0)
651
- log.info('secrets_loaded_from_disk', { count, file });
683
+ if (count > 0) {
684
+ log.info(reason === 'startup' ? 'secrets_loaded_from_disk' : 'secrets_reloaded_from_disk', { count, file });
685
+ }
686
+ }
687
+ loadSecrets('startup');
688
+ // Watch the parent dir (not the file directly) so we still pick up
689
+ // the first write — fs.watch on a non-existent file throws, and
690
+ // editor-style "atomic save" replaces the inode which most
691
+ // file-watchers miss when scoped to the path.
692
+ if (existsSync(dir)) {
693
+ try {
694
+ const watcher = watch(dir, { persistent: false }, (_event, filename) => {
695
+ if (filename === 'secrets.env')
696
+ loadSecrets('change');
697
+ });
698
+ watcher.on('error', (err) => {
699
+ log.warn('secrets_watcher_error', { file, error: err.message });
700
+ });
701
+ }
702
+ catch (err) {
703
+ log.warn('secrets_watch_failed', { dir, error: err instanceof Error ? err.message : String(err) });
704
+ }
652
705
  }
653
706
  }
654
707
  // ---- Secrets write + per-connection detail -----------------------------
@@ -911,6 +964,8 @@ export async function createLocalServer(config) {
911
964
  app.use(createTaskRouter({ sessionManager, createTaskSession: createTaskSessionComponents }));
912
965
  // Inspect
913
966
  app.use(createInspectRouter({ getBundle, repoPath: config.repoPath }));
967
+ // Package updates (npm view + cache)
968
+ app.use(createPackageUpdatesRouter({ repoPath: config.repoPath, logger: log }));
914
969
  // Messaging channels
915
970
  if (channelsResult) {
916
971
  app.use('/channels', channelsResult.router);