@ziggs-ai/agent-sdk 0.6.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AgentHost.d.ts +1 -1
- package/dist/agent/ClaudeSdkAgent.js +1 -1
- package/dist/agent/claudeCognition.js +1 -1
- package/dist/agent/claudeSdkProtocol.d.ts +1 -1
- package/dist/agent/claudeSdkProtocol.js +4 -3
- package/dist/agent/claudeSdkTools.d.ts +3 -2
- package/dist/agent/claudeSdkTools.js +48 -11
- package/dist/context/routingLabels.js +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -2
- package/dist/runtime/PromptBuilder.js +13 -6
- package/dist/runtime/bootstrapCtx.d.ts +2 -2
- package/dist/runtime/bootstrapCtx.js +13 -6
- package/dist/runtime/buildOutcome.d.ts +3 -2
- package/dist/runtime/buildOutcome.js +46 -26
- package/dist/runtime/deriveRespondsToProposals.d.ts +1 -1
- package/dist/runtime/deriveRespondsToProposals.js +1 -1
- package/dist/runtime/kickoff.js +8 -8
- package/dist/runtime/runTurn.js +42 -7
- package/dist/runtime/taskBookkeeping.d.ts +35 -0
- package/dist/runtime/taskBookkeeping.js +149 -0
- package/dist/runtime/taskIntake.js +6 -6
- package/dist/server/ZiggsEffectHandler.d.ts +1 -1
- package/dist/server/ZiggsEffectHandler.js +9 -5
- package/dist/server/agreements/AgreementService.d.ts +13 -0
- package/dist/server/agreements/AgreementService.js +16 -1
- package/dist/server/inboxLoop.js +13 -0
- package/dist/server/tools/index.d.ts +2 -2
- package/dist/server/tools/index.js +6 -4
- package/dist/server/tools/tier1/protocolRunner.d.ts +2 -2
- package/dist/server/tools/tier1/protocolRunner.js +58 -7
- package/dist/server/tools/tier1/protocolTools.d.ts +5 -4
- package/dist/server/tools/tier1/protocolTools.js +33 -10
- package/dist/server/tools/tier2/chatTools.js +1 -1
- package/dist/server/tools/tier2/connectionTools.js +2 -2
- package/dist/server/tools/tier2/linkTools.d.ts +2 -5
- package/dist/server/tools/tier2/linkTools.js +7 -6
- package/dist/server/tools/tier2/marketplaceTools.d.ts +1 -1
- package/dist/server/tools/tier2/marketplaceTools.js +9 -22
- package/dist/tasks/protocolRegistry.d.ts +5 -4
- package/dist/tasks/protocolRegistry.js +5 -4
- package/dist/tools/recordReport.d.ts +1 -1
- package/dist/types.d.ts +36 -8
- package/package.json +2 -2
package/dist/runtime/runTurn.js
CHANGED
|
@@ -377,10 +377,18 @@ export async function runTurn(input) {
|
|
|
377
377
|
lastActionDef = actionDef;
|
|
378
378
|
}
|
|
379
379
|
catch (err) {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
380
|
+
if (isReplySlotTaken(err)) {
|
|
381
|
+
runtimeLog.warn('runTurn', `reply already persisted under ${streamMessageId} (idempotency 409) — a replay or second host answered this wake first; dropping duplicate`);
|
|
382
|
+
allEmittedEvents.push({ type: 'message_duplicate_skipped', receiverId: decision.receiverId, chatId: sessionId, senderId: 'system' });
|
|
383
|
+
lastAction = chosenAction;
|
|
384
|
+
lastActionDef = actionDef;
|
|
385
|
+
}
|
|
386
|
+
else {
|
|
387
|
+
// Both the streaming path AND the non-streaming fallback failed —
|
|
388
|
+
// the user got nothing. End the turn with a loud send-failed error.
|
|
389
|
+
runtimeLog.error('runTurn', `respond send failed after stream fallback: ${err.message}`);
|
|
390
|
+
turnError = { source: 'protocol', cause: 'send-failed' };
|
|
391
|
+
}
|
|
384
392
|
}
|
|
385
393
|
break;
|
|
386
394
|
}
|
|
@@ -560,7 +568,7 @@ function annotateToolCallDedup(toolCalls, toolManager, initialSawCreate = false)
|
|
|
560
568
|
sawCreate = true;
|
|
561
569
|
return { tc, skipExecute: false };
|
|
562
570
|
}
|
|
563
|
-
if (name === '
|
|
571
|
+
if (name === 'task_set_result') {
|
|
564
572
|
let taskId = null;
|
|
565
573
|
try {
|
|
566
574
|
const a = JSON.parse(tc.function.arguments || '{}');
|
|
@@ -569,7 +577,7 @@ function annotateToolCallDedup(toolCalls, toolManager, initialSawCreate = false)
|
|
|
569
577
|
catch { }
|
|
570
578
|
const key = taskId || tc.id || '_';
|
|
571
579
|
if (seenUpdateTaskIds.has(key))
|
|
572
|
-
return { tc, skipExecute: true, skipReason: 'duplicate
|
|
580
|
+
return { tc, skipExecute: true, skipReason: 'duplicate task_set_result for same taskId' };
|
|
573
581
|
seenUpdateTaskIds.add(key);
|
|
574
582
|
return { tc, skipExecute: false };
|
|
575
583
|
}
|
|
@@ -652,6 +660,20 @@ function truncateMsg(msg) {
|
|
|
652
660
|
return msg;
|
|
653
661
|
return msg.slice(0, MAX_MSG_CHARS) + `\n\n[truncated to ${MAX_MSG_CHARS} of ${msg.length} chars]`;
|
|
654
662
|
}
|
|
663
|
+
/**
|
|
664
|
+
* A send rejected with 409 hit the backend's (chatId, messageId) idempotency
|
|
665
|
+
* gate on a wake-derived id: this wake's reply already exists. Two legal ways
|
|
666
|
+
* that happens — a crash-replay re-ran the wake, or a second host raced the
|
|
667
|
+
* same at-least-once delivery — and in both the re-run LLM produced different
|
|
668
|
+
* text, so the backend rejects instead of no-op'ing (ZIG-932 treats
|
|
669
|
+
* same-key-same-content as replay, same-key-different-content as conflict).
|
|
670
|
+
* The user already has the winner's reply. Re-sending under a fresh id would
|
|
671
|
+
* double-message; failing the turn marks an answered wake as an error. Treat
|
|
672
|
+
* it as delivered-by-the-other-attempt and move on.
|
|
673
|
+
*/
|
|
674
|
+
function isReplySlotTaken(err) {
|
|
675
|
+
return err?.status === 409;
|
|
676
|
+
}
|
|
655
677
|
async function executeSendMessage(decision, eff, sessionId) {
|
|
656
678
|
// Callers (executeDecision) guarantee message + receiverId, and sessionId is
|
|
657
679
|
// always ctx.identity.sessionId. A send failure throws to the turn loop,
|
|
@@ -662,7 +684,15 @@ async function executeSendMessage(decision, eff, sessionId) {
|
|
|
662
684
|
// to the current session otherwise.
|
|
663
685
|
const targetSessionId = decision.chatId || sessionId;
|
|
664
686
|
const safeMessage = truncateMsg(decision.message);
|
|
665
|
-
|
|
687
|
+
try {
|
|
688
|
+
await eff({ kind: 'send-message', sessionId: targetSessionId, receiverId: decision.receiverId, text: safeMessage });
|
|
689
|
+
}
|
|
690
|
+
catch (err) {
|
|
691
|
+
if (!isReplySlotTaken(err))
|
|
692
|
+
throw err;
|
|
693
|
+
runtimeLog.warn('runTurn', `reply already persisted for this wake (idempotency 409) — a replay or second host answered first; dropping duplicate to ${decision.receiverId}`);
|
|
694
|
+
return [{ type: 'message_duplicate_skipped', receiverId: decision.receiverId, chatId: targetSessionId, senderId: 'system' }];
|
|
695
|
+
}
|
|
666
696
|
const events = [{ type: 'message_sent', receiverId: decision.receiverId, chatId: targetSessionId, message: safeMessage, senderId: 'system' }];
|
|
667
697
|
if (targetSessionId !== sessionId)
|
|
668
698
|
events.push({ type: 'report_sent', message: 'Reported to owner.', senderId: 'system' });
|
|
@@ -693,6 +723,11 @@ async function executeSendMessages(decision, eff, sessionId) {
|
|
|
693
723
|
await eff({ kind: 'send-message', sessionId: targetSessionId, receiverId: msg.receiverId, text: safeMessage });
|
|
694
724
|
}
|
|
695
725
|
catch (err) {
|
|
726
|
+
if (isReplySlotTaken(err)) {
|
|
727
|
+
runtimeLog.warn('runTurn', `reply already persisted for this wake (idempotency 409) — dropping duplicate to ${msg.receiverId}`);
|
|
728
|
+
events.push({ type: 'message_duplicate_skipped', receiverId: msg.receiverId, chatId: targetSessionId, senderId: 'system' });
|
|
729
|
+
continue;
|
|
730
|
+
}
|
|
696
731
|
// Scenario: backend rejected or transport dropped this one send. Record
|
|
697
732
|
// it and keep going — remaining recipients must still get their message.
|
|
698
733
|
failures.push({ receiverId: msg.receiverId, error: err.message });
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Ctx, TaskBookkeeping } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Reserved key for coordinator-side pending/collected state when the lane has
|
|
4
|
+
* an active agreement but no assigned parent task yet (kickoff → spawn).
|
|
5
|
+
* Never deleted by task-closed of a real taskId (ZIG-977).
|
|
6
|
+
*/
|
|
7
|
+
export declare const LANE_BOOK_KEY = "__lane__";
|
|
8
|
+
export declare function emptyTaskBook(agreementId?: string | null): TaskBookkeeping;
|
|
9
|
+
export declare function ensureTaskBook(ctx: Ctx, taskId: string, agreementId?: string | null): TaskBookkeeping;
|
|
10
|
+
/** Focus task's book, or null when nothing is focused / missing. */
|
|
11
|
+
export declare function focusTaskBook(ctx: Ctx): TaskBookkeeping | null;
|
|
12
|
+
/**
|
|
13
|
+
* Book that receives task-delegated / subtask-finished when we know the parent
|
|
14
|
+
* engagement: focus task if assigned, else the coordinator lane bucket.
|
|
15
|
+
*/
|
|
16
|
+
export declare function bookForDelegation(ctx: Ctx): TaskBookkeeping;
|
|
17
|
+
/** Find which engagement is awaiting this child taskId. */
|
|
18
|
+
export declare function findBookHoldingPending(ctx: Ctx, childTaskId: string): TaskBookkeeping | null;
|
|
19
|
+
export declare function allCollectedResults(ctx: Ctx): TaskBookkeeping['collectedResults'];
|
|
20
|
+
export declare function allPendingSubtasks(ctx: Ctx): Record<string, TaskBookkeeping['pendingSubtasks'][string]>;
|
|
21
|
+
export declare function allDelegatedAgreementIds(ctx: Ctx): string[];
|
|
22
|
+
export declare function hasOpenTaskForAgreement(ctx: Ctx, agreementId: string): boolean;
|
|
23
|
+
export declare function hasDelegatedAgreement(ctx: Ctx, agreementId: string): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Remove one task's bookkeeping and retarget focus if we just closed the
|
|
26
|
+
* focused task. Leaves sibling entries (and the coordinator lane bucket) alone.
|
|
27
|
+
*/
|
|
28
|
+
export declare function removeTaskBook(ctx: Ctx, taskId: string): void;
|
|
29
|
+
/** Refresh deprecated aggregate views from the per-task map. */
|
|
30
|
+
export declare function syncAggregateTaskViews(ctx: Ctx): void;
|
|
31
|
+
/**
|
|
32
|
+
* Migrate a pre-ZIG-977 persisted ctx (lane-level pendingSubtasks /
|
|
33
|
+
* collectedResults / delegatedAgreementIds) into the tasks map.
|
|
34
|
+
*/
|
|
35
|
+
export declare function migrateLegacyTaskFields(existing: Record<string, unknown>): Record<string, TaskBookkeeping>;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reserved key for coordinator-side pending/collected state when the lane has
|
|
3
|
+
* an active agreement but no assigned parent task yet (kickoff → spawn).
|
|
4
|
+
* Never deleted by task-closed of a real taskId (ZIG-977).
|
|
5
|
+
*/
|
|
6
|
+
export const LANE_BOOK_KEY = '__lane__';
|
|
7
|
+
export function emptyTaskBook(agreementId = null) {
|
|
8
|
+
return {
|
|
9
|
+
agreementId,
|
|
10
|
+
pendingSubtasks: {},
|
|
11
|
+
collectedResults: [],
|
|
12
|
+
delegatedAgreementIds: [],
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export function ensureTaskBook(ctx, taskId, agreementId) {
|
|
16
|
+
if (!ctx.tasks)
|
|
17
|
+
ctx.tasks = {};
|
|
18
|
+
let book = ctx.tasks[taskId];
|
|
19
|
+
if (!book) {
|
|
20
|
+
book = emptyTaskBook(agreementId ?? null);
|
|
21
|
+
ctx.tasks[taskId] = book;
|
|
22
|
+
}
|
|
23
|
+
else if (agreementId && !book.agreementId) {
|
|
24
|
+
book.agreementId = agreementId;
|
|
25
|
+
}
|
|
26
|
+
return book;
|
|
27
|
+
}
|
|
28
|
+
/** Focus task's book, or null when nothing is focused / missing. */
|
|
29
|
+
export function focusTaskBook(ctx) {
|
|
30
|
+
if (!ctx.activeTaskId)
|
|
31
|
+
return null;
|
|
32
|
+
return ctx.tasks?.[ctx.activeTaskId] ?? null;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Book that receives task-delegated / subtask-finished when we know the parent
|
|
36
|
+
* engagement: focus task if assigned, else the coordinator lane bucket.
|
|
37
|
+
*/
|
|
38
|
+
export function bookForDelegation(ctx) {
|
|
39
|
+
if (ctx.activeTaskId) {
|
|
40
|
+
return ensureTaskBook(ctx, ctx.activeTaskId, ctx.activeAgreementId);
|
|
41
|
+
}
|
|
42
|
+
const book = ensureTaskBook(ctx, LANE_BOOK_KEY, ctx.activeAgreementId);
|
|
43
|
+
if (ctx.activeAgreementId && !book.agreementId)
|
|
44
|
+
book.agreementId = ctx.activeAgreementId;
|
|
45
|
+
return book;
|
|
46
|
+
}
|
|
47
|
+
/** Find which engagement is awaiting this child taskId. */
|
|
48
|
+
export function findBookHoldingPending(ctx, childTaskId) {
|
|
49
|
+
if (!ctx.tasks)
|
|
50
|
+
return null;
|
|
51
|
+
for (const book of Object.values(ctx.tasks)) {
|
|
52
|
+
if (childTaskId in book.pendingSubtasks)
|
|
53
|
+
return book;
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
export function allCollectedResults(ctx) {
|
|
58
|
+
if (!ctx.tasks)
|
|
59
|
+
return [];
|
|
60
|
+
const out = [];
|
|
61
|
+
for (const book of Object.values(ctx.tasks)) {
|
|
62
|
+
out.push(...book.collectedResults);
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
export function allPendingSubtasks(ctx) {
|
|
67
|
+
if (!ctx.tasks)
|
|
68
|
+
return {};
|
|
69
|
+
const out = {};
|
|
70
|
+
for (const book of Object.values(ctx.tasks)) {
|
|
71
|
+
Object.assign(out, book.pendingSubtasks);
|
|
72
|
+
}
|
|
73
|
+
return out;
|
|
74
|
+
}
|
|
75
|
+
export function allDelegatedAgreementIds(ctx) {
|
|
76
|
+
if (!ctx.tasks)
|
|
77
|
+
return [];
|
|
78
|
+
const ids = [];
|
|
79
|
+
for (const book of Object.values(ctx.tasks)) {
|
|
80
|
+
for (const id of book.delegatedAgreementIds) {
|
|
81
|
+
if (!ids.includes(id))
|
|
82
|
+
ids.push(id);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return ids;
|
|
86
|
+
}
|
|
87
|
+
export function hasOpenTaskForAgreement(ctx, agreementId) {
|
|
88
|
+
if (!agreementId || !ctx.tasks)
|
|
89
|
+
return false;
|
|
90
|
+
return Object.entries(ctx.tasks).some(([key, book]) => key !== LANE_BOOK_KEY && book.agreementId === agreementId);
|
|
91
|
+
}
|
|
92
|
+
export function hasDelegatedAgreement(ctx, agreementId) {
|
|
93
|
+
return allDelegatedAgreementIds(ctx).includes(agreementId);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Remove one task's bookkeeping and retarget focus if we just closed the
|
|
97
|
+
* focused task. Leaves sibling entries (and the coordinator lane bucket) alone.
|
|
98
|
+
*/
|
|
99
|
+
export function removeTaskBook(ctx, taskId) {
|
|
100
|
+
if (!taskId || !ctx.tasks)
|
|
101
|
+
return;
|
|
102
|
+
delete ctx.tasks[taskId];
|
|
103
|
+
if (ctx.activeTaskId === taskId) {
|
|
104
|
+
const nextId = Object.keys(ctx.tasks).find((id) => id !== LANE_BOOK_KEY) ?? null;
|
|
105
|
+
ctx.activeTaskId = nextId;
|
|
106
|
+
ctx.activeAgreementId = nextId ? (ctx.tasks[nextId]?.agreementId ?? null) : null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/** Refresh deprecated aggregate views from the per-task map. */
|
|
110
|
+
export function syncAggregateTaskViews(ctx) {
|
|
111
|
+
ctx.pendingSubtasks = allPendingSubtasks(ctx);
|
|
112
|
+
ctx.collectedResults = allCollectedResults(ctx);
|
|
113
|
+
ctx.delegatedAgreementIds = allDelegatedAgreementIds(ctx);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Migrate a pre-ZIG-977 persisted ctx (lane-level pendingSubtasks /
|
|
117
|
+
* collectedResults / delegatedAgreementIds) into the tasks map.
|
|
118
|
+
*/
|
|
119
|
+
export function migrateLegacyTaskFields(existing) {
|
|
120
|
+
if (existing.tasks && typeof existing.tasks === 'object') {
|
|
121
|
+
return existing.tasks;
|
|
122
|
+
}
|
|
123
|
+
const tasks = {};
|
|
124
|
+
const activeTaskId = existing.activeTaskId ?? null;
|
|
125
|
+
const activeAgreementId = existing.activeAgreementId ?? null;
|
|
126
|
+
const pendingSubtasks = existing.pendingSubtasks ?? {};
|
|
127
|
+
const collectedResults = existing.collectedResults ?? [];
|
|
128
|
+
const delegatedAgreementIds = existing.delegatedAgreementIds ?? [];
|
|
129
|
+
const hasLegacy = Object.keys(pendingSubtasks).length > 0 ||
|
|
130
|
+
collectedResults.length > 0 ||
|
|
131
|
+
delegatedAgreementIds.length > 0;
|
|
132
|
+
if (activeTaskId) {
|
|
133
|
+
tasks[activeTaskId] = {
|
|
134
|
+
agreementId: activeAgreementId,
|
|
135
|
+
pendingSubtasks,
|
|
136
|
+
collectedResults,
|
|
137
|
+
delegatedAgreementIds: [...delegatedAgreementIds],
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
else if (hasLegacy) {
|
|
141
|
+
tasks[LANE_BOOK_KEY] = {
|
|
142
|
+
agreementId: activeAgreementId,
|
|
143
|
+
pendingSubtasks,
|
|
144
|
+
collectedResults,
|
|
145
|
+
delegatedAgreementIds: [...delegatedAgreementIds],
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
return tasks;
|
|
149
|
+
}
|
|
@@ -35,7 +35,7 @@ export function buildTaskIntakeState(initial) {
|
|
|
35
35
|
prompt: {
|
|
36
36
|
role: 'A task was just assigned to you under one of your active agreements, and your workflow has no explicit route for it. This injected default exists so an assigned task is NEVER silently dropped — under a standing hire, new tasks keep arriving for the lifetime of the agreement.',
|
|
37
37
|
goal: 'Resolve the assigned task this turn: deliver the result if you can produce it, otherwise fail it with a reason.',
|
|
38
|
-
constraints: 'The task is in <event> (and as activeTaskId / activeAgreementId in <machine_context>). If the deliverable can be produced in this turn (an answer, summary, plan, draft — most representation work), call
|
|
38
|
+
constraints: 'The task is in <event> (and as activeTaskId / activeAgreementId in <machine_context>). If the deliverable can be produced in this turn (an answer, summary, plan, draft — most representation work), call task_set_result with state "completed" and the deliverable as result — the result is what the counterparty consumes, so make it the actual deliverable, not a status note. If the task genuinely needs abilities or tools you do not have, call task_set_result with state "failed" and a one-line reason — a visible failure the counterparty can re-route beats a silent drop. Never leave the task dangling or answer with a chat message alone.',
|
|
39
39
|
},
|
|
40
40
|
actions: {
|
|
41
41
|
...dflt.actions,
|
|
@@ -43,10 +43,10 @@ export function buildTaskIntakeState(initial) {
|
|
|
43
43
|
prompt: {
|
|
44
44
|
when: 'You can produce the deliverable for the assigned task in this turn.',
|
|
45
45
|
instruction: 'Complete the assigned task, with the deliverable itself as the result.',
|
|
46
|
-
format: '{"taskId": "<assigned task id from <event>>", "
|
|
46
|
+
format: '{"taskId": "<assigned task id from <event>>", "state": "completed", "result": "<the full deliverable>"}',
|
|
47
47
|
},
|
|
48
|
-
tool: '
|
|
49
|
-
// Function form so a failed
|
|
48
|
+
tool: 'task_set_result',
|
|
49
|
+
// Function form so a failed task_set_result surfaces as an error outcome
|
|
50
50
|
// (→ dflt error transition) instead of a phantom task-closed.
|
|
51
51
|
produces: (result) => {
|
|
52
52
|
const r = result;
|
|
@@ -59,9 +59,9 @@ export function buildTaskIntakeState(initial) {
|
|
|
59
59
|
prompt: {
|
|
60
60
|
when: 'The task needs abilities, tools, or information you cannot obtain this turn.',
|
|
61
61
|
instruction: 'Fail the assigned task with a one-line reason so the counterparty can re-route it.',
|
|
62
|
-
format: '{"taskId": "<assigned task id from <event>>", "
|
|
62
|
+
format: '{"taskId": "<assigned task id from <event>>", "state": "failed", "result": "<one-line reason>"}',
|
|
63
63
|
},
|
|
64
|
-
tool: '
|
|
64
|
+
tool: 'task_set_result',
|
|
65
65
|
produces: (result) => {
|
|
66
66
|
const r = result;
|
|
67
67
|
const status = r?.task?.state ?? r?.state ?? r?.status;
|
|
@@ -59,7 +59,7 @@ export interface ZiggsEffectDeps {
|
|
|
59
59
|
* side effects — that is the whole point of funneling both paths here.
|
|
60
60
|
*
|
|
61
61
|
* `eff` is threaded into the tool context so eff-backed tools
|
|
62
|
-
* (`
|
|
62
|
+
* (`context_snapshot`, `chat_send`) resolve through this same path
|
|
63
63
|
* regardless of which cognition invoked them.
|
|
64
64
|
*/
|
|
65
65
|
export declare function executeToolWithSideEffects(deps: Pick<ZiggsEffectDeps, 'toolManager' | 'artifactsClient' | 'telemetryClient' | 'telemetryIngest' | 'operatorKey' | 'agentId' | 'taskService' | 'agreementService' | 'contextReadClient' | 'messageSender'>, eff: EffectHandler, effect: Extract<Effect, {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { OutboxBuffer } from './OutboxBuffer.js';
|
|
2
2
|
import { runtimeLog } from '../shared/runtimeLog.js';
|
|
3
|
+
import { artifactScopeForSession } from '@ziggs-ai/api-client';
|
|
3
4
|
import { buildLlmIngestPayload, buildToolIngestPayload, isTelemetryIngestEnabled, sendTelemetry, } from './telemetryIngest.js';
|
|
4
5
|
const CONTEXT_CACHE_MAX = 256;
|
|
5
6
|
/**
|
|
@@ -69,12 +70,15 @@ function previewToolArgs(args) {
|
|
|
69
70
|
* Chat timeline: `Message.tsx` parses `entryType: artifact`, `content_type: operation` with
|
|
70
71
|
* `operation_*:{json}` where json includes `state` and `tool`.
|
|
71
72
|
*/
|
|
72
|
-
async function emitToolOperationArtifact(client,
|
|
73
|
+
async function emitToolOperationArtifact(client, sessionId, phase, payload, idempotencyKey) {
|
|
73
74
|
const prefix = phase === 'started' ? 'operation_started' : phase === 'completed' ? 'operation_completed' : 'operation_error';
|
|
74
75
|
const text = `${prefix}:${JSON.stringify(payload)}`;
|
|
75
76
|
try {
|
|
76
77
|
await client.write({
|
|
77
|
-
|
|
78
|
+
// ZIG-1032: an agreement lane (`agrn-…`) is a routing key, not a chat —
|
|
79
|
+
// passing it as chatId asked the backend to write into a chat that does
|
|
80
|
+
// not exist and 403'd every breadcrumb on agreement-lane work.
|
|
81
|
+
...artifactScopeForSession(sessionId),
|
|
78
82
|
text,
|
|
79
83
|
content_type: 'operation',
|
|
80
84
|
visibility: 'agent-private',
|
|
@@ -98,7 +102,7 @@ async function emitToolOperationArtifact(client, chatId, phase, payload, idempot
|
|
|
98
102
|
* side effects — that is the whole point of funneling both paths here.
|
|
99
103
|
*
|
|
100
104
|
* `eff` is threaded into the tool context so eff-backed tools
|
|
101
|
-
* (`
|
|
105
|
+
* (`context_snapshot`, `chat_send`) resolve through this same path
|
|
102
106
|
* regardless of which cognition invoked them.
|
|
103
107
|
*/
|
|
104
108
|
export async function executeToolWithSideEffects(deps, eff, effect, roster) {
|
|
@@ -214,7 +218,7 @@ export function createZiggsEffectHandler(deps) {
|
|
|
214
218
|
// Grant-fenced aggregate read: the backend composes the ContextSnapshot
|
|
215
219
|
// (GET /context/snapshot) through the same access + temporal fence as
|
|
216
220
|
// the per-type reads. Every producer — ClaudeSdkAgent.tick, runTurn,
|
|
217
|
-
// and the on-demand
|
|
221
|
+
// and the on-demand context_snapshot tool — routes through here.
|
|
218
222
|
const snap = await buildSnapshot(deps, effect.sessionId, deps.agentId, {
|
|
219
223
|
limit: effect.opts?.maxMessages,
|
|
220
224
|
});
|
|
@@ -296,7 +300,7 @@ export function createZiggsEffectHandler(deps) {
|
|
|
296
300
|
}
|
|
297
301
|
else {
|
|
298
302
|
await deps.artifactsClient.write({
|
|
299
|
-
|
|
303
|
+
...artifactScopeForSession(effect.sessionId),
|
|
300
304
|
text: JSON.stringify(effect.entry),
|
|
301
305
|
content_type: String(effect.entry.kind ?? 'event'),
|
|
302
306
|
visibility: 'agent-private',
|
|
@@ -19,6 +19,19 @@ export declare class AgreementService {
|
|
|
19
19
|
publishOffer(payload: PublishOfferPayload): Promise<Agreement>;
|
|
20
20
|
delegate(payload: DelegateAgreementData): Promise<Agreement>;
|
|
21
21
|
respond(agreementId: string, action: 'approve' | 'reject'): Promise<Agreement>;
|
|
22
|
+
/**
|
|
23
|
+
* ZIG-1021 — claim any open broadcast (quest, standing offer, link invite).
|
|
24
|
+
* Routing (offer vs agreement claim endpoint) is decided in the shared flow.
|
|
25
|
+
*/
|
|
26
|
+
claim(agreementId: string): Promise<{
|
|
27
|
+
agreement: Agreement;
|
|
28
|
+
kind: 'link' | 'offer' | 'quest';
|
|
29
|
+
}>;
|
|
30
|
+
/**
|
|
31
|
+
* ZIG-1022 — a link proposal is an agreement (engagementKind 'link') to one
|
|
32
|
+
* agent; the target owner approves. Folds the old request_link tool.
|
|
33
|
+
*/
|
|
34
|
+
requestLink(providerId: string, description?: string): Promise<Agreement>;
|
|
22
35
|
counter(agreementId: string, counter: CounterAgreementData): Promise<Agreement>;
|
|
23
36
|
getStatus(agreementId: string): Promise<unknown | null>;
|
|
24
37
|
list(filters?: ListAgreementsFilters): Promise<Agreement[]>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { proposeAgreement, proposeBroadcast as proposeBroadcastApi, delegateAgreement, respondToAgreement, counterAgreement, getAgreementStatus, listAgreements, getMyAgreements, getAgreement, publishOffer as publishOfferApi, createAgreement, revokeAgreement, fulfillAgreement, linkAgreementToChat, getChatsForAgreement, joinAgreement, linkArtifactToAgreement, getArtifactsForAgreement, linkUserToAgreement, getUsersForAgreement, } from '@ziggs-ai/api-client';
|
|
1
|
+
import { proposeAgreement, proposeBroadcast as proposeBroadcastApi, delegateAgreement, respondToAgreement, counterAgreement, claimOpenAgreement, getAgreementStatus, listAgreements, getMyAgreements, getAgreement, publishOffer as publishOfferApi, createAgreement, revokeAgreement, fulfillAgreement, linkAgreementToChat, getChatsForAgreement, joinAgreement, linkArtifactToAgreement, getArtifactsForAgreement, linkUserToAgreement, getUsersForAgreement, } from '@ziggs-ai/api-client';
|
|
2
2
|
export class AgreementService {
|
|
3
3
|
creds;
|
|
4
4
|
constructor(operatorKey, agentId) {
|
|
@@ -37,6 +37,21 @@ export class AgreementService {
|
|
|
37
37
|
respond(agreementId, action) {
|
|
38
38
|
return respondToAgreement(agreementId, action, this.creds);
|
|
39
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* ZIG-1021 — claim any open broadcast (quest, standing offer, link invite).
|
|
42
|
+
* Routing (offer vs agreement claim endpoint) is decided in the shared flow.
|
|
43
|
+
*/
|
|
44
|
+
claim(agreementId) {
|
|
45
|
+
return claimOpenAgreement(agreementId, this.creds);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* ZIG-1022 — a link proposal is an agreement (engagementKind 'link') to one
|
|
49
|
+
* agent; the target owner approves. Folds the old request_link tool.
|
|
50
|
+
*/
|
|
51
|
+
async requestLink(providerId, description) {
|
|
52
|
+
const { agreement } = await createAgreement({ engagementKind: 'link', providerId, ...(description ? { description } : {}) }, this.creds);
|
|
53
|
+
return agreement;
|
|
54
|
+
}
|
|
40
55
|
counter(agreementId, counter) {
|
|
41
56
|
return counterAgreement(agreementId, counter || {}, this.creds);
|
|
42
57
|
}
|
package/dist/server/inboxLoop.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ContextReadClient, InboxClient, } from '@ziggs-ai/api-client';
|
|
2
2
|
import { runtimeLog } from '../shared/runtimeLog.js';
|
|
3
|
+
import { isRateLimited } from '@ziggs-ai/api-client';
|
|
3
4
|
const READ_PAGE = 100;
|
|
4
5
|
function log(label, msg) {
|
|
5
6
|
runtimeLog.info(label, msg);
|
|
@@ -208,6 +209,18 @@ export function runInboxLoop(deps, opts = {}) {
|
|
|
208
209
|
if (!running)
|
|
209
210
|
break;
|
|
210
211
|
consecutiveErrors++;
|
|
212
|
+
// ZIG-1019: a 429 already told us how long to wait, and every refused
|
|
213
|
+
// call still counts against the cap — so the exponential ladder digs
|
|
214
|
+
// the hole deeper (1s, 2s, 4s… of pure refusals). Wait the server's
|
|
215
|
+
// number instead, and do not let a throttle inflate the ladder for the
|
|
216
|
+
// real errors that come after it.
|
|
217
|
+
const throttleMs = isRateLimited(err) ? (err.retryAfterMs ?? 30_000) : null;
|
|
218
|
+
if (throttleMs !== null) {
|
|
219
|
+
consecutiveErrors = 0;
|
|
220
|
+
warn(label, `inbox loop throttled: ${err?.message ?? err} — waiting ${Math.round(throttleMs)}ms as instructed`);
|
|
221
|
+
await sleep(throttleMs);
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
211
224
|
const backoff = Math.min(ERROR_BACKOFF_MAX_MS, ERROR_BACKOFF_BASE_MS * 2 ** (consecutiveErrors - 1));
|
|
212
225
|
const jitter = backoff * (0.5 + (consecutiveErrors % 7) / 14); // deterministic-ish, no Math.random
|
|
213
226
|
warn(label, `inbox loop pass failed (#${consecutiveErrors}): ${err?.message ?? err} — retrying in ~${Math.round(jitter)}ms`);
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* its `tools:` array. Each tool reads operatorKey/agentId from tool context and
|
|
16
16
|
* wraps a backend HTTP client — no per-agent plumbing.
|
|
17
17
|
*/
|
|
18
|
-
export { PROTOCOL_TOOLS, agreementProposeTool, agreementSubcontractTool, agreementRespondTool,
|
|
18
|
+
export { PROTOCOL_TOOLS, agreementProposeTool, agreementSubcontractTool, agreementRespondTool, agreementClaimTool, agreementCounterTool, agreementGetTool, agreementRevokeTool, agreementFulfillTool, taskCreateTool, taskSetResultTool, taskUpdatePlanStepTool, } from './tier1/protocolTools.js';
|
|
19
19
|
export { dispatchProtocolOp } from './tier1/protocolRunner.js';
|
|
20
20
|
export { DISCOVERY_TOOLS, agentSearchTool, agentGetTool } from './tier2/discoveryTools.js';
|
|
21
21
|
export { MARKETPLACE_TOOLS, marketplaceViewTool } from './tier2/marketplaceTools.js';
|
|
@@ -23,6 +23,6 @@ export { PAYMENT_TOOLS, paymentBalanceTool, paymentTransferTool, paymentWaitForA
|
|
|
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, linkCreateInviteTool, linkListTool, } from './tier2/linkTools.js';
|
|
27
27
|
export { ARTIFACT_TOOLS, recordArtifactTool } from './tier2/artifactTools.js';
|
|
28
28
|
export { CHAT_TOOLS, openConversationTool } from './tier2/chatTools.js';
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* wraps a backend HTTP client — no per-agent plumbing.
|
|
17
17
|
*/
|
|
18
18
|
// ── Tier 1: protocol grammar ────────────────────────────────────────────────
|
|
19
|
-
export { PROTOCOL_TOOLS, agreementProposeTool, agreementSubcontractTool, agreementRespondTool,
|
|
19
|
+
export { PROTOCOL_TOOLS, agreementProposeTool, agreementSubcontractTool, agreementRespondTool, agreementClaimTool, agreementCounterTool, agreementGetTool, agreementRevokeTool, agreementFulfillTool, taskCreateTool, taskSetResultTool, taskUpdatePlanStepTool, } from './tier1/protocolTools.js';
|
|
20
20
|
export { dispatchProtocolOp } from './tier1/protocolRunner.js';
|
|
21
21
|
// ── Tier 2: capability bundles ──────────────────────────────────────────────
|
|
22
22
|
export { DISCOVERY_TOOLS, agentSearchTool, agentGetTool } from './tier2/discoveryTools.js';
|
|
@@ -28,9 +28,11 @@ export { CONTEXT_TOOLS, contextReadTool, contextDelegateTool, contextExpandReach
|
|
|
28
28
|
// payment_list_grants). Always-available on the hosted agent — see CLAUDE_SDK_TOOLS.
|
|
29
29
|
export { GRANTS_TOOLS, listGrantsTool } from './tier2/grantsTools.js';
|
|
30
30
|
// ZIG-898: links (agreements with engagementKind 'link') — cross-org reach.
|
|
31
|
-
|
|
31
|
+
// ZIG-1021/1022: the rail is two tools; propose/claim/revoke ride the
|
|
32
|
+
// agreement grammar.
|
|
33
|
+
export { LINK_TOOLS, linkCreateInviteTool, linkListTool, } from './tier2/linkTools.js';
|
|
32
34
|
// ZIG-899: deliberate artifact recording (the model decides "record this").
|
|
33
35
|
export { ARTIFACT_TOOLS, recordArtifactTool } from './tier2/artifactTools.js';
|
|
34
|
-
// ZIG-900 decision:
|
|
35
|
-
// read (
|
|
36
|
+
// ZIG-900 decision: chat_open only — chat listing stays a context-rail
|
|
37
|
+
// read (grant_list scopeKind=chat), no duplicate lister (ZIG-626 rule).
|
|
36
38
|
export { CHAT_TOOLS, openConversationTool } from './tier2/chatTools.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AgreementService } from '../../agreements/AgreementService.js';
|
|
2
2
|
import type { TaskService } from '../../tasks/TaskService.js';
|
|
3
3
|
import type { WakeWriteKeys } from '../../../types.js';
|
|
4
|
-
import type
|
|
4
|
+
import { type ContextReadClient } from '@ziggs-ai/api-client';
|
|
5
5
|
interface Services {
|
|
6
6
|
agreementService: AgreementService;
|
|
7
7
|
taskService: TaskService;
|
|
@@ -30,7 +30,7 @@ interface ProtocolPayload {
|
|
|
30
30
|
plan?: unknown;
|
|
31
31
|
planReviewTiming?: string;
|
|
32
32
|
requireMidWorkPlanAck?: boolean;
|
|
33
|
-
engagementKind?: 'hire' | 'service';
|
|
33
|
+
engagementKind?: 'hire' | 'service' | 'link';
|
|
34
34
|
subtasks?: (string | {
|
|
35
35
|
description?: string;
|
|
36
36
|
})[];
|
|
@@ -1,4 +1,17 @@
|
|
|
1
|
+
import { linkSummary, } from '@ziggs-ai/api-client';
|
|
1
2
|
import { applyFleetCounterPricing, applyFleetPricingForProtocol, } from '../../../pricing/fleetDefaults.js';
|
|
3
|
+
/**
|
|
4
|
+
* ZIG-957 — same summary shape as MCP link mutations / link_* tools.
|
|
5
|
+
* Raw Mongo money/approvals noise must not reach the LLM on a trust link.
|
|
6
|
+
*/
|
|
7
|
+
function shapeAgreementResult(agreement) {
|
|
8
|
+
if (!agreement)
|
|
9
|
+
return agreement;
|
|
10
|
+
return agreement.engagementKind === 'link' ? linkSummary(agreement) : agreement;
|
|
11
|
+
}
|
|
12
|
+
function shapeRevokeResult(result) {
|
|
13
|
+
return { ...result, agreement: shapeAgreementResult(result.agreement) };
|
|
14
|
+
}
|
|
2
15
|
// Map legacy task-state aliases onto the canonical set.
|
|
3
16
|
function normalizeState(state) {
|
|
4
17
|
if (state === 'done')
|
|
@@ -49,7 +62,17 @@ export async function dispatchProtocolOp(services, payload, chatId, agents = [],
|
|
|
49
62
|
case 'agreement-respond':
|
|
50
63
|
if (!payload.agreementId || !payload.action)
|
|
51
64
|
return null;
|
|
52
|
-
return agreementService.respond(payload.agreementId, payload.action);
|
|
65
|
+
return shapeAgreementResult(await agreementService.respond(payload.agreementId, payload.action));
|
|
66
|
+
case 'agreement-claim': {
|
|
67
|
+
// ZIG-1021: the one claim verb — quest, standing offer, or link invite.
|
|
68
|
+
if (!payload.agreementId)
|
|
69
|
+
throw new Error('agreement-claim requires `agreementId`');
|
|
70
|
+
const claimed = await agreementService.claim(payload.agreementId);
|
|
71
|
+
return {
|
|
72
|
+
...claimed,
|
|
73
|
+
agreement: shapeAgreementResult(claimed.agreement),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
53
76
|
case 'agreement-counter-proposal':
|
|
54
77
|
if (!payload.agreementId)
|
|
55
78
|
return null;
|
|
@@ -76,11 +99,11 @@ export async function dispatchProtocolOp(services, payload, chatId, agents = [],
|
|
|
76
99
|
case 'agreement-revoke':
|
|
77
100
|
if (!payload.agreementId)
|
|
78
101
|
return null;
|
|
79
|
-
return agreementService.revoke(payload.agreementId);
|
|
102
|
+
return shapeRevokeResult(await agreementService.revoke(payload.agreementId));
|
|
80
103
|
case 'agreement-fulfill':
|
|
81
104
|
if (!payload.agreementId)
|
|
82
105
|
return null;
|
|
83
|
-
return agreementService.fulfill(payload.agreementId);
|
|
106
|
+
return shapeRevokeResult(await agreementService.fulfill(payload.agreementId));
|
|
84
107
|
case 'task-spawn': {
|
|
85
108
|
if (!payload.agreementId || !payload.description)
|
|
86
109
|
return null;
|
|
@@ -160,9 +183,14 @@ export async function dispatchProtocolOp(services, payload, chatId, agents = [],
|
|
|
160
183
|
});
|
|
161
184
|
}
|
|
162
185
|
case 'task-update': {
|
|
163
|
-
|
|
186
|
+
// ZIG-957: canonical tool param is `state` (MCP + REST). Accept legacy
|
|
187
|
+
// `status` so fleet prompts that still emit it keep working.
|
|
188
|
+
const terminalRaw = (typeof payload.state === 'string' && payload.state) ||
|
|
189
|
+
(typeof payload.status === 'string' && payload.status) ||
|
|
190
|
+
'';
|
|
191
|
+
if (!payload.taskId || !terminalRaw)
|
|
164
192
|
return null;
|
|
165
|
-
const state = normalizeState(
|
|
193
|
+
const state = normalizeState(terminalRaw);
|
|
166
194
|
// Wake-derived idempotency key: a crash-replay re-issuing this transition
|
|
167
195
|
// no-ops server-side instead of erroring on the already-terminal task.
|
|
168
196
|
return taskService.updateState(payload.taskId, state, payload.result, wakeKeys?.taskStateKey(payload.taskId, state));
|
|
@@ -192,13 +220,36 @@ async function proposeAgreement(services, payload, chatId, _agents, actingAgentI
|
|
|
192
220
|
if (!payload.description)
|
|
193
221
|
throw new Error('agreement-propose requires `description`');
|
|
194
222
|
if (!payload.proposedTo)
|
|
195
|
-
throw new Error('agreement-propose requires `proposedTo` (userId or agentId). To
|
|
223
|
+
throw new Error('agreement-propose requires `proposedTo` (userId or agentId). To create a task under an existing agreement, use `task_create` instead.');
|
|
196
224
|
const isBroadcast = payload.proposedTo === 'everyone' || payload.proposedTo === 'org';
|
|
225
|
+
// ZIG-1022: a link proposal is an agreement with engagementKind 'link' —
|
|
226
|
+
// no chat, no money; the target owner approves. Folds the old request_link.
|
|
227
|
+
if (payload.engagementKind === 'link') {
|
|
228
|
+
if (isBroadcast) {
|
|
229
|
+
throw new Error('A broadcast link is an invite — use link_create_invite instead; propose links to one agent id.');
|
|
230
|
+
}
|
|
231
|
+
return shapeAgreementResult(await services.agreementService.requestLink(payload.proposedTo, payload.description));
|
|
232
|
+
}
|
|
197
233
|
if (!isBroadcast && !payload.providerId) {
|
|
198
234
|
throw new Error('agreement-propose requires `providerId` — name who does the work: your own id (you are offering to work) or the proposedTo id (you are commissioning the recipient).');
|
|
199
235
|
}
|
|
200
236
|
if (isBroadcast && payload.providerId) {
|
|
201
|
-
|
|
237
|
+
// ZIG-1022: providerId = self on a broadcast publishes a STANDING OFFER
|
|
238
|
+
// (seller side open to any claimer). Any other providerId stays an error.
|
|
239
|
+
if (actingAgentId && payload.providerId === actingAgentId) {
|
|
240
|
+
const priced = applyFleetPricingForProtocol('agreement-propose', payload, actingAgentId);
|
|
241
|
+
return services.agreementService.publishOffer({
|
|
242
|
+
description: String(priced.description ?? ''),
|
|
243
|
+
price: typeof priced.price === 'number' ? priced.price : undefined,
|
|
244
|
+
lifecycle: typeof priced.lifecycle === 'string' ? priced.lifecycle : undefined,
|
|
245
|
+
expiresAt: typeof priced.expiresAt === 'string' ? priced.expiresAt : undefined,
|
|
246
|
+
maxExecutions: typeof priced.maxExecutions === 'number' ? priced.maxExecutions : undefined,
|
|
247
|
+
engagementKind: priced.engagementKind === 'hire' ? 'hire' : priced.engagementKind === 'service' ? 'service' : undefined,
|
|
248
|
+
billing: priced['billing'] === 'per_task' ? 'per_task' : priced['billing'] === 'total' ? 'total' : undefined,
|
|
249
|
+
audience: payload.proposedTo === 'org' ? 'org' : 'everyone',
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
throw new Error('On a broadcast, providerId must be your own agent id (a standing offer) or omitted (a quest — the claiming agent fills the open side).');
|
|
202
253
|
}
|
|
203
254
|
const priced = applyFleetPricingForProtocol('agreement-propose', payload, actingAgentId);
|
|
204
255
|
const { operation: _op, ...fields } = priced;
|
|
@@ -2,11 +2,12 @@ import { type ToolDefinition } from '../../../tools/defineTool.js';
|
|
|
2
2
|
export declare const agreementProposeTool: ToolDefinition;
|
|
3
3
|
export declare const agreementSubcontractTool: ToolDefinition;
|
|
4
4
|
export declare const agreementRespondTool: ToolDefinition;
|
|
5
|
-
export declare const
|
|
6
|
-
export declare const
|
|
7
|
-
export declare const
|
|
5
|
+
export declare const agreementClaimTool: ToolDefinition;
|
|
6
|
+
export declare const taskSetResultTool: ToolDefinition;
|
|
7
|
+
export declare const agreementCounterTool: ToolDefinition;
|
|
8
|
+
export declare const agreementGetTool: ToolDefinition;
|
|
8
9
|
export declare const agreementRevokeTool: ToolDefinition;
|
|
9
10
|
export declare const agreementFulfillTool: ToolDefinition;
|
|
10
11
|
export declare const taskUpdatePlanStepTool: ToolDefinition;
|
|
11
|
-
export declare const
|
|
12
|
+
export declare const taskCreateTool: ToolDefinition;
|
|
12
13
|
export declare const PROTOCOL_TOOLS: ToolDefinition[];
|