@ziggs-ai/agent-sdk 0.7.2 → 0.8.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.
@@ -1,7 +1,22 @@
1
1
  import { query } from '@anthropic-ai/claude-agent-sdk';
2
2
  import type { Workflow } from '../types.js';
3
3
  import type { ToolManager } from '../tools/ToolManager.js';
4
+ import { type RawEvent } from '../runtime/buildOutcome.js';
4
5
  import type { TickInput, TickOutput } from './Agent.js';
6
+ /**
7
+ * ZIG-1035 / ZIG-1052: status notices must not spend a Claude turn answering
8
+ * nobody. Real work wakes arrive as chat `message`, synthetic `task_result`
9
+ * from `wakeTaskAssignment`, or `agreement_lifecycle` (hire / proposal
10
+ * resolution — the FSM kickoffs on the same event; Claude must act too).
11
+ *
12
+ * Intentionally not mirrored as an FSM "notice skip": the FSM already no-ops
13
+ * enter-only outcomes in AgentMachine; `agreement_lifecycle` never produces
14
+ * `enter`, so hire wakes always step. Claude's filter is narrower — pure
15
+ * `notification` only.
16
+ *
17
+ * Empty / missing batch → notice-only (do not burn a turn on a malformed wake).
18
+ */
19
+ export declare function isNoticeOnlyWake(event: RawEvent | undefined): boolean;
5
20
  export type CognitionKind = 'fsm' | 'claude-sdk';
6
21
  export interface ClaudeSdkAgentOptions {
7
22
  workflow: Workflow;
@@ -9,19 +9,23 @@ import { claudeQueryOptions, buildClaudeEventPrompt, summarizeContext, extractAs
9
9
  import { assertHostedAgentZiggsTools } from './hostedAgentConnection.js';
10
10
  import { runtimeLog } from '../shared/runtimeLog.js';
11
11
  /**
12
- * ZIG-1035: lifecycle / status notices must not spend a Claude turn answering
13
- * nobody. Real work wakes arrive as chat `message` or as synthetic `task_result`
14
- * from `wakeTaskAssignment` (task channel). Terminal task_result notices still
15
- * run so orchestrators can consume completions.
12
+ * ZIG-1035 / ZIG-1052: status notices must not spend a Claude turn answering
13
+ * nobody. Real work wakes arrive as chat `message`, synthetic `task_result`
14
+ * from `wakeTaskAssignment`, or `agreement_lifecycle` (hire / proposal
15
+ * resolution — the FSM kickoffs on the same event; Claude must act too).
16
+ *
17
+ * Intentionally not mirrored as an FSM "notice skip": the FSM already no-ops
18
+ * enter-only outcomes in AgentMachine; `agreement_lifecycle` never produces
19
+ * `enter`, so hire wakes always step. Claude's filter is narrower — pure
20
+ * `notification` only.
21
+ *
22
+ * Empty / missing batch → notice-only (do not burn a turn on a malformed wake).
16
23
  */
17
- function isNoticeOnlyWake(event) {
24
+ export function isNoticeOnlyWake(event) {
18
25
  const events = getBatchEvents(event);
19
26
  if (events.length === 0)
20
- return false;
21
- return events.every((ev) => {
22
- const t = ev?.type;
23
- return t === 'notification' || t === 'agreement_lifecycle';
24
- });
27
+ return true;
28
+ return events.every((ev) => ev?.type === 'notification');
25
29
  }
26
30
  const DEFAULT_STATE = 'ready';
27
31
  /**
@@ -72,17 +72,27 @@ export function summarizeContext(snap) {
72
72
  function renderEventLines(event, lines) {
73
73
  if (event?.type)
74
74
  lines.push(`Event type: ${event.type}`);
75
- // ZIG-1035: lifecycle notices are events, not counterparty speech.
75
+ // ZIG-1035 / ZIG-1052: notifications are not counterparty speech.
76
+ // agreement_lifecycle is a work signal (hire approval) — label it as such.
76
77
  if (event?.text) {
77
- const notice = event.type === 'notification' ||
78
- event.type === 'agreement_lifecycle' ||
79
- event.entryType === 'notification';
80
- lines.push(`${notice ? 'Notification' : 'Message'}: ${event.text}`);
78
+ const label = event.type === 'agreement_lifecycle'
79
+ ? 'Agreement lifecycle'
80
+ : event.type === 'notification' || event.entryType === 'notification'
81
+ ? 'Notification'
82
+ : 'Message';
83
+ lines.push(`${label}: ${event.text}`);
81
84
  }
82
85
  if (event?.senderId)
83
86
  lines.push(`Sender: ${event.senderId}`);
84
87
  if (event?.operation)
85
88
  lines.push(`Operation: ${event.operation}`);
89
+ if (event?.type === 'agreement_lifecycle' &&
90
+ (event.operation === 'proposal_approved' || event.operation === 'proposal_approved_execute')) {
91
+ lines.push('Work signal: proposal approved. If you proposed this agreement, spawn the first task (task_create) under it now. If you are the hired provider, begin fulfilling per the agreement — do not ignore this wake.');
92
+ }
93
+ if (typeof event?.agreementId === 'string' && event.agreementId) {
94
+ lines.push(`Agreement: ${event.agreementId}`);
95
+ }
86
96
  // ZIG-832: surface the task/agreement this event carries. Without it the brain
87
97
  // can't tell WHICH task to update/complete (workers otherwise stall asking for
88
98
  // their own taskId) and can't see whether the referenced step already ran or
@@ -64,7 +64,14 @@ interface WireMetadata {
64
64
  service?: {
65
65
  task?: WireTask;
66
66
  operation?: string;
67
- agreement?: unknown;
67
+ agreement?: {
68
+ agreementId?: string;
69
+ status?: string;
70
+ proposal?: {
71
+ status?: string;
72
+ };
73
+ parties?: WireParties;
74
+ };
68
75
  agreementId?: string;
69
76
  };
70
77
  [key: string]: unknown;
@@ -19,12 +19,41 @@ function isTaskRelated(metadata, taskData) {
19
19
  || contentType === 'task' || contentType === 'task_update'
20
20
  || Boolean(taskData.state || taskData.proposal || taskData.parentTaskId || taskData.executorId || taskData.agentId || taskData.payerId || taskData.createdBy));
21
21
  }
22
+ function wireOperation(metadata) {
23
+ const op = metadata.service?.operation ?? metadata.operation;
24
+ return typeof op === 'string' ? op : undefined;
25
+ }
26
+ function wireAgreementId(metadata) {
27
+ const raw = metadata.service?.agreementId ??
28
+ metadata.service?.agreement?.agreementId ??
29
+ metadata.agreementId;
30
+ return typeof raw === 'string' ? raw : '';
31
+ }
32
+ /**
33
+ * Self-hire / fully-consented create activates before the chat fan-out lands.
34
+ * The wire still says `proposal_created`, but the embedded agreement is already
35
+ * `active` / proposal `approved`. That is the hire work signal (same as
36
+ * `proposal_approved_execute`) — Claude must not park it as a notice (ZIG-1052).
37
+ */
38
+ function isAlreadyActivatedProposalCreated(metadata) {
39
+ if (wireOperation(metadata) !== 'proposal_created')
40
+ return false;
41
+ const agr = metadata.service?.agreement;
42
+ if (!agr || typeof agr !== 'object')
43
+ return false;
44
+ const status = agr.status;
45
+ const proposalStatus = agr.proposal?.status;
46
+ return status === 'active' || proposalStatus === 'approved';
47
+ }
22
48
  function isAgreementLifecycleNotification(metadata) {
23
49
  const entryType = metadata.entryType || 'message';
24
- const operation = metadata.service?.operation ?? metadata.operation;
25
- return (entryType === 'notification' &&
26
- typeof operation === 'string' &&
27
- PROPOSAL_RESOLVED_OPERATIONS.has(operation));
50
+ if (entryType !== 'notification')
51
+ return false;
52
+ const operation = wireOperation(metadata);
53
+ if (typeof operation === 'string' && PROPOSAL_RESOLVED_OPERATIONS.has(operation)) {
54
+ return true;
55
+ }
56
+ return isAlreadyActivatedProposalCreated(metadata);
28
57
  }
29
58
  function isTerminalTaskState(state) {
30
59
  return state === 'completed' || state === 'failed' || state === 'cancelled';
@@ -57,9 +86,10 @@ export function normalizeIncomingEvent({ text, metadata = {} }) {
57
86
  ...(entryType ? { entryType } : {}),
58
87
  };
59
88
  if (isAgreementLifecycleNotification(metadata)) {
60
- const operation = metadata.service?.operation ?? metadata.operation;
61
- const rawAgreementId = metadata.service?.agreementId ?? metadata.agreementId;
62
- const agreementId = typeof rawAgreementId === 'string' ? rawAgreementId : '';
89
+ const operation = isAlreadyActivatedProposalCreated(metadata)
90
+ ? 'proposal_approved_execute'
91
+ : wireOperation(metadata);
92
+ const agreementId = wireAgreementId(metadata);
63
93
  return {
64
94
  chatId,
65
95
  event: {
@@ -1,4 +1,4 @@
1
- import { type Agreement, type ProposeAgreementData, type ProposeDirectInput, type ProposeBroadcastInput, type PublishOfferPayload, type DelegateAgreementData, type CounterAgreementData, type CreateAgreementBody, type ListAgreementsFilters, type GetMyAgreementsFilters, type ChatLinkType, type ArtifactLinkType, type UserRole } from '@ziggs-ai/api-client';
1
+ import { type Agreement, type ClaimedKind, type ProposeAgreementData, type ProposeDirectInput, type ProposeBroadcastInput, type PublishOfferPayload, type DelegateAgreementData, type CounterAgreementData, type CreateAgreementBody, type ListAgreementsFilters, type GetMyAgreementsFilters, type ChatLinkType, type ArtifactLinkType, type UserRole } from '@ziggs-ai/api-client';
2
2
  export declare class AgreementService {
3
3
  private creds;
4
4
  constructor(operatorKey: string, agentId: string);
@@ -20,12 +20,14 @@ export declare class AgreementService {
20
20
  delegate(payload: DelegateAgreementData): Promise<Agreement>;
21
21
  respond(agreementId: string, action: 'approve' | 'reject'): Promise<Agreement>;
22
22
  /**
23
- * ZIG-1021 — claim any open broadcast (quest, standing offer, link invite).
24
- * Routing (offer vs agreement claim endpoint) is decided in the shared flow.
23
+ * ZIG-1021 — claim any open broadcast (quest, standing offer, hand-off,
24
+ * link invite). Routing (offer vs agreement claim endpoint) is decided in
25
+ * the shared flow; `kind` is the api-client ClaimedKind so this signature
26
+ * can't drift from the flow again (a 'hand-off' addition broke the build).
25
27
  */
26
28
  claim(agreementId: string): Promise<{
27
29
  agreement: Agreement;
28
- kind: 'link' | 'offer' | 'quest';
30
+ kind: ClaimedKind;
29
31
  }>;
30
32
  /**
31
33
  * ZIG-1022 — a link proposal is an agreement (engagementKind 'link') to one
@@ -38,8 +38,10 @@ export class AgreementService {
38
38
  return respondToAgreement(agreementId, action, this.creds);
39
39
  }
40
40
  /**
41
- * ZIG-1021 — claim any open broadcast (quest, standing offer, link invite).
42
- * Routing (offer vs agreement claim endpoint) is decided in the shared flow.
41
+ * ZIG-1021 — claim any open broadcast (quest, standing offer, hand-off,
42
+ * link invite). Routing (offer vs agreement claim endpoint) is decided in
43
+ * the shared flow; `kind` is the api-client ClaimedKind so this signature
44
+ * can't drift from the flow again (a 'hand-off' addition broke the build).
43
45
  */
44
46
  claim(agreementId) {
45
47
  return claimOpenAgreement(agreementId, this.creds);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ziggs-ai/agent-sdk",
3
- "version": "0.7.2",
3
+ "version": "0.8.1",
4
4
  "description": "Agent framework SDK for building autonomous agents on the Ziggs platform",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -26,7 +26,7 @@
26
26
  "@anthropic-ai/claude-agent-sdk": "^0.3.204",
27
27
  "@anthropic-ai/sdk": "^0.110.0",
28
28
  "@modelcontextprotocol/sdk": "^1.29.0",
29
- "@ziggs-ai/api-client": "^0.6.3",
29
+ "@ziggs-ai/api-client": "^0.7.1",
30
30
  "ajv": "^8.17.1",
31
31
  "dotenv": "^17.2.3",
32
32
  "openai": "^4.0.0",