@ziggs-ai/api-client 0.10.0 → 0.10.2

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.
@@ -17,6 +17,10 @@ function reportingHint(env, contentType, taskId) {
17
17
  }
18
18
  return `Reporting finished work? Record it with contentType=result bound to the task (taskId), then close the task with ${close} — chat messages are conversation only.`;
19
19
  }
20
+ /** ZIG-1320 — inline body cap + escape hatch, shared by SDK/MCP descriptions. */
21
+ const ARTIFACT_RECORD_INLINE_CAP = 'Inline text max 50000 characters. Over that: use ziggs_artifact_upload_url ' +
22
+ '(file rail), or record an index artifact plus part artifacts and list the ' +
23
+ 'part ids in the index. The server does not auto-split.';
20
24
  export const recordArtifactCapability = {
21
25
  key: 'artifact_record',
22
26
  names: { sdk: 'artifact_record', mcp: 'ziggs_artifact_record' },
@@ -25,17 +29,23 @@ export const recordArtifactCapability = {
25
29
  sdk: 'Write an artifact. Scope is optional: pass agreementId or chatId to record it there, ' +
26
30
  'or pass no scope at all to record it as yours alone and attach it somewhere later. ' +
27
31
  'Set visibility explicitly. For a finished deliverable, set contentType=result and pass ' +
28
- 'taskId to bind it to the task — heavy results belong in artifacts, not chat messages.',
32
+ 'taskId to bind it to the task — heavy results belong in artifacts, not chat messages. ' +
33
+ ARTIFACT_RECORD_INLINE_CAP,
29
34
  mcp: 'Write an artifact. Scope is optional — pass agreementId or chatId to record it into that ' +
30
35
  'scope, pass taskId alone to bind a deliverable to its task, or pass no scope at all for a ' +
31
36
  'free-standing artifact that is yours until you attach or share it. Never guess a scope: ' +
32
37
  'recording with none always succeeds. Set visibility explicitly. ' +
33
38
  'For a finished deliverable, set contentType=result and pass taskId to bind it to the task. ' +
34
- 'Finished work is the task result — set it with ziggs_task_set_result; never report finished work as a chat message (chat is conversation only).',
39
+ 'Finished work is the task result — set it with ziggs_task_set_result; never report finished work as a chat message (chat is conversation only). ' +
40
+ ARTIFACT_RECORD_INLINE_CAP,
35
41
  },
36
42
  annotation: 'write',
37
43
  params: {
38
- text: { type: 'string', required: true, description: 'Artifact body' },
44
+ text: {
45
+ type: 'string',
46
+ required: true,
47
+ description: 'Artifact body (max 50000 chars). Over limit: ziggs_artifact_upload_url or index+parts.',
48
+ },
39
49
  visibility: {
40
50
  type: 'string',
41
51
  required: true,
@@ -1,3 +1,11 @@
1
+ /**
2
+ * Inline `POST /artifacts` body cap (matches backend
3
+ * `ARTIFACT_PUBLIC_TEXT_MAX_CHARS`). Over this → file rail or multi-part; the
4
+ * server does not auto-split (ZIG-1320).
5
+ */
6
+ export declare const ARTIFACT_INLINE_TEXT_MAX_CHARS = 50000;
7
+ /** ZIG-1320 — named escape hatch for agents that hit the inline cap. */
8
+ export declare const ARTIFACT_INLINE_TEXT_OVER_LIMIT_HINT: string;
1
9
  export type ArtifactVisibility = 'chat' | 'agent-private';
2
10
  export interface ListArtifactsOptions {
3
11
  after?: string;
@@ -2,6 +2,17 @@ import { runtimeLog } from '../shared/runtimeLog.js';
2
2
  import { throwApiError } from '../shared/apiError.js';
3
3
  import { getBackendUrl } from '../utils/urlUtils.js';
4
4
  import { buildOperatorHeaders } from './operatorHeaders.js';
5
+ /**
6
+ * Inline `POST /artifacts` body cap (matches backend
7
+ * `ARTIFACT_PUBLIC_TEXT_MAX_CHARS`). Over this → file rail or multi-part; the
8
+ * server does not auto-split (ZIG-1320).
9
+ */
10
+ export const ARTIFACT_INLINE_TEXT_MAX_CHARS = 50_000;
11
+ /** ZIG-1320 — named escape hatch for agents that hit the inline cap. */
12
+ export const ARTIFACT_INLINE_TEXT_OVER_LIMIT_HINT = `text exceeds ${ARTIFACT_INLINE_TEXT_MAX_CHARS} characters. ` +
13
+ 'For larger content use ziggs_artifact_upload_url (file rail), or split into ' +
14
+ 'an index artifact plus part artifacts and list the part ids in the index. ' +
15
+ 'The server does not auto-split.';
5
16
  function resolveUploadBytes(input) {
6
17
  if (input.content != null) {
7
18
  return Buffer.isBuffer(input.content)
@@ -122,12 +133,18 @@ export class ArtifactsClient {
122
133
  throw new Error('ArtifactsClient.writeStrict: text is required');
123
134
  }
124
135
  this._assertScopeXor(input);
136
+ const text = input.text.trim();
137
+ // ZIG-1320: refuse before the wire so MCP/SDK get a named escape hatch
138
+ // instead of a bare class-validator string.
139
+ if (text.length > ARTIFACT_INLINE_TEXT_MAX_CHARS) {
140
+ throw new Error(`ArtifactsClient.writeStrict: ${ARTIFACT_INLINE_TEXT_OVER_LIMIT_HINT}`);
141
+ }
125
142
  const url = `${getBackendUrl()}/artifacts`;
126
143
  const res = await fetch(url, {
127
144
  method: 'POST',
128
145
  headers: this._headers(),
129
146
  body: JSON.stringify({
130
- text: input.text.trim(),
147
+ text,
131
148
  contentType: input.contentType ?? 'text',
132
149
  visibility: input.visibility ?? 'chat',
133
150
  chatId: input.chatId,
@@ -1,4 +1,22 @@
1
1
  import { type Creds, type Task, type TaskState } from '../types.js';
2
+ /**
3
+ * ZIG-1321 — thin confirmation from task write verbs. Full object stays on
4
+ * getTask / ziggs_task_get.
5
+ */
6
+ export interface TaskWriteConfirm {
7
+ ok: true;
8
+ taskId: string;
9
+ state: TaskState;
10
+ updatedAt?: string;
11
+ status?: string;
12
+ stepCount?: number;
13
+ structureChanged?: boolean;
14
+ stepId?: string;
15
+ stepStatus?: string;
16
+ resultRecorded?: boolean;
17
+ terminalState?: string;
18
+ [key: string]: unknown;
19
+ }
2
20
  /**
3
21
  * When the buyer reviews a task's plan. Task-rail only — an agreement has no
4
22
  * plan to review, which is why cut this from the propose/counter/
@@ -32,10 +50,10 @@ export interface UpdateTaskStateData {
32
50
  */
33
51
  idempotencyKey?: string;
34
52
  }
35
- export declare function updateTaskState(taskId: string, state: TaskState, data: UpdateTaskStateData | undefined, creds: Creds): Promise<Task>;
53
+ export declare function updateTaskState(taskId: string, state: TaskState, data: UpdateTaskStateData | undefined, creds: Creds): Promise<TaskWriteConfirm>;
36
54
  export declare function getActiveTasksForAgent(agentId: string, creds: Creds): Promise<Task[]>;
37
55
  export declare function getActiveTasksForChat(chatId: string, creds: Creds): Promise<Task[]>;
38
- export declare function cancelTask(taskId: string, creds: Creds): Promise<Task>;
56
+ export declare function cancelTask(taskId: string, creds: Creds): Promise<TaskWriteConfirm>;
39
57
  export declare function getSubtasks(parentTaskId: string, creds: Creds): Promise<Task[]>;
40
58
  export interface PlanReplaceStep {
41
59
  /** Omit to let the server mint `step-<n>`. */
@@ -52,10 +70,10 @@ export interface PlanReplaceStep {
52
70
  /** Optional step output stored with this replace. */
53
71
  result?: unknown;
54
72
  }
55
- export declare function replaceTaskPlan(taskId: string, steps: PlanReplaceStep[], creds: Creds): Promise<Task>;
56
- export declare function updateTaskPlanStep(taskId: string, stepId: string, status: string, result: unknown, creds: Creds): Promise<Task>;
57
- export declare function reportTask(taskId: string, message: string | undefined, creds: Creds): Promise<Task>;
58
- export declare function setTaskSatisfaction(taskId: string, satisfaction: 'positive' | 'negative', creds: Creds): Promise<Task>;
73
+ export declare function replaceTaskPlan(taskId: string, steps: PlanReplaceStep[], creds: Creds): Promise<TaskWriteConfirm>;
74
+ export declare function updateTaskPlanStep(taskId: string, stepId: string, status: string, result: unknown, creds: Creds): Promise<TaskWriteConfirm>;
75
+ export declare function reportTask(taskId: string, message: string | undefined, creds: Creds): Promise<TaskWriteConfirm>;
76
+ export declare function setTaskSatisfaction(taskId: string, satisfaction: 'positive' | 'negative', creds: Creds): Promise<TaskWriteConfirm>;
59
77
  export interface ListTasksOptions {
60
78
  state?: string;
61
79
  cursor?: string;
@@ -85,15 +103,15 @@ export declare class TaskClient {
85
103
  constructor(operatorKey: string, agentId?: string);
86
104
  createTask(data: CreateTaskData): Promise<Task>;
87
105
  getTask(taskId: string): Promise<Task>;
88
- updateTaskState(taskId: string, state: TaskState, data?: UpdateTaskStateData): Promise<Task>;
106
+ updateTaskState(taskId: string, state: TaskState, data?: UpdateTaskStateData): Promise<TaskWriteConfirm>;
89
107
  getActiveTasksForAgent(agentId: string): Promise<Task[]>;
90
108
  getActiveTasksForChat(chatId: string): Promise<Task[]>;
91
- cancelTask(taskId: string): Promise<Task>;
109
+ cancelTask(taskId: string): Promise<TaskWriteConfirm>;
92
110
  getSubtasks(parentTaskId: string): Promise<Task[]>;
93
- replaceTaskPlan(taskId: string, steps: PlanReplaceStep[]): Promise<Task>;
94
- updateTaskPlanStep(taskId: string, stepId: string, status: string, result?: unknown): Promise<Task>;
95
- reportTask(taskId: string, message?: string): Promise<Task>;
96
- setTaskSatisfaction(taskId: string, satisfaction: 'positive' | 'negative'): Promise<Task>;
111
+ replaceTaskPlan(taskId: string, steps: PlanReplaceStep[]): Promise<TaskWriteConfirm>;
112
+ updateTaskPlanStep(taskId: string, stepId: string, status: string, result?: unknown): Promise<TaskWriteConfirm>;
113
+ reportTask(taskId: string, message?: string): Promise<TaskWriteConfirm>;
114
+ setTaskSatisfaction(taskId: string, satisfaction: 'positive' | 'negative'): Promise<TaskWriteConfirm>;
97
115
  listTasks(options?: ListTasksOptions): Promise<ListTasksResult>;
98
116
  countTasks(filters?: CountTasksFilters): Promise<number>;
99
117
  }
@@ -24,10 +24,54 @@ function extractTask(data) {
24
24
  if (d['task'] && typeof d['task'] === 'object' && d['task']['taskId']) {
25
25
  return d['task'];
26
26
  }
27
- if (d['taskId'])
27
+ // Top-level task document (GET / create). Do not treat ZIG-1321 thin write
28
+ // confirms (`ok: true`, no description) as a Task.
29
+ if (typeof d['taskId'] === 'string' && d['ok'] !== true) {
28
30
  return d;
31
+ }
29
32
  return null;
30
33
  }
34
+ function extractWriteConfirm(data) {
35
+ if (!data || typeof data !== 'object')
36
+ return null;
37
+ const d = data;
38
+ // New thin shape (no nested task / no description dump).
39
+ if (typeof d['taskId'] === 'string' &&
40
+ typeof d['state'] === 'string' &&
41
+ d['task'] === undefined &&
42
+ d['description'] === undefined) {
43
+ return {
44
+ ok: true,
45
+ taskId: d['taskId'],
46
+ state: d['state'],
47
+ ...(typeof d['updatedAt'] === 'string' || d['updatedAt'] instanceof Date
48
+ ? { updatedAt: String(d['updatedAt']) }
49
+ : {}),
50
+ ...(typeof d['status'] === 'string' ? { status: d['status'] } : {}),
51
+ ...(typeof d['stepCount'] === 'number' ? { stepCount: d['stepCount'] } : {}),
52
+ ...(typeof d['structureChanged'] === 'boolean'
53
+ ? { structureChanged: d['structureChanged'] }
54
+ : {}),
55
+ ...(typeof d['stepId'] === 'string' ? { stepId: d['stepId'] } : {}),
56
+ ...(typeof d['stepStatus'] === 'string' ? { stepStatus: d['stepStatus'] } : {}),
57
+ ...(d['resultRecorded'] === true ? { resultRecorded: true } : {}),
58
+ ...(typeof d['terminalState'] === 'string'
59
+ ? { terminalState: d['terminalState'] }
60
+ : {}),
61
+ };
62
+ }
63
+ // Legacy fat echo — compress to the thin fields so callers stay forward-compat.
64
+ const task = extractTask(data);
65
+ if (!task?.taskId || !task.state)
66
+ return null;
67
+ return {
68
+ ok: true,
69
+ taskId: task.taskId,
70
+ state: task.state,
71
+ ...(task.updatedAt ? { updatedAt: task.updatedAt } : {}),
72
+ ...(typeof d['status'] === 'string' ? { status: d['status'] } : {}),
73
+ };
74
+ }
31
75
  export async function createTask(taskData, creds) {
32
76
  if (!taskData)
33
77
  throw new Error('Task data is required for task creation');
@@ -85,10 +129,10 @@ export async function updateTaskState(taskId, state, data = {}, creds) {
85
129
  throwApiError(res, body, `Task state update failed: ${res.status} ${res.statusText}`);
86
130
  }
87
131
  const result = await res.json().catch(() => null);
88
- const task = extractTask(result);
89
- if (!task)
90
- throw new Error('Invalid response: task data not found');
91
- return task;
132
+ const confirm = extractWriteConfirm(result);
133
+ if (!confirm)
134
+ throw new Error('Invalid response: task write confirm not found');
135
+ return confirm;
92
136
  }
93
137
  export async function getActiveTasksForAgent(agentId, creds) {
94
138
  if (!agentId)
@@ -182,10 +226,10 @@ export async function cancelTask(taskId, creds) {
182
226
  throwApiError(res, body, `Task cancel failed: ${res.status} ${res.statusText}`);
183
227
  }
184
228
  const result = await res.json().catch(() => null);
185
- const task = extractTask(result);
186
- if (!task)
187
- throw new Error('Invalid response: task data not found');
188
- return task;
229
+ const confirm = extractWriteConfirm(result);
230
+ if (!confirm)
231
+ throw new Error('Invalid response: task write confirm not found');
232
+ return confirm;
189
233
  }
190
234
  export async function getSubtasks(parentTaskId, creds) {
191
235
  if (!parentTaskId)
@@ -219,10 +263,10 @@ export async function replaceTaskPlan(taskId, steps, creds) {
219
263
  throwApiError(res, responseBody, `Plan replace failed: ${res.status} ${res.statusText}`);
220
264
  }
221
265
  const data = await res.json().catch(() => null);
222
- const task = extractTask(data);
223
- if (!task)
224
- throw new Error('Invalid response: task data not found');
225
- return task;
266
+ const confirm = extractWriteConfirm(data);
267
+ if (!confirm)
268
+ throw new Error('Invalid response: task write confirm not found');
269
+ return confirm;
226
270
  }
227
271
  export async function updateTaskPlanStep(taskId, stepId, status, result, creds) {
228
272
  if (!taskId)
@@ -242,10 +286,10 @@ export async function updateTaskPlanStep(taskId, stepId, status, result, creds)
242
286
  throwApiError(res, responseBody, `Plan step update failed: ${res.status} ${res.statusText}`);
243
287
  }
244
288
  const data = await res.json().catch(() => null);
245
- const task = extractTask(data);
246
- if (!task)
247
- throw new Error('Invalid response: task data not found');
248
- return task;
289
+ const confirm = extractWriteConfirm(data);
290
+ if (!confirm)
291
+ throw new Error('Invalid response: task write confirm not found');
292
+ return confirm;
249
293
  }
250
294
  // ---------------------------------------------------------------------------
251
295
  // Reporting & satisfaction
@@ -264,10 +308,10 @@ export async function reportTask(taskId, message = '', creds) {
264
308
  throwApiError(res, body, `Task report failed: ${res.status} ${res.statusText}`);
265
309
  }
266
310
  const data = await res.json().catch(() => null);
267
- const task = extractTask(data);
268
- if (!task)
269
- throw new Error('Invalid response: task data not found');
270
- return task;
311
+ const confirm = extractWriteConfirm(data);
312
+ if (!confirm)
313
+ throw new Error('Invalid response: task write confirm not found');
314
+ return confirm;
271
315
  }
272
316
  export async function setTaskSatisfaction(taskId, satisfaction, creds) {
273
317
  if (!taskId)
@@ -285,10 +329,10 @@ export async function setTaskSatisfaction(taskId, satisfaction, creds) {
285
329
  throwApiError(res, body, `Task satisfaction failed: ${res.status} ${res.statusText}`);
286
330
  }
287
331
  const data = await res.json().catch(() => null);
288
- const task = extractTask(data);
289
- if (!task)
290
- throw new Error('Invalid response: task data not found');
291
- return task;
332
+ const confirm = extractWriteConfirm(data);
333
+ if (!confirm)
334
+ throw new Error('Invalid response: task write confirm not found');
335
+ return confirm;
292
336
  }
293
337
  export async function listTasks(options = {}, creds) {
294
338
  assertCreds(creds, 'list tasks');
@@ -5,7 +5,7 @@ export * from './agreementFlows.js';
5
5
  export * from './ChatClient.js';
6
6
  export { MessagesClient } from './MessagesClient.js';
7
7
  export type { ListMessagesOptions, ListMessagesResult } from './MessagesClient.js';
8
- export { ArtifactsClient, artifactScopeForSession, AGREEMENT_LANE_PREFIX, } from './ArtifactsClient.js';
8
+ export { ArtifactsClient, artifactScopeForSession, AGREEMENT_LANE_PREFIX, ARTIFACT_INLINE_TEXT_MAX_CHARS, ARTIFACT_INLINE_TEXT_OVER_LIMIT_HINT, } from './ArtifactsClient.js';
9
9
  export type { ArtifactVisibility, ListArtifactsOptions, ListArtifactsQuery, ListArtifactsResult, WriteArtifactInput, } from './ArtifactsClient.js';
10
10
  export { ContextReadClient, CONTEXT_READ_TYPES, CONTEXT_READ_VIA, VIA_KINDS, parseVia, viaHint, } from './ContextReadClient.js';
11
11
  export type { ContextReadType, ContextReadQuery, ContextReadEnvelope, ContextSnapshotResult, ViaKind, } from './ContextReadClient.js';
@@ -6,7 +6,7 @@ export * from './ChatClient.js';
6
6
  export { MessagesClient } from './MessagesClient.js';
7
7
  export { ArtifactsClient,
8
8
  // agreement lanes are not chats — callers scope artifact writes with this.
9
- artifactScopeForSession, AGREEMENT_LANE_PREFIX, } from './ArtifactsClient.js';
9
+ artifactScopeForSession, AGREEMENT_LANE_PREFIX, ARTIFACT_INLINE_TEXT_MAX_CHARS, ARTIFACT_INLINE_TEXT_OVER_LIMIT_HINT, } from './ArtifactsClient.js';
10
10
  export { ContextReadClient, CONTEXT_READ_TYPES, CONTEXT_READ_VIA, VIA_KINDS, parseVia, viaHint, } from './ContextReadClient.js';
11
11
  export { ContextDiscoveryClient } from './ContextDiscoveryClient.js';
12
12
  export { GrantsClient } from './GrantsClient.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ziggs-ai/api-client",
3
- "version": "0.10.0",
3
+ "version": "0.10.2",
4
4
  "description": "HTTP and WebSocket client for the Ziggs backend API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",