@intentic/sandbox-contract 1.337.0 → 1.338.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 +36 -0
- package/dist/chores/probes.d.ts.map +1 -1
- package/dist/chores/probes.js +4 -7
- package/dist/chores/probes.js.map +1 -1
- package/dist/contracts/agent.contract.d.ts +32 -23
- package/dist/contracts/agent.contract.d.ts.map +1 -1
- package/dist/contracts/agents.contract.d.ts +6016 -5994
- package/dist/contracts/agents.contract.d.ts.map +1 -1
- package/dist/contracts/agents.contract.js +3 -0
- package/dist/contracts/agents.contract.js.map +1 -1
- package/dist/contracts/approvals.contract.d.ts +8 -0
- package/dist/contracts/approvals.contract.d.ts.map +1 -1
- package/dist/contracts/automations.contract.d.ts +3 -3
- package/dist/contracts/capabilities.contract.d.ts +28 -0
- package/dist/contracts/capabilities.contract.d.ts.map +1 -1
- package/dist/contracts/capabilities.contract.js +11 -1
- package/dist/contracts/capabilities.contract.js.map +1 -1
- package/dist/contracts/device.contract.d.ts +225 -0
- package/dist/contracts/device.contract.d.ts.map +1 -1
- package/dist/contracts/device.contract.js +10 -0
- package/dist/contracts/device.contract.js.map +1 -1
- package/dist/contracts/extensions.contract.d.ts +16 -0
- package/dist/contracts/extensions.contract.d.ts.map +1 -1
- package/dist/contracts/needs.contract.d.ts +22 -7
- package/dist/contracts/needs.contract.d.ts.map +1 -1
- package/dist/contracts/org.contract.d.ts +35 -0
- package/dist/contracts/org.contract.d.ts.map +1 -0
- package/dist/contracts/org.contract.js +14 -0
- package/dist/contracts/org.contract.js.map +1 -0
- package/dist/contracts/privacy.contract.d.ts +115 -0
- package/dist/contracts/privacy.contract.d.ts.map +1 -1
- package/dist/contracts/privacy.contract.js +11 -2
- package/dist/contracts/privacy.contract.js.map +1 -1
- package/dist/contracts/runner.contract.d.ts +11 -7
- package/dist/contracts/runner.contract.d.ts.map +1 -1
- package/dist/contracts/secrets.contract.d.ts +85 -0
- package/dist/contracts/secrets.contract.d.ts.map +1 -1
- package/dist/contracts/secrets.contract.js +48 -1
- package/dist/contracts/secrets.contract.js.map +1 -1
- package/dist/contracts/sessions.contract.d.ts +4 -1
- package/dist/contracts/sessions.contract.d.ts.map +1 -1
- package/dist/contracts/settings.contract.d.ts +11 -3
- package/dist/contracts/settings.contract.d.ts.map +1 -1
- package/dist/contracts/system.contract.d.ts +20 -2
- package/dist/contracts/system.contract.d.ts.map +1 -1
- package/dist/events/agent-events.d.ts +17 -4
- package/dist/events/agent-events.d.ts.map +1 -1
- package/dist/events/requests.d.ts +4 -0
- package/dist/events/requests.d.ts.map +1 -1
- package/dist/events/requests.js +1 -1
- package/dist/events/requests.js.map +1 -1
- package/dist/events/system-events.d.ts +4 -0
- package/dist/events/system-events.d.ts.map +1 -1
- package/dist/events/system-events.js +2 -0
- package/dist/events/system-events.js.map +1 -1
- package/dist/events/transcript.d.ts +27 -6
- package/dist/events/transcript.d.ts.map +1 -1
- package/dist/index.d.ts +7555 -7426
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +31 -257
- package/dist/index.js.map +1 -1
- package/dist/policy/identity-assertion.d.ts.map +1 -1
- package/dist/policy/identity-assertion.js +2 -8
- package/dist/policy/identity-assertion.js.map +1 -1
- package/dist/policy/keyring-seal.d.ts +48 -0
- package/dist/policy/keyring-seal.d.ts.map +1 -0
- package/dist/policy/keyring-seal.js +94 -0
- package/dist/policy/keyring-seal.js.map +1 -0
- package/dist/policy/message-limits.d.ts +2 -0
- package/dist/policy/message-limits.d.ts.map +1 -1
- package/dist/policy/message-limits.js +2 -0
- package/dist/policy/message-limits.js.map +1 -1
- package/dist/policy/org-locks.d.ts +58 -0
- package/dist/policy/org-locks.d.ts.map +1 -0
- package/dist/policy/org-locks.js +113 -0
- package/dist/policy/org-locks.js.map +1 -0
- package/dist/policy/reserved-servers.d.ts.map +1 -1
- package/dist/policy/reserved-servers.js +1 -1
- package/dist/policy/reserved-servers.js.map +1 -1
- package/dist/protocol/doors/webext-links.d.ts +3 -0
- package/dist/protocol/doors/webext-links.d.ts.map +1 -1
- package/dist/protocol/doors/webext-links.js +3 -0
- package/dist/protocol/doors/webext-links.js.map +1 -1
- package/dist/protocol/raw/raw-json-routes.d.ts +269 -0
- package/dist/protocol/raw/raw-json-routes.d.ts.map +1 -1
- package/dist/protocol/raw/raw-json-routes.js +14 -1
- package/dist/protocol/raw/raw-json-routes.js.map +1 -1
- package/dist/protocol/raw/raw-routes.d.ts +70 -0
- package/dist/protocol/raw/raw-routes.d.ts.map +1 -1
- package/dist/protocol/raw/raw-routes.js +18 -0
- package/dist/protocol/raw/raw-routes.js.map +1 -1
- package/dist/protocol/route-meta.d.ts.map +1 -1
- package/dist/protocol/route-meta.js.map +1 -1
- package/dist/protocol/routes.d.ts +4 -0
- package/dist/protocol/routes.d.ts.map +1 -1
- package/dist/protocol/routes.js +12 -0
- package/dist/protocol/routes.js.map +1 -1
- package/dist/schemas/agents.d.ts +1 -0
- package/dist/schemas/agents.d.ts.map +1 -1
- package/dist/schemas/agents.js +1 -0
- package/dist/schemas/agents.js.map +1 -1
- package/dist/schemas/approvals.d.ts +20 -0
- package/dist/schemas/approvals.d.ts.map +1 -1
- package/dist/schemas/approvals.js +10 -0
- package/dist/schemas/approvals.js.map +1 -1
- package/dist/schemas/capabilities.d.ts +26 -0
- package/dist/schemas/capabilities.d.ts.map +1 -1
- package/dist/schemas/capabilities.js +17 -0
- package/dist/schemas/capabilities.js.map +1 -1
- package/dist/schemas/data-protection.d.ts +5 -0
- package/dist/schemas/data-protection.d.ts.map +1 -1
- package/dist/schemas/data-protection.js +29 -0
- package/dist/schemas/data-protection.js.map +1 -1
- package/dist/schemas/devices.d.ts +11 -0
- package/dist/schemas/devices.d.ts.map +1 -1
- package/dist/schemas/devices.js +2 -1
- package/dist/schemas/devices.js.map +1 -1
- package/dist/schemas/extension-updates.d.ts +32 -0
- package/dist/schemas/extension-updates.d.ts.map +1 -1
- package/dist/schemas/git/git.d.ts +19 -0
- package/dist/schemas/git/git.d.ts.map +1 -1
- package/dist/schemas/git/git.js +12 -1
- package/dist/schemas/git/git.js.map +1 -1
- package/dist/schemas/hosts.d.ts +2 -0
- package/dist/schemas/hosts.d.ts.map +1 -1
- package/dist/schemas/hosts.js +11 -1
- package/dist/schemas/hosts.js.map +1 -1
- package/dist/schemas/keyring.d.ts +421 -0
- package/dist/schemas/keyring.d.ts.map +1 -0
- package/dist/schemas/keyring.js +176 -0
- package/dist/schemas/keyring.js.map +1 -0
- package/dist/schemas/links.d.ts +208 -0
- package/dist/schemas/links.d.ts.map +1 -0
- package/dist/schemas/links.js +116 -0
- package/dist/schemas/links.js.map +1 -0
- package/dist/schemas/maintenance.d.ts.map +1 -1
- package/dist/schemas/maintenance.js.map +1 -1
- package/dist/schemas/members.d.ts.map +1 -1
- package/dist/schemas/members.js +2 -0
- package/dist/schemas/members.js.map +1 -1
- package/dist/schemas/needs.d.ts +25 -8
- package/dist/schemas/needs.d.ts.map +1 -1
- package/dist/schemas/needs.js +15 -3
- package/dist/schemas/needs.js.map +1 -1
- package/dist/schemas/organisation.d.ts +106 -0
- package/dist/schemas/organisation.d.ts.map +1 -0
- package/dist/schemas/organisation.js +52 -0
- package/dist/schemas/organisation.js.map +1 -0
- package/dist/schemas/privacy.d.ts +232 -0
- package/dist/schemas/privacy.d.ts.map +1 -1
- package/dist/schemas/privacy.js +154 -4
- package/dist/schemas/privacy.js.map +1 -1
- package/dist/schemas/secrets.d.ts +151 -0
- package/dist/schemas/secrets.d.ts.map +1 -1
- package/dist/schemas/secrets.js +71 -8
- package/dist/schemas/secrets.js.map +1 -1
- package/dist/schemas/settings.d.ts +8 -0
- package/dist/schemas/settings.d.ts.map +1 -1
- package/dist/schemas/settings.js +4 -0
- package/dist/schemas/settings.js.map +1 -1
- package/dist/schemas/speech.d.ts +12 -0
- package/dist/schemas/speech.d.ts.map +1 -1
- package/dist/schemas/speech.js +23 -0
- package/dist/schemas/speech.js.map +1 -1
- package/dist/schemas/terminal.d.ts +15 -0
- package/dist/schemas/terminal.d.ts.map +1 -1
- package/dist/schemas/terminal.js +11 -2
- package/dist/schemas/terminal.js.map +1 -1
- package/dist/schemas/voice.d.ts +3 -0
- package/dist/schemas/voice.d.ts.map +1 -1
- package/dist/schemas/voice.js +7 -0
- package/dist/schemas/voice.js.map +1 -1
- package/dist/schemas/webext.d.ts +17 -0
- package/dist/schemas/webext.d.ts.map +1 -1
- package/dist/schemas/webext.js +7 -1
- package/dist/schemas/webext.js.map +1 -1
- package/dist/state/contract-lock.d.ts +3 -1
- package/dist/state/contract-lock.d.ts.map +1 -1
- package/dist/state/contract-lock.js +34 -9
- package/dist/state/contract-lock.js.map +1 -1
- package/dist/state/contract-modules.d.ts +4 -0
- package/dist/state/contract-modules.d.ts.map +1 -0
- package/dist/state/contract-modules.js +28 -0
- package/dist/state/contract-modules.js.map +1 -0
- package/dist/state/daemon-floor.d.ts +4 -0
- package/dist/state/daemon-floor.d.ts.map +1 -0
- package/dist/state/daemon-floor.js +8 -0
- package/dist/state/daemon-floor.js.map +1 -0
- package/dist/state/definition.d.ts +34 -0
- 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 +7 -0
- package/dist/state/history-state.js.map +1 -1
- package/dist/text/documents.d.ts +3 -1
- package/dist/text/documents.d.ts.map +1 -1
- package/dist/text/documents.js +30 -0
- package/dist/text/documents.js.map +1 -1
- package/dist/text/pages.d.ts.map +1 -1
- package/dist/text/pages.js +3 -7
- package/dist/text/pages.js.map +1 -1
- package/package.json +27 -5
- package/src/chores/probes.ts +4 -6
- package/src/contracts/agents.contract.ts +5 -0
- package/src/contracts/capabilities.contract.ts +14 -0
- package/src/contracts/device.contract.ts +30 -0
- package/src/contracts/org.contract.ts +19 -0
- package/src/contracts/privacy.contract.ts +13 -1
- package/src/contracts/secrets.contract.ts +64 -0
- package/src/events/requests.ts +3 -1
- package/src/events/system-events.ts +9 -0
- package/src/index.ts +41 -279
- package/src/policy/identity-assertion.ts +2 -8
- package/src/policy/keyring-seal.test.ts +93 -0
- package/src/policy/keyring-seal.ts +148 -0
- package/src/policy/message-limits.ts +3 -0
- package/src/policy/org-locks.ts +173 -0
- package/src/policy/reserved-servers.ts +3 -2
- package/src/policy/tests/org-locks.test.ts +92 -0
- package/src/policy/tests/reserved-servers.test.ts +3 -2
- package/src/protocol/doors/listener-protocol.ts +1 -1
- package/src/protocol/doors/webext-links.ts +13 -0
- package/src/protocol/ingress-contract.fixture.json +2 -0
- package/src/protocol/pairing-redeem.ts +1 -1
- package/src/protocol/peer-dial.test.ts +6 -6
- package/src/protocol/peer-mcp-server.test.ts +2 -1
- package/src/protocol/raw/raw-json-routes.ts +31 -1
- package/src/protocol/raw/raw-routes.ts +35 -1
- package/src/protocol/route-meta.ts +2 -1
- package/src/protocol/routes.test.ts +33 -1
- package/src/protocol/routes.ts +18 -0
- package/src/protocol/sse.ts +1 -1
- package/src/schemas/agent.ts +1 -1
- package/src/schemas/agents.ts +1 -0
- package/src/schemas/approvals.ts +14 -0
- package/src/schemas/capabilities.ts +25 -0
- package/src/schemas/data-protection.ts +47 -0
- package/src/schemas/devices.ts +8 -1
- package/src/schemas/git/git.ts +22 -2
- package/src/schemas/hosts.ts +14 -1
- package/src/schemas/keyring.ts +277 -0
- package/src/schemas/links.ts +180 -0
- package/src/schemas/maintenance.ts +1 -0
- package/src/schemas/members.ts +4 -0
- package/src/schemas/needs.ts +21 -5
- package/src/schemas/organisation.ts +110 -0
- package/src/schemas/privacy.test.ts +63 -0
- package/src/schemas/privacy.ts +213 -5
- package/src/schemas/secrets.ts +96 -8
- package/src/schemas/settings.ts +8 -0
- package/src/schemas/speech.ts +38 -1
- package/src/schemas/terminal.ts +13 -2
- package/src/schemas/voice.ts +11 -0
- package/src/schemas/webext.ts +16 -1
- package/src/state/contract-lock.test.ts +12 -12
- package/src/state/contract-lock.ts +43 -12
- package/src/state/contract-modules.ts +41 -0
- package/src/state/daemon-floor.test.ts +81 -0
- package/src/state/daemon-floor.ts +32 -0
- package/src/state/history-state.ts +14 -0
- package/src/state/workspace-state.ts +1 -1
- package/src/text/documents.test.ts +30 -1
- package/src/text/documents.ts +43 -1
- package/src/text/pages.ts +5 -7
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
// Secrets that live in a keyring on the owner's own computer (`intentic-keyring`, a process of its own beside
|
|
4
|
+
// intentic-machine) rather than in the sandbox. The sandbox holds a POINTER to each one shared with it, never the value,
|
|
5
|
+
// and asks for a value by sending a grant it signed: the machine relays the grant to the keyring and the answer back,
|
|
6
|
+
// and the answer is sealed to whoever the grant names, so the machine carries only ciphertext. What a grant may ask for
|
|
7
|
+
// is the keyring's to decide (its share mode, its program list, a person's tap); the sandbox's own gates run first.
|
|
8
|
+
// The sealing and signing themselves are policy/keyring-seal.ts.
|
|
9
|
+
|
|
10
|
+
// Raw 32-byte keys travel as base64url without padding, the `x` of their JWK: X25519 for sealing, Ed25519 for signing.
|
|
11
|
+
export const KEYRING_KEY_RE = /^[A-Za-z0-9_-]{43}$/u;
|
|
12
|
+
export const KeyringPublicKeySchema = z.string().regex(KEYRING_KEY_RE).describe("A raw 32-byte public key, base64url without padding.");
|
|
13
|
+
|
|
14
|
+
// One secret's name, the same shape a sandbox secret's name and a reference to it take (SECRET_KEY_RE), so a shared
|
|
15
|
+
// entry is spent, typed and listed exactly like a sandbox-held one.
|
|
16
|
+
export const KEYRING_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/u;
|
|
17
|
+
export const KeyringSecretNameSchema = z
|
|
18
|
+
.string()
|
|
19
|
+
.regex(KEYRING_NAME_RE)
|
|
20
|
+
.describe("The secret's name, as a reference to it carries it: a letter or underscore, then letters, digits or `_`.");
|
|
21
|
+
|
|
22
|
+
export const KeyringShareModeSchema = z
|
|
23
|
+
.enum(["each-use", "while-here", "copy"])
|
|
24
|
+
.describe(
|
|
25
|
+
"How the keyring answers this sandbox: `each-use` asks the person at the computer every time (Windows Hello, or the keyring's passphrase); `while-here` answers unasked while the keyring is unlocked, for as long as the unlock lasts; `copy` keeps a copy in the sandbox, so it works with the computer off, which makes it an ordinary sandbox secret.",
|
|
26
|
+
);
|
|
27
|
+
export type KeyringShareMode = z.infer<typeof KeyringShareModeSchema>;
|
|
28
|
+
|
|
29
|
+
// A program a value may be typed into without asking: an executable's file name (`FortiClient.exe`, matched without case)
|
|
30
|
+
// or its whole path. Android apps are package names, kept apart in `packages`.
|
|
31
|
+
export const KEYRING_PROGRAM_MAX = 260;
|
|
32
|
+
export const KeyringProgramSchema = z
|
|
33
|
+
.string()
|
|
34
|
+
.min(1)
|
|
35
|
+
.max(KEYRING_PROGRAM_MAX)
|
|
36
|
+
.refine((value) => !/[\0\r\n]/u.test(value), "one program per entry, no line breaks")
|
|
37
|
+
.describe("A program, by its executable's file name (`FortiClient.exe`, `mstsc.exe`, `ssh`) or its whole path.");
|
|
38
|
+
export const KEYRING_PACKAGE_RE = /^[A-Za-z][A-Za-z0-9_]*(\.[A-Za-z0-9_]+)+$/u;
|
|
39
|
+
export const KeyringPackageSchema = z.string().regex(KEYRING_PACKAGE_RE).max(255).describe("An Android app, by its package name (`com.fortinet.forticlient`).");
|
|
40
|
+
export const KEYRING_PROGRAMS_MAX = 32;
|
|
41
|
+
|
|
42
|
+
export const KeyringSourceSchema = z
|
|
43
|
+
.enum(["keyring", "bitwarden", "1password", "keepassxc"])
|
|
44
|
+
.describe("Where the value itself is read from when it is used: the keyring's own vault, or a password manager the keyring reads it from.");
|
|
45
|
+
export type KeyringSource = z.infer<typeof KeyringSourceSchema>;
|
|
46
|
+
|
|
47
|
+
// What the keyring tells a sandbox about one entry shared with it: everything the sandbox needs to list it, mask it
|
|
48
|
+
// once released and judge a use before asking, never the value.
|
|
49
|
+
export const KeyringPointerSchema = z.object({
|
|
50
|
+
name: KeyringSecretNameSchema,
|
|
51
|
+
mode: KeyringShareModeSchema,
|
|
52
|
+
kind: z.enum(["password", "text"]).describe("A password, typed only into a password field unless `anyField` says otherwise; or text, typed anywhere a program on the list has focus."),
|
|
53
|
+
programs: z
|
|
54
|
+
.array(KeyringProgramSchema)
|
|
55
|
+
.max(KEYRING_PROGRAMS_MAX)
|
|
56
|
+
.describe("Programs on this computer it may be typed into without asking. Empty: typing it into any window asks the person at the computer."),
|
|
57
|
+
packages: z.array(KeyringPackageSchema).max(KEYRING_PROGRAMS_MAX).describe("Android apps on a phone attached to this computer it may be typed into."),
|
|
58
|
+
hosts: z
|
|
59
|
+
.array(z.string().min(1).max(253))
|
|
60
|
+
.max(KEYRING_PROGRAMS_MAX)
|
|
61
|
+
.describe("Sites it may be typed into or sent to, exact or `*.domain`, the same shape as a sandbox secret's host guard."),
|
|
62
|
+
anyField: z.boolean().describe("Whether it may be typed into a field the program does not mark as a password field."),
|
|
63
|
+
source: KeyringSourceSchema,
|
|
64
|
+
updatedAt: z.number().describe("When the entry last changed, in milliseconds."),
|
|
65
|
+
});
|
|
66
|
+
export type KeyringPointer = z.infer<typeof KeyringPointerSchema>;
|
|
67
|
+
|
|
68
|
+
// The keyring's own public keys: `seal` for a value the sandbox sends it to type (a sandbox-held secret, or one another
|
|
69
|
+
// computer released for this one), `inbox` for a value sent to be kept, readable only once the vault is unlocked.
|
|
70
|
+
export const KeyringDeviceKeysSchema = z.object({
|
|
71
|
+
seal: KeyringPublicKeySchema.describe("Seals a value this keyring is asked to type. Its private half sits beside the vault, readable while locked."),
|
|
72
|
+
inbox: KeyringPublicKeySchema.describe("Seals a value sent to be kept. Its private half is inside the vault, so nothing reads an inbox item until a person unlocks it."),
|
|
73
|
+
});
|
|
74
|
+
export type KeyringDeviceKeys = z.infer<typeof KeyringDeviceKeysSchema>;
|
|
75
|
+
|
|
76
|
+
// A sandbox's own keyring keys, served unauthenticated at GET /keyring/keys so a keyring can fetch them over TLS from the
|
|
77
|
+
// sandbox's address itself rather than take them from whoever relays a share: `sign` verifies its grants, `seal` is what
|
|
78
|
+
// a released value is sealed to.
|
|
79
|
+
export const KeyringSandboxKeysSchema = z.object({
|
|
80
|
+
sign: KeyringPublicKeySchema.describe("Verifies the grants this sandbox signs (Ed25519)."),
|
|
81
|
+
seal: KeyringPublicKeySchema.describe("What a value released to this sandbox is sealed to (X25519)."),
|
|
82
|
+
});
|
|
83
|
+
export type KeyringSandboxKeys = z.infer<typeof KeyringSandboxKeysSchema>;
|
|
84
|
+
|
|
85
|
+
export const KeyringStateSchema = z.object({
|
|
86
|
+
version: z.string().describe("The keyring's own release."),
|
|
87
|
+
locked: z.boolean().describe("Whether the vault is locked. Locked, `while-here` entries ask like `each-use` ones."),
|
|
88
|
+
unlockedUntil: z.number().optional().describe("When the current unlock ends, in milliseconds. Absent while locked."),
|
|
89
|
+
unlock: z
|
|
90
|
+
.array(z.enum(["hello", "passphrase"]))
|
|
91
|
+
.describe("How a person unlocks it here: Windows Hello, or the keyring's passphrase."),
|
|
92
|
+
keys: KeyringDeviceKeysSchema.optional().describe("Absent until the keyring is set up: there is no vault, so no inbox to seal a value to."),
|
|
93
|
+
pendingInbox: z.number().int().nonnegative().describe("Values sent to be kept that wait for the next unlock."),
|
|
94
|
+
});
|
|
95
|
+
export type KeyringState = z.infer<typeof KeyringStateSchema>;
|
|
96
|
+
|
|
97
|
+
// The machine's answer to `keyringShares`: the keyring's state and the entries shared with the asking sandbox. Absent
|
|
98
|
+
// `keyring`: none is installed, or it does not answer.
|
|
99
|
+
export const KeyringSharesSchema = z.object({
|
|
100
|
+
keyring: KeyringStateSchema.optional().describe("The keyring on this computer. Absent when none is installed or it does not answer."),
|
|
101
|
+
pointers: z.array(KeyringPointerSchema).describe("The entries shared with the sandbox asking. Names and rules only, never values."),
|
|
102
|
+
reason: z.string().optional().describe("Why there is no keyring to speak of, in words, when there is not."),
|
|
103
|
+
});
|
|
104
|
+
export type KeyringShares = z.infer<typeof KeyringSharesSchema>;
|
|
105
|
+
|
|
106
|
+
// ---- grants ----
|
|
107
|
+
|
|
108
|
+
export const KeyringLaneSchema = z
|
|
109
|
+
.enum(["shell", "code", "browser", "desktop"])
|
|
110
|
+
.describe("Where in the sandbox a released value goes: a command, a script, a page in a sandbox browser, a window on the sandbox's desktop.");
|
|
111
|
+
|
|
112
|
+
// What a grant asks the keyring to do with one value.
|
|
113
|
+
export const KeyringGrantTargetSchema = z.discriminatedUnion("kind", [
|
|
114
|
+
// Hand the value to the sandbox, sealed to its pinned `seal` key, for one use in one lane.
|
|
115
|
+
z.object({ kind: z.literal("sandbox"), lane: KeyringLaneSchema }),
|
|
116
|
+
// Type it into the focused window on this computer. `programs` and `anyField` are the sandbox's own guard for a
|
|
117
|
+
// value it sent; for the keyring's own entry the keyring's list is the one in force and these only narrow it.
|
|
118
|
+
z.object({
|
|
119
|
+
kind: z.literal("window"),
|
|
120
|
+
programs: z.array(KeyringProgramSchema).max(KEYRING_PROGRAMS_MAX),
|
|
121
|
+
anyField: z.boolean(),
|
|
122
|
+
// The owner's own Type into…, from the editor: a moment to put the cursor where the value goes.
|
|
123
|
+
countdownSeconds: z.number().int().min(0).max(10).optional(),
|
|
124
|
+
}),
|
|
125
|
+
// Type it on an Android phone attached to this computer, into one of these apps.
|
|
126
|
+
z.object({ kind: z.literal("android"), serial: z.string().min(1).max(128), packages: z.array(KeyringPackageSchema).max(KEYRING_PROGRAMS_MAX) }),
|
|
127
|
+
// Seal it to another computer's keyring, which the sandbox then asks to type it: the value goes computer to computer
|
|
128
|
+
// and the sandbox carries only ciphertext.
|
|
129
|
+
z.object({ kind: z.literal("relay"), sealTo: KeyringPublicKeySchema, toMachine: z.string().min(1).max(128) }),
|
|
130
|
+
]);
|
|
131
|
+
export type KeyringGrantTarget = z.infer<typeof KeyringGrantTargetSchema>;
|
|
132
|
+
|
|
133
|
+
// A grant lives two minutes at most: long enough for a person to answer a Windows Hello prompt, short enough that one
|
|
134
|
+
// lifted from a log is spent or stale.
|
|
135
|
+
export const KEYRING_GRANT_TTL_MAX_MS = 2 * 60_000;
|
|
136
|
+
export const KEYRING_NONCE_RE = /^[A-Za-z0-9_-]{22}$/u;
|
|
137
|
+
|
|
138
|
+
export const KeyringGrantSchema = z
|
|
139
|
+
.object({
|
|
140
|
+
v: z.literal(1),
|
|
141
|
+
sandbox: z.string().url().describe("The sandbox's own address: which share this grant spends, and whose signing key verifies it."),
|
|
142
|
+
secret: KeyringSecretNameSchema,
|
|
143
|
+
target: KeyringGrantTargetSchema,
|
|
144
|
+
purpose: z.enum(["agent", "owner"]).describe("Whether an agent asked for this use, or the owner did from the editor's Type into…."),
|
|
145
|
+
conversation: z.string().max(200).optional().describe("The conversation the use is for, shown in the keyring's prompt and its record."),
|
|
146
|
+
detail: z.string().min(1).max(300).describe("What the use is, in words, for the person answering the prompt: the command's start, the site, the window."),
|
|
147
|
+
nonce: z.string().regex(KEYRING_NONCE_RE).describe("16 random bytes, base64url: a grant answers once."),
|
|
148
|
+
issuedAt: z.number().int(),
|
|
149
|
+
expiresAt: z.number().int(),
|
|
150
|
+
})
|
|
151
|
+
.strict();
|
|
152
|
+
export type KeyringGrant = z.infer<typeof KeyringGrantSchema>;
|
|
153
|
+
|
|
154
|
+
export const SignedKeyringGrantSchema = z
|
|
155
|
+
.object({
|
|
156
|
+
payload: z.string().min(1).max(8192).describe("The grant's JSON, base64url."),
|
|
157
|
+
sig: z.string().regex(/^[A-Za-z0-9_-]{86}$/u).describe("The sandbox's Ed25519 signature over the grant, base64url."),
|
|
158
|
+
})
|
|
159
|
+
.strict();
|
|
160
|
+
export type SignedKeyringGrant = z.infer<typeof SignedKeyringGrantSchema>;
|
|
161
|
+
|
|
162
|
+
// X25519 to the recipient's key with a fresh key per box, HKDF-SHA256, AES-256-GCM; the additional data binds a box to
|
|
163
|
+
// what it was sealed for (policy/keyring-seal.ts), so one cannot be replayed as another.
|
|
164
|
+
export const SealedBoxSchema = z
|
|
165
|
+
.object({
|
|
166
|
+
v: z.literal(1),
|
|
167
|
+
epk: KeyringPublicKeySchema.describe("The sender's one-time public key."),
|
|
168
|
+
iv: z.string().regex(/^[A-Za-z0-9_-]{16}$/u),
|
|
169
|
+
tag: z.string().regex(/^[A-Za-z0-9_-]{22}$/u),
|
|
170
|
+
data: z.string().max(65536).regex(/^[A-Za-z0-9_-]*$/u),
|
|
171
|
+
})
|
|
172
|
+
.strict();
|
|
173
|
+
export type SealedBox = z.infer<typeof SealedBoxSchema>;
|
|
174
|
+
|
|
175
|
+
export const KeyringRefusalCodeSchema = z
|
|
176
|
+
.enum(["no-keyring", "locked", "declined", "unknown", "not-shared", "bad-grant", "expired", "replayed", "guard", "unavailable", "busy"])
|
|
177
|
+
.describe(
|
|
178
|
+
"Why: no keyring here; locked with nobody to unlock it; the person said no; no such entry; not shared with this sandbox; a grant that does not verify; one past its time; one already spent; the focused window or field is not one it may go to; its password manager did not answer; another prompt is open.",
|
|
179
|
+
);
|
|
180
|
+
export type KeyringRefusalCode = z.infer<typeof KeyringRefusalCodeSchema>;
|
|
181
|
+
|
|
182
|
+
const refusal = z.object({ ok: z.literal(false), code: KeyringRefusalCodeSchema, refusal: z.string().describe("What happened, in words a person can act on.") });
|
|
183
|
+
|
|
184
|
+
export const KeyringReleaseResultSchema = z.discriminatedUnion("ok", [
|
|
185
|
+
z.object({ ok: z.literal(true), sealed: SealedBoxSchema, asked: z.boolean().describe("Whether the person at the computer was asked.") }),
|
|
186
|
+
refusal,
|
|
187
|
+
]);
|
|
188
|
+
export type KeyringReleaseResult = z.infer<typeof KeyringReleaseResultSchema>;
|
|
189
|
+
|
|
190
|
+
export const KeyringTypeInputSchema = z
|
|
191
|
+
.object({
|
|
192
|
+
grant: SignedKeyringGrantSchema,
|
|
193
|
+
// A value the keyring does not hold itself: a sandbox-held secret, or one another computer released for this one,
|
|
194
|
+
// sealed to this keyring's `seal` key.
|
|
195
|
+
sealed: SealedBoxSchema.optional(),
|
|
196
|
+
})
|
|
197
|
+
.strict();
|
|
198
|
+
export type KeyringTypeInput = z.infer<typeof KeyringTypeInputSchema>;
|
|
199
|
+
|
|
200
|
+
export const KeyringTypeResultSchema = z.discriminatedUnion("ok", [
|
|
201
|
+
z.object({
|
|
202
|
+
ok: z.literal(true),
|
|
203
|
+
program: z.string().describe("The program it was typed into: its executable's file name, or the Android app's package."),
|
|
204
|
+
field: z.enum(["password", "text"]).describe("Whether the focused field was a password field."),
|
|
205
|
+
asked: z.boolean().describe("Whether the person at the computer was asked."),
|
|
206
|
+
}),
|
|
207
|
+
refusal,
|
|
208
|
+
]);
|
|
209
|
+
export type KeyringTypeResult = z.infer<typeof KeyringTypeResultSchema>;
|
|
210
|
+
|
|
211
|
+
// A value sent from the sandbox to be kept in this computer's keyring (a `secrets ask` answered with "keep it on my
|
|
212
|
+
// computer"), sealed to the vault's inbox key and taken in at the next unlock, when the person sees its name and says yes.
|
|
213
|
+
export const KeyringInboxItemSchema = z
|
|
214
|
+
.object({
|
|
215
|
+
name: KeyringSecretNameSchema,
|
|
216
|
+
sealed: SealedBoxSchema,
|
|
217
|
+
mode: KeyringShareModeSchema,
|
|
218
|
+
kind: z.enum(["password", "text"]),
|
|
219
|
+
programs: z.array(KeyringProgramSchema).max(KEYRING_PROGRAMS_MAX),
|
|
220
|
+
hosts: z.array(z.string().min(1).max(253)).max(KEYRING_PROGRAMS_MAX),
|
|
221
|
+
sandbox: z.string().url().describe("The sandbox it came from, which it is shared with once taken in."),
|
|
222
|
+
})
|
|
223
|
+
.strict();
|
|
224
|
+
export type KeyringInboxItem = z.infer<typeof KeyringInboxItemSchema>;
|
|
225
|
+
|
|
226
|
+
export const KeyringInboxResultSchema = z.discriminatedUnion("ok", [z.object({ ok: z.literal(true), pending: z.boolean().describe("Whether it waits for the next unlock.") }), refusal]);
|
|
227
|
+
export type KeyringInboxResult = z.infer<typeof KeyringInboxResultSchema>;
|
|
228
|
+
|
|
229
|
+
// `copy` entries, sealed to the asking sandbox's key, for the sandbox to keep as its own; asked for on connect and after
|
|
230
|
+
// a change, so an edit on the computer reaches the copy.
|
|
231
|
+
export const KeyringCopiesSchema = z.object({
|
|
232
|
+
copies: z.array(z.object({ name: KeyringSecretNameSchema, sealed: SealedBoxSchema, updatedAt: z.number() })),
|
|
233
|
+
});
|
|
234
|
+
export type KeyringCopies = z.infer<typeof KeyringCopiesSchema>;
|
|
235
|
+
|
|
236
|
+
// ---- sync between the owner's computers ----
|
|
237
|
+
|
|
238
|
+
// Keyrings on two of the owner's computers keep one set of entries by sealing each change to the other's sync key and
|
|
239
|
+
// leaving it in a sandbox both link to, which carries only ciphertext and never a key that opens it. Two keyrings are
|
|
240
|
+
// paired once, at a person's word on each computer, each showing both fingerprints to compare; a sandbox that swapped a
|
|
241
|
+
// key would show a fingerprint the other screen does not.
|
|
242
|
+
export const KeyringSyncPeerSchema = z.object({
|
|
243
|
+
device: z.string().min(1).max(128).describe("The other computer, by its device card."),
|
|
244
|
+
key: KeyringPublicKeySchema.describe("Its keyring's sync key."),
|
|
245
|
+
pairedAt: z.number().describe("When the two were paired, in milliseconds."),
|
|
246
|
+
});
|
|
247
|
+
export type KeyringSyncPeer = z.infer<typeof KeyringSyncPeerSchema>;
|
|
248
|
+
|
|
249
|
+
export const KeyringSyncStateSchema = z.object({
|
|
250
|
+
key: KeyringPublicKeySchema.describe("This keyring's own sync key."),
|
|
251
|
+
peers: z.array(KeyringSyncPeerSchema).describe("The keyrings it keeps in step with."),
|
|
252
|
+
revision: z.number().int().nonnegative().describe("How many changes this keyring has made, for ordering what it sends."),
|
|
253
|
+
});
|
|
254
|
+
export type KeyringSyncState = z.infer<typeof KeyringSyncStateSchema>;
|
|
255
|
+
|
|
256
|
+
export const KeyringSyncItemSchema = z
|
|
257
|
+
.object({
|
|
258
|
+
from: KeyringPublicKeySchema.describe("The sending keyring's sync key."),
|
|
259
|
+
to: KeyringPublicKeySchema.describe("The receiving keyring's sync key."),
|
|
260
|
+
revision: z.number().int().nonnegative(),
|
|
261
|
+
sealed: SealedBoxSchema.describe("The sender's entries, sealed to the receiver for `sync` with its own revision."),
|
|
262
|
+
})
|
|
263
|
+
.strict();
|
|
264
|
+
export type KeyringSyncItem = z.infer<typeof KeyringSyncItemSchema>;
|
|
265
|
+
|
|
266
|
+
export const KeyringSyncPairSchema = z
|
|
267
|
+
.object({
|
|
268
|
+
peer: z.object({ device: z.string().min(1).max(128), key: KeyringPublicKeySchema }).strict(),
|
|
269
|
+
// The device card this keyring's own computer is known by, so the prompt can name both.
|
|
270
|
+
self: z.string().min(1).max(128),
|
|
271
|
+
})
|
|
272
|
+
.strict();
|
|
273
|
+
|
|
274
|
+
export const KeyringSyncPairResultSchema = z.discriminatedUnion("ok", [z.object({ ok: z.literal(true) }), refusal]);
|
|
275
|
+
export const KeyringSyncOutboxSchema = z.object({ items: z.array(KeyringSyncItemSchema) });
|
|
276
|
+
export const KeyringSyncDeliverSchema = z.object({ items: z.array(KeyringSyncItemSchema).max(64) }).strict();
|
|
277
|
+
export const KeyringSyncDeliverResultSchema = z.object({ accepted: z.number().int().nonnegative().describe("How many it took; the rest were not from a paired keyring or did not open.") });
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { AskQuestionSchema } from "../events/requests.js";
|
|
3
|
+
import { entryId } from "./internal.js";
|
|
4
|
+
|
|
5
|
+
// LINKED SANDBOXES: one of the owner's sandboxes handing tasks to another. The sandbox that hands work over holds a
|
|
6
|
+
// `link` capability card (schemas/capabilities.ts LinkConfigSchema); the one that takes it holds an inbound link, minted
|
|
7
|
+
// by its owner, whose token is that card's credential. Everything the taking side does with a task is decided there, by
|
|
8
|
+
// the terms on its own link: which persona the work wears, whether each task waits for its owner, how many turns an hour.
|
|
9
|
+
// The handing side only asks, reads and stops.
|
|
10
|
+
|
|
11
|
+
// What a link token looks like: issuer-prefixed like a control token (`ict_`), and never the same credential.
|
|
12
|
+
export const LINK_TOKEN_PREFIX = "ilk_";
|
|
13
|
+
// The header the taking sandbox's link doors read it from.
|
|
14
|
+
export const LINK_HEADER = "x-intentic-link";
|
|
15
|
+
// How many links a task may have crossed before the one it is asking across. A task a person started is at 0; a sandbox
|
|
16
|
+
// that took a task through a link and hands part of it on asks at 1. Past this the asking sandbox refuses before it
|
|
17
|
+
// calls, and the taking sandbox refuses again, so two sandboxes cannot hand one task back and forth for ever.
|
|
18
|
+
export const LINK_MAX_HOPS = 2;
|
|
19
|
+
// The ceiling a link's own hourly allowance may be set to, and what one is minted with when its owner names none.
|
|
20
|
+
export const LINK_TURNS_PER_HOUR_MAX = 120;
|
|
21
|
+
export const LINK_TURNS_PER_HOUR_DEFAULT = 20;
|
|
22
|
+
|
|
23
|
+
// THE TERMS, set on the taking side and enforced there: what the asking side is told, never what it decides.
|
|
24
|
+
export const LinkTermsSchema = z.object({
|
|
25
|
+
actsAs: entryId
|
|
26
|
+
.optional()
|
|
27
|
+
.describe(
|
|
28
|
+
"The persona every task through this link wears: its accounts, its shelves, its folders, its model. Absent, the task runs with no signed-in account and the full toolbox, as any unattended wake does.",
|
|
29
|
+
),
|
|
30
|
+
approve: z
|
|
31
|
+
.boolean()
|
|
32
|
+
.default(false)
|
|
33
|
+
.describe("Each new task waits for this sandbox's owner to allow it before anything runs. Follow-up messages to a task already allowed do not."),
|
|
34
|
+
turnsPerHour: z
|
|
35
|
+
.number()
|
|
36
|
+
.int()
|
|
37
|
+
.min(1)
|
|
38
|
+
.max(LINK_TURNS_PER_HOUR_MAX)
|
|
39
|
+
.default(LINK_TURNS_PER_HOUR_DEFAULT)
|
|
40
|
+
.describe("How many turns the link may start in an hour, new tasks and follow-ups alike. Words said into a turn already running are not counted."),
|
|
41
|
+
});
|
|
42
|
+
export type LinkTerms = z.infer<typeof LinkTermsSchema>;
|
|
43
|
+
|
|
44
|
+
// Where the link's holder says it is, as the owner's editor named it at pairing; informational, never a credential.
|
|
45
|
+
export const LinkPeerSchema = z.object({
|
|
46
|
+
sandboxId: z.string().max(100).optional().describe("The asking sandbox's id on the owner's account."),
|
|
47
|
+
url: z.string().max(500).optional().describe("The asking sandbox's own address."),
|
|
48
|
+
});
|
|
49
|
+
export type LinkPeer = z.infer<typeof LinkPeerSchema>;
|
|
50
|
+
|
|
51
|
+
// A task waiting for the taking sandbox's owner, when the link's terms say every task waits.
|
|
52
|
+
export const LinkRequestSchema = z.object({
|
|
53
|
+
id: z.string().describe("The task's own id, the one the asking sandbox gave it."),
|
|
54
|
+
conversationId: z.string().describe("The conversation the task opens here once it is allowed."),
|
|
55
|
+
task: z.string().describe("What it asks, cut to a readable length."),
|
|
56
|
+
title: z.string().optional().describe("Its one-line name."),
|
|
57
|
+
askedAt: z.number().describe("When it arrived, in epoch milliseconds."),
|
|
58
|
+
});
|
|
59
|
+
export type LinkRequest = z.infer<typeof LinkRequestSchema>;
|
|
60
|
+
|
|
61
|
+
// One inbound link as the owner's Access page lists it: everything but the token, which is kept only as a hash.
|
|
62
|
+
export const InboundLinkSummarySchema = z.object({
|
|
63
|
+
id: z.string(),
|
|
64
|
+
label: z.string().describe("What the asking sandbox is called here: its name at pairing, or what the owner typed."),
|
|
65
|
+
from: LinkPeerSchema.optional(),
|
|
66
|
+
terms: LinkTermsSchema,
|
|
67
|
+
createdAt: z.number(),
|
|
68
|
+
createdBy: z.string().optional().describe("The owner who minted it."),
|
|
69
|
+
lastUsedAt: z.number().optional().describe("The last time it was used, coarse on purpose; absent means never."),
|
|
70
|
+
conversations: z.number().int().nonnegative().describe("How many conversations it has opened here."),
|
|
71
|
+
requests: z.array(LinkRequestSchema).describe("Tasks waiting for the owner's yes, oldest first."),
|
|
72
|
+
});
|
|
73
|
+
export type InboundLinkSummary = z.infer<typeof InboundLinkSummarySchema>;
|
|
74
|
+
export const InboundLinksListSchema = z.object({ links: z.array(InboundLinkSummarySchema) });
|
|
75
|
+
export type InboundLinksList = z.infer<typeof InboundLinksListSchema>;
|
|
76
|
+
|
|
77
|
+
// POST /system/links: what the owner (or their editor, pairing two of their sandboxes) mints a link with.
|
|
78
|
+
export const InboundLinkMintSchema = z.object({
|
|
79
|
+
label: z.string().trim().min(1).max(60),
|
|
80
|
+
from: LinkPeerSchema.optional(),
|
|
81
|
+
terms: LinkTermsSchema.partial().optional(),
|
|
82
|
+
});
|
|
83
|
+
export type InboundLinkMint = z.infer<typeof InboundLinkMintSchema>;
|
|
84
|
+
// The raw token, shown exactly once.
|
|
85
|
+
export const InboundLinkMintedSchema = z.object({ id: z.string(), token: z.string() });
|
|
86
|
+
export type InboundLinkMinted = z.infer<typeof InboundLinkMintedSchema>;
|
|
87
|
+
// POST /system/links/{id}: new terms, whole fields replaced, the rest kept.
|
|
88
|
+
export const InboundLinkTermsUpdateSchema = LinkTermsSchema.partial().extend({ label: z.string().trim().min(1).max(60).optional() });
|
|
89
|
+
export type InboundLinkTermsUpdate = z.infer<typeof InboundLinkTermsUpdateSchema>;
|
|
90
|
+
// POST /system/links/{id}/requests/{request}: the owner's answer to one waiting task.
|
|
91
|
+
export const LinkRequestDecisionSchema = z.object({ approve: z.boolean() });
|
|
92
|
+
export type LinkRequestDecision = z.infer<typeof LinkRequestDecisionSchema>;
|
|
93
|
+
|
|
94
|
+
/* THE LINK DOORS, called by the asking sandbox's daemon with the link token, never by a browser. */
|
|
95
|
+
|
|
96
|
+
// GET /link: the link still works, and what it is held to.
|
|
97
|
+
export const LinkHelloSchema = z.object({
|
|
98
|
+
sandbox: z.string().describe("What the taking sandbox calls itself."),
|
|
99
|
+
link: z.object({ id: z.string(), label: z.string() }),
|
|
100
|
+
terms: z.object({
|
|
101
|
+
approve: z.boolean(),
|
|
102
|
+
turnsPerHour: z.number().int(),
|
|
103
|
+
persona: z.string().optional().describe("The name of the persona its tasks wear, when they wear one."),
|
|
104
|
+
}),
|
|
105
|
+
maxHops: z.number().int(),
|
|
106
|
+
});
|
|
107
|
+
export type LinkHello = z.infer<typeof LinkHelloSchema>;
|
|
108
|
+
|
|
109
|
+
// POST /link/conversations: one task, opening one conversation. `requestId` is the asking side's own id for it: asking
|
|
110
|
+
// again under the same id answers with what became of the first ask, so a lost answer never opens a second conversation.
|
|
111
|
+
export const LinkTaskSchema = z.object({
|
|
112
|
+
requestId: z
|
|
113
|
+
.string()
|
|
114
|
+
.min(1)
|
|
115
|
+
.max(100)
|
|
116
|
+
.regex(/^[A-Za-z0-9][A-Za-z0-9_.:-]*$/),
|
|
117
|
+
task: z.string().min(1).max(60_000).describe("The whole task, self-contained: the taking agent sees nothing else."),
|
|
118
|
+
title: z.string().max(80).optional().describe("A one-line name for the conversation it opens."),
|
|
119
|
+
hops: z.number().int().min(0).max(10).default(0).describe("How many links the task already crossed before this one."),
|
|
120
|
+
tainted: z
|
|
121
|
+
.string()
|
|
122
|
+
.max(500)
|
|
123
|
+
.optional()
|
|
124
|
+
.describe("What outside content the asking turn had taken in, when it had: the taking turn carries it on."),
|
|
125
|
+
from: z
|
|
126
|
+
.object({
|
|
127
|
+
conversation: z.string().max(100).optional(),
|
|
128
|
+
title: z.string().max(200).optional(),
|
|
129
|
+
})
|
|
130
|
+
.optional()
|
|
131
|
+
.describe("The asking conversation, for the taking sandbox's own record."),
|
|
132
|
+
});
|
|
133
|
+
export type LinkTask = z.infer<typeof LinkTaskSchema>;
|
|
134
|
+
|
|
135
|
+
// Where a linked task stands, as the asking side reads it.
|
|
136
|
+
// held: waiting for the taking sandbox's owner to allow it. running: a turn is working on it. blocked: parked on a card;
|
|
137
|
+
// `question` carries one the asking agent may answer, anything else waits for the taking sandbox's owner. finished: its
|
|
138
|
+
// last turn ended, `report` is what it said. failed: its last turn failed. declined: the owner said no. stopped: the
|
|
139
|
+
// asking side stopped it, or its owner did.
|
|
140
|
+
export const LinkTaskStateSchema = z.enum(["held", "running", "blocked", "finished", "failed", "declined", "stopped"]);
|
|
141
|
+
export type LinkTaskState = z.infer<typeof LinkTaskStateSchema>;
|
|
142
|
+
|
|
143
|
+
export const LinkQuestionSchema = z.object({
|
|
144
|
+
requestId: z.string(),
|
|
145
|
+
questions: z.array(AskQuestionSchema),
|
|
146
|
+
});
|
|
147
|
+
export type LinkQuestion = z.infer<typeof LinkQuestionSchema>;
|
|
148
|
+
|
|
149
|
+
export const LinkTaskStatusSchema = z.object({
|
|
150
|
+
conversationId: z.string(),
|
|
151
|
+
state: LinkTaskStateSchema,
|
|
152
|
+
title: z.string().optional(),
|
|
153
|
+
report: z.string().optional().describe("The last settled turn's closing message, whole."),
|
|
154
|
+
question: LinkQuestionSchema.optional(),
|
|
155
|
+
waitingOn: z.string().optional().describe("What a blocked task waits on when it is not a question the asking agent may answer."),
|
|
156
|
+
error: z.string().optional(),
|
|
157
|
+
// Moves every time anything above changes, so a long poll can wait for a version it has not seen.
|
|
158
|
+
version: z.number().int().nonnegative(),
|
|
159
|
+
updatedAt: z.number(),
|
|
160
|
+
});
|
|
161
|
+
export type LinkTaskStatus = z.infer<typeof LinkTaskStatusSchema>;
|
|
162
|
+
|
|
163
|
+
// POST /link/conversations/{id}/messages: follow-up words, said into the running turn or opening the next one.
|
|
164
|
+
export const LinkMessageSchema = z.object({
|
|
165
|
+
message: z.string().min(1).max(60_000),
|
|
166
|
+
messageId: z.string().min(1).max(128).optional(),
|
|
167
|
+
tainted: z.string().max(500).optional(),
|
|
168
|
+
});
|
|
169
|
+
export type LinkMessage = z.infer<typeof LinkMessageSchema>;
|
|
170
|
+
export const LinkMessageReceiptSchema = z.object({
|
|
171
|
+
delivered: z.enum(["started", "steered", "queued"]),
|
|
172
|
+
});
|
|
173
|
+
export type LinkMessageReceipt = z.infer<typeof LinkMessageReceiptSchema>;
|
|
174
|
+
|
|
175
|
+
// POST /link/conversations/{id}/answer: picks for the question the task is parked on, and only a question.
|
|
176
|
+
export const LinkAnswerSchema = z.object({
|
|
177
|
+
requestId: z.string().min(1),
|
|
178
|
+
answers: z.record(z.string(), z.array(z.string())),
|
|
179
|
+
});
|
|
180
|
+
export type LinkAnswer = z.infer<typeof LinkAnswerSchema>;
|
|
@@ -341,6 +341,7 @@ export const ChoreProbeRequestSchema = z.object({
|
|
|
341
341
|
});
|
|
342
342
|
// POST /chores/ledger: record a run, or snooze. Daemon-written so a turn started from anywhere (panel, automation,
|
|
343
343
|
// agent) lands in one ledger.
|
|
344
|
+
/** @alias */
|
|
344
345
|
export const ChoreLedgerWriteSchema = ChoreLedgerEntrySchema;
|
|
345
346
|
// `warn` is a real third state, not a soft failure: reporting "pass" when nothing has been exercised yet would be the
|
|
346
347
|
// check lying.
|
package/src/schemas/members.ts
CHANGED
|
@@ -30,6 +30,10 @@ export const REACHABLE_CREDENTIAL_KINDS: readonly CapabilityKind[] = [
|
|
|
30
30
|
"webext",
|
|
31
31
|
"plugin",
|
|
32
32
|
"extension",
|
|
33
|
+
// The owner's account: a person this sandbox works for may be kept from making sandboxes on it.
|
|
34
|
+
"fleet",
|
|
35
|
+
// Another of the owner's sandboxes: a person may be kept from handing it work.
|
|
36
|
+
"link",
|
|
33
37
|
];
|
|
34
38
|
|
|
35
39
|
// WHAT ONE PERSON MAY DO, as three independent answers rather than one rung of a ladder: whether their conversations
|
package/src/schemas/needs.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { KeyringShareModeSchema } from "./keyring.js";
|
|
2
3
|
import { AutomationSchema } from "./automations.js";
|
|
3
4
|
|
|
4
5
|
// A need: something an agent cannot finish its task without that only a person can provide (a connection, a secret's
|
|
@@ -29,8 +30,10 @@ export const isOpenNeed = (need: { readonly status: NeedStatus }): boolean => OP
|
|
|
29
30
|
const FieldValuesSchema = z.record(z.string(), z.string());
|
|
30
31
|
|
|
31
32
|
export const CapabilityNeedModeSchema = z
|
|
32
|
-
.enum(["connect", "reconnect", "change"])
|
|
33
|
-
.describe(
|
|
33
|
+
.enum(["connect", "reconnect", "change", "remove"])
|
|
34
|
+
.describe(
|
|
35
|
+
"Connect something new, give a connected one a credential that works again, change a setting on a connected one, or remove a connected one.",
|
|
36
|
+
);
|
|
34
37
|
export type CapabilityNeedMode = z.infer<typeof CapabilityNeedModeSchema>;
|
|
35
38
|
|
|
36
39
|
export const CapabilityNeedSchema = z.object({
|
|
@@ -38,7 +41,7 @@ export const CapabilityNeedSchema = z.object({
|
|
|
38
41
|
entry: z.string().min(1).describe("The catalog entry, as the catalog names it."),
|
|
39
42
|
name: z.string().describe("What the catalog calls it, never the agent's spelling."),
|
|
40
43
|
mode: CapabilityNeedModeSchema,
|
|
41
|
-
instance: z.string().optional().describe("The connection a reconnect or a
|
|
44
|
+
instance: z.string().optional().describe("The connection a reconnect, a change or a removal is about."),
|
|
42
45
|
target: z.string().optional().describe("The site, host or address the connection is for, when the entry can hold several."),
|
|
43
46
|
prefill: FieldValuesSchema.optional().describe(
|
|
44
47
|
"Settings the agent could fill in for a new connection, never a credential: the daemon keeps only the entry's own non-secret fields.",
|
|
@@ -192,6 +195,10 @@ export const NeedAskSchema = z.discriminatedUnion("kind", [
|
|
|
192
195
|
.boolean()
|
|
193
196
|
.optional()
|
|
194
197
|
.describe("It is connected, but its credential is being refused: ask for a new one rather than being told to use it."),
|
|
198
|
+
remove: z
|
|
199
|
+
.boolean()
|
|
200
|
+
.optional()
|
|
201
|
+
.describe("Take a connection away: the person removes it in one press on the card, as the Capabilities page's Remove does."),
|
|
195
202
|
}),
|
|
196
203
|
SecretNeedSchema,
|
|
197
204
|
z.object({
|
|
@@ -239,7 +246,7 @@ export const NeedAnswerSchema = z.discriminatedUnion("kind", [
|
|
|
239
246
|
z.object({ kind: z.literal("decline"), note: z.string().max(500).optional().describe("Why not, passed to the agent.") }),
|
|
240
247
|
// A connection's yes before its form is saved: moves the need to working, so the board keeps saying so.
|
|
241
248
|
z.object({ kind: z.literal("accept") }),
|
|
242
|
-
// A change to a connected capability's settings, applied as the card showed it.
|
|
249
|
+
// A change to a connected capability's settings, or its removal, applied as the card showed it.
|
|
243
250
|
z.object({ kind: z.literal("apply") }),
|
|
244
251
|
z.object({ kind: z.literal("grant"), scope: GrantScopeSchema }),
|
|
245
252
|
z.object({ kind: z.literal("release") }),
|
|
@@ -251,6 +258,15 @@ export const NeedIdParamSchema = z.object({ id: z.string().min(1).describe("Whic
|
|
|
251
258
|
export const NeedAnswerInputSchema = NeedIdParamSchema.extend({ answer: NeedAnswerSchema });
|
|
252
259
|
export const NeedSecretInputSchema = NeedIdParamSchema.extend({
|
|
253
260
|
value: z.string().min(1).max(64_000).describe("The secret's value. Stored, never echoed, never written into a transcript."),
|
|
261
|
+
// "Keep it on my computer": the value is sealed to that computer's keyring inbox (schemas/keyring.ts) and kept there,
|
|
262
|
+
// and this sandbox holds a pointer to it; nothing is stored here. Absent: stored in the sandbox, as before.
|
|
263
|
+
keepOn: z
|
|
264
|
+
.object({
|
|
265
|
+
device: z.string().min(1).describe("Which computer's keyring keeps it, by its device card."),
|
|
266
|
+
mode: KeyringShareModeSchema.describe("How that keyring answers this sandbox for it."),
|
|
267
|
+
})
|
|
268
|
+
.optional()
|
|
269
|
+
.describe("Keep it in the keyring on one of your computers instead of in the sandbox."),
|
|
254
270
|
});
|
|
255
271
|
export const NeedsQuerySchema = z.object({
|
|
256
272
|
conversationId: z.string().optional().describe("One conversation's needs. Absent is every conversation's."),
|
|
@@ -355,7 +371,7 @@ export const OwnerGrantSchema = z.discriminatedUnion("kind", [
|
|
|
355
371
|
hooks: z.array(z.string()).describe("Each hook in the set in the daemon's words: when it runs and what it runs."),
|
|
356
372
|
at: z.number().describe("When it was approved, in milliseconds."),
|
|
357
373
|
}),
|
|
358
|
-
// A workspace extension approved for the powers it declared then (
|
|
374
|
+
// A workspace extension approved for the powers it declared then (capabilities/extension-inventory/extension-approvals.ts).
|
|
359
375
|
z.object({
|
|
360
376
|
kind: z.literal("extension"),
|
|
361
377
|
id: z.string().describe("The extension, by its publisher.name id."),
|