@ziggs-ai/api-client 0.18.0 → 0.20.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/dist/capabilities/agreements.d.ts +3 -0
- package/dist/capabilities/agreements.js +9 -1
- package/dist/capabilities/artifacts.d.ts +38 -0
- package/dist/capabilities/artifacts.js +115 -1
- package/dist/capabilities/context.d.ts +2 -0
- package/dist/capabilities/context.js +83 -0
- package/dist/capabilities/index.d.ts +3 -3
- package/dist/capabilities/index.js +3 -3
- package/dist/capabilities/introductions.js +12 -15
- package/dist/capabilities/links.js +1 -0
- package/dist/capabilities/nextCall.d.ts +72 -14
- package/dist/capabilities/nextCall.js +221 -4
- package/dist/http/ArtifactsClient.d.ts +47 -0
- package/dist/http/ArtifactsClient.js +33 -0
- package/dist/http/ContextOpenClient.d.ts +70 -0
- package/dist/http/ContextOpenClient.js +52 -0
- package/dist/http/InboxClient.d.ts +15 -0
- package/dist/http/InboxClient.js +33 -0
- package/dist/http/TaskClient.d.ts +2 -0
- package/dist/http/TaskClient.js +1 -0
- package/dist/http/index.d.ts +4 -0
- package/dist/http/index.js +2 -0
- package/dist/inboxAck.d.ts +70 -0
- package/dist/inboxAck.js +115 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +2 -0
- package/dist/instanceIdentity.d.ts +1 -1
- package/dist/instanceIdentity.js +2 -2
- package/dist/sessionOrientation.d.ts +50 -0
- package/dist/sessionOrientation.js +43 -0
- package/dist/types.d.ts +17 -0
- package/package.json +1 -1
package/dist/inboxAck.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One answer for "what does this envelope's ack list, and may we send it?"
|
|
3
|
+
*
|
|
4
|
+
* The SDK loop and the MCP inbox tool both call this. The bury-guard is the
|
|
5
|
+
* same question on both rails: list every assigned row this reader actually
|
|
6
|
+
* handled, and do not ack a window that is incomplete or that failed in its
|
|
7
|
+
* hands.
|
|
8
|
+
*/
|
|
9
|
+
function isOutOfReach(note) {
|
|
10
|
+
if (!note || typeof note !== 'object')
|
|
11
|
+
return false;
|
|
12
|
+
const n = note;
|
|
13
|
+
return (typeof n.scopeKind === 'string' &&
|
|
14
|
+
typeof n.scopeId === 'string' &&
|
|
15
|
+
typeof n.remedy === 'string');
|
|
16
|
+
}
|
|
17
|
+
/** Resource ids this envelope's ack must list. */
|
|
18
|
+
export function inboxAckHandledIds(envelope, ownAgentId) {
|
|
19
|
+
const mine = (assigneeId) => !ownAgentId || assigneeId === ownAgentId;
|
|
20
|
+
return [
|
|
21
|
+
...new Set([
|
|
22
|
+
...(envelope.deliveries ?? [])
|
|
23
|
+
.filter((d) => mine(d.assigneeId))
|
|
24
|
+
.filter((d) => !isOutOfReach(d.outOfReach))
|
|
25
|
+
.map((d) => d.resourceId),
|
|
26
|
+
...(envelope.openRequestsAwaitingMe ?? []).map((q) => q.agreementId),
|
|
27
|
+
]),
|
|
28
|
+
].filter((id) => typeof id === 'string' && id.length > 0);
|
|
29
|
+
}
|
|
30
|
+
/** Whether an ack is allowed for this envelope, given handover state. */
|
|
31
|
+
export function inboxAckAllowed(envelope, opts = {}) {
|
|
32
|
+
if (!envelope.ackTo)
|
|
33
|
+
return false;
|
|
34
|
+
if ((envelope.truncatedRequests ?? 0) !== 0)
|
|
35
|
+
return false;
|
|
36
|
+
const failures = opts.failures ?? 0;
|
|
37
|
+
if (failures > 0 && !opts.forceAck)
|
|
38
|
+
return false;
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
export function planInboxAck(envelope, opts = {}) {
|
|
42
|
+
return {
|
|
43
|
+
allowed: inboxAckAllowed(envelope, opts),
|
|
44
|
+
handledResourceIds: inboxAckHandledIds(envelope, opts.ownAgentId),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/** Every row whose instant the server's bury-guard will ask about. */
|
|
48
|
+
function gatingRows(envelope, ownAgentId) {
|
|
49
|
+
const mine = (assigneeId) => !ownAgentId || assigneeId === ownAgentId;
|
|
50
|
+
return [
|
|
51
|
+
...(envelope.deliveries ?? [])
|
|
52
|
+
.filter((d) => mine(d.assigneeId))
|
|
53
|
+
.filter((d) => !isOutOfReach(d.outOfReach))
|
|
54
|
+
.map((d) => ({ id: d.resourceId ?? '', ts: d.ts ?? '' })),
|
|
55
|
+
// Requests are projected out of `deliveries` for triage, but their rows
|
|
56
|
+
// are still in the log under the caller's stamp, so they gate a mark
|
|
57
|
+
// exactly as a delivery does.
|
|
58
|
+
...(envelope.openRequestsAwaitingMe ?? []).map((q) => ({
|
|
59
|
+
id: q.agreementId ?? '',
|
|
60
|
+
ts: q.ts ?? '',
|
|
61
|
+
})),
|
|
62
|
+
].filter((r) => r.id.length > 0 && r.ts.length > 0);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The furthest point in this envelope that is safe to ack RIGHT NOW, given what
|
|
66
|
+
* the reader has finished so far, or null when that is nowhere.
|
|
67
|
+
*
|
|
68
|
+
* Acking once at the end of a pass freezes the watermark for the whole pass,
|
|
69
|
+
* which is long enough for the stand-in's 90s takeover to elapse while the
|
|
70
|
+
* agent is still working. This lets the reader give the mark back in steps.
|
|
71
|
+
*
|
|
72
|
+
* The stopping point is a global instant, NOT the reader's current unit of
|
|
73
|
+
* work. A mark advances every source it names at once, so acking after one
|
|
74
|
+
* chat fold would bury an older row from a different chat — the guard would
|
|
75
|
+
* refuse it, correctly. So: walk the instants the server minted a token for,
|
|
76
|
+
* and stop at the last one where everything at or before it is handled.
|
|
77
|
+
* Anything else is either refused or a bug.
|
|
78
|
+
*
|
|
79
|
+
* Returns null when the server minted no per-row tokens, which is how an older
|
|
80
|
+
* backend is handled: the caller keeps its single end-of-pass ack.
|
|
81
|
+
*/
|
|
82
|
+
export function planPartialInboxAck(envelope, handled, opts = {}) {
|
|
83
|
+
if ((envelope.truncatedRequests ?? 0) !== 0)
|
|
84
|
+
return null;
|
|
85
|
+
const failures = opts.failures ?? 0;
|
|
86
|
+
if (failures > 0 && !opts.forceAck)
|
|
87
|
+
return null;
|
|
88
|
+
const tokenByInstant = new Map();
|
|
89
|
+
for (const d of envelope.deliveries ?? []) {
|
|
90
|
+
if (d.ts && d.ackTo)
|
|
91
|
+
tokenByInstant.set(d.ts, d.ackTo);
|
|
92
|
+
}
|
|
93
|
+
if (tokenByInstant.size === 0)
|
|
94
|
+
return null;
|
|
95
|
+
const gating = gatingRows(envelope, opts.ownAgentId);
|
|
96
|
+
// ISO-8601 sorts lexicographically in time order, which is what every other
|
|
97
|
+
// ts comparison on this rail already relies on.
|
|
98
|
+
const instants = [...tokenByInstant.keys()].sort();
|
|
99
|
+
let upTo = null;
|
|
100
|
+
for (const instant of instants) {
|
|
101
|
+
const settled = gating.every((r) => r.ts > instant || handled.has(r.id));
|
|
102
|
+
if (!settled)
|
|
103
|
+
break;
|
|
104
|
+
upTo = instant;
|
|
105
|
+
}
|
|
106
|
+
if (!upTo)
|
|
107
|
+
return null;
|
|
108
|
+
const reached = upTo;
|
|
109
|
+
return {
|
|
110
|
+
ackTo: tokenByInstant.get(reached),
|
|
111
|
+
handledResourceIds: [
|
|
112
|
+
...new Set(gating.filter((r) => r.ts <= reached).map((r) => r.id)),
|
|
113
|
+
],
|
|
114
|
+
};
|
|
115
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -12,7 +12,11 @@ export { parseErrorMessage, parseErrorCode, throwApiError, } from './shared/apiE
|
|
|
12
12
|
export { ApiError } from './types.js';
|
|
13
13
|
export type { Creds, Task, TaskState, PlanStep, PlanStepStatus, Agreement, AgreementParties, AgreementPartySide, EngagementKind, BroadcastAudience, EntryType, ContentType, MessageMetadata, MessageHandler, InboxDeliveryKind, InboxDeliveryRef, InboxChatNews, InboxProposalRef, InboxTaskRef, InboxRequestRef, InboxConnectionRequestRef, InboxHumanAttention, InboxEnvelope, InboxAckResult, InboxPeek, InboxReadOptions, } from './types.js';
|
|
14
14
|
export type { ProposeTerms, ProposeDirectInput, ProposeBroadcastInput, ProposeAgreementData, ClaimOptions, } from './http/AgreementClient.js';
|
|
15
|
+
export { planInboxAck, planPartialInboxAck, inboxAckAllowed, inboxAckHandledIds, } from './inboxAck.js';
|
|
16
|
+
export type { InboxAckDecision, InboxAckStep, InboxAckEnvelope, PlanInboxAckOptions, } from './inboxAck.js';
|
|
15
17
|
export { agreementLaneId, agreementIdFromLane, isAgreementLaneId, isAgreementId } from '@ziggs-ai/contracts';
|
|
16
18
|
export * from './shared/operatorKey.js';
|
|
17
19
|
export { INBOX_HOST_CLAIMANT_HEADER, INSTANCE_IDENTITY_MAX_LENGTH, cliHostIdentity, hostIdentity, instanceIdentity, mcpConnectionIdentity, } from './instanceIdentity.js';
|
|
18
20
|
export * from './utils/appUrls.js';
|
|
21
|
+
export { sessionOrientation, } from './sessionOrientation.js';
|
|
22
|
+
export type { ContinuationKind, InboxAckDriver, InboxHandling, RepresentedPerson, SessionContinuation, SessionOrientation, SessionOrientationInput, } from './sessionOrientation.js';
|
package/dist/index.js
CHANGED
|
@@ -13,7 +13,9 @@ export { RateLimitedError, isRateLimited, parseRetryAfterMs, pollSurfaceError, }
|
|
|
13
13
|
// half of them had drifted).
|
|
14
14
|
export { parseErrorMessage, parseErrorCode, throwApiError, } from './shared/apiError.js';
|
|
15
15
|
export { ApiError } from './types.js';
|
|
16
|
+
export { planInboxAck, planPartialInboxAck, inboxAckAllowed, inboxAckHandledIds, } from './inboxAck.js';
|
|
16
17
|
export { agreementLaneId, agreementIdFromLane, isAgreementLaneId, isAgreementId } from '@ziggs-ai/contracts';
|
|
17
18
|
export * from './shared/operatorKey.js';
|
|
18
19
|
export { INBOX_HOST_CLAIMANT_HEADER, INSTANCE_IDENTITY_MAX_LENGTH, cliHostIdentity, hostIdentity, instanceIdentity, mcpConnectionIdentity, } from './instanceIdentity.js';
|
|
19
20
|
export * from './utils/appUrls.js';
|
|
21
|
+
export { sessionOrientation, } from './sessionOrientation.js';
|
|
@@ -25,7 +25,7 @@ export declare function cliHostIdentity(): string;
|
|
|
25
25
|
*
|
|
26
26
|
* Not the backend task's identity. That process serves every connected
|
|
27
27
|
* assistant at once, so its own identity makes them one host and changes under
|
|
28
|
-
* all of them on every deploy — a hosted read answering 409 for up to
|
|
28
|
+
* all of them on every deploy — a hosted read answering 409 for up to 220
|
|
29
29
|
* seconds after each restart, and replicas refusing each other's agents.
|
|
30
30
|
*
|
|
31
31
|
* The key survives both. A token refresh re-signs the same `keyId`, so an
|
package/dist/instanceIdentity.js
CHANGED
|
@@ -4,7 +4,7 @@ import { decodeOperatorKeyClaims } from './shared/operatorKey.js';
|
|
|
4
4
|
*
|
|
5
5
|
* `X-Ziggs-Instance` decides inbox ownership: the first host to read acquires
|
|
6
6
|
* an agent's whole inbox, a second host is refused with
|
|
7
|
-
* `409 INBOX_HOST_CONFLICT` until the owner has stopped renewing for
|
|
7
|
+
* `409 INBOX_HOST_CONFLICT` until the owner has stopped renewing for 220
|
|
8
8
|
* seconds, and an ack renews an owner but never acquires. A missing or
|
|
9
9
|
* overlong value is `400 INBOX_HOST_REQUIRED`.
|
|
10
10
|
*
|
|
@@ -71,7 +71,7 @@ export function cliHostIdentity() {
|
|
|
71
71
|
*
|
|
72
72
|
* Not the backend task's identity. That process serves every connected
|
|
73
73
|
* assistant at once, so its own identity makes them one host and changes under
|
|
74
|
-
* all of them on every deploy — a hosted read answering 409 for up to
|
|
74
|
+
* all of them on every deploy — a hosted read answering 409 for up to 220
|
|
75
75
|
* seconds after each restart, and replicas refusing each other's agents.
|
|
76
76
|
*
|
|
77
77
|
* The key survives both. A token refresh re-signs the same `keyId`, so an
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Who this session is, who it represents, and whether it can continue
|
|
3
|
+
* unattended. Built from the binding the caller already has. Does not
|
|
4
|
+
* read mail and does not take the inbox lease.
|
|
5
|
+
*/
|
|
6
|
+
export type InboxAckDriver = 'agent' | 'host';
|
|
7
|
+
export type ContinuationKind = 'manual' | 'scheduled';
|
|
8
|
+
export interface RepresentedPerson {
|
|
9
|
+
/** The human this agent acts for. Null when the key does not name one. */
|
|
10
|
+
id: string | null;
|
|
11
|
+
kind: 'user';
|
|
12
|
+
}
|
|
13
|
+
export interface SessionContinuation {
|
|
14
|
+
kind: ContinuationKind;
|
|
15
|
+
canScheduleWake: boolean;
|
|
16
|
+
summary: string;
|
|
17
|
+
}
|
|
18
|
+
export interface InboxHandling {
|
|
19
|
+
/** Who closes the inbox loop. Never both on the same identity. */
|
|
20
|
+
ackDriver: InboxAckDriver;
|
|
21
|
+
readAcquiresHost: true;
|
|
22
|
+
peekDoesNotAcquire: true;
|
|
23
|
+
peek: {
|
|
24
|
+
tool: string;
|
|
25
|
+
http: {
|
|
26
|
+
method: 'GET';
|
|
27
|
+
path: '/inbox/peek';
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export interface SessionOrientation {
|
|
32
|
+
representedPerson: RepresentedPerson;
|
|
33
|
+
continuation: SessionContinuation;
|
|
34
|
+
inboxHandling: InboxHandling;
|
|
35
|
+
identity: {
|
|
36
|
+
agentId: string;
|
|
37
|
+
distinctFromBackgroundWorker: true;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export interface SessionOrientationInput {
|
|
41
|
+
agentId: string;
|
|
42
|
+
ownerUserId: string | null;
|
|
43
|
+
/** MCP assistants ack themselves. The hosted SDK host acks; the brain does not. */
|
|
44
|
+
surface: 'mcp' | 'sdk';
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Facts a cold entry needs that `session.ownerId` never labelled: the
|
|
48
|
+
* represented person, whether this host can arm a wake, and who drives ack.
|
|
49
|
+
*/
|
|
50
|
+
export declare function sessionOrientation(input: SessionOrientationInput): SessionOrientation;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Who this session is, who it represents, and whether it can continue
|
|
3
|
+
* unattended. Built from the binding the caller already has. Does not
|
|
4
|
+
* read mail and does not take the inbox lease.
|
|
5
|
+
*/
|
|
6
|
+
const PEEK = {
|
|
7
|
+
tool: 'ziggs_inbox_peek',
|
|
8
|
+
http: { method: 'GET', path: '/inbox/peek' },
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Facts a cold entry needs that `session.ownerId` never labelled: the
|
|
12
|
+
* represented person, whether this host can arm a wake, and who drives ack.
|
|
13
|
+
*/
|
|
14
|
+
export function sessionOrientation(input) {
|
|
15
|
+
const mcp = input.surface === 'mcp';
|
|
16
|
+
return {
|
|
17
|
+
representedPerson: {
|
|
18
|
+
id: input.ownerUserId,
|
|
19
|
+
kind: 'user',
|
|
20
|
+
},
|
|
21
|
+
continuation: mcp
|
|
22
|
+
? {
|
|
23
|
+
kind: 'manual',
|
|
24
|
+
canScheduleWake: false,
|
|
25
|
+
summary: 'This MCP session has no wake_at. Continue by calling ziggs_inbox again — a scheduler in the host is the only unattended path.',
|
|
26
|
+
}
|
|
27
|
+
: {
|
|
28
|
+
kind: 'scheduled',
|
|
29
|
+
canScheduleWake: true,
|
|
30
|
+
summary: 'The hosted runtime can arm wake_at. The brain does not ack; the host owns the inbox loop.',
|
|
31
|
+
},
|
|
32
|
+
inboxHandling: {
|
|
33
|
+
ackDriver: mcp ? 'agent' : 'host',
|
|
34
|
+
readAcquiresHost: true,
|
|
35
|
+
peekDoesNotAcquire: true,
|
|
36
|
+
peek: PEEK,
|
|
37
|
+
},
|
|
38
|
+
identity: {
|
|
39
|
+
agentId: input.agentId,
|
|
40
|
+
distinctFromBackgroundWorker: true,
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -442,6 +442,23 @@ export interface InboxEnvelope extends InboxRequestChannel {
|
|
|
442
442
|
deliveries: InboxDeliveryRef[];
|
|
443
443
|
/** True when there was more than one envelope's worth; the rest stay unacked. */
|
|
444
444
|
deliveriesCapped: boolean;
|
|
445
|
+
/**
|
|
446
|
+
* How far behind this window is, when it stops short of the present. Null
|
|
447
|
+
* when it reaches it, and absent on older servers.
|
|
448
|
+
*
|
|
449
|
+
* The window is the oldest unacked rows: a reader that acks slides it
|
|
450
|
+
* forward every pass, and one that does not accumulates behind it. Hold this
|
|
451
|
+
* and you can never honestly report an empty inbox — say how far back you
|
|
452
|
+
* are looking, and ack to reach the rest.
|
|
453
|
+
*/
|
|
454
|
+
backlog?: {
|
|
455
|
+
/** Unacked rows newer than `windowEndsAt`, across every mailbox. */
|
|
456
|
+
beyondWindow: number;
|
|
457
|
+
/** The newest unacked row anywhere in this view. */
|
|
458
|
+
newestAt: string | null;
|
|
459
|
+
/** The last row IN this window; everything after it is the backlog. */
|
|
460
|
+
windowEndsAt: string;
|
|
461
|
+
} | null;
|
|
445
462
|
/** This agent's ASSIGNED chat mail, folded by chat. */
|
|
446
463
|
chats: InboxChatNews[];
|
|
447
464
|
/**
|