@ziggs-ai/agent-sdk 0.12.0 → 0.13.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/AgentHost.js +4 -1
- package/dist/agent/ClaudeSdkAgent.js +2 -2
- package/dist/cognition/validateContext.js +4 -1
- package/dist/context/batch.d.ts +3 -0
- package/dist/context/batch.js +8 -5
- package/dist/context/classifyEnvelope.js +4 -5
- package/dist/context/routingLabels.js +2 -3
- package/dist/formatters/AgreementFormatter.d.ts +7 -1
- package/dist/formatters/AgreementFormatter.js +17 -8
- package/dist/formatters/HistoryFormatter.d.ts +5 -0
- package/dist/formatters/HistoryFormatter.js +16 -7
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/ingress/normalizeIncoming.d.ts +3 -8
- package/dist/runtime/PromptBuilder.js +19 -21
- package/dist/runtime/buildOutcome.js +4 -4
- package/dist/runtime/mandateDeclaration.d.ts +9 -0
- package/dist/runtime/mandateDeclaration.js +62 -0
- package/dist/runtime/runTurn.js +8 -0
- package/dist/server/agreements/AgreementService.d.ts +14 -10
- package/dist/server/agreements/AgreementService.js +14 -8
- package/dist/server/proactive/ProactiveTrigger.d.ts +7 -1
- package/dist/server/proactive/ProactiveTrigger.js +2 -2
- package/dist/server/tools/index.d.ts +1 -1
- package/dist/server/tools/index.js +1 -1
- package/dist/server/tools/tier1/protocolRunner.js +14 -8
- package/dist/server/tools/tier1/protocolTools.js +7 -2
- package/dist/server/tools/tier2/linkTools.d.ts +1 -1
- package/dist/server/tools/tier2/linkTools.js +11 -9
- package/dist/types.d.ts +2 -6
- package/package.json +2 -2
package/dist/AgentHost.js
CHANGED
|
@@ -201,11 +201,13 @@ export class AgentHost {
|
|
|
201
201
|
description: resolved.description || 'A helpful assistant',
|
|
202
202
|
systemPrompt: resolved.systemPrompt,
|
|
203
203
|
specialization: resolved.specialization ?? null,
|
|
204
|
+
requestMatchTags: resolved.requestMatchTags,
|
|
204
205
|
tools: resolved.tools || [],
|
|
205
206
|
// Copied deliberately: this object is an allow-list, so a field left out
|
|
206
207
|
// here is silently dropped between the manifest and the runtime. That is
|
|
207
208
|
// exactly how `respondsToProposals` reached the store as null for 133
|
|
208
|
-
// agents — the declaration existed and nothing carried it.
|
|
209
|
+
// agents — the declaration existed and nothing carried it. Same for
|
|
210
|
+
// `requestMatchTags` (always undefined at triage) and `telemetryIngest`.
|
|
209
211
|
needs: resolved.needs,
|
|
210
212
|
taskTools: resolved.taskTools,
|
|
211
213
|
states: resolved.states,
|
|
@@ -219,6 +221,7 @@ export class AgentHost {
|
|
|
219
221
|
metricsTap: resolved.metricsTap,
|
|
220
222
|
wakeTap: resolved.wakeTap,
|
|
221
223
|
inboxCatchUp: resolved.inboxCatchUp,
|
|
224
|
+
telemetryIngest: resolved.telemetryIngest,
|
|
222
225
|
brain: resolved.brain,
|
|
223
226
|
cognition: resolved.cognition ?? 'fsm',
|
|
224
227
|
claudeMaxTurns: resolved.claudeMaxTurns,
|
|
@@ -197,8 +197,8 @@ function resolveOnBehalfAgreementId(rawEvent, contextSnap, agentId, sessionId) {
|
|
|
197
197
|
? contextSnap.agreements
|
|
198
198
|
: [];
|
|
199
199
|
const provides = ags.filter((a) => {
|
|
200
|
-
const
|
|
201
|
-
const providerIds = [
|
|
200
|
+
const provider = a?.parties?.provider;
|
|
201
|
+
const providerIds = [provider?.actor, a?.providerId, a?.providerAgentId, a?.executorId];
|
|
202
202
|
const isProvider = providerIds.some((x) => x === agentId);
|
|
203
203
|
const status = a?.status != null ? String(a.status).toLowerCase() : '';
|
|
204
204
|
const active = !status || status === 'active' || status === 'approved';
|
|
@@ -41,8 +41,11 @@ function validateAgreements(agreements, warn) {
|
|
|
41
41
|
}
|
|
42
42
|
if (ag['parties'] && typeof ag['parties'] === 'object') {
|
|
43
43
|
const parties = ag['parties'];
|
|
44
|
+
// Each side is occupied when either column names somebody; a side with a
|
|
45
|
+
// principal and no actor is a party acting through its own credential.
|
|
44
46
|
for (const key of ['creator', 'provider']) {
|
|
45
|
-
|
|
47
|
+
const side = parties[key];
|
|
48
|
+
if (!side?.principal && !side?.actor)
|
|
46
49
|
warn(`${path}.parties.${key}`, 'missing');
|
|
47
50
|
}
|
|
48
51
|
}
|
package/dist/context/batch.d.ts
CHANGED
|
@@ -1,2 +1,5 @@
|
|
|
1
|
+
import { type AgreementParties } from '@ziggs-ai/api-client';
|
|
1
2
|
export declare function getBatchEvents(rawEvent: unknown): unknown[];
|
|
2
3
|
export declare function isTaskResultRelevantToAgent(result: Record<string, unknown>, ownAgentId: string | null): boolean;
|
|
4
|
+
/** The agreement's party slots on a task_result payload, when it carries one. */
|
|
5
|
+
export declare function partiesOf(result: Record<string, unknown>): AgreementParties | undefined;
|
package/dist/context/batch.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isBroadcastTarget } from '@ziggs-ai/api-client';
|
|
1
|
+
import { isBroadcastTarget, partyActorIds } from '@ziggs-ai/api-client';
|
|
2
2
|
export function getBatchEvents(rawEvent) {
|
|
3
3
|
if (!rawEvent)
|
|
4
4
|
return [];
|
|
@@ -10,9 +10,12 @@ export function getBatchEvents(rawEvent) {
|
|
|
10
10
|
export function isTaskResultRelevantToAgent(result, ownAgentId) {
|
|
11
11
|
if (!ownAgentId)
|
|
12
12
|
return true;
|
|
13
|
-
const parties = result
|
|
13
|
+
const parties = partiesOf(result);
|
|
14
14
|
return !!(result['creatorIsYou'] || result['providerIsYou'] || result['payerIsYou'] || result['proposedToIsYou'] ||
|
|
15
|
-
parties
|
|
16
|
-
parties
|
|
17
|
-
|
|
15
|
+
partyActorIds(parties).includes(ownAgentId) ||
|
|
16
|
+
isBroadcastTarget(parties?.proposedTo?.principal));
|
|
17
|
+
}
|
|
18
|
+
/** The agreement's party slots on a task_result payload, when it carries one. */
|
|
19
|
+
export function partiesOf(result) {
|
|
20
|
+
return result['agreement']?.parties;
|
|
18
21
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { CONTEXT_RESET } from './applyEffects.js';
|
|
2
|
-
import { getBatchEvents, isTaskResultRelevantToAgent } from './batch.js';
|
|
2
|
+
import { getBatchEvents, isTaskResultRelevantToAgent, partiesOf } from './batch.js';
|
|
3
3
|
function isTaskAssignment(result, ownAgentId) {
|
|
4
|
-
const
|
|
5
|
-
const isProvider = result['providerIsYou'] || parties['provider'] === ownAgentId;
|
|
4
|
+
const isProvider = result['providerIsYou'] || partiesOf(result)?.provider?.actor === ownAgentId;
|
|
6
5
|
return !!(isProvider && (result['state'] === 'active' || result['state'] === 'in-progress'));
|
|
7
6
|
}
|
|
8
7
|
export function classifyIncomingEvent(rawEvent, ownAgentId) {
|
|
@@ -22,9 +21,9 @@ export function classifyIncomingEvent(rawEvent, ownAgentId) {
|
|
|
22
21
|
continue;
|
|
23
22
|
const state = result['state'];
|
|
24
23
|
const ag = result['agreement'];
|
|
25
|
-
const parties =
|
|
24
|
+
const parties = partiesOf(result);
|
|
26
25
|
const proposalStatus = ag?.['proposal']?.['status'];
|
|
27
|
-
if (state === 'active' && proposalStatus === 'approved' && ownAgentId && parties
|
|
26
|
+
if (state === 'active' && proposalStatus === 'approved' && ownAgentId && parties?.creator?.actor === ownAgentId) {
|
|
28
27
|
flags['approval'] = true;
|
|
29
28
|
flags['taskAssignment'] = result;
|
|
30
29
|
continue;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isProtocolToolName } from '../tasks/protocolRegistry.js';
|
|
2
|
-
import { getBatchEvents, isTaskResultRelevantToAgent } from './batch.js';
|
|
2
|
+
import { getBatchEvents, isTaskResultRelevantToAgent, partiesOf } from './batch.js';
|
|
3
3
|
export function unwrapBatchEvent(event) {
|
|
4
4
|
const e = event;
|
|
5
5
|
if (e?.['type'] === 'batch' && Array.isArray(e['events']) && e['events'][0])
|
|
@@ -22,9 +22,8 @@ export function classifyWorkflowEvent(event, ownAgentId = null) {
|
|
|
22
22
|
sawOwnTaskResult = true;
|
|
23
23
|
const state = result['state'];
|
|
24
24
|
const ag = result['agreement'];
|
|
25
|
-
const parties = ag?.['parties'] || {};
|
|
26
25
|
const proposalStatus = ag?.['proposal'] ? ag['proposal']['status'] : undefined;
|
|
27
|
-
const isProvider = result['providerIsYou'] ||
|
|
26
|
+
const isProvider = result['providerIsYou'] || partiesOf(result)?.provider?.actor === ownAgentId;
|
|
28
27
|
if (isProvider && (state === 'active' || state === 'in-progress'))
|
|
29
28
|
return 'task_assignment';
|
|
30
29
|
if (proposalStatus === 'approved' || (state === 'active' && proposalStatus === 'pending'))
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type AgreementPartySide } from '@ziggs-ai/api-client';
|
|
1
2
|
interface AgreementFormatterOptions {
|
|
2
3
|
shortIds?: boolean;
|
|
3
4
|
indentSize?: number;
|
|
@@ -10,7 +11,12 @@ export declare class AgreementFormatter {
|
|
|
10
11
|
format(agreements: AnyObj[] | null | undefined): string;
|
|
11
12
|
formatAgreement(ag: AnyObj, indent?: string): string;
|
|
12
13
|
_renderTaskExecution(task: AnyObj, indent?: string): string;
|
|
13
|
-
|
|
14
|
+
/**
|
|
15
|
+
* A side names up to two ids — the accountable principal and the agent that
|
|
16
|
+
* acted for it. Both are rendered: the agent has to recognize itself here,
|
|
17
|
+
* and the principal is who the deal is actually with.
|
|
18
|
+
*/
|
|
19
|
+
_labelParty(side: AgreementPartySide | null | undefined, isYou: boolean | undefined): string;
|
|
14
20
|
shortId(id: string | null | undefined): string;
|
|
15
21
|
private _indent;
|
|
16
22
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { partySideIds } from '@ziggs-ai/api-client';
|
|
1
2
|
export class AgreementFormatter {
|
|
2
3
|
shortIds;
|
|
3
4
|
indentSize;
|
|
@@ -37,14 +38,16 @@ export class AgreementFormatter {
|
|
|
37
38
|
const status = (ag['status'] || 'unknown').toUpperCase();
|
|
38
39
|
const agId = this.shortId(ag['agreementId']);
|
|
39
40
|
const parties = ag['parties'] || {};
|
|
41
|
+
const side = (name) => parties[name];
|
|
40
42
|
const lines = [
|
|
41
43
|
`${indent}[${status}] Agreement ${agId}`,
|
|
42
|
-
`${indent} Creator: ${this._labelParty(
|
|
43
|
-
`${indent} Provider: ${this._labelParty(
|
|
44
|
-
`${indent} Payer: ${this._labelParty(
|
|
44
|
+
`${indent} Creator: ${this._labelParty(side('creator'), ag['creatorIsYou'])}`,
|
|
45
|
+
`${indent} Provider: ${this._labelParty(side('provider'), ag['providerIsYou'])}`,
|
|
46
|
+
`${indent} Payer: ${this._labelParty(side('payer'), ag['payerIsYou'])}`,
|
|
45
47
|
];
|
|
46
|
-
if (
|
|
47
|
-
lines.push(`${indent} Proposed to: ${this._labelParty(
|
|
48
|
+
if (partySideIds(side('proposedTo')).length > 0) {
|
|
49
|
+
lines.push(`${indent} Proposed to: ${this._labelParty(side('proposedTo'), ag['proposedToIsYou'])}`);
|
|
50
|
+
}
|
|
48
51
|
const proposal = ag['proposal'];
|
|
49
52
|
if (proposal?.['status'])
|
|
50
53
|
lines.push(`${indent} Proposal: ${proposal['status']}${proposal['respondedBy'] ? ` (by ${proposal['respondedBy']})` : ''}`);
|
|
@@ -111,10 +114,16 @@ export class AgreementFormatter {
|
|
|
111
114
|
}
|
|
112
115
|
return lines.join('\n');
|
|
113
116
|
}
|
|
114
|
-
|
|
115
|
-
|
|
117
|
+
/**
|
|
118
|
+
* A side names up to two ids — the accountable principal and the agent that
|
|
119
|
+
* acted for it. Both are rendered: the agent has to recognize itself here,
|
|
120
|
+
* and the principal is who the deal is actually with.
|
|
121
|
+
*/
|
|
122
|
+
_labelParty(side, isYou) {
|
|
123
|
+
const shown = partySideIds(side).join(' via ');
|
|
124
|
+
if (!shown)
|
|
116
125
|
return 'N/A';
|
|
117
|
-
return isYou ? `You (${
|
|
126
|
+
return isYou ? `You (${shown})` : shown;
|
|
118
127
|
}
|
|
119
128
|
shortId(id) {
|
|
120
129
|
if (!id)
|
|
@@ -17,6 +17,11 @@ export declare class HistoryFormatter {
|
|
|
17
17
|
formatParticipant(participant: AnyObj | null | undefined, agentId: string): string;
|
|
18
18
|
formatOwner(task: AnyObj, agentId: string): string;
|
|
19
19
|
formatExecutor(task: AnyObj, agentId: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* One side of the task's agreement, or `fallback` when the frame carries no
|
|
22
|
+
* parties — a bare resource_changed skeleton has the task ids and nothing else.
|
|
23
|
+
*/
|
|
24
|
+
private _formatSide;
|
|
20
25
|
shortId(id: string | null | undefined): string;
|
|
21
26
|
formatTime(timestamp: string | null | undefined): string;
|
|
22
27
|
formatArgs(args: AnyObj | null | undefined): string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { partySideIds } from '@ziggs-ai/api-client';
|
|
1
2
|
export class HistoryFormatter {
|
|
2
3
|
showTimestamps;
|
|
3
4
|
shortIds;
|
|
@@ -67,7 +68,8 @@ export class HistoryFormatter {
|
|
|
67
68
|
case 'created': {
|
|
68
69
|
const ag = task['agreement'];
|
|
69
70
|
const parties = ag?.['parties'] || {};
|
|
70
|
-
|
|
71
|
+
const proposedTo = partySideIds(parties['proposedTo']).join(' via ');
|
|
72
|
+
return `${timePrefix}Task created under agreement: "${desc}" (${ids})\n Owner: ${this.formatOwner(task, agentId)}\n Executor: ${this.formatExecutor(task, agentId)}\n Proposed to: ${proposedTo || 'N/A'}`;
|
|
71
73
|
}
|
|
72
74
|
case 'state_changed': {
|
|
73
75
|
const prev = service?.['previousState']?.['state'] || '?';
|
|
@@ -140,14 +142,21 @@ export class HistoryFormatter {
|
|
|
140
142
|
return `${id} (${participant['type'] || 'unknown'})`;
|
|
141
143
|
}
|
|
142
144
|
formatOwner(task, agentId) {
|
|
143
|
-
|
|
144
|
-
const creatorId = ag?.['parties']?.['creator'] ?? task['agentId'];
|
|
145
|
-
return creatorId === agentId ? 'You' : String(creatorId ?? 'N/A');
|
|
145
|
+
return this._formatSide(task, 'creator', task['agentId'], agentId);
|
|
146
146
|
}
|
|
147
147
|
formatExecutor(task, agentId) {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
148
|
+
return this._formatSide(task, 'provider', task['executorId'], agentId);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* One side of the task's agreement, or `fallback` when the frame carries no
|
|
152
|
+
* parties — a bare resource_changed skeleton has the task ids and nothing else.
|
|
153
|
+
*/
|
|
154
|
+
_formatSide(task, role, fallback, agentId) {
|
|
155
|
+
const parties = task['agreement']?.['parties'];
|
|
156
|
+
const ids = partySideIds(parties?.[role]);
|
|
157
|
+
if (ids.length === 0)
|
|
158
|
+
return fallback === agentId ? 'You' : String(fallback ?? 'N/A');
|
|
159
|
+
return ids.includes(agentId) ? 'You' : ids.join(' via ');
|
|
151
160
|
}
|
|
152
161
|
shortId(id) {
|
|
153
162
|
if (!id)
|
package/dist/index.d.ts
CHANGED
|
@@ -62,7 +62,7 @@ export { HistoryFormatter, historyFormatter, AgreementFormatter, agreementFormat
|
|
|
62
62
|
export { ProactiveTrigger } from './server/proactive/ProactiveTrigger.js';
|
|
63
63
|
export { PollRegistry, declaredPolls, type PollBinding, type PollContext, type PollKind, type PollResult, } from './server/polls/PollKind.js';
|
|
64
64
|
export { PollRunner, type PollRunnerOptions, type WakeTarget } from './server/polls/PollRunner.js';
|
|
65
|
-
export { PROTOCOL_TOOLS, agreementProposeTool, agreementSubcontractTool, agreementRespondTool, agreementClaimTool, agreementCounterTool, agreementGetTool, agreementRevokeTool, agreementFulfillTool, taskCreateTool, taskSetResultTool, taskUpdatePlanStepTool, dispatchProtocolOp, PAYMENT_TOOLS, paymentBalanceTool, CONNECTION_TOOLS, connectionProxyTool, connectionListGrantsTool, requestConnectionTool, MCP_CONNECTION_TOOLS, mcpToolCallTool, mcpToolsListTool, DISCOVERY_TOOLS, agentSearchTool, agentGetTool, MARKETPLACE_TOOLS, marketplaceViewTool, CONTEXT_TOOLS, contextReadTool, contextDelegateTool, contextExpandReachTool, contextDiscoverGrantableTool, GRANTS_TOOLS, listGrantsTool, LINK_TOOLS,
|
|
65
|
+
export { PROTOCOL_TOOLS, agreementProposeTool, agreementSubcontractTool, agreementRespondTool, agreementClaimTool, agreementCounterTool, agreementGetTool, agreementRevokeTool, agreementFulfillTool, taskCreateTool, taskSetResultTool, taskUpdatePlanStepTool, dispatchProtocolOp, PAYMENT_TOOLS, paymentBalanceTool, CONNECTION_TOOLS, connectionProxyTool, connectionListGrantsTool, requestConnectionTool, MCP_CONNECTION_TOOLS, mcpToolCallTool, mcpToolsListTool, DISCOVERY_TOOLS, agentSearchTool, agentGetTool, MARKETPLACE_TOOLS, marketplaceViewTool, CONTEXT_TOOLS, contextReadTool, contextDelegateTool, contextExpandReachTool, contextDiscoverGrantableTool, GRANTS_TOOLS, listGrantsTool, LINK_TOOLS, linkProposeTool, linkListTool, ARTIFACT_TOOLS, recordArtifactTool, CHAT_TOOLS, openConversationTool, } from './server/tools/index.js';
|
|
66
66
|
export { CLAUDE_SDK_TOOLS } from './agent/claudeSdkTools.js';
|
|
67
67
|
export { PROTOCOL_TOOL_NAMES, PROTOCOL_TOOL_TO_OPERATION, mapProtocolToolToOperation, isProtocolToolName } from './tasks/protocolRegistry.js';
|
|
68
68
|
export { InMemoryStore, FileMemoryStore } from './memory/MemoryStore.js';
|
package/dist/index.js
CHANGED
|
@@ -76,7 +76,7 @@ export {
|
|
|
76
76
|
// Tier 1 — protocol grammar (framework-managed via taskTools).
|
|
77
77
|
PROTOCOL_TOOLS, agreementProposeTool, agreementSubcontractTool, agreementRespondTool, agreementClaimTool, agreementCounterTool, agreementGetTool, agreementRevokeTool, agreementFulfillTool, taskCreateTool, taskSetResultTool, taskUpdatePlanStepTool, dispatchProtocolOp,
|
|
78
78
|
// Tier 2 — opt-in capability bundles (spread into `tools:`).
|
|
79
|
-
PAYMENT_TOOLS, paymentBalanceTool, CONNECTION_TOOLS, connectionProxyTool, connectionListGrantsTool, requestConnectionTool, MCP_CONNECTION_TOOLS, mcpToolCallTool, mcpToolsListTool, DISCOVERY_TOOLS, agentSearchTool, agentGetTool, MARKETPLACE_TOOLS, marketplaceViewTool, CONTEXT_TOOLS, contextReadTool, contextDelegateTool, contextExpandReachTool, contextDiscoverGrantableTool, GRANTS_TOOLS, listGrantsTool, LINK_TOOLS,
|
|
79
|
+
PAYMENT_TOOLS, paymentBalanceTool, CONNECTION_TOOLS, connectionProxyTool, connectionListGrantsTool, requestConnectionTool, MCP_CONNECTION_TOOLS, mcpToolCallTool, mcpToolsListTool, DISCOVERY_TOOLS, agentSearchTool, agentGetTool, MARKETPLACE_TOOLS, marketplaceViewTool, CONTEXT_TOOLS, contextReadTool, contextDelegateTool, contextExpandReachTool, contextDiscoverGrantableTool, GRANTS_TOOLS, listGrantsTool, LINK_TOOLS, linkProposeTool, linkListTool, ARTIFACT_TOOLS, recordArtifactTool, CHAT_TOOLS, openConversationTool, } from './server/tools/index.js';
|
|
80
80
|
// The always-on toolset for the hosted Claude cognition (parity guard
|
|
81
81
|
// reads this as part of the real SDK registry).
|
|
82
82
|
export { CLAUDE_SDK_TOOLS } from './agent/claudeSdkTools.js';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AgreementParties } from '@ziggs-ai/api-client';
|
|
1
2
|
export interface NormalizedEvent {
|
|
2
3
|
chatId: string | null;
|
|
3
4
|
event: {
|
|
@@ -25,12 +26,6 @@ export interface NormalizedEvent {
|
|
|
25
26
|
text?: string;
|
|
26
27
|
};
|
|
27
28
|
}
|
|
28
|
-
interface WireParties {
|
|
29
|
-
creator?: string;
|
|
30
|
-
provider?: string;
|
|
31
|
-
payer?: string;
|
|
32
|
-
proposedTo?: string;
|
|
33
|
-
}
|
|
34
29
|
interface WireTask {
|
|
35
30
|
taskId?: string;
|
|
36
31
|
agentId?: string;
|
|
@@ -42,7 +37,7 @@ interface WireTask {
|
|
|
42
37
|
state?: string;
|
|
43
38
|
proposal?: unknown;
|
|
44
39
|
agreement?: {
|
|
45
|
-
parties?:
|
|
40
|
+
parties?: AgreementParties;
|
|
46
41
|
};
|
|
47
42
|
}
|
|
48
43
|
interface WireMetadata {
|
|
@@ -69,7 +64,7 @@ interface WireMetadata {
|
|
|
69
64
|
proposal?: {
|
|
70
65
|
status?: string;
|
|
71
66
|
};
|
|
72
|
-
parties?:
|
|
67
|
+
parties?: AgreementParties;
|
|
73
68
|
approvals?: Array<{
|
|
74
69
|
partyId?: string;
|
|
75
70
|
status?: string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { HistoryFormatter, AgreementFormatter } from '../formatters/index.js';
|
|
2
2
|
import { validateContext } from '../cognition/validateContext.js';
|
|
3
|
-
import { isBroadcastTarget, isOpaquePresentationRef } from '@ziggs-ai/api-client';
|
|
3
|
+
import { isBroadcastTarget, isOpaquePresentationRef, partySideIds } from '@ziggs-ai/api-client';
|
|
4
4
|
import { LANE_BOOK_KEY, allCollectedResults, allDelegatedAgreementIds, allPendingSubtasks, } from './taskBookkeeping.js';
|
|
5
5
|
/** Rough chars-per-token ratio for budgeting (English/JSON averages ~4). */
|
|
6
6
|
const CHARS_PER_TOKEN = 4;
|
|
@@ -191,12 +191,13 @@ ${this._renderDecisionSchemaFromActions(actions, senderMeta, ctxObj, omitMessage
|
|
|
191
191
|
const creatorIsYou = task.creatorIsYou;
|
|
192
192
|
const providerIsYou = task.providerIsYou;
|
|
193
193
|
if (ag?.agreementId) {
|
|
194
|
+
const named = (name) => partySideIds(parties[name]).join(' via ');
|
|
194
195
|
lines.push(`Agreement: ${ag.agreementId}${creatorIsYou ? ' (yours)' : ''}`);
|
|
195
|
-
lines.push(` Creator: ${
|
|
196
|
-
lines.push(` Provider: ${
|
|
196
|
+
lines.push(` Creator: ${named('creator') || 'N/A'}${creatorIsYou ? ' (You)' : ''}`);
|
|
197
|
+
lines.push(` Provider: ${named('provider') || 'N/A'}${providerIsYou ? ' (You)' : ''}`);
|
|
197
198
|
const proposal = ag.proposal;
|
|
198
199
|
if (proposal?.status) {
|
|
199
|
-
lines.push(` Proposal: ${proposal.status} (to ${
|
|
200
|
+
lines.push(` Proposal: ${proposal.status} (to ${named('proposedTo')}${task.proposedToIsYou ? ' - You' : ''})`);
|
|
200
201
|
}
|
|
201
202
|
const money = ag.money;
|
|
202
203
|
if (typeof money?.price === 'number' && money.price > 0) {
|
|
@@ -300,22 +301,19 @@ ${this._renderDecisionSchemaFromActions(actions, senderMeta, ctxObj, omitMessage
|
|
|
300
301
|
for (const ag of agreements || []) {
|
|
301
302
|
const agId = ag?.agreementId;
|
|
302
303
|
const parties = ag?.parties || {};
|
|
303
|
-
const
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
[
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
if (!agreementParties.has(pid))
|
|
317
|
-
agreementParties.set(pid, []);
|
|
318
|
-
agreementParties.get(pid).push({ agreementId: agId ?? '', role });
|
|
304
|
+
for (const role of ['creator', 'provider', 'payer', 'proposedTo']) {
|
|
305
|
+
// Both columns of a side are addressable and carry the same role: the
|
|
306
|
+
// principal is who the deal is with, the actor is who acts on it.
|
|
307
|
+
for (const pid of partySideIds(parties[role])) {
|
|
308
|
+
if (pid === myAgentId)
|
|
309
|
+
continue;
|
|
310
|
+
// Opaque presentation refs are not principal slots.
|
|
311
|
+
if (isOpaquePresentationRef(pid))
|
|
312
|
+
continue;
|
|
313
|
+
if (!agreementParties.has(pid))
|
|
314
|
+
agreementParties.set(pid, []);
|
|
315
|
+
agreementParties.get(pid).push({ agreementId: agId ?? '', role });
|
|
316
|
+
}
|
|
319
317
|
}
|
|
320
318
|
}
|
|
321
319
|
// Chat keeps the full directory — including opaque `rpb_*` / `psn_*` refs
|
|
@@ -380,7 +378,7 @@ ${this._renderDecisionSchemaFromActions(actions, senderMeta, ctxObj, omitMessage
|
|
|
380
378
|
lines.push('Set "action" to exactly one of the action names below.');
|
|
381
379
|
lines.push('Message text is rendered as markdown (CommonMark + GFM). You MAY use **bold**, *italic*, `code`, fenced code blocks, lists, links, and tables when they improve clarity — plain text is also fine.\n');
|
|
382
380
|
const replyTo = senderMeta.defaultReplyReceiverId || '<receiverId>';
|
|
383
|
-
const pendingProposals = (ctxObj.agreements || []).filter((a) => a.proposal?.status === 'pending' && (a.proposedToIsYou || isBroadcastTarget(a.parties?.proposedTo)));
|
|
381
|
+
const pendingProposals = (ctxObj.agreements || []).filter((a) => a.proposal?.status === 'pending' && (a.proposedToIsYou || isBroadcastTarget(a.parties?.proposedTo?.principal)));
|
|
384
382
|
if (pendingProposals.length > 0) {
|
|
385
383
|
lines.push(`PRIORITY: You have ${pendingProposals.length} pending proposal(s) directed at you. Respond BEFORE other actions.`);
|
|
386
384
|
lines.push(`{"thought": "...", "action": "<action_bound_to_agreement_respond>", "args": {"agreementId": "<id>", "action": "approve|reject"}}\n`);
|
|
@@ -92,7 +92,7 @@ function singleEventToOutcome(ev, ownAgentId) {
|
|
|
92
92
|
const parties = ag?.parties || {};
|
|
93
93
|
const state = result.state;
|
|
94
94
|
// Direct assignment to me (provider) — task spawned, citing an active agreement.
|
|
95
|
-
const isAssignedToMe = (result.providerIsYou || parties.provider === ownAgentId) &&
|
|
95
|
+
const isAssignedToMe = (result.providerIsYou || parties.provider?.actor === ownAgentId) &&
|
|
96
96
|
(state === 'active' || state === 'in-progress');
|
|
97
97
|
if (isAssignedToMe) {
|
|
98
98
|
return { kind: 'task-assigned', task };
|
|
@@ -421,7 +421,7 @@ export function applyOutcomeToCtx(ctx, outcome) {
|
|
|
421
421
|
const book = bookForDelegation(ctx);
|
|
422
422
|
if (tid && !(tid in book.pendingSubtasks)) {
|
|
423
423
|
book.pendingSubtasks[tid] = {
|
|
424
|
-
agentId: t.assigneeId ?? t.executorId ?? t.agreement?.parties?.provider ?? null,
|
|
424
|
+
agentId: t.assigneeId ?? t.executorId ?? t.agreement?.parties?.provider?.actor ?? null,
|
|
425
425
|
spawnedAt: Date.now(),
|
|
426
426
|
};
|
|
427
427
|
}
|
|
@@ -465,7 +465,7 @@ export function applyOutcomeToCtx(ctx, outcome) {
|
|
|
465
465
|
if (!existing) {
|
|
466
466
|
book.collectedResults.push({
|
|
467
467
|
taskId,
|
|
468
|
-
agentId: pending?.agentId ?? t?.assigneeId ?? t?.executorId ?? t?.agreement?.parties?.provider ?? null,
|
|
468
|
+
agentId: pending?.agentId ?? t?.assigneeId ?? t?.executorId ?? t?.agreement?.parties?.provider?.actor ?? null,
|
|
469
469
|
status: outcome.status,
|
|
470
470
|
result: payload,
|
|
471
471
|
});
|
|
@@ -479,7 +479,7 @@ export function applyOutcomeToCtx(ctx, outcome) {
|
|
|
479
479
|
existing.status = outcome.status;
|
|
480
480
|
existing.result = payload;
|
|
481
481
|
if (existing.agentId == null) {
|
|
482
|
-
existing.agentId = t?.assigneeId ?? t?.executorId ?? t?.agreement?.parties?.provider ?? null;
|
|
482
|
+
existing.agentId = t?.assigneeId ?? t?.executorId ?? t?.agreement?.parties?.provider?.actor ?? null;
|
|
483
483
|
}
|
|
484
484
|
}
|
|
485
485
|
if (agId) {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ToolManager } from '../tools/ToolManager.js';
|
|
2
|
+
/**
|
|
3
|
+
* Add the wake's own mandate to a tool call that declares one and left it blank.
|
|
4
|
+
*
|
|
5
|
+
* Returns the args unchanged whenever there is nothing to say: the tool takes no
|
|
6
|
+
* declaration, the model already made one, or this wake is not running inside a
|
|
7
|
+
* job at all.
|
|
8
|
+
*/
|
|
9
|
+
export declare function withDeclaredMandate(args: Record<string, unknown>, toolName: string | null | undefined, toolManager: ToolManager | undefined, activeAgreementId: string | null | undefined): Record<string, unknown>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fill in the job an agent is working inside, so it does not have to.
|
|
3
|
+
*
|
|
4
|
+
* The consent gate excuses an agreement an agent forms INSIDE a job its human
|
|
5
|
+
* already approved, and it verifies the claim server-side. What was missing was
|
|
6
|
+
* the claim itself: nothing on the agent surface said "I am doing this as part
|
|
7
|
+
* of job X", so every engagement an agent formed arrived as an outsider and
|
|
8
|
+
* waited on a human — including buying the help the job it was hired for needs.
|
|
9
|
+
*
|
|
10
|
+
* The declaration is made at the CALLER, never resolved on the server: the
|
|
11
|
+
* agreements module must not read the tasks module, and a guessed mandate is a
|
|
12
|
+
* false claim the termination cascade would act on destructively. Here the fact
|
|
13
|
+
* is free — the runtime already knows which task this wake is executing and
|
|
14
|
+
* which agreement that task runs under, which is exactly the answer.
|
|
15
|
+
*
|
|
16
|
+
* The model can still name a job itself (and does, when it is working on
|
|
17
|
+
* something other than its assigned task). An explicit value is never
|
|
18
|
+
* overwritten, including an explicit empty string, which reads as "this belongs
|
|
19
|
+
* to no job" — the difference between saying nothing and saying none.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* The parameter a tool uses to carry the declaration.
|
|
23
|
+
*
|
|
24
|
+
* Two names, because the doors differ and each name is true at its own door: on
|
|
25
|
+
* a tool that CREATES an agreement the job becomes that row's parent, so the
|
|
26
|
+
* field is the parent id; on a tool that claims a row somebody else posted
|
|
27
|
+
* nothing is re-parented — the lineage stays with the publisher — and the job is
|
|
28
|
+
* only an authority. Listed together here so the runtime has one place that
|
|
29
|
+
* knows both, rather than a name test at each call site.
|
|
30
|
+
*/
|
|
31
|
+
const MANDATE_PARAM_NAMES = ['mandateAgreementId', 'parentAgreementId'];
|
|
32
|
+
/** The mandate parameter this tool declares, if it declares one. */
|
|
33
|
+
function mandateParamOf(toolName, toolManager) {
|
|
34
|
+
if (!toolName)
|
|
35
|
+
return null;
|
|
36
|
+
const schema = toolManager?.getTool?.(toolName)?.schema;
|
|
37
|
+
const properties = schema?.function?.parameters?.properties;
|
|
38
|
+
if (!properties)
|
|
39
|
+
return null;
|
|
40
|
+
for (const name of MANDATE_PARAM_NAMES) {
|
|
41
|
+
if (Object.prototype.hasOwnProperty.call(properties, name))
|
|
42
|
+
return name;
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Add the wake's own mandate to a tool call that declares one and left it blank.
|
|
48
|
+
*
|
|
49
|
+
* Returns the args unchanged whenever there is nothing to say: the tool takes no
|
|
50
|
+
* declaration, the model already made one, or this wake is not running inside a
|
|
51
|
+
* job at all.
|
|
52
|
+
*/
|
|
53
|
+
export function withDeclaredMandate(args, toolName, toolManager, activeAgreementId) {
|
|
54
|
+
if (!activeAgreementId)
|
|
55
|
+
return args;
|
|
56
|
+
const param = mandateParamOf(toolName, toolManager);
|
|
57
|
+
if (!param)
|
|
58
|
+
return args;
|
|
59
|
+
if (args[param] !== undefined)
|
|
60
|
+
return args;
|
|
61
|
+
return { ...args, [param]: activeAgreementId };
|
|
62
|
+
}
|
package/dist/runtime/runTurn.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { outcomeFromActionResult } from './buildOutcome.js';
|
|
2
2
|
import { runtimeLog } from '../shared/runtimeLog.js';
|
|
3
3
|
import { safeParseJSON } from '../utils/jsonExtractor.js';
|
|
4
|
+
import { withDeclaredMandate } from './mandateDeclaration.js';
|
|
4
5
|
const MAX_TOOL_LOOP_ITERS = 20;
|
|
5
6
|
const MAX_MSG_CHARS = 3500;
|
|
6
7
|
/**
|
|
@@ -260,6 +261,9 @@ export async function runTurn(input) {
|
|
|
260
261
|
args = JSON.parse(tc.function.arguments || '{}');
|
|
261
262
|
}
|
|
262
263
|
catch { }
|
|
264
|
+
// Say which job this is part of, when the tool asks and the model did
|
|
265
|
+
// not. The runtime knows; the model would have to be told.
|
|
266
|
+
args = withDeclaredMandate(args, toolName, toolManager, ctx.activeAgreementId);
|
|
263
267
|
const decision = { action: matchingAction, tool: toolName, args };
|
|
264
268
|
const result = await executeDecision(decision, actionDef, effWithTrace, sessionId, memory, null, addToolUsage);
|
|
265
269
|
toolCallResults.push({ tc, matchingAction, result, args, actionDef });
|
|
@@ -311,6 +315,10 @@ export async function runTurn(input) {
|
|
|
311
315
|
const decision = parsed.decision;
|
|
312
316
|
const chosenAction = decision.action;
|
|
313
317
|
const actionDef = actions[chosenAction];
|
|
318
|
+
// Same mandate declaration as the tool-call path above. Both routes reach
|
|
319
|
+
// the same tools, so filling it on only one would make the excuse depend on
|
|
320
|
+
// which shape the model happened to answer in.
|
|
321
|
+
decision.args = withDeclaredMandate((decision.args ?? {}), actionDef?.tool, toolManager, ctx.activeAgreementId);
|
|
314
322
|
if (decision.thought)
|
|
315
323
|
await emitThought(decision.thought);
|
|
316
324
|
// 2-call streaming: if this is a respond action (has receiverId, no bound tool),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Agreement, type ClaimedKind, type ProposeAgreementData, type ProposeDirectInput, type ProposeBroadcastInput, type PublishOfferPayload, type DelegateAgreementData, type CounterAgreementData, type CreateLinkBody, type ListAgreementsFilters, type GetMyAgreementsFilters, type ChatLinkType, type ArtifactLinkType, type UserRole } from '@ziggs-ai/api-client';
|
|
1
|
+
import { type Agreement, type ClaimedKind, type ClaimOptions, type ProposeAgreementData, type ProposeDirectInput, type ProposeBroadcastInput, type PublishOfferPayload, type DelegateAgreementData, type CounterAgreementData, type CreateLinkBody, type CreateLinkResult, 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);
|
|
@@ -25,15 +25,22 @@ export declare class AgreementService {
|
|
|
25
25
|
* the shared flow; `kind` is the api-client ClaimedKind so this signature
|
|
26
26
|
* can't drift from the flow again (a 'hand-off' addition broke the build).
|
|
27
27
|
*/
|
|
28
|
-
claim(agreementId: string): Promise<{
|
|
28
|
+
claim(agreementId: string, opts?: ClaimOptions): Promise<{
|
|
29
29
|
agreement: Agreement;
|
|
30
30
|
kind: ClaimedKind;
|
|
31
31
|
}>;
|
|
32
32
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
33
|
+
* Connect with the person an agent answers for.
|
|
34
|
+
*
|
|
35
|
+
* `to` is an email or an agent id. A link is with the PERSON either way: an id
|
|
36
|
+
* is only a way to find its owner, and one that answers to an org is refused.
|
|
37
|
+
*
|
|
38
|
+
* The reply says only that the invitation went. Handing back the agreement for
|
|
39
|
+
* an id target while an address target got a bare acknowledgement told the
|
|
40
|
+
* caller more about one kind of target than the other, which made the id form
|
|
41
|
+
* a way to check who has an account.
|
|
35
42
|
*/
|
|
36
|
-
requestLink(
|
|
43
|
+
requestLink(to: string, description?: string): Promise<CreateLinkResult>;
|
|
37
44
|
counter(agreementId: string, counter: CounterAgreementData): Promise<Agreement>;
|
|
38
45
|
getStatus(agreementId: string): Promise<unknown | null>;
|
|
39
46
|
list(filters?: ListAgreementsFilters): Promise<Agreement[]>;
|
|
@@ -46,14 +53,11 @@ export declare class AgreementService {
|
|
|
46
53
|
mine(filters?: GetMyAgreementsFilters): Promise<Agreement[]>;
|
|
47
54
|
get(agreementId: string): Promise<Agreement | null>;
|
|
48
55
|
/**
|
|
49
|
-
*
|
|
56
|
+
* Connect with someone — by email, by agent id, or with a share link. Work
|
|
50
57
|
* agreements are created by `propose` / the marketplace claim routes, which
|
|
51
58
|
* run the term normalisation and consent gates this rail never had.
|
|
52
59
|
*/
|
|
53
|
-
createLink(body: CreateLinkBody): Promise<
|
|
54
|
-
ok: boolean;
|
|
55
|
-
agreement: Agreement;
|
|
56
|
-
}>;
|
|
60
|
+
createLink(body: CreateLinkBody): Promise<CreateLinkResult>;
|
|
57
61
|
revoke(agreementId: string): Promise<{
|
|
58
62
|
ok: boolean;
|
|
59
63
|
agreement: Agreement;
|
|
@@ -43,16 +43,22 @@ export class AgreementService {
|
|
|
43
43
|
* the shared flow; `kind` is the api-client ClaimedKind so this signature
|
|
44
44
|
* can't drift from the flow again (a 'hand-off' addition broke the build).
|
|
45
45
|
*/
|
|
46
|
-
claim(agreementId) {
|
|
47
|
-
return claimOpenAgreement(agreementId, this.creds);
|
|
46
|
+
claim(agreementId, opts = {}) {
|
|
47
|
+
return claimOpenAgreement(agreementId, this.creds, opts);
|
|
48
48
|
}
|
|
49
49
|
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
50
|
+
* Connect with the person an agent answers for.
|
|
51
|
+
*
|
|
52
|
+
* `to` is an email or an agent id. A link is with the PERSON either way: an id
|
|
53
|
+
* is only a way to find its owner, and one that answers to an org is refused.
|
|
54
|
+
*
|
|
55
|
+
* The reply says only that the invitation went. Handing back the agreement for
|
|
56
|
+
* an id target while an address target got a bare acknowledgement told the
|
|
57
|
+
* caller more about one kind of target than the other, which made the id form
|
|
58
|
+
* a way to check who has an account.
|
|
52
59
|
*/
|
|
53
|
-
async requestLink(
|
|
54
|
-
|
|
55
|
-
return agreement;
|
|
60
|
+
async requestLink(to, description) {
|
|
61
|
+
return createLink({ to, ...(description ? { description } : {}) }, this.creds);
|
|
56
62
|
}
|
|
57
63
|
counter(agreementId, counter) {
|
|
58
64
|
return counterAgreement(agreementId, counter || {}, this.creds);
|
|
@@ -76,7 +82,7 @@ export class AgreementService {
|
|
|
76
82
|
return getAgreement(agreementId, this.creds);
|
|
77
83
|
}
|
|
78
84
|
/**
|
|
79
|
-
*
|
|
85
|
+
* Connect with someone — by email, by agent id, or with a share link. Work
|
|
80
86
|
* agreements are created by `propose` / the marketplace claim routes, which
|
|
81
87
|
* run the term normalisation and consent gates this rail never had.
|
|
82
88
|
*/
|
|
@@ -5,7 +5,13 @@ interface TickContext {
|
|
|
5
5
|
marketplace: {
|
|
6
6
|
publishOffer: (payload: PublishOfferPayload) => Promise<unknown>;
|
|
7
7
|
pullOffers: (options?: PullOffersOptions) => Promise<unknown[]>;
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Claim any open broadcast. One verb: the backend routes on which side of
|
|
10
|
+
* the row is open, and a caller cannot work that out for itself — reading
|
|
11
|
+
* the agreement first is party-scoped, and a claimer is by definition not
|
|
12
|
+
* yet a party.
|
|
13
|
+
*/
|
|
14
|
+
claimAgreement: (agreementId: string) => Promise<unknown>;
|
|
9
15
|
};
|
|
10
16
|
}
|
|
11
17
|
interface ProactiveTriggerOptions {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { publishOffer, pullOffers,
|
|
1
|
+
import { publishOffer, pullOffers, claimOpenAgreement } from '@ziggs-ai/api-client';
|
|
2
2
|
import { runtimeLog } from '../../shared/runtimeLog.js';
|
|
3
3
|
export class ProactiveTrigger {
|
|
4
4
|
interval;
|
|
@@ -64,7 +64,7 @@ export class ProactiveTrigger {
|
|
|
64
64
|
marketplace: {
|
|
65
65
|
publishOffer: (payload) => publishOffer(payload, creds),
|
|
66
66
|
pullOffers: (options) => pullOffers(options, creds),
|
|
67
|
-
|
|
67
|
+
claimAgreement: (agreementId) => claimOpenAgreement(agreementId, creds),
|
|
68
68
|
},
|
|
69
69
|
};
|
|
70
70
|
}
|
|
@@ -23,7 +23,7 @@ export { PAYMENT_TOOLS, paymentBalanceTool } from './tier2/paymentTools.js';
|
|
|
23
23
|
export { CONNECTION_TOOLS, connectionProxyTool, connectionListGrantsTool, requestConnectionTool, } from './tier2/connectionTools.js';
|
|
24
24
|
export { CONTEXT_TOOLS, contextReadTool, contextDelegateTool, contextExpandReachTool, contextDiscoverGrantableTool, } from './tier2/contextTools.js';
|
|
25
25
|
export { GRANTS_TOOLS, listGrantsTool } from './tier2/grantsTools.js';
|
|
26
|
-
export { LINK_TOOLS,
|
|
26
|
+
export { LINK_TOOLS, linkProposeTool, linkListTool, } from './tier2/linkTools.js';
|
|
27
27
|
export { ARTIFACT_TOOLS, recordArtifactTool } from './tier2/artifactTools.js';
|
|
28
28
|
export { MCP_CONNECTION_TOOLS, mcpToolCallTool, mcpToolsListTool, } from './tier2/mcpConnectionTools.js';
|
|
29
29
|
export { CHAT_TOOLS, openConversationTool } from './tier2/chatTools.js';
|
|
@@ -30,7 +30,7 @@ export { GRANTS_TOOLS, listGrantsTool } from './tier2/grantsTools.js';
|
|
|
30
30
|
// links (agreements with engagementKind 'link') — cross-org reach.
|
|
31
31
|
///1022: the rail is two tools; propose/claim/revoke ride the
|
|
32
32
|
// agreement grammar.
|
|
33
|
-
export { LINK_TOOLS,
|
|
33
|
+
export { LINK_TOOLS, linkProposeTool, linkListTool, } from './tier2/linkTools.js';
|
|
34
34
|
// deliberate artifact recording (the model decides "record this").
|
|
35
35
|
export { ARTIFACT_TOOLS, recordArtifactTool } from './tier2/artifactTools.js';
|
|
36
36
|
export { MCP_CONNECTION_TOOLS, mcpToolCallTool, mcpToolsListTool, } from './tier2/mcpConnectionTools.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isBroadcastTarget, } from '@ziggs-ai/api-client';
|
|
1
|
+
import { isBroadcastTarget, partyActorIds, } from '@ziggs-ai/api-client';
|
|
2
2
|
import { applyFleetCounterPricing, applyFleetPricingForProtocol, } from '../../../pricing/fleetDefaults.js';
|
|
3
3
|
// the link-summary rule lives in AgreementClient, at the point the
|
|
4
4
|
// document is parsed. Every verb here inherits it through the client instead of
|
|
@@ -66,7 +66,16 @@ cachedAgreements) {
|
|
|
66
66
|
// the one claim verb — request, standing offer, or link invite.
|
|
67
67
|
if (!payload.agreementId)
|
|
68
68
|
throw new Error('agreement-claim requires `agreementId`');
|
|
69
|
-
|
|
69
|
+
// The job this claim is part of, when the caller named one
|
|
70
|
+
// (the runtime fills it from the task being executed). It authorizes the
|
|
71
|
+
// claim; it does not re-parent the row, which stays in the publisher's
|
|
72
|
+
// tree.
|
|
73
|
+
const declaredMandate = payload.mandateAgreementId;
|
|
74
|
+
return await agreementService.claim(payload.agreementId, {
|
|
75
|
+
...(typeof declaredMandate === 'string' && declaredMandate
|
|
76
|
+
? { mandateAgreementId: declaredMandate }
|
|
77
|
+
: {}),
|
|
78
|
+
});
|
|
70
79
|
}
|
|
71
80
|
case 'agreement-counter-proposal':
|
|
72
81
|
if (!payload.agreementId)
|
|
@@ -217,7 +226,7 @@ async function proposeAgreement(services, payload, chatId, _agents, actingAgentI
|
|
|
217
226
|
// no chat, no money; the target owner approves. Folds the old request_link.
|
|
218
227
|
if (payload.engagementKind === 'link') {
|
|
219
228
|
if (isBroadcast) {
|
|
220
|
-
throw new Error('A broadcast link is an invite — use
|
|
229
|
+
throw new Error('A broadcast link is an invite — use link_propose with no target instead; it returns a share link.');
|
|
221
230
|
}
|
|
222
231
|
return services.agreementService.requestLink(payload.proposedTo, payload.description);
|
|
223
232
|
}
|
|
@@ -283,13 +292,10 @@ async function agreementsInChat(services, chatId, cachedAgreements) {
|
|
|
283
292
|
}
|
|
284
293
|
}
|
|
285
294
|
function agIsProvider(a, agentId) {
|
|
286
|
-
|
|
287
|
-
return p['provider'] === agentId || p['providerAgent'] === agentId || a['providerId'] === agentId;
|
|
295
|
+
return a.parties?.provider?.actor === agentId || a['providerId'] === agentId;
|
|
288
296
|
}
|
|
289
297
|
function agIsParty(a, agentId) {
|
|
290
|
-
|
|
291
|
-
return ['provider', 'providerAgent', 'payer', 'proposedTo', 'creator', 'creatorAgent']
|
|
292
|
-
.some((k) => p[k] === agentId);
|
|
298
|
+
return partyActorIds(a.parties).includes(agentId);
|
|
293
299
|
}
|
|
294
300
|
function agActive(a) {
|
|
295
301
|
return !a.status || !TERMINAL_STATUS.has(String(a.status));
|
|
@@ -43,10 +43,15 @@ function handlerFor(toolName) {
|
|
|
43
43
|
return result;
|
|
44
44
|
};
|
|
45
45
|
}
|
|
46
|
-
export const agreementProposeTool = defineTool('agreement_propose', { description: { type: 'string', required: true }, proposedTo: { type: 'string', required: true }, engagementKind: { type: 'string', enum: ['hire', 'service'], description: 'Contract type. Defaults to service. For a bilateral trust link, use link_propose.' }, parentAgreementId: 'string', providerId: { type: 'string', description: AGREEMENT_PROPOSE_PROVIDER_ID_DESCRIPTION }, price: { type: 'number', description: 'Price in POINTS, as an integer of hundredths — 500 means ϟ5.00. Convert both ways: a human asking for "ϟ2 per task" means 200, not 2. ϟ is the points symbol, not a hundredth.' }, lifecycle: lifecycleParam, expiresAt: 'string', maxExecutions: 'integer', billing: { type: 'string', enum: ['total', 'per_task'], description: "How price reads: 'total' (default, escrowed and paid at the end) or 'per_task' (a rate settled as each task completes — standing agreements only, and the default for a hire)." }, agreementDescription: 'string' }, handlerFor('agreement_propose'), { description: 'Propose an agreement — direct, broadcast, or link; there are no separate publish tools. Engagement precedence (posted-first): 1) REUSE an active agreement on matching terms, 2) CLAIM the counterparty\'s listing (marketplace_view → agreement_claim; never counter a listing), 3) POST a request broadcast here when nothing listed fits, 4) direct-propose ONLY for bespoke terms, renegotiation, or commissioning a named counterparty with no listing — most published agents are claim-only and refuse direct proposals with a pointer at their listing. Direction is stated, never guessed: providerId = your own id ("I work, you pay") or providerId = proposedTo ("you work, I pay") on direct proposals. Broadcast with proposedTo="everyone" (fully public) or "org" (your org only): no providerId publishes a REQUEST any agent may claim; providerId = your own id publishes a STANDING OFFER (claiming is agreement_claim; browsing is marketplace_view). For a bilateral trust link, use link_propose
|
|
46
|
+
export const agreementProposeTool = defineTool('agreement_propose', { description: { type: 'string', required: true }, proposedTo: { type: 'string', required: true }, engagementKind: { type: 'string', enum: ['hire', 'service'], description: 'Contract type. Defaults to service. For a bilateral trust link, use link_propose.' }, parentAgreementId: 'string', providerId: { type: 'string', description: AGREEMENT_PROPOSE_PROVIDER_ID_DESCRIPTION }, price: { type: 'number', description: 'Price in POINTS, as an integer of hundredths — 500 means ϟ5.00. Convert both ways: a human asking for "ϟ2 per task" means 200, not 2. ϟ is the points symbol, not a hundredth.' }, lifecycle: lifecycleParam, expiresAt: 'string', maxExecutions: 'integer', billing: { type: 'string', enum: ['total', 'per_task'], description: "How price reads: 'total' (default, escrowed and paid at the end) or 'per_task' (a rate settled as each task completes — standing agreements only, and the default for a hire)." }, agreementDescription: 'string' }, handlerFor('agreement_propose'), { description: 'Propose an agreement — direct, broadcast, or link; there are no separate publish tools. Engagement precedence (posted-first): 1) REUSE an active agreement on matching terms, 2) CLAIM the counterparty\'s listing (marketplace_view → agreement_claim; never counter a listing), 3) POST a request broadcast here when nothing listed fits, 4) direct-propose ONLY for bespoke terms, renegotiation, or commissioning a named counterparty with no listing — most published agents are claim-only and refuse direct proposals with a pointer at their listing. Direction is stated, never guessed: providerId = your own id ("I work, you pay") or providerId = proposedTo ("you work, I pay") on direct proposals. Broadcast with proposedTo="everyone" (fully public) or "org" (your org only): no providerId publishes a REQUEST any agent may claim; providerId = your own id publishes a STANDING OFFER (claiming is agreement_claim; browsing is marketplace_view). For a bilateral trust link, use link_propose: it takes an email or an agent id, or neither for a share link. engagementKind defaults to service. Agreements are STANDING by default (no expiry, unlimited tasks under one agreement); set expiresAt or maxExecutions only when the engagement should end on its own.', isAgreementCreation: true });
|
|
47
47
|
export const agreementSubcontractTool = defineTool('agreement_subcontract', { description: { type: 'string', required: true }, executorId: { type: 'string', required: true }, parentAgreementId: { type: 'string', required: true }, payerId: 'string', price: { type: 'number', description: 'Price in POINTS, as an integer of hundredths — 500 means ϟ5.00. Convert both ways: a human asking for "ϟ2 per task" means 200, not 2. ϟ is the points symbol, not a hundredth.' }, lifecycle: lifecycleParam, expiresAt: 'string', maxExecutions: 'integer', agreementDescription: 'string' }, handlerFor('agreement_subcontract'), { description: 'Delegate work to another agent under an existing parent agreement. Requires parentAgreementId.', isAgreementCreation: true });
|
|
48
48
|
export const agreementRespondTool = defineTool('agreement_respond', { agreementId: { type: 'string', required: true }, action: { type: 'string', required: true, enum: ['approve', 'reject'] } }, handlerFor('agreement_respond'), { description: 'Approve or reject a pending DIRECT agreement proposal addressed to you. Open broadcasts (requests, standing offers, link invites) have no personal approval slot — claim those with agreement_claim instead, or ignore them to pass. ONE exception: a hand-off that pins YOU as provider carries your pending approval even as an open broadcast — approving consents to serving whoever claims it (the row stays open for claims); rejecting cancels the hand-off. Ignoring it leaves the hand-off unclaimable, so always respond.' });
|
|
49
|
-
export const agreementClaimTool = defineTool('agreement_claim', {
|
|
49
|
+
export const agreementClaimTool = defineTool('agreement_claim', {
|
|
50
|
+
agreementId: { type: 'string', required: true, description: 'The open agreement to claim (request / offer / link invite id)' },
|
|
51
|
+
// The job this claim is part of. Filled in from the task this wake is
|
|
52
|
+
// executing when the agent does not name one itself.
|
|
53
|
+
mandateAgreementId: { type: 'string', description: 'The active agreement whose work this claim is part of — the job you are doing. Claiming for a job your human already approved needs no second approval from them, but you have to name the job: the server checks you are a party to it, and a wrong name simply earns nothing. Leave it out when the claim belongs to no job.' },
|
|
54
|
+
}, handlerFor('agreement_claim'), { description: 'Claim an open broadcast agreement by id — the DEFAULT way to engage: terms are already posted, consent is the claim, activation is instant, no negotiation turns. Claims a request (you do the work, the publisher pays), a standing offer (you buy, the publisher works), a hand-off (providerPinned: the publisher\'s hired agent works FOR you — you become the customer, and the payer when it is priced), or a link invite (bilateral trust forms immediately). You become the open party and the agreement activates. A hand-off is claimable only after its provider has accepted (409 until then). Find requests/offers with marketplace_view; listings are take-it-or-leave-it — never counter one. Direct proposals are approved with agreement_respond instead, not claimed.' });
|
|
50
55
|
export const taskSetResultTool = defineTool('task_set_result', {
|
|
51
56
|
taskId: { type: 'string', required: true },
|
|
52
57
|
// same param name as MCP ziggs_task_set_result / PATCH /tasks/:id/state.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { ToolDefinition } from '../../../tools/defineTool.js';
|
|
2
|
-
export declare const
|
|
2
|
+
export declare const linkProposeTool: ToolDefinition;
|
|
3
3
|
export declare const linkListTool: ToolDefinition;
|
|
4
4
|
export declare const LINK_TOOLS: ToolDefinition[];
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import { LINK_CAPABILITIES,
|
|
1
|
+
import { LINK_CAPABILITIES, listLinksCapability, proposeLinkCapability, } from '@ziggs-ai/api-client';
|
|
2
2
|
import { toolFromCapability, toolsFromCapabilities } from '../../../tools/capabilityAdapter.js';
|
|
3
|
-
// links are agreements with engagementKind 'link':
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
3
|
+
// links are agreements with engagementKind 'link': bilateral trust between two
|
|
4
|
+
// PEOPLE that gives either side reach to the other. Defined once in api-client's
|
|
5
|
+
// shared capability layer (including the linkSummary shaping); this file only
|
|
6
|
+
// adapts them onto the SDK.
|
|
7
7
|
//
|
|
8
|
-
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
|
|
8
|
+
// Two tools. There were three: `link_create_invite` was `link_propose` with a
|
|
9
|
+
// different way of naming the other side, so a caller had to choose between them
|
|
10
|
+
// before knowing which applied. One verb takes an email, an agent id, or
|
|
11
|
+
// nothing. The agreement verbs carry the rest: claim an invite with
|
|
12
|
+
// agreement_claim, end a link with agreement_revoke.
|
|
13
|
+
export const linkProposeTool = toolFromCapability(proposeLinkCapability);
|
|
12
14
|
export const linkListTool = toolFromCapability(listLinksCapability);
|
|
13
15
|
export const LINK_TOOLS = toolsFromCapabilities(LINK_CAPABILITIES);
|
package/dist/types.d.ts
CHANGED
|
@@ -4,15 +4,11 @@
|
|
|
4
4
|
* One outcome vocabulary, one ctx shape, one transition signature.
|
|
5
5
|
* Every state — parked or thinking — re-enters with exactly one Outcome.
|
|
6
6
|
*/
|
|
7
|
+
import type { AgreementParties } from '@ziggs-ai/api-client';
|
|
7
8
|
import type { AgentNeed } from './tools/needs.js';
|
|
8
9
|
export interface AgreementRef {
|
|
9
10
|
agreementId: string;
|
|
10
|
-
parties?:
|
|
11
|
-
creator?: string;
|
|
12
|
-
provider?: string;
|
|
13
|
-
payer?: string;
|
|
14
|
-
proposedTo?: string;
|
|
15
|
-
};
|
|
11
|
+
parties?: AgreementParties;
|
|
16
12
|
proposal?: {
|
|
17
13
|
status?: 'pending' | 'approved' | 'rejected' | 'cancelled';
|
|
18
14
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ziggs-ai/agent-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
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.
|
|
29
|
+
"@ziggs-ai/api-client": "0.12.0",
|
|
30
30
|
"ajv": "^8.17.1",
|
|
31
31
|
"dotenv": "^17.2.3",
|
|
32
32
|
"openai": "^4.0.0",
|