@kuralle-agents/engagement 0.7.2 → 0.8.5

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 CHANGED
@@ -96,6 +96,8 @@ import { whatsappPolicy, webPolicy, instagramPolicy } from '@kuralle-agents/enga
96
96
  - **`sessionConsentStore` / `consentGate`** — opt-in/opt-out and STOP handling (customer-keyed).
97
97
  - **`sessionOwnershipStore` / `ownershipGate`** — bot vs human thread ownership; human-owned inbound does not run flows.
98
98
  - **`createBroadcasts` / `createDrip`** — campaign ledger idempotency and drip schedules (see exports in `src/index.ts`).
99
+ - **`createOwnershipEscalationHandler` / `resolveEscalation`** — the channel side of `HarnessConfig.escalation`: claim the thread for the human + notify on escalation; release + resume the bot (with the resolution in context) when the human is done.
100
+ - The `Scheduler` contract is owned by `@kuralle-agents/core` (re-exported here) — drips, broadcasts, and runtime wake turns (`RunOptions.wake`) share backends: in-process timers in dev, Cloudflare DO alarms via `@kuralle-agents/cf-agent`.
99
101
 
100
102
  ## Example
101
103
 
@@ -0,0 +1,38 @@
1
+ import type { EscalationHandler, EscalationOutcome, EscalationRequest } from '@kuralle-agents/core';
2
+ import type { OwnershipStore } from '@kuralle-agents/messaging';
3
+ /**
4
+ * Channel-side escalation bridge: when the runtime escalates, claim thread
5
+ * ownership for the human (so `ownershipGate` suppresses bot sends) and
6
+ * notify the human channel; when the human is done, `resolveEscalation`
7
+ * releases ownership and hands the conversation back to the bot with the
8
+ * resolution in context.
9
+ */
10
+ export interface EscalationBridgeOptions {
11
+ ownership: OwnershipStore;
12
+ /** Map the escalation to the channel threadId to claim. Default: `request.sessionId`. */
13
+ threadIdFor?: (request: EscalationRequest) => string;
14
+ /**
15
+ * Notify the human side (inbox, Slack, pager). The returned outcome is
16
+ * surfaced to the runtime; returning nothing defaults to
17
+ * `{ status: 'queued', queueId: sessionId }`.
18
+ */
19
+ notify?: (request: EscalationRequest) => Promise<EscalationOutcome | void>;
20
+ }
21
+ export declare function createOwnershipEscalationHandler(options: EscalationBridgeOptions): EscalationHandler;
22
+ /** The runtime surface `resolveEscalation` needs (satisfied by `Runtime`). */
23
+ export interface EscalationResumable {
24
+ resumeFromEscalation(sessionId: string, opts?: {
25
+ resolutionSummary?: string;
26
+ }): Promise<void>;
27
+ }
28
+ export interface ResolveEscalationOptions {
29
+ runtime: EscalationResumable;
30
+ ownership: OwnershipStore;
31
+ sessionId: string;
32
+ /** Channel threadId that was claimed. Default: `sessionId`. */
33
+ threadId?: string;
34
+ /** What the human did — appended to the conversation so the bot resumes with context. */
35
+ resolutionSummary?: string;
36
+ }
37
+ /** Human is done: release the thread back to the bot and resume the run with context. */
38
+ export declare function resolveEscalation(options: ResolveEscalationOptions): Promise<void>;
@@ -0,0 +1,20 @@
1
+ export function createOwnershipEscalationHandler(options) {
2
+ return async (request) => {
3
+ const threadId = options.threadIdFor?.(request) ?? request.sessionId;
4
+ await options.ownership.claim(threadId, 'human');
5
+ if (options.notify) {
6
+ const outcome = await options.notify(request);
7
+ if (outcome) {
8
+ return outcome;
9
+ }
10
+ }
11
+ return { status: 'queued', queueId: request.sessionId };
12
+ };
13
+ }
14
+ /** Human is done: release the thread back to the bot and resume the run with context. */
15
+ export async function resolveEscalation(options) {
16
+ await options.ownership.release(options.threadId ?? options.sessionId);
17
+ await options.runtime.resumeFromEscalation(options.sessionId, {
18
+ resolutionSummary: options.resolutionSummary,
19
+ });
20
+ }
package/dist/index.d.ts CHANGED
@@ -16,6 +16,7 @@ export { aiTemplateSelector } from './selector.js';
16
16
  export type { OutboundTemplateComponent } from '@kuralle-agents/messaging';
17
17
  export { webPolicy } from './policies/web.js';
18
18
  export { sessionOwnershipStore, ownershipGate, OWNERSHIP_WM_KEY, } from './ownership.js';
19
+ export { createOwnershipEscalationHandler, resolveEscalation, type EscalationBridgeOptions, type EscalationResumable, type ResolveEscalationOptions, } from './escalation.js';
19
20
  export { sessionConsentStore, consentGate, CONSENT_WM_KEY } from './consent.js';
20
21
  export { createInProcessScheduler, type Scheduler, type SendJob, } from './scheduler.js';
21
22
  export { createInMemoryBroadcastLedger, createRedisBroadcastLedger, type BroadcastLedger, } from './broadcast-ledger.js';
package/dist/index.js CHANGED
@@ -15,6 +15,7 @@ export { whatsappTemplateCatalog, mapTemplateInfoToDescriptor, isApprovedNonPaus
15
15
  export { aiTemplateSelector } from './selector.js';
16
16
  export { webPolicy } from './policies/web.js';
17
17
  export { sessionOwnershipStore, ownershipGate, OWNERSHIP_WM_KEY, } from './ownership.js';
18
+ export { createOwnershipEscalationHandler, resolveEscalation, } from './escalation.js';
18
19
  export { sessionConsentStore, consentGate, CONSENT_WM_KEY } from './consent.js';
19
20
  export { createInProcessScheduler, } from './scheduler.js';
20
21
  export { createInMemoryBroadcastLedger, createRedisBroadcastLedger, } from './broadcast-ledger.js';
package/dist/policy.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { InboundMessage, InteractiveMessage } from '@kuralle-agents/messaging';
2
- import type { ChoiceOption, ResolvedSelection } from '@kuralle-agents/core';
2
+ import type { ChoiceOption, ResolvedSelection, UserInputContent } from '@kuralle-agents/core';
3
3
  import type { SmartSendStrategist } from './strategist.js';
4
4
  export type { SmartSendStrategist } from './strategist.js';
5
5
  export type { ChoiceOption } from '@kuralle-agents/core';
@@ -21,7 +21,7 @@ export interface ChannelPolicy {
21
21
  readonly consentRequired: boolean;
22
22
  renderInteractive(options: ChoiceOption[], prompt: string): InteractiveMessage;
23
23
  resolveInbound(m: InboundMessage): {
24
- input: string;
24
+ input: UserInputContent;
25
25
  selection?: ResolvedSelection;
26
26
  };
27
27
  }
@@ -1,27 +1,12 @@
1
- /** A unit of deferred work (broadcast step / drip step). Shape is engagement-internal. */
2
- export interface SendJob {
3
- kind: string;
4
- payload: Record<string, unknown>;
5
- }
6
- export interface Scheduler {
7
- enqueue(job: SendJob, opts?: {
8
- delayMs?: number;
9
- }): Promise<string>;
10
- cancel(jobId: string): Promise<void>;
11
- }
12
- export type InjectableTimer = {
13
- set(fn: () => void, ms: number): unknown;
14
- clear(handle: unknown): void;
15
- };
16
1
  /**
17
- * Default in-process scheduler (timer-based). For single-process/dev.
18
- * Production adapters (interface-compatible, not implemented here):
19
- * - BullMQ (Redis-backed queue)
20
- * - Google Cloud Tasks
21
- * - cron / system scheduler
22
- * Inject a durable adapter for multi-process / serverless.
2
+ * The scheduler contract is owned by `@kuralle-agents/core` — one interface
3
+ * for engagement send-jobs and runtime wake turns (broadcasts, drips, and
4
+ * proactive agent-initiated messages share backends: in-process timers in
5
+ * dev, DO alarms on Cloudflare, any queue in between). Re-exported here so
6
+ * existing engagement consumers keep their import path.
23
7
  */
24
- export declare function createInProcessScheduler(opts: {
25
- run: (job: SendJob) => void | Promise<void>;
26
- timer?: InjectableTimer;
27
- }): Scheduler;
8
+ import type { ScheduledJob } from '@kuralle-agents/core';
9
+ export { createInProcessScheduler } from '@kuralle-agents/core';
10
+ export type { Scheduler, InjectableTimer } from '@kuralle-agents/core';
11
+ /** A unit of deferred engagement work (broadcast step / drip step). */
12
+ export type SendJob = ScheduledJob;
package/dist/scheduler.js CHANGED
@@ -1,36 +1 @@
1
- const defaultTimer = {
2
- set: (fn, ms) => setTimeout(fn, ms),
3
- clear: (handle) => clearTimeout(handle),
4
- };
5
- /**
6
- * Default in-process scheduler (timer-based). For single-process/dev.
7
- * Production adapters (interface-compatible, not implemented here):
8
- * - BullMQ (Redis-backed queue)
9
- * - Google Cloud Tasks
10
- * - cron / system scheduler
11
- * Inject a durable adapter for multi-process / serverless.
12
- */
13
- export function createInProcessScheduler(opts) {
14
- const timer = opts.timer ?? defaultTimer;
15
- let nextJobId = 0;
16
- const handles = new Map();
17
- return {
18
- async enqueue(job, options) {
19
- const jobId = String(++nextJobId);
20
- const delayMs = options?.delayMs ?? 0;
21
- const handle = timer.set(() => {
22
- handles.delete(jobId);
23
- void opts.run(job);
24
- }, delayMs);
25
- handles.set(jobId, handle);
26
- return jobId;
27
- },
28
- async cancel(jobId) {
29
- const handle = handles.get(jobId);
30
- if (handle === undefined)
31
- return;
32
- timer.clear(handle);
33
- handles.delete(jobId);
34
- },
35
- };
36
- }
1
+ export { createInProcessScheduler } from '@kuralle-agents/core';
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "url": "git+https://github.com/kuralle/kuralle-agents.git",
7
7
  "directory": "packages/kuralle-engagement"
8
8
  },
9
- "version": "0.7.2",
9
+ "version": "0.8.5",
10
10
  "description": "Channel-agnostic engagement layer for Kuralle agents (window-safe outbound, smart-send, interactive fidelity, handoff, consent, proactive).",
11
11
  "type": "module",
12
12
  "main": "dist/index.js",
@@ -27,9 +27,9 @@
27
27
  "dependencies": {
28
28
  "ai": "^6.0.0",
29
29
  "zod": "^4.0.0",
30
- "@kuralle-agents/core": "0.7.2",
31
- "@kuralle-agents/messaging-meta": "0.7.2",
32
- "@kuralle-agents/messaging": "0.7.2"
30
+ "@kuralle-agents/core": "0.8.5",
31
+ "@kuralle-agents/messaging-meta": "0.8.5",
32
+ "@kuralle-agents/messaging": "0.8.5"
33
33
  },
34
34
  "devDependencies": {
35
35
  "typescript": "^5.8.2",