@ziggs-ai/agent-sdk 0.9.0 → 0.10.1

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.
Files changed (55) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/AgentHost.d.ts +53 -14
  3. package/dist/AgentHost.js +137 -8
  4. package/dist/agent/ClaudeSdkAgent.d.ts +27 -2
  5. package/dist/agent/ClaudeSdkAgent.js +151 -78
  6. package/dist/agent/claudeSdkTools.js +39 -11
  7. package/dist/context/applyEffects.js +4 -3
  8. package/dist/defineAgent.js +4 -1
  9. package/dist/formatters/HistoryFormatter.js +11 -0
  10. package/dist/index.d.ts +3 -2
  11. package/dist/index.js +3 -2
  12. package/dist/ingress/normalizeIncoming.d.ts +5 -0
  13. package/dist/ingress/normalizeIncoming.js +23 -1
  14. package/dist/pricing/fleetDefaults.d.ts +0 -3
  15. package/dist/pricing/fleetDefaults.js +0 -6
  16. package/dist/runtime/PromptBuilder.js +9 -5
  17. package/dist/runtime/bootstrapCtx.js +6 -8
  18. package/dist/runtime/buildOutcome.d.ts +9 -1
  19. package/dist/runtime/buildOutcome.js +29 -6
  20. package/dist/runtime/runTurn.js +1 -1
  21. package/dist/runtime/taskBookkeeping.d.ts +0 -2
  22. package/dist/runtime/taskBookkeeping.js +0 -6
  23. package/dist/runtime/taskIntake.js +6 -4
  24. package/dist/runtime/wakeWriteKeys.js +23 -21
  25. package/dist/server/ConnectionPool.d.ts +10 -52
  26. package/dist/server/ConnectionPool.js +26 -57
  27. package/dist/server/EventQueue.d.ts +6 -0
  28. package/dist/server/EventQueue.js +21 -6
  29. package/dist/server/SeenMessages.d.ts +7 -0
  30. package/dist/server/SeenMessages.js +15 -0
  31. package/dist/server/ZiggsEffectHandler.d.ts +1 -0
  32. package/dist/server/ZiggsEffectHandler.js +21 -11
  33. package/dist/server/inboxLoop.d.ts +17 -2
  34. package/dist/server/inboxLoop.js +92 -16
  35. package/dist/server/polls/PollKind.d.ts +72 -0
  36. package/dist/server/polls/PollKind.js +53 -0
  37. package/dist/server/polls/PollRunner.d.ts +62 -0
  38. package/dist/server/polls/PollRunner.js +138 -0
  39. package/dist/server/questMatch.d.ts +5 -0
  40. package/dist/server/questMatch.js +14 -0
  41. package/dist/server/runLauncher.d.ts +0 -6
  42. package/dist/server/runLauncher.js +54 -27
  43. package/dist/server/tasks/TaskService.d.ts +2 -11
  44. package/dist/server/tools/tier1/protocolRunner.d.ts +3 -1
  45. package/dist/server/tools/tier1/protocolRunner.js +40 -41
  46. package/dist/server/tools/tier1/protocolTools.js +23 -13
  47. package/dist/server/tools/tier2/connectionTools.js +12 -1
  48. package/dist/tasks/protocolRegistry.d.ts +27 -0
  49. package/dist/tasks/protocolRegistry.js +36 -0
  50. package/dist/tools/capabilityAdapter.js +12 -1
  51. package/dist/tools/recordReport.js +8 -4
  52. package/dist/types.d.ts +24 -11
  53. package/package.json +2 -2
  54. package/dist/server/staleBacklogGate.d.ts +0 -57
  55. package/dist/server/staleBacklogGate.js +0 -100
package/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.10.0 — unreleased
4
+
5
+ ### Removed (breaking)
6
+
7
+ - Removed exports `createStaleBacklogGate`, `resolveReplayPolicy`, `newestActionableAt`, `parseReplayMaxAge`. The launcher rewrite deleted the wake machinery these gated: every agent stays connected and the write signals it, so there is no backlog to age out and nothing to replay-gate (ZIG-993). Pinned to 0.4.x and importing any of the four? Delete the call — the behaviour it bought is now unconditional. There is no replacement API.
8
+ - `FLEET_REPLAY_MAX_AGE` is no longer read; the replay window it set no longer exists. Nothing in this repo sets it, but it can come out of any deployment env that does.
9
+ - `Ctx` no longer carries `pendingSubtasks`, `collectedResults`, or `delegatedAgreementIds`. ZIG-977 moved the real bookkeeping into `Ctx.tasks` (keyed by taskId) and ZIG-1057 stopped syncing these mirrors, but they stayed on the interface as required-and-always-empty — so a workflow guard reading one saw empty forever and silently checked nothing. Read via `allPendingSubtasks(ctx)` / `allCollectedResults(ctx)` / `allDelegatedAgreementIds(ctx)`. Sessions persisted before `tasks` existed are still migrated on restore.
10
+ - `WireResult.status` removed. ZIG-1057 cut `status` from the wire in favour of `state`; four readers kept a `|| r.status` tail, which would have absorbed a backend regression instead of failing on it. Task state resolves as `task?.state || r.state`.
11
+
12
+ ### Added
13
+
14
+ - Declared polls: an agent names the external sources it depends on (`polls`, `pollKinds`) and never executes them. New exports `PollRunner`, `PollRegistry`, `declaredPolls`, and types `PollBinding`, `PollContext`, `PollKind`, `PollResult`, `PollRunnerOptions`, `WakeTarget`. `runLauncher` runs declared polls at fleet level — outside any `AgentHost`, because a host is evicted when idle and that is exactly when its source still needs watching.
15
+
16
+ ### Changed
17
+
18
+ - Attention gate: only the addressed agent spends a turn. A message addressed elsewhere in a shared room no longer wakes every participant.
19
+ - No turn on a lane whose agreement has ended, and none on the echo of a task that already reported — both previously cost a turn and produced nothing.
20
+ - Persona wire shapes are understood on inbound frames.
21
+ - A wake declares its lane so the backend can fence reach (ZIG-1092).
22
+ - A proposal awaiting the agent's own consent counts as actionable rather than a notice to park (ZIG-1078).
23
+ - Artifact idempotency keys are derived from content (ZIG-1083).
24
+ - Blank tool arguments fall back to the wake's lane (ZIG-1081).
25
+
3
26
  ## 0.9.0 — 2026-07-30
4
27
 
5
28
  ### Changed (breaking)
@@ -71,8 +71,17 @@ export interface AgentHostOptions {
71
71
  agentId?: string;
72
72
  name?: string | null;
73
73
  wsUrl?: string;
74
+ /** Store-facing one-liner. See DefineAgentInput.description. */
74
75
  description?: string;
76
+ /** Claude-brain system prompt (ZIG-1080); falls back to `description`. */
77
+ systemPrompt?: string;
75
78
  specialization?: string | null;
79
+ /**
80
+ * ZIG-1185 — strings compared exactly against a quest's `match` field.
81
+ * Miss → ack with zero LLM. Match → normal wake. `specialization` is also
82
+ * checked when set.
83
+ */
84
+ questMatchTags?: string[];
76
85
  tools?: ToolDefinition[];
77
86
  taskTools?: 'all' | 'none' | string[];
78
87
  /**
@@ -117,14 +126,6 @@ export interface AgentHostOptions {
117
126
  sessionStore?: SessionStore;
118
127
  /** Observability tap: called after every effect with the effect and its response. */
119
128
  effectTap?: (effect: import('./types.js').Effect, response: unknown) => void;
120
- /**
121
- * Liveness signal: called whenever the host receives or processes work (an
122
- * inbox message or resource event delivered to a lane). Hosting pools use it
123
- * to reset their idle-eviction timer — without it a host that is actively
124
- * working (but never addressed via pool.sendTo) is torn down mid-stream at
125
- * the idle timeout, discarding lane state, Claude session ids, and timers.
126
- */
127
- onActivity?: () => void;
128
129
  /** Observability tap: called on every FSM state transition, no-match, depth-guard, or error-escape (ZIG-442). */
129
130
  transitionTap?: (event: import('./runtime/AgentMachine.js').TransitionEvent) => void;
130
131
  /**
@@ -195,11 +196,54 @@ export declare class AgentHost {
195
196
  handleResourceEvent(event: Record<string, unknown>): Promise<void>;
196
197
  /** Wire entry. Normalize → resolve session → enqueue on the right lane. */
197
198
  handleMessage(text: string, metadata?: Record<string, unknown>): Promise<void>;
199
+ /**
200
+ * ZIG-1144 part 3 — an agreement lane outlives the agreement.
201
+ *
202
+ * A lane named `agrn-<agreementId>` keeps its Claude session for the whole
203
+ * engagement, so a trailing event after the deal ends pays for all of it: on
204
+ * 2026-08-03 a cancelled hand-off woke its lane once for 92k tokens, and on
205
+ * 08-04 the same shape cost 147k with the session reaching 399,864 input tokens
206
+ * before it reset. Nothing was produced either time — the work had finished and
207
+ * the contract was gone.
208
+ *
209
+ * So a lane whose agreement is terminal does not get a turn. The event is still
210
+ * consumed (and acked by the inbox loop), the FSM is untouched, and a chat lane
211
+ * is never affected: only the agreement-scoped routing key is checked.
212
+ *
213
+ * Failing open is deliberate. If the lookup errors we run the turn — a missed
214
+ * suppression costs tokens, while a wrongly suppressed turn drops work.
215
+ */
216
+ private laneAgreementIsTerminal;
217
+ /**
218
+ * ZIG-1153 — the trailing turn on work that is already reported.
219
+ *
220
+ * A lane session carries the whole engagement, so the LAST wake is the most
221
+ * expensive one: on prod, the frame that followed a completed elicitation cost
222
+ * **158,733 tokens** with the session's input at 366,417 before ZIG-816 reset
223
+ * it, and produced nothing — the spec had been written and the task closed one
224
+ * frame earlier.
225
+ *
226
+ * A task frame that carries a terminal state AND a result is an echo of work
227
+ * this agent has already finished: there is no decision left in it. Dropping it
228
+ * is not the same suppression as {@link laneAgreementIsTerminal}, which waits
229
+ * for the agreement to end — this fires while the engagement is still live,
230
+ * which is where the cost actually was.
231
+ *
232
+ * Deliberately narrow: only a `task_result` frame, only with a terminal state,
233
+ * and only when a result is attached. A completed task with no result recorded
234
+ * still gets its turn, because that is an agent with something left to say.
235
+ */
236
+ private isSettledTaskEcho;
198
237
  private _processEvent;
199
238
  /** Arms the lane's liveness timer iff stateName is a parked state declaring timeoutMs. */
200
239
  private _armIfTimedParked;
201
240
  private _resolveOwnAgentId;
202
241
  connectAsync(_timeout?: number): Promise<this>;
242
+ /**
243
+ * ZIG-1185 — plain string compare against {@link AgentHostOptions.questMatchTags}
244
+ * and `specialization`. No match → return (caller acks). Match → normal wake.
245
+ */
246
+ private triageQuestDelivery;
203
247
  private _bootstrapAssignedActiveTasks;
204
248
  /** ZIG-802: replay task-assigned for an existing active task (demo recovery). */
205
249
  wakeTaskAssignment(taskId: string): Promise<void>;
@@ -214,10 +258,5 @@ export declare class AgentHost {
214
258
  * createAgentPool([cfg, ...]) → ConnectionPool (lazy fleet, async-loaded)
215
259
  */
216
260
  export declare function createAgent(options?: AgentHostOptions | AgentConfig): AgentHost;
217
- export declare function createAgentPool(configs: AgentHostOptions[] | AgentConfig[], opts?: {
218
- poolOptions?: {
219
- maxActive?: number;
220
- idleTimeoutMs?: number;
221
- };
222
- }): Promise<unknown>;
261
+ export declare function createAgentPool(configs: AgentHostOptions[] | AgentConfig[]): Promise<unknown>;
223
262
  export default AgentHost;
package/dist/AgentHost.js CHANGED
@@ -18,6 +18,7 @@ import { ArtifactsClient, ContextReadClient, TelemetryClient, sendChatMessage, }
18
18
  import { TaskService } from './server/tasks/TaskService.js';
19
19
  import { AgreementService } from './server/agreements/AgreementService.js';
20
20
  import { runInboxLoop } from './server/inboxLoop.js';
21
+ import { questExactMatch } from './server/questMatch.js';
21
22
  import { SeenMessages } from './server/SeenMessages.js';
22
23
  import { runtimeLog } from './shared/runtimeLog.js';
23
24
  /**
@@ -90,7 +91,12 @@ function createBackendServices(options) {
90
91
  const agentId = options.agentId;
91
92
  const artifactsClient = new ArtifactsClient(operatorKey, agentId);
92
93
  const contextReadClient = new ContextReadClient(operatorKey, agentId);
93
- const promptBuilder = new PromptBuilder({ description: options.description, specialization: options.specialization });
94
+ // ZIG-1080: the role text is the prompt when the author split the two; the
95
+ // store-facing `description` is only the fallback for agents that never did.
96
+ const promptBuilder = new PromptBuilder({
97
+ description: options.systemPrompt || options.description,
98
+ specialization: options.specialization,
99
+ });
94
100
  const taskService = new TaskService(operatorKey, agentId);
95
101
  const agreementService = new AgreementService(operatorKey, agentId);
96
102
  const telemetryClient = new TelemetryClient(operatorKey, agentId);
@@ -145,6 +151,7 @@ export class AgentHost {
145
151
  name: resolved.name || null,
146
152
  wsUrl: resolved.wsUrl,
147
153
  description: resolved.description || 'A helpful assistant',
154
+ systemPrompt: resolved.systemPrompt,
148
155
  specialization: resolved.specialization ?? null,
149
156
  tools: resolved.tools || [],
150
157
  taskTools: resolved.taskTools,
@@ -268,6 +275,7 @@ export class AgentHost {
268
275
  workflow,
269
276
  toolManager: services.toolManager,
270
277
  description: this.options.description,
278
+ systemPrompt: this.options.systemPrompt,
271
279
  specialization: this.options.specialization,
272
280
  model: resolveClaudeModel(this.options.model, this.options.services?.llm?.model),
273
281
  anthropicKey: this.options.anthropicKey,
@@ -285,7 +293,14 @@ export class AgentHost {
285
293
  promptBuilder: services.promptBuilder,
286
294
  });
287
295
  this.eventQueue = new EventQueue((event, laneKey) => this._processEvent(event, laneKey));
288
- this.stateTimers = new StateTimers((ev, laneKey) => { void this.eventQueue.enqueue(ev, laneKey); });
296
+ // A timer frame is not a delivery: there is no watermark waiting on it and
297
+ // nothing to redeliver, so its failure is logged and dropped here rather
298
+ // than left as an unhandled rejection (ZIG-1191 made enqueue reject).
299
+ this.stateTimers = new StateTimers((ev, laneKey) => {
300
+ void this.eventQueue.enqueue(ev, laneKey).catch((err) => {
301
+ runtimeLog.warn('AgentHost', `state-timer frame failed laneKey=${laneKey}: ${err?.message || err}`);
302
+ });
303
+ });
289
304
  }
290
305
  /**
291
306
  * Handler for a resource change (artifact created, task state transition,
@@ -364,12 +379,96 @@ export class AgentHost {
364
379
  // The inbox loop owns ack (act-before-ack, delivery law); the host only
365
380
  // routes the event onto its lane. No live-path watermark ack — there is no
366
381
  // separate live/push channel to reconcile against anymore.
367
- return this.eventQueue.enqueue(normalized.event, laneKey);
382
+ //
383
+ // ZIG-1191: a failure un-marks the messageId. The mark above is what stops
384
+ // the same message being processed twice; leaving it set on a failed wake
385
+ // would make the redelivery this rejection buys us a no-op.
386
+ try {
387
+ await this.eventQueue.enqueue(normalized.event, laneKey);
388
+ }
389
+ catch (err) {
390
+ if (messageId)
391
+ this.seenMessages.unmark(messageId);
392
+ throw err;
393
+ }
394
+ }
395
+ /**
396
+ * ZIG-1144 part 3 — an agreement lane outlives the agreement.
397
+ *
398
+ * A lane named `agrn-<agreementId>` keeps its Claude session for the whole
399
+ * engagement, so a trailing event after the deal ends pays for all of it: on
400
+ * 2026-08-03 a cancelled hand-off woke its lane once for 92k tokens, and on
401
+ * 08-04 the same shape cost 147k with the session reaching 399,864 input tokens
402
+ * before it reset. Nothing was produced either time — the work had finished and
403
+ * the contract was gone.
404
+ *
405
+ * So a lane whose agreement is terminal does not get a turn. The event is still
406
+ * consumed (and acked by the inbox loop), the FSM is untouched, and a chat lane
407
+ * is never affected: only the agreement-scoped routing key is checked.
408
+ *
409
+ * Failing open is deliberate. If the lookup errors we run the turn — a missed
410
+ * suppression costs tokens, while a wrongly suppressed turn drops work.
411
+ */
412
+ async laneAgreementIsTerminal(sessionId) {
413
+ if (!sessionId.startsWith('agrn-'))
414
+ return false;
415
+ const agreementId = sessionId.slice('agrn-'.length);
416
+ if (!agreementId || !this.agreementService)
417
+ return false;
418
+ try {
419
+ const agreement = await this.agreementService.get(agreementId);
420
+ const status = String(agreement?.status ?? '');
421
+ return (status === 'cancelled' || status === 'refunded' || status === 'fulfilled');
422
+ }
423
+ catch {
424
+ return false;
425
+ }
426
+ }
427
+ /**
428
+ * ZIG-1153 — the trailing turn on work that is already reported.
429
+ *
430
+ * A lane session carries the whole engagement, so the LAST wake is the most
431
+ * expensive one: on prod, the frame that followed a completed elicitation cost
432
+ * **158,733 tokens** with the session's input at 366,417 before ZIG-816 reset
433
+ * it, and produced nothing — the spec had been written and the task closed one
434
+ * frame earlier.
435
+ *
436
+ * A task frame that carries a terminal state AND a result is an echo of work
437
+ * this agent has already finished: there is no decision left in it. Dropping it
438
+ * is not the same suppression as {@link laneAgreementIsTerminal}, which waits
439
+ * for the agreement to end — this fires while the engagement is still live,
440
+ * which is where the cost actually was.
441
+ *
442
+ * Deliberately narrow: only a `task_result` frame, only with a terminal state,
443
+ * and only when a result is attached. A completed task with no result recorded
444
+ * still gets its turn, because that is an agent with something left to say.
445
+ */
446
+ isSettledTaskEcho(event) {
447
+ const frame = event;
448
+ if (frame?.type !== 'task_result')
449
+ return false;
450
+ const result = frame.result;
451
+ if (!result)
452
+ return false;
453
+ const state = String(result.state ?? '');
454
+ if (state !== 'completed' && state !== 'cancelled' && state !== 'failed') {
455
+ return false;
456
+ }
457
+ const reported = result.result;
458
+ return reported !== undefined && reported !== null && reported !== '';
368
459
  }
369
460
  async _processEvent(event, laneKey) {
370
461
  const session = this.sessions.get(laneKey);
371
462
  if (!session?.sessionId)
372
463
  return;
464
+ if (await this.laneAgreementIsTerminal(session.sessionId)) {
465
+ runtimeLog.info(this.options.name || 'agent', `skipping wake on ${session.sessionId} — its agreement is terminal`);
466
+ return;
467
+ }
468
+ if (this.isSettledTaskEcho(event)) {
469
+ runtimeLog.info(this.options.name || 'agent', `skipping wake on ${session.sessionId} — its task already reported a result`);
470
+ return;
471
+ }
373
472
  // Stale timer frames (superseded epoch) are dropped BEFORE touching the
374
473
  // live timer — a pure-stale frame must not reset a freshly armed countdown.
375
474
  const frame = this.stateTimers.stripStale(event, laneKey);
@@ -472,6 +571,13 @@ export class AgentHost {
472
571
  if (senderId && senderType === 'USER') {
473
572
  this.eff({ kind: 'send-message', sessionId: session.sessionId, receiverId: senderId, text: 'Something went wrong. Please try again.' }).catch(() => { });
474
573
  }
574
+ // ZIG-1191: and then say so. Everything above is recovery for the lane —
575
+ // it does not mean the event was handled, and the caller is about to
576
+ // decide whether the delivery that carried it can be acked. Swallowing
577
+ // here is what turned a failed wake into a lost one: two hand-off publish
578
+ // notifications were acked after failing, so nothing ever redelivered
579
+ // them and a claim stayed blocked on consent that no wake would give.
580
+ throw err;
475
581
  }
476
582
  finally {
477
583
  clearTimeout(tickWatchdog);
@@ -510,18 +616,15 @@ export class AgentHost {
510
616
  seen: this.seenMessages,
511
617
  label,
512
618
  onMessage: (text, metadata) => {
513
- this.options.onActivity?.();
514
619
  return this.handleMessage(text, metadata);
515
620
  },
516
621
  onResourceEvent: (event) => {
517
- this.options.onActivity?.();
518
622
  return this.handleResourceEvent(event);
519
623
  },
520
624
  // ZIG-973: work delivered by the inbox's task channel, not by a chat
521
625
  // row — this is what reaches a task under a chat-less agreement or one
522
626
  // the agent assigned to itself.
523
627
  onTask: async (task) => {
524
- this.options.onActivity?.();
525
628
  try {
526
629
  await this.wakeTaskAssignment(task.taskId);
527
630
  }
@@ -529,10 +632,35 @@ export class AgentHost {
529
632
  runtimeLog.warn(this.options.name || 'agent', `inbox task ${task.taskId} could not be woken: ${err.message}`);
530
633
  }
531
634
  },
635
+ // ZIG-1185: exact-match triage in code — miss acks with 0 tokens.
636
+ onQuest: async (quest) => {
637
+ await this.triageQuestDelivery(quest);
638
+ },
532
639
  });
533
640
  }
534
641
  return this;
535
642
  }
643
+ /**
644
+ * ZIG-1185 — plain string compare against {@link AgentHostOptions.questMatchTags}
645
+ * and `specialization`. No match → return (caller acks). Match → normal wake.
646
+ */
647
+ async triageQuestDelivery(quest) {
648
+ const tags = [
649
+ ...(this.options.questMatchTags ?? []),
650
+ ...(this.options.specialization ? [this.options.specialization] : []),
651
+ ];
652
+ if (!questExactMatch(quest.match, tags)) {
653
+ runtimeLog.debug(this.options.name || 'agent', `quest ${quest.agreementId}: no tag match for ${JSON.stringify(quest.match)} — ack without wake`);
654
+ return;
655
+ }
656
+ const needle = quest.match.trim();
657
+ const title = quest.title?.trim() || needle;
658
+ await this.handleMessage(`New marketplace quest matching your tags (${needle}): ${title}. Claim with agreement_claim if you will take it.`, {
659
+ messageId: `quest-wake:${quest.agreementId}:${quest.ts}`,
660
+ chatId: `marketplace:${quest.agreementId}`,
661
+ agreementId: quest.agreementId,
662
+ });
663
+ }
536
664
  async _bootstrapAssignedActiveTasks() {
537
665
  const agentId = this.options.agentId;
538
666
  if (!agentId || !this.taskService)
@@ -619,10 +747,11 @@ export class AgentHost {
619
747
  export function createAgent(options = {}) {
620
748
  return new AgentHost(options);
621
749
  }
622
- export async function createAgentPool(configs, opts = {}) {
750
+ export async function createAgentPool(configs) {
623
751
  const ConnectionPool = await loadConnectionPool();
624
- const pool = new ConnectionPool(opts.poolOptions);
752
+ const pool = new ConnectionPool();
625
753
  pool.register(configs);
754
+ await pool.connectAll();
626
755
  return pool;
627
756
  }
628
757
  export default AgentHost;
@@ -15,13 +15,21 @@ import type { TickInput, TickOutput } from './Agent.js';
15
15
  * `notification` only.
16
16
  *
17
17
  * Empty / missing batch → notice-only (do not burn a turn on a malformed wake).
18
+ *
19
+ * A message addressed to a DIFFERENT party is notice-only too (attention
20
+ * gate): the sender named who should act, and it wasn't this agent. The
21
+ * message still lands in chat history and arrives as context on the next
22
+ * real wake — skipped here is the LLM turn, not the information.
18
23
  */
19
- export declare function isNoticeOnlyWake(event: RawEvent | undefined): boolean;
24
+ export declare function isNoticeOnlyWake(event: RawEvent | undefined, ownAgentId?: string | null): boolean;
20
25
  export type CognitionKind = 'fsm' | 'claude-sdk';
21
26
  export interface ClaudeSdkAgentOptions {
22
27
  workflow: Workflow;
23
28
  toolManager: ToolManager;
29
+ /** Store-facing one-liner. Used as the role block only when `systemPrompt` is absent. */
24
30
  description: string;
31
+ /** The agent's system prompt (ZIG-1080). Preferred over `description` for the role block. */
32
+ systemPrompt?: string;
25
33
  specialization?: string | null;
26
34
  model: string;
27
35
  anthropicKey: string;
@@ -35,6 +43,16 @@ export interface ClaudeSdkAgentOptions {
35
43
  /** Injectable for tests. Defaults to @anthropic-ai/claude-agent-sdk query(). */
36
44
  queryFn?: typeof query;
37
45
  }
46
+ /**
47
+ * ZIG-1191 — "the transcript you asked me to resume is not here".
48
+ *
49
+ * Claude Code answers a `resume` of an unknown id with this, and it is the only
50
+ * failure that a fresh session reliably fixes: the id is valid-looking but its
51
+ * transcript lived on a container that is gone. Matched on the message because
52
+ * that is all the CLI gives us — no code, no typed error — so keep it narrow
53
+ * enough that a different failure never claims the retry.
54
+ */
55
+ export declare function isMissingSessionError(err: unknown): boolean;
38
56
  /**
39
57
  * A Ziggs agent whose brain is the Claude Agent SDK. Same host, same `eff`
40
58
  * side channel, same tool surface as the FSM `Agent` — the ONLY difference is
@@ -44,7 +62,7 @@ export interface ClaudeSdkAgentOptions {
44
62
  export declare class ClaudeSdkAgent {
45
63
  workflow: Workflow;
46
64
  toolManager: ToolManager;
47
- private description;
65
+ private rolePrompt;
48
66
  private specialization;
49
67
  private model;
50
68
  private anthropicKey;
@@ -53,6 +71,13 @@ export declare class ClaudeSdkAgent {
53
71
  private maxTurns;
54
72
  private statelessPerWake;
55
73
  private queryFn;
74
+ /**
75
+ * ZIG-1120 #7 — tool set is fixed at host construction; rebuilding the
76
+ * in-process MCP server (+ zod schema tree) every wake was pure waste.
77
+ * A mutable holder supplies per-wake context without closing over `tick`.
78
+ */
79
+ private toolContextHolder;
80
+ private sdkMcpServer;
56
81
  constructor(opts: ClaudeSdkAgentOptions);
57
82
  tick({ event, ctx, state, identity, eff, signal }: TickInput): Promise<TickOutput>;
58
83
  }