@ziggs-ai/agent-sdk 0.15.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/README.md +110 -88
- package/dist/{AgentHost.d.ts → Agent.d.ts} +66 -27
- package/dist/{AgentHost.js → Agent.js} +221 -119
- package/dist/adapters/AnthropicAdapter.d.ts +1 -1
- package/dist/adapters/AnthropicAdapter.js +1 -1
- package/dist/{agent/ClaudeSdkAgent.d.ts → brain/ClaudeBrain.d.ts} +23 -11
- package/dist/{agent/ClaudeSdkAgent.js → brain/ClaudeBrain.js} +145 -95
- package/dist/{agent/Agent.d.ts → brain/ZiggsBrain.d.ts} +23 -20
- package/dist/{agent/Agent.js → brain/ZiggsBrain.js} +12 -11
- package/dist/{agent → brain}/claudeCognition.d.ts +1 -1
- package/dist/{agent → brain}/claudeCognition.js +1 -1
- package/dist/{agent → brain}/claudeSdkProtocol.d.ts +2 -2
- package/dist/{agent → brain}/claudeSdkProtocol.js +2 -2
- package/dist/{agent → brain}/claudeSdkTools.d.ts +2 -1
- package/dist/{agent → brain}/claudeSdkTools.js +9 -65
- package/dist/{agent → brain}/hostedAgentConnection.d.ts +1 -1
- package/dist/{agent → brain}/hostedAgentConnection.js +3 -3
- package/dist/brain/index.d.ts +6 -6
- package/dist/brain/index.js +2 -2
- package/dist/brain/onBehalfAgreement.d.ts +51 -0
- package/dist/brain/onBehalfAgreement.js +99 -0
- package/dist/{agent → brain}/toolManagerToSdkMcp.js +10 -1
- package/dist/defineAgent.js +4 -4
- package/dist/formatters/HistoryFormatter.js +2 -1
- package/dist/index.d.ts +30 -31
- package/dist/index.js +24 -25
- package/dist/{adapters/McpAdapter.d.ts → mcp/createMcpAgent.d.ts} +16 -15
- package/dist/{adapters/McpAdapter.js → mcp/createMcpAgent.js} +54 -40
- package/dist/memory/MemoryStore.d.ts +18 -1
- package/dist/memory/MemoryStore.js +43 -0
- package/dist/{server → platform}/ConnectionPool.d.ts +9 -3
- package/dist/{server → platform}/ConnectionPool.js +16 -4
- package/dist/{server → platform}/EventQueue.d.ts +1 -1
- package/dist/{server → platform}/StateTimers.d.ts +1 -1
- package/dist/{server → platform}/StateTimers.js +1 -1
- package/dist/{server → platform}/ZiggsEffectHandler.d.ts +4 -4
- package/dist/{server → platform}/ZiggsEffectHandler.js +2 -2
- package/dist/{server → platform}/agreements/AgreementService.d.ts +10 -1
- package/dist/{server → platform}/agreements/AgreementService.js +9 -1
- package/dist/{server → platform}/inboxLoop.d.ts +34 -1
- package/dist/{server → platform}/inboxLoop.js +122 -14
- package/dist/{server → platform}/polls/PollKind.d.ts +4 -7
- package/dist/{server → platform}/polls/PollKind.js +3 -6
- package/dist/{server → platform}/polls/PollRunner.d.ts +5 -6
- package/dist/{server → platform}/polls/PollRunner.js +24 -12
- package/dist/platform/polls/declaredPollRunner.d.ts +4 -0
- package/dist/platform/polls/declaredPollRunner.js +29 -0
- package/dist/{server → platform}/tools/index.d.ts +1 -0
- package/dist/{server → platform}/tools/index.js +1 -0
- package/dist/{server → platform}/tools/tier1/protocolRunner.js +33 -27
- package/dist/{server → platform}/tools/tier1/protocolTools.js +2 -7
- package/dist/{server → platform}/tools/tier2/mcpConnectionTools.js +6 -3
- package/dist/platform/tools/tier2/taskTools.d.ts +3 -0
- package/dist/platform/tools/tier2/taskTools.js +6 -0
- package/dist/runtime/bootstrapCtx.d.ts +1 -1
- package/dist/runtime/bootstrapCtx.js +1 -1
- package/dist/runtime/buildOutcome.d.ts +0 -8
- package/dist/runtime/buildOutcome.js +20 -6
- package/dist/runtime/kickoff.js +1 -1
- package/dist/runtime/runTurn.d.ts +1 -1
- package/dist/runtime/runTurn.js +18 -18
- package/dist/runtime/validateWorkflow.js +1 -1
- package/dist/runtime/wakeOutcome.d.ts +27 -0
- package/dist/runtime/wakeOutcome.js +43 -0
- package/dist/skills/parseSkill.d.ts +7 -2
- package/dist/skills/parseSkill.js +22 -5
- package/dist/skills/skillReadReference.d.ts +6 -0
- package/dist/skills/skillReadReference.js +27 -0
- package/dist/tools/needs.js +2 -2
- package/dist/tools/recordReport.d.ts +5 -0
- package/dist/tools/recordReport.js +5 -0
- package/dist/tools/toolCreds.d.ts +1 -1
- package/dist/tools/toolCreds.js +1 -1
- package/dist/types.d.ts +3 -3
- package/package.json +2 -2
- package/dist/server/createHealthServer.d.ts +0 -7
- package/dist/server/createHealthServer.js +0 -75
- package/dist/server/runLauncher.d.ts +0 -22
- package/dist/server/runLauncher.js +0 -160
- /package/dist/{agent → brain}/toolManagerToSdkMcp.d.ts +0 -0
- /package/dist/{server → platform}/EventQueue.js +0 -0
- /package/dist/{server → platform}/OutboxBuffer.d.ts +0 -0
- /package/dist/{server → platform}/OutboxBuffer.js +0 -0
- /package/dist/{server → platform}/SeenMessages.d.ts +0 -0
- /package/dist/{server → platform}/SeenMessages.js +0 -0
- /package/dist/{server → platform}/proactive/ProactiveTrigger.d.ts +0 -0
- /package/dist/{server → platform}/proactive/ProactiveTrigger.js +0 -0
- /package/dist/{server → platform}/requestMatch.d.ts +0 -0
- /package/dist/{server → platform}/requestMatch.js +0 -0
- /package/dist/{server → platform}/tasks/TaskService.d.ts +0 -0
- /package/dist/{server → platform}/tasks/TaskService.js +0 -0
- /package/dist/{server → platform}/tasks/index.d.ts +0 -0
- /package/dist/{server → platform}/tasks/index.js +0 -0
- /package/dist/{server → platform}/telemetryIngest.d.ts +0 -0
- /package/dist/{server → platform}/telemetryIngest.js +0 -0
- /package/dist/{server → platform}/tools/tier1/protocolRunner.d.ts +0 -0
- /package/dist/{server → platform}/tools/tier1/protocolTools.d.ts +0 -0
- /package/dist/{server → platform}/tools/tier2/artifactTools.d.ts +0 -0
- /package/dist/{server → platform}/tools/tier2/artifactTools.js +0 -0
- /package/dist/{server → platform}/tools/tier2/chatTools.d.ts +0 -0
- /package/dist/{server → platform}/tools/tier2/chatTools.js +0 -0
- /package/dist/{server → platform}/tools/tier2/connectionTools.d.ts +0 -0
- /package/dist/{server → platform}/tools/tier2/connectionTools.js +0 -0
- /package/dist/{server → platform}/tools/tier2/contextTools.d.ts +0 -0
- /package/dist/{server → platform}/tools/tier2/contextTools.js +0 -0
- /package/dist/{server → platform}/tools/tier2/discoveryTools.d.ts +0 -0
- /package/dist/{server → platform}/tools/tier2/discoveryTools.js +0 -0
- /package/dist/{server → platform}/tools/tier2/grantsTools.d.ts +0 -0
- /package/dist/{server → platform}/tools/tier2/grantsTools.js +0 -0
- /package/dist/{server → platform}/tools/tier2/introductionTools.d.ts +0 -0
- /package/dist/{server → platform}/tools/tier2/introductionTools.js +0 -0
- /package/dist/{server → platform}/tools/tier2/linkTools.d.ts +0 -0
- /package/dist/{server → platform}/tools/tier2/linkTools.js +0 -0
- /package/dist/{server → platform}/tools/tier2/marketplaceTools.d.ts +0 -0
- /package/dist/{server → platform}/tools/tier2/marketplaceTools.js +0 -0
- /package/dist/{server → platform}/tools/tier2/mcpConnectionTools.d.ts +0 -0
- /package/dist/{server → platform}/tools/tier2/paymentTools.d.ts +0 -0
- /package/dist/{server → platform}/tools/tier2/paymentTools.js +0 -0
|
@@ -23,6 +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 { TASK_TOOLS, listTasksTool } from './tier2/taskTools.js';
|
|
26
27
|
export { LINK_TOOLS, linkProposeTool, linkListTool, } from './tier2/linkTools.js';
|
|
27
28
|
export { INTRODUCTION_TOOLS, introductionMintTool, introductionRedeemTool, introductionListTool, introductionRevokeTool, } from './tier2/introductionTools.js';
|
|
28
29
|
export { ARTIFACT_TOOLS, recordArtifactTool } from './tier2/artifactTools.js';
|
|
@@ -27,6 +27,7 @@ export { CONTEXT_TOOLS, contextReadTool, contextDelegateTool, contextExpandReach
|
|
|
27
27
|
// one grant lister across every rail (retires context_discover +
|
|
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
|
+
export { TASK_TOOLS, listTasksTool } from './tier2/taskTools.js';
|
|
30
31
|
// links (agreements with engagementKind 'link') — cross-org reach.
|
|
31
32
|
///1022: the rail is two tools; propose/claim/revoke ride the
|
|
32
33
|
// agreement grammar.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isBroadcastTarget, partyActorIds, } from '@ziggs-ai/api-client';
|
|
1
|
+
import { isBroadcastTarget, partyActorIds, presentClaimResult, } 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
|
|
@@ -71,11 +71,16 @@ cachedAgreements) {
|
|
|
71
71
|
// claim; it does not re-parent the row, which stays in the publisher's
|
|
72
72
|
// tree.
|
|
73
73
|
const declaredMandate = payload.mandateAgreementId;
|
|
74
|
-
|
|
74
|
+
const { agreement, kind } = await agreementService.claim(payload.agreementId, {
|
|
75
75
|
...(typeof declaredMandate === 'string' && declaredMandate
|
|
76
76
|
? { mandateAgreementId: declaredMandate }
|
|
77
77
|
: {}),
|
|
78
78
|
});
|
|
79
|
+
return presentClaimResult(agreement, kind, {
|
|
80
|
+
creds: { operatorKey: 'sdk', agentId: actorId ?? 'agent' },
|
|
81
|
+
surface: 'sdk',
|
|
82
|
+
webUrl: process.env.ZIGGS_WEB_URL,
|
|
83
|
+
});
|
|
79
84
|
}
|
|
80
85
|
case 'agreement-counter-proposal':
|
|
81
86
|
if (!payload.agreementId)
|
|
@@ -221,38 +226,16 @@ async function proposeAgreement(services, payload, chatId, _agents, actingAgentI
|
|
|
221
226
|
throw new Error('agreement-propose requires `description`');
|
|
222
227
|
if (!payload.proposedTo)
|
|
223
228
|
throw new Error('agreement-propose requires `proposedTo` (userId or agentId). To create a task under an existing agreement, use `task_create` instead.');
|
|
224
|
-
const isBroadcast = isBroadcastTarget(payload.proposedTo);
|
|
225
229
|
// a link proposal is an agreement with engagementKind 'link' —
|
|
226
230
|
// no chat, no money; the target owner approves. Folds the old request_link.
|
|
231
|
+
// The shared propose grammar no longer has a link arm (link_propose owns it).
|
|
227
232
|
if (payload.engagementKind === 'link') {
|
|
228
|
-
if (
|
|
233
|
+
if (isBroadcastTarget(payload.proposedTo)) {
|
|
229
234
|
throw new Error('A broadcast link is an invite — use link_propose with no target instead; it returns a share link.');
|
|
230
235
|
}
|
|
231
236
|
return services.agreementService.requestLink(payload.proposedTo, payload.description);
|
|
232
237
|
}
|
|
233
|
-
if (!isBroadcast && !payload.providerId) {
|
|
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).');
|
|
235
|
-
}
|
|
236
|
-
if (isBroadcast && payload.providerId) {
|
|
237
|
-
// 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 request — the claiming agent fills the open side).');
|
|
253
|
-
}
|
|
254
238
|
const priced = applyFleetPricingForProtocol('agreement-propose', payload, actingAgentId);
|
|
255
|
-
const { operation: _op, ...fields } = priced;
|
|
256
239
|
// Wake-derived idempotency key (delivery law): a crash-replay re-issuing this
|
|
257
240
|
// propose resolves to the already-created agreement server-side instead of
|
|
258
241
|
// minting a duplicate proposal. Keyed on what the proposal writes, so
|
|
@@ -263,7 +246,30 @@ async function proposeAgreement(services, payload, chatId, _agents, actingAgentI
|
|
|
263
246
|
description: payload.description,
|
|
264
247
|
parentAgreementId: payload.parentAgreementId,
|
|
265
248
|
});
|
|
266
|
-
|
|
249
|
+
const engagementKind = priced.engagementKind === 'hire' || priced.engagementKind === 'service'
|
|
250
|
+
? priced.engagementKind
|
|
251
|
+
: undefined;
|
|
252
|
+
const billing = priced['billing'] === 'per_task' || priced['billing'] === 'total'
|
|
253
|
+
? priced['billing']
|
|
254
|
+
: undefined;
|
|
255
|
+
const { agreement, shape } = await services.agreementService.proposeUnified({
|
|
256
|
+
proposedTo: priced.proposedTo ?? payload.proposedTo,
|
|
257
|
+
chatId: chatId ?? undefined,
|
|
258
|
+
description: String(priced.description ?? payload.description),
|
|
259
|
+
providerId: priced.providerId,
|
|
260
|
+
price: typeof priced.price === 'number' ? priced.price : undefined,
|
|
261
|
+
lifecycle: typeof priced.lifecycle === 'string' ? priced.lifecycle : undefined,
|
|
262
|
+
expiresAt: typeof priced.expiresAt === 'string' ? priced.expiresAt : undefined,
|
|
263
|
+
maxExecutions: typeof priced.maxExecutions === 'number' ? priced.maxExecutions : undefined,
|
|
264
|
+
billing,
|
|
265
|
+
agreementDescription: priced.agreementDescription,
|
|
266
|
+
parentAgreementId: priced.parentAgreementId,
|
|
267
|
+
engagementKind,
|
|
268
|
+
idempotencyKey,
|
|
269
|
+
});
|
|
270
|
+
// Keep the agreement fields at the top level (the shape agents already
|
|
271
|
+
// read) and add the discriminant the shared flow returns.
|
|
272
|
+
return { ...agreement, shape };
|
|
267
273
|
}
|
|
268
274
|
// ── deterministic sub-agreement resolution ─────────────────────────
|
|
269
275
|
// The orchestrator delegates one task per worker, but the LLM inconsistently
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { defineTool } from '../../../tools/defineTool.js';
|
|
2
2
|
import { requireProtocolOperation } from '../../../tasks/protocolRegistry.js';
|
|
3
3
|
import { dispatchProtocolOp } from './protocolRunner.js';
|
|
4
|
-
import { AGREEMENT_PROPOSE_PROVIDER_ID_DESCRIPTION, } from '@ziggs-ai/api-client';
|
|
4
|
+
import { AGREEMENT_PROPOSE_PROVIDER_ID_DESCRIPTION, agreementClaimCapability, } from '@ziggs-ai/api-client';
|
|
5
5
|
/** keep these enums in lockstep with MCP ziggs_agreement_* / ziggs_task_create. */
|
|
6
6
|
const LIFECYCLE_ENUM = ['open', 'time-bound', 'count-bound'];
|
|
7
7
|
const PLAN_REVIEW_TIMING_ENUM = ['with_proposal', 'before_execution'];
|
|
@@ -46,12 +46,7 @@ function handlerFor(toolName) {
|
|
|
46
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', {
|
|
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.' });
|
|
49
|
+
export const agreementClaimTool = defineTool('agreement_claim', agreementClaimCapability.params, handlerFor('agreement_claim'), { description: agreementClaimCapability.descriptions.sdk });
|
|
55
50
|
export const taskSetResultTool = defineTool('task_set_result', {
|
|
56
51
|
taskId: { type: 'string', required: true },
|
|
57
52
|
// same param name as MCP ziggs_task_set_result / PATCH /tasks/:id/state.
|
|
@@ -2,7 +2,7 @@ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
|
2
2
|
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
3
3
|
import { ConnectionsClient } from '@ziggs-ai/api-client';
|
|
4
4
|
import { defineTool } from '../../../tools/defineTool.js';
|
|
5
|
-
import { resolveZiggsGatewayTarget } from '../../../
|
|
5
|
+
import { resolveZiggsGatewayTarget } from '../../../mcp/createMcpAgent.js';
|
|
6
6
|
/**
|
|
7
7
|
* Calling a tool on a connected MCP server, one shot.
|
|
8
8
|
*
|
|
@@ -15,7 +15,7 @@ import { resolveZiggsGatewayTarget } from '../../../adapters/McpAdapter.js';
|
|
|
15
15
|
*
|
|
16
16
|
* The gateway (`POST /connections/:id/mcp`) has always been the right door and is
|
|
17
17
|
* already in production; what was missing was a tool that walks through it. The
|
|
18
|
-
* transport, headers and MCP handshake are the same ones `
|
|
18
|
+
* transport, headers and MCP handshake are the same ones `createMcpAgent`'s `ziggs`
|
|
19
19
|
* transport uses — reused rather than re-derived, so a change to the gateway
|
|
20
20
|
* contract lands in one place.
|
|
21
21
|
*
|
|
@@ -66,7 +66,10 @@ async function withClient(ctx, connectionId, grantId, run) {
|
|
|
66
66
|
grantId,
|
|
67
67
|
operatorKey: ctx['operatorKey'],
|
|
68
68
|
agentId: ctx['agentId'],
|
|
69
|
-
}, {
|
|
69
|
+
}, {
|
|
70
|
+
agentId: ctx['agentId'],
|
|
71
|
+
laneId: ctx['laneId'],
|
|
72
|
+
});
|
|
70
73
|
const transport = new StreamableHTTPClientTransport(target.url, {
|
|
71
74
|
requestInit: { headers: target.headers },
|
|
72
75
|
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { listTasksCapability, TASK_CAPABILITIES } from '@ziggs-ai/api-client';
|
|
2
|
+
import { toolFromCapability, toolsFromCapabilities } from '../../../tools/capabilityAdapter.js';
|
|
3
|
+
// GET /tasks, defined once in api-client. MCP ziggs_task_list and this
|
|
4
|
+
// hosted-Claude tool used to re-declare the same filters.
|
|
5
|
+
export const listTasksTool = toolFromCapability(listTasksCapability);
|
|
6
|
+
export const TASK_TOOLS = toolsFromCapabilities(TASK_CAPABILITIES);
|
|
@@ -3,7 +3,7 @@ import type { Ctx, Identity } from '../types.js';
|
|
|
3
3
|
* The one place a per-lane `Ctx` is constructed.
|
|
4
4
|
*
|
|
5
5
|
* Both cognitions bootstrap the same Ctx: the FSM (`AgentMachine` constructor)
|
|
6
|
-
* and the Claude Agent SDK brain (`
|
|
6
|
+
* and the Claude Agent SDK brain (`ClaudeBrain.tick`). Before this, each
|
|
7
7
|
* carried its own copy of the fresh-Ctx literal and the restore-path defaulting,
|
|
8
8
|
* and they had already drifted (the "default them ONCE here" comment in
|
|
9
9
|
* AgentMachine no longer held). Keep it here so the shape lives in a single spot.
|
|
@@ -3,7 +3,7 @@ import { migrateLegacyTaskFields } from './taskBookkeeping.js';
|
|
|
3
3
|
* The one place a per-lane `Ctx` is constructed.
|
|
4
4
|
*
|
|
5
5
|
* Both cognitions bootstrap the same Ctx: the FSM (`AgentMachine` constructor)
|
|
6
|
-
* and the Claude Agent SDK brain (`
|
|
6
|
+
* and the Claude Agent SDK brain (`ClaudeBrain.tick`). Before this, each
|
|
7
7
|
* carried its own copy of the fresh-Ctx literal and the restore-path defaulting,
|
|
8
8
|
* and they had already drifted (the "default them ONCE here" comment in
|
|
9
9
|
* AgentMachine no longer held). Keep it here so the shape lives in a single spot.
|
|
@@ -36,14 +36,6 @@ export interface EmittedEvent {
|
|
|
36
36
|
senderId?: string;
|
|
37
37
|
[key: string]: unknown;
|
|
38
38
|
}
|
|
39
|
-
/**
|
|
40
|
-
* A chat message whose stated receiver is a DIFFERENT party. `receiverId` is
|
|
41
|
-
* the sender's addressing decision, carried on every wire frame; the
|
|
42
|
-
* human-broadcast sentinel counts as "elsewhere" for an agent (a broadcast is
|
|
43
|
-
* addressed to the room's humans). A frame with NO receiver keeps the turn —
|
|
44
|
-
* we cannot know it is not ours, and skipping on absence would deafen agents
|
|
45
|
-
* to malformed or legacy frames.
|
|
46
|
-
*/
|
|
47
39
|
export declare function isMessageAddressedElsewhere(ev: RawEvent | null | undefined, ownAgentId: string | null | undefined): boolean;
|
|
48
40
|
/**
|
|
49
41
|
* Converts a raw incoming event into the single Outcome that re-enters the
|
|
@@ -9,12 +9,26 @@ import { bookForDelegation, ensureTaskBook, findBookHoldingPending, removeTaskBo
|
|
|
9
9
|
* we cannot know it is not ours, and skipping on absence would deafen agents
|
|
10
10
|
* to malformed or legacy frames.
|
|
11
11
|
*/
|
|
12
|
+
function isSyntheticOrgReceiver(id) {
|
|
13
|
+
if (/^([a-z0-9][a-z0-9-]*)\1@ziggs\.local$/i.test(id))
|
|
14
|
+
return true;
|
|
15
|
+
const sep = id.indexOf(':::');
|
|
16
|
+
if (sep > 0)
|
|
17
|
+
return /@ziggs\.local$/i.test(id.slice(sep + 3));
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
12
20
|
export function isMessageAddressedElsewhere(ev, ownAgentId) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
21
|
+
if (!ev || ev.type !== 'message' || !ownAgentId || !ev.receiverId) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
if (ev.receiverId === ownAgentId)
|
|
25
|
+
return false;
|
|
26
|
+
// Fleet synthetic users are not a party that will answer. A hire room
|
|
27
|
+
// born with the org ghost as the other face used to address the human's
|
|
28
|
+
// message there, and the agent skipped the turn — claimed, acked, silence.
|
|
29
|
+
if (isSyntheticOrgReceiver(String(ev.receiverId)))
|
|
30
|
+
return false;
|
|
31
|
+
return true;
|
|
18
32
|
}
|
|
19
33
|
// ── outcomeFromEvent: incoming wire event → Outcome ──────────────────────────
|
|
20
34
|
/**
|
|
@@ -148,7 +162,7 @@ function singleEventToOutcome(ev, ownAgentId) {
|
|
|
148
162
|
return null;
|
|
149
163
|
}
|
|
150
164
|
if (ev.type === 'state-timeout') {
|
|
151
|
-
// Synthetic frame from
|
|
165
|
+
// Synthetic frame from Agent's StateTimers — host-constructed, fields always present.
|
|
152
166
|
return { kind: 'state-timeout', state: ev.state, waitedMs: ev.waitedMs };
|
|
153
167
|
}
|
|
154
168
|
// Only real inbound chat messages should drive the FSM. Backend bookkeeping
|
package/dist/runtime/kickoff.js
CHANGED
|
@@ -185,7 +185,7 @@ export function buildClaimKickoffState(initial) {
|
|
|
185
185
|
prompt: {
|
|
186
186
|
when: 'There is not yet a real chat with the buyer (common on store claims).',
|
|
187
187
|
instruction: 'Open or reuse a chat with the buyer who claimed the listing.',
|
|
188
|
-
format: '{"participantId": "<buyer actorId / Sender from the event>"}',
|
|
188
|
+
format: '{"participantId": "<buyer actorId / Sender from the event>", "agreementId": "<the claimed agreement id>"}',
|
|
189
189
|
},
|
|
190
190
|
tool: 'chat_open',
|
|
191
191
|
produces: (result) => {
|
|
@@ -15,7 +15,7 @@ interface RunTurnInput {
|
|
|
15
15
|
toolManager: ToolManager;
|
|
16
16
|
promptBuilder: PromptBuilder;
|
|
17
17
|
/**
|
|
18
|
-
* Agent-private long-term memory, threaded from
|
|
18
|
+
* Agent-private long-term memory, threaded from Agent. Currently
|
|
19
19
|
* surfaced into `eff` as part of the tool-call effect payload so tools
|
|
20
20
|
* can read/write notes; the prompt builder doesn't consume it yet.
|
|
21
21
|
*/
|
package/dist/runtime/runTurn.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { isAgreementId } from '@ziggs-ai/api-client';
|
|
1
2
|
import { outcomeFromActionResult } from './buildOutcome.js';
|
|
2
3
|
import { runtimeLog } from '../shared/runtimeLog.js';
|
|
3
4
|
import { safeParseJSON } from '../utils/jsonExtractor.js';
|
|
4
5
|
import { withDeclaredMandate } from './mandateDeclaration.js';
|
|
6
|
+
import { resolveOnBehalfAgreementId, } from '../brain/onBehalfAgreement.js';
|
|
5
7
|
const MAX_TOOL_LOOP_ITERS = 20;
|
|
6
8
|
const MAX_MSG_CHARS = 3500;
|
|
7
9
|
/**
|
|
@@ -117,9 +119,7 @@ export async function runTurn(input) {
|
|
|
117
119
|
// only. a bare agreement id as sessionId is a mis-keyed lane —
|
|
118
120
|
// loud-log the empty snapshot rather than staying silent.
|
|
119
121
|
if (ctxObj.chatMissing) {
|
|
120
|
-
const looksLikeBareAgreement =
|
|
121
|
-
sessionId.startsWith('agr_') &&
|
|
122
|
-
!sessionId.startsWith('agrn-');
|
|
122
|
+
const looksLikeBareAgreement = isAgreementId(sessionId);
|
|
123
123
|
if (looksLikeBareAgreement) {
|
|
124
124
|
runtimeLog.warn('runTurn', `chatMissing on non-chat sessionId=${sessionId} (state=${stateId}) — ` +
|
|
125
125
|
`lane was keyed by agreementId instead of the agreement's origin/notification chat; ` +
|
|
@@ -265,7 +265,9 @@ export async function runTurn(input) {
|
|
|
265
265
|
// not. The runtime knows; the model would have to be told.
|
|
266
266
|
args = withDeclaredMandate(args, toolName, toolManager, ctx.activeAgreementId);
|
|
267
267
|
const decision = { action: matchingAction, tool: toolName, args };
|
|
268
|
-
const result = await executeDecision(decision, actionDef, effWithTrace, sessionId, memory,
|
|
268
|
+
const result = await executeDecision(decision, actionDef, effWithTrace, sessionId, memory, incomingEvent, addToolUsage, ctx.identity.agentId, Array.isArray(ctxObj.agreements)
|
|
269
|
+
? ctxObj.agreements
|
|
270
|
+
: null);
|
|
269
271
|
toolCallResults.push({ tc, matchingAction, result, args, actionDef });
|
|
270
272
|
}
|
|
271
273
|
let hasTerminal = false;
|
|
@@ -418,7 +420,9 @@ export async function runTurn(input) {
|
|
|
418
420
|
try {
|
|
419
421
|
// effWithTrace, not eff: JSON-path tool calls must carry runId/stateId
|
|
420
422
|
// and the wake's write keys exactly like tool-loop calls do.
|
|
421
|
-
result = await executeDecision(decision, actionDef, effWithTrace, sessionId, memory,
|
|
423
|
+
result = await executeDecision(decision, actionDef, effWithTrace, sessionId, memory, incomingEvent, addToolUsage, ctx.identity.agentId, Array.isArray(ctxObj.agreements)
|
|
424
|
+
? ctxObj.agreements
|
|
425
|
+
: null);
|
|
422
426
|
}
|
|
423
427
|
catch (err) {
|
|
424
428
|
// Only outbound sends can throw out of executeDecision — executeTool
|
|
@@ -642,17 +646,18 @@ function findActionForTool(toolName, allowedActionNames, actions) {
|
|
|
642
646
|
}
|
|
643
647
|
return null;
|
|
644
648
|
}
|
|
645
|
-
async function executeDecision(decision, actionDef, eff, sessionId, memory, incomingEvent = null, onUsage) {
|
|
649
|
+
async function executeDecision(decision, actionDef, eff, sessionId, memory, incomingEvent = null, onUsage, agentId = null, agreements = null) {
|
|
646
650
|
const boundTool = actionDef?.tool || null;
|
|
647
651
|
if (boundTool) {
|
|
648
652
|
decision.tool = boundTool;
|
|
649
|
-
return await executeTool(decision, eff, sessionId, memory, incomingEvent, onUsage);
|
|
653
|
+
return await executeTool(decision, eff, sessionId, memory, incomingEvent, onUsage, agentId, agreements);
|
|
650
654
|
}
|
|
651
655
|
if (decision.action === '_genericDiscoveryTool' && decision.tool) {
|
|
652
|
-
return await executeTool(decision, eff, sessionId, memory, incomingEvent, onUsage);
|
|
656
|
+
return await executeTool(decision, eff, sessionId, memory, incomingEvent, onUsage, agentId, agreements);
|
|
657
|
+
}
|
|
658
|
+
if (decision.tool) {
|
|
659
|
+
return await executeTool(decision, eff, sessionId, memory, incomingEvent, onUsage, agentId, agreements);
|
|
653
660
|
}
|
|
654
|
-
if (decision.tool)
|
|
655
|
-
return await executeTool(decision, eff, sessionId, memory, incomingEvent, onUsage);
|
|
656
661
|
if (decision.messages && Array.isArray(decision.messages) && decision.messages.length > 0) {
|
|
657
662
|
return await executeSendMessages(decision, eff, sessionId);
|
|
658
663
|
}
|
|
@@ -775,7 +780,7 @@ async function executeSendMessages(decision, eff, sessionId) {
|
|
|
775
780
|
* dependencies (taskService, agreementService, agents list, etc.), and
|
|
776
781
|
* wrapping the call in telemetry / operation-record entries if it wants.
|
|
777
782
|
*/
|
|
778
|
-
async function executeTool(decision, eff, sessionId, memory, incomingEvent = null, onUsage) {
|
|
783
|
+
async function executeTool(decision, eff, sessionId, memory, incomingEvent = null, onUsage, agentId = null, agreements = null) {
|
|
779
784
|
const { tool, args } = decision;
|
|
780
785
|
if (!tool)
|
|
781
786
|
return null;
|
|
@@ -788,13 +793,8 @@ async function executeTool(decision, eff, sessionId, memory, incomingEvent = nul
|
|
|
788
793
|
memory,
|
|
789
794
|
senderId: incomingEvent?.senderId != null ? String(incomingEvent.senderId) : undefined,
|
|
790
795
|
senderType: incomingEvent?.senderType != null ? String(incomingEvent.senderType) : undefined,
|
|
791
|
-
//
|
|
792
|
-
|
|
793
|
-
// resolve the originating end-user, not the delegating agent (senderId).
|
|
794
|
-
onBehalfOfAgreementId: (() => {
|
|
795
|
-
const aid = incomingEvent?.result?.agreementId;
|
|
796
|
-
return typeof aid === 'string' && aid ? aid : undefined;
|
|
797
|
-
})(),
|
|
796
|
+
// Never bind on-behalf to a sub the agent only pays for.
|
|
797
|
+
onBehalfOfAgreementId: resolveOnBehalfAgreementId(incomingEvent ?? undefined, agreements, agentId, sessionId),
|
|
798
798
|
});
|
|
799
799
|
if (r.usage)
|
|
800
800
|
onUsage?.(r.usage);
|
|
@@ -94,7 +94,7 @@ export function validateWorkflow(workflow) {
|
|
|
94
94
|
errors.push(`thinking state "${name}" requires at least one action`);
|
|
95
95
|
}
|
|
96
96
|
if (state.timeoutMs !== undefined) {
|
|
97
|
-
errors.push(`state "${name}" is thinking but declares timeoutMs — liveness timers apply to parked states only (thinking turns are bounded by
|
|
97
|
+
errors.push(`state "${name}" is thinking but declares timeoutMs — liveness timers apply to parked states only (thinking turns are bounded by Agent tickTimeoutMs)`);
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
if (state.kind === 'mechanical') {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every claimed wake must leave a recorded outcome.
|
|
3
|
+
*
|
|
4
|
+
* Claim + ack with no externally visible effect used to exist: the host
|
|
5
|
+
* consumed the delivery, the agent stayed silent, and metering had no row.
|
|
6
|
+
* A wake ends in exactly one of: a reply, task progress, or an explicit
|
|
7
|
+
* no-op with a reason.
|
|
8
|
+
*/
|
|
9
|
+
export type WakeOutcomeKind = 'replied' | 'task-progress' | 'no-op';
|
|
10
|
+
export interface WakeOutcome {
|
|
11
|
+
kind: WakeOutcomeKind;
|
|
12
|
+
reason?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function classifyWakeOutcome(input: {
|
|
15
|
+
skippedReason?: string;
|
|
16
|
+
lastOutcomeKind?: string | null;
|
|
17
|
+
llmCalls: number;
|
|
18
|
+
sentMessage?: boolean;
|
|
19
|
+
taskWrite?: boolean;
|
|
20
|
+
}): WakeOutcome;
|
|
21
|
+
export declare function buildWakeOutcomePayload(input: {
|
|
22
|
+
agentId: string;
|
|
23
|
+
sessionId: string;
|
|
24
|
+
laneKey: string;
|
|
25
|
+
outcome: WakeOutcome;
|
|
26
|
+
llmCalls?: number;
|
|
27
|
+
}): Record<string, unknown>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every claimed wake must leave a recorded outcome.
|
|
3
|
+
*
|
|
4
|
+
* Claim + ack with no externally visible effect used to exist: the host
|
|
5
|
+
* consumed the delivery, the agent stayed silent, and metering had no row.
|
|
6
|
+
* A wake ends in exactly one of: a reply, task progress, or an explicit
|
|
7
|
+
* no-op with a reason.
|
|
8
|
+
*/
|
|
9
|
+
export function classifyWakeOutcome(input) {
|
|
10
|
+
if (input.skippedReason) {
|
|
11
|
+
return { kind: 'no-op', reason: input.skippedReason };
|
|
12
|
+
}
|
|
13
|
+
if (input.sentMessage || input.lastOutcomeKind === 'message-sent') {
|
|
14
|
+
return { kind: 'replied' };
|
|
15
|
+
}
|
|
16
|
+
if (input.taskWrite ||
|
|
17
|
+
input.lastOutcomeKind === 'task-delegated' ||
|
|
18
|
+
input.lastOutcomeKind === 'task-completed' ||
|
|
19
|
+
input.lastOutcomeKind === 'task-progress') {
|
|
20
|
+
return { kind: 'task-progress' };
|
|
21
|
+
}
|
|
22
|
+
if (input.llmCalls > 0) {
|
|
23
|
+
return { kind: 'no-op', reason: 'llm_no_visible_effect' };
|
|
24
|
+
}
|
|
25
|
+
return { kind: 'no-op', reason: 'no_llm' };
|
|
26
|
+
}
|
|
27
|
+
export function buildWakeOutcomePayload(input) {
|
|
28
|
+
return {
|
|
29
|
+
source: 'agent',
|
|
30
|
+
chatId: input.sessionId,
|
|
31
|
+
sessionId: input.sessionId,
|
|
32
|
+
execution: {
|
|
33
|
+
llmCallsTotal: input.llmCalls ?? 0,
|
|
34
|
+
},
|
|
35
|
+
meta: {
|
|
36
|
+
effect: 'wake-outcome',
|
|
37
|
+
kind: input.outcome.kind,
|
|
38
|
+
...(input.outcome.reason ? { reason: input.outcome.reason } : {}),
|
|
39
|
+
laneKey: input.laneKey,
|
|
40
|
+
agentId: input.agentId,
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
@@ -61,9 +61,14 @@ export declare function skillHash(files: SkillFile[]): string;
|
|
|
61
61
|
export declare function parseSkill(files: SkillFile[], opts?: {
|
|
62
62
|
maxChars?: number;
|
|
63
63
|
}): ParsedSkill;
|
|
64
|
+
/** Catalog name for a reference path (`references/tone.md` → `tone`). */
|
|
65
|
+
export declare function skillReferenceName(path: string): string;
|
|
66
|
+
/** First prose line of a reference, for the on-prompt catalog. */
|
|
67
|
+
export declare function skillReferenceOneLine(content: string): string;
|
|
64
68
|
/**
|
|
65
|
-
* Assemble the skill's role prompt: body
|
|
66
|
-
*
|
|
69
|
+
* Assemble the skill's role prompt: the body, then a catalog of references.
|
|
70
|
+
* Full reference text is fetched with `skill_read_reference` — it does not
|
|
71
|
+
* ride every turn.
|
|
67
72
|
*
|
|
68
73
|
* This is only the ROLE block. The Ziggs protocol is prepended separately, by
|
|
69
74
|
* the one place that builds a Claude-brain system prompt — a skill is never in a
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* with the agent's full authority, so "instructions-only" has to be a structural
|
|
9
9
|
* property of what we accept, not a convention authors are trusted to follow.
|
|
10
10
|
*
|
|
11
|
-
* This module is deliberately free of
|
|
11
|
+
* This module is deliberately free of Agent / LLM / MCP imports so it stays
|
|
12
12
|
* unit-testable standalone (same rule as scripts/lib/mcp-provision.ts).
|
|
13
13
|
*/
|
|
14
14
|
import { createHash } from 'node:crypto';
|
|
@@ -124,9 +124,24 @@ export function parseSkill(files, opts = {}) {
|
|
|
124
124
|
}
|
|
125
125
|
return { name: name, description: description, body, references, hash: skillHash(files) };
|
|
126
126
|
}
|
|
127
|
+
/** Catalog name for a reference path (`references/tone.md` → `tone`). */
|
|
128
|
+
export function skillReferenceName(path) {
|
|
129
|
+
return path.replace(/^references\//, '').replace(/\.md$/, '');
|
|
130
|
+
}
|
|
131
|
+
/** First prose line of a reference, for the on-prompt catalog. */
|
|
132
|
+
export function skillReferenceOneLine(content) {
|
|
133
|
+
for (const line of content.split(/\r?\n/)) {
|
|
134
|
+
const trimmed = line.trim();
|
|
135
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
136
|
+
continue;
|
|
137
|
+
return trimmed.length > 160 ? `${trimmed.slice(0, 157)}…` : trimmed;
|
|
138
|
+
}
|
|
139
|
+
return '(empty)';
|
|
140
|
+
}
|
|
127
141
|
/**
|
|
128
|
-
* Assemble the skill's role prompt: body
|
|
129
|
-
*
|
|
142
|
+
* Assemble the skill's role prompt: the body, then a catalog of references.
|
|
143
|
+
* Full reference text is fetched with `skill_read_reference` — it does not
|
|
144
|
+
* ride every turn.
|
|
130
145
|
*
|
|
131
146
|
* This is only the ROLE block. The Ziggs protocol is prepended separately, by
|
|
132
147
|
* the one place that builds a Claude-brain system prompt — a skill is never in a
|
|
@@ -134,8 +149,10 @@ export function parseSkill(files, opts = {}) {
|
|
|
134
149
|
*/
|
|
135
150
|
export function skillRolePrompt(skill) {
|
|
136
151
|
const parts = [skill.body];
|
|
137
|
-
|
|
138
|
-
parts.push(
|
|
152
|
+
if (skill.references.length > 0) {
|
|
153
|
+
parts.push('## Skill references', 'These files are not in this prompt. Call skill_read_reference with the name when you need one. Do not invent their contents.', skill.references
|
|
154
|
+
.map((ref) => `- ${skillReferenceName(ref.path)}: ${skillReferenceOneLine(ref.content)}`)
|
|
155
|
+
.join('\n'));
|
|
139
156
|
}
|
|
140
157
|
return parts.join('\n\n');
|
|
141
158
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type ParsedSkill } from './parseSkill.js';
|
|
2
|
+
/**
|
|
3
|
+
* Fetch one skill reference by catalog name. The role prompt lists names and
|
|
4
|
+
* one line each; this is how the rest of the file is loaded.
|
|
5
|
+
*/
|
|
6
|
+
export declare function skillReadReferenceTool(skill: ParsedSkill): import("../tools/defineTool.js").ToolDefinition;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { defineTool } from '../tools/defineTool.js';
|
|
2
|
+
import { skillReferenceName, } from './parseSkill.js';
|
|
3
|
+
/**
|
|
4
|
+
* Fetch one skill reference by catalog name. The role prompt lists names and
|
|
5
|
+
* one line each; this is how the rest of the file is loaded.
|
|
6
|
+
*/
|
|
7
|
+
export function skillReadReferenceTool(skill) {
|
|
8
|
+
const byName = new Map(skill.references.map((ref) => [skillReferenceName(ref.path), ref]));
|
|
9
|
+
return defineTool('skill_read_reference', {
|
|
10
|
+
name: {
|
|
11
|
+
type: 'string',
|
|
12
|
+
required: true,
|
|
13
|
+
description: 'Reference name from the skill catalog (filename without .md)',
|
|
14
|
+
},
|
|
15
|
+
}, async (args) => {
|
|
16
|
+
const name = String(args['name'] ?? '').trim();
|
|
17
|
+
const ref = byName.get(name);
|
|
18
|
+
if (!ref) {
|
|
19
|
+
const available = [...byName.keys()];
|
|
20
|
+
throw new Error(`unknown skill reference "${name}". Available: ${available.join(', ') || '(none)'}`);
|
|
21
|
+
}
|
|
22
|
+
return { name, path: ref.path, content: ref.content };
|
|
23
|
+
}, {
|
|
24
|
+
description: 'Read one skill reference by name. The catalog of names is in your role prompt.',
|
|
25
|
+
isGenericFallback: true,
|
|
26
|
+
});
|
|
27
|
+
}
|
package/dist/tools/needs.js
CHANGED
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
* Scope is agents built on this SDK.
|
|
12
12
|
*/
|
|
13
13
|
import { toolName } from './defineTool.js';
|
|
14
|
-
import { connectionListGrantsTool, connectionProxyTool } from '../
|
|
15
|
-
import { MCP_CONNECTION_TOOLS } from '../
|
|
14
|
+
import { connectionListGrantsTool, connectionProxyTool } from '../platform/tools/tier2/connectionTools.js';
|
|
15
|
+
import { MCP_CONNECTION_TOOLS } from '../platform/tools/tier2/mcpConnectionTools.js';
|
|
16
16
|
/**
|
|
17
17
|
* A remote MCP server is stored as `provider: 'mcp'` and goes through the
|
|
18
18
|
* gateway. `connection_proxy` resolves a NAMED connector and refuses it with
|
|
@@ -38,6 +38,11 @@ export interface RecordReportResult {
|
|
|
38
38
|
* When `deliverable` is provided, also writes a task-bound result artifact
|
|
39
39
|
* via ArtifactsClient so it is retrievable as `via=task:<taskId>`.
|
|
40
40
|
*
|
|
41
|
+
* One deliverable per task: the idempotency key is `task-result:<taskId>`.
|
|
42
|
+
* A retry after a partial failure reuses that row (intentional). A later
|
|
43
|
+
* revision of the same task still returns the original artifact — change the
|
|
44
|
+
* key if you mean a new deliverable; do not change this helper's key.
|
|
45
|
+
*
|
|
41
46
|
* Implements the B1 convention (Agents/REPORTING.md).
|
|
42
47
|
*/
|
|
43
48
|
export declare function recordReport(input: RecordReportInput): Promise<RecordReportResult>;
|
|
@@ -6,6 +6,11 @@ import { ArtifactsClient, updateTaskState, } from '@ziggs-ai/api-client';
|
|
|
6
6
|
* When `deliverable` is provided, also writes a task-bound result artifact
|
|
7
7
|
* via ArtifactsClient so it is retrievable as `via=task:<taskId>`.
|
|
8
8
|
*
|
|
9
|
+
* One deliverable per task: the idempotency key is `task-result:<taskId>`.
|
|
10
|
+
* A retry after a partial failure reuses that row (intentional). A later
|
|
11
|
+
* revision of the same task still returns the original artifact — change the
|
|
12
|
+
* key if you mean a new deliverable; do not change this helper's key.
|
|
13
|
+
*
|
|
9
14
|
* Implements the B1 convention (Agents/REPORTING.md).
|
|
10
15
|
*/
|
|
11
16
|
export async function recordReport(input) {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* A custom tool builds `{ operatorKey, agentId }` by hand and stops there, so
|
|
5
5
|
* every call it makes runs on the narrowest reach fence there is: the agent's
|
|
6
|
-
* own org. `
|
|
6
|
+
* own org. `Agent` already threads the wake's lane into the tool context
|
|
7
7
|
* (as `chatId` / `sessionId`, which is what the capability adapter reads), and
|
|
8
8
|
* the backend's fence is "own org ∪ the orgs of the parties to this lane" — so
|
|
9
9
|
* a tool that does not pass it loses reach it is entitled to, and loses it
|
package/dist/tools/toolCreds.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* A custom tool builds `{ operatorKey, agentId }` by hand and stops there, so
|
|
5
5
|
* every call it makes runs on the narrowest reach fence there is: the agent's
|
|
6
|
-
* own org. `
|
|
6
|
+
* own org. `Agent` already threads the wake's lane into the tool context
|
|
7
7
|
* (as `chatId` / `sessionId`, which is what the capability adapter reads), and
|
|
8
8
|
* the backend's fence is "own org ∪ the orgs of the parties to this lane" — so
|
|
9
9
|
* a tool that does not pass it loses reach it is entitled to, and loses it
|