@app-vitals/agent-admin 0.8.0 → 0.9.1
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/e2e/chat-thread-page.e2e.ts +0 -79
- package/openapi.json +14 -1
- package/openapi.platform.json +14 -1
- package/package.json +2 -2
- package/prisma/client/internal/class.ts +4 -4
- package/prisma/client/internal/prismaNamespace.ts +1 -0
- package/prisma/client/internal/prismaNamespaceBrowser.ts +1 -0
- package/prisma/client/models/Agent.ts +76 -1
- package/prisma/migrations/20261029000000_add_agent_specialty/migration.sql +5 -0
- package/prisma/schema/schema.prisma +3 -0
- package/src/admin-local-agent.smoke.test.ts +6 -2
- package/src/admin-ui-pages.ts +109 -153
- package/src/admin-ui-pages.unit.test.ts +85 -221
- package/src/admin-ui-push-test.ts +5 -1
- package/src/admin-ui.smoke.test.ts +69 -6
- package/src/admin-ui.ts +71 -10
- package/src/agent-cron-jobs.ts +6 -4
- package/src/agent-specialty.ts +22 -0
- package/src/agent-specialty.unit.test.ts +43 -0
- package/src/agents-api.smoke.test.ts +1 -0
- package/src/agents-api.ts +6 -1
- package/src/agents.integration.test.ts +90 -0
- package/src/agents.ts +39 -15
- package/src/agents.unit.test.ts +19 -2
- package/src/api.ts +11 -0
- package/src/chat.smoke.test.ts +78 -16
- package/src/http-range.ts +31 -0
- package/src/http-range.unit.test.ts +65 -0
- package/src/openapi-schemas.ts +3 -0
- package/src/push-toggle.ts +6 -2
- package/src/pwa.ts +1 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { parseByteRange } from "./http-range.ts";
|
|
3
|
+
|
|
4
|
+
describe("parseByteRange", () => {
|
|
5
|
+
test("no header means no range", () => {
|
|
6
|
+
expect(parseByteRange(undefined, 100)).toEqual({ kind: "none" });
|
|
7
|
+
expect(parseByteRange(null, 100)).toEqual({ kind: "none" });
|
|
8
|
+
});
|
|
9
|
+
test("mid-file range", () => {
|
|
10
|
+
expect(parseByteRange("bytes=10-19", 100)).toEqual({
|
|
11
|
+
kind: "range",
|
|
12
|
+
start: 10,
|
|
13
|
+
end: 19,
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
test("open-ended range", () => {
|
|
17
|
+
expect(parseByteRange("bytes=90-", 100)).toEqual({
|
|
18
|
+
kind: "range",
|
|
19
|
+
start: 90,
|
|
20
|
+
end: 99,
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
test("suffix range", () => {
|
|
24
|
+
expect(parseByteRange("bytes=-10", 100)).toEqual({
|
|
25
|
+
kind: "range",
|
|
26
|
+
start: 90,
|
|
27
|
+
end: 99,
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
test("suffix longer than file clamps to whole file", () => {
|
|
31
|
+
expect(parseByteRange("bytes=-500", 100)).toEqual({
|
|
32
|
+
kind: "range",
|
|
33
|
+
start: 0,
|
|
34
|
+
end: 99,
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
test("end past EOF is clamped", () => {
|
|
38
|
+
expect(parseByteRange("bytes=50-999", 100)).toEqual({
|
|
39
|
+
kind: "range",
|
|
40
|
+
start: 50,
|
|
41
|
+
end: 99,
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
test("start at or beyond size is unsatisfiable", () => {
|
|
45
|
+
expect(parseByteRange("bytes=100-", 100)).toEqual({
|
|
46
|
+
kind: "unsatisfiable",
|
|
47
|
+
});
|
|
48
|
+
expect(parseByteRange("bytes=500-600", 100)).toEqual({
|
|
49
|
+
kind: "unsatisfiable",
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
test("zero-length suffix and empty file are unsatisfiable", () => {
|
|
53
|
+
expect(parseByteRange("bytes=-0", 100)).toEqual({ kind: "unsatisfiable" });
|
|
54
|
+
expect(parseByteRange("bytes=0-", 0)).toEqual({ kind: "unsatisfiable" });
|
|
55
|
+
});
|
|
56
|
+
test("start > end is ignored (treated as no range)", () => {
|
|
57
|
+
expect(parseByteRange("bytes=20-10", 100)).toEqual({ kind: "none" });
|
|
58
|
+
});
|
|
59
|
+
test("malformed, multi-range and non-bytes units are ignored", () => {
|
|
60
|
+
expect(parseByteRange("bytes=abc", 100)).toEqual({ kind: "none" });
|
|
61
|
+
expect(parseByteRange("bytes=0-1,5-6", 100)).toEqual({ kind: "none" });
|
|
62
|
+
expect(parseByteRange("items=0-1", 100)).toEqual({ kind: "none" });
|
|
63
|
+
expect(parseByteRange("bytes=-", 100)).toEqual({ kind: "none" });
|
|
64
|
+
});
|
|
65
|
+
});
|
package/src/openapi-schemas.ts
CHANGED
|
@@ -35,6 +35,7 @@ export const AgentSummarySchema = z
|
|
|
35
35
|
name: z.string().openapi({ example: "Bodhi" }),
|
|
36
36
|
selfHosted: z.boolean().openapi({ example: false }),
|
|
37
37
|
typeName: z.string().openapi({ example: "coding" }),
|
|
38
|
+
specialty: z.string().openapi({ example: "coding" }),
|
|
38
39
|
})
|
|
39
40
|
.openapi("AgentSummary");
|
|
40
41
|
|
|
@@ -1344,6 +1345,8 @@ export const AgentConfigResponseSchema = z
|
|
|
1344
1345
|
* falls back to "coding" templates when absent.
|
|
1345
1346
|
*/
|
|
1346
1347
|
typeName: z.string().optional().openapi({ example: "coding" }),
|
|
1348
|
+
/** AGR-6.1: specialty (specialty ?? typeName); additive next to typeName. */
|
|
1349
|
+
specialty: z.string().optional().openapi({ example: "coding" }),
|
|
1347
1350
|
/**
|
|
1348
1351
|
* PMC-1.1: phase -> subagentType (or null) map, covering all six pipeline
|
|
1349
1352
|
* phases (see AGENT_PHASES) regardless of whether the agent has an
|
package/src/push-toggle.ts
CHANGED
|
@@ -114,14 +114,18 @@ export function renderPushToggle(args: PushToggleArgs): string {
|
|
|
114
114
|
var json = subscription.toJSON();
|
|
115
115
|
return fetch(SUBSCRIBE_URL, {
|
|
116
116
|
method: 'POST',
|
|
117
|
-
headers: { 'Content-Type': 'application/json' },
|
|
117
|
+
headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
|
|
118
118
|
body: JSON.stringify({
|
|
119
119
|
endpoint: subscription.endpoint,
|
|
120
120
|
p256dh: json.keys && json.keys.p256dh,
|
|
121
121
|
auth: json.keys && json.keys.auth,
|
|
122
122
|
}),
|
|
123
123
|
});
|
|
124
|
-
}).then(function() {
|
|
124
|
+
}).then(function(res) {
|
|
125
|
+
if (res && res.status === 401) {
|
|
126
|
+
window.location.href = '/admin/login?returnTo=' + encodeURIComponent(location.pathname + location.search);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
125
129
|
setStatus('Notifications enabled.');
|
|
126
130
|
btn.textContent = 'Notifications on';
|
|
127
131
|
});
|
package/src/pwa.ts
CHANGED