@oneie/sdk 0.14.8 → 0.14.10

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/dist/receivers.js CHANGED
@@ -776,6 +776,36 @@ export const RECEIVERS = {
776
776
  name: z.string(),
777
777
  tags: z.array(z.string()),
778
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(),
779
809
  })),
780
810
  }),
781
811
  effect: "ask", idempotent: true,
@@ -787,6 +817,9 @@ export const RECEIVERS = {
787
817
  title: z.string(),
788
818
  tags: z.array(z.string()).optional(),
789
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(),
790
823
  // The viewed /u/<slug> workspace to file the task under. Honored only when the
791
824
  // caller is authorized for it (attested staff or owner-tree control); otherwise
792
825
  // the resolver falls back to the caller's own slug. Reconciles the create tag
@@ -796,6 +829,78 @@ export const RECEIVERS = {
796
829
  response: z.object({ ok: z.boolean(), tid: z.string().optional(), tags: z.array(z.string()).optional() }),
797
830
  effect: "ask", idempotent: false,
798
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(),
879
+ })),
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
+ }),
902
+ effect: "ask", idempotent: true,
903
+ }),
799
904
  // ── agents (TRADE / lifecycle) ──
800
905
  "agents:register": receiver({
801
906
  receiver: "agents:register",
@@ -1266,12 +1371,13 @@ export const RECEIVERS = {
1266
1371
  }),
1267
1372
  "notify": receiver({
1268
1373
  receiver: "notify",
1269
- summary: "Notify any actor (human or agent) by uid — channels routes the door (Telegram/Discord/peer inbox), mirrors into the web inbox, and nudges an open client live. One verb, both actor kinds.",
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.",
1270
1375
  request: z.object({
1271
1376
  receiver: z.string(),
1272
1377
  kind: z.string().optional(),
1273
1378
  body: z.union([z.string(), z.record(z.string(), z.unknown())]).optional(),
1274
1379
  content: z.string().optional(),
1380
+ action: z.object({ label: z.string(), redirect: z.string() }).optional(),
1275
1381
  }),
1276
1382
  response: z.object({
1277
1383
  ok: z.boolean(),
@@ -1306,6 +1412,17 @@ export const RECEIVERS = {
1306
1412
  response: z.object({ ok: z.boolean(), ts: z.number().optional(), error: z.string().optional() }),
1307
1413
  effect: "ask", idempotent: true,
1308
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
+ }),
1309
1426
  "thread:append": receiver({
1310
1427
  receiver: "thread:append",
1311
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.",
@@ -1977,6 +2094,14 @@ export const RECEIVERS = {
1977
2094
  service: z.object({
1978
2095
  service_id: z.string(), name: z.string(), duration_min: z.number(),
1979
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
1980
2105
  }).nullable(),
1981
2106
  slots: z.array(z.object({ date: z.string(), times: z.array(z.string()) })), // CalendarCard shape
1982
2107
  error: z.string().optional(),
@@ -1984,6 +2109,35 @@ export const RECEIVERS = {
1984
2109
  effect: "ask", cost: "free", idempotent: true, simulatable: false,
1985
2110
  examples: [{ slug: "clearwater", date: "2026-06-10" }],
1986
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
+ }),
1987
2141
  "booking:create": receiver({
1988
2142
  receiver: "booking:create",
1989
2143
  summary: "Hold a slot for a customer; returns a ref and a payment intent when price > 0",
@@ -1992,7 +2146,12 @@ export const RECEIVERS = {
1992
2146
  service: z.string(),
1993
2147
  slotDate: z.string(), // 'YYYY-MM-DD'
1994
2148
  slotTime: z.string(), // 'HH:MM' 24h, slot start
1995
- customer: z.object({ name: z.string(), email: z.string().email().optional() }),
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
1996
2155
  paymentRail: z.enum(["stripe", "sui"]).optional(), // resolver overrides by caller kind
1997
2156
  }),
1998
2157
  response: z.object({
@@ -2002,7 +2161,7 @@ export const RECEIVERS = {
2002
2161
  paymentRail: z.enum(["stripe", "sui"]).nullable().optional(),
2003
2162
  paymentIntent: z.object({ clientSecret: z.string(), amount: z.number() }).optional(), // human, price>0
2004
2163
  escrow: z.object({ escrowId: z.string(), bounty: z.number(), deadline: z.string() }).optional(), // agent
2005
- error: z.string().optional(), // 'slot_taken' | 'closed' | 'unknown_service'
2164
+ error: z.string().optional(), // 'slot_taken' | 'closed' | 'unknown_service' | 'details_too_large'
2006
2165
  }),
2007
2166
  effect: "ask", cost: "variable", settles: "offchain", reversible: false,
2008
2167
  simulatable: true, idempotent: true, // idempotencyKey dedupes a retried hold
@@ -2027,6 +2186,15 @@ export const RECEIVERS = {
2027
2186
  hours_json: z.array(z.array(z.object({ start: z.string(), end: z.string() }))), // 7 entries, 0=Sun
2028
2187
  tz: z.string().optional(),
2029
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
2030
2198
  }),
2031
2199
  response: z.object({ ok: z.boolean(), service_id: z.string().optional(), error: z.string().optional() }),
2032
2200
  effect: "ask", cost: "free", idempotent: true, reversible: true, auth: "manage_clients",
@@ -2048,25 +2216,56 @@ export const RECEIVERS = {
2048
2216
  id: z.string(), ref: z.string(), service_name: z.string(),
2049
2217
  slot_date: z.string(), slot_time: z.string(),
2050
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
2051
2221
  })),
2052
2222
  error: z.string().optional(),
2053
2223
  }),
2054
2224
  effect: "ask", cost: "free", idempotent: true, auth: "manage_clients",
2055
2225
  examples: [{ slug: "clearwater", month: "2026-06" }],
2056
2226
  }),
2057
- // booking:cancel — provider dissolves a booking by ref, releasing the slot
2058
- // immediately (the partial unique index drops dissolved rows from scope, §9).
2059
- // Tenant-private write: scoped to the authenticated owner. Idempotent.
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).
2060
2231
  "booking:cancel": receiver({
2061
2232
  receiver: "booking:cancel",
2062
- summary: "Provider-gated: dissolve a booking by ref, releasing the slot",
2233
+ summary: "Dissolve a booking by ref provider (ctx-gated) or customer (ref + matching contact)",
2063
2234
  request: z.object({
2064
2235
  slug: z.string(),
2065
2236
  ref: z.string(),
2237
+ email: z.string().email().optional(), // customer path: possession = ref + matching contact
2238
+ phone: z.string().optional(),
2066
2239
  }),
2067
2240
  response: z.object({ ok: z.boolean(), error: z.string().optional() }),
2068
- effect: "ask", cost: "free", idempotent: true, reversible: false, auth: "manage_clients",
2069
- 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" }],
2070
2269
  }),
2071
2270
  // ── workflows — define an SOP, run it, watch it learn (text/workflows-plan.md) ──
2072
2271
  // Every workflow op is a typed receiver. No new REST routes. apply-diff is
@@ -2127,6 +2326,23 @@ export const RECEIVERS = {
2127
2326
  effect: "ask", cost: "free", idempotent: true, simulatable: false, auth: "view_workflows",
2128
2327
  examples: [{ workflowId: "wf_abc", limit: 20 }, { workflowId: "wf_abc", runId: "run_1" }],
2129
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
+ }),
2130
2346
  "workflow:create": receiver({
2131
2347
  receiver: "workflow:create",
2132
2348
  summary: "Create a blank workflow (group group-type=workflow) — optionally cloned from a template",
@@ -2639,6 +2855,48 @@ export const RECEIVERS = {
2639
2855
  response: z.object({ broadcast: z.record(z.string(), z.unknown()) }),
2640
2856
  effect: "ask", cost: "free", reversible: false, idempotent: true, auth: "read",
2641
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
+ }),
2642
2900
  "broadcast:test": receiver({
2643
2901
  receiver: "broadcast:test",
2644
2902
  summary: "Send a test email for a broadcast to one address",