@intentic/sandbox-contract 1.333.1 → 1.335.0
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/README.md +8 -3
- package/dist/contracts/accounts.contract.d.ts +11 -11
- package/dist/contracts/activity.contract.d.ts +1 -0
- package/dist/contracts/activity.contract.d.ts.map +1 -1
- package/dist/contracts/activity.contract.js +1 -1
- package/dist/contracts/activity.contract.js.map +1 -1
- package/dist/contracts/agent.contract.d.ts +102 -18
- package/dist/contracts/agent.contract.d.ts.map +1 -1
- package/dist/contracts/agents.contract.d.ts +163 -0
- package/dist/contracts/agents.contract.d.ts.map +1 -1
- package/dist/contracts/agents.contract.js +10 -1
- package/dist/contracts/agents.contract.js.map +1 -1
- package/dist/contracts/audit.contract.d.ts +84 -0
- package/dist/contracts/audit.contract.d.ts.map +1 -0
- package/dist/contracts/audit.contract.js +43 -0
- package/dist/contracts/audit.contract.js.map +1 -0
- package/dist/contracts/automations.contract.d.ts +15 -0
- package/dist/contracts/automations.contract.d.ts.map +1 -1
- package/dist/contracts/ci.contract.d.ts +10 -0
- package/dist/contracts/ci.contract.d.ts.map +1 -1
- package/dist/contracts/compliance.contract.d.ts +163 -0
- package/dist/contracts/compliance.contract.d.ts.map +1 -0
- package/dist/contracts/compliance.contract.js +41 -0
- package/dist/contracts/compliance.contract.js.map +1 -0
- package/dist/contracts/device.contract.d.ts +6 -0
- package/dist/contracts/device.contract.d.ts.map +1 -1
- package/dist/contracts/extensions.contract.d.ts +2 -0
- package/dist/contracts/extensions.contract.d.ts.map +1 -1
- package/dist/contracts/loops.contract.d.ts +15 -0
- package/dist/contracts/loops.contract.d.ts.map +1 -1
- package/dist/contracts/needs.contract.d.ts +49 -0
- package/dist/contracts/needs.contract.d.ts.map +1 -1
- package/dist/contracts/offload.contract.d.ts +5 -0
- package/dist/contracts/offload.contract.d.ts.map +1 -1
- package/dist/contracts/personas.contract.d.ts +10 -0
- package/dist/contracts/personas.contract.d.ts.map +1 -1
- package/dist/contracts/providers.contract.d.ts +2 -0
- package/dist/contracts/providers.contract.d.ts.map +1 -1
- package/dist/contracts/providers.contract.js +4 -0
- package/dist/contracts/providers.contract.js.map +1 -1
- package/dist/contracts/runner.contract.d.ts +42 -10
- package/dist/contracts/runner.contract.d.ts.map +1 -1
- package/dist/contracts/sessions.contract.d.ts +17 -0
- package/dist/contracts/sessions.contract.d.ts.map +1 -1
- package/dist/contracts/settings.contract.d.ts +84 -0
- package/dist/contracts/settings.contract.d.ts.map +1 -1
- package/dist/contracts/settings.contract.js +7 -0
- package/dist/contracts/settings.contract.js.map +1 -1
- package/dist/contracts/system.contract.d.ts +16 -2
- package/dist/contracts/system.contract.d.ts.map +1 -1
- package/dist/contracts/workflows.contract.d.ts +30 -0
- package/dist/contracts/workflows.contract.d.ts.map +1 -1
- package/dist/contracts/workspace.contract.d.ts +3 -0
- package/dist/contracts/workspace.contract.d.ts.map +1 -1
- package/dist/events/agent-events.d.ts +74 -0
- package/dist/events/agent-events.d.ts.map +1 -1
- package/dist/events/agent-events.js +1 -0
- package/dist/events/agent-events.js.map +1 -1
- package/dist/events/requests.d.ts +45 -0
- package/dist/events/requests.d.ts.map +1 -1
- package/dist/events/system-events.d.ts +12 -0
- package/dist/events/system-events.d.ts.map +1 -1
- package/dist/events/transcript.d.ts +117 -0
- package/dist/events/transcript.d.ts.map +1 -1
- package/dist/index.d.ts +828 -73
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -1
- package/dist/models/agent-catalog.d.ts +18 -3
- package/dist/models/agent-catalog.d.ts.map +1 -1
- package/dist/models/agent-catalog.js +61 -12
- package/dist/models/agent-catalog.js.map +1 -1
- package/dist/models/agent-runtimes.d.ts +5 -2
- package/dist/models/agent-runtimes.d.ts.map +1 -1
- package/dist/models/agent-runtimes.js +73 -40
- package/dist/models/agent-runtimes.js.map +1 -1
- package/dist/models/model-setups.d.ts +5 -0
- package/dist/models/model-setups.d.ts.map +1 -1
- package/dist/models/provider-specs.d.ts +153 -32
- package/dist/models/provider-specs.d.ts.map +1 -1
- package/dist/models/provider-specs.js +97 -11
- package/dist/models/provider-specs.js.map +1 -1
- package/dist/policy/reserved-servers.d.ts.map +1 -1
- package/dist/policy/reserved-servers.js +1 -0
- package/dist/policy/reserved-servers.js.map +1 -1
- package/dist/policy/sandbox-power.d.ts +7 -2
- package/dist/policy/sandbox-power.d.ts.map +1 -1
- package/dist/policy/sandbox-power.js +34 -1
- package/dist/policy/sandbox-power.js.map +1 -1
- package/dist/policy/turned-away.d.ts.map +1 -1
- package/dist/policy/turned-away.js +1 -0
- package/dist/policy/turned-away.js.map +1 -1
- package/dist/protocol/doors/listener-protocol.d.ts +43 -1
- package/dist/protocol/doors/listener-protocol.d.ts.map +1 -1
- package/dist/protocol/doors/listener-protocol.js +10 -0
- package/dist/protocol/doors/listener-protocol.js.map +1 -1
- package/dist/protocol/raw/raw-json-routes.d.ts +92 -0
- package/dist/protocol/raw/raw-json-routes.d.ts.map +1 -1
- package/dist/protocol/raw/raw-json-routes.js +2 -1
- package/dist/protocol/raw/raw-json-routes.js.map +1 -1
- package/dist/protocol/raw/raw-routes.d.ts +13 -0
- package/dist/protocol/raw/raw-routes.d.ts.map +1 -1
- package/dist/protocol/raw/raw-routes.js +3 -0
- package/dist/protocol/raw/raw-routes.js.map +1 -1
- package/dist/schemas/activity.d.ts +2 -0
- package/dist/schemas/activity.d.ts.map +1 -1
- package/dist/schemas/agent.d.ts +34 -0
- package/dist/schemas/agent.d.ts.map +1 -1
- package/dist/schemas/agent.js +7 -6
- package/dist/schemas/agent.js.map +1 -1
- package/dist/schemas/agents-list.d.ts +7 -0
- package/dist/schemas/agents-list.d.ts.map +1 -1
- package/dist/schemas/agents.d.ts +18 -0
- package/dist/schemas/agents.d.ts.map +1 -1
- package/dist/schemas/ai-literacy.d.ts +54 -0
- package/dist/schemas/ai-literacy.d.ts.map +1 -0
- package/dist/schemas/ai-literacy.js +15 -0
- package/dist/schemas/ai-literacy.js.map +1 -0
- package/dist/schemas/audit.d.ts +166 -0
- package/dist/schemas/audit.d.ts.map +1 -0
- package/dist/schemas/audit.js +100 -0
- package/dist/schemas/audit.js.map +1 -0
- package/dist/schemas/automations.d.ts +27 -0
- package/dist/schemas/automations.d.ts.map +1 -1
- package/dist/schemas/automations.js +11 -0
- package/dist/schemas/automations.js.map +1 -1
- package/dist/schemas/ci.d.ts +10 -0
- package/dist/schemas/ci.d.ts.map +1 -1
- package/dist/schemas/compliance.d.ts +423 -0
- package/dist/schemas/compliance.d.ts.map +1 -0
- package/dist/schemas/compliance.js +135 -0
- package/dist/schemas/compliance.js.map +1 -0
- package/dist/schemas/data-protection.d.ts +28 -0
- package/dist/schemas/data-protection.d.ts.map +1 -0
- package/dist/schemas/data-protection.js +49 -0
- package/dist/schemas/data-protection.js.map +1 -0
- package/dist/schemas/devices.d.ts +12 -0
- package/dist/schemas/devices.d.ts.map +1 -1
- package/dist/schemas/engines.d.ts +22 -1
- package/dist/schemas/engines.d.ts.map +1 -1
- package/dist/schemas/engines.js +1 -1
- package/dist/schemas/engines.js.map +1 -1
- package/dist/schemas/extension-updates.d.ts +4 -0
- package/dist/schemas/extension-updates.d.ts.map +1 -1
- package/dist/schemas/git/git.d.ts +33 -0
- package/dist/schemas/git/git.d.ts.map +1 -1
- package/dist/schemas/git/git.js +29 -0
- package/dist/schemas/git/git.js.map +1 -1
- package/dist/schemas/hosts.d.ts +36 -0
- package/dist/schemas/hosts.d.ts.map +1 -1
- package/dist/schemas/hosts.js +2 -0
- package/dist/schemas/hosts.js.map +1 -1
- package/dist/schemas/intentic-os.d.ts +117 -0
- package/dist/schemas/intentic-os.d.ts.map +1 -0
- package/dist/schemas/intentic-os.js +66 -0
- package/dist/schemas/intentic-os.js.map +1 -0
- package/dist/schemas/loops.d.ts +15 -0
- package/dist/schemas/loops.d.ts.map +1 -1
- package/dist/schemas/members.d.ts +90 -0
- package/dist/schemas/members.d.ts.map +1 -1
- package/dist/schemas/members.js +73 -3
- package/dist/schemas/members.js.map +1 -1
- package/dist/schemas/needs.d.ts +56 -0
- package/dist/schemas/needs.d.ts.map +1 -1
- package/dist/schemas/offload.d.ts +5 -0
- package/dist/schemas/offload.d.ts.map +1 -1
- package/dist/schemas/personas.d.ts +10 -0
- package/dist/schemas/personas.d.ts.map +1 -1
- package/dist/schemas/providers/plan-limits.d.ts +20 -0
- package/dist/schemas/providers/plan-limits.d.ts.map +1 -1
- package/dist/schemas/providers/provider-oauth.js +1 -1
- package/dist/schemas/providers/provider-oauth.js.map +1 -1
- package/dist/schemas/providers/usage.d.ts +1 -0
- package/dist/schemas/providers/usage.d.ts.map +1 -1
- package/dist/schemas/providers/usage.js +4 -0
- package/dist/schemas/providers/usage.js.map +1 -1
- package/dist/schemas/settings.d.ts +48 -0
- package/dist/schemas/settings.d.ts.map +1 -1
- package/dist/schemas/settings.js +14 -0
- package/dist/schemas/settings.js.map +1 -1
- package/dist/schemas/system.d.ts +8 -0
- package/dist/schemas/system.d.ts.map +1 -1
- package/dist/schemas/system.js +2 -0
- package/dist/schemas/system.js.map +1 -1
- package/dist/schemas/workflows.d.ts +50 -0
- package/dist/schemas/workflows.d.ts.map +1 -1
- package/dist/schemas/workspace/workspace-tree.d.ts +4 -0
- package/dist/schemas/workspace/workspace-tree.d.ts.map +1 -1
- package/dist/schemas/workspace/workspace-tree.js +16 -1
- package/dist/schemas/workspace/workspace-tree.js.map +1 -1
- package/dist/state/definition.d.ts +80 -4
- package/dist/state/definition.d.ts.map +1 -1
- package/dist/state/history-state.d.ts.map +1 -1
- package/dist/state/history-state.js +2 -0
- package/dist/state/history-state.js.map +1 -1
- package/package.json +5 -5
- package/src/contracts/activity.contract.ts +1 -1
- package/src/contracts/agents.contract.ts +13 -1
- package/src/contracts/audit.contract.ts +48 -0
- package/src/contracts/compliance.contract.ts +57 -0
- package/src/contracts/providers.contract.ts +9 -0
- package/src/contracts/settings.contract.ts +7 -0
- package/src/events/agent-events.ts +4 -0
- package/src/index.ts +11 -0
- package/src/models/agent-catalog.test.ts +317 -53
- package/src/models/agent-catalog.ts +115 -22
- package/src/models/agent-runtimes.ts +273 -75
- package/src/models/capability-ledger.test.ts +12 -1
- package/src/models/provider-specs.test.ts +42 -7
- package/src/models/provider-specs.ts +160 -20
- package/src/policy/reserved-servers.ts +2 -1
- package/src/policy/sandbox-power.ts +67 -8
- package/src/policy/tests/sandbox-power.test.ts +59 -1
- package/src/policy/turned-away.ts +2 -0
- package/src/protocol/doors/listener-protocol.ts +74 -6
- package/src/protocol/raw/raw-json-routes.ts +2 -1
- package/src/protocol/raw/raw-routes.ts +7 -0
- package/src/schemas/activity.ts +2 -2
- package/src/schemas/agent.ts +22 -9
- package/src/schemas/ai-literacy.ts +17 -0
- package/src/schemas/audit.ts +131 -0
- package/src/schemas/automations.ts +13 -0
- package/src/schemas/compliance.ts +195 -0
- package/src/schemas/daemon-health.test.ts +2 -0
- package/src/schemas/data-protection.ts +99 -0
- package/src/schemas/engines.ts +1 -1
- package/src/schemas/git/git.ts +46 -0
- package/src/schemas/hosts.ts +5 -0
- package/src/schemas/intentic-os.test.ts +86 -0
- package/src/schemas/intentic-os.ts +130 -0
- package/src/schemas/members.test.ts +47 -0
- package/src/schemas/members.ts +141 -6
- package/src/schemas/providers/provider-oauth.ts +1 -1
- package/src/schemas/providers/usage.ts +6 -0
- package/src/schemas/settings.ts +32 -0
- package/src/schemas/system.ts +2 -0
- package/src/schemas/workspace/workspace-tree.ts +26 -1
- package/src/state/history-state.ts +7 -0
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import type { ToolKind } from "../../events/requests.js";
|
|
2
3
|
import { ActivityStatusSchema } from "../../schemas/activity.js";
|
|
3
4
|
import { rawRouteUrl } from "../raw/raw-routes.js";
|
|
4
5
|
|
|
5
6
|
// Wire between the daemon and an extension's realtime-listener gateway (ext-discord, ext-slack, ext-telegram,
|
|
6
|
-
// ext-whatsapp, ext-imap, ext-google-workspace): the
|
|
7
|
-
// status). Shared here so both ends compile against one declaration.
|
|
7
|
+
// ext-whatsapp, ext-imap, ext-google-workspace): the five /listeners/:provider routes (state, dispatch, failure,
|
|
8
|
+
// status, stop). Shared here so both ends compile against one declaration.
|
|
8
9
|
|
|
9
|
-
// The path of one of the
|
|
10
|
+
// The path of one of the five listener routes for a provider, built from RAW_ROUTES so the gateway and the daemon
|
|
10
11
|
// cannot spell it two ways.
|
|
11
|
-
export const listenerRouteUrl = (route: "state" | "dispatch" | "failure" | "status", provider: string): string =>
|
|
12
|
+
export const listenerRouteUrl = (route: "state" | "dispatch" | "failure" | "status" | "stop", provider: string): string =>
|
|
12
13
|
route === "state"
|
|
13
14
|
? rawRouteUrl("GET /listeners/{provider}/state", { provider })
|
|
14
15
|
: rawRouteUrl(`POST /listeners/{provider}/${route}`, { provider });
|
|
@@ -39,7 +40,14 @@ export const ListenerMessageSchema = z.object({
|
|
|
39
40
|
provider: z.string().min(1),
|
|
40
41
|
type: z.string().min(1),
|
|
41
42
|
id: z.string(),
|
|
43
|
+
// The room the message was posted in, as an automation's channel filter names it: for a message inside a thread,
|
|
44
|
+
// the channel the thread hangs off.
|
|
42
45
|
channelId: z.string(),
|
|
46
|
+
// The thread inside that channel the message belongs to, by the provider's own id: a Discord thread's channel id (a
|
|
47
|
+
// thread started from a message carries that message's id, so a mention names the thread its answer will open), a
|
|
48
|
+
// Slack thread's root ts. One conversation per thread, and where the reply goes. Absent, the channel is the
|
|
49
|
+
// conversation.
|
|
50
|
+
thread: z.string().min(1).optional(),
|
|
43
51
|
// `id` is what the service vouches for and what sender rules match; `name` is display only. `groups` are the
|
|
44
52
|
// service's own group ids on this sender (Discord role ids), absent on a source without them.
|
|
45
53
|
author: z.object({ id: z.string(), name: z.string(), groups: z.array(z.string()).optional() }),
|
|
@@ -64,16 +72,76 @@ export const ListenerMessageSchema = z.object({
|
|
|
64
72
|
});
|
|
65
73
|
export type ListenerMessage = z.infer<typeof ListenerMessageSchema>;
|
|
66
74
|
|
|
67
|
-
//
|
|
68
|
-
|
|
75
|
+
// The turn a message started or continued: the conversation it runs in, and where a person opens it in the app.
|
|
76
|
+
export interface ListenerTurnOpened {
|
|
77
|
+
readonly conversationId: string;
|
|
78
|
+
readonly link?: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// One piece of the agent's work as a status line shows it: what kind of work, on what, and how it went. Never the
|
|
82
|
+
// call's output. `delegate` is a subagent the turn started, its target the task it was given.
|
|
83
|
+
export interface ListenerStep {
|
|
84
|
+
readonly id: string;
|
|
85
|
+
readonly tool: string;
|
|
86
|
+
readonly kind: ToolKind | "delegate";
|
|
87
|
+
readonly target?: string;
|
|
88
|
+
readonly status: "running" | "done" | "failed";
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// One item of the agent's own checklist, replaced whole each time the agent rewrites it.
|
|
92
|
+
export interface ListenerPlanItem {
|
|
93
|
+
readonly text: string;
|
|
94
|
+
readonly status: "pending" | "in_progress" | "completed";
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// One ndjson frame of a /listeners/<provider>/dispatch?stream=1 response, per matched automation: the reply's text, the
|
|
98
|
+
// turn's progress, or how the message was taken. A type, not a schema, since only the daemon produces these; every field
|
|
99
|
+
// but `automationId` is optional, so a gateway older than one of them reads the rest.
|
|
69
100
|
export interface ListenerDispatchFrame {
|
|
70
101
|
readonly automationId: string;
|
|
102
|
+
// The reply's text as the model writes it, the turn's own words only (a subagent's stay in the conversation).
|
|
71
103
|
readonly delta?: string;
|
|
72
104
|
// The turn's failure, forwarded verbatim since a gateway delivers it into the owner's own channel.
|
|
73
105
|
readonly failed?: string;
|
|
106
|
+
// A turn began for this message; sent once, before any of its work.
|
|
107
|
+
readonly opened?: ListenerTurnOpened;
|
|
108
|
+
// A tool call starting or settling, keyed by `id`: a later frame with the same id replaces the earlier one.
|
|
109
|
+
readonly step?: ListenerStep;
|
|
110
|
+
// The agent's checklist as it now stands.
|
|
111
|
+
readonly plan?: readonly ListenerPlanItem[];
|
|
112
|
+
// The wake waits for a person's approval before anything runs; the sentence to say where the message came from.
|
|
113
|
+
readonly held?: string;
|
|
114
|
+
// The wake waits for other turns of this automation to finish first; the sentence to say why.
|
|
115
|
+
readonly queued?: string;
|
|
116
|
+
// The message went into the turn already running in its thread instead of starting one; that turn answers it.
|
|
117
|
+
readonly steered?: boolean;
|
|
74
118
|
readonly end?: boolean;
|
|
75
119
|
}
|
|
76
120
|
|
|
121
|
+
// The body the daemon POSTs to a gateway's loopback /deliver: a message to post outside any live turn, into a channel by
|
|
122
|
+
// the provider's own id, and into the thread inside it the conversation lives in, when it lives in one.
|
|
123
|
+
export const ListenerDeliverSchema = z.object({
|
|
124
|
+
channelId: z.string().min(1),
|
|
125
|
+
text: z.string().min(1),
|
|
126
|
+
thread: z.string().min(1).optional(),
|
|
127
|
+
});
|
|
128
|
+
export type ListenerDeliver = z.infer<typeof ListenerDeliverSchema>;
|
|
129
|
+
|
|
130
|
+
// The body of POST /listeners/<provider>/stop: which conversation's running turn to stop, and who in the channel asked,
|
|
131
|
+
// as the gateway vouches for them. The daemon lets them only if the automation's sender rules would let them talk to it
|
|
132
|
+
// without a person's approval.
|
|
133
|
+
export const ListenerStopSchema = z.object({
|
|
134
|
+
conversationId: z.string().min(1),
|
|
135
|
+
author: z.object({ id: z.string(), name: z.string(), groups: z.array(z.string()).optional() }),
|
|
136
|
+
});
|
|
137
|
+
export type ListenerStop = z.infer<typeof ListenerStopSchema>;
|
|
138
|
+
|
|
139
|
+
// What the stop route answers: whether a turn was stopped, and the sentence to show the person when it was not.
|
|
140
|
+
export interface ListenerStopAnswer {
|
|
141
|
+
readonly stopped: boolean;
|
|
142
|
+
readonly reason?: string;
|
|
143
|
+
}
|
|
144
|
+
|
|
77
145
|
// Where a device-linking ceremony stands (whatsapp), reported per status tick; every state here means not yet paired.
|
|
78
146
|
// `since` stamps the current code's age, since WhatsApp reissues a fresh one on each reopen.
|
|
79
147
|
export const ListenerPairingSchema = z.object({
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
import { SyncStatusSchema } from "../../schemas/devices.js";
|
|
16
16
|
import { HostsListSchema } from "../../schemas/hosts.js";
|
|
17
17
|
import { VoicesViewSchema } from "../../schemas/voice.js";
|
|
18
|
-
import { MemberEmailSchema, MemberGrantSchema, MembersListSchema, MembersRosterSchema } from "../../schemas/members.js";
|
|
18
|
+
import { MemberEmailSchema, MemberGrantSchema, MemberSelfSchema, MembersListSchema, MembersRosterSchema } from "../../schemas/members.js";
|
|
19
19
|
import { PhonesListSchema, PhoneWakeRegistrationSchema } from "../../schemas/phone.js";
|
|
20
20
|
import { OkSchema } from "../../schemas/shared.js";
|
|
21
21
|
import { EnrollCodeAnswerSchema, EnrollCodeRequestSchema, PeerPairingAnswerSchema, SyncPairAnswerSchema, SyncPairRequestSchema } from "../../schemas/short-codes.js";
|
|
@@ -75,6 +75,7 @@ export const RAW_JSON_ROUTES = {
|
|
|
75
75
|
"POST /members": { input: MemberGrantSchema, output: MembersListSchema },
|
|
76
76
|
"DELETE /members": { input: MemberEmailSchema, output: MembersListSchema },
|
|
77
77
|
"DELETE /members/self": { output: OkSchema },
|
|
78
|
+
"GET /members/self": { output: MemberSelfSchema },
|
|
78
79
|
// Every environment write answers the environment after it. `/environment/contents` takes `?refresh` to probe again.
|
|
79
80
|
"GET /environment": { output: EnvironmentSchema },
|
|
80
81
|
"GET /environment/contents": { output: EnvironmentContentsSchema },
|
|
@@ -79,6 +79,8 @@ export const RAW_ROUTES = {
|
|
|
79
79
|
"POST /members": { control: "never" },
|
|
80
80
|
"DELETE /members": { control: "never" },
|
|
81
81
|
"DELETE /members/self": { floor: "viewer", guest: true, control: "never" },
|
|
82
|
+
// What one's own grant allows, read by the editor to offer only what will be taken.
|
|
83
|
+
"GET /members/self": { floor: "viewer", guest: true, control: "never" },
|
|
82
84
|
// The owner's Reconnect: register with the platform now, having it adopt the sandbox first when it holds no record.
|
|
83
85
|
"POST /platform/relink": { control: "never" },
|
|
84
86
|
"GET /environment": {},
|
|
@@ -126,6 +128,8 @@ export const RAW_ROUTES = {
|
|
|
126
128
|
// The `agents` CLI's children: start, follow up, answer (never a consent card), cancel, merge held work, list.
|
|
127
129
|
"POST /children/spawn": { agent: true, control: "never" },
|
|
128
130
|
"GET /children/providers": { agent: true, control: "never" },
|
|
131
|
+
// Marking a provider's model spent (or clearing the mark) by hand, for a plan that publishes no allowance to read.
|
|
132
|
+
"POST /children/providers/spent": { agent: true, control: "never" },
|
|
129
133
|
"POST /children/wait": { agent: true, control: "never" },
|
|
130
134
|
"POST /children/send": { agent: true, control: "never" },
|
|
131
135
|
"POST /children/answer": { agent: true, control: "never" },
|
|
@@ -153,6 +157,9 @@ export const RAW_ROUTES = {
|
|
|
153
157
|
"POST /listeners/{provider}/dispatch": { panel: false, control: "never" },
|
|
154
158
|
"POST /listeners/{provider}/failure": { panel: false, control: "never" },
|
|
155
159
|
"POST /listeners/{provider}/status": { panel: false, control: "never" },
|
|
160
|
+
// A person in the channel pressing Stop on a turn that channel started; the handler asks the automation's own
|
|
161
|
+
// sender rules whether that person may.
|
|
162
|
+
"POST /listeners/{provider}/stop": { panel: false, control: "never" },
|
|
156
163
|
// What an extension's own code asks about itself (extension-protocol.ts): its settings with their secrets, and its
|
|
157
164
|
// event stream. Every extension token reaches both undeclared, and each answers only about the extension asking, so
|
|
158
165
|
// no other credential reaches them; the handler resolves the caller from its token again.
|
package/src/schemas/activity.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// Activity audit log schemas (historyRoot/activity.jsonl).
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { AgentOriginSchema } from "./agent.js";
|
|
4
|
-
// One provider-agnostic event per agent↔provider interaction, appended only by the daemon; the log lives outside /work
|
|
5
|
-
//
|
|
4
|
+
// One provider-agnostic event per agent↔provider interaction, appended only by the daemon; the log lives outside /work.
|
|
5
|
+
// It is not tamper-evident (an unfenced agent can reach /history); the hash-chained audit trail (audit.ts) is.
|
|
6
6
|
|
|
7
7
|
export const ActivityEventSchema = z.object({
|
|
8
8
|
id: z.string().describe("The entry's own id."),
|
package/src/schemas/agent.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { TurnErrandSchema } from "./speaker.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { CONVERSATION_ID } from "../ids/conversation-ids.js";
|
|
4
|
+
import { canonicalHarness } from "../models/agent-catalog.js";
|
|
4
5
|
import { ModelRoleSchema } from "../models/model-roles.js";
|
|
5
6
|
import { NATIVE_PROVIDERS } from "../models/provider-specs.js";
|
|
6
7
|
import { AgentPlacementSchema } from "../protocol/doors/runner-protocol.js";
|
|
@@ -14,9 +15,14 @@ export type AgentProvider = z.infer<typeof AgentProviderSchema>;
|
|
|
14
15
|
// Provider naming a catalog in providers.contract.ts; an enum, not the open string above, since only native providers
|
|
15
16
|
// have a daemon-held catalog to look up. An unknown id is a 400 here, not an empty list.
|
|
16
17
|
export const NativeProviderParamSchema = z.object({ provider: z.enum(NATIVE_PROVIDERS) });
|
|
17
|
-
// The harness (agentic loop) a turn runs on, orthogonal to the provider.
|
|
18
|
-
|
|
18
|
+
// The harness (agentic loop) a turn runs on, orthogonal to the provider. `native` is an alias for whichever loop the
|
|
19
|
+
// provider runs by default; every other value names a loop outright. `vendor` is the provider's own agent where that is
|
|
20
|
+
// none of the named host loops (Cursor's SDK, the Devin CLI, an installed ACP agent). Which loops a provider offers is
|
|
21
|
+
// its row's `loops` (models/provider-specs.ts); values are only ever appended, since stored turns and pins carry them.
|
|
22
|
+
export const AgentHarnessSchema = z.enum(["native", "claude-code", "codex", "opencode", "pi", "muse", "vendor"]);
|
|
19
23
|
export type AgentHarness = z.infer<typeof AgentHarnessSchema>;
|
|
24
|
+
// A loop named outright, as a provider row offers it; `native` only ever resolves to one of these (loopOf).
|
|
25
|
+
export type AgentLoop = Exclude<AgentHarness, "native">;
|
|
20
26
|
// One repo at one immutable commit; used both as the checkout instruction and as handoffs' comparison base, so a
|
|
21
27
|
// multi-repo run has one provenance record instead of a hard-coded branch name.
|
|
22
28
|
export const RepoBaseSchema = z.object({ repo: z.string(), base: z.string().min(1) });
|
|
@@ -53,6 +59,9 @@ export const AgentOriginSchema = z.object({
|
|
|
53
59
|
provider: z.string(),
|
|
54
60
|
// External thread it arrived on (Discord channel id, widget conversation id); absent for webhooks.
|
|
55
61
|
channelId: z.string().optional(),
|
|
62
|
+
// The thread inside that channel (a Discord thread, a Slack thread's root ts), when the conversation is one; what is
|
|
63
|
+
// said back between turns goes into it rather than onto the channel.
|
|
64
|
+
threadId: z.string().optional(),
|
|
56
65
|
// Who sent it, as the source names them.
|
|
57
66
|
author: z.string().optional(),
|
|
58
67
|
});
|
|
@@ -169,8 +178,8 @@ const AgentTurnFieldsSchema = z.object({
|
|
|
169
178
|
),
|
|
170
179
|
// Which provider serves the turn, absent = claude; a sessionId resumes only on the provider that minted it.
|
|
171
180
|
agent: AgentProviderSchema.optional().describe("Which model provider serves this turn. Leave it out for Claude."),
|
|
172
|
-
// Which agentic loop runs the turn, absent = provider's own;
|
|
173
|
-
//
|
|
181
|
+
// Which agentic loop runs the turn, absent = provider's own; a loop the provider doesn't offer runs its own too
|
|
182
|
+
// (withRuntimeDefaults), rather than refusing a turn a stale composer setting named.
|
|
174
183
|
harness: AgentHarnessSchema.optional().describe("Which agentic loop runs the turn. Leave it out to use each provider's own."),
|
|
175
184
|
// Intent, not a guess: absent continues on the conversation's own account, or picks by serviceability where there is
|
|
176
185
|
// none on this provider yet (agent/providers/accounts/routing.ts).
|
|
@@ -376,13 +385,17 @@ export const AgentRunSchema = AgentTurnSchema.safeExtend({
|
|
|
376
385
|
export type RoutedAgentTurn = AgentTurn & { readonly agent: AgentProvider; readonly harness: AgentHarness };
|
|
377
386
|
export const DEFAULT_PROVIDER: AgentProvider = "claude";
|
|
378
387
|
export const DEFAULT_HARNESS: AgentHarness = "native";
|
|
388
|
+
// A loop the provider does not offer is not the loop that runs (capabilitiesOf answers its own runtime either way), so the
|
|
389
|
+
// turn is named for the one that does: one composer setting left on Claude Code must not stamp "claude-code" on a Cursor,
|
|
390
|
+
// Google or Devin turn's profile, transcript and ledger row.
|
|
391
|
+
// Naming the provider's default loop outright is the same turn as naming none, so it is stamped `native` too: one turn,
|
|
392
|
+
// one spelling, whichever way the request put it.
|
|
379
393
|
export const withRuntimeDefaults = <T extends { readonly agent?: AgentProvider | undefined; readonly harness?: AgentHarness | undefined }>(
|
|
380
394
|
turn: T,
|
|
381
|
-
): T & { readonly agent: AgentProvider; readonly harness: AgentHarness } =>
|
|
382
|
-
|
|
383
|
-
agent: turn.
|
|
384
|
-
|
|
385
|
-
});
|
|
395
|
+
): T & { readonly agent: AgentProvider; readonly harness: AgentHarness } => {
|
|
396
|
+
const agent = turn.agent ?? DEFAULT_PROVIDER;
|
|
397
|
+
return { ...turn, agent, harness: canonicalHarness(agent, turn.harness) };
|
|
398
|
+
};
|
|
386
399
|
// Which turn this is, as opposed to what it says: who serves it and how (agent, harness, account, model and its knobs),
|
|
387
400
|
// as whom (actsAs), where (isolated), whether anyone watches (unattended) and for which job (runRole). A turn that
|
|
388
401
|
// continues another (a resume, a wake, a nudge, a child's follow-up) carries this whole, never a hand-picked part.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export const AI_LITERACY_TEXT = {
|
|
4
|
+
en: "You are working with an AI assistant. It can be wrong or invent facts, legal references and quotations. Check important claims against their sources. You remain responsible for decisions and anything you send. Share only the personal data needed for the task. Follow your office's rules for using AI. Report problems to the sandbox owner.",
|
|
5
|
+
pl: "Pracujesz z asystentem AI. Może się mylić lub wymyślać fakty, przepisy i cytaty. Ważne informacje sprawdzaj w źródłach. Odpowiadasz za swoje decyzje i za to, co wysyłasz. Podawaj tylko dane osobowe potrzebne do zadania. Stosuj zasady korzystania z AI obowiązujące w urzędzie. Problemy zgłaszaj osobie, która zarządza tym sandboxem.",
|
|
6
|
+
de: "Sie arbeiten mit einem KI-Assistenten. Er kann Fehler machen oder Fakten, Rechtsverweise und Zitate erfinden. Prüfen Sie wichtige Aussagen anhand der Quellen. Sie sind für Entscheidungen und versendete Inhalte verantwortlich. Geben Sie nur personenbezogene Daten ein, die für die Aufgabe nötig sind. Beachten Sie die KI-Regeln Ihrer Dienststelle. Melden Sie Probleme der verantwortlichen Person für diesen Arbeitsbereich.",
|
|
7
|
+
fr: "Vous travaillez avec un assistant IA. Il peut se tromper ou inventer des faits, des références juridiques et des citations. Vérifiez les points importants dans les sources. Vous restez responsable de vos décisions et de ce que vous envoyez. Ne saisissez que les données personnelles nécessaires à la tâche. Respectez les règles de votre service sur l'IA. Signalez les problèmes au responsable de cet espace.",
|
|
8
|
+
es: "Trabaja con un asistente de IA. Puede equivocarse o inventar hechos, referencias legales y citas. Compruebe los datos importantes en sus fuentes. Usted sigue siendo responsable de sus decisiones y de lo que envía. Introduzca solo los datos personales necesarios para la tarea. Siga las normas de su oficina sobre el uso de IA. Informe de los problemas a quien administra este espacio.",
|
|
9
|
+
} as const;
|
|
10
|
+
export const AiLiteracyLocaleSchema = z.enum(["en", "pl", "de", "fr", "es"]);
|
|
11
|
+
// The read names its language in the path, which an OpenAPI route takes from an object keyed by the parameter.
|
|
12
|
+
export const AiLiteracyQuerySchema = z.object({ locale: AiLiteracyLocaleSchema });
|
|
13
|
+
export const AiLiteracyConfirmSchema = z.object({ version: z.string().length(64), locale: AiLiteracyLocaleSchema });
|
|
14
|
+
export const AiLiteracyStatusSchema = z.object({ show: z.boolean(), text: z.string(), version: z.string(), confirmedAt: z.string().optional() });
|
|
15
|
+
export const AiLiteracyRecordSchema = z.object({ member: z.string(), version: z.string(), confirmedAt: z.string() });
|
|
16
|
+
export type AiLiteracyRecord = z.infer<typeof AiLiteracyRecordSchema>;
|
|
17
|
+
export const AiLiteracyListSchema = z.object({ records: z.array(AiLiteracyRecordSchema) });
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// audit: the sandbox's audit trail (historyRoot/audit/), who changed a policy, a member, a secret's gate or a setting,
|
|
2
|
+
// who signed in, and (when `settings.audit.recordReads` is on) who read what. Separate from the activity log, which
|
|
3
|
+
// records what team-chat bots sent and received. Every record carries the hash of the one before it, so a record
|
|
4
|
+
// changed, removed or inserted afterwards breaks the chain at that point, and /audit/verify names where.
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
|
|
7
|
+
// Who did it, as the daemon verified it, never as a client said it. A member is named by the address they signed in
|
|
8
|
+
// with and the tier they held at that moment; a program by its control token; an agent by its conversation; the
|
|
9
|
+
// sandbox itself (a retention sweep, a boot) as `system`.
|
|
10
|
+
export const AuditActorSchema = z.discriminatedUnion("kind", [
|
|
11
|
+
z.object({
|
|
12
|
+
kind: z.literal("member"),
|
|
13
|
+
email: z.string().describe("The address they signed in with, lowercased."),
|
|
14
|
+
role: z.string().describe("Their tier at the moment they acted: owner, maintainer, writer, collaborator, viewer or guest."),
|
|
15
|
+
}),
|
|
16
|
+
z.object({
|
|
17
|
+
kind: z.literal("token"),
|
|
18
|
+
id: z.string().describe("The control token's id."),
|
|
19
|
+
label: z.string().describe("The name its owner gave the token."),
|
|
20
|
+
}),
|
|
21
|
+
z.object({ kind: z.literal("agent"), conversationId: z.string().describe("The conversation whose agent did it.") }),
|
|
22
|
+
z.object({
|
|
23
|
+
kind: z.literal("system"),
|
|
24
|
+
reason: z.string().optional().describe("What part of the sandbox acted, such as a retention sweep or a change found in a file."),
|
|
25
|
+
}),
|
|
26
|
+
]);
|
|
27
|
+
export type AuditActor = z.infer<typeof AuditActorSchema>;
|
|
28
|
+
|
|
29
|
+
export const AUDIT_ACTION_MAX = 120;
|
|
30
|
+
export const AUDIT_TARGET_MAX = 500;
|
|
31
|
+
|
|
32
|
+
// What a module hands services.audit.record: the daemon fills in the id, the time and the chain.
|
|
33
|
+
export const AuditEventSchema = z.object({
|
|
34
|
+
actor: AuditActorSchema,
|
|
35
|
+
action: z
|
|
36
|
+
.string()
|
|
37
|
+
.min(1)
|
|
38
|
+
.max(AUDIT_ACTION_MAX)
|
|
39
|
+
.describe("What happened, as a dotted name: settings.change, member.grant, secret.gate.set, auth.sign_in, conversation.read."),
|
|
40
|
+
target: z.string().max(AUDIT_TARGET_MAX).optional().describe("What it happened to: a member's address, a secret's name, a conversation id, a file's path."),
|
|
41
|
+
outcome: z.enum(["ok", "refused", "failed"]).optional().describe("How it ended. Absent means it succeeded."),
|
|
42
|
+
details: z
|
|
43
|
+
.record(z.string(), z.unknown())
|
|
44
|
+
.optional()
|
|
45
|
+
.describe("A few facts that make the record readable: which settings changed, a new role. Never a secret's value, and as little personal data as will do."),
|
|
46
|
+
});
|
|
47
|
+
export type AuditEvent = z.infer<typeof AuditEventSchema>;
|
|
48
|
+
|
|
49
|
+
// One line of a segment. `v` is the record format, so a later build that adds a field still verifies an older line:
|
|
50
|
+
// the hash is over exactly the fields the line holds.
|
|
51
|
+
export const AuditRecordSchema = AuditEventSchema.extend({
|
|
52
|
+
v: z.literal(1).describe("The record format."),
|
|
53
|
+
id: z.string().describe("The record's own id."),
|
|
54
|
+
at: z.number().describe("When it was written, in milliseconds since 1970. Strictly increasing, so it is also what you page by."),
|
|
55
|
+
prevHash: z.string().describe("The hash of the record before it, or of the checkpoint left by pruning, or 64 zeros for the first record ever written."),
|
|
56
|
+
hash: z.string().describe("SHA-256, in hex, over this record's canonical JSON without `hash` itself."),
|
|
57
|
+
});
|
|
58
|
+
export type AuditRecord = z.infer<typeof AuditRecordSchema>;
|
|
59
|
+
|
|
60
|
+
export const AuditQuerySchema = z.object({
|
|
61
|
+
from: z.coerce.number().optional().describe("Only records written at or after this time, in milliseconds."),
|
|
62
|
+
to: z.coerce.number().optional().describe("Only records written before this time, in milliseconds."),
|
|
63
|
+
action: z.string().max(AUDIT_ACTION_MAX).optional().describe("Only actions starting with this, such as `member.` or `settings.change`."),
|
|
64
|
+
actor: z.string().max(200).optional().describe("Only records whose actor's address, token name or conversation id contains this."),
|
|
65
|
+
target: z.string().max(AUDIT_TARGET_MAX).optional().describe("Only records whose target contains this."),
|
|
66
|
+
before: z.coerce.number().optional().describe("Paging cursor: only records written before this time. Pass the previous page's `next`."),
|
|
67
|
+
limit: z.coerce.number().int().min(1).max(500).default(100).describe("How many records to return."),
|
|
68
|
+
});
|
|
69
|
+
export type AuditQuery = z.infer<typeof AuditQuerySchema>;
|
|
70
|
+
|
|
71
|
+
export const AuditListSchema = z.object({
|
|
72
|
+
records: z.array(AuditRecordSchema).describe("The records, newest first."),
|
|
73
|
+
next: z.number().optional().describe("The cursor for the next, older page. Absent on the last page."),
|
|
74
|
+
});
|
|
75
|
+
export type AuditList = z.infer<typeof AuditListSchema>;
|
|
76
|
+
|
|
77
|
+
export const AuditVerifySchema = z.object({
|
|
78
|
+
ok: z.boolean().describe("Whether every record kept is intact and in an unbroken chain."),
|
|
79
|
+
checked: z.number().describe("How many records were checked."),
|
|
80
|
+
segments: z.number().describe("How many monthly files were checked."),
|
|
81
|
+
prunedThrough: z
|
|
82
|
+
.object({ at: z.number(), hash: z.string() })
|
|
83
|
+
.optional()
|
|
84
|
+
.describe("Where the kept chain starts: the time and hash of the last record retention deleted. Absent while nothing was ever deleted."),
|
|
85
|
+
broken: z
|
|
86
|
+
.object({
|
|
87
|
+
segment: z.string().describe("The monthly file it is in."),
|
|
88
|
+
line: z.number().describe("Its line number in that file, from 1."),
|
|
89
|
+
id: z.string().optional().describe("The record's id, when the line could still be read."),
|
|
90
|
+
at: z.number().optional().describe("When the record says it was written."),
|
|
91
|
+
reason: z
|
|
92
|
+
.enum(["unreadable", "hash-mismatch", "chain-break"])
|
|
93
|
+
.describe(
|
|
94
|
+
"unreadable: the line is not a record. hash-mismatch: the record was changed after it was written. chain-break: a record before it was removed, inserted or reordered.",
|
|
95
|
+
),
|
|
96
|
+
})
|
|
97
|
+
.optional()
|
|
98
|
+
.describe("The first record that does not check out. Absent when the chain is whole."),
|
|
99
|
+
torn: z.boolean().describe("Whether the newest file ends in a half-written line, which a crash mid-write leaves. It is skipped, not counted as tampering."),
|
|
100
|
+
});
|
|
101
|
+
export type AuditVerify = z.infer<typeof AuditVerifySchema>;
|
|
102
|
+
|
|
103
|
+
export const AuditExportQuerySchema = z.object({
|
|
104
|
+
from: z.coerce.number().describe("The first moment to include, in milliseconds."),
|
|
105
|
+
to: z.coerce.number().describe("The moment to stop before, in milliseconds."),
|
|
106
|
+
});
|
|
107
|
+
export type AuditExportQuery = z.infer<typeof AuditExportQuerySchema>;
|
|
108
|
+
|
|
109
|
+
export const AuditExportSchema = z.object({
|
|
110
|
+
filename: z.string().describe("A file name to save it under."),
|
|
111
|
+
jsonl: z.string().describe("The records, one JSON object per line, oldest first, exactly as stored, so the hashes still check out."),
|
|
112
|
+
count: z.number().describe("How many records it holds."),
|
|
113
|
+
});
|
|
114
|
+
export type AuditExport = z.infer<typeof AuditExportSchema>;
|
|
115
|
+
|
|
116
|
+
// The forwarder's state, so the settings page can say whether the outside copy is keeping up.
|
|
117
|
+
export const AuditStatusSchema = z.object({
|
|
118
|
+
forwarding: z
|
|
119
|
+
.object({
|
|
120
|
+
url: z.string().describe("Where records are sent."),
|
|
121
|
+
pending: z.number().describe("How many records are waiting to be sent."),
|
|
122
|
+
lastSentAt: z.number().optional().describe("When the last record reached it."),
|
|
123
|
+
lastError: z.string().optional().describe("Why the last attempt failed, when it did."),
|
|
124
|
+
nextAttemptAt: z.number().optional().describe("When the next attempt is due, while it is backing off."),
|
|
125
|
+
})
|
|
126
|
+
.optional()
|
|
127
|
+
.describe("Absent while no forward address is set."),
|
|
128
|
+
oldestAt: z.number().optional().describe("When the oldest record kept was written."),
|
|
129
|
+
segments: z.array(z.string()).describe("The monthly files kept, oldest first."),
|
|
130
|
+
});
|
|
131
|
+
export type AuditStatus = z.infer<typeof AuditStatusSchema>;
|
|
@@ -253,6 +253,17 @@ export const WebchatConfigSchema = z.object({
|
|
|
253
253
|
// How long a visitor thread resumes the same conversation before a new message starts a fresh one; absent uses the
|
|
254
254
|
// daemon's own default.
|
|
255
255
|
sessionTtlMinutes: z.number().int().positive().optional(),
|
|
256
|
+
// The AI Act (Art. 50(1)) has a person told they are talking to an AI unless it is obvious. Absent means shown, since
|
|
257
|
+
// a one-line label costs nothing and leaving it out is the decision that needs making on purpose.
|
|
258
|
+
aiDisclosure: z
|
|
259
|
+
.boolean()
|
|
260
|
+
.optional()
|
|
261
|
+
.describe("Tell visitors, in the panel, that the replies come from an AI. Absent means on."),
|
|
262
|
+
aiDisclosureText: z
|
|
263
|
+
.string()
|
|
264
|
+
.max(300)
|
|
265
|
+
.optional()
|
|
266
|
+
.describe("Your own wording for that line. Absent uses the built-in one in the visitor's language."),
|
|
256
267
|
});
|
|
257
268
|
export type WebchatConfig = z.infer<typeof WebchatConfigSchema>;
|
|
258
269
|
// Default daily agent-turn ceiling for an unconfigured Visitor chat; high enough for real traffic, low enough to blunt a
|
|
@@ -276,6 +287,8 @@ export const WebchatPublicConfigSchema = z.object({
|
|
|
276
287
|
antiBot: z.enum(["turnstile", "pow", "off"]),
|
|
277
288
|
turnstileSiteKey: z.string().optional(),
|
|
278
289
|
googleClientId: z.string().optional(),
|
|
290
|
+
aiDisclosure: z.boolean(),
|
|
291
|
+
aiDisclosureText: z.string().optional(),
|
|
279
292
|
});
|
|
280
293
|
export type WebchatPublicConfig = z.infer<typeof WebchatPublicConfigSchema>;
|
|
281
294
|
// A proof-of-work challenge: find a nonce whose SHA-256 of `${salt}:${nonce}` starts with `difficulty` zero bits. One
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
// compliance: what a data protection officer (in Poland, the IOD) needs before a sandbox handles anyone's personal data,
|
|
2
|
+
// read off the sandbox's LIVE configuration rather than written from memory. Three answers: where data can leave
|
|
3
|
+
// (`flows`), what protects it (`measures`), and the documents an office files about it (the record of processing under
|
|
4
|
+
// GDPR Art. 30, the impact assessment under Art. 35, the technical and organisational measures under Art. 32). Plus the
|
|
5
|
+
// public-office preset: a list of stricter settings, each shown as current → proposed, applied only item by item on
|
|
6
|
+
// request. Nothing here changes a default; a sandbox that never opens the page behaves as it did before it existed.
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
|
|
9
|
+
export const ComplianceLanguageSchema = z.enum(["pl", "en"]);
|
|
10
|
+
export type ComplianceLanguage = z.infer<typeof ComplianceLanguageSchema>;
|
|
11
|
+
|
|
12
|
+
// The groups the data-flow page draws, in this order.
|
|
13
|
+
export const DATA_FLOW_GROUPS = ["models", "messaging", "connectors", "speech", "web", "platform", "maintenance", "analytics"] as const;
|
|
14
|
+
export const DataFlowGroupSchema = z.enum(DATA_FLOW_GROUPS);
|
|
15
|
+
export type DataFlowGroup = z.infer<typeof DataFlowGroupSchema>;
|
|
16
|
+
|
|
17
|
+
// One kind per outbound path the code has; the editor and the documents both word each kind in the reader's language,
|
|
18
|
+
// so the daemon sends the kind and the facts, never a sentence.
|
|
19
|
+
export const DATA_FLOW_KINDS = [
|
|
20
|
+
"model-provider",
|
|
21
|
+
"model-endpoint",
|
|
22
|
+
"local-model",
|
|
23
|
+
"email-imap",
|
|
24
|
+
"email-smtp",
|
|
25
|
+
"team-chat",
|
|
26
|
+
"webchat",
|
|
27
|
+
"connector",
|
|
28
|
+
"speech",
|
|
29
|
+
"web-search",
|
|
30
|
+
"web-fetch",
|
|
31
|
+
"browser",
|
|
32
|
+
"platform-link",
|
|
33
|
+
"ingress",
|
|
34
|
+
"update-check",
|
|
35
|
+
"model-download",
|
|
36
|
+
"product-analytics",
|
|
37
|
+
"push-notifications",
|
|
38
|
+
] as const;
|
|
39
|
+
export const DataFlowKindSchema = z.enum(DATA_FLOW_KINDS);
|
|
40
|
+
export type DataFlowKind = z.infer<typeof DataFlowKindSchema>;
|
|
41
|
+
|
|
42
|
+
// Whether personal data can ride this path: `yes` when it carries what people wrote, `possible` when only what an agent
|
|
43
|
+
// chose to send or look up, `no` when it carries only the sandbox's own metadata.
|
|
44
|
+
export const PersonalDataReachSchema = z.enum(["yes", "possible", "no"]);
|
|
45
|
+
export type PersonalDataReach = z.infer<typeof PersonalDataReachSchema>;
|
|
46
|
+
|
|
47
|
+
// What the privacy shield does on this path: `masked` (personal data becomes tokens before it leaves), `trusted` (the
|
|
48
|
+
// shield is on but lets this destination read values as they are), `watched` (the shield only records), `local` (it
|
|
49
|
+
// never leaves the machine), `blocked` (the shield is on and refuses what it cannot read, so nothing is sent unmasked),
|
|
50
|
+
// `none` (the shield does not sit on this path, or is off).
|
|
51
|
+
export const ShieldCoverSchema = z.enum(["masked", "trusted", "watched", "local", "blocked", "none"]);
|
|
52
|
+
export type ShieldCover = z.infer<typeof ShieldCoverSchema>;
|
|
53
|
+
|
|
54
|
+
export const DataFlowSchema = z.object({
|
|
55
|
+
id: z.string().describe("Stable id of this path, such as `model-provider:claude` or `team-chat:discord`."),
|
|
56
|
+
group: DataFlowGroupSchema,
|
|
57
|
+
kind: DataFlowKindSchema,
|
|
58
|
+
name: z.string().describe("The destination's own name, such as Anthropic Claude or a connector's title. Proper nouns only."),
|
|
59
|
+
hosts: z.array(z.string()).describe("The hosts data is sent to, as configured. Empty when the code cannot know them in advance."),
|
|
60
|
+
operator: z.string().optional().describe("Who runs the destination, where known."),
|
|
61
|
+
region: z.string().optional().describe("Where it is processed, as far as the operator says: `EU`, `US`, `this machine`, or a hosting region."),
|
|
62
|
+
thirdCountry: z
|
|
63
|
+
.boolean()
|
|
64
|
+
.optional()
|
|
65
|
+
.describe("True when the operator processes the data outside the European Economic Area, which makes this a transfer to a third country."),
|
|
66
|
+
active: z.boolean().describe("Whether this path is in use now. An inactive one is listed because a single switch would open it."),
|
|
67
|
+
personalData: PersonalDataReachSchema,
|
|
68
|
+
shield: ShieldCoverSchema,
|
|
69
|
+
switch: z
|
|
70
|
+
.string()
|
|
71
|
+
.optional()
|
|
72
|
+
.describe("What opens or closes this path: a settings key (`productAnalytics`), a page, or an environment variable."),
|
|
73
|
+
question: z
|
|
74
|
+
.string()
|
|
75
|
+
.optional()
|
|
76
|
+
.describe("Set when the sandbox cannot tell something about this path and the office must answer it, as a short key the editor words."),
|
|
77
|
+
});
|
|
78
|
+
export type DataFlow = z.infer<typeof DataFlowSchema>;
|
|
79
|
+
|
|
80
|
+
// `on`: in place. `partial`: in place for some of what it covers. `off`: available, switched off. `unavailable`: this
|
|
81
|
+
// sandbox cannot offer it as it runs now. `unknown`: outside what the sandbox can see (backups), so the office says.
|
|
82
|
+
export const MeasureStatusSchema = z.enum(["on", "partial", "off", "unavailable", "unknown"]);
|
|
83
|
+
export type MeasureStatus = z.infer<typeof MeasureStatusSchema>;
|
|
84
|
+
|
|
85
|
+
export const MEASURE_IDS = [
|
|
86
|
+
"privacy-shield",
|
|
87
|
+
"privacy-images",
|
|
88
|
+
"privacy-names",
|
|
89
|
+
"trusted-providers",
|
|
90
|
+
"local-models",
|
|
91
|
+
"member-roles",
|
|
92
|
+
"areas",
|
|
93
|
+
"guest-jail",
|
|
94
|
+
"sign-in",
|
|
95
|
+
"action-rules",
|
|
96
|
+
"admission",
|
|
97
|
+
"command-judge",
|
|
98
|
+
"credential-gates",
|
|
99
|
+
"host-guards",
|
|
100
|
+
"retention",
|
|
101
|
+
"audit",
|
|
102
|
+
"audit-reads",
|
|
103
|
+
"audit-forward",
|
|
104
|
+
"encryption-at-rest",
|
|
105
|
+
"encryption-in-transit",
|
|
106
|
+
"agent-domain",
|
|
107
|
+
"product-analytics",
|
|
108
|
+
"ai-notice",
|
|
109
|
+
"webchat-ai-disclosure",
|
|
110
|
+
"backups",
|
|
111
|
+
] as const;
|
|
112
|
+
export const MeasureIdSchema = z.enum(MEASURE_IDS);
|
|
113
|
+
export type MeasureId = z.infer<typeof MeasureIdSchema>;
|
|
114
|
+
|
|
115
|
+
// The TOMs categories of GDPR Art. 32(1) the documents sort measures under.
|
|
116
|
+
export const MeasureCategorySchema = z.enum(["confidentiality", "minimisation", "integrity", "accountability", "transparency", "availability"]);
|
|
117
|
+
export type MeasureCategory = z.infer<typeof MeasureCategorySchema>;
|
|
118
|
+
|
|
119
|
+
export const MeasureSchema = z.object({
|
|
120
|
+
id: MeasureIdSchema,
|
|
121
|
+
category: MeasureCategorySchema,
|
|
122
|
+
status: MeasureStatusSchema,
|
|
123
|
+
facts: z
|
|
124
|
+
.record(z.string(), z.union([z.string(), z.number(), z.boolean()]))
|
|
125
|
+
.describe("The configured values behind the status, such as `{ mode: \"on\", trusted: 2 }`, for the reader to word."),
|
|
126
|
+
preset: z.boolean().describe("The public-office preset changes this measure."),
|
|
127
|
+
});
|
|
128
|
+
export type Measure = z.infer<typeof MeasureSchema>;
|
|
129
|
+
|
|
130
|
+
export const ComplianceReportSchema = z.object({
|
|
131
|
+
generatedAt: z.string().describe("When this was read, as an ISO timestamp."),
|
|
132
|
+
flows: z.array(DataFlowSchema),
|
|
133
|
+
measures: z.array(MeasureSchema),
|
|
134
|
+
});
|
|
135
|
+
export type ComplianceReport = z.infer<typeof ComplianceReportSchema>;
|
|
136
|
+
|
|
137
|
+
export const ComplianceDocumentKindSchema = z.enum(["ropa", "dpia", "toms"]);
|
|
138
|
+
export type ComplianceDocumentKind = z.infer<typeof ComplianceDocumentKindSchema>;
|
|
139
|
+
export const ComplianceDocumentFormatSchema = z.enum(["md", "docx"]);
|
|
140
|
+
export type ComplianceDocumentFormat = z.infer<typeof ComplianceDocumentFormatSchema>;
|
|
141
|
+
|
|
142
|
+
export const ComplianceDocumentQuerySchema = z.object({
|
|
143
|
+
kind: ComplianceDocumentKindSchema.describe("ropa: the record of processing activities entry. dpia: the impact assessment draft. toms: the measures."),
|
|
144
|
+
language: ComplianceLanguageSchema.default("pl").describe("pl or en."),
|
|
145
|
+
format: ComplianceDocumentFormatSchema.default("md").describe("md for Markdown text, docx for a Word document."),
|
|
146
|
+
});
|
|
147
|
+
export type ComplianceDocumentQuery = z.input<typeof ComplianceDocumentQuerySchema>;
|
|
148
|
+
|
|
149
|
+
export const ComplianceDocumentSchema = z.object({
|
|
150
|
+
filename: z.string(),
|
|
151
|
+
mediaType: z.string(),
|
|
152
|
+
encoding: z.enum(["utf8", "base64"]).describe("utf8 for Markdown, base64 for a Word document's bytes."),
|
|
153
|
+
content: z.string(),
|
|
154
|
+
});
|
|
155
|
+
export type ComplianceDocument = z.infer<typeof ComplianceDocumentSchema>;
|
|
156
|
+
|
|
157
|
+
// The preset. Each item is one setting, keyed by where it lives: `privacy.mode`, `retention.transcriptDays`,
|
|
158
|
+
// `actionRules.discord.message.send`, `admission.webchat`. A value absent today (an action rule nobody wrote) is null.
|
|
159
|
+
export const PresetValueSchema = z.union([z.string(), z.number(), z.boolean(), z.null()]);
|
|
160
|
+
export type PresetValue = z.infer<typeof PresetValueSchema>;
|
|
161
|
+
|
|
162
|
+
export const PRESET_GROUPS = ["privacy", "retention", "audit", "analytics", "notice", "actions", "admission", "judge"] as const;
|
|
163
|
+
export const PresetGroupSchema = z.enum(PRESET_GROUPS);
|
|
164
|
+
export type PresetGroup = z.infer<typeof PresetGroupSchema>;
|
|
165
|
+
|
|
166
|
+
export const PresetItemSchema = z.object({
|
|
167
|
+
id: z.string(),
|
|
168
|
+
group: PresetGroupSchema,
|
|
169
|
+
current: PresetValueSchema,
|
|
170
|
+
proposed: PresetValueSchema,
|
|
171
|
+
changes: z.boolean().describe("Applying it would change something; false when the sandbox already has the proposed value."),
|
|
172
|
+
available: z.boolean().describe("It can be applied here. False when what it needs is missing, such as a local name model."),
|
|
173
|
+
owner: z.boolean().describe("Only the owner may apply it, since it changes the privacy shield."),
|
|
174
|
+
});
|
|
175
|
+
export type PresetItem = z.infer<typeof PresetItemSchema>;
|
|
176
|
+
|
|
177
|
+
export const PresetPreviewSchema = z.object({ items: z.array(PresetItemSchema) });
|
|
178
|
+
export type PresetPreview = z.infer<typeof PresetPreviewSchema>;
|
|
179
|
+
|
|
180
|
+
export const PRESET_APPLY_MAX = 200;
|
|
181
|
+
export const PresetApplySchema = z.object({
|
|
182
|
+
items: z
|
|
183
|
+
.array(z.string().min(1).max(200))
|
|
184
|
+
.min(1)
|
|
185
|
+
.max(PRESET_APPLY_MAX)
|
|
186
|
+
.describe("The ids of the items to apply, as the preview lists them. Anything not named is left as it is."),
|
|
187
|
+
});
|
|
188
|
+
export type PresetApply = z.infer<typeof PresetApplySchema>;
|
|
189
|
+
|
|
190
|
+
export const PresetSkipReasonSchema = z.enum(["unknown", "unavailable", "unchanged", "owner"]);
|
|
191
|
+
export const PresetApplyResultSchema = z.object({
|
|
192
|
+
applied: z.array(z.string()),
|
|
193
|
+
skipped: z.array(z.object({ id: z.string(), reason: PresetSkipReasonSchema })),
|
|
194
|
+
});
|
|
195
|
+
export type PresetApplyResult = z.infer<typeof PresetApplyResultSchema>;
|
|
@@ -10,6 +10,7 @@ const answer: DaemonHealth = {
|
|
|
10
10
|
reach: { state: "reachable", at: 3 },
|
|
11
11
|
reachedBy: "tunnel",
|
|
12
12
|
build: { version: "1.2.3" },
|
|
13
|
+
atRestEncryption: { active: false, stores: [] },
|
|
13
14
|
};
|
|
14
15
|
|
|
15
16
|
test("the daemon's answer parses whole as the contract's health", () => {
|
|
@@ -23,6 +24,7 @@ test("a reader takes any daemon's answer, a field of the wrong shape reading as
|
|
|
23
24
|
boot: undefined,
|
|
24
25
|
state: { journal: undefined },
|
|
25
26
|
reachedBy: "tunnel",
|
|
27
|
+
atRestEncryption: undefined,
|
|
26
28
|
});
|
|
27
29
|
expect(DaemonHealthReadingSchema.parse(answer)).toMatchObject({ sandboxId: "0123456789ab", boot: { ready: true }, announce: { state: "registered" } });
|
|
28
30
|
});
|