@ikenga/contract 0.9.0 → 0.10.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.
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Approve-gate draft types — the run-then-pause (`ux_mode: approve`) payload.
3
+ *
4
+ * An approve-aware action does its work, then — instead of performing the
5
+ * external side effect — hands the shell a batch of `DraftItem`s plus an
6
+ * `ApproveGateMeta` via the (forthcoming) `host.paActionsPause` verb. The shell
7
+ * persists them to `pa_action_drafts` (ikenga.db), emits `pa-action-paused`, and
8
+ * mounts the approve-gate panel
9
+ * (`shell/src/shell/atelier/surfaces/approve-gate-panel.tsx`) at
10
+ * `/outbox/approvals`. The panel renders the rich `PausedDraft` view-model;
11
+ * `fromDraftItem` derives it from the lean producer `DraftItem` so producers
12
+ * stay simple (they never hand-build display state).
13
+ *
14
+ * Scope: `plans/atelier/10-approve-gate-seam.md` (WP-1); behaviour:
15
+ * `07-fe-button-renderer.md` §3.5. Plain-TS by convention (mirrors
16
+ * `host-verbs.ts`) — runtime validation, when needed, lives at the host boundary.
17
+ */
18
+ /** Outbound channel an approve gate commits to. Locked to provider identity. */
19
+ export type DraftChannel = 'smtp' | 'resend' | 'listmonk' | 'buffer';
20
+ /** Human channel label (display). */
21
+ export declare const CHANNEL_LABEL: Record<DraftChannel, string>;
22
+ /** A draft's position inside an outreach sequence/drip, if any. */
23
+ export interface DraftSequence {
24
+ name: string;
25
+ step: number;
26
+ total: number;
27
+ /** Audience size for this step (e.g. a 388-recipient newsletter section). */
28
+ recipients: number;
29
+ }
30
+ /**
31
+ * What an approve-aware action emits per draft — the lean, authoritative
32
+ * producer shape. The shell derives the panel's `PausedDraft` from this via
33
+ * `fromDraftItem` (computes preview, time bucketing, consequence), so an action
34
+ * never hand-builds display state.
35
+ */
36
+ export interface DraftItem {
37
+ id: string;
38
+ /** Recipient display (a person, or a broadcast target like "L5 Winback · 388 recipients"). */
39
+ recipient: string;
40
+ recipientEmail: string | null;
41
+ tenantId?: string | null;
42
+ subject: string;
43
+ body: string;
44
+ channel: DraftChannel;
45
+ /** Sending address, e.g. "chinedum@royalti.io" or "ned@getroyalti.com". */
46
+ senderAddress: string;
47
+ /** Sending route display, e.g. "SMTP · Fastmail". */
48
+ fromProvider: string;
49
+ /** Cold outreach (separate sender domain / reputation). */
50
+ cold?: boolean;
51
+ /**
52
+ * Audience size for the consequence line; defaults to 1.
53
+ * Kept for display (e.g. "388 recipients"); use `recipientsList` for the actual
54
+ * per-recipient send list on direct-email channels.
55
+ */
56
+ recipients?: number;
57
+ /**
58
+ * Actual recipient list for direct-email channels (smtp, resend).
59
+ * Adapters iterate this for per-recipient sends + partial-success tracking (DEC-9).
60
+ * Broadcast channels (listmonk, buffer) address a list/channel audience and
61
+ * ignore this field.
62
+ */
63
+ recipientsList?: {
64
+ name?: string;
65
+ email: string;
66
+ }[];
67
+ /**
68
+ * Body content type — adapters set the MIME part accordingly.
69
+ * Defaults to `'text'` when absent.
70
+ */
71
+ bodyFormat?: 'html' | 'text';
72
+ /** Reply-To header for direct-email channels (smtp, resend). */
73
+ replyTo?: string;
74
+ /** Machine schedule time (ISO) for overdue/today bucketing; null = unscheduled. */
75
+ scheduledIso: string | null;
76
+ /** Row time display, e.g. "scheduled 17:00" / "today" / "2d late". */
77
+ scheduledLabel: string;
78
+ /** Detail-chip schedule display, e.g. "Scheduled · today 17:00". Falls back to scheduledLabel. */
79
+ scheduledChip?: string;
80
+ sequence?: DraftSequence | null;
81
+ deal?: string | null;
82
+ threadCount?: string;
83
+ /** Explicit section bucket; otherwise derived from `scheduledIso`. */
84
+ section?: string;
85
+ }
86
+ /**
87
+ * Partial-success result returned by a `ChannelAdapter.send()` call (DEC-9).
88
+ * `ok` is true when at least one recipient was accepted; `failed` lists
89
+ * per-recipient errors so callers can surface them without dropping success.
90
+ * Shared here so the contract module is the single SoT; the daemon's
91
+ * `lib/channels/types.ts` imports it directly from `@ikenga/contract`.
92
+ */
93
+ export interface SendResult {
94
+ /** False only when ALL recipients failed (or a batch-level error occurred). */
95
+ ok: boolean;
96
+ /** Provider message/campaign/post id — write around the network call where supported (G-05). */
97
+ externalId?: string;
98
+ /** Addresses the provider accepted (partial success — DEC-9). */
99
+ sent?: string[];
100
+ /** Per-recipient failures for partial-success scenarios. */
101
+ failed?: {
102
+ email: string;
103
+ error: string;
104
+ }[];
105
+ /** Batch-level error message (to error_text column) when ok is false. */
106
+ error?: string;
107
+ /** True for transient errors (5xx/429/network) — drives in-worker retry vs permanent fail. */
108
+ retryable?: boolean;
109
+ }
110
+ /** Batch-level metadata for one approve-mode run (the gate header + undo window). */
111
+ export interface ApproveGateMeta {
112
+ /** `<pkgId>/<verb>` that produced the batch. */
113
+ actionId: string;
114
+ /** Human action name for the gate header. */
115
+ actionName: string;
116
+ /** Drafting agent label, e.g. "PA" / "CMO" / "CBO". */
117
+ agent: string;
118
+ /** Engine/model that drafted, e.g. "Opus 4.7". */
119
+ model: string;
120
+ /** Undo window before commit, ms. Defaults to 10_000. */
121
+ undoMs?: number;
122
+ }
123
+ /**
124
+ * The rich view-model the approve-gate panel renders. Kept byte-identical to the
125
+ * panel's prior local definition so the panel just imports it. Derived from a
126
+ * `DraftItem` + `ApproveGateMeta` via `fromDraftItem`; the panel flips
127
+ * `everEdited`/`status` locally as the operator edits.
128
+ */
129
+ export interface PausedDraft {
130
+ id: string;
131
+ recipient: string;
132
+ recipientEmail: string | null;
133
+ tenantId: string | null;
134
+ subject: string;
135
+ body: string;
136
+ bodyPreview: string;
137
+ channel: DraftChannel;
138
+ agent: string;
139
+ senderAddress: string;
140
+ cold: boolean;
141
+ /**
142
+ * Panel display status. `'failed'` means the send worker exhausted retries
143
+ * and the draft needs operator attention (see `errorMessage` / `attempts`).
144
+ */
145
+ status: 'awaiting' | 'edited' | 'overdue' | 'failed';
146
+ scheduledAt: string;
147
+ scheduledLabel: string;
148
+ timeVariant: 'is-today' | 'is-overdue' | null;
149
+ overdue: boolean;
150
+ everEdited: boolean;
151
+ section: string;
152
+ sequence: DraftSequence | null;
153
+ fromProvider: string;
154
+ model: string;
155
+ threadCount: string;
156
+ deal: string | null;
157
+ consequence: {
158
+ target: string;
159
+ recipients: number;
160
+ channel: string;
161
+ time: string;
162
+ undoMs: number;
163
+ };
164
+ /** Last error surfaced by the send worker (from pa_action_drafts.error_text). */
165
+ errorMessage?: string;
166
+ /** Number of send attempts so far (from pa_action_drafts.attempts). */
167
+ attempts?: number;
168
+ }
169
+ /** Collapse whitespace + truncate `body` for the row preview line. */
170
+ export declare function draftPreview(body: string, max?: number): string;
171
+ /**
172
+ * Derive the panel's `PausedDraft` from a producer `DraftItem` + the batch
173
+ * `ApproveGateMeta`. `now` is injectable for deterministic tests; it only affects
174
+ * time bucketing (overdue / today / section). Pure + side-effect-free.
175
+ *
176
+ * `errorMessage` and `attempts` are not derivable from a `DraftItem` (they live
177
+ * on the DB row after worker runs); callers that reconstruct a `PausedDraft` from
178
+ * a stored row should set them on the returned object directly.
179
+ */
180
+ export declare function fromDraftItem(item: DraftItem, meta: ApproveGateMeta, now?: number): PausedDraft;
181
+ //# sourceMappingURL=pa-actions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pa-actions.d.ts","sourceRoot":"","sources":["../src/pa-actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,gFAAgF;AAChF,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,QAAQ,GAAG,UAAU,GAAG,QAAQ,CAAC;AAErE,qCAAqC;AACrC,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAKtD,CAAC;AAEF,mEAAmE;AACnE,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,6EAA6E;IAC7E,UAAU,EAAE,MAAM,CAAC;CACnB;AAED;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,8FAA8F;IAC9F,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,CAAC;IACtB,2EAA2E;IAC3E,aAAa,EAAE,MAAM,CAAC;IACtB,qDAAqD;IACrD,YAAY,EAAE,MAAM,CAAC;IACrB,2DAA2D;IAC3D,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,cAAc,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACpD;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mFAAmF;IACnF,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,sEAAsE;IACtE,cAAc,EAAE,MAAM,CAAC;IACvB,kGAAkG;IAClG,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,UAAU;IAC1B,+EAA+E;IAC/E,EAAE,EAAE,OAAO,CAAC;IACZ,gGAAgG;IAChG,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,4DAA4D;IAC5D,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC5C,yEAAyE;IACzE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8FAA8F;IAC9F,SAAS,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,qFAAqF;AACrF,MAAM,WAAW,eAAe;IAC/B,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,yDAAyD;IACzD,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,YAAY,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,OAAO,CAAC;IACd;;;OAGG;IACH,MAAM,EAAE,UAAU,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;IACrD,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,UAAU,GAAG,YAAY,GAAG,IAAI,CAAC;IAC9C,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,WAAW,EAAE;QACZ,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;KACf,CAAC;IACF,iFAAiF;IACjF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAKD,sEAAsE;AACtE,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,SAAc,GAAG,MAAM,CAGpE;AAYD;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC5B,IAAI,EAAE,SAAS,EACf,IAAI,EAAE,eAAe,EACrB,GAAG,GAAE,MAAmB,GACtB,WAAW,CA4Cb"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Approve-gate draft types — the run-then-pause (`ux_mode: approve`) payload.
3
+ *
4
+ * An approve-aware action does its work, then — instead of performing the
5
+ * external side effect — hands the shell a batch of `DraftItem`s plus an
6
+ * `ApproveGateMeta` via the (forthcoming) `host.paActionsPause` verb. The shell
7
+ * persists them to `pa_action_drafts` (ikenga.db), emits `pa-action-paused`, and
8
+ * mounts the approve-gate panel
9
+ * (`shell/src/shell/atelier/surfaces/approve-gate-panel.tsx`) at
10
+ * `/outbox/approvals`. The panel renders the rich `PausedDraft` view-model;
11
+ * `fromDraftItem` derives it from the lean producer `DraftItem` so producers
12
+ * stay simple (they never hand-build display state).
13
+ *
14
+ * Scope: `plans/atelier/10-approve-gate-seam.md` (WP-1); behaviour:
15
+ * `07-fe-button-renderer.md` §3.5. Plain-TS by convention (mirrors
16
+ * `host-verbs.ts`) — runtime validation, when needed, lives at the host boundary.
17
+ */
18
+ /** Human channel label (display). */
19
+ export const CHANNEL_LABEL = {
20
+ smtp: 'SMTP',
21
+ resend: 'Resend',
22
+ listmonk: 'Listmonk',
23
+ buffer: 'Buffer',
24
+ };
25
+ const PREVIEW_MAX = 160;
26
+ const DEFAULT_UNDO_MS = 10_000;
27
+ /** Collapse whitespace + truncate `body` for the row preview line. */
28
+ export function draftPreview(body, max = PREVIEW_MAX) {
29
+ const flat = body.replace(/\s+/g, ' ').trim();
30
+ return flat.length > max ? `${flat.slice(0, max - 1).trimEnd()}…` : flat;
31
+ }
32
+ function sameLocalDay(a, b) {
33
+ const da = new Date(a);
34
+ const db = new Date(b);
35
+ return (da.getFullYear() === db.getFullYear() &&
36
+ da.getMonth() === db.getMonth() &&
37
+ da.getDate() === db.getDate());
38
+ }
39
+ /**
40
+ * Derive the panel's `PausedDraft` from a producer `DraftItem` + the batch
41
+ * `ApproveGateMeta`. `now` is injectable for deterministic tests; it only affects
42
+ * time bucketing (overdue / today / section). Pure + side-effect-free.
43
+ *
44
+ * `errorMessage` and `attempts` are not derivable from a `DraftItem` (they live
45
+ * on the DB row after worker runs); callers that reconstruct a `PausedDraft` from
46
+ * a stored row should set them on the returned object directly.
47
+ */
48
+ export function fromDraftItem(item, meta, now = Date.now()) {
49
+ const scheduledMs = item.scheduledIso ? Date.parse(item.scheduledIso) : null;
50
+ const hasTime = scheduledMs !== null && !Number.isNaN(scheduledMs);
51
+ const overdue = hasTime && scheduledMs < now;
52
+ const today = hasTime && !overdue && sameLocalDay(scheduledMs, now);
53
+ const timeVariant = overdue
54
+ ? 'is-overdue'
55
+ : today
56
+ ? 'is-today'
57
+ : null;
58
+ const section = item.section ?? (overdue ? 'Overdue' : today ? 'Today' : 'This week');
59
+ return {
60
+ id: item.id,
61
+ recipient: item.recipient,
62
+ recipientEmail: item.recipientEmail,
63
+ tenantId: item.tenantId ?? null,
64
+ subject: item.subject,
65
+ body: item.body,
66
+ bodyPreview: draftPreview(item.body),
67
+ channel: item.channel,
68
+ agent: meta.agent,
69
+ senderAddress: item.senderAddress,
70
+ cold: item.cold ?? false,
71
+ status: overdue ? 'overdue' : 'awaiting',
72
+ scheduledAt: item.scheduledLabel,
73
+ scheduledLabel: item.scheduledChip ?? item.scheduledLabel,
74
+ timeVariant,
75
+ overdue,
76
+ everEdited: false,
77
+ section,
78
+ sequence: item.sequence ?? null,
79
+ fromProvider: item.fromProvider,
80
+ model: meta.model,
81
+ threadCount: item.threadCount ?? '',
82
+ deal: item.deal ?? null,
83
+ consequence: {
84
+ target: item.recipient,
85
+ recipients: item.recipients ?? 1,
86
+ channel: CHANNEL_LABEL[item.channel],
87
+ time: item.scheduledLabel,
88
+ undoMs: meta.undoMs ?? DEFAULT_UNDO_MS,
89
+ },
90
+ };
91
+ }
92
+ //# sourceMappingURL=pa-actions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pa-actions.js","sourceRoot":"","sources":["../src/pa-actions.ts"],"names":[],"mappings":"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"}