@oneie/sdk 0.14.7 → 0.14.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -1
- package/dist/broadcast.d.ts +25 -0
- package/dist/broadcast.d.ts.map +1 -1
- package/dist/gateway.d.ts +4 -0
- package/dist/gateway.d.ts.map +1 -1
- package/dist/gateway.js +5 -1
- package/dist/gateway.js.map +1 -1
- package/dist/generated/schemas.d.ts +6 -0
- package/dist/generated/schemas.d.ts.map +1 -1
- package/dist/generated/schemas.js +6 -0
- package/dist/generated/schemas.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/receivers.d.ts +293 -6
- package/dist/receivers.d.ts.map +1 -1
- package/dist/receivers.js +321 -18
- package/dist/receivers.js.map +1 -1
- package/dist/schemas.d.ts +0 -1
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +0 -1
- package/dist/schemas.js.map +1 -1
- package/dist/telemetry.d.ts +2 -1
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +38 -26
- package/dist/telemetry.js.map +1 -1
- package/dist/work-contract-grammar.json +10 -0
- package/package.json +1 -1
package/dist/receivers.js
CHANGED
|
@@ -668,28 +668,39 @@ export const RECEIVERS = {
|
|
|
668
668
|
}),
|
|
669
669
|
"world:announce": receiver({
|
|
670
670
|
receiver: "world:announce",
|
|
671
|
-
summary: "Broadcast a tagged signal; the world fans it to every subscriber whose follow-tags intersect",
|
|
671
|
+
summary: "Broadcast a tagged signal; the world fans it to every subscriber whose follow-tags intersect. reply:true also invokes up to 5 of the matched actors and returns their answers inline (repliesTruncated:true if more matched)",
|
|
672
672
|
request: z.object({
|
|
673
673
|
tags: z.array(z.string()),
|
|
674
674
|
content: z.string().optional(),
|
|
675
|
+
limit: z.number().optional(),
|
|
676
|
+
reply: z.boolean().optional(),
|
|
675
677
|
board: z.literal("marketplace").optional(),
|
|
676
678
|
slug: z.string().optional(),
|
|
677
679
|
workspace: z.string().optional(),
|
|
680
|
+
}).catchall(z.unknown()), // extra keys = the message payload — carried through to every fanned-out actor, never stripped
|
|
681
|
+
response: z.object({
|
|
682
|
+
ok: z.boolean(),
|
|
683
|
+
matched: z.number().optional(),
|
|
684
|
+
actors: z.array(z.string()).optional(),
|
|
685
|
+
replies: z.array(z.object({
|
|
686
|
+
actorId: z.string(), ok: z.boolean(), text: z.string().optional(), error: z.string().optional(),
|
|
687
|
+
})).optional(),
|
|
688
|
+
repliesTruncated: z.boolean().optional(),
|
|
678
689
|
}),
|
|
679
|
-
response: z.object({ ok: z.boolean(), matched: z.number().optional(), actors: z.array(z.string()).optional() }),
|
|
680
690
|
effect: "ask",
|
|
681
691
|
}),
|
|
682
692
|
"world": receiver({
|
|
683
693
|
receiver: "world",
|
|
684
|
-
summary: "THE door — say it once, the world decides. Routes a tagged signal via the ladder (learned path → staked subscribers → CEO); fan:'all' broadcasts to every matching subscriber instead. Address form world:<tag-expr> carries tags in the receiver",
|
|
694
|
+
summary: "THE door — say it once, the world decides. Routes a tagged signal via the ladder (learned path → staked subscribers → CEO); fan:'all' broadcasts to every matching subscriber instead. Address form world:<tag-expr> carries tags in the receiver. reply:true also invokes the resolved actor and returns its answer inline (single-actor routes only, not fan:'all')",
|
|
685
695
|
request: z.object({
|
|
686
696
|
tags: z.array(z.string()).optional(),
|
|
687
697
|
fan: z.enum(["one", "all"]).optional(),
|
|
688
698
|
deliver: z.boolean().optional(),
|
|
699
|
+
reply: z.boolean().optional(),
|
|
689
700
|
board: z.literal("marketplace").optional(),
|
|
690
701
|
slug: z.string().optional(),
|
|
691
702
|
workspace: z.string().optional(),
|
|
692
|
-
}),
|
|
703
|
+
}).catchall(z.unknown()), // extra keys = the message payload (e.g. content) — carried through to the resolved actor, never stripped
|
|
693
704
|
response: z.object({
|
|
694
705
|
ok: z.boolean(),
|
|
695
706
|
routed: z.enum(["learned", "staked", "explore", "ceo"]).optional(),
|
|
@@ -697,6 +708,7 @@ export const RECEIVERS = {
|
|
|
697
708
|
matched: z.number().optional(),
|
|
698
709
|
actors: z.array(z.string()).optional(),
|
|
699
710
|
tags: z.array(z.string()).optional(),
|
|
711
|
+
reply: z.object({ ok: z.boolean(), text: z.string().optional(), error: z.string().optional() }).optional(),
|
|
700
712
|
}),
|
|
701
713
|
effect: "ask",
|
|
702
714
|
}),
|
|
@@ -722,19 +734,21 @@ export const RECEIVERS = {
|
|
|
722
734
|
}),
|
|
723
735
|
"world:route": receiver({
|
|
724
736
|
receiver: "world:route",
|
|
725
|
-
summary: "Route a tagged signal to THE handler via the ladder: learned tag-node path (follow_route) → staked subscribers (reputation-ranked) → CEO standing workflow; marks the delivery path so the next route is smarter",
|
|
737
|
+
summary: "Route a tagged signal to THE handler via the ladder: learned tag-node path (follow_route) → staked subscribers (reputation-ranked) → CEO standing workflow; marks the delivery path so the next route is smarter. reply:true also invokes the resolved actor and returns its answer inline",
|
|
726
738
|
request: z.object({
|
|
727
739
|
tags: z.array(z.string()),
|
|
728
740
|
deliver: z.boolean().optional(),
|
|
741
|
+
reply: z.boolean().optional(),
|
|
729
742
|
board: z.literal("marketplace").optional(),
|
|
730
743
|
slug: z.string().optional(),
|
|
731
744
|
workspace: z.string().optional(),
|
|
732
|
-
}),
|
|
745
|
+
}).catchall(z.unknown()), // extra keys = the message payload (e.g. content) — carried through to the resolved actor, never stripped
|
|
733
746
|
response: z.object({
|
|
734
747
|
ok: z.boolean(),
|
|
735
748
|
routed: z.enum(["learned", "staked", "explore", "ceo"]).optional(),
|
|
736
749
|
actor: z.string().optional(),
|
|
737
750
|
tags: z.array(z.string()).optional(),
|
|
751
|
+
reply: z.object({ ok: z.boolean(), text: z.string().optional(), error: z.string().optional() }).optional(),
|
|
738
752
|
}),
|
|
739
753
|
effect: "ask",
|
|
740
754
|
}),
|
|
@@ -762,8 +776,129 @@ export const RECEIVERS = {
|
|
|
762
776
|
name: z.string(),
|
|
763
777
|
tags: z.array(z.string()),
|
|
764
778
|
weight: z.number(),
|
|
779
|
+
// The context a puller needs to act: notes = the prose goal / what "done" looks
|
|
780
|
+
// like; contextDocs = the doc stems (`<slug>-plan`, `<slug>-todo`, …) to load
|
|
781
|
+
// before running /do <slug>. Empty until the task is enriched (seed/backfill).
|
|
782
|
+
notes: z.string().optional(),
|
|
783
|
+
contextDocs: z.array(z.string()).optional(),
|
|
784
|
+
})),
|
|
785
|
+
}),
|
|
786
|
+
effect: "ask", idempotent: true,
|
|
787
|
+
}),
|
|
788
|
+
"tasks:everywhere": receiver({
|
|
789
|
+
receiver: "tasks:everywhere",
|
|
790
|
+
summary: "Every workspace I belong to, one queue: open+picked tasks visible by assignment/follow/ownership, with claimable + mine + offeredByMe flags; closed:true returns the logbook (done/verified, newest first)",
|
|
791
|
+
request: z.object({ limit: z.number().optional(), closed: z.boolean().optional() }),
|
|
792
|
+
response: z.object({
|
|
793
|
+
ok: z.boolean(),
|
|
794
|
+
tasks: z.array(z.object({
|
|
795
|
+
id: z.string(),
|
|
796
|
+
name: z.string(),
|
|
797
|
+
tags: z.array(z.string()),
|
|
798
|
+
weight: z.number(),
|
|
799
|
+
notes: z.string().optional(),
|
|
800
|
+
contextDocs: z.array(z.string()).optional(),
|
|
801
|
+
workspace: z.string(),
|
|
802
|
+
status: z.string(),
|
|
803
|
+
dueAt: z.string().optional(),
|
|
804
|
+
assignee: z.string().optional(),
|
|
805
|
+
mine: z.boolean(),
|
|
806
|
+
claimable: z.boolean(),
|
|
807
|
+
offeredByMe: z.boolean(),
|
|
808
|
+
closedAt: z.string().optional(),
|
|
809
|
+
})),
|
|
810
|
+
}),
|
|
811
|
+
effect: "ask", idempotent: true,
|
|
812
|
+
}),
|
|
813
|
+
"tasks:create": receiver({
|
|
814
|
+
receiver: "tasks:create",
|
|
815
|
+
summary: "Write one open task to the substrate and announce it by its tags in the same act — the quick-add entry point",
|
|
816
|
+
request: z.object({
|
|
817
|
+
title: z.string(),
|
|
818
|
+
tags: z.array(z.string()).optional(),
|
|
819
|
+
assignee: z.string().optional(),
|
|
820
|
+
// Prose goal / what "done" looks like — the context a human attaches at create so
|
|
821
|
+
// the task is pickable by an agent or a Claude Code session without a round trip.
|
|
822
|
+
notes: z.string().optional(),
|
|
823
|
+
// The viewed /u/<slug> workspace to file the task under. Honored only when the
|
|
824
|
+
// caller is authorized for it (attested staff or owner-tree control); otherwise
|
|
825
|
+
// the resolver falls back to the caller's own slug. Reconciles the create tag
|
|
826
|
+
// with the /api/things read filter so a created task survives reload.
|
|
827
|
+
workspace: z.string().optional(),
|
|
828
|
+
}),
|
|
829
|
+
response: z.object({ ok: z.boolean(), tid: z.string().optional(), tags: z.array(z.string()).optional() }),
|
|
830
|
+
effect: "ask", idempotent: false,
|
|
831
|
+
}),
|
|
832
|
+
"tasks:claim": receiver({
|
|
833
|
+
receiver: "tasks:claim",
|
|
834
|
+
summary: "Take a task off the queue: blocker-gate → status picked → tag the claimant @<slug>. The claimant is the attested caller, never a body field",
|
|
835
|
+
request: z.object({
|
|
836
|
+
tid: z.string(),
|
|
837
|
+
// Viewed workspace — same authorization contract as tasks:create's workspace.
|
|
838
|
+
workspace: z.string().optional(),
|
|
839
|
+
}),
|
|
840
|
+
response: z.object({
|
|
841
|
+
ok: z.boolean().optional(),
|
|
842
|
+
tid: z.string().optional(),
|
|
843
|
+
status: z.string().optional(),
|
|
844
|
+
claimant: z.string().optional(),
|
|
845
|
+
error: z.string().optional(),
|
|
846
|
+
}),
|
|
847
|
+
effect: "ask", idempotent: true,
|
|
848
|
+
}),
|
|
849
|
+
"tasks:undepend": receiver({
|
|
850
|
+
receiver: "tasks:undepend",
|
|
851
|
+
summary: "Remove one blocks edge — the inverse of tasks:depend. Deletes exactly the (blockedBy → tid) pair; does not touch task status. Auth: attested caller only, both ends in the caller's workspace",
|
|
852
|
+
request: z.object({
|
|
853
|
+
tid: z.string(),
|
|
854
|
+
blockedBy: z.string(),
|
|
855
|
+
workspace: z.string().optional(),
|
|
856
|
+
}),
|
|
857
|
+
response: z.object({
|
|
858
|
+
ok: z.boolean().optional(),
|
|
859
|
+
tid: z.string().optional(),
|
|
860
|
+
blockedBy: z.string().optional(),
|
|
861
|
+
error: z.string().optional(),
|
|
862
|
+
}),
|
|
863
|
+
effect: "ask", idempotent: true,
|
|
864
|
+
}),
|
|
865
|
+
"tasks:launch": receiver({
|
|
866
|
+
receiver: "tasks:launch",
|
|
867
|
+
summary: "Batch-launch ready agent-assigned tasks: per task re-check blockers, resolve the @assignee, set picked, fire agent:run, mark owner→assignee on success or revert to open + warn on failure. Per-task receipts — a batch is never all-or-nothing. Caps at 20 tids; skips human assignees",
|
|
868
|
+
request: z.object({
|
|
869
|
+
tids: z.array(z.string()),
|
|
870
|
+
workspace: z.string().optional(),
|
|
871
|
+
}),
|
|
872
|
+
response: z.object({
|
|
873
|
+
ok: z.boolean(),
|
|
874
|
+
results: z.array(z.object({
|
|
875
|
+
tid: z.string(),
|
|
876
|
+
ok: z.boolean(),
|
|
877
|
+
actorId: z.string().optional(),
|
|
878
|
+
reason: z.enum(["unassigned", "human-assignee", "blocked", "forbidden", "fire_failed"]).optional(),
|
|
765
879
|
})),
|
|
766
880
|
}),
|
|
881
|
+
effect: "ask", idempotent: false,
|
|
882
|
+
}),
|
|
883
|
+
"tasks:link": receiver({
|
|
884
|
+
receiver: "tasks:link",
|
|
885
|
+
summary: "Close the handoff: record that a claimed task was built via /do <slug> — append a provenance line to the task's notes (the id back-ref + doc stems) and move it to done. Auth: attested caller only",
|
|
886
|
+
request: z.object({
|
|
887
|
+
tid: z.string(),
|
|
888
|
+
// The kebab /do slug the task was built into (the docs backfilled under text/<slug>*).
|
|
889
|
+
slug: z.string(),
|
|
890
|
+
// Doc stems the /do run produced (e.g. ["usage-billing", "usage-billing-plan"]).
|
|
891
|
+
docs: z.array(z.string()).optional(),
|
|
892
|
+
workspace: z.string().optional(),
|
|
893
|
+
}),
|
|
894
|
+
response: z.object({
|
|
895
|
+
ok: z.boolean().optional(),
|
|
896
|
+
tid: z.string().optional(),
|
|
897
|
+
slug: z.string().optional(),
|
|
898
|
+
docs: z.array(z.string()).optional(),
|
|
899
|
+
status: z.string().optional(),
|
|
900
|
+
error: z.string().optional(),
|
|
901
|
+
}),
|
|
767
902
|
effect: "ask", idempotent: true,
|
|
768
903
|
}),
|
|
769
904
|
// ── agents (TRADE / lifecycle) ──
|
|
@@ -1236,12 +1371,13 @@ export const RECEIVERS = {
|
|
|
1236
1371
|
}),
|
|
1237
1372
|
"notify": receiver({
|
|
1238
1373
|
receiver: "notify",
|
|
1239
|
-
summary: "Notify any actor (human or agent) by uid — channels routes the door (Telegram/Discord/peer inbox), mirrors into the web inbox,
|
|
1374
|
+
summary: "Notify any actor (human or agent) by uid — channels routes the door (Telegram/Discord/peer inbox), mirrors into the web inbox, nudges an open client live, and sends a web push if the recipient subscribed. One verb, both actor kinds. An optional action renders as one tap-through link.",
|
|
1240
1375
|
request: z.object({
|
|
1241
1376
|
receiver: z.string(),
|
|
1242
1377
|
kind: z.string().optional(),
|
|
1243
1378
|
body: z.union([z.string(), z.record(z.string(), z.unknown())]).optional(),
|
|
1244
1379
|
content: z.string().optional(),
|
|
1380
|
+
action: z.object({ label: z.string(), redirect: z.string() }).optional(),
|
|
1245
1381
|
}),
|
|
1246
1382
|
response: z.object({
|
|
1247
1383
|
ok: z.boolean(),
|
|
@@ -1276,6 +1412,17 @@ export const RECEIVERS = {
|
|
|
1276
1412
|
response: z.object({ ok: z.boolean(), ts: z.number().optional(), error: z.string().optional() }),
|
|
1277
1413
|
effect: "ask", idempotent: true,
|
|
1278
1414
|
}),
|
|
1415
|
+
"push:subscribe": receiver({
|
|
1416
|
+
receiver: "push:subscribe",
|
|
1417
|
+
summary: "Store a browser's Web Push subscription so notify() can reach the caller off-screen — the send side behind the shipped inbox service worker. Optional slug keys the row to a workspace the caller controls (else their own slug).",
|
|
1418
|
+
request: z.object({
|
|
1419
|
+
endpoint: z.string(),
|
|
1420
|
+
keys: z.object({ p256dh: z.string(), auth: z.string() }),
|
|
1421
|
+
slug: z.string().optional(),
|
|
1422
|
+
}),
|
|
1423
|
+
response: z.object({ ok: z.boolean(), error: z.string().optional() }),
|
|
1424
|
+
effect: "ask", idempotent: true,
|
|
1425
|
+
}),
|
|
1279
1426
|
"thread:append": receiver({
|
|
1280
1427
|
receiver: "thread:append",
|
|
1281
1428
|
summary: "Gateway-callable write into web threads — carries tags into entity_tags so slug/in renders every channel by tag. Called by the channels bridge after every inbound signal.",
|
|
@@ -1893,7 +2040,14 @@ export const RECEIVERS = {
|
|
|
1893
2040
|
response: z.object({ ok: z.boolean(), threadId: z.string().optional(), error: z.string().optional() }),
|
|
1894
2041
|
effect: "ask", cost: "free", reversible: false, idempotent: false, auth: "member",
|
|
1895
2042
|
}),
|
|
1896
|
-
|
|
2043
|
+
"pages:view": receiver({
|
|
2044
|
+
receiver: "pages:view",
|
|
2045
|
+
summary: "Read one published page's Puck render data by workspace + page slug (public) — the read an external site's page-editor plugin renders through; drafts are invisible (see pages:get for the member-scoped editor read)",
|
|
2046
|
+
request: z.object({ slug: z.string(), page: z.string() }),
|
|
2047
|
+
response: z.object({ ok: z.boolean(), slug: z.string().optional(), title: z.string().optional(), data: z.unknown().optional(), url: z.string().optional(), error: z.string().optional() }),
|
|
2048
|
+
effect: "ask", cost: "free", reversible: true, idempotent: true, auth: "public",
|
|
2049
|
+
}),
|
|
2050
|
+
// ──billing — handlers in resolvers/market.ts ──────────────────────────────────
|
|
1897
2051
|
"billing:plan": receiver({
|
|
1898
2052
|
receiver: "billing:plan",
|
|
1899
2053
|
summary: "Change a workspace's plan — returns a checkout URL to complete the upgrade",
|
|
@@ -1940,6 +2094,14 @@ export const RECEIVERS = {
|
|
|
1940
2094
|
service: z.object({
|
|
1941
2095
|
service_id: z.string(), name: z.string(), duration_min: z.number(),
|
|
1942
2096
|
price: z.number(), currency: z.string(),
|
|
2097
|
+
description: z.string().nullable().optional(), // shown on /book, read by the agent (§12.2)
|
|
2098
|
+
location: z.string().nullable().optional(), // 'video' | 'phone' | free-text address
|
|
2099
|
+
intake: z.array(z.object({
|
|
2100
|
+
key: z.string(), label: z.string(),
|
|
2101
|
+
type: z.enum(["text", "number", "choice"]).optional(),
|
|
2102
|
+
required: z.boolean().optional(),
|
|
2103
|
+
options: z.array(z.string()).optional(),
|
|
2104
|
+
})).optional(), // per-service intake questions — both doors ask them
|
|
1943
2105
|
}).nullable(),
|
|
1944
2106
|
slots: z.array(z.object({ date: z.string(), times: z.array(z.string()) })), // CalendarCard shape
|
|
1945
2107
|
error: z.string().optional(),
|
|
@@ -1947,6 +2109,35 @@ export const RECEIVERS = {
|
|
|
1947
2109
|
effect: "ask", cost: "free", idempotent: true, simulatable: false,
|
|
1948
2110
|
examples: [{ slug: "clearwater", date: "2026-06-10" }],
|
|
1949
2111
|
}),
|
|
2112
|
+
// booking:list-services — public discover: the service menu (§12.3). Lets the
|
|
2113
|
+
// chat agent offer "in-home survey, video walkthrough, or storage?" instead of
|
|
2114
|
+
// silently defaulting to the first active row. Active services, public fields
|
|
2115
|
+
// only (hours/buffer are inferable from list-slots output, so not secrets).
|
|
2116
|
+
"booking:list-services": receiver({
|
|
2117
|
+
receiver: "booking:list-services",
|
|
2118
|
+
summary: "List a workspace's active bookable services (menu: name, duration, price, description, location, intake)",
|
|
2119
|
+
request: z.object({ slug: z.string() }),
|
|
2120
|
+
response: z.object({
|
|
2121
|
+
services: z.array(z.object({
|
|
2122
|
+
service_id: z.string(), name: z.string(), duration_min: z.number(),
|
|
2123
|
+
price: z.number(), currency: z.string(),
|
|
2124
|
+
description: z.string().nullable().optional(),
|
|
2125
|
+
location: z.string().nullable().optional(),
|
|
2126
|
+
buffer_min: z.number().optional(),
|
|
2127
|
+
tz: z.string().optional(),
|
|
2128
|
+
hours_json: z.array(z.array(z.object({ start: z.string(), end: z.string() }))).optional(),
|
|
2129
|
+
intake: z.array(z.object({
|
|
2130
|
+
key: z.string(), label: z.string(),
|
|
2131
|
+
type: z.enum(["text", "number", "choice"]).optional(),
|
|
2132
|
+
required: z.boolean().optional(),
|
|
2133
|
+
options: z.array(z.string()).optional(),
|
|
2134
|
+
})).optional(),
|
|
2135
|
+
})),
|
|
2136
|
+
error: z.string().optional(),
|
|
2137
|
+
}),
|
|
2138
|
+
effect: "ask", cost: "free", idempotent: true, auth: "public",
|
|
2139
|
+
examples: [{ slug: "clearwater" }],
|
|
2140
|
+
}),
|
|
1950
2141
|
"booking:create": receiver({
|
|
1951
2142
|
receiver: "booking:create",
|
|
1952
2143
|
summary: "Hold a slot for a customer; returns a ref and a payment intent when price > 0",
|
|
@@ -1955,7 +2146,12 @@ export const RECEIVERS = {
|
|
|
1955
2146
|
service: z.string(),
|
|
1956
2147
|
slotDate: z.string(), // 'YYYY-MM-DD'
|
|
1957
2148
|
slotTime: z.string(), // 'HH:MM' 24h, slot start
|
|
1958
|
-
customer: z.object({
|
|
2149
|
+
customer: z.object({
|
|
2150
|
+
name: z.string(),
|
|
2151
|
+
email: z.string().email().optional(),
|
|
2152
|
+
phone: z.string().optional(), // speed-to-lead callback number (§12.3)
|
|
2153
|
+
}),
|
|
2154
|
+
details: z.union([z.string(), z.record(z.string(), z.unknown())]).optional(), // intake answers OR free text; 2KB serialized cap at the resolver, never shape-validated
|
|
1959
2155
|
paymentRail: z.enum(["stripe", "sui"]).optional(), // resolver overrides by caller kind
|
|
1960
2156
|
}),
|
|
1961
2157
|
response: z.object({
|
|
@@ -1965,7 +2161,7 @@ export const RECEIVERS = {
|
|
|
1965
2161
|
paymentRail: z.enum(["stripe", "sui"]).nullable().optional(),
|
|
1966
2162
|
paymentIntent: z.object({ clientSecret: z.string(), amount: z.number() }).optional(), // human, price>0
|
|
1967
2163
|
escrow: z.object({ escrowId: z.string(), bounty: z.number(), deadline: z.string() }).optional(), // agent
|
|
1968
|
-
error: z.string().optional(), // 'slot_taken' | 'closed' | 'unknown_service'
|
|
2164
|
+
error: z.string().optional(), // 'slot_taken' | 'closed' | 'unknown_service' | 'details_too_large'
|
|
1969
2165
|
}),
|
|
1970
2166
|
effect: "ask", cost: "variable", settles: "offchain", reversible: false,
|
|
1971
2167
|
simulatable: true, idempotent: true, // idempotencyKey dedupes a retried hold
|
|
@@ -1990,6 +2186,15 @@ export const RECEIVERS = {
|
|
|
1990
2186
|
hours_json: z.array(z.array(z.object({ start: z.string(), end: z.string() }))), // 7 entries, 0=Sun
|
|
1991
2187
|
tz: z.string().optional(),
|
|
1992
2188
|
active: z.boolean().optional(),
|
|
2189
|
+
description: z.string().optional(), // shown on /book + read by the agent (§12.1)
|
|
2190
|
+
location: z.string().optional(), // 'video' | 'phone' | free-text address
|
|
2191
|
+
buffer_min: z.number().int().nonnegative().optional(), // drive time between slots (§12.5)
|
|
2192
|
+
intake: z.array(z.object({
|
|
2193
|
+
key: z.string(), label: z.string(),
|
|
2194
|
+
type: z.enum(["text", "number", "choice"]).optional(),
|
|
2195
|
+
required: z.boolean().optional(),
|
|
2196
|
+
options: z.array(z.string()).optional(),
|
|
2197
|
+
})).optional(), // per-service intake questions (§12.2); full-body upsert — omitted clears
|
|
1993
2198
|
}),
|
|
1994
2199
|
response: z.object({ ok: z.boolean(), service_id: z.string().optional(), error: z.string().optional() }),
|
|
1995
2200
|
effect: "ask", cost: "free", idempotent: true, reversible: true, auth: "manage_clients",
|
|
@@ -2011,25 +2216,56 @@ export const RECEIVERS = {
|
|
|
2011
2216
|
id: z.string(), ref: z.string(), service_name: z.string(),
|
|
2012
2217
|
slot_date: z.string(), slot_time: z.string(),
|
|
2013
2218
|
customer_name: z.string(), status: z.string(),
|
|
2219
|
+
customer_phone: z.string().nullable().optional(), // §12.3 — the job, not just the name
|
|
2220
|
+
details: z.union([z.string(), z.record(z.string(), z.unknown())]).nullable().optional(), // parsed details_json (record) or verbatim free text; untrusted display data
|
|
2014
2221
|
})),
|
|
2015
2222
|
error: z.string().optional(),
|
|
2016
2223
|
}),
|
|
2017
2224
|
effect: "ask", cost: "free", idempotent: true, auth: "manage_clients",
|
|
2018
2225
|
examples: [{ slug: "clearwater", month: "2026-06" }],
|
|
2019
2226
|
}),
|
|
2020
|
-
// booking:cancel —
|
|
2021
|
-
//
|
|
2022
|
-
//
|
|
2227
|
+
// booking:cancel — dual-path (§12.6). Provider dissolves by ref (ctx-gated to
|
|
2228
|
+
// the attested owner). Customer self-cancels with ref + matching contact (public;
|
|
2229
|
+
// possession IS the gate, exactly like the public book path). Either way
|
|
2230
|
+
// status='dissolved' releases the slot via the partial unique index (§9).
|
|
2023
2231
|
"booking:cancel": receiver({
|
|
2024
2232
|
receiver: "booking:cancel",
|
|
2025
|
-
summary: "
|
|
2233
|
+
summary: "Dissolve a booking by ref — provider (ctx-gated) or customer (ref + matching contact)",
|
|
2026
2234
|
request: z.object({
|
|
2027
2235
|
slug: z.string(),
|
|
2028
2236
|
ref: z.string(),
|
|
2237
|
+
email: z.string().email().optional(), // customer path: possession = ref + matching contact
|
|
2238
|
+
phone: z.string().optional(),
|
|
2029
2239
|
}),
|
|
2030
2240
|
response: z.object({ ok: z.boolean(), error: z.string().optional() }),
|
|
2031
|
-
effect: "ask", cost: "free", idempotent: true, reversible: false, auth: "
|
|
2032
|
-
examples: [{ slug: "clearwater", ref: "CLE-2026-4821" }],
|
|
2241
|
+
effect: "ask", cost: "free", idempotent: true, reversible: false, auth: "public",
|
|
2242
|
+
examples: [{ slug: "clearwater", ref: "CLE-2026-4821", email: "s@ex.com" }],
|
|
2243
|
+
}),
|
|
2244
|
+
// booking:reschedule — customer self-serve move (§12.6). Public; the gate is
|
|
2245
|
+
// ref + matching contact. Hold-new-first: the resolver INSERTs the new slot
|
|
2246
|
+
// (same guarded INSERT — can lose with slot_taken) THEN dissolves the old row,
|
|
2247
|
+
// so a lost race leaves the original booking standing. Payment rail/ref carry over.
|
|
2248
|
+
"booking:reschedule": receiver({
|
|
2249
|
+
receiver: "booking:reschedule",
|
|
2250
|
+
summary: "Customer self-serve: move a booking to a new slot by ref + matching contact (hold-new-first)",
|
|
2251
|
+
request: z.object({
|
|
2252
|
+
slug: z.string(),
|
|
2253
|
+
ref: z.string(),
|
|
2254
|
+
email: z.string().email().optional(),
|
|
2255
|
+
phone: z.string().optional(),
|
|
2256
|
+
newDate: z.string(), // 'YYYY-MM-DD'
|
|
2257
|
+
newTime: z.string(), // 'HH:MM' 24h slot start
|
|
2258
|
+
}),
|
|
2259
|
+
response: z.object({
|
|
2260
|
+
ok: z.boolean(),
|
|
2261
|
+
ref: z.string().optional(), // the NEW booking ref (ref is UNIQUE — a moved booking gets a fresh ref)
|
|
2262
|
+
status: z.enum(["pending", "confirmed"]).optional(),
|
|
2263
|
+
slotDate: z.string().optional(),
|
|
2264
|
+
slotTime: z.string().optional(),
|
|
2265
|
+
error: z.string().optional(), // 'not_found' | 'slot_taken' | 'closed' | 'unknown_service'
|
|
2266
|
+
}),
|
|
2267
|
+
effect: "ask", cost: "free", idempotent: true, reversible: false, auth: "public",
|
|
2268
|
+
examples: [{ slug: "clearwater", ref: "CLE-2026-4821", email: "s@ex.com", newDate: "2026-06-12", newTime: "10:30" }],
|
|
2033
2269
|
}),
|
|
2034
2270
|
// ── workflows — define an SOP, run it, watch it learn (text/workflows-plan.md) ──
|
|
2035
2271
|
// Every workflow op is a typed receiver. No new REST routes. apply-diff is
|
|
@@ -2090,6 +2326,23 @@ export const RECEIVERS = {
|
|
|
2090
2326
|
effect: "ask", cost: "free", idempotent: true, simulatable: false, auth: "view_workflows",
|
|
2091
2327
|
examples: [{ workflowId: "wf_abc", limit: 20 }, { workflowId: "wf_abc", runId: "run_1" }],
|
|
2092
2328
|
}),
|
|
2329
|
+
"workflow:preview-step": receiver({
|
|
2330
|
+
receiver: "workflow:preview-step",
|
|
2331
|
+
summary: "Resolve one step's receiver + args through the real substitution code, with an optional mock trigger/step payload standing in for run history — never dispatches, reads no run history",
|
|
2332
|
+
request: z.object({
|
|
2333
|
+
workflowId: z.string(),
|
|
2334
|
+
stepId: z.string(),
|
|
2335
|
+
mockTrigger: z.record(z.string(), z.unknown()).optional(),
|
|
2336
|
+
mockSteps: z.record(z.string(), z.record(z.string(), z.unknown())).optional(),
|
|
2337
|
+
}),
|
|
2338
|
+
response: z.object({
|
|
2339
|
+
receiver: z.string().nullable(),
|
|
2340
|
+
data: z.record(z.string(), z.unknown()),
|
|
2341
|
+
error: z.string().optional(),
|
|
2342
|
+
}),
|
|
2343
|
+
effect: "ask", cost: "free", reversible: false, idempotent: true, simulatable: false, auth: "view_workflows",
|
|
2344
|
+
examples: [{ workflowId: "wf_abc", stepId: "step_1", mockTrigger: { email: "a@b.com" } }],
|
|
2345
|
+
}),
|
|
2093
2346
|
"workflow:create": receiver({
|
|
2094
2347
|
receiver: "workflow:create",
|
|
2095
2348
|
summary: "Create a blank workflow (group group-type=workflow) — optionally cloned from a template",
|
|
@@ -2294,13 +2547,21 @@ export const RECEIVERS = {
|
|
|
2294
2547
|
"skills:list": receiver({
|
|
2295
2548
|
receiver: "skills:list",
|
|
2296
2549
|
summary: "List the caller's workspace skill catalog (R2) — powers the canvas SkillPicker",
|
|
2297
|
-
request: z.object({}),
|
|
2550
|
+
request: z.object({ name: z.string().optional() }),
|
|
2298
2551
|
response: z.object({
|
|
2299
|
-
skills: z.array(z.object({ name: z.string(), size: z.number(), sealed: z.boolean().optional() })),
|
|
2552
|
+
skills: z.array(z.object({ name: z.string(), size: z.number(), sealed: z.boolean().optional(), description: z.string().optional(), inputs: z.array(z.object({ name: z.string(), label: z.string(), required: z.boolean().optional() })).optional() })),
|
|
2300
2553
|
}),
|
|
2301
2554
|
effect: "ask", cost: "free", idempotent: true, simulatable: false, auth: "view_workflows",
|
|
2302
2555
|
examples: [{}],
|
|
2303
2556
|
}),
|
|
2557
|
+
"skill:save": receiver({
|
|
2558
|
+
receiver: "skill:save",
|
|
2559
|
+
summary: "Write a skill body into a workspace's R2 catalog — the store skill:run/skills:list read; explicit-slug, ownership-walk authorized (mirrors world:publish-agent)",
|
|
2560
|
+
request: z.object({ slug: z.string(), name: z.string(), content: z.string() }),
|
|
2561
|
+
response: z.object({ ok: z.boolean(), name: z.string(), bytes: z.number() }),
|
|
2562
|
+
effect: "ask", auth: "update_group", idempotent: true,
|
|
2563
|
+
examples: [{ slug: "elitemoversca", name: "quote-estimate", content: "---\nname: quote-estimate\n---\n…" }],
|
|
2564
|
+
}),
|
|
2304
2565
|
"skill:run": receiver({
|
|
2305
2566
|
receiver: "skill:run",
|
|
2306
2567
|
summary: "Run a workspace skill — loads its body from R2, runs one bounded turn via channels, returns { text }",
|
|
@@ -2594,6 +2855,48 @@ export const RECEIVERS = {
|
|
|
2594
2855
|
response: z.object({ broadcast: z.record(z.string(), z.unknown()) }),
|
|
2595
2856
|
effect: "ask", cost: "free", reversible: false, idempotent: true, auth: "read",
|
|
2596
2857
|
}),
|
|
2858
|
+
"segment:create": receiver({
|
|
2859
|
+
receiver: "segment:create",
|
|
2860
|
+
summary: "Create an audience segment from a rule definition (SegmentDef: {all, any?})",
|
|
2861
|
+
request: z.object({ workspace: z.string(), name: z.string(), description: z.string().optional(), definition: z.record(z.string(), z.unknown()) }),
|
|
2862
|
+
response: z.object({ id: z.string() }),
|
|
2863
|
+
effect: "ask", cost: "free", reversible: true, idempotent: false, auth: "write",
|
|
2864
|
+
}),
|
|
2865
|
+
"segment:update": receiver({
|
|
2866
|
+
receiver: "segment:update",
|
|
2867
|
+
summary: "Update an audience segment — name, description, or definition",
|
|
2868
|
+
request: z.object({ workspace: z.string(), id: z.string(), name: z.string().optional(), description: z.string().optional(), definition: z.record(z.string(), z.unknown()).optional() }),
|
|
2869
|
+
response: z.object({ ok: z.boolean() }),
|
|
2870
|
+
effect: "ask", cost: "free", reversible: true, idempotent: true, auth: "write",
|
|
2871
|
+
}),
|
|
2872
|
+
"segment:list": receiver({
|
|
2873
|
+
receiver: "segment:list",
|
|
2874
|
+
summary: "List audience segments for a workspace",
|
|
2875
|
+
request: z.object({ workspace: z.string() }),
|
|
2876
|
+
response: z.object({ segments: z.array(z.record(z.string(), z.unknown())) }),
|
|
2877
|
+
effect: "ask", cost: "free", reversible: false, idempotent: true, auth: "read",
|
|
2878
|
+
}),
|
|
2879
|
+
"segment:get": receiver({
|
|
2880
|
+
receiver: "segment:get",
|
|
2881
|
+
summary: "Get one audience segment with its rule definition",
|
|
2882
|
+
request: z.object({ workspace: z.string(), id: z.string() }),
|
|
2883
|
+
response: z.object({ segment: z.record(z.string(), z.unknown()) }),
|
|
2884
|
+
effect: "ask", cost: "free", reversible: false, idempotent: true, auth: "read",
|
|
2885
|
+
}),
|
|
2886
|
+
"segment:preview": receiver({
|
|
2887
|
+
receiver: "segment:preview",
|
|
2888
|
+
summary: "Preview an audience segment — live count + up to 10 sample addresses (read-only)",
|
|
2889
|
+
request: z.object({ workspace: z.string(), definition: z.record(z.string(), z.unknown()), channel: z.enum(["email", "sms", "whatsapp"]).optional() }),
|
|
2890
|
+
response: z.object({ count: z.number(), sample: z.array(z.string()) }),
|
|
2891
|
+
effect: "ask", cost: "free", reversible: false, idempotent: true, auth: "read",
|
|
2892
|
+
}),
|
|
2893
|
+
"segment:delete": receiver({
|
|
2894
|
+
receiver: "segment:delete",
|
|
2895
|
+
summary: "Delete an audience segment and its membership snapshot",
|
|
2896
|
+
request: z.object({ workspace: z.string(), id: z.string() }),
|
|
2897
|
+
response: z.object({ ok: z.boolean() }),
|
|
2898
|
+
effect: "ask", cost: "free", reversible: true, idempotent: true, auth: "write",
|
|
2899
|
+
}),
|
|
2597
2900
|
"broadcast:test": receiver({
|
|
2598
2901
|
receiver: "broadcast:test",
|
|
2599
2902
|
summary: "Send a test email for a broadcast to one address",
|