@keemakr/agent-sdk 0.19.0-dev-20261014201650 → 0.19.0-dev-20261014211203

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.
@@ -85,6 +85,7 @@ export interface FakeCore {
85
85
  };
86
86
  channel?: ChannelDelivery['channel'];
87
87
  setup?: ChannelDelivery['setup'];
88
+ continuity?: ChannelDelivery['continuity'];
88
89
  contract?: number;
89
90
  timeoutMs?: number;
90
91
  }): Promise<Delivered>;
@@ -192,11 +192,19 @@ export async function startFakeCore(options = {}) {
192
192
  req.on('end', () => {
193
193
  const action = JSON.parse(raw || '{}');
194
194
  // Checked as core checks it: a reach ref counts only if THIS turn was served it.
195
- const cited = Array.isArray(action.grounded_on) ? action.grounded_on : [];
195
+ const effects = action.effects;
196
+ const cited = action.action === 'turn'
197
+ ? (effects?.replies?.flatMap((r) => r.groundedOn ?? []) ?? [])
198
+ : Array.isArray(action.grounded_on)
199
+ ? action.grounded_on
200
+ : [];
196
201
  unverified.set(String(action.delivery_id), cited.filter((g) => typeof g === 'string' &&
197
202
  (!(served.get(trace)?.has(/^(record|op|hold|kb|workflow):/.test(g) ? g : `kb:${g}`) ??
198
203
  false) ||
199
- (g.startsWith('workflow:') && workflowText.get(`${trace}:${g}`) !== action.text))));
204
+ (g.startsWith('workflow:') &&
205
+ (action.action === 'turn'
206
+ ? (effects?.replies?.some((r) => r.groundedOn?.includes(g) && workflowText.get(`${trace}:${g}`) !== r.text) ?? false)
207
+ : workflowText.get(`${trace}:${g}`) !== action.text)))));
200
208
  const resolve = waiting.get(String(action.delivery_id));
201
209
  if (resolve) {
202
210
  waiting.delete(String(action.delivery_id));
@@ -276,6 +284,7 @@ export async function startFakeCore(options = {}) {
276
284
  ...(input.language ? { language: input.language } : {}),
277
285
  ...(input.channel ? { channel: input.channel } : {}),
278
286
  ...(input.setup ? { setup: input.setup } : {}),
287
+ ...(input.continuity ? { continuity: input.continuity } : {}),
279
288
  }),
280
289
  });
281
290
  const action = (await answered);
package/dist/channel.d.ts CHANGED
@@ -14,8 +14,53 @@ export interface ChannelPresentation {
14
14
  format: 'chat' | 'email' | 'sms';
15
15
  maxTextChars?: number;
16
16
  }
17
+ export interface ChannelQuestion {
18
+ id: string;
19
+ revision: number;
20
+ text: string;
21
+ sourcePosition: number;
22
+ state: 'open' | 'partitioned' | 'answered' | 'cancelled';
23
+ attempts: number;
24
+ }
25
+ export interface QuestionRef {
26
+ id: string;
27
+ revision: number;
28
+ }
29
+ export interface ChannelTurnEffects {
30
+ protocol: 'scoped-v1';
31
+ withdrawals?: Array<{
32
+ question: QuestionRef;
33
+ request: QuestionRef;
34
+ }>;
35
+ partitions?: Array<{
36
+ question: QuestionRef;
37
+ parts: Array<{
38
+ key: string;
39
+ text: string;
40
+ }>;
41
+ }>;
42
+ replies: Array<{
43
+ key: string;
44
+ text: string;
45
+ kind: 'answer' | 'conversation' | 'workflow';
46
+ confidence?: number;
47
+ coverage: 'answered' | 'clarification' | 'limitation';
48
+ groundedOn: string[];
49
+ questions: QuestionRef[];
50
+ }>;
51
+ assistance: Array<{
52
+ key: string;
53
+ questions: QuestionRef[];
54
+ reason: 'customer_requested' | 'clinical' | 'repeated_gap' | 'urgent' | 'operation_uncertain';
55
+ stopAgent?: boolean;
56
+ }>;
57
+ }
17
58
  /** The envelope core POSTs. Loose by rule — core adds fields without asking packs first. */
18
59
  export interface ChannelDelivery {
60
+ continuity?: {
61
+ protocol: 'scoped-v1';
62
+ questions: ChannelQuestion[];
63
+ };
19
64
  event: string;
20
65
  delivery_id: string;
21
66
  conversation: {
@@ -46,6 +91,10 @@ export interface ChannelDelivery {
46
91
  }
47
92
  /** What a pack proposes. Core decides what happens to it. */
48
93
  export type ChannelAction = {
94
+ action: 'turn';
95
+ effects: ChannelTurnEffects;
96
+ usage: Usage;
97
+ } | {
49
98
  action: 'reply';
50
99
  text: string;
51
100
  confidence?: number;
@@ -208,6 +257,10 @@ export declare class ChannelReplyNotDeliverable extends Error {
208
257
  constructor();
209
258
  }
210
259
  export interface ChannelContext {
260
+ continuity?: ChannelDelivery['continuity'];
261
+ turn(effects: ChannelTurnEffects, opts?: {
262
+ usage?: Usage;
263
+ }): ChannelAction;
211
264
  tenantId: string;
212
265
  conversationId: string;
213
266
  deliveryId: string;
package/dist/channel.js CHANGED
@@ -224,6 +224,8 @@ export function createChannelAgent(options) {
224
224
  // A CONTRACT THIS SDK DOES NOT SPEAK IS A REFUSAL, NOT A GUESS. Absent means a core
225
225
  // that predates the field. Anything else means core has changed the envelope's SHAPE,
226
226
  // and answering it with v1 assumptions is how a pack confidently misreads a customer.
227
+ if (body.continuity && body.continuity.protocol !== 'scoped-v1')
228
+ return refuse(400, 'turn_effects_unsupported');
227
229
  if (body.contract !== undefined && body.contract !== SUPPORTED_CONTRACT) {
228
230
  return refuse(400, 'contract_unsupported');
229
231
  }
@@ -310,6 +312,7 @@ export function createChannelAgent(options) {
310
312
  contact: body.contact,
311
313
  channel: body.channel,
312
314
  setup: body.setup,
315
+ continuity: body.continuity,
313
316
  booking: {
314
317
  async requestVerification(appointmentId) {
315
318
  return (await readOrRefuse(await authed('/api/capability/booking/verification/request', {
@@ -521,6 +524,15 @@ export function createChannelAgent(options) {
521
524
  // PURE BUILDERS. They return an action and touch nothing — so a pack's `answer` can
522
525
  // be tested by calling it and inspecting what came back, with no core, no fake and no
523
526
  // network.
527
+ turn(effects, opts) {
528
+ if (body.continuity?.protocol !== 'scoped-v1' || effects.protocol !== 'scoped-v1')
529
+ throw new Error('turn_effects_not_negotiated');
530
+ return {
531
+ action: 'turn',
532
+ effects,
533
+ usage: opts?.usage ?? { input_tokens: 0, output_tokens: 0 },
534
+ };
535
+ },
524
536
  reply(text, opts) {
525
537
  const run = body.unanswered_messages ?? [];
526
538
  if (run.length > 0 && run.every((m) => m.email && !m.email.replyable)) {
@@ -598,6 +610,8 @@ export function createChannelAgent(options) {
598
610
  // ANSWERED on the preview path — so the owner is shown, under v1 assumptions, something
599
611
  // no customer of theirs would ever receive. A preview that lies is worse than no
600
612
  // preview, because it is trusted.
613
+ if (body.continuity && body.continuity.protocol !== 'scoped-v1')
614
+ return refuse(400, 'turn_effects_unsupported');
601
615
  if (body.contract !== undefined && body.contract !== SUPPORTED_CONTRACT) {
602
616
  return refuse(400, 'contract_unsupported');
603
617
  }
package/dist/index.d.ts CHANGED
@@ -6,4 +6,4 @@ export { useKee, MemoryConflictError, RecordConflictError, RecordValidationError
6
6
  export { keemakrToolDirectory } from './tool-directory.js';
7
7
  export { refreshGrant, REFRESH_THRESHOLD_SECONDS } from './refresh.js';
8
8
  export { audioCanvas, imageCanvas, type AudioCanvas, type Canvas, type ImageCanvas, type ReportCanvas, } from './canvases.js';
9
- export { createChannelAgent, assertTrustworthyCore, ChannelReachError, ChannelReplyNotDeliverable, isApprovalRequired, isHoldSettled, isOutcomeUnknown, isWriteDone, type ChannelWriteDone, type ChannelWriteResult, type ChannelWriteOptions, type ChannelApprovalRequired, type ChannelHoldSettled, type ChannelOutcomeUnknown, type HeldActionStatus, type ChannelReach, type ChannelRecord, type ChannelAction, type ChannelAgent, type ChannelAgentOptions, type ChannelContext, type ChannelDelivery, type ChannelEmail, type ChannelMessage, type HandoffReason, type Usage, } from './channel.js';
9
+ export { createChannelAgent, assertTrustworthyCore, ChannelReachError, ChannelReplyNotDeliverable, isApprovalRequired, isHoldSettled, isOutcomeUnknown, isWriteDone, type ChannelWriteDone, type ChannelWriteResult, type ChannelWriteOptions, type ChannelApprovalRequired, type ChannelHoldSettled, type ChannelOutcomeUnknown, type HeldActionStatus, type ChannelReach, type ChannelRecord, type ChannelAction, type ChannelAgent, type ChannelAgentOptions, type ChannelContext, type ChannelDelivery, type ChannelTurnEffects, type ChannelQuestion, type QuestionRef, type ChannelEmail, type ChannelMessage, type HandoffReason, type Usage, } from './channel.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keemakr/agent-sdk",
3
- "version": "0.19.0-dev-20261014201650",
3
+ "version": "0.19.0-dev-20261014211203",
4
4
  "description": "The floor for keemakr marketplace agents: verify the capability grant and reach tenant connections, memory, and shared platform tools through keemakr-core — without holding raw secrets or resolving the tenant yourself.",
5
5
  "license": "MIT",
6
6
  "type": "module",