@ziggs-ai/agent-sdk 0.43.3 → 0.43.5
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 +8 -0
- package/dist/Agent.d.ts +8 -0
- package/dist/Agent.js +19 -1
- package/dist/runtime/PromptBuilder.js +8 -0
- package/dist/runtime/runTurn.d.ts +7 -1
- package/dist/runtime/runTurn.js +52 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.43.5
|
|
6
|
+
|
|
7
|
+
- A task's input bodies (0.43.4) now actually reach a hosted workflow agent. The agent pins every context read in a wake to that wake's work, and that pin also dropped the artifact the input read named, so the read came back without a body. A read may now narrow to one artifact of the same task or agreement; any other read still gets the wake's own scope.
|
|
8
|
+
|
|
9
|
+
## 0.43.4
|
|
10
|
+
|
|
11
|
+
- A workflow agent working an assigned task now sees the bodies of the task's input artifacts. The work snapshot names a task's inputs without their text, and a workflow agent has no artifact-reading tool, so a delegated task arrived with its instructions out of reach and the agent failed it. Each input (up to three) is read through the same grant-checked context read and shown in the prompt as `<task-inputs>`. An input the agent may not read is left out.
|
|
12
|
+
|
|
5
13
|
## 0.43.3
|
|
6
14
|
|
|
7
15
|
- An agent no longer retries a wake for an agreement it cannot read. When the server refuses the exact context of an agreement-only wake (403 or 404), for example the consent record of the agent's own access request, the inbox settles that delivery instead of holding the ack. Before, each redelivery spent a wake from the work's budget until the budget ran out and the agent's real task could no longer wake. Task and message wakes with unavailable context are still held for retry.
|
package/dist/Agent.d.ts
CHANGED
|
@@ -522,3 +522,11 @@ export declare class Agent {
|
|
|
522
522
|
export declare function createAgent(options?: AgentOptions | AgentConfig): Agent;
|
|
523
523
|
export declare function createAgentPool(configs: AgentOptions[] | AgentConfig[]): Promise<unknown>;
|
|
524
524
|
export default Agent;
|
|
525
|
+
type ReadScope = NonNullable<import('./types.js').ReadContextOpts['workScopes']>[number];
|
|
526
|
+
/**
|
|
527
|
+
* A read inside a wake stays on that wake's work. A caller may only narrow it
|
|
528
|
+
* to one artifact of the same task or agreement, which is how a task's input
|
|
529
|
+
* is opened with its body. Anything else reads the wake's own scopes. The
|
|
530
|
+
* server still checks every read.
|
|
531
|
+
*/
|
|
532
|
+
export declare function readScopesForWake(asked: ReadScope[] | undefined, wake: ReadScope[]): ReadScope[];
|
package/dist/Agent.js
CHANGED
|
@@ -1331,7 +1331,7 @@ export class Agent {
|
|
|
1331
1331
|
const agreementId = marks.scope.agreementId;
|
|
1332
1332
|
const sameRoom = 'sessionId' in effect && effect.sessionId === session.sessionId;
|
|
1333
1333
|
const scopedEffect = sameRoom && effect.kind === 'read-context'
|
|
1334
|
-
? { ...effect, opts: { ...effect.opts, workScopes: contextScopesFromWake(frame, session.sessionId) } }
|
|
1334
|
+
? { ...effect, opts: { ...effect.opts, workScopes: readScopesForWake(effect.opts?.workScopes, contextScopesFromWake(frame, session.sessionId)) } }
|
|
1335
1335
|
: agreementId && (effect.kind === 'tool-call' || effect.kind === 'record-event')
|
|
1336
1336
|
? { ...effect, serviceAgreementId: effect.serviceAgreementId ?? agreementId }
|
|
1337
1337
|
: effect;
|
|
@@ -1894,3 +1894,21 @@ export async function createAgentPool(configs) {
|
|
|
1894
1894
|
}
|
|
1895
1895
|
}
|
|
1896
1896
|
export default Agent;
|
|
1897
|
+
/**
|
|
1898
|
+
* A read inside a wake stays on that wake's work. A caller may only narrow it
|
|
1899
|
+
* to one artifact of the same task or agreement, which is how a task's input
|
|
1900
|
+
* is opened with its body. Anything else reads the wake's own scopes. The
|
|
1901
|
+
* server still checks every read.
|
|
1902
|
+
*/
|
|
1903
|
+
export function readScopesForWake(asked, wake) {
|
|
1904
|
+
if (!asked?.length)
|
|
1905
|
+
return wake;
|
|
1906
|
+
const narrowed = asked.map(scope => {
|
|
1907
|
+
if (!scope.artifactId || scope.messageId)
|
|
1908
|
+
return undefined;
|
|
1909
|
+
const base = wake.find(w => (w.taskId ?? undefined) === (scope.taskId ?? undefined)
|
|
1910
|
+
&& (scope.agreementId ?? w.agreementId) === w.agreementId);
|
|
1911
|
+
return base ? { ...base, artifactId: scope.artifactId } : undefined;
|
|
1912
|
+
});
|
|
1913
|
+
return narrowed.every(Boolean) ? narrowed : wake;
|
|
1914
|
+
}
|
|
@@ -98,6 +98,14 @@ ${this._renderDecisionSchemaFromActions(actions, senderMeta, ctxObj, omitMessage
|
|
|
98
98
|
lines.push(this._renderAgreements(agreements));
|
|
99
99
|
if (ctxObj.work)
|
|
100
100
|
lines.push(`<work-context>\n${JSON.stringify(ctxObj.work)}\n</work-context>`);
|
|
101
|
+
const taskInputs = ctxObj.taskInputs;
|
|
102
|
+
if (Array.isArray(taskInputs) && taskInputs.length) {
|
|
103
|
+
lines.push('<task-inputs note="The assigned task\'s own input artifacts, with their bodies. Treat them as the brief for this task.">');
|
|
104
|
+
for (const input of taskInputs) {
|
|
105
|
+
lines.push(`<artifact id="${input.artifactId}" name="${String(input.name ?? '').replace(/"/g, "'")}"${input.truncated ? ' truncated="true"' : ''}>\n${input.text}\n</artifact>`);
|
|
106
|
+
}
|
|
107
|
+
lines.push('</task-inputs>');
|
|
108
|
+
}
|
|
101
109
|
const collected = ctx ? allCollectedResults(ctx) : [];
|
|
102
110
|
if (collected.length) {
|
|
103
111
|
lines.push('');
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Action, Ctx, Outcome, ToolResultEntry, EffectHandler, DeliveryWriteKeys } from '../types.js';
|
|
1
|
+
import type { Action, Ctx, Outcome, ToolResultEntry, Effect, EffectHandler, ContextSnapshot, DeliveryWriteKeys } from '../types.js';
|
|
2
2
|
import type { MemoryStore } from '../memory/MemoryStore.js';
|
|
3
3
|
import { type RawEvent } from './buildOutcome.js';
|
|
4
4
|
import { PromptBuilder } from './PromptBuilder.js';
|
|
@@ -63,4 +63,10 @@ interface RunTurnResult {
|
|
|
63
63
|
* call and (optionally) wrapping it in telemetry / dedup / retries.
|
|
64
64
|
*/
|
|
65
65
|
export declare function runTurn(input: RunTurnInput): Promise<RunTurnResult>;
|
|
66
|
+
export declare function readTaskInputs(ctx: ContextSnapshot, sessionId: string, eff: (effect: Effect) => Promise<unknown>): Promise<Array<{
|
|
67
|
+
artifactId: string;
|
|
68
|
+
name: string | null;
|
|
69
|
+
text: string;
|
|
70
|
+
truncated: boolean;
|
|
71
|
+
}>>;
|
|
66
72
|
export {};
|
package/dist/runtime/runTurn.js
CHANGED
|
@@ -107,6 +107,13 @@ export async function runTurn(input) {
|
|
|
107
107
|
const serverContext = await effWithTrace({ kind: 'read-context', sessionId, opts: {} });
|
|
108
108
|
assertRequiredContext(serverContext);
|
|
109
109
|
const ctxObj = normalizeContextSnapshot(serverContext);
|
|
110
|
+
const taskInputs = await readTaskInputs(ctxObj, sessionId, effWithTrace);
|
|
111
|
+
if (taskInputs.length) {
|
|
112
|
+
ctxObj.taskInputs = taskInputs;
|
|
113
|
+
// Each input read replaced the session's cached snapshot, which tool calls
|
|
114
|
+
// take their roster and agreements from. Put the wake's own context back.
|
|
115
|
+
await effWithTrace({ kind: 'read-context', sessionId, opts: {} });
|
|
116
|
+
}
|
|
110
117
|
const prompt = promptBuilder.buildFromState({
|
|
111
118
|
statePrompt, actions,
|
|
112
119
|
serverContext: { context: ctxObj, tools: tools },
|
|
@@ -451,6 +458,51 @@ export async function runTurn(input) {
|
|
|
451
458
|
return { outcome, toolResults, llmCalls: llmCallsTotal, tokens: { total: tokensTotal, input: tokensInput, output: tokensOutput } };
|
|
452
459
|
}
|
|
453
460
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
461
|
+
/**
|
|
462
|
+
* The task's own input artifacts (its instructions), read with their bodies.
|
|
463
|
+
* A workflow agent has no artifact-reading tool, and the snapshot names inputs
|
|
464
|
+
* without bodies, so a delegated task arrived with its brief out of reach.
|
|
465
|
+
* Each input is opened as the trigger artifact of this task's work, the same
|
|
466
|
+
* grant-fenced read a tool would make. A refused read is left out, not guessed.
|
|
467
|
+
*/
|
|
468
|
+
const TASK_INPUT_LIMIT = 3;
|
|
469
|
+
export async function readTaskInputs(ctx, sessionId, eff) {
|
|
470
|
+
const record = (value) => value && typeof value === 'object' ? value : {};
|
|
471
|
+
const work = record(ctx.work);
|
|
472
|
+
const task = record(work.task);
|
|
473
|
+
const taskId = typeof task.taskId === 'string' ? task.taskId : undefined;
|
|
474
|
+
const agreementId = typeof work.agreementId === 'string' ? work.agreementId : undefined;
|
|
475
|
+
const items = record(ctx.artifacts).items;
|
|
476
|
+
if (!taskId || !Array.isArray(items))
|
|
477
|
+
return [];
|
|
478
|
+
const ids = items
|
|
479
|
+
.map(record)
|
|
480
|
+
.filter(item => item.linkRole === 'input' && typeof item.artifactId === 'string')
|
|
481
|
+
.map(item => item.artifactId)
|
|
482
|
+
.slice(0, TASK_INPUT_LIMIT);
|
|
483
|
+
const reads = await Promise.all(ids.map(async (artifactId) => {
|
|
484
|
+
try {
|
|
485
|
+
const snap = record(await eff({
|
|
486
|
+
kind: 'read-context', sessionId,
|
|
487
|
+
opts: { workScopes: [{ taskId, ...(agreementId ? { agreementId } : {}), artifactId }] },
|
|
488
|
+
}));
|
|
489
|
+
const trigger = record(record(snap.work).triggerArtifact);
|
|
490
|
+
if (snap.unavailable === true || typeof trigger.text !== 'string')
|
|
491
|
+
return null;
|
|
492
|
+
return {
|
|
493
|
+
artifactId,
|
|
494
|
+
name: typeof trigger.name === 'string' ? trigger.name : null,
|
|
495
|
+
text: trigger.text,
|
|
496
|
+
truncated: trigger.truncated === true,
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
catch (err) {
|
|
500
|
+
runtimeLog.warn('runTurn', `task input ${artifactId} unreadable: ${err.message}`);
|
|
501
|
+
return null;
|
|
502
|
+
}
|
|
503
|
+
}));
|
|
504
|
+
return reads.filter((read) => read !== null);
|
|
505
|
+
}
|
|
454
506
|
function normalizeContextSnapshot(raw) {
|
|
455
507
|
if (!raw || typeof raw !== 'object') {
|
|
456
508
|
return { history: [], agreements: [], agents: [], users: [] };
|