@ziggs-ai/api-client 0.38.2 → 0.39.0
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/CHANGELOG.md +21 -0
- package/README.md +14 -36
- package/dist/capabilities/agreementVerbs.js +13 -21
- package/dist/capabilities/agreements.d.ts +2 -3
- package/dist/capabilities/agreements.js +7 -42
- package/dist/capabilities/artifacts.js +41 -43
- package/dist/capabilities/connections.js +10 -7
- package/dist/capabilities/context.d.ts +1 -1
- package/dist/capabilities/context.js +41 -45
- package/dist/capabilities/discovery.js +5 -5
- package/dist/capabilities/everyday.d.ts +1 -1
- package/dist/capabilities/everyday.js +1 -2
- package/dist/capabilities/index.d.ts +1 -2
- package/dist/capabilities/index.js +1 -2
- package/dist/capabilities/introductions.d.ts +1 -1
- package/dist/capabilities/introductions.js +3 -11
- package/dist/capabilities/links.d.ts +1 -9
- package/dist/capabilities/links.js +7 -28
- package/dist/capabilities/mcpBriefs.js +22 -28
- package/dist/capabilities/messaging.d.ts +9 -0
- package/dist/capabilities/messaging.js +11 -0
- package/dist/capabilities/nextCall.js +8 -17
- package/dist/capabilities/sources.d.ts +2 -3
- package/dist/capabilities/sources.js +1 -2
- package/dist/capabilities/tasks.js +1 -1
- package/dist/capabilities/toolLane.js +2 -2
- package/dist/engagementGuide.d.ts +2 -2
- package/dist/engagementGuide.js +6 -6
- package/dist/handles.d.ts +1 -1
- package/dist/handles.js +2 -2
- package/dist/http/AgreementClient.d.ts +17 -64
- package/dist/http/AgreementClient.js +14 -65
- package/dist/http/ArtifactsClient.d.ts +17 -19
- package/dist/http/ArtifactsClient.js +30 -62
- package/dist/http/ConnectionsClient.d.ts +6 -7
- package/dist/http/ConnectionsClient.js +5 -3
- package/dist/http/ContextDiscoveryClient.d.ts +2 -2
- package/dist/http/ContextGrantsClient.d.ts +8 -15
- package/dist/http/ContextGrantsClient.js +3 -5
- package/dist/http/ContextOpenClient.d.ts +5 -6
- package/dist/http/ContextOpenClient.js +11 -4
- package/dist/http/ContextReadClient.d.ts +23 -27
- package/dist/http/ContextReadClient.js +24 -23
- package/dist/http/ExecutionContextClient.d.ts +5 -1
- package/dist/http/ExecutionContextClient.js +48 -1
- package/dist/http/IntroductionsClient.d.ts +6 -4
- package/dist/http/MessageRecordsClient.d.ts +36 -0
- package/dist/http/MessageRecordsClient.js +30 -0
- package/dist/http/SessionsClient.d.ts +5 -5
- package/dist/http/SessionsClient.js +6 -6
- package/dist/http/TaskClient.d.ts +11 -5
- package/dist/http/TaskClient.js +11 -34
- package/dist/http/agreementFlows.d.ts +2 -3
- package/dist/http/agreementFlows.js +5 -5
- package/dist/http/index.d.ts +3 -5
- package/dist/http/index.js +3 -4
- package/dist/http/operatorHeaders.d.ts +3 -0
- package/dist/http/operatorHeaders.js +14 -5
- package/dist/inboxAck.d.ts +2 -2
- package/dist/inboxAck.js +2 -2
- package/dist/index.d.ts +4 -4
- package/dist/index.js +2 -2
- package/dist/types.d.ts +15 -55
- package/package.json +2 -2
- package/dist/capabilities/chat.d.ts +0 -43
- package/dist/capabilities/chat.js +0 -196
- package/dist/capabilities/chatListFields.d.ts +0 -14
- package/dist/capabilities/chatListFields.js +0 -61
- package/dist/http/ChatClient.d.ts +0 -187
- package/dist/http/ChatClient.js +0 -219
- package/dist/http/MessagesClient.d.ts +0 -40
- package/dist/http/MessagesClient.js +0 -81
|
@@ -4,7 +4,6 @@ import { pollSurfaceError } from '../shared/rateLimit.js';
|
|
|
4
4
|
import { buildOperatorHeaders } from './operatorHeaders.js';
|
|
5
5
|
export const CONTEXT_OPEN_KINDS = [
|
|
6
6
|
'artifact',
|
|
7
|
-
'chat',
|
|
8
7
|
'task',
|
|
9
8
|
'agreement',
|
|
10
9
|
];
|
|
@@ -43,21 +42,29 @@ export class ContextOpenClient {
|
|
|
43
42
|
if (!res.ok) {
|
|
44
43
|
throw pollSurfaceError(`ContextOpenClient ${path}`, res, text);
|
|
45
44
|
}
|
|
46
|
-
|
|
45
|
+
const result = JSON.parse(text);
|
|
46
|
+
if (result?.ref?.kind === 'address') {
|
|
47
|
+
throw Object.assign(new Error('Room references are retired; select an exact artifact, task or agreement'), { code: 'INVALID_INPUT' });
|
|
48
|
+
}
|
|
49
|
+
return result;
|
|
47
50
|
}
|
|
48
51
|
}
|
|
49
52
|
export function normalizeContextOpenBody(args) {
|
|
50
53
|
// A mistyped handle is refused here, before any call: its check letter
|
|
51
54
|
// fails. Any other id goes to the server, which knows older shapes.
|
|
52
55
|
const invalid = (message) => Object.assign(new Error(message), { code: 'INVALID_INPUT' });
|
|
53
|
-
|
|
56
|
+
if ('addressId' in args)
|
|
57
|
+
throw invalid('addressId is retired; select an exact artifact, task or agreement');
|
|
58
|
+
const keys = ['ref', 'artifactId', 'taskId', 'agreementId'].filter(key => args[key] !== undefined);
|
|
54
59
|
if (keys.length !== 1)
|
|
55
|
-
throw invalid('Pass exactly one resource reference: ref, artifactId,
|
|
60
|
+
throw invalid('Pass exactly one resource reference: ref, artifactId, taskId, or agreementId');
|
|
56
61
|
const key = keys[0];
|
|
57
62
|
const raw = typeof args[key] === 'string' ? args[key].trim() : '';
|
|
58
63
|
if (!raw)
|
|
59
64
|
throw invalid(`${key} must be a non-empty resource reference`);
|
|
60
65
|
const ref = parseRef(raw);
|
|
66
|
+
if (ref?.kind === 'address' || /^(?:address|chat):/.test(raw))
|
|
67
|
+
throw invalid('Room references are retired; select an exact artifact, task or agreement');
|
|
61
68
|
if (!ref && looksLikeHandle(raw))
|
|
62
69
|
throw invalid(`${raw} is not a handle: its check letter does not match. Check it for a typo`);
|
|
63
70
|
const body = { [key]: ref?.id ?? raw };
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
export declare const CONTEXT_READ_TYPES: readonly ["
|
|
1
|
+
export declare const CONTEXT_READ_TYPES: readonly ["artifacts", "agreements", "tasks"];
|
|
2
2
|
export type ContextReadType = (typeof CONTEXT_READ_TYPES)[number];
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Retained resource entry points for context reads and snapshots.
|
|
5
|
+
* The server still accepts legacy room entries until coordinated removal;
|
|
6
|
+
* new callers use only the domain resources below.
|
|
7
7
|
*/
|
|
8
|
-
export declare const VIA_KINDS: readonly ["
|
|
8
|
+
export declare const VIA_KINDS: readonly ["agreement", "task", "artifact", "message"];
|
|
9
9
|
export type ViaKind = (typeof VIA_KINDS)[number];
|
|
10
10
|
/**
|
|
11
|
-
* Which entry points each read type actually accepts,
|
|
11
|
+
* Which entry points each read type actually accepts, using the retained subset of the server's
|
|
12
12
|
* `CONTEXT_READ_VIA`. Stated once here so the tool descriptions, the argument
|
|
13
13
|
* check, and this type all read off the same list instead of three prose
|
|
14
14
|
* copies that drift — and so a wrong pairing is refused with the reason rather
|
|
15
15
|
* than as a bare 400 from a round-trip away.
|
|
16
16
|
*/
|
|
17
17
|
export declare const CONTEXT_READ_VIA: Record<ContextReadType, readonly ViaKind[]>;
|
|
18
|
-
/** `
|
|
18
|
+
/** `agreement:<id>, task:<id>` — the accepted entries for one read type, for humans. */
|
|
19
19
|
export declare function viaHint(type: ContextReadType): string;
|
|
20
|
-
/** Split `
|
|
20
|
+
/** Split `agreement:abc` into its parts, or null when it is not a `via` at all. */
|
|
21
21
|
export declare function parseVia(via: string): {
|
|
22
22
|
kind: ViaKind;
|
|
23
23
|
id: string;
|
|
@@ -43,7 +43,7 @@ export interface ContextReadEnvelope<T = unknown> {
|
|
|
43
43
|
nextCursor: string | null;
|
|
44
44
|
latestSequence?: string | null;
|
|
45
45
|
}
|
|
46
|
-
/**
|
|
46
|
+
/** Authorized resource snapshot from `GET /context/snapshot`. */
|
|
47
47
|
/**
|
|
48
48
|
* One participant on the snapshot roster. `kind` is the declared holder kind the
|
|
49
49
|
* server stamped on the row, so a reader takes what a participant IS from the
|
|
@@ -56,6 +56,14 @@ export interface ContextSnapshotParticipant {
|
|
|
56
56
|
isYou?: boolean;
|
|
57
57
|
[key: string]: unknown;
|
|
58
58
|
}
|
|
59
|
+
export interface ContextSnapshotQuery {
|
|
60
|
+
/** Exact message, task, agreement, or artifact entry; no implicit room. */
|
|
61
|
+
via: string;
|
|
62
|
+
agreementId?: string;
|
|
63
|
+
artifactId?: string;
|
|
64
|
+
maxMessages?: number;
|
|
65
|
+
contextGrantId?: string;
|
|
66
|
+
}
|
|
59
67
|
export interface ContextSnapshotResult {
|
|
60
68
|
/** Server-resolved orientation; writes recheck authority. */
|
|
61
69
|
workBrief?: Record<string, unknown> & {
|
|
@@ -64,14 +72,14 @@ export interface ContextSnapshotResult {
|
|
|
64
72
|
work?: Record<string, unknown>;
|
|
65
73
|
history: unknown[];
|
|
66
74
|
agreements: unknown[];
|
|
67
|
-
/** Bounded
|
|
75
|
+
/** Bounded authorized artifact inventory, without bodies. */
|
|
68
76
|
artifacts?: ContextReadEnvelope<{
|
|
69
77
|
artifactId: string;
|
|
70
78
|
name: string | null;
|
|
71
79
|
contentType: string;
|
|
72
80
|
timestamp: string | null;
|
|
73
81
|
}>;
|
|
74
|
-
/** Direct artifact grants
|
|
82
|
+
/** Direct artifact grants outside the selected work inventory. Id and name only. */
|
|
75
83
|
grantedArtifacts?: {
|
|
76
84
|
items: Array<{
|
|
77
85
|
artifactId: string;
|
|
@@ -87,11 +95,10 @@ export interface ContextSnapshotResult {
|
|
|
87
95
|
userId: string;
|
|
88
96
|
}>;
|
|
89
97
|
latestSequence?: string | null;
|
|
90
|
-
chatMissing?: boolean;
|
|
91
98
|
}
|
|
92
99
|
/**
|
|
93
100
|
* Protocol-first uniform context reads.
|
|
94
|
-
* Wraps `GET /context/read/:type` — one client, one envelope,
|
|
101
|
+
* Wraps `GET /context/read/:type` — one client, one envelope, three types.
|
|
95
102
|
*/
|
|
96
103
|
export declare class ContextReadClient {
|
|
97
104
|
private readonly operatorKey;
|
|
@@ -100,18 +107,7 @@ export declare class ContextReadClient {
|
|
|
100
107
|
/** Authority is supplied by withWorkExecution / withWorkExecutionId. */
|
|
101
108
|
constructor(operatorKey: string, agentId?: string, baseUrl?: string);
|
|
102
109
|
read<T = unknown>(type: ContextReadType, query: ContextReadQuery): Promise<ContextReadEnvelope<T>>;
|
|
103
|
-
/**
|
|
104
|
-
*
|
|
105
|
-
|
|
106
|
-
* readers as {@link read}, so hosted agents pull one fenced view per poll cycle.
|
|
107
|
-
* Rejects with an `Error & { status }` on non-OK so callers can branch (404).
|
|
108
|
-
*/
|
|
109
|
-
snapshot(chatId: string, opts?: {
|
|
110
|
-
maxMessages?: number;
|
|
111
|
-
contextGrantId?: string;
|
|
112
|
-
agreementId?: string;
|
|
113
|
-
taskId?: string;
|
|
114
|
-
artifactId?: string;
|
|
115
|
-
via?: string;
|
|
116
|
-
}): Promise<ContextSnapshotResult>;
|
|
110
|
+
/** A snapshot selects one domain resource; the current execution headers
|
|
111
|
+
* remain authoritative and are never cleared after a refusal. */
|
|
112
|
+
snapshot(opts: ContextSnapshotQuery): Promise<ContextSnapshotResult>;
|
|
117
113
|
}
|
|
@@ -2,41 +2,38 @@ import { getBackendUrl } from '../utils/urlUtils.js';
|
|
|
2
2
|
import { pollSurfaceError } from '../shared/rateLimit.js';
|
|
3
3
|
import { buildOperatorHeaders } from './operatorHeaders.js';
|
|
4
4
|
export const CONTEXT_READ_TYPES = [
|
|
5
|
-
'messages',
|
|
6
5
|
'artifacts',
|
|
7
6
|
'agreements',
|
|
8
7
|
'tasks',
|
|
9
8
|
];
|
|
10
9
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* Retained resource entry points for context reads and snapshots.
|
|
11
|
+
* The server still accepts legacy room entries until coordinated removal;
|
|
12
|
+
* new callers use only the domain resources below.
|
|
14
13
|
*/
|
|
15
14
|
export const VIA_KINDS = [
|
|
16
|
-
'chat',
|
|
17
15
|
'agreement',
|
|
18
16
|
'task',
|
|
19
|
-
'counterparty',
|
|
20
17
|
'artifact',
|
|
18
|
+
'message',
|
|
21
19
|
];
|
|
22
20
|
/**
|
|
23
|
-
* Which entry points each read type actually accepts,
|
|
21
|
+
* Which entry points each read type actually accepts, using the retained subset of the server's
|
|
24
22
|
* `CONTEXT_READ_VIA`. Stated once here so the tool descriptions, the argument
|
|
25
23
|
* check, and this type all read off the same list instead of three prose
|
|
26
24
|
* copies that drift — and so a wrong pairing is refused with the reason rather
|
|
27
25
|
* than as a bare 400 from a round-trip away.
|
|
28
26
|
*/
|
|
29
27
|
export const CONTEXT_READ_VIA = {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
agreements: ['chat', 'agreement'],
|
|
28
|
+
artifacts: ['agreement', 'task', 'artifact'],
|
|
29
|
+
agreements: ['agreement'],
|
|
33
30
|
tasks: ['agreement', 'task'],
|
|
34
31
|
};
|
|
35
|
-
/** `
|
|
32
|
+
/** `agreement:<id>, task:<id>` — the accepted entries for one read type, for humans. */
|
|
36
33
|
export function viaHint(type) {
|
|
37
34
|
return CONTEXT_READ_VIA[type].map((k) => `${k}:<id>`).join(', ');
|
|
38
35
|
}
|
|
39
|
-
/** Split `
|
|
36
|
+
/** Split `agreement:abc` into its parts, or null when it is not a `via` at all. */
|
|
40
37
|
export function parseVia(via) {
|
|
41
38
|
const at = via.indexOf(':');
|
|
42
39
|
if (at <= 0)
|
|
@@ -51,7 +48,7 @@ export function parseVia(via) {
|
|
|
51
48
|
}
|
|
52
49
|
/**
|
|
53
50
|
* Protocol-first uniform context reads.
|
|
54
|
-
* Wraps `GET /context/read/:type` — one client, one envelope,
|
|
51
|
+
* Wraps `GET /context/read/:type` — one client, one envelope, three types.
|
|
55
52
|
*/
|
|
56
53
|
export class ContextReadClient {
|
|
57
54
|
operatorKey;
|
|
@@ -69,6 +66,10 @@ export class ContextReadClient {
|
|
|
69
66
|
if (!query.via?.trim()) {
|
|
70
67
|
throw new Error('ContextReadClient.read: via is required');
|
|
71
68
|
}
|
|
69
|
+
const parsed = parseVia(query.via.trim());
|
|
70
|
+
if (!parsed || !CONTEXT_READ_VIA[type]?.includes(parsed.kind)) {
|
|
71
|
+
throw new Error(`ContextReadClient.read: ${type} reads accept via=${CONTEXT_READ_VIA[type] ? viaHint(type) : 'a retained resource'}`);
|
|
72
|
+
}
|
|
72
73
|
const url = new URL(`${this.baseUrl}/context/read/${encodeURIComponent(type)}`);
|
|
73
74
|
url.searchParams.set('via', query.via.trim());
|
|
74
75
|
if (query.agreementId)
|
|
@@ -101,18 +102,18 @@ export class ContextReadClient {
|
|
|
101
102
|
}
|
|
102
103
|
return JSON.parse(body);
|
|
103
104
|
}
|
|
104
|
-
/**
|
|
105
|
-
*
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
if (
|
|
112
|
-
throw new Error('ContextReadClient.snapshot:
|
|
105
|
+
/** A snapshot selects one domain resource; the current execution headers
|
|
106
|
+
* remain authoritative and are never cleared after a refusal. */
|
|
107
|
+
async snapshot(opts) {
|
|
108
|
+
const via = opts?.via?.trim();
|
|
109
|
+
if (!via || !parseVia(via)) {
|
|
110
|
+
throw new Error('ContextReadClient.snapshot: an explicit resource is required (message:<id>, task:<id>, agreement:<id>, or artifact:<id>)');
|
|
111
|
+
}
|
|
112
|
+
if (parseVia(via)?.kind === 'message' && (opts.agreementId || opts.artifactId)) {
|
|
113
|
+
throw new Error('ContextReadClient.snapshot: select an exact message independently of other resources');
|
|
113
114
|
}
|
|
114
115
|
const url = new URL(`${this.baseUrl}/context/snapshot`);
|
|
115
|
-
url.searchParams.set('via',
|
|
116
|
+
url.searchParams.set('via', via);
|
|
116
117
|
if (opts.agreementId)
|
|
117
118
|
url.searchParams.set('agreementId', opts.agreementId);
|
|
118
119
|
if (opts.artifactId)
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
-
import type { WorkExecutionContext } from '@ziggs-ai/contracts';
|
|
1
|
+
import type { ActionRef, WorkExecutionContext } from '@ziggs-ai/contracts';
|
|
2
2
|
/** Fail closed before resuming model state, including timers and recorded wakes. */
|
|
3
3
|
export declare function validateWorkExecution(context: WorkExecutionContext, operatorKey: string, baseUrl?: string): Promise<void>;
|
|
4
|
+
/** Explicit admission, outside an existing execution. Never retries a refused context. */
|
|
5
|
+
export declare function prepareWorkExecution(action: ActionRef, operatorKey: string, agentId: string, baseUrl?: string): Promise<WorkExecutionContext>;
|
|
6
|
+
/** The host holds a fenced lease while it runs; release is not domain completion. */
|
|
7
|
+
export declare function runWorkExecution<T>(context: WorkExecutionContext, operatorKey: string, run: (context: WorkExecutionContext) => Promise<T>, baseUrl?: string): Promise<T>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
2
|
-
import { buildOperatorHeaders, withWorkExecution } from './operatorHeaders.js';
|
|
2
|
+
import { buildOperatorHeaders, withWorkExecution, withoutExecution } from './operatorHeaders.js';
|
|
3
3
|
import { throwApiError } from '../shared/apiError.js';
|
|
4
4
|
/** Fail closed before resuming model state, including timers and recorded wakes. */
|
|
5
5
|
export async function validateWorkExecution(context, operatorKey, baseUrl = getBackendUrl()) {
|
|
@@ -15,3 +15,50 @@ export async function validateWorkExecution(context, operatorKey, baseUrl = getB
|
|
|
15
15
|
throw new Error('Execution preflight returned a different context');
|
|
16
16
|
});
|
|
17
17
|
}
|
|
18
|
+
/** Explicit admission, outside an existing execution. Never retries a refused context. */
|
|
19
|
+
export async function prepareWorkExecution(action, operatorKey, agentId, baseUrl = getBackendUrl()) {
|
|
20
|
+
const response = await fetch(`${baseUrl}/execution/prepare`, {
|
|
21
|
+
method: 'POST', headers: { ...buildOperatorHeaders(operatorKey, agentId), 'content-type': 'application/json' },
|
|
22
|
+
body: JSON.stringify(action),
|
|
23
|
+
});
|
|
24
|
+
const text = await response.text();
|
|
25
|
+
if (!response.ok)
|
|
26
|
+
throwApiError(response, text, 'Action execution is unavailable');
|
|
27
|
+
const context = JSON.parse(text);
|
|
28
|
+
if (!context.contextId || context.actorId !== agentId)
|
|
29
|
+
throw new Error('Invalid action execution response');
|
|
30
|
+
return context;
|
|
31
|
+
}
|
|
32
|
+
/** The host holds a fenced lease while it runs; release is not domain completion. */
|
|
33
|
+
export async function runWorkExecution(context, operatorKey, run, baseUrl = getBackendUrl()) {
|
|
34
|
+
const call = async (body, selected = context) => withWorkExecution(selected, async () => {
|
|
35
|
+
const finish = body.operation === 'finish';
|
|
36
|
+
const headers = buildOperatorHeaders(operatorKey, context.actorId);
|
|
37
|
+
const response = await fetch(`${baseUrl}/execution/attempt`, { method: 'POST',
|
|
38
|
+
headers: { ...(finish ? withoutExecution(headers) : headers), 'content-type': 'application/json' },
|
|
39
|
+
body: JSON.stringify(finish ? { ...body, contextId: selected.contextId, attemptId: selected.attemptId } : body) });
|
|
40
|
+
const text = await response.text();
|
|
41
|
+
if (!response.ok)
|
|
42
|
+
throwApiError(response, text, 'Execution attempt refused');
|
|
43
|
+
return JSON.parse(text);
|
|
44
|
+
});
|
|
45
|
+
const attempt = await call({ operation: 'claim' });
|
|
46
|
+
if (attempt.contextId !== context.contextId || !attempt.attemptId)
|
|
47
|
+
throw new Error('Invalid execution attempt response');
|
|
48
|
+
let lost;
|
|
49
|
+
const timer = setInterval(() => { void call({ operation: 'renew' }, attempt).catch(error => { lost = error; }); }, 20_000);
|
|
50
|
+
timer.unref();
|
|
51
|
+
try {
|
|
52
|
+
const result = await withWorkExecution(attempt, () => run(attempt));
|
|
53
|
+
if (lost)
|
|
54
|
+
throw lost;
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
clearInterval(timer);
|
|
59
|
+
// A stale worker never releases its replacement. Failure here keeps durable
|
|
60
|
+
// inputs pending; it must not transform an execution refusal into a retry
|
|
61
|
+
// without its selected authority.
|
|
62
|
+
await call({ operation: 'finish' }, attempt);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -6,8 +6,10 @@ export type IntroductionStatus = 'open' | 'redeemed' | 'declined' | 'revoked' |
|
|
|
6
6
|
* (your own token, a teammate's, or someone you are already linked to).
|
|
7
7
|
* `claimed` / `bid` / `accepted`: the listing, request or proposal it names.
|
|
8
8
|
* `link_pending`: a link both people still approve.
|
|
9
|
+
* `held`: an agent with no person around holds it as a guest until a person
|
|
10
|
+
* claims that agent; the claim finishes the redeem as them.
|
|
9
11
|
*/
|
|
10
|
-
export type IntroductionOutcome = 'joined' | 'claimed' | 'bid' | 'accepted' | 'link_pending';
|
|
12
|
+
export type IntroductionOutcome = 'joined' | 'claimed' | 'bid' | 'accepted' | 'link_pending' | 'held';
|
|
11
13
|
export interface IntroductionFrom {
|
|
12
14
|
orgId: string;
|
|
13
15
|
agentId: string | null;
|
|
@@ -34,7 +36,7 @@ export interface IntroductionView {
|
|
|
34
36
|
note: string | null;
|
|
35
37
|
/** What the token is about. The public view gives kind and title only. */
|
|
36
38
|
about?: {
|
|
37
|
-
kind: 'agreement' | 'task' | '
|
|
39
|
+
kind: 'agreement' | 'task' | 'address' | 'workspace';
|
|
38
40
|
id?: string;
|
|
39
41
|
title: string | null;
|
|
40
42
|
shape: 'listing' | 'request' | 'proposal' | null;
|
|
@@ -60,7 +62,7 @@ export interface IntroductionView {
|
|
|
60
62
|
at: string;
|
|
61
63
|
}>;
|
|
62
64
|
/** Returned to a redeemer only: the room its agent is now in. */
|
|
63
|
-
|
|
65
|
+
addressId?: string | null;
|
|
64
66
|
expiresAt: string;
|
|
65
67
|
/** The public page the string points at. This is what you hand over. */
|
|
66
68
|
url: string;
|
|
@@ -89,7 +91,7 @@ export interface MintIntroductionInput {
|
|
|
89
91
|
note?: string;
|
|
90
92
|
/** What the token is about: a listing, request or proposal (agreement), a task, or a chat you hold. */
|
|
91
93
|
about?: {
|
|
92
|
-
kind: 'agreement' | 'task' | '
|
|
94
|
+
kind: 'agreement' | 'task' | 'address' | 'workspace';
|
|
93
95
|
id: string;
|
|
94
96
|
};
|
|
95
97
|
/** How many people it serves, 1 to 25. Your own sessions never use one up. */
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { MessageCommand, MessageCommandResult, MessageRecord, MessagingControls } from '@ziggs-ai/contracts';
|
|
2
|
+
export interface MessageQuery {
|
|
3
|
+
q?: string;
|
|
4
|
+
senderId?: string;
|
|
5
|
+
recipientIds?: string[];
|
|
6
|
+
agreementId?: string;
|
|
7
|
+
taskId?: string;
|
|
8
|
+
participantIds?: string[];
|
|
9
|
+
before?: string;
|
|
10
|
+
limit?: number;
|
|
11
|
+
}
|
|
12
|
+
export interface MessagePage {
|
|
13
|
+
messages: MessageRecord[];
|
|
14
|
+
nextCursor: string | null;
|
|
15
|
+
}
|
|
16
|
+
/** One command and one representation, independent of navigation. */
|
|
17
|
+
export declare class MessageRecordsClient {
|
|
18
|
+
private readonly operatorKey;
|
|
19
|
+
private readonly agentId;
|
|
20
|
+
private readonly baseUrl?;
|
|
21
|
+
constructor(operatorKey: string, agentId: string, baseUrl?: string | undefined);
|
|
22
|
+
private request;
|
|
23
|
+
get(id: string): Promise<MessageRecord>;
|
|
24
|
+
send(command: MessageCommand): Promise<MessageCommandResult>;
|
|
25
|
+
list(input?: MessageQuery): Promise<MessagePage>;
|
|
26
|
+
replies(id: string, input?: {
|
|
27
|
+
before?: string;
|
|
28
|
+
limit?: number;
|
|
29
|
+
}): Promise<MessagePage>;
|
|
30
|
+
controls(agreementId: string, taskId?: string): Promise<MessagingControls>;
|
|
31
|
+
answers(id: string): Promise<unknown>;
|
|
32
|
+
cancelAnswer(id: string, requestId: string): Promise<{
|
|
33
|
+
state: 'cancelled';
|
|
34
|
+
}>;
|
|
35
|
+
}
|
|
36
|
+
export type { MessageCommand, MessageCommandResult, MessageRecord } from '@ziggs-ai/contracts';
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { parseMessageCommand } from '@ziggs-ai/contracts';
|
|
2
|
+
import { buildOperatorHeaders } from './operatorHeaders.js';
|
|
3
|
+
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
4
|
+
import { throwApiError } from '../shared/apiError.js';
|
|
5
|
+
/** One command and one representation, independent of navigation. */
|
|
6
|
+
export class MessageRecordsClient {
|
|
7
|
+
operatorKey;
|
|
8
|
+
agentId;
|
|
9
|
+
baseUrl;
|
|
10
|
+
constructor(operatorKey, agentId, baseUrl) {
|
|
11
|
+
this.operatorKey = operatorKey;
|
|
12
|
+
this.agentId = agentId;
|
|
13
|
+
this.baseUrl = baseUrl;
|
|
14
|
+
}
|
|
15
|
+
async request(path, init = {}) { const res = await fetch(`${this.baseUrl ?? getBackendUrl()}/messaging/${path}`, { ...init, headers: { 'content-type': 'application/json', ...buildOperatorHeaders(this.operatorKey, this.agentId) } }); if (!res.ok)
|
|
16
|
+
throwApiError(res, await res.text().catch(() => ''), `Messaging failed: ${res.status}`); return await res.json(); }
|
|
17
|
+
async get(id) { const row = await this.request(`messages/${encodeURIComponent(id)}`); if (row.messageId !== id || !row.content || !Array.isArray(row.requestedAnswers))
|
|
18
|
+
throw new Error('Invalid exact message response'); return row; }
|
|
19
|
+
async send(command) { const result = await this.request('messages', { method: 'POST', body: JSON.stringify(parseMessageCommand(command)) }); if (!['created', 'existing'].includes(result.outcome) || !result.message?.messageId || !result.message.content || !Array.isArray(result.message.recipients) || !Array.isArray(result.message.requestedAnswers))
|
|
20
|
+
throw new Error('Invalid message command response'); return result; }
|
|
21
|
+
list(input = {}) { const query = new URLSearchParams(); for (const [key, value] of Object.entries(input))
|
|
22
|
+
if (value !== undefined)
|
|
23
|
+
query.set(key, Array.isArray(value) ? value.join(',') : String(value)); return this.request(`messages?${query}`); }
|
|
24
|
+
replies(id, input = {}) { const query = new URLSearchParams(); for (const [key, value] of Object.entries(input))
|
|
25
|
+
if (value !== undefined)
|
|
26
|
+
query.set(key, String(value)); return this.request(`messages/${encodeURIComponent(id)}/replies?${query}`); }
|
|
27
|
+
controls(agreementId, taskId) { const query = new URLSearchParams({ agreementId, ...(taskId ? { taskId } : {}) }); return this.request(`controls?${query}`); }
|
|
28
|
+
answers(id) { return this.request(`messages/${encodeURIComponent(id)}/answers`); }
|
|
29
|
+
cancelAnswer(id, requestId) { return this.request(`messages/${encodeURIComponent(id)}/answers/${encodeURIComponent(requestId)}/cancel`, { method: 'POST' }); }
|
|
30
|
+
}
|
|
@@ -8,11 +8,11 @@ export interface AssistantSessionRow {
|
|
|
8
8
|
lastSeenAt: string;
|
|
9
9
|
expiresAt: string;
|
|
10
10
|
workspaceIds: string[];
|
|
11
|
-
|
|
11
|
+
heldExecutionIds: string[];
|
|
12
12
|
awake: boolean;
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
|
-
* List sibling windows and hold
|
|
15
|
+
* List sibling windows and hold an execution in this one.
|
|
16
16
|
* Uses the same session headers as the inbox so "this window" is the
|
|
17
17
|
* X-Ziggs-Session the host already sends.
|
|
18
18
|
*/
|
|
@@ -23,12 +23,12 @@ export declare class SessionsClient {
|
|
|
23
23
|
private url;
|
|
24
24
|
list(): Promise<AssistantSessionRow[]>;
|
|
25
25
|
/**
|
|
26
|
-
* Hold `
|
|
26
|
+
* Hold `executionContextId` in this window (omit sessionId; server resolves from
|
|
27
27
|
* X-Ziggs-Session), move it to `sessionId`, or pass null to release.
|
|
28
28
|
*/
|
|
29
|
-
hold(
|
|
29
|
+
hold(executionContextId: string, sessionId?: string | null): Promise<{
|
|
30
30
|
success: true;
|
|
31
|
-
|
|
31
|
+
executionContextId: string;
|
|
32
32
|
sessionId: string | null;
|
|
33
33
|
}>;
|
|
34
34
|
}
|
|
@@ -2,7 +2,7 @@ import { getBackendUrl } from '../utils/urlUtils.js';
|
|
|
2
2
|
import { pollSurfaceError } from '../shared/rateLimit.js';
|
|
3
3
|
import { buildCredsHeaders } from './operatorHeaders.js';
|
|
4
4
|
/**
|
|
5
|
-
* List sibling windows and hold
|
|
5
|
+
* List sibling windows and hold an execution in this one.
|
|
6
6
|
* Uses the same session headers as the inbox so "this window" is the
|
|
7
7
|
* X-Ziggs-Session the host already sends.
|
|
8
8
|
*/
|
|
@@ -48,14 +48,14 @@ export class SessionsClient {
|
|
|
48
48
|
return Array.isArray(rows) ? rows : [];
|
|
49
49
|
}
|
|
50
50
|
/**
|
|
51
|
-
* Hold `
|
|
51
|
+
* Hold `executionContextId` in this window (omit sessionId; server resolves from
|
|
52
52
|
* X-Ziggs-Session), move it to `sessionId`, or pass null to release.
|
|
53
53
|
*/
|
|
54
|
-
async hold(
|
|
55
|
-
if (!
|
|
56
|
-
throw new Error('SessionsClient.hold:
|
|
54
|
+
async hold(executionContextId, sessionId) {
|
|
55
|
+
if (!executionContextId?.trim())
|
|
56
|
+
throw new Error('SessionsClient.hold: executionContextId is required');
|
|
57
57
|
const body = {
|
|
58
|
-
|
|
58
|
+
executionContextId: executionContextId.trim(),
|
|
59
59
|
};
|
|
60
60
|
if (sessionId !== undefined)
|
|
61
61
|
body.sessionId = sessionId;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { type TaskUpdateState } from '@ziggs-ai/contracts';
|
|
2
|
+
export type { TaskUpdateState } from '@ziggs-ai/contracts';
|
|
1
3
|
import type { WriteSource } from '../capabilities/sources.js';
|
|
2
4
|
import { type Creds, type Task, type TaskState } from '../types.js';
|
|
3
5
|
/**
|
|
@@ -5,6 +7,8 @@ import { type Creds, type Task, type TaskState } from '../types.js';
|
|
|
5
7
|
* getTask / ziggs_task_get.
|
|
6
8
|
*/
|
|
7
9
|
export interface TaskWriteConfirm {
|
|
10
|
+
success: true;
|
|
11
|
+
/** @deprecated Use success. */
|
|
8
12
|
ok: true;
|
|
9
13
|
taskId: string;
|
|
10
14
|
/** The agreement the task hangs under, when the server reports it. */
|
|
@@ -152,9 +156,8 @@ export interface UpdateTaskStateData {
|
|
|
152
156
|
/** What the result drew on. Recorded only when a result is written. */
|
|
153
157
|
sources?: WriteSource[];
|
|
154
158
|
}
|
|
155
|
-
export declare function updateTaskState(taskId: string, state:
|
|
159
|
+
export declare function updateTaskState(taskId: string, state: TaskUpdateState, data: UpdateTaskStateData | undefined, creds: Creds): Promise<TaskWriteConfirm>;
|
|
156
160
|
export declare function getActiveTasksForAgent(agentId: string, creds: Creds): Promise<Task[]>;
|
|
157
|
-
export declare function getActiveTasksForChat(chatId: string, creds: Creds): Promise<Task[]>;
|
|
158
161
|
export declare function cancelTask(taskId: string, creds: Creds): Promise<TaskWriteConfirm>;
|
|
159
162
|
export declare function getSubtasks(parentTaskId: string, creds: Creds): Promise<Task[]>;
|
|
160
163
|
export interface PlanReplaceStep {
|
|
@@ -205,6 +208,8 @@ export interface ListTasksOptions {
|
|
|
205
208
|
limit?: number;
|
|
206
209
|
/** Filter to tasks assigned to this id. */
|
|
207
210
|
assignedTo?: string;
|
|
211
|
+
/** Stored assignee, including tasks held by dependencies. */
|
|
212
|
+
assigneeId?: string;
|
|
208
213
|
/**
|
|
209
214
|
* Filter to tasks CREATED by this id — what the caller handed out, as
|
|
210
215
|
* opposed to what was handed to it.
|
|
@@ -213,12 +218,14 @@ export interface ListTasksOptions {
|
|
|
213
218
|
}
|
|
214
219
|
export interface ListTasksResult {
|
|
215
220
|
tasks: Task[];
|
|
221
|
+
/** Number of tasks on this page, not a total across pages. */
|
|
222
|
+
count: number;
|
|
216
223
|
hasMore: boolean;
|
|
217
224
|
nextCursor?: string | null;
|
|
218
225
|
}
|
|
219
226
|
export declare function listTasks(options: ListTasksOptions | undefined, creds: Creds): Promise<ListTasksResult>;
|
|
220
227
|
export interface CountTasksFilters {
|
|
221
|
-
|
|
228
|
+
agreementId?: string;
|
|
222
229
|
state?: string;
|
|
223
230
|
userId?: string;
|
|
224
231
|
agentId?: string;
|
|
@@ -237,9 +244,8 @@ export declare class TaskClient {
|
|
|
237
244
|
children?: CreatedTaskChild[];
|
|
238
245
|
}>;
|
|
239
246
|
getTask(taskId: string): Promise<Task>;
|
|
240
|
-
updateTaskState(taskId: string, state:
|
|
247
|
+
updateTaskState(taskId: string, state: TaskUpdateState, data?: UpdateTaskStateData): Promise<TaskWriteConfirm>;
|
|
241
248
|
getActiveTasksForAgent(agentId: string): Promise<Task[]>;
|
|
242
|
-
getActiveTasksForChat(chatId: string): Promise<Task[]>;
|
|
243
249
|
cancelTask(taskId: string): Promise<TaskWriteConfirm>;
|
|
244
250
|
getSubtasks(parentTaskId: string): Promise<Task[]>;
|
|
245
251
|
replaceTaskPlan(taskId: string, steps: PlanReplaceStep[]): Promise<TaskWriteConfirm>;
|
package/dist/http/TaskClient.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { TASK_UPDATE_STATES } from '@ziggs-ai/contracts';
|
|
1
2
|
import { decodeOperatorKeyClaims } from '../shared/operatorKey.js';
|
|
2
3
|
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
3
4
|
import { partyActorIds } from '../types.js';
|
|
@@ -59,6 +60,7 @@ function extractWriteConfirm(data) {
|
|
|
59
60
|
d['task'] === undefined &&
|
|
60
61
|
d['description'] === undefined) {
|
|
61
62
|
return {
|
|
63
|
+
success: true,
|
|
62
64
|
ok: true,
|
|
63
65
|
...(d['control'] ? { control: d['control'] } : {}),
|
|
64
66
|
taskId: d['taskId'],
|
|
@@ -86,6 +88,7 @@ function extractWriteConfirm(data) {
|
|
|
86
88
|
if (!task?.taskId || !task.state)
|
|
87
89
|
return null;
|
|
88
90
|
return {
|
|
91
|
+
success: true,
|
|
89
92
|
ok: true,
|
|
90
93
|
taskId: task.taskId,
|
|
91
94
|
state: task.state,
|
|
@@ -142,6 +145,9 @@ export async function updateTaskState(taskId, state, data = {}, creds) {
|
|
|
142
145
|
throw new Error('updateTaskState: taskId is required');
|
|
143
146
|
if (!state)
|
|
144
147
|
throw new Error('updateTaskState: state is required');
|
|
148
|
+
if (!TASK_UPDATE_STATES.includes(state)) {
|
|
149
|
+
throw new Error('updateTaskState accepts active, completed or failed; use cancelTask to cancel');
|
|
150
|
+
}
|
|
145
151
|
assertCreds(creds, 'task state update');
|
|
146
152
|
const res = await fetch(`${getTaskBaseUrl()}/${taskId}/state`, {
|
|
147
153
|
method: 'PATCH',
|
|
@@ -219,37 +225,6 @@ async function getActiveTasksForAgentViaPartyAgreements(agentId, creds) {
|
|
|
219
225
|
const merged = lists.flat();
|
|
220
226
|
return workable(merged.filter((t) => !t.assigneeId || t.assigneeId === agentId));
|
|
221
227
|
}
|
|
222
|
-
// Backend deliberately has no `/chats/:id/tasks` route — it's composable
|
|
223
|
-
// from links → per-agreement tasks. We do the composition here so callers
|
|
224
|
-
// don't have to.
|
|
225
|
-
export async function getActiveTasksForChat(chatId, creds) {
|
|
226
|
-
if (!chatId)
|
|
227
|
-
throw new Error('getActiveTasksForChat: chatId is required');
|
|
228
|
-
assertCreds(creds, 'getting active tasks for chat');
|
|
229
|
-
const linksRes = await fetch(`${getBackendUrl()}/agreements/by-chat/${encodeURIComponent(chatId)}`, { method: 'GET', headers: buildHeaders(creds) });
|
|
230
|
-
if (!linksRes.ok) {
|
|
231
|
-
const body = await linksRes.text().catch(() => '');
|
|
232
|
-
throwApiError(linksRes, body, `Get agreements for chat failed: ${linksRes.status} ${linksRes.statusText}`);
|
|
233
|
-
}
|
|
234
|
-
const linksData = await linksRes.json().catch(() => null);
|
|
235
|
-
const rawLinks = linksData?.['links'] ?? linksData?.['agreements'];
|
|
236
|
-
const agreements = Array.isArray(rawLinks) ? rawLinks : [];
|
|
237
|
-
const agreementIds = Array.from(new Set(agreements.map((a) => a['agreementId']).filter(Boolean)));
|
|
238
|
-
if (agreementIds.length === 0)
|
|
239
|
-
return [];
|
|
240
|
-
const lists = await Promise.all(agreementIds.map(async (aid) => {
|
|
241
|
-
const url = new URL(`${getBackendUrl()}/agreements/${encodeURIComponent(aid)}/tasks`);
|
|
242
|
-
url.searchParams.set('state', 'active');
|
|
243
|
-
const res = await fetch(url.toString(), { method: 'GET', headers: buildHeaders(creds) });
|
|
244
|
-
if (!res.ok) {
|
|
245
|
-
const body = await res.text().catch(() => '');
|
|
246
|
-
throwApiError(res, body, `Get tasks for agreement ${aid} failed: ${res.status}`);
|
|
247
|
-
}
|
|
248
|
-
const data = await res.json().catch(() => null);
|
|
249
|
-
return Array.isArray(data?.['tasks']) ? data['tasks'] : [];
|
|
250
|
-
}));
|
|
251
|
-
return lists.flat();
|
|
252
|
-
}
|
|
253
228
|
export async function cancelTask(taskId, creds) {
|
|
254
229
|
if (!taskId)
|
|
255
230
|
throw new Error('cancelTask: taskId is required');
|
|
@@ -426,6 +401,8 @@ export async function listTasks(options = {}, creds) {
|
|
|
426
401
|
url.searchParams.set('limit', String(options.limit));
|
|
427
402
|
if (options.assignedTo)
|
|
428
403
|
url.searchParams.set('assignedTo', options.assignedTo);
|
|
404
|
+
if (options.assigneeId)
|
|
405
|
+
url.searchParams.set('assigneeId', options.assigneeId);
|
|
429
406
|
if (options.createdBy)
|
|
430
407
|
url.searchParams.set('createdBy', options.createdBy);
|
|
431
408
|
const res = await fetch(url.toString(), {
|
|
@@ -440,6 +417,7 @@ export async function listTasks(options = {}, creds) {
|
|
|
440
417
|
const tasks = Array.isArray(data?.['tasks']) ? data['tasks'] : [];
|
|
441
418
|
return {
|
|
442
419
|
tasks,
|
|
420
|
+
count: tasks.length,
|
|
443
421
|
hasMore: Boolean(data?.['hasMore']),
|
|
444
422
|
nextCursor: data?.['nextCursor'] ?? null,
|
|
445
423
|
};
|
|
@@ -448,8 +426,8 @@ export async function countTasks(filters = {}, creds) {
|
|
|
448
426
|
assertCreds(creds, 'count tasks');
|
|
449
427
|
// Canonical REST: GET /tasks/count.
|
|
450
428
|
const url = new URL(`${getTaskBaseUrl()}/count`);
|
|
451
|
-
if (filters.
|
|
452
|
-
url.searchParams.set('
|
|
429
|
+
if (filters.agreementId)
|
|
430
|
+
url.searchParams.set('agreementId', filters.agreementId);
|
|
453
431
|
if (filters.state)
|
|
454
432
|
url.searchParams.set('state', filters.state);
|
|
455
433
|
if (filters.userId)
|
|
@@ -489,7 +467,6 @@ export class TaskClient {
|
|
|
489
467
|
getTask(taskId) { return getTask(taskId, this.creds); }
|
|
490
468
|
updateTaskState(taskId, state, data) { return updateTaskState(taskId, state, data ?? {}, this.creds); }
|
|
491
469
|
getActiveTasksForAgent(agentId) { return getActiveTasksForAgent(agentId, this.creds); }
|
|
492
|
-
getActiveTasksForChat(chatId) { return getActiveTasksForChat(chatId, this.creds); }
|
|
493
470
|
cancelTask(taskId) { return cancelTask(taskId, this.creds); }
|
|
494
471
|
getSubtasks(parentTaskId) { return getSubtasks(parentTaskId, this.creds); }
|
|
495
472
|
replaceTaskPlan(taskId, steps) { return replaceTaskPlan(taskId, steps, this.creds); }
|