@ziggs-ai/agent-sdk 0.19.0 → 0.21.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/Agent.d.ts +28 -7
- package/dist/Agent.js +177 -72
- package/dist/brain/ClaudeBrain.d.ts +11 -1
- package/dist/brain/ClaudeBrain.js +71 -18
- package/dist/brain/ZiggsBrain.d.ts +2 -0
- package/dist/brain/ZiggsBrain.js +1 -0
- package/dist/brain/claudeCognition.d.ts +14 -1
- package/dist/brain/claudeCognition.js +106 -6
- package/dist/brain/claudeSdkProtocol.d.ts +1 -12
- package/dist/brain/claudeSdkProtocol.js +4 -1
- package/dist/brain/claudeSdkTools.d.ts +3 -0
- package/dist/brain/claudeSdkTools.js +4 -2
- package/dist/brain/hireActivationNotice.d.ts +19 -0
- package/dist/brain/hireActivationNotice.js +79 -0
- package/dist/brain/standingOfferNotice.d.ts +15 -0
- package/dist/brain/standingOfferNotice.js +89 -0
- package/dist/inboundDiscoveryPolicy.d.ts +2 -0
- package/dist/inboundDiscoveryPolicy.js +2 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/ingress/normalizeIncoming.d.ts +3 -0
- package/dist/ingress/normalizeIncoming.js +8 -0
- package/dist/mcp/createMcpAgent.d.ts +1 -1
- package/dist/mcp/createMcpAgent.js +1 -1
- package/dist/memory/MemoryStore.d.ts +18 -4
- package/dist/memory/MemoryStore.js +52 -12
- package/dist/platform/ConnectionPool.d.ts +9 -4
- package/dist/platform/ConnectionPool.js +16 -6
- package/dist/platform/StateTimers.d.ts +12 -6
- package/dist/platform/StateTimers.js +26 -6
- package/dist/platform/ZiggsEffectHandler.js +5 -1
- package/dist/platform/inboxLoop.d.ts +5 -7
- package/dist/platform/inboxLoop.js +113 -96
- package/dist/platform/polls/declaredPollRunner.d.ts +7 -1
- package/dist/platform/polls/declaredPollRunner.js +7 -9
- package/dist/platform/tools/index.d.ts +1 -0
- package/dist/platform/tools/index.js +1 -0
- package/dist/platform/tools/tier2/contextTools.d.ts +2 -0
- package/dist/platform/tools/tier2/contextTools.js +3 -1
- package/dist/platform/tools/tier2/planTools.d.ts +20 -0
- package/dist/platform/tools/tier2/planTools.js +64 -0
- package/dist/pricing/fleetDefaults.js +4 -9
- package/dist/pricing/fleetTierByAgentId.gen.d.ts +1 -1
- package/dist/pricing/fleetTierByAgentId.gen.js +2 -752
- package/dist/runtime/AgentMachine.d.ts +3 -0
- package/dist/runtime/AgentMachine.js +6 -1
- package/dist/runtime/PromptBuilder.js +2 -0
- package/dist/runtime/runTurn.d.ts +1 -0
- package/dist/runtime/runTurn.js +19 -15
- package/dist/runtime/wakeEffects.d.ts +35 -0
- package/dist/runtime/wakeEffects.js +121 -0
- package/package.json +2 -2
- package/dist/pricing/fleetFreeTierA.gen.d.ts +0 -6
- package/dist/pricing/fleetFreeTierA.gen.js +0 -11
|
@@ -105,6 +105,8 @@ export declare class AgentMachine {
|
|
|
105
105
|
input: number;
|
|
106
106
|
output: number;
|
|
107
107
|
};
|
|
108
|
+
/** Last turn skipped the model; host records a no-op naming why. */
|
|
109
|
+
_skippedReason: string | undefined;
|
|
108
110
|
_lastUserSenderId: string | null;
|
|
109
111
|
_stateTimeoutUnhandled: boolean;
|
|
110
112
|
_transitionTap: ((event: TransitionEvent) => void) | undefined;
|
|
@@ -117,6 +119,7 @@ export declare class AgentMachine {
|
|
|
117
119
|
input: number;
|
|
118
120
|
output: number;
|
|
119
121
|
};
|
|
122
|
+
get skippedReason(): string | undefined;
|
|
120
123
|
get stateTimeoutUnhandled(): boolean;
|
|
121
124
|
constructor({ workflow, executionCore, eff, toolManager, promptBuilder, memory, input, transitionTap, maxTurnsWithoutUserInput, writeKeys }: MachineOptions);
|
|
122
125
|
getSnapshot(): MachineSnapshot;
|
|
@@ -41,6 +41,8 @@ export class AgentMachine {
|
|
|
41
41
|
_draining = false;
|
|
42
42
|
_llmCalls = 0;
|
|
43
43
|
_tokens = { total: 0, input: 0, output: 0 };
|
|
44
|
+
/** Last turn skipped the model; host records a no-op naming why. */
|
|
45
|
+
_skippedReason;
|
|
44
46
|
_lastUserSenderId = null;
|
|
45
47
|
_stateTimeoutUnhandled = false;
|
|
46
48
|
_transitionTap;
|
|
@@ -49,6 +51,7 @@ export class AgentMachine {
|
|
|
49
51
|
_maxTurnsWithoutUserInput;
|
|
50
52
|
get llmCalls() { return this._llmCalls; }
|
|
51
53
|
get tokens() { return this._tokens; }
|
|
54
|
+
get skippedReason() { return this._skippedReason; }
|
|
52
55
|
get stateTimeoutUnhandled() { return this._stateTimeoutUnhandled; }
|
|
53
56
|
constructor({ workflow, executionCore, eff, toolManager, promptBuilder, memory, input, transitionTap, maxTurnsWithoutUserInput, writeKeys }) {
|
|
54
57
|
this.workflow = workflow;
|
|
@@ -316,7 +319,7 @@ export class AgentMachine {
|
|
|
316
319
|
// Thinking: run a turn, produce an outcome, re-step.
|
|
317
320
|
this._running = true;
|
|
318
321
|
try {
|
|
319
|
-
const { outcome: turnOutcome, toolResults, llmCalls, tokens } = await this.executionCore({
|
|
322
|
+
const { outcome: turnOutcome, toolResults, llmCalls, tokens, skippedReason } = await this.executionCore({
|
|
320
323
|
stateId: this.state,
|
|
321
324
|
statePrompt: sd.prompt,
|
|
322
325
|
actions: sd.actions,
|
|
@@ -331,6 +334,8 @@ export class AgentMachine {
|
|
|
331
334
|
});
|
|
332
335
|
this.ctx.toolResults = toolResults;
|
|
333
336
|
this._llmCalls += llmCalls;
|
|
337
|
+
if (skippedReason)
|
|
338
|
+
this._skippedReason = skippedReason;
|
|
334
339
|
this._turnsSinceUserInput += 1;
|
|
335
340
|
this._tokens.total += tokens.total;
|
|
336
341
|
this._tokens.input += tokens.input;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { INBOUND_DISCOVERY_POLICY } from '../inboundDiscoveryPolicy.js';
|
|
1
2
|
import { HistoryFormatter, AgreementFormatter } from '../formatters/index.js';
|
|
2
3
|
import { validateContext } from '../cognition/validateContext.js';
|
|
3
4
|
import { isBroadcastTarget, isOpaquePresentationRef, partySideIds } from '@ziggs-ai/api-client';
|
|
@@ -67,6 +68,7 @@ ${this._renderDecisionSchemaFromActions(actions, senderMeta, ctxObj, omitMessage
|
|
|
67
68
|
if (statePrompt.constraints)
|
|
68
69
|
lines.push(`Constraints: ${statePrompt.constraints}`);
|
|
69
70
|
lines.push('</state>');
|
|
71
|
+
lines.push(`\n<security>\n${INBOUND_DISCOVERY_POLICY}\n</security>`);
|
|
70
72
|
if (tools?.length) {
|
|
71
73
|
lines.push('\n<tools>');
|
|
72
74
|
for (const tool of tools) {
|
package/dist/runtime/runTurn.js
CHANGED
|
@@ -112,12 +112,13 @@ export async function runTurn(input) {
|
|
|
112
112
|
: [];
|
|
113
113
|
const serverContext = await effWithTrace({ kind: 'read-context', sessionId, opts: {} });
|
|
114
114
|
const ctxObj = normalizeContextSnapshot(serverContext);
|
|
115
|
-
// A
|
|
116
|
-
//
|
|
117
|
-
//
|
|
118
|
-
//
|
|
119
|
-
//
|
|
120
|
-
// loud-log the empty
|
|
115
|
+
// A wake whose chat no longer resolves (deleted, or never visible to this
|
|
116
|
+
// agent) has no world to decide over — deciding anyway means reasoning
|
|
117
|
+
// about a fabricated empty history plus whatever stale scraps other
|
|
118
|
+
// fetches returned. Skip the LLM and park. Resource wakes used to slip
|
|
119
|
+
// past a message-only gate and burn a full turn. A bare
|
|
120
|
+
// agreement id as sessionId is a mis-keyed lane — loud-log the empty
|
|
121
|
+
// snapshot rather than staying silent.
|
|
121
122
|
if (ctxObj.chatMissing) {
|
|
122
123
|
const looksLikeBareAgreement = isAgreementId(sessionId);
|
|
123
124
|
if (looksLikeBareAgreement) {
|
|
@@ -125,15 +126,18 @@ export async function runTurn(input) {
|
|
|
125
126
|
`lane was keyed by agreementId instead of the agreement's origin/notification chat; ` +
|
|
126
127
|
`wake continues with an empty snapshot`);
|
|
127
128
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
129
|
+
else {
|
|
130
|
+
// Any wake in a room this agent cannot read — not only
|
|
131
|
+
// message wakes. Resource notices used to spend a full turn here.
|
|
132
|
+
runtimeLog.warn('runTurn', `chat ${sessionId} not readable — skipping wake (state=${stateId})`);
|
|
133
|
+
return {
|
|
134
|
+
outcome: { kind: 'wait' },
|
|
135
|
+
toolResults: [],
|
|
136
|
+
llmCalls: 0,
|
|
137
|
+
tokens: { total: 0, input: 0, output: 0 },
|
|
138
|
+
skippedReason: `chat_unreadable:${sessionId}`,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
137
141
|
}
|
|
138
142
|
const prompt = promptBuilder.buildFromState({
|
|
139
143
|
statePrompt, actions,
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Effect } from '../types.js';
|
|
2
|
+
export interface WorkScope {
|
|
3
|
+
agreementId?: string;
|
|
4
|
+
taskId?: string;
|
|
5
|
+
}
|
|
6
|
+
/** Never combine a task from one event with an agreement from another. */
|
|
7
|
+
export declare function uniqueWorkScope(scopes: WorkScope[]): WorkScope;
|
|
8
|
+
/** Only structured host/backend fields are authority; message text and timer reasons are not. */
|
|
9
|
+
export declare function workScopeFromWake(event: unknown, sessionId?: string): WorkScope;
|
|
10
|
+
/** Per-wake observation, including effects nested inside tools and cross-room sends. */
|
|
11
|
+
export declare class WakeEffects {
|
|
12
|
+
private readonly initialScope;
|
|
13
|
+
private readonly sessionId?;
|
|
14
|
+
private readonly inferScope;
|
|
15
|
+
sentMessage: boolean;
|
|
16
|
+
taskWrite: boolean;
|
|
17
|
+
readonly planVersions: Set<string>;
|
|
18
|
+
readonly sentMessageKeys: Set<string>;
|
|
19
|
+
/** Agreements the wake's OWN room reported, from its fenced snapshot. */
|
|
20
|
+
private roomAgreementIds;
|
|
21
|
+
/** Confirmed task writes: work this wake actually performed. */
|
|
22
|
+
private taskWrites;
|
|
23
|
+
constructor(initialScope: WorkScope, sessionId?: string | undefined, inferScope?: boolean);
|
|
24
|
+
static forWake(event: unknown, sessionId: string): WakeEffects;
|
|
25
|
+
/**
|
|
26
|
+
* Structured wake fields first. Otherwise the room's one agreement is the
|
|
27
|
+
* scope — spend follows who the agent acts for, and in a room that is the
|
|
28
|
+
* room's agreement — and a confirmed task write names its task only when
|
|
29
|
+
* the server tied the write to that same agreement. A plan written for
|
|
30
|
+
* customer A from customer B's room stays billed to B and names no task; a
|
|
31
|
+
* write whose agreement the server did not report adds nothing but the room.
|
|
32
|
+
*/
|
|
33
|
+
get scope(): WorkScope;
|
|
34
|
+
observe(effect: Effect, response: unknown): void;
|
|
35
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { agreementIdFromLane } from '@ziggs-ai/api-client';
|
|
2
|
+
const record = (value) => value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
3
|
+
const id = (value) => typeof value === 'string' && value.trim() ? value : undefined;
|
|
4
|
+
/** Never combine a task from one event with an agreement from another. */
|
|
5
|
+
export function uniqueWorkScope(scopes) {
|
|
6
|
+
const agreements = new Set(scopes.map(s => s.agreementId).filter(Boolean));
|
|
7
|
+
const tasks = new Set(scopes.map(s => s.taskId).filter(Boolean));
|
|
8
|
+
if (agreements.size > 1 || tasks.size > 1)
|
|
9
|
+
return {};
|
|
10
|
+
if (agreements.size && tasks.size && scopes.some(s => s.taskId && !s.agreementId))
|
|
11
|
+
return {};
|
|
12
|
+
return {
|
|
13
|
+
...(agreements.size ? { agreementId: [...agreements][0] } : {}),
|
|
14
|
+
...(tasks.size ? { taskId: [...tasks][0] } : {}),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/** Only structured host/backend fields are authority; message text and timer reasons are not. */
|
|
18
|
+
export function workScopeFromWake(event, sessionId) {
|
|
19
|
+
const rec = record(event);
|
|
20
|
+
if (rec.type === 'batch' && Array.isArray(rec.events)) {
|
|
21
|
+
return uniqueWorkScope(rec.events.map(e => workScopeFromWake(e, sessionId)));
|
|
22
|
+
}
|
|
23
|
+
const resource = record(rec.resource);
|
|
24
|
+
const task = rec.type === 'task_result' || rec.type === 'notification' ? record(rec.result) : {};
|
|
25
|
+
const agreementId = id(rec.agreementId) ?? id(resource.agreementId) ?? id(task.agreementId)
|
|
26
|
+
?? agreementIdFromLane(rec.chatId) ?? agreementIdFromLane(resource.chatId)
|
|
27
|
+
?? agreementIdFromLane(sessionId) ?? undefined;
|
|
28
|
+
const taskId = id(rec.taskId) ?? id(resource.taskId) ?? id(task.taskId);
|
|
29
|
+
return { ...(agreementId ? { agreementId } : {}), ...(taskId ? { taskId } : {}) };
|
|
30
|
+
}
|
|
31
|
+
const TASK_WRITES = new Set(['task_create', 'task_set_result', 'task_update_plan_step', 'task_replace_plan']);
|
|
32
|
+
/** Per-wake observation, including effects nested inside tools and cross-room sends. */
|
|
33
|
+
export class WakeEffects {
|
|
34
|
+
initialScope;
|
|
35
|
+
sessionId;
|
|
36
|
+
inferScope;
|
|
37
|
+
sentMessage = false;
|
|
38
|
+
taskWrite = false;
|
|
39
|
+
planVersions = new Set();
|
|
40
|
+
sentMessageKeys = new Set();
|
|
41
|
+
/** Agreements the wake's OWN room reported, from its fenced snapshot. */
|
|
42
|
+
roomAgreementIds = [];
|
|
43
|
+
/** Confirmed task writes: work this wake actually performed. */
|
|
44
|
+
taskWrites = [];
|
|
45
|
+
constructor(initialScope, sessionId, inferScope = true) {
|
|
46
|
+
this.initialScope = initialScope;
|
|
47
|
+
this.sessionId = sessionId;
|
|
48
|
+
this.inferScope = inferScope;
|
|
49
|
+
}
|
|
50
|
+
static forWake(event, sessionId) {
|
|
51
|
+
const initialScope = workScopeFromWake(event, sessionId);
|
|
52
|
+
const frame = record(event);
|
|
53
|
+
const conflictingBatch = frame.type === 'batch' && Array.isArray(frame.events)
|
|
54
|
+
&& !initialScope.agreementId && !initialScope.taskId
|
|
55
|
+
&& frame.events.some(item => Object.keys(workScopeFromWake(item, sessionId)).length > 0);
|
|
56
|
+
return new WakeEffects(initialScope, sessionId, !conflictingBatch);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Structured wake fields first. Otherwise the room's one agreement is the
|
|
60
|
+
* scope — spend follows who the agent acts for, and in a room that is the
|
|
61
|
+
* room's agreement — and a confirmed task write names its task only when
|
|
62
|
+
* the server tied the write to that same agreement. A plan written for
|
|
63
|
+
* customer A from customer B's room stays billed to B and names no task; a
|
|
64
|
+
* write whose agreement the server did not report adds nothing but the room.
|
|
65
|
+
*/
|
|
66
|
+
get scope() {
|
|
67
|
+
if (this.initialScope.agreementId || this.initialScope.taskId)
|
|
68
|
+
return this.initialScope;
|
|
69
|
+
if (!this.inferScope)
|
|
70
|
+
return {};
|
|
71
|
+
const room = new Set(this.roomAgreementIds);
|
|
72
|
+
if (room.size > 1)
|
|
73
|
+
return {};
|
|
74
|
+
const agreementId = [...room][0];
|
|
75
|
+
if (!agreementId)
|
|
76
|
+
return uniqueWorkScope(this.taskWrites);
|
|
77
|
+
const tasks = new Set(this.taskWrites.filter(w => w.agreementId === agreementId).map(w => w.taskId));
|
|
78
|
+
return { agreementId, ...(tasks.size === 1 ? { taskId: [...tasks][0] } : {}) };
|
|
79
|
+
}
|
|
80
|
+
observe(effect, response) {
|
|
81
|
+
const outer = record(response);
|
|
82
|
+
if (effect.kind === 'send-message' && outer.ok === true)
|
|
83
|
+
this.sentMessage = true;
|
|
84
|
+
// This is the fenced snapshot of the wake's own chat, not an arbitrary
|
|
85
|
+
// agreement consulted while answering. A room with multiple agreements
|
|
86
|
+
// remains ambiguous, and cross-room reads cannot change the wake's scope.
|
|
87
|
+
if (effect.kind === 'read-context' && effect.sessionId === this.sessionId
|
|
88
|
+
&& outer.chatMissing !== true && Array.isArray(outer.agreements)) {
|
|
89
|
+
for (const row of outer.agreements) {
|
|
90
|
+
const agreementId = id(record(row).agreementId);
|
|
91
|
+
if (agreementId)
|
|
92
|
+
this.roomAgreementIds.push(agreementId);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (effect.kind !== 'tool-call' || outer.ok !== true)
|
|
96
|
+
return;
|
|
97
|
+
const result = record(outer.result);
|
|
98
|
+
if (result.ok === false || result.success === false || result.error)
|
|
99
|
+
return;
|
|
100
|
+
const name = effect.name.replace(/^(functions|tools|function|tool)\./, '');
|
|
101
|
+
if (name === 'chat_send') {
|
|
102
|
+
this.sentMessage = true;
|
|
103
|
+
if (id(result.chatId) && id(result.messageId)) {
|
|
104
|
+
this.sentMessageKeys.add(JSON.stringify([result.chatId, result.messageId]));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (TASK_WRITES.has(name))
|
|
108
|
+
this.taskWrite = true;
|
|
109
|
+
if ((name === 'task_replace_plan' || name === 'task_update_plan_step') && id(result.taskId)
|
|
110
|
+
&& result.taskId === record(effect.args).taskId && id(result.updatedAt)) {
|
|
111
|
+
this.planVersions.add(JSON.stringify([result.taskId, result.updatedAt]));
|
|
112
|
+
}
|
|
113
|
+
// A confirmed task write identifies work actually performed. A broad
|
|
114
|
+
// task_list, arbitrary agreement_get or ids in message prose do not.
|
|
115
|
+
const agreementId = id(result.agreementId);
|
|
116
|
+
const taskId = id(result.taskId);
|
|
117
|
+
if (TASK_WRITES.has(name) && taskId) {
|
|
118
|
+
this.taskWrites.push({ taskId, ...(agreementId ? { agreementId } : {}) });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ziggs-ai/agent-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.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.20.0",
|
|
30
30
|
"ajv": "^8.17.1",
|
|
31
31
|
"dotenv": "^17.2.3",
|
|
32
32
|
"openai": "^4.0.0",
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AUTO-GENERATED by scripts/build-fleet-tier-map.mjs — do not edit by hand.
|
|
3
|
-
* Tier A trivial public lookups (free when price omitted).
|
|
4
|
-
*/
|
|
5
|
-
export const FREE_TIER_A_AGENT_IDS = new Set([
|
|
6
|
-
"site-health-checker",
|
|
7
|
-
"timezone-expert",
|
|
8
|
-
"uv-index-advisor",
|
|
9
|
-
"weather-forecaster"
|
|
10
|
-
]);
|
|
11
|
-
export const FREE_TIER_A_COUNT = 4;
|