@mycadet/assistant 0.12.8 → 0.12.9
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/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/package.json +1 -1
- package/node_modules/@mycadet/ces-client/package.json +1 -1
- package/node_modules/@mycadet/ces-client/src/__tests__/ces-client.test.ts +14 -14
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/package.json +1 -1
- package/node_modules/@mycadet/service-contracts/package.json +1 -1
- package/openapi.yaml +2 -18
- package/package.json +2 -2
- package/src/__tests__/cloud-profile-guard.test.ts +21 -19
- package/src/__tests__/config-set-route.test.ts +12 -8
- package/src/__tests__/credential-health-service.test.ts +4 -2
- package/src/__tests__/get-skill-detail-audit.test.ts +134 -289
- package/src/__tests__/mcp-abort-signal.test.ts +0 -1
- package/src/__tests__/mcp-client-auth.test.ts +0 -1
- package/src/__tests__/mcp-config-secret-boundary.test.ts +44 -40
- package/src/__tests__/mcp-tool-annotations-risk.test.ts +0 -1
- package/src/__tests__/plugin-api-store-credential.test.ts +4 -1
- package/src/__tests__/skills-file-content-endpoint.test.ts +221 -535
- package/src/__tests__/skills-files-catalog-fallback.test.ts +258 -576
- package/src/api/events/show-cloud-login.ts +2 -2
- package/src/cli/commands/cloud/__tests__/callback-routes-list.test.ts +2 -2
- package/src/cli/commands/cloud/index.ts +1 -1
- package/src/cli/commands/config.ts +1 -1
- package/src/config/__tests__/substrate-twin-shadowing.test.ts +36 -26
- package/src/config/env-registry.ts +2 -9
- package/src/config/schemas/ingress.ts +30 -0
- package/src/config/schemas/llm.ts +0 -9
- package/src/config/seed-inference-profiles.ts +2 -2
- package/src/config/substrate-twin-shadowing.ts +12 -17
- package/src/config/sync-gated-profiles.ts +3 -4
- package/src/credential-execution/prompted-credential.ts +1 -10
- package/src/credential-health/credential-health-service.ts +8 -4
- package/src/daemon/credential-transcript-scrub.ts +11 -0
- package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +5 -0
- package/src/providers/inference/backfill.ts +2 -2
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +100 -57
- package/src/runtime/routes/cloud-routes.ts +5 -5
- package/src/runtime/routes/conversation-query-routes.ts +275 -397
- package/src/runtime/routes/secret-routes.ts +2 -17
- package/src/tools/credentials/store.ts +1 -10
- package/src/util/layout.ts +479 -0
- package/src/util/platform.ts +11 -467
- package/src/workspace/migrations/127-backfill-default-provider.ts +2 -2
|
@@ -141,7 +141,7 @@ describe("CesHttpCredentialClient", () => {
|
|
|
141
141
|
Promise.resolve(
|
|
142
142
|
Response.json({ account: "test-acct", value: "secret-123" }),
|
|
143
143
|
),
|
|
144
|
-
) as typeof fetch;
|
|
144
|
+
) as unknown as typeof fetch;
|
|
145
145
|
|
|
146
146
|
const client = createCesHttpCredentialClient(
|
|
147
147
|
createTestConfig(),
|
|
@@ -156,7 +156,7 @@ describe("CesHttpCredentialClient", () => {
|
|
|
156
156
|
test("get returns not-found on 404", async () => {
|
|
157
157
|
globalThis.fetch = mock(() =>
|
|
158
158
|
Promise.resolve(new Response(null, { status: 404 })),
|
|
159
|
-
) as typeof fetch;
|
|
159
|
+
) as unknown as typeof fetch;
|
|
160
160
|
|
|
161
161
|
const client = createCesHttpCredentialClient(
|
|
162
162
|
createTestConfig(),
|
|
@@ -171,7 +171,7 @@ describe("CesHttpCredentialClient", () => {
|
|
|
171
171
|
test("get returns unreachable on 500", async () => {
|
|
172
172
|
globalThis.fetch = mock(() =>
|
|
173
173
|
Promise.resolve(new Response("internal error", { status: 500 })),
|
|
174
|
-
) as typeof fetch;
|
|
174
|
+
) as unknown as typeof fetch;
|
|
175
175
|
|
|
176
176
|
const client = createCesHttpCredentialClient(
|
|
177
177
|
createTestConfig(),
|
|
@@ -186,7 +186,7 @@ describe("CesHttpCredentialClient", () => {
|
|
|
186
186
|
test("get returns unreachable on network error", async () => {
|
|
187
187
|
globalThis.fetch = mock(() =>
|
|
188
188
|
Promise.reject(new Error("ECONNREFUSED")),
|
|
189
|
-
) as typeof fetch;
|
|
189
|
+
) as unknown as typeof fetch;
|
|
190
190
|
|
|
191
191
|
const client = createCesHttpCredentialClient(
|
|
192
192
|
createTestConfig(),
|
|
@@ -201,7 +201,7 @@ describe("CesHttpCredentialClient", () => {
|
|
|
201
201
|
test("delete returns deleted on 200", async () => {
|
|
202
202
|
globalThis.fetch = mock(() =>
|
|
203
203
|
Promise.resolve(Response.json({ ok: true, account: "acct" })),
|
|
204
|
-
) as typeof fetch;
|
|
204
|
+
) as unknown as typeof fetch;
|
|
205
205
|
|
|
206
206
|
const client = createCesHttpCredentialClient(
|
|
207
207
|
createTestConfig(),
|
|
@@ -215,7 +215,7 @@ describe("CesHttpCredentialClient", () => {
|
|
|
215
215
|
test("delete returns not-found on 404", async () => {
|
|
216
216
|
globalThis.fetch = mock(() =>
|
|
217
217
|
Promise.resolve(new Response(null, { status: 404 })),
|
|
218
|
-
) as typeof fetch;
|
|
218
|
+
) as unknown as typeof fetch;
|
|
219
219
|
|
|
220
220
|
const client = createCesHttpCredentialClient(
|
|
221
221
|
createTestConfig(),
|
|
@@ -229,7 +229,7 @@ describe("CesHttpCredentialClient", () => {
|
|
|
229
229
|
test("delete returns error on network failure", async () => {
|
|
230
230
|
globalThis.fetch = mock(() =>
|
|
231
231
|
Promise.reject(new Error("network down")),
|
|
232
|
-
) as typeof fetch;
|
|
232
|
+
) as unknown as typeof fetch;
|
|
233
233
|
|
|
234
234
|
const client = createCesHttpCredentialClient(
|
|
235
235
|
createTestConfig(),
|
|
@@ -243,7 +243,7 @@ describe("CesHttpCredentialClient", () => {
|
|
|
243
243
|
test("list returns accounts on 200", async () => {
|
|
244
244
|
globalThis.fetch = mock(() =>
|
|
245
245
|
Promise.resolve(Response.json({ accounts: ["a", "b", "c"] })),
|
|
246
|
-
) as typeof fetch;
|
|
246
|
+
) as unknown as typeof fetch;
|
|
247
247
|
|
|
248
248
|
const client = createCesHttpCredentialClient(
|
|
249
249
|
createTestConfig(),
|
|
@@ -258,7 +258,7 @@ describe("CesHttpCredentialClient", () => {
|
|
|
258
258
|
test("list returns unreachable on error", async () => {
|
|
259
259
|
globalThis.fetch = mock(() =>
|
|
260
260
|
Promise.reject(new Error("timeout")),
|
|
261
|
-
) as typeof fetch;
|
|
261
|
+
) as unknown as typeof fetch;
|
|
262
262
|
|
|
263
263
|
const client = createCesHttpCredentialClient(
|
|
264
264
|
createTestConfig(),
|
|
@@ -273,7 +273,7 @@ describe("CesHttpCredentialClient", () => {
|
|
|
273
273
|
test("bulkSet returns all-failed when request fails", async () => {
|
|
274
274
|
globalThis.fetch = mock(() =>
|
|
275
275
|
Promise.reject(new Error("network")),
|
|
276
|
-
) as typeof fetch;
|
|
276
|
+
) as unknown as typeof fetch;
|
|
277
277
|
|
|
278
278
|
const client = createCesHttpCredentialClient(
|
|
279
279
|
createTestConfig(),
|
|
@@ -311,7 +311,7 @@ describe("fetchCesLogExport", () => {
|
|
|
311
311
|
headers: { "Content-Type": "application/gzip" },
|
|
312
312
|
}),
|
|
313
313
|
),
|
|
314
|
-
) as typeof fetch;
|
|
314
|
+
) as unknown as typeof fetch;
|
|
315
315
|
|
|
316
316
|
const result = await fetchCesLogExport({
|
|
317
317
|
baseUrl: "http://ces-test:8090",
|
|
@@ -327,7 +327,7 @@ describe("fetchCesLogExport", () => {
|
|
|
327
327
|
test("returns error on non-OK status", async () => {
|
|
328
328
|
globalThis.fetch = mock(() =>
|
|
329
329
|
Promise.resolve(new Response("forbidden", { status: 403 })),
|
|
330
|
-
) as typeof fetch;
|
|
330
|
+
) as unknown as typeof fetch;
|
|
331
331
|
|
|
332
332
|
const result = await fetchCesLogExport({
|
|
333
333
|
baseUrl: "http://ces-test:8090",
|
|
@@ -343,7 +343,7 @@ describe("fetchCesLogExport", () => {
|
|
|
343
343
|
test("returns error on network failure", async () => {
|
|
344
344
|
globalThis.fetch = mock(() =>
|
|
345
345
|
Promise.reject(new Error("connection refused")),
|
|
346
|
-
) as typeof fetch;
|
|
346
|
+
) as unknown as typeof fetch;
|
|
347
347
|
|
|
348
348
|
const result = await fetchCesLogExport({
|
|
349
349
|
baseUrl: "http://ces-test:8090",
|
|
@@ -361,7 +361,7 @@ describe("fetchCesLogExport", () => {
|
|
|
361
361
|
globalThis.fetch = mock((url: string | URL | Request) => {
|
|
362
362
|
capturedUrl = typeof url === "string" ? url : url.toString();
|
|
363
363
|
return Promise.resolve(new Response(new ArrayBuffer(0), { status: 200 }));
|
|
364
|
-
}) as typeof fetch;
|
|
364
|
+
}) as unknown as typeof fetch;
|
|
365
365
|
|
|
366
366
|
await fetchCesLogExport(
|
|
367
367
|
{ baseUrl: "http://ces-test:8090", serviceToken: "tok" },
|
package/openapi.yaml
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
openapi: 3.1.0
|
|
4
4
|
info:
|
|
5
5
|
title: Cadet Assistant API
|
|
6
|
-
version: 0.12.
|
|
6
|
+
version: 0.12.9
|
|
7
7
|
description: Auto-generated OpenAPI specification for the Cadet Assistant runtime HTTP server.
|
|
8
8
|
servers:
|
|
9
9
|
- url: http://127.0.0.1:7821
|
|
@@ -32738,7 +32738,7 @@ paths:
|
|
|
32738
32738
|
summary: Register a cloud callback route
|
|
32739
32739
|
description: Registers a callback route with the cloud gateway for inbound provider webhooks.
|
|
32740
32740
|
tags:
|
|
32741
|
-
-
|
|
32741
|
+
- cloud
|
|
32742
32742
|
requestBody:
|
|
32743
32743
|
required: true
|
|
32744
32744
|
content:
|
|
@@ -45141,14 +45141,6 @@ components:
|
|
|
45141
45141
|
type: string
|
|
45142
45142
|
minLength: 1
|
|
45143
45143
|
- type: "null"
|
|
45144
|
-
supportsVision:
|
|
45145
|
-
anyOf:
|
|
45146
|
-
- type: boolean
|
|
45147
|
-
- type: "null"
|
|
45148
|
-
invariant:
|
|
45149
|
-
anyOf:
|
|
45150
|
-
- type: boolean
|
|
45151
|
-
- type: "null"
|
|
45152
45144
|
additionalProperties: {}
|
|
45153
45145
|
InputModalities:
|
|
45154
45146
|
type: object
|
|
@@ -45501,10 +45493,6 @@ components:
|
|
|
45501
45493
|
readOnly: true
|
|
45502
45494
|
type: string
|
|
45503
45495
|
minLength: 1
|
|
45504
|
-
supportsVision:
|
|
45505
|
-
type: boolean
|
|
45506
|
-
invariant:
|
|
45507
|
-
type: boolean
|
|
45508
45496
|
ConnectionProvider:
|
|
45509
45497
|
type: string
|
|
45510
45498
|
enum:
|
|
@@ -46285,10 +46273,6 @@ components:
|
|
|
46285
46273
|
readOnly: true
|
|
46286
46274
|
type: string
|
|
46287
46275
|
minLength: 1
|
|
46288
|
-
supportsVision:
|
|
46289
|
-
type: boolean
|
|
46290
|
-
invariant:
|
|
46291
|
-
type: boolean
|
|
46292
46276
|
additionalProperties: false
|
|
46293
46277
|
InputModalitiesOutput:
|
|
46294
46278
|
type: object
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mycadet/assistant",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.9",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"probe:skill-retrieval": "bun run scripts/probe-skill-retrieval.ts",
|
|
21
21
|
"format": "prettier --write .",
|
|
22
22
|
"format:check": "prettier --check .",
|
|
23
|
-
"lint": "bun run lint:skills && bun run lint:openapi && eslint --max-warnings
|
|
23
|
+
"lint": "bun run lint:skills && bun run lint:openapi && eslint --max-warnings 2956",
|
|
24
24
|
"lint:unused": "knip --include files,dependencies,unlisted",
|
|
25
25
|
"lint:circular": "bun run scripts/check-circular-deps.ts",
|
|
26
26
|
"lint:unused:production": "knip --production --include exports",
|
|
@@ -592,12 +592,12 @@ describe("PATCH /v1/config — cloud profile deletion guard", () => {
|
|
|
592
592
|
});
|
|
593
593
|
|
|
594
594
|
expect(result).toHaveProperty("ingress");
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
595
|
+
const committed = committedRaw() as Record<string, any>;
|
|
596
|
+
expect(committed.ingress.publicBaseUrl).toBe("https://manual.example.test");
|
|
597
|
+
expect(committed.ingress.publicBaseUrlManagedBy).toBe("tunnel");
|
|
598
|
+
expect("lastTunnel" in committed.ingress).toBe(false);
|
|
599
|
+
expect("pairingTunnel" in committed.ingress).toBe(false);
|
|
600
|
+
expect("assistantId" in committed.ingress).toBe(false);
|
|
601
601
|
});
|
|
602
602
|
|
|
603
603
|
test("allows patches that modify a cloud profile (non-null)", async () => {
|
|
@@ -756,7 +756,7 @@ describe("cloud-profile invariant guard — rejected writes", () => {
|
|
|
756
756
|
patchRoute.handler({
|
|
757
757
|
body: { llm: { profiles: { balanced: "junk" } } },
|
|
758
758
|
}),
|
|
759
|
-
).rejects.toThrow(
|
|
759
|
+
).rejects.toThrow("expected object, received string");
|
|
760
760
|
expectNothingCommitted();
|
|
761
761
|
});
|
|
762
762
|
|
|
@@ -765,7 +765,7 @@ describe("cloud-profile invariant guard — rejected writes", () => {
|
|
|
765
765
|
patchRoute.handler({
|
|
766
766
|
body: { llm: { profiles: { balanced: ["disabled"] } } },
|
|
767
767
|
}),
|
|
768
|
-
).rejects.toThrow(
|
|
768
|
+
).rejects.toThrow("expected object, received array");
|
|
769
769
|
expectNothingCommitted();
|
|
770
770
|
});
|
|
771
771
|
|
|
@@ -806,10 +806,7 @@ describe("cloud-profile invariant guard — rejected writes", () => {
|
|
|
806
806
|
patchRoute.handler({
|
|
807
807
|
body: { llm: { profiles: { balanced: { status: "weird" } } } },
|
|
808
808
|
}),
|
|
809
|
-
).rejects.toThrow(
|
|
810
|
-
'Cannot set status "weird" on cloud profile "balanced". ' +
|
|
811
|
-
'Only re-enabling (status "active") is allowed.',
|
|
812
|
-
);
|
|
809
|
+
).rejects.toThrow("llm.profiles.balanced.status: Invalid option");
|
|
813
810
|
expectNothingCommitted();
|
|
814
811
|
});
|
|
815
812
|
|
|
@@ -818,7 +815,7 @@ describe("cloud-profile invariant guard — rejected writes", () => {
|
|
|
818
815
|
setRoute.handler({
|
|
819
816
|
body: { path: "llm.profiles.balanced.status", value: 123 },
|
|
820
817
|
}),
|
|
821
|
-
).rejects.toThrow(
|
|
818
|
+
).rejects.toThrow("llm.profiles.balanced.status: Invalid option");
|
|
822
819
|
expectNothingCommitted();
|
|
823
820
|
});
|
|
824
821
|
});
|
|
@@ -939,12 +936,12 @@ describe("default-profile invariant guard — allowed writes", () => {
|
|
|
939
936
|
|
|
940
937
|
test("PATCH non-profile llm writes are unaffected", async () => {
|
|
941
938
|
const result = await patchRoute.handler({
|
|
942
|
-
body: { llm: { activeProfile: "
|
|
939
|
+
body: { llm: { activeProfile: "balanced" } },
|
|
943
940
|
});
|
|
944
941
|
expect(result).toHaveProperty("llm");
|
|
945
942
|
expectOneCommitCycle();
|
|
946
943
|
expect((committedRaw() as Record<string, any>).llm.activeProfile).toBe(
|
|
947
|
-
"
|
|
944
|
+
"balanced",
|
|
948
945
|
);
|
|
949
946
|
// The invariant profiles are untouched by an unrelated llm write.
|
|
950
947
|
expect(savedProfile("balanced")).toEqual(
|
|
@@ -1004,12 +1001,13 @@ describe("wire-only profile keys are stripped from writes", () => {
|
|
|
1004
1001
|
expect(profile.model).toBe("claude-sonnet");
|
|
1005
1002
|
});
|
|
1006
1003
|
|
|
1007
|
-
test("SET of a wire-only leaf path
|
|
1004
|
+
test("SET of a wire-only leaf path never persists the key", async () => {
|
|
1008
1005
|
const result = await setRoute.handler({
|
|
1009
1006
|
body: { path: "llm.profiles.balanced.supportsVision", value: true },
|
|
1010
1007
|
});
|
|
1011
1008
|
expect(result).toEqual({ ok: true });
|
|
1012
|
-
|
|
1009
|
+
expectOneCommitCycle();
|
|
1010
|
+
expect("supportsVision" in savedProfile("balanced")).toBe(false);
|
|
1013
1011
|
});
|
|
1014
1012
|
|
|
1015
1013
|
// -------------------------------------------------------------------------
|
|
@@ -1425,7 +1423,11 @@ describe("PATCH /v1/config — provider-only services scrub legacy mode", () =>
|
|
|
1425
1423
|
string,
|
|
1426
1424
|
Record<string, Record<string, unknown>>
|
|
1427
1425
|
>;
|
|
1428
|
-
|
|
1429
|
-
|
|
1426
|
+
// The section schemas are closed: the parse strips the legacy `mode`
|
|
1427
|
+
// key and materializes the section's defaults around the user's values.
|
|
1428
|
+
expect("mode" in raw.services["image-generation"]!).toBe(false);
|
|
1429
|
+
expect(raw.services["image-generation"]!.provider).toBe("cadet");
|
|
1430
|
+
expect("mode" in raw.services["web-search"]!).toBe(false);
|
|
1431
|
+
expect(raw.services["web-search"]!.provider).toBe("cadet");
|
|
1430
1432
|
});
|
|
1431
1433
|
});
|
|
@@ -85,13 +85,13 @@ describe("config_set route - request validation", () => {
|
|
|
85
85
|
});
|
|
86
86
|
|
|
87
87
|
test("accepts body with explicit null value", async () => {
|
|
88
|
-
seedRawConfig({ heartbeat: {
|
|
88
|
+
seedRawConfig({ heartbeat: { timezone: "America/New_York" } });
|
|
89
89
|
const result = await configSetRoute.handler({
|
|
90
|
-
body: { path: "heartbeat.
|
|
90
|
+
body: { path: "heartbeat.timezone", value: null },
|
|
91
91
|
});
|
|
92
92
|
expect(result).toEqual({ ok: true });
|
|
93
93
|
const heartbeat = loadRawConfig().heartbeat as Record<string, unknown>;
|
|
94
|
-
expect(heartbeat.
|
|
94
|
+
expect(heartbeat.timezone).toBeNull();
|
|
95
95
|
});
|
|
96
96
|
|
|
97
97
|
test("rejects body missing path field", async () => {
|
|
@@ -192,12 +192,16 @@ describe("config_set route - request validation", () => {
|
|
|
192
192
|
body: { path: "memory.cleanup.conversationRetentionDays", value: 30 },
|
|
193
193
|
});
|
|
194
194
|
const saved = loadRawConfig();
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
195
|
+
// The write is schema-canonical: the parse materializes section defaults
|
|
196
|
+
// around what the user set. The contract under test — a nested set may
|
|
197
|
+
// not drop or mangle unrelated top-level keys — is asserted leaf-wise.
|
|
198
|
+
expect((saved.llm as Record<string, unknown>).default).toEqual({
|
|
199
|
+
provider: "anthropic",
|
|
200
200
|
});
|
|
201
|
+
expect((saved.calls as Record<string, unknown>).enabled).toBe(true);
|
|
202
|
+
const heartbeat = saved.heartbeat as Record<string, unknown>;
|
|
203
|
+
expect(heartbeat.activeHoursStart).toBe(9);
|
|
204
|
+
expect(heartbeat.activeHoursEnd).toBe(22);
|
|
201
205
|
});
|
|
202
206
|
});
|
|
203
207
|
|
|
@@ -780,8 +780,10 @@ describe("credential-health-service", () => {
|
|
|
780
780
|
// carries the repair.
|
|
781
781
|
const assistantKeyResult = checkAssistantApiKey;
|
|
782
782
|
|
|
783
|
-
test("is
|
|
784
|
-
|
|
783
|
+
test("is missing_token when no key is stored, connected or not", async () => {
|
|
784
|
+
const result = await assistantKeyResult();
|
|
785
|
+
expect(result!.status).toBe("missing_token");
|
|
786
|
+
expect(result!.canAutoRecover).toBe(true);
|
|
785
787
|
});
|
|
786
788
|
|
|
787
789
|
test("is healthy when the cloud accepts the stored key", async () => {
|