@aria-cli/tools 1.0.9 → 1.0.11
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.
- package/package.json +9 -5
- package/src/__tests__/web-fetch-download.test.ts +0 -433
- package/src/__tests__/web-tools.test.ts +0 -619
- package/src/ask-user-interaction.ts +0 -33
- package/src/cache/web-cache.ts +0 -110
- package/src/definitions/arion.ts +0 -118
- package/src/definitions/browser/browser.ts +0 -502
- package/src/definitions/browser/index.ts +0 -5
- package/src/definitions/browser/pw-downloads.ts +0 -142
- package/src/definitions/browser/pw-interactions.ts +0 -282
- package/src/definitions/browser/pw-responses.ts +0 -98
- package/src/definitions/browser/pw-session.ts +0 -405
- package/src/definitions/browser/pw-shared.ts +0 -85
- package/src/definitions/browser/pw-snapshot.ts +0 -383
- package/src/definitions/browser/pw-state.ts +0 -101
- package/src/definitions/browser/types.ts +0 -203
- package/src/definitions/code-intelligence.ts +0 -526
- package/src/definitions/core.ts +0 -118
- package/src/definitions/delegation.ts +0 -567
- package/src/definitions/deploy.ts +0 -73
- package/src/definitions/filesystem.ts +0 -217
- package/src/definitions/frg.ts +0 -67
- package/src/definitions/index.ts +0 -28
- package/src/definitions/memory.ts +0 -150
- package/src/definitions/messaging.ts +0 -734
- package/src/definitions/meta.ts +0 -392
- package/src/definitions/network.ts +0 -179
- package/src/definitions/outlook.ts +0 -318
- package/src/definitions/patch/apply-patch.ts +0 -235
- package/src/definitions/patch/fuzzy-match.ts +0 -217
- package/src/definitions/patch/index.ts +0 -1
- package/src/definitions/patch/patch-parser.ts +0 -297
- package/src/definitions/patch/sandbox-paths.ts +0 -129
- package/src/definitions/process/index.ts +0 -5
- package/src/definitions/process/process-registry.ts +0 -303
- package/src/definitions/process/process.ts +0 -456
- package/src/definitions/process/pty-keys.ts +0 -298
- package/src/definitions/process/session-slug.ts +0 -147
- package/src/definitions/quip.ts +0 -225
- package/src/definitions/search.ts +0 -67
- package/src/definitions/session-history.ts +0 -79
- package/src/definitions/shell.ts +0 -202
- package/src/definitions/slack.ts +0 -211
- package/src/definitions/web.ts +0 -119
- package/src/executors/apply-patch.ts +0 -1035
- package/src/executors/arion.ts +0 -199
- package/src/executors/code-intelligence.ts +0 -1179
- package/src/executors/deploy.ts +0 -1066
- package/src/executors/filesystem.ts +0 -1428
- package/src/executors/frg-freshness.ts +0 -743
- package/src/executors/frg.ts +0 -394
- package/src/executors/index.ts +0 -280
- package/src/executors/learning-meta.ts +0 -1367
- package/src/executors/lsp-client.ts +0 -355
- package/src/executors/memory.ts +0 -978
- package/src/executors/meta.ts +0 -293
- package/src/executors/process-registry.ts +0 -570
- package/src/executors/pty-session-store.ts +0 -43
- package/src/executors/pty.ts +0 -342
- package/src/executors/restart.ts +0 -133
- package/src/executors/search-freshness.ts +0 -249
- package/src/executors/search-types.ts +0 -98
- package/src/executors/search.ts +0 -89
- package/src/executors/self-diagnose.ts +0 -552
- package/src/executors/session-history.ts +0 -435
- package/src/executors/shell-safety.ts +0 -519
- package/src/executors/shell.ts +0 -1243
- package/src/executors/utils.ts +0 -40
- package/src/executors/web.ts +0 -786
- package/src/extraction/content-extraction.ts +0 -281
- package/src/extraction/index.ts +0 -5
- package/src/headless-control-contract.ts +0 -1149
- package/src/index.ts +0 -788
- package/src/local-control-http-auth.ts +0 -2
- package/src/mcp/client.ts +0 -218
- package/src/mcp/connection.ts +0 -568
- package/src/mcp/index.ts +0 -11
- package/src/mcp/jsonrpc.ts +0 -195
- package/src/mcp/types.ts +0 -199
- package/src/network-control-adapter.ts +0 -88
- package/src/network-runtime/address-types.ts +0 -218
- package/src/network-runtime/db-owner-fencing.ts +0 -91
- package/src/network-runtime/delivery-receipts.ts +0 -372
- package/src/network-runtime/direct-endpoint-authority.ts +0 -35
- package/src/network-runtime/index.ts +0 -316
- package/src/network-runtime/local-control-contract.ts +0 -784
- package/src/network-runtime/node-store-contract.ts +0 -46
- package/src/network-runtime/pair-route-contract.ts +0 -97
- package/src/network-runtime/peer-capabilities.ts +0 -48
- package/src/network-runtime/peer-principal-ref.ts +0 -20
- package/src/network-runtime/peer-state-machine.ts +0 -160
- package/src/network-runtime/protocol-schemas.ts +0 -265
- package/src/network-runtime/runtime-bootstrap-contract.ts +0 -83
- package/src/outlook/desktop-session.ts +0 -409
- package/src/policy.ts +0 -171
- package/src/providers/brave.ts +0 -80
- package/src/providers/duckduckgo.ts +0 -199
- package/src/providers/exa.ts +0 -85
- package/src/providers/firecrawl.ts +0 -77
- package/src/providers/index.ts +0 -8
- package/src/providers/jina.ts +0 -70
- package/src/providers/router.ts +0 -121
- package/src/providers/search-provider.ts +0 -74
- package/src/providers/tavily.ts +0 -74
- package/src/quip/desktop-session.ts +0 -435
- package/src/registry/index.ts +0 -1
- package/src/registry/registry.ts +0 -905
- package/src/runtime-socket-local-control-client.ts +0 -632
- package/src/security/dns-normalization.ts +0 -34
- package/src/security/dns-pinning.ts +0 -138
- package/src/security/external-content.ts +0 -129
- package/src/security/ssrf.ts +0 -207
- package/src/slack/desktop-session.ts +0 -493
- package/src/tool-factory.ts +0 -91
- package/src/types.ts +0 -1341
- package/src/utils/retry.ts +0 -163
- package/src/utils/safe-parse-json.ts +0 -176
- package/src/utils/url.ts +0 -20
- package/tests/benchmarks/registry.bench.ts +0 -57
- package/tests/cache/web-cache.test.ts +0 -147
- package/tests/critical-integration.test.ts +0 -1465
- package/tests/definitions/apply-patch.test.ts +0 -586
- package/tests/definitions/browser.test.ts +0 -495
- package/tests/definitions/delegation-pause-resume.test.ts +0 -758
- package/tests/definitions/execution.test.ts +0 -671
- package/tests/definitions/messaging-inbox-scope.test.ts +0 -229
- package/tests/definitions/messaging.test.ts +0 -1468
- package/tests/definitions/outlook.test.ts +0 -30
- package/tests/definitions/process.test.ts +0 -469
- package/tests/definitions/slack.test.ts +0 -28
- package/tests/definitions/tool-inventory.test.ts +0 -218
- package/tests/e2e/delegation-quest-orchestration.e2e.test.ts +0 -433
- package/tests/e2e/memory-tool-discovery-contract.e2e.test.ts +0 -81
- package/tests/executors/apply-patch.test.ts +0 -538
- package/tests/executors/arion.test.ts +0 -309
- package/tests/executors/conversation-primitives.test.ts +0 -250
- package/tests/executors/deploy.test.ts +0 -746
- package/tests/executors/filesystem-tools.test.ts +0 -357
- package/tests/executors/filesystem.test.ts +0 -959
- package/tests/executors/frg-freshness.test.ts +0 -136
- package/tests/executors/frg-merge.test.ts +0 -70
- package/tests/executors/frg-session-content.test.ts +0 -40
- package/tests/executors/frg.test.ts +0 -56
- package/tests/executors/memory-bugfixes.test.ts +0 -257
- package/tests/executors/memory-real-memoria.integration.test.ts +0 -316
- package/tests/executors/memory.test.ts +0 -853
- package/tests/executors/meta-tools.test.ts +0 -411
- package/tests/executors/meta.test.ts +0 -683
- package/tests/executors/path-containment.test.ts +0 -51
- package/tests/executors/process-registry.test.ts +0 -505
- package/tests/executors/pty.test.ts +0 -664
- package/tests/executors/quest-security.test.ts +0 -249
- package/tests/executors/read-file-media.test.ts +0 -230
- package/tests/executors/recall-knowledge-schema.test.ts +0 -209
- package/tests/executors/recall-tags.test.ts +0 -278
- package/tests/executors/remember-null-safety.contract.test.ts +0 -41
- package/tests/executors/restart.test.ts +0 -67
- package/tests/executors/search-unified.test.ts +0 -381
- package/tests/executors/session-history.test.ts +0 -340
- package/tests/executors/session-transcript.test.ts +0 -561
- package/tests/executors/shell-abort.test.ts +0 -416
- package/tests/executors/shell-env-blocklist.test.ts +0 -648
- package/tests/executors/shell-env-process.test.ts +0 -245
- package/tests/executors/shell-process-registry.test.ts +0 -334
- package/tests/executors/shell-tools.test.ts +0 -393
- package/tests/executors/shell.test.ts +0 -690
- package/tests/executors/web-abort-vs-timeout.test.ts +0 -213
- package/tests/executors/web-integration.test.ts +0 -633
- package/tests/executors/web-symlink.test.ts +0 -18
- package/tests/executors/web.test.ts +0 -1400
- package/tests/executors/write-stdin.test.ts +0 -145
- package/tests/extraction/content-extraction.test.ts +0 -153
- package/tests/guards/tools-default-test-lane.integration.test.ts +0 -21
- package/tests/guards/tools-package-test-commands.e2e.test.ts +0 -43
- package/tests/guards/tools-test-lane-manifest.contract.test.ts +0 -76
- package/tests/guards/tools-vitest-workspace-alias.contract.test.ts +0 -63
- package/tests/helpers/async-waits.ts +0 -53
- package/tests/integration/headless-control-contract.integration.test.ts +0 -153
- package/tests/integration/memory-tool-schema-parity.integration.test.ts +0 -67
- package/tests/integration/meta-tools-round-trip.integration.test.ts +0 -506
- package/tests/integration/quest-round-trip.test.ts +0 -303
- package/tests/integration/registry-executor-flow.test.ts +0 -85
- package/tests/integration.test.ts +0 -177
- package/tests/loading-tier.test.ts +0 -126
- package/tests/mcp/client-reconnect.test.ts +0 -267
- package/tests/mcp/connection.test.ts +0 -846
- package/tests/mcp/injectable-logger.test.ts +0 -83
- package/tests/mcp/jsonrpc.test.ts +0 -109
- package/tests/mcp/lifecycle.test.ts +0 -879
- package/tests/network-runtime/address-types.contract.test.ts +0 -143
- package/tests/network-runtime/continuity-bind-schema.contract.test.ts +0 -203
- package/tests/network-runtime/local-control-contract.test.ts +0 -869
- package/tests/network-runtime/local-control-invite-token.contract.test.ts +0 -146
- package/tests/network-runtime/node-store-contract.test.ts +0 -11
- package/tests/network-runtime/pair-protocol-nodeid.contract.test.ts +0 -15
- package/tests/network-runtime/peer-state-machine.contract.test.ts +0 -148
- package/tests/network-runtime/protocol-schemas.contract.test.ts +0 -512
- package/tests/network-runtime/relay-pending-nodeid.contract.test.ts +0 -62
- package/tests/network-runtime/runtime-bootstrap-contract.test.ts +0 -227
- package/tests/network-runtime/runtime-socket-local-control-client.test.ts +0 -621
- package/tests/network-runtime/wait-for-message-script.test.ts +0 -288
- package/tests/parallel.test.ts +0 -71
- package/tests/policy.test.ts +0 -184
- package/tests/print-default-test-lane.ts +0 -14
- package/tests/print-test-lane-manifest.ts +0 -22
- package/tests/providers/brave.test.ts +0 -159
- package/tests/providers/duckduckgo.test.ts +0 -207
- package/tests/providers/exa.test.ts +0 -175
- package/tests/providers/firecrawl.test.ts +0 -168
- package/tests/providers/jina.test.ts +0 -144
- package/tests/providers/router.test.ts +0 -328
- package/tests/providers/tavily.test.ts +0 -165
- package/tests/registry/discovery.test.ts +0 -154
- package/tests/registry/injectable-logger.test.ts +0 -230
- package/tests/registry/input-validation.test.ts +0 -361
- package/tests/registry/interface-completeness.test.ts +0 -85
- package/tests/registry/mcp-integration.test.ts +0 -103
- package/tests/registry/mcp-read-only-hint.test.ts +0 -60
- package/tests/registry/memoria-discovery.test.ts +0 -390
- package/tests/registry/nested-validation.test.ts +0 -283
- package/tests/registry/pseudo-tool-filtering.test.ts +0 -258
- package/tests/registry/registration-lifecycle.test.ts +0 -133
- package/tests/registry-validation.test.ts +0 -424
- package/tests/registry.test.ts +0 -460
- package/tests/security/dns-pinning.test.ts +0 -162
- package/tests/security/external-content.test.ts +0 -144
- package/tests/security/ssrf.test.ts +0 -118
- package/tests/shell-safety-integration.test.ts +0 -32
- package/tests/shell-safety.test.ts +0 -365
- package/tests/slack/desktop-session.test.ts +0 -50
- package/tests/test-lane-manifest.ts +0 -440
- package/tests/test-utils.ts +0 -27
- package/tests/tool-factory.test.ts +0 -188
- package/tests/utils/retry.test.ts +0 -231
- package/tests/utils/url.test.ts +0 -63
- package/tsconfig.cjs.json +0 -24
- package/tsconfig.json +0 -12
- package/vitest.config.ts +0 -55
- package/vitest.e2e.config.ts +0 -24
- package/vitest.integration.config.ts +0 -24
- package/vitest.native.config.ts +0 -24
|
@@ -1,390 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
2
|
-
import { ToolRegistry } from "../../src/registry/registry.js";
|
|
3
|
-
|
|
4
|
-
describe("Tool Registry - Memoria Discovery", () => {
|
|
5
|
-
let registry: ToolRegistry;
|
|
6
|
-
let mockMemoria: any;
|
|
7
|
-
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
registry = new ToolRegistry();
|
|
10
|
-
mockMemoria = {
|
|
11
|
-
recallTools: vi.fn(),
|
|
12
|
-
rememberTool: vi.fn(),
|
|
13
|
-
rememberSkill: vi.fn().mockResolvedValue("skill-id"),
|
|
14
|
-
recallSkills: vi.fn().mockResolvedValue([]),
|
|
15
|
-
getSkill: vi.fn().mockResolvedValue(null),
|
|
16
|
-
recordSkillExecution: vi
|
|
17
|
-
.fn()
|
|
18
|
-
.mockResolvedValue({ id: "exec-1", skillId: "s1", success: true, timestamp: new Date() }),
|
|
19
|
-
};
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it("should discover tools from Memoria", async () => {
|
|
23
|
-
mockMemoria.recallTools.mockResolvedValue([
|
|
24
|
-
{
|
|
25
|
-
id: "tool-1",
|
|
26
|
-
name: "memoria_tool",
|
|
27
|
-
description: "A tool from Memoria",
|
|
28
|
-
category: "filesystem",
|
|
29
|
-
parameters: { type: "object", properties: { input: { type: "string" } } },
|
|
30
|
-
riskLevel: "safe",
|
|
31
|
-
responseTemplate: "result: {{input}}",
|
|
32
|
-
},
|
|
33
|
-
]);
|
|
34
|
-
|
|
35
|
-
await registry.discoverFromMemoria(mockMemoria);
|
|
36
|
-
|
|
37
|
-
expect(mockMemoria.recallTools).toHaveBeenCalledWith({
|
|
38
|
-
query: "",
|
|
39
|
-
limit: 200,
|
|
40
|
-
offset: 0,
|
|
41
|
-
matchAll: true,
|
|
42
|
-
updateAccessStats: false,
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
expect(registry.has("memoria_tool")).toBe(true);
|
|
46
|
-
const tool = registry.get("memoria_tool");
|
|
47
|
-
expect(tool?.description).toBe("A tool from Memoria");
|
|
48
|
-
expect(tool?.loadingTier).toBe("deferred");
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it("should map Memoria failures into discovered tool issues", async () => {
|
|
52
|
-
mockMemoria.recallTools.mockResolvedValue([
|
|
53
|
-
{
|
|
54
|
-
id: "tool-risk",
|
|
55
|
-
name: "git_force_push",
|
|
56
|
-
description: "Force push helper",
|
|
57
|
-
category: "shell",
|
|
58
|
-
parameters: { type: "object", properties: { branch: { type: "string" } } },
|
|
59
|
-
riskLevel: "dangerous",
|
|
60
|
-
failures: [
|
|
61
|
-
{ error: "force-push can overwrite remote history" },
|
|
62
|
-
{ error: "no branch protection check" },
|
|
63
|
-
{ error: "force-push can overwrite remote history" },
|
|
64
|
-
],
|
|
65
|
-
responseTemplate: "force push {{branch}}",
|
|
66
|
-
},
|
|
67
|
-
]);
|
|
68
|
-
|
|
69
|
-
await registry.discoverFromMemoria(mockMemoria);
|
|
70
|
-
|
|
71
|
-
const tool = registry.get("git_force_push");
|
|
72
|
-
expect(tool).toBeDefined();
|
|
73
|
-
expect(tool?.issues).toEqual([
|
|
74
|
-
"force-push can overwrite remote history",
|
|
75
|
-
"no branch protection check",
|
|
76
|
-
]);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it("should handle empty Memoria results gracefully", async () => {
|
|
80
|
-
mockMemoria.recallTools.mockResolvedValue([]);
|
|
81
|
-
|
|
82
|
-
await registry.discoverFromMemoria(mockMemoria);
|
|
83
|
-
|
|
84
|
-
expect(registry.size).toBe(0);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it("should page through matchAll recall so discovery includes all tools", async () => {
|
|
88
|
-
const total = 250;
|
|
89
|
-
mockMemoria.recallTools.mockImplementation(
|
|
90
|
-
async (opts: { limit?: number; offset?: number }) => {
|
|
91
|
-
const limit = opts.limit ?? 200;
|
|
92
|
-
const offset = opts.offset ?? 0;
|
|
93
|
-
if (offset >= total) return [];
|
|
94
|
-
return Array.from({ length: Math.min(limit, total - offset) }, (_, i) => {
|
|
95
|
-
const index = offset + i + 1;
|
|
96
|
-
return {
|
|
97
|
-
id: `tool-${index}`,
|
|
98
|
-
name: `bulk_tool_${index}`,
|
|
99
|
-
description: `Bulk tool ${index}`,
|
|
100
|
-
category: "meta",
|
|
101
|
-
parameters: { type: "object", properties: {} },
|
|
102
|
-
riskLevel: "safe",
|
|
103
|
-
responseTemplate: `ok-${index}`,
|
|
104
|
-
};
|
|
105
|
-
});
|
|
106
|
-
},
|
|
107
|
-
);
|
|
108
|
-
|
|
109
|
-
const count = await registry.discoverFromMemoria(mockMemoria);
|
|
110
|
-
|
|
111
|
-
expect(count).toBe(total);
|
|
112
|
-
expect(mockMemoria.recallTools).toHaveBeenNthCalledWith(1, {
|
|
113
|
-
query: "",
|
|
114
|
-
limit: 200,
|
|
115
|
-
offset: 0,
|
|
116
|
-
matchAll: true,
|
|
117
|
-
updateAccessStats: false,
|
|
118
|
-
});
|
|
119
|
-
expect(mockMemoria.recallTools).toHaveBeenNthCalledWith(2, {
|
|
120
|
-
query: "",
|
|
121
|
-
limit: 200,
|
|
122
|
-
offset: 200,
|
|
123
|
-
matchAll: true,
|
|
124
|
-
updateAccessStats: false,
|
|
125
|
-
});
|
|
126
|
-
expect(registry.has("bulk_tool_250")).toBe(true);
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
it("should skip invalid or knowledge-only recalled tools and keep executable template tools", async () => {
|
|
130
|
-
mockMemoria.recallTools.mockResolvedValue([
|
|
131
|
-
{
|
|
132
|
-
id: "tool-1",
|
|
133
|
-
name: "first_tool",
|
|
134
|
-
description: "First tool",
|
|
135
|
-
},
|
|
136
|
-
{
|
|
137
|
-
id: "tool-2",
|
|
138
|
-
name: "second_tool",
|
|
139
|
-
description: "Valid tool",
|
|
140
|
-
category: "meta",
|
|
141
|
-
parameters: { type: "object", properties: {} },
|
|
142
|
-
riskLevel: "safe",
|
|
143
|
-
responseTemplate: "second tool: {{input}}",
|
|
144
|
-
},
|
|
145
|
-
{
|
|
146
|
-
id: "tool-3",
|
|
147
|
-
name: "third_tool",
|
|
148
|
-
description: "Knowledge-only tool",
|
|
149
|
-
category: "meta",
|
|
150
|
-
parameters: { type: "object", properties: {} },
|
|
151
|
-
riskLevel: "safe",
|
|
152
|
-
knowledge: "third tool knowledge",
|
|
153
|
-
},
|
|
154
|
-
]);
|
|
155
|
-
|
|
156
|
-
await registry.discoverFromMemoria(mockMemoria);
|
|
157
|
-
|
|
158
|
-
expect(registry.has("first_tool")).toBe(false);
|
|
159
|
-
expect(registry.has("second_tool")).toBe(true);
|
|
160
|
-
expect(registry.has("third_tool")).toBe(false);
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
it("should default missing parameters for template-backed tools", async () => {
|
|
164
|
-
mockMemoria.recallTools.mockResolvedValue([
|
|
165
|
-
{
|
|
166
|
-
id: "tool-missing-params",
|
|
167
|
-
name: "templated_without_params",
|
|
168
|
-
description: "Template tool missing explicit schema",
|
|
169
|
-
category: "meta",
|
|
170
|
-
riskLevel: "safe",
|
|
171
|
-
responseTemplate: "Template result: {{input}}",
|
|
172
|
-
},
|
|
173
|
-
]);
|
|
174
|
-
|
|
175
|
-
const count = await registry.discoverFromMemoria(mockMemoria);
|
|
176
|
-
|
|
177
|
-
expect(count).toBe(1);
|
|
178
|
-
const tool = registry.get("templated_without_params");
|
|
179
|
-
expect(tool).toBeDefined();
|
|
180
|
-
expect(tool?.parameters).toEqual({
|
|
181
|
-
type: "object",
|
|
182
|
-
properties: {},
|
|
183
|
-
additionalProperties: true,
|
|
184
|
-
});
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
it("should persist tool to Memoria", async () => {
|
|
188
|
-
const mockTool = {
|
|
189
|
-
name: "persist_test",
|
|
190
|
-
description: "Tool to persist",
|
|
191
|
-
category: "meta" as const,
|
|
192
|
-
parameters: { arg: { type: "string" } },
|
|
193
|
-
riskLevel: "safe" as const,
|
|
194
|
-
execute: async () => ({ success: true, message: "ok" }),
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
// Register the tool first
|
|
198
|
-
registry.register(mockTool);
|
|
199
|
-
|
|
200
|
-
mockMemoria.rememberTool.mockResolvedValue("tool-123");
|
|
201
|
-
|
|
202
|
-
await registry.saveToMemoria("persist_test", mockMemoria);
|
|
203
|
-
|
|
204
|
-
expect(mockMemoria.rememberTool).toHaveBeenCalledWith({
|
|
205
|
-
name: "persist_test",
|
|
206
|
-
description: "Tool to persist",
|
|
207
|
-
category: "meta",
|
|
208
|
-
parameters: { arg: { type: "string" } },
|
|
209
|
-
riskLevel: "safe",
|
|
210
|
-
});
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
it("persists explicit non-shell responseTemplate fields", async () => {
|
|
214
|
-
const mockTool = {
|
|
215
|
-
name: "persist_with_template",
|
|
216
|
-
description: "Templated tool",
|
|
217
|
-
category: "meta" as const,
|
|
218
|
-
parameters: { type: "object", properties: { name: { type: "string" } } },
|
|
219
|
-
riskLevel: "safe" as const,
|
|
220
|
-
responseTemplate: "hello {{name}}",
|
|
221
|
-
execute: async () => ({ success: true, message: "ok" }),
|
|
222
|
-
};
|
|
223
|
-
|
|
224
|
-
registry.register(mockTool);
|
|
225
|
-
mockMemoria.rememberTool.mockResolvedValue("tool-template");
|
|
226
|
-
|
|
227
|
-
await registry.saveToMemoria("persist_with_template", mockMemoria);
|
|
228
|
-
|
|
229
|
-
expect(mockMemoria.rememberTool).toHaveBeenCalledWith(
|
|
230
|
-
expect.objectContaining({
|
|
231
|
-
name: "persist_with_template",
|
|
232
|
-
responseTemplate: "hello {{name}}",
|
|
233
|
-
}),
|
|
234
|
-
);
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
it("persists shell responseTemplate fields for executable discovery", async () => {
|
|
238
|
-
const mockTool = {
|
|
239
|
-
name: "persist_shell_template",
|
|
240
|
-
description: "Shell template should not be persisted",
|
|
241
|
-
category: "shell" as const,
|
|
242
|
-
parameters: { type: "object", properties: {} },
|
|
243
|
-
riskLevel: "dangerous" as const,
|
|
244
|
-
responseTemplate: "bash:echo hi",
|
|
245
|
-
execute: async () => ({ success: true, message: "ok" }),
|
|
246
|
-
};
|
|
247
|
-
|
|
248
|
-
registry.register(mockTool);
|
|
249
|
-
mockMemoria.rememberTool.mockResolvedValue("tool-shell-template");
|
|
250
|
-
|
|
251
|
-
await registry.saveToMemoria("persist_shell_template", mockMemoria);
|
|
252
|
-
|
|
253
|
-
expect(mockMemoria.rememberTool).toHaveBeenCalledWith(
|
|
254
|
-
expect.objectContaining({
|
|
255
|
-
name: "persist_shell_template",
|
|
256
|
-
responseTemplate: "bash:echo hi",
|
|
257
|
-
}),
|
|
258
|
-
);
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
it("persists discovered tool issues as failure records", async () => {
|
|
262
|
-
const mockTool = {
|
|
263
|
-
name: "persist_risky",
|
|
264
|
-
description: "Risky persisted tool",
|
|
265
|
-
category: "shell" as const,
|
|
266
|
-
parameters: { type: "object", properties: {} },
|
|
267
|
-
riskLevel: "dangerous" as const,
|
|
268
|
-
issues: ["force push may overwrite history", "no branch protection check"],
|
|
269
|
-
execute: async () => ({ success: true, message: "ok" }),
|
|
270
|
-
};
|
|
271
|
-
|
|
272
|
-
registry.register(mockTool);
|
|
273
|
-
mockMemoria.rememberTool.mockResolvedValue("tool-risky");
|
|
274
|
-
|
|
275
|
-
await registry.saveToMemoria("persist_risky", mockMemoria);
|
|
276
|
-
|
|
277
|
-
expect(mockMemoria.rememberTool).toHaveBeenCalledWith(
|
|
278
|
-
expect.objectContaining({
|
|
279
|
-
name: "persist_risky",
|
|
280
|
-
riskLevel: "dangerous",
|
|
281
|
-
failures: expect.arrayContaining([
|
|
282
|
-
expect.objectContaining({ error: "force push may overwrite history" }),
|
|
283
|
-
expect.objectContaining({ error: "no branch protection check" }),
|
|
284
|
-
]),
|
|
285
|
-
}),
|
|
286
|
-
);
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
it("uses structured fields, ignores legacy content field", async () => {
|
|
290
|
-
mockMemoria.recallTools.mockResolvedValue([
|
|
291
|
-
{
|
|
292
|
-
id: "tool-legacy",
|
|
293
|
-
name: "legacy_tool",
|
|
294
|
-
description: "From structured field", // NEW format
|
|
295
|
-
content: JSON.stringify({ description: "From content field" }), // OLD format
|
|
296
|
-
category: "meta",
|
|
297
|
-
riskLevel: "safe",
|
|
298
|
-
parameters: { type: "object", properties: {} },
|
|
299
|
-
responseTemplate: "legacy: {{input}}",
|
|
300
|
-
},
|
|
301
|
-
]);
|
|
302
|
-
|
|
303
|
-
await registry.discoverFromMemoria(mockMemoria);
|
|
304
|
-
|
|
305
|
-
expect(registry.has("legacy_tool")).toBe(true);
|
|
306
|
-
const tool = registry.get("legacy_tool");
|
|
307
|
-
expect(tool?.description).toBe("From structured field"); // Uses description, not parsed content
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
it("discovers Memoria shell templates and upgrades them to dangerous risk", async () => {
|
|
311
|
-
mockMemoria.recallTools.mockResolvedValue([
|
|
312
|
-
{
|
|
313
|
-
id: "tool-bash-safe",
|
|
314
|
-
name: "safe_bash_echo",
|
|
315
|
-
description: "Echoes the provided value safely",
|
|
316
|
-
category: "shell",
|
|
317
|
-
parameters: {
|
|
318
|
-
type: "object",
|
|
319
|
-
properties: { value: { type: "string" } },
|
|
320
|
-
required: ["value"],
|
|
321
|
-
},
|
|
322
|
-
riskLevel: "moderate",
|
|
323
|
-
responseTemplate: "bash:printf '%s' {{value}}",
|
|
324
|
-
},
|
|
325
|
-
{
|
|
326
|
-
id: "tool-text-ok",
|
|
327
|
-
name: "safe_text_tool",
|
|
328
|
-
description: "Normal template should load",
|
|
329
|
-
category: "meta",
|
|
330
|
-
parameters: { type: "object", properties: {} },
|
|
331
|
-
riskLevel: "safe",
|
|
332
|
-
responseTemplate: "ok",
|
|
333
|
-
},
|
|
334
|
-
]);
|
|
335
|
-
|
|
336
|
-
const count = await registry.discoverFromMemoria(mockMemoria);
|
|
337
|
-
|
|
338
|
-
expect(count).toBe(2);
|
|
339
|
-
expect(registry.has("safe_bash_echo")).toBe(true);
|
|
340
|
-
expect(registry.has("safe_text_tool")).toBe(true);
|
|
341
|
-
expect(registry.get("safe_bash_echo")?.riskLevel).toBe("dangerous");
|
|
342
|
-
});
|
|
343
|
-
|
|
344
|
-
it("validates full schema constraints for discovered template tools", async () => {
|
|
345
|
-
mockMemoria.recallTools.mockResolvedValue([
|
|
346
|
-
{
|
|
347
|
-
id: "tool-schema",
|
|
348
|
-
name: "strict_schema_tool",
|
|
349
|
-
description: "Validates strict schema",
|
|
350
|
-
category: "meta",
|
|
351
|
-
parameters: {
|
|
352
|
-
type: "object",
|
|
353
|
-
properties: {
|
|
354
|
-
slug: { type: "string", pattern: "^[a-z-]+$" },
|
|
355
|
-
limit: { type: "number", minimum: 1, maximum: 3 },
|
|
356
|
-
},
|
|
357
|
-
required: ["slug"],
|
|
358
|
-
additionalProperties: false,
|
|
359
|
-
},
|
|
360
|
-
riskLevel: "safe",
|
|
361
|
-
responseTemplate: "slug={{slug}} limit={{limit}}",
|
|
362
|
-
},
|
|
363
|
-
]);
|
|
364
|
-
|
|
365
|
-
await registry.discoverFromMemoria(mockMemoria);
|
|
366
|
-
|
|
367
|
-
const tool = registry.get("strict_schema_tool");
|
|
368
|
-
expect(tool).toBeDefined();
|
|
369
|
-
|
|
370
|
-
const invalidInput = await tool!.execute(
|
|
371
|
-
{ slug: "Hello World", limit: 0, extra: true },
|
|
372
|
-
{
|
|
373
|
-
workingDir: process.cwd(),
|
|
374
|
-
env: process.env as Record<string, string>,
|
|
375
|
-
},
|
|
376
|
-
);
|
|
377
|
-
expect(invalidInput.success).toBe(false);
|
|
378
|
-
expect(String(invalidInput.message ?? "")).toContain("Invalid input");
|
|
379
|
-
|
|
380
|
-
const validInput = await tool!.execute(
|
|
381
|
-
{ slug: "release-notes", limit: 2 },
|
|
382
|
-
{
|
|
383
|
-
workingDir: process.cwd(),
|
|
384
|
-
env: process.env as Record<string, string>,
|
|
385
|
-
},
|
|
386
|
-
);
|
|
387
|
-
expect(validInput.success).toBe(true);
|
|
388
|
-
expect(String(validInput.data ?? "")).toContain("release-notes");
|
|
389
|
-
});
|
|
390
|
-
});
|
|
@@ -1,283 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @aria/tools - Nested object validation tests for validateToolInput
|
|
3
|
-
*
|
|
4
|
-
* Verifies that validateToolInput recursively validates nested object schemas,
|
|
5
|
-
* catching missing required fields and type mismatches at arbitrary depth.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { describe, it, expect } from "vitest";
|
|
9
|
-
import { validateToolInput } from "../../src/registry/index.js";
|
|
10
|
-
|
|
11
|
-
// ---------------------------------------------------------------------------
|
|
12
|
-
// Single-level nesting
|
|
13
|
-
// ---------------------------------------------------------------------------
|
|
14
|
-
|
|
15
|
-
describe("validateToolInput - nested objects", () => {
|
|
16
|
-
const schema = {
|
|
17
|
-
type: "object",
|
|
18
|
-
properties: {
|
|
19
|
-
config: {
|
|
20
|
-
type: "object",
|
|
21
|
-
required: ["host"],
|
|
22
|
-
properties: {
|
|
23
|
-
host: { type: "string" },
|
|
24
|
-
port: { type: "number" },
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
required: ["config"],
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
it("accepts valid nested object", () => {
|
|
32
|
-
expect(validateToolInput({ config: { host: "localhost", port: 8080 } }, schema)).toBeNull();
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it("accepts nested object with only required fields", () => {
|
|
36
|
-
expect(validateToolInput({ config: { host: "localhost" } }, schema)).toBeNull();
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it("rejects missing required nested property", () => {
|
|
40
|
-
const err = validateToolInput({ config: { port: 8080 } }, schema);
|
|
41
|
-
expect(err).not.toBeNull();
|
|
42
|
-
expect(err).toMatch(/Missing required properties.*host/);
|
|
43
|
-
// Error should include the path to the nested property
|
|
44
|
-
expect(err).toMatch(/config/);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it("rejects wrong type in nested property", () => {
|
|
48
|
-
const err = validateToolInput({ config: { host: 123 } }, schema);
|
|
49
|
-
expect(err).not.toBeNull();
|
|
50
|
-
expect(err).toMatch(/Property "config\.host" expected type string; got number/);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it("rejects non-object value for nested object schema", () => {
|
|
54
|
-
const err = validateToolInput({ config: "not-an-object" }, schema);
|
|
55
|
-
expect(err).not.toBeNull();
|
|
56
|
-
expect(err).toMatch(/Property "config" expected type object; got string/);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it("rejects null value for nested object with required fields", () => {
|
|
60
|
-
const err = validateToolInput({ config: null }, schema);
|
|
61
|
-
expect(err).not.toBeNull();
|
|
62
|
-
expect(err).toMatch(/Property "config" expected type object; got null/);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it("rejects array value for nested object schema", () => {
|
|
66
|
-
const err = validateToolInput({ config: [1, 2] }, schema);
|
|
67
|
-
expect(err).not.toBeNull();
|
|
68
|
-
expect(err).toMatch(/Property "config" expected type object; got array/);
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
// ---------------------------------------------------------------------------
|
|
73
|
-
// Multi-level nesting (two levels deep)
|
|
74
|
-
// ---------------------------------------------------------------------------
|
|
75
|
-
|
|
76
|
-
describe("validateToolInput - deeply nested objects", () => {
|
|
77
|
-
const schema = {
|
|
78
|
-
type: "object",
|
|
79
|
-
properties: {
|
|
80
|
-
outer: {
|
|
81
|
-
type: "object",
|
|
82
|
-
required: ["inner"],
|
|
83
|
-
properties: {
|
|
84
|
-
inner: {
|
|
85
|
-
type: "object",
|
|
86
|
-
required: ["x"],
|
|
87
|
-
properties: {
|
|
88
|
-
x: { type: "string" },
|
|
89
|
-
y: { type: "number" },
|
|
90
|
-
},
|
|
91
|
-
},
|
|
92
|
-
},
|
|
93
|
-
},
|
|
94
|
-
},
|
|
95
|
-
required: ["outer"],
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
it("accepts valid deeply nested object", () => {
|
|
99
|
-
expect(validateToolInput({ outer: { inner: { x: "hello", y: 42 } } }, schema)).toBeNull();
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it("accepts deeply nested object with only required fields", () => {
|
|
103
|
-
expect(validateToolInput({ outer: { inner: { x: "hello" } } }, schema)).toBeNull();
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it("rejects missing required property at depth 2", () => {
|
|
107
|
-
const err = validateToolInput({ outer: { inner: {} } }, schema);
|
|
108
|
-
expect(err).not.toBeNull();
|
|
109
|
-
expect(err).toMatch(/Missing required properties.*x/);
|
|
110
|
-
// Path should indicate full nesting
|
|
111
|
-
expect(err).toMatch(/outer\.inner/);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it("rejects missing required property at depth 1", () => {
|
|
115
|
-
const err = validateToolInput({ outer: {} }, schema);
|
|
116
|
-
expect(err).not.toBeNull();
|
|
117
|
-
expect(err).toMatch(/Missing required properties.*inner/);
|
|
118
|
-
expect(err).toMatch(/outer/);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it("rejects wrong type at depth 2", () => {
|
|
122
|
-
const err = validateToolInput({ outer: { inner: { x: 999 } } }, schema);
|
|
123
|
-
expect(err).not.toBeNull();
|
|
124
|
-
expect(err).toMatch(/Property "outer\.inner\.x" expected type string; got number/);
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
it("rejects non-object at depth 1", () => {
|
|
128
|
-
const err = validateToolInput({ outer: { inner: "flat" } }, schema);
|
|
129
|
-
expect(err).not.toBeNull();
|
|
130
|
-
expect(err).toMatch(/Property "outer\.inner" expected type object; got string/);
|
|
131
|
-
});
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
// ---------------------------------------------------------------------------
|
|
135
|
-
// Nested with additionalProperties: false
|
|
136
|
-
// ---------------------------------------------------------------------------
|
|
137
|
-
|
|
138
|
-
describe("validateToolInput - nested additionalProperties", () => {
|
|
139
|
-
const schema = {
|
|
140
|
-
type: "object",
|
|
141
|
-
properties: {
|
|
142
|
-
settings: {
|
|
143
|
-
type: "object",
|
|
144
|
-
properties: {
|
|
145
|
-
verbose: { type: "boolean" },
|
|
146
|
-
},
|
|
147
|
-
additionalProperties: false,
|
|
148
|
-
},
|
|
149
|
-
},
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
it("accepts valid nested object without extra properties", () => {
|
|
153
|
-
expect(validateToolInput({ settings: { verbose: true } }, schema)).toBeNull();
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
it("rejects unknown properties in nested object", () => {
|
|
157
|
-
const err = validateToolInput({ settings: { verbose: true, debug: true } }, schema);
|
|
158
|
-
expect(err).not.toBeNull();
|
|
159
|
-
expect(err).toMatch(/Unknown properties.*debug/);
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
// ---------------------------------------------------------------------------
|
|
164
|
-
// Optional nested objects (no required on the outer)
|
|
165
|
-
// ---------------------------------------------------------------------------
|
|
166
|
-
|
|
167
|
-
describe("validateToolInput - optional nested objects", () => {
|
|
168
|
-
const schema = {
|
|
169
|
-
type: "object",
|
|
170
|
-
properties: {
|
|
171
|
-
metadata: {
|
|
172
|
-
type: "object",
|
|
173
|
-
required: ["version"],
|
|
174
|
-
properties: {
|
|
175
|
-
version: { type: "string" },
|
|
176
|
-
},
|
|
177
|
-
},
|
|
178
|
-
},
|
|
179
|
-
// metadata is NOT required at the top level
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
it("accepts when optional nested object is absent", () => {
|
|
183
|
-
expect(validateToolInput({}, schema)).toBeNull();
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
it("validates nested object when present", () => {
|
|
187
|
-
const err = validateToolInput({ metadata: {} }, schema);
|
|
188
|
-
expect(err).not.toBeNull();
|
|
189
|
-
expect(err).toMatch(/Missing required properties.*version/);
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
it("accepts valid optional nested object", () => {
|
|
193
|
-
expect(validateToolInput({ metadata: { version: "1.0" } }, schema)).toBeNull();
|
|
194
|
-
});
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
// ---------------------------------------------------------------------------
|
|
198
|
-
// Nested object with no properties/required (just type: "object")
|
|
199
|
-
// ---------------------------------------------------------------------------
|
|
200
|
-
|
|
201
|
-
describe("validateToolInput - nested object without constraints", () => {
|
|
202
|
-
const schema = {
|
|
203
|
-
type: "object",
|
|
204
|
-
properties: {
|
|
205
|
-
data: { type: "object" },
|
|
206
|
-
},
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
it("accepts any object when nested schema has no properties/required", () => {
|
|
210
|
-
expect(validateToolInput({ data: { anything: "goes" } }, schema)).toBeNull();
|
|
211
|
-
expect(validateToolInput({ data: {} }, schema)).toBeNull();
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
it("still rejects non-object types", () => {
|
|
215
|
-
const err = validateToolInput({ data: "string" }, schema);
|
|
216
|
-
expect(err).not.toBeNull();
|
|
217
|
-
expect(err).toMatch(/Property "data" expected type object; got string/);
|
|
218
|
-
});
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
// ---------------------------------------------------------------------------
|
|
222
|
-
// Realistic shell/filesystem tool schema (the motivating use case)
|
|
223
|
-
// ---------------------------------------------------------------------------
|
|
224
|
-
|
|
225
|
-
describe("validateToolInput - realistic nested schema", () => {
|
|
226
|
-
const shellToolSchema = {
|
|
227
|
-
type: "object",
|
|
228
|
-
properties: {
|
|
229
|
-
command: { type: "string" },
|
|
230
|
-
options: {
|
|
231
|
-
type: "object",
|
|
232
|
-
required: ["cwd"],
|
|
233
|
-
properties: {
|
|
234
|
-
cwd: { type: "string" },
|
|
235
|
-
env: {
|
|
236
|
-
type: "object",
|
|
237
|
-
properties: {
|
|
238
|
-
PATH: { type: "string" },
|
|
239
|
-
HOME: { type: "string" },
|
|
240
|
-
},
|
|
241
|
-
},
|
|
242
|
-
timeout: { type: "number" },
|
|
243
|
-
},
|
|
244
|
-
},
|
|
245
|
-
},
|
|
246
|
-
required: ["command", "options"],
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
it("accepts fully valid shell tool input", () => {
|
|
250
|
-
const input = {
|
|
251
|
-
command: "ls -la",
|
|
252
|
-
options: {
|
|
253
|
-
cwd: "/home/user",
|
|
254
|
-
env: { PATH: "/usr/bin", HOME: "/home/user" },
|
|
255
|
-
timeout: 5000,
|
|
256
|
-
},
|
|
257
|
-
};
|
|
258
|
-
expect(validateToolInput(input, shellToolSchema)).toBeNull();
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
it("rejects missing nested required cwd", () => {
|
|
262
|
-
const input = {
|
|
263
|
-
command: "ls",
|
|
264
|
-
options: { timeout: 3000 },
|
|
265
|
-
};
|
|
266
|
-
const err = validateToolInput(input, shellToolSchema);
|
|
267
|
-
expect(err).not.toBeNull();
|
|
268
|
-
expect(err).toMatch(/Missing required properties.*cwd/);
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
it("rejects wrong type in deeply nested env property", () => {
|
|
272
|
-
const input = {
|
|
273
|
-
command: "ls",
|
|
274
|
-
options: {
|
|
275
|
-
cwd: "/tmp",
|
|
276
|
-
env: { PATH: 123 },
|
|
277
|
-
},
|
|
278
|
-
};
|
|
279
|
-
const err = validateToolInput(input, shellToolSchema);
|
|
280
|
-
expect(err).not.toBeNull();
|
|
281
|
-
expect(err).toMatch(/Property "options\.env\.PATH" expected type string; got number/);
|
|
282
|
-
});
|
|
283
|
-
});
|