@vellumai/assistant 0.11.7-dev.202608282003.8ce5421 → 0.11.7-dev.202608282312.37594b7
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/ARCHITECTURE.md +9 -9
- package/Dockerfile +1 -0
- package/docs/trusted-contact-access.md +13 -12
- package/knip.json +1 -0
- package/node_modules/@vellumai/avatar-catalog/package.json +19 -0
- package/{src/avatar/character-components.ts → node_modules/@vellumai/avatar-catalog/src/catalog.ts} +12 -44
- package/node_modules/@vellumai/avatar-catalog/src/colors.ts +19 -0
- package/node_modules/@vellumai/avatar-catalog/src/index.ts +25 -0
- package/node_modules/@vellumai/avatar-catalog/src/types.ts +47 -0
- package/node_modules/@vellumai/avatar-catalog/tsconfig.json +20 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/guardian-request-contract.test.ts +8 -18
- package/node_modules/@vellumai/gateway-client/src/guardian-request-contract.ts +40 -18
- package/node_modules/@vellumai/gateway-client/src/index.ts +4 -4
- package/package.json +3 -1
- package/src/__tests__/guardian-card-withdrawal.test.ts +128 -5
- package/src/__tests__/guardian-expiry-notifier.test.ts +117 -13
- package/src/__tests__/guardian-gateway-sim.test.ts +72 -0
- package/src/__tests__/guardian-gateway-sim.ts +28 -15
- package/src/approvals/guardian-card-withdrawal.ts +61 -18
- package/src/approvals/guardian-expiry-notifier.ts +22 -16
- package/src/avatar/svg-compositor.ts +1 -1
- package/src/avatar/traits-png-sync.ts +1 -1
- package/src/calls/guardian-action-sweep.ts +6 -1
- package/src/channels/__tests__/gateway-guardian-requests.test.ts +12 -12
- package/src/channels/gateway-guardian-requests.ts +14 -13
- package/src/daemon/conversation-surfaces.ts +12 -2
- package/src/daemon/daemon-control.ts +6 -18
- package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +7 -0
- package/src/messaging/providers/slack/__tests__/auth.test.ts +35 -0
- package/src/messaging/providers/slack/api.test.ts +10 -0
- package/src/messaging/providers/slack/auth.ts +32 -6
- package/src/messaging/providers/telegram-bot/adapter.ts +5 -1
- package/src/messaging/providers/telegram-bot/withdraw.ts +13 -5
- package/src/monitoring/control.ts +2 -2
- package/src/notifications/guardian-delivery-recorder.ts +10 -3
- package/src/permissions/question-guardian-request.test.ts +1 -1
- package/src/plugins/defaults/memory/jobs-worker.ts +1 -2
- package/src/plugins/defaults/memory/src/__tests__/memory-worker-routes.test.ts +4 -7
- package/src/plugins/defaults/memory/src/memory-worker-routes.ts +1 -1
- package/src/plugins/defaults/memory/worker-control.ts +1 -2
- package/src/routes/__tests__/control.test.ts +4 -3
- package/src/routes/control.ts +1 -1
- package/src/routes/route-host-client.ts +1 -1
- package/src/runtime/AGENTS.md +23 -11
- package/src/runtime/routes/__tests__/monitoring-routes.test.ts +3 -4
- package/src/runtime/routes/__tests__/route-host-worker-routes.test.ts +3 -4
- package/src/runtime/routes/__tests__/schedule-worker-routes.test.ts +5 -6
- package/src/runtime/routes/avatar-routes.ts +1 -1
- package/src/runtime/routes/guardian-expiry-sweep.ts +80 -31
- package/src/runtime/routes/monitoring-routes.ts +0 -3
- package/src/runtime/routes/route-host-worker-routes.ts +1 -1
- package/src/runtime/routes/schedule-worker-routes.ts +1 -1
- package/src/schedule/scheduler.ts +1 -1
- package/src/schedule/worker-control.ts +2 -2
- package/src/util/__tests__/worker-ownership.test.ts +77 -0
- package/src/util/worker-ownership.ts +76 -9
- package/src/util/worker-process.ts +7 -18
- package/src/workspace/git-service.ts +1 -0
package/ARCHITECTURE.md
CHANGED
|
@@ -203,7 +203,7 @@ The guardian request system provides a channel-agnostic, unified domain for all
|
|
|
203
203
|
|
|
204
204
|
**Resolver registry:** Kind-specific resolvers (`src/approvals/guardian-request-resolvers.ts`) handle side effects after CAS resolution. Built-in resolvers: `tool_approval` (channel/desktop approval path), `pending_question` (voice call question path), and `access_request` (trusted-contact verification session creation). New request kinds register resolvers without touching the core primitive.
|
|
205
205
|
|
|
206
|
-
**Expiry:** the daemon's 60-second sweep (`src/runtime/routes/guardian-expiry-sweep.ts`)
|
|
206
|
+
**Expiry:** the daemon's 60-second sweep (`src/runtime/routes/guardian-expiry-sweep.ts`) lists a bounded batch of past-deadline pending requests (`guardian_requests_list_expired_pending`, read-only), fans out card withdrawal + requester notices per row, and only then confirms each with the per-request `guardian_requests_expire` CAS, so a lost response or crash leaves the row listed for the next round; interaction-bound kinds (`tool_approval`, `pending_question`) are expired at daemon boot via `guardian_requests_expire_interaction_bound` since they die with the in-memory pending-interactions map, and the boot call never touches persistent kinds. `src/calls/guardian-action-sweep.ts` sends the guardian-facing expiry notices for voice questions.
|
|
207
207
|
|
|
208
208
|
**Key source files:**
|
|
209
209
|
|
|
@@ -267,19 +267,19 @@ Guardian requests (tool approvals, access requests, voice ASK_GUARDIAN questions
|
|
|
267
267
|
|
|
268
268
|
**Voice consultation timeout:** When a voice call's ASK_GUARDIAN consultation times out before the guardian responds, the call controller expires the request via the gateway (`guardian_requests_expire` — deliveries expire with it), sends expiry notices to every surface the approval card reached (vellum conversations get an assistant message; external channels get a direct channel reply), and injects a `[GUARDIAN_TIMEOUT]` instruction so the model apologizes to the caller and offers a message or callback on the next voice turn.
|
|
269
269
|
|
|
270
|
-
**Background expiry sweep:** Independent of any active call, a 60-second sweep
|
|
270
|
+
**Background expiry sweep:** Independent of any active call, a 60-second sweep lists pending guardian requests past their `expiresAt` (`guardian_requests_list_expired_pending`), runs the side effects per row (withdrawing the approval cards on every surface, notifying the requester, releasing any in-memory pending interaction), and then confirms each with the per-request `guardian_requests_expire` CAS; a decision arriving past the deadline loses to expiry atomically, since the decide CAS guards on `expires_at`.
|
|
271
271
|
|
|
272
272
|
**Generated messaging requirement:** All user-facing copy in the guardian timeout/expiry path is generated through the `guardian-action-message-composer.ts` composition system, which uses a 2-tier priority chain: (1) daemon-injected LLM generator for natural, varied text; (2) deterministic fallback templates for reliability. No hardcoded user-facing strings exist in the flow files outside of internal log messages and LLM-instruction prompts. A guard test (`guardian-action-no-hardcoded-copy.test.ts`) enforces this invariant.
|
|
273
273
|
|
|
274
274
|
**Key source files:**
|
|
275
275
|
|
|
276
|
-
| File | Purpose
|
|
277
|
-
| ------------------------------------------------- |
|
|
278
|
-
| `src/channels/gateway-guardian-requests.ts` | Gateway-backed guardian-request client: typed wrappers over the `guardian_requests_*` IPC routes that own request/delivery state and expiry
|
|
279
|
-
| `src/runtime/routes/guardian-expiry-sweep.ts` | Periodic 60s sweep:
|
|
280
|
-
| `src/calls/call-controller.ts` | Voice timeout handling: expires the request, sends expiry notices, injects `[GUARDIAN_TIMEOUT]` instruction for the generated voice response
|
|
281
|
-
| `src/calls/guardian-action-sweep.ts` | Expiry notice delivery to guardian destinations (vellum conversation message or direct channel reply)
|
|
282
|
-
| `src/runtime/guardian-action-message-composer.ts` | 2-tier text generation: daemon-injected LLM generator with deterministic fallback templates
|
|
276
|
+
| File | Purpose |
|
|
277
|
+
| ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
278
|
+
| `src/channels/gateway-guardian-requests.ts` | Gateway-backed guardian-request client: typed wrappers over the `guardian_requests_*` IPC routes that own request/delivery state and expiry |
|
|
279
|
+
| `src/runtime/routes/guardian-expiry-sweep.ts` | Periodic 60s sweep: bounded expired-pending list, per-row card-withdrawal, requester-notice, and pending-interaction fan-out, then per-request expire CAS |
|
|
280
|
+
| `src/calls/call-controller.ts` | Voice timeout handling: expires the request, sends expiry notices, injects `[GUARDIAN_TIMEOUT]` instruction for the generated voice response |
|
|
281
|
+
| `src/calls/guardian-action-sweep.ts` | Expiry notice delivery to guardian destinations (vellum conversation message or direct channel reply) |
|
|
282
|
+
| `src/runtime/guardian-action-message-composer.ts` | 2-tier text generation: daemon-injected LLM generator with deterministic fallback templates |
|
|
283
283
|
|
|
284
284
|
### WhatsApp Channel (Meta Cloud API)
|
|
285
285
|
|
package/Dockerfile
CHANGED
|
@@ -117,6 +117,7 @@ RUN ln -sf /usr/local/bin/bun /usr/local/bin/bunx
|
|
|
117
117
|
COPY --from=builder /app /app
|
|
118
118
|
|
|
119
119
|
# Copy source after installing dependencies for better layer caching.
|
|
120
|
+
COPY packages/avatar-catalog /app/packages/avatar-catalog
|
|
120
121
|
COPY packages/avatar-manifest /app/packages/avatar-manifest
|
|
121
122
|
COPY packages/ces-client /app/packages/ces-client
|
|
122
123
|
COPY packages/service-contracts /app/packages/service-contracts
|
|
@@ -68,12 +68,12 @@ requested -> pending_guardian -> verification_pending -> active | denied | expir
|
|
|
68
68
|
|
|
69
69
|
Identity binding ensures the verification code can only be consumed by the intended recipient on the intended channel. The binding fields are set on the gateway `channel_verification_sessions` record when the session is created; the check itself is `checkIdentityMatch` (`gateway/src/verification/identity-match.ts`).
|
|
70
70
|
|
|
71
|
-
| Channel | Identity fields
|
|
72
|
-
| --------- |
|
|
73
|
-
| Telegram | `expectedExternalUserId` = Telegram user ID, `expectedChatId` = Telegram chat ID
|
|
74
|
-
| Voice | `expectedPhoneE164` = phone number in E.164 format
|
|
71
|
+
| Channel | Identity fields | Binding behavior |
|
|
72
|
+
| --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
73
|
+
| Telegram | `expectedExternalUserId` = Telegram user ID, `expectedChatId` = Telegram chat ID | Both are taken from the original denied message's metadata. The `identityBindingStatus` is `'bound'`. Verification requires the actor's user ID or chat ID to match (user ID required when both are set on a shared chat). |
|
|
74
|
+
| Voice | `expectedPhoneE164` = phone number in E.164 format | Phone-based identity binding. Verification requires the caller's number to match the expected phone. |
|
|
75
75
|
| Discord | `expectedExternalUserId` = Discord user snowflake. `expectedChatId` is unset on the guardian-initiated and trusted-contact paths, and is the originating guild channel on the access-request approval path (`mint_outbound_session`), which sets it for every channel alike. | The code is delivered to a DM and answered there, but the DM channel does not exist until it is opened, so those two paths have no chat id to record at mint time. Where the approval path does set one, it is a room rather than an identity and never becomes a sufficient credential on its own: `checkIdentityMatch` requires the `expectedExternalUserId` match whenever both fields are set. That path also writes the room into `destinationAddress`, where it describes nothing the code is ever sent to (see LUM-3110). |
|
|
76
|
-
| Bootstrap | none yet (`identityBindingStatus: 'pending_bootstrap'`)
|
|
76
|
+
| Bootstrap | none yet (`identityBindingStatus: 'pending_bootstrap'`) | Unbound deep-link sessions use a 32-byte hex token instead of a numeric code; the identity is bound when the token is redeemed (`verification_sessions_bind_identity`). |
|
|
77
77
|
|
|
78
78
|
**Anti-oracle invariant:** When identity verification fails, the error message is identical to the "invalid or expired code" message. This prevents attackers from distinguishing between a wrong code and a wrong identity, which would leak information about which identities have pending sessions.
|
|
79
79
|
|
|
@@ -129,10 +129,10 @@ Separately, the admitted-mode introduction **nudge** (for a contact who cleared
|
|
|
129
129
|
|
|
130
130
|
### Stage: `expired`
|
|
131
131
|
|
|
132
|
-
| Store | Table | Record
|
|
133
|
-
| ------------------------------------------------ | ------------------------------- |
|
|
134
|
-
| gateway `guardian-request-store.ts` (gateway DB) | `guardian_requests` | Updated to `status: 'expired'` via `
|
|
135
|
-
| `session-store.ts` (gateway DB) | `channel_verification_sessions` | Expires naturally: `expiresAt < Date.now()` makes it invisible to `findPendingSessionByHash()`.
|
|
132
|
+
| Store | Table | Record |
|
|
133
|
+
| ------------------------------------------------ | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
134
|
+
| gateway `guardian-request-store.ts` (gateway DB) | `guardian_requests` | Updated to `status: 'expired'` via the per-request `guardian_requests_expire` CAS, after the daemon's 60s sweep has run that row's side effects. |
|
|
135
|
+
| `session-store.ts` (gateway DB) | `channel_verification_sessions` | Expires naturally: `expiresAt < Date.now()` makes it invisible to `findPendingSessionByHash()`. |
|
|
136
136
|
|
|
137
137
|
### Invites (alternative path)
|
|
138
138
|
|
|
@@ -216,10 +216,11 @@ sequenceDiagram
|
|
|
216
216
|
|
|
217
217
|
else Guardian never responds
|
|
218
218
|
Note over A: runGuardianExpirySweep()<br/>runs every 60 seconds
|
|
219
|
-
A->>GW:
|
|
220
|
-
GW->>GW: Approval TTL elapsed → status: 'expired' (CAS)
|
|
219
|
+
A->>GW: guardian_requests_list_expired_pending (IPC)
|
|
221
220
|
A->>A: Withdraw approval cards on all surfaces
|
|
222
221
|
A-->>U: "Your access request has expired."
|
|
222
|
+
A->>GW: guardian_requests_expire (IPC)
|
|
223
|
+
GW->>GW: status: 'expired' (CAS, the fan-out's receipt)
|
|
223
224
|
|
|
224
225
|
else Code expires (requester never enters it)
|
|
225
226
|
Note over GW: Verification session TTL: 10 min
|
|
@@ -232,7 +233,7 @@ sequenceDiagram
|
|
|
232
233
|
|
|
233
234
|
### Guardian never responds
|
|
234
235
|
|
|
235
|
-
- The `runGuardianExpirySweep()` timer (`runtime/routes/guardian-expiry-sweep.ts`) runs every 60 seconds; the gateway
|
|
236
|
+
- The `runGuardianExpirySweep()` timer (`runtime/routes/guardian-expiry-sweep.ts`) runs every 60 seconds; the gateway returns a bounded, read-only batch of pending requests past their `expiresAt` (`guardian_requests_list_expired_pending`), and the daemon confirms each row with the per-request `guardian_requests_expire` CAS only after running its side effects, so the fan-out is recoverable from state.
|
|
236
237
|
- The daemon then withdraws the now-stale approval cards on every surface and notifies the requester that the request expired.
|
|
237
238
|
|
|
238
239
|
### Verification code expires
|
package/knip.json
CHANGED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vellumai/avatar-catalog",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"private": true,
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.ts",
|
|
9
|
+
"./colors": "./src/colors.ts"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"typecheck": "bunx tsc --noEmit",
|
|
13
|
+
"test": "bun test src/"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@types/bun": "1.3.11",
|
|
17
|
+
"typescript": "5.9.3"
|
|
18
|
+
}
|
|
19
|
+
}
|
package/{src/avatar/character-components.ts → node_modules/@vellumai/avatar-catalog/src/catalog.ts}
RENAMED
|
@@ -1,43 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
viewBox: { width: number; height: number };
|
|
4
|
-
faceCenter: { x: number; y: number };
|
|
5
|
-
svgPath: string;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
interface EyePathDefinition {
|
|
9
|
-
svgPath: string;
|
|
10
|
-
color: string; // hex e.g. "#F2F2F2"
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
interface EyeStyleDefinition {
|
|
14
|
-
id: string;
|
|
15
|
-
sourceViewBox: { width: number; height: number };
|
|
16
|
-
eyeCenter: { x: number; y: number };
|
|
17
|
-
paths: EyePathDefinition[];
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
interface ColorDefinition {
|
|
21
|
-
id: string;
|
|
22
|
-
hex: string; // e.g. "#4C9B50"
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
interface FaceCenterOverride {
|
|
26
|
-
bodyShape: string;
|
|
27
|
-
eyeStyle: string;
|
|
28
|
-
faceCenter: { x: number; y: number };
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
interface CharacterComponents {
|
|
32
|
-
bodyShapes: BodyShapeDefinition[];
|
|
33
|
-
eyeStyles: EyeStyleDefinition[];
|
|
34
|
-
colors: ColorDefinition[];
|
|
35
|
-
faceCenterOverrides: FaceCenterOverride[];
|
|
36
|
-
}
|
|
1
|
+
import { AVATAR_COLORS } from "./colors.js";
|
|
2
|
+
import type { CharacterComponents } from "./types.js";
|
|
37
3
|
|
|
38
4
|
const SCLERA = "#F2F2F2";
|
|
39
5
|
const PUPIL = "#1A1A1A";
|
|
40
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Every body shape, eye style, and face-centre override an avatar can be
|
|
9
|
+
* composed from, plus the palette re-exposed on `colors`.
|
|
10
|
+
*
|
|
11
|
+
* This is the single definition. The daemon serves it over
|
|
12
|
+
* `/avatar/character-components`; clients that must compose an avatar before
|
|
13
|
+
* the daemon answers (a hatching screen, a chooser card) import it directly
|
|
14
|
+
* so no copy has to be kept in step with this one.
|
|
15
|
+
*/
|
|
41
16
|
export function getCharacterComponents(): CharacterComponents {
|
|
42
17
|
return {
|
|
43
18
|
bodyShapes: [
|
|
@@ -369,14 +344,7 @@ export function getCharacterComponents(): CharacterComponents {
|
|
|
369
344
|
},
|
|
370
345
|
],
|
|
371
346
|
|
|
372
|
-
colors:
|
|
373
|
-
{ id: "green", hex: "#4C9B50" },
|
|
374
|
-
{ id: "orange", hex: "#E9642F" },
|
|
375
|
-
{ id: "pink", hex: "#DB4B77" },
|
|
376
|
-
{ id: "purple", hex: "#A665C9" },
|
|
377
|
-
{ id: "teal", hex: "#0E9B8B" },
|
|
378
|
-
{ id: "yellow", hex: "#E9C91A" },
|
|
379
|
-
],
|
|
347
|
+
colors: AVATAR_COLORS,
|
|
380
348
|
|
|
381
349
|
faceCenterOverrides: [
|
|
382
350
|
// Ghost body — shift non-native eyes from y=167 -> y=200
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ColorDefinition } from "./types.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The avatar colour palette, in the order the picker offers it.
|
|
5
|
+
*
|
|
6
|
+
* Its own module because the palette is the one part of the catalog that
|
|
7
|
+
* boot-path code needs: a sidebar pin resolves a stored colour id on first
|
|
8
|
+
* render, and the accent CSS variable falls back to the first entry before
|
|
9
|
+
* the daemon's own catalog arrives. Importing the shapes to reach six hex
|
|
10
|
+
* values would put ~47 kB of SVG path data on that path.
|
|
11
|
+
*/
|
|
12
|
+
export const AVATAR_COLORS: ColorDefinition[] = [
|
|
13
|
+
{ id: "green", hex: "#4C9B50" },
|
|
14
|
+
{ id: "orange", hex: "#E9642F" },
|
|
15
|
+
{ id: "pink", hex: "#DB4B77" },
|
|
16
|
+
{ id: "purple", hex: "#A665C9" },
|
|
17
|
+
{ id: "teal", hex: "#0E9B8B" },
|
|
18
|
+
{ id: "yellow", hex: "#E9C91A" },
|
|
19
|
+
];
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @vellumai/avatar-catalog: the single definition of the components an
|
|
3
|
+
* assistant's character avatar is composed from.
|
|
4
|
+
*
|
|
5
|
+
* The daemon serves this catalog over `/avatar/character-components` and
|
|
6
|
+
* renders PNGs from it; clients bundle it because an avatar has to compose
|
|
7
|
+
* before the daemon can answer (the hatching screen, the assistant chooser,
|
|
8
|
+
* a transcript's subagent chips). Bundling is why this is a package rather
|
|
9
|
+
* than a daemon module every client copies.
|
|
10
|
+
*
|
|
11
|
+
* The palette is deliberately not re-exported here. It lives behind
|
|
12
|
+
* `@vellumai/avatar-catalog/colors`, and importing it from that subpath is
|
|
13
|
+
* what keeps boot-path code (a sidebar pin resolving a stored colour id, the
|
|
14
|
+
* accent variable's fallback) off the ~47 kB of SVG path data the shapes
|
|
15
|
+
* carry. Re-exporting it would make the expensive import the convenient one.
|
|
16
|
+
*
|
|
17
|
+
* The shapes' types stay internal for the same reason they were private to
|
|
18
|
+
* the daemon module this package replaces: the wire contract for this data is
|
|
19
|
+
* the route's `responseBody` schema, which clients consume through their
|
|
20
|
+
* generated OpenAPI types. A hand-written type exported from here would be a
|
|
21
|
+
* second name for that shape, and the closer one to reach for.
|
|
22
|
+
*
|
|
23
|
+
* Leaf package: no dependencies, no runtime imports.
|
|
24
|
+
*/
|
|
25
|
+
export { getCharacterComponents } from "./catalog.js";
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shapes for the catalog literal in this package.
|
|
3
|
+
*
|
|
4
|
+
* Declared here rather than derived from the generated OpenAPI types because
|
|
5
|
+
* this is a leaf package with no dependencies, and the clients that consume
|
|
6
|
+
* those generated types depend on it rather than the other way round. The
|
|
7
|
+
* canonical wire shape is the `avatar/character-components` route's
|
|
8
|
+
* `responseBody` schema; these are not exported so no consumer picks them up
|
|
9
|
+
* as a second name for it.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export interface BodyShapeDefinition {
|
|
13
|
+
id: string;
|
|
14
|
+
viewBox: { width: number; height: number };
|
|
15
|
+
faceCenter: { x: number; y: number };
|
|
16
|
+
svgPath: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface EyePathDefinition {
|
|
20
|
+
svgPath: string;
|
|
21
|
+
color: string; // hex e.g. "#F2F2F2"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface EyeStyleDefinition {
|
|
25
|
+
id: string;
|
|
26
|
+
sourceViewBox: { width: number; height: number };
|
|
27
|
+
eyeCenter: { x: number; y: number };
|
|
28
|
+
paths: EyePathDefinition[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ColorDefinition {
|
|
32
|
+
id: string;
|
|
33
|
+
hex: string; // e.g. "#4C9B50"
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface FaceCenterOverride {
|
|
37
|
+
bodyShape: string;
|
|
38
|
+
eyeStyle: string;
|
|
39
|
+
faceCenter: { x: number; y: number };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface CharacterComponents {
|
|
43
|
+
bodyShapes: BodyShapeDefinition[];
|
|
44
|
+
eyeStyles: EyeStyleDefinition[];
|
|
45
|
+
colors: ColorDefinition[];
|
|
46
|
+
faceCenterOverrides: FaceCenterOverride[];
|
|
47
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"forceConsistentCasingInFileNames": true,
|
|
10
|
+
"resolveJsonModule": true,
|
|
11
|
+
"declaration": true,
|
|
12
|
+
"declarationMap": true,
|
|
13
|
+
"sourceMap": true,
|
|
14
|
+
"outDir": "./dist",
|
|
15
|
+
"rootDir": "./src",
|
|
16
|
+
"types": ["bun"]
|
|
17
|
+
},
|
|
18
|
+
"include": ["src/**/*"],
|
|
19
|
+
"exclude": ["node_modules", "dist"]
|
|
20
|
+
}
|
package/node_modules/@vellumai/gateway-client/src/__tests__/guardian-request-contract.test.ts
CHANGED
|
@@ -31,8 +31,7 @@ import {
|
|
|
31
31
|
ListGuardianRequestsIpcParamsSchema,
|
|
32
32
|
ListPendingGuardianRequestsByDestinationIpcParamsSchema,
|
|
33
33
|
ListPendingGuardianRequestsByScopeIpcParamsSchema,
|
|
34
|
-
|
|
35
|
-
SweepExpiredGuardianRequestsIpcResponseSchema,
|
|
34
|
+
ListExpiredPendingGuardianRequestsIpcParamsSchema,
|
|
36
35
|
UpdateGuardianRequestIpcParamsSchema,
|
|
37
36
|
type GuardianRequestWire,
|
|
38
37
|
} from "../guardian-request-contract.js";
|
|
@@ -439,29 +438,20 @@ describe("decide IPC schemas", () => {
|
|
|
439
438
|
});
|
|
440
439
|
|
|
441
440
|
describe("expiry IPC schemas", () => {
|
|
442
|
-
test("expire and
|
|
441
|
+
test("expire and expired-pending list round-trip", () => {
|
|
443
442
|
expect(ExpireGuardianRequestIpcParamsSchema.parse({ id: "req-1" })).toEqual(
|
|
444
443
|
{ id: "req-1" },
|
|
445
444
|
);
|
|
446
445
|
|
|
447
446
|
expect(
|
|
448
|
-
|
|
447
|
+
ListExpiredPendingGuardianRequestsIpcParamsSchema.parse({
|
|
449
448
|
now: 1_700_000_000_000,
|
|
449
|
+
limit: 50,
|
|
450
450
|
}),
|
|
451
|
-
).toEqual({ now: 1_700_000_000_000 });
|
|
452
|
-
expect(
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
expect(
|
|
456
|
-
SweepExpiredGuardianRequestsIpcResponseSchema.parse({
|
|
457
|
-
expired: [expiredRow],
|
|
458
|
-
}),
|
|
459
|
-
).toEqual({ expired: [expiredRow] });
|
|
460
|
-
expect(() =>
|
|
461
|
-
SweepExpiredGuardianRequestsIpcResponseSchema.parse({
|
|
462
|
-
expired: ["req-1"],
|
|
463
|
-
}),
|
|
464
|
-
).toThrow();
|
|
451
|
+
).toEqual({ now: 1_700_000_000_000, limit: 50 });
|
|
452
|
+
expect(ListExpiredPendingGuardianRequestsIpcParamsSchema.parse({})).toEqual(
|
|
453
|
+
{},
|
|
454
|
+
);
|
|
465
455
|
|
|
466
456
|
expect(
|
|
467
457
|
ExpireInteractionBoundIpcResponseSchema.parse({ expired: 3 }),
|
|
@@ -189,7 +189,7 @@ export const GUARDIAN_REQUESTS_IPC_METHODS = {
|
|
|
189
189
|
decide: "guardian_requests_decide",
|
|
190
190
|
expire: "guardian_requests_expire",
|
|
191
191
|
expireInteractionBound: "guardian_requests_expire_interaction_bound",
|
|
192
|
-
|
|
192
|
+
listExpiredPending: "guardian_requests_list_expired_pending",
|
|
193
193
|
createDelivery: "guardian_requests_create_delivery",
|
|
194
194
|
updateDelivery: "guardian_requests_update_delivery",
|
|
195
195
|
listDeliveries: "guardian_requests_list_deliveries",
|
|
@@ -201,6 +201,32 @@ export const GUARDIAN_REQUESTS_IPC_METHODS = {
|
|
|
201
201
|
getByPendingQuestion: "guardian_requests_get_by_pending_question",
|
|
202
202
|
} as const;
|
|
203
203
|
|
|
204
|
+
/**
|
|
205
|
+
* The delivery-row statuses this codebase writes and matches on.
|
|
206
|
+
*
|
|
207
|
+
* `GuardianRequestDeliverySchema.status` stays a plain `z.string()` rather
|
|
208
|
+
* than an enum built from these: the column is persisted, so a row written
|
|
209
|
+
* by a different build must still read rather than throw on parse. These
|
|
210
|
+
* constants exist so a call site names a value instead of spelling it, not
|
|
211
|
+
* to close the set.
|
|
212
|
+
*
|
|
213
|
+
* `pending` is the creation default; `sent` and `failed` carry the
|
|
214
|
+
* notification delivery result the recorder maps in; `expired` is stamped
|
|
215
|
+
* when the request itself expires; `withdrawn` is the daemon's per-surface
|
|
216
|
+
* receipt that a card was durably withdrawn, which the gateway's
|
|
217
|
+
* per-request expire preserves and a retrying withdrawal skips.
|
|
218
|
+
*/
|
|
219
|
+
export const DELIVERY_STATUS = {
|
|
220
|
+
pending: "pending",
|
|
221
|
+
sent: "sent",
|
|
222
|
+
failed: "failed",
|
|
223
|
+
expired: "expired",
|
|
224
|
+
withdrawn: "withdrawn",
|
|
225
|
+
} as const;
|
|
226
|
+
|
|
227
|
+
export type DeliveryStatus =
|
|
228
|
+
(typeof DELIVERY_STATUS)[keyof typeof DELIVERY_STATUS];
|
|
229
|
+
|
|
204
230
|
export type GuardianRequestsIpcMethod =
|
|
205
231
|
(typeof GUARDIAN_REQUESTS_IPC_METHODS)[keyof typeof GUARDIAN_REQUESTS_IPC_METHODS];
|
|
206
232
|
|
|
@@ -418,7 +444,7 @@ const OUTCOME_TYPES_BY_DECISION_STATUS: Record<
|
|
|
418
444
|
/**
|
|
419
445
|
* Request for `guardian_requests_decide` (status CAS + optional ACL outcome).
|
|
420
446
|
* Decisions only resolve a pending request to approved/denied — expiry has
|
|
421
|
-
* `guardian_requests_expire`/`
|
|
447
|
+
* `guardian_requests_expire`/`_list_expired_pending`, so a malformed call can never
|
|
422
448
|
* apply an `aclOutcome` while leaving the request decidable again. The
|
|
423
449
|
* outcome type must agree with the status: activation/minting only on
|
|
424
450
|
* approval, seeding/blocking only on denial.
|
|
@@ -508,26 +534,22 @@ export type ExpireInteractionBoundIpcResponse = z.infer<
|
|
|
508
534
|
typeof ExpireInteractionBoundIpcResponseSchema
|
|
509
535
|
>;
|
|
510
536
|
|
|
511
|
-
/** Request for `guardian_requests_sweep_expired` (`now` defaults gateway-side). */
|
|
512
|
-
export const SweepExpiredGuardianRequestsIpcParamsSchema = z.object({
|
|
513
|
-
now: z.number().optional(),
|
|
514
|
-
});
|
|
515
|
-
|
|
516
|
-
export type SweepExpiredGuardianRequestsIpcParams = z.infer<
|
|
517
|
-
typeof SweepExpiredGuardianRequestsIpcParamsSchema
|
|
518
|
-
>;
|
|
519
|
-
|
|
520
537
|
/**
|
|
521
|
-
*
|
|
522
|
-
*
|
|
523
|
-
*
|
|
538
|
+
* Request for `guardian_requests_list_expired_pending` (`now` defaults
|
|
539
|
+
* gateway-side; `limit` bounds the batch and is capped gateway-side).
|
|
540
|
+
* Read-only: the rows stay `pending` until the daemon has run each one's
|
|
541
|
+
* expiry side effects and confirms with `guardian_requests_expire`, so a
|
|
542
|
+
* lost response leaves the work discoverable by the next sweep round
|
|
543
|
+
* instead of silently done. Past-deadline pending rows are undecidable
|
|
544
|
+
* either way: every decision path checks `expiresAt` before any write.
|
|
524
545
|
*/
|
|
525
|
-
export const
|
|
526
|
-
|
|
546
|
+
export const ListExpiredPendingGuardianRequestsIpcParamsSchema = z.object({
|
|
547
|
+
now: z.number().optional(),
|
|
548
|
+
limit: z.number().optional(),
|
|
527
549
|
});
|
|
528
550
|
|
|
529
|
-
export type
|
|
530
|
-
typeof
|
|
551
|
+
export type ListExpiredPendingGuardianRequestsIpcParams = z.infer<
|
|
552
|
+
typeof ListExpiredPendingGuardianRequestsIpcParamsSchema
|
|
531
553
|
>;
|
|
532
554
|
|
|
533
555
|
// ---------------------------------------------------------------------------
|
|
@@ -268,8 +268,8 @@ export {
|
|
|
268
268
|
ListGuardianRequestsIpcParamsSchema,
|
|
269
269
|
ListPendingGuardianRequestsByDestinationIpcParamsSchema,
|
|
270
270
|
ListPendingGuardianRequestsByScopeIpcParamsSchema,
|
|
271
|
-
|
|
272
|
-
|
|
271
|
+
DELIVERY_STATUS,
|
|
272
|
+
ListExpiredPendingGuardianRequestsIpcParamsSchema,
|
|
273
273
|
UpdateGuardianRequestDeliveryIpcParamsSchema,
|
|
274
274
|
UpdateGuardianRequestIpcParamsSchema,
|
|
275
275
|
} from "./guardian-request-contract.js";
|
|
@@ -279,6 +279,7 @@ export type {
|
|
|
279
279
|
CreateGuardianRequestIpcParams,
|
|
280
280
|
DecideGuardianRequestIpcParams,
|
|
281
281
|
DecideGuardianRequestIpcResponse,
|
|
282
|
+
DeliveryStatus,
|
|
282
283
|
ExpireGuardianRequestIpcParams,
|
|
283
284
|
ExpireInteractionBoundIpcParams,
|
|
284
285
|
ExpireInteractionBoundIpcResponse,
|
|
@@ -306,8 +307,7 @@ export type {
|
|
|
306
307
|
ListGuardianRequestsIpcParams,
|
|
307
308
|
ListPendingGuardianRequestsByDestinationIpcParams,
|
|
308
309
|
ListPendingGuardianRequestsByScopeIpcParams,
|
|
309
|
-
|
|
310
|
-
SweepExpiredGuardianRequestsIpcResponse,
|
|
310
|
+
ListExpiredPendingGuardianRequestsIpcParams,
|
|
311
311
|
UpdateGuardianRequestDeliveryIpcParams,
|
|
312
312
|
UpdateGuardianRequestIpcParams,
|
|
313
313
|
} from "./guardian-request-contract.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vellumai/assistant",
|
|
3
|
-
"version": "0.11.7-dev.
|
|
3
|
+
"version": "0.11.7-dev.202608282312.37594b7",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"@qdrant/js-client-rest": "1.17.0",
|
|
44
44
|
"@resvg/resvg-js": "2.6.2",
|
|
45
45
|
"@slack/types": "2.22.0",
|
|
46
|
+
"@vellumai/avatar-catalog": "file:../packages/avatar-catalog",
|
|
46
47
|
"@vellumai/avatar-manifest": "file:../packages/avatar-manifest",
|
|
47
48
|
"@vellumai/ces-client": "file:../packages/ces-client",
|
|
48
49
|
"@vellumai/credential-storage": "file:../packages/credential-storage",
|
|
@@ -87,6 +88,7 @@
|
|
|
87
88
|
"node": ">=20.12.0"
|
|
88
89
|
},
|
|
89
90
|
"bundledDependencies": [
|
|
91
|
+
"@vellumai/avatar-catalog",
|
|
90
92
|
"@vellumai/avatar-manifest",
|
|
91
93
|
"@vellumai/ces-client",
|
|
92
94
|
"@vellumai/credential-storage",
|