@alvin0/ai-agent-sdk-core 0.1.4 → 0.1.6
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/README.md +13 -1
- package/dist/agent/define/definition.d.ts +1 -1
- package/dist/agent/define/definition.js +1 -1
- package/dist/agent/define/definition.js.map +1 -1
- package/dist/agent/define/session/types.d.ts +6 -0
- package/dist/agent/define/session/types.d.ts.map +1 -1
- package/dist/agent/define/session.d.ts +39 -2
- package/dist/agent/define/session.d.ts.map +1 -1
- package/dist/agent/define/session.js +1 -1
- package/dist/agent/define/session.js.map +1 -1
- package/dist/agent/loop/admission.js +2 -0
- package/dist/agent/loop/admission.js.map +1 -0
- package/dist/agent/loop/events.d.ts +4 -0
- package/dist/agent/loop/events.d.ts.map +1 -1
- package/dist/agent/loop/program.js +2 -0
- package/dist/agent/loop/program.js.map +1 -0
- package/dist/agent/loop/run-turn.d.ts.map +1 -1
- package/dist/agent/loop/run-turn.js +1 -1
- package/dist/agent/loop/run-turn.js.map +1 -1
- package/dist/agent/loop/schedule.d.ts.map +1 -1
- package/dist/agent/loop/schedule.js +2 -2
- package/dist/agent/loop/schedule.js.map +1 -1
- package/dist/agent/loop/tool-call-support.js +2 -0
- package/dist/agent/loop/tool-call-support.js.map +1 -0
- package/dist/agent/loop/turn/hooks.js +1 -1
- package/dist/agent/loop/turn/hooks.js.map +1 -1
- package/dist/agent/loop/turn/model-request-boundary.js +2 -0
- package/dist/agent/loop/turn/model-request-boundary.js.map +1 -0
- package/dist/agent/loop/turn/model-round.js +1 -1
- package/dist/agent/loop/turn/model-round.js.map +1 -1
- package/dist/agent/loop/turn/step-projection.js +2 -0
- package/dist/agent/loop/turn/step-projection.js.map +1 -0
- package/dist/agent/loop/turn/types.d.ts +6 -0
- package/dist/agent/loop/turn/types.d.ts.map +1 -1
- package/dist/agent/memory/context-optimizer.d.ts +61 -0
- package/dist/agent/memory/context-optimizer.d.ts.map +1 -0
- package/dist/agent/memory/context-optimizer.js +4 -0
- package/dist/agent/memory/context-optimizer.js.map +1 -0
- package/dist/agent/memory/memory.js +1 -1
- package/dist/agent/memory/memory.js.map +1 -1
- package/dist/agent/mode/run-agent.d.ts +15 -1
- package/dist/agent/mode/run-agent.d.ts.map +1 -1
- package/dist/agent/mode/run-agent.js +2 -2
- package/dist/agent/mode/run-agent.js.map +1 -1
- package/dist/agent/skill/provider/snapshot.js +1 -1
- package/dist/agent/team/contracts.d.ts +5 -0
- package/dist/agent/team/contracts.d.ts.map +1 -1
- package/dist/agent/team/managed.d.ts +33 -64
- package/dist/agent/team/managed.d.ts.map +1 -1
- package/dist/agent/team/managed.js +6 -3
- package/dist/agent/team/managed.js.map +1 -1
- package/dist/agent/team/team.d.ts +2 -0
- package/dist/agent/team/team.d.ts.map +1 -1
- package/dist/agent/team/team.js +1 -1
- package/dist/agent/team/team.js.map +1 -1
- package/dist/agent/team/types.d.ts +1 -0
- package/dist/agent/team/types.d.ts.map +1 -1
- package/dist/agent/tool/action-fusion.d.ts +30 -0
- package/dist/agent/tool/action-fusion.d.ts.map +1 -0
- package/dist/agent/tool/action-fusion.js +2 -0
- package/dist/agent/tool/action-fusion.js.map +1 -0
- package/dist/agent/tool/capture.js +1 -1
- package/dist/agent/tool/capture.js.map +1 -1
- package/dist/agent/tool/definition.d.ts +8 -0
- package/dist/agent/tool/definition.d.ts.map +1 -1
- package/dist/agent/tool/definition.js.map +1 -1
- package/dist/agent/tool/evidence-reducer.d.ts +41 -0
- package/dist/agent/tool/evidence-reducer.d.ts.map +1 -0
- package/dist/agent/tool/evidence-reducer.js +7 -0
- package/dist/agent/tool/evidence-reducer.js.map +1 -0
- package/dist/agent/tool/nested.d.ts +117 -0
- package/dist/agent/tool/nested.d.ts.map +1 -0
- package/dist/agent/tool/nested.js +2 -0
- package/dist/agent/tool/nested.js.map +1 -0
- package/dist/agent/tool/output-budget.d.ts.map +1 -1
- package/dist/agent/tool/output-budget.js +2 -2
- package/dist/agent/tool/output-budget.js.map +1 -1
- package/dist/agent/tool/output-schema.js +2 -0
- package/dist/agent/tool/output-schema.js.map +1 -0
- package/dist/agent/tool/pipeline.d.ts +7 -0
- package/dist/agent/tool/pipeline.d.ts.map +1 -1
- package/dist/agent/tool/pipeline.js +2 -2
- package/dist/agent/tool/pipeline.js.map +1 -1
- package/dist/agent/tool/program-results.js +2 -0
- package/dist/agent/tool/program-results.js.map +1 -0
- package/dist/agent.d.ts +6 -2
- package/dist/agent.js +1 -1
- package/dist/composition/agent/definition.js +1 -1
- package/dist/composition/agent/options.js +1 -1
- package/dist/composition/agent/options.js.map +1 -1
- package/dist/composition/agent/session.js +1 -1
- package/dist/composition/agent/session.js.map +1 -1
- package/dist/composition/agent/types.d.ts +6 -0
- package/dist/composition/agent/types.d.ts.map +1 -1
- package/dist/composition/tool-source/snapshot.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/memory.d.ts +2 -1
- package/dist/memory.js +1 -1
- package/dist/observability/bus.js +1 -1
- package/dist/primitives/version.d.ts +1 -1
- package/dist/primitives/version.js +1 -1
- package/dist/primitives/version.js.map +1 -1
- package/dist/runtime/registry.js +1 -1
- package/dist/tools.d.ts +4 -1
- package/dist/tools.js +1 -1
- package/package.json +1 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{createSpanId as e}from"../../../observation/context.js";import{MODEL_ERROR_CODES as t}from"../../../errors/model-error.js";import{BlockAssembler as n}from"../../../stream/assembler.js";import{normalizeToolPairing as r}from"../../history/normalize.js";import{positiveFinite as
|
|
1
|
+
import{createSpanId as e}from"../../../observation/context.js";import{MODEL_ERROR_CODES as t}from"../../../errors/model-error.js";import{BlockAssembler as n}from"../../../stream/assembler.js";import{normalizeToolPairing as r}from"../../history/normalize.js";import{observeModelRequestBoundary as i}from"./model-request-boundary.js";import{stepProjectionSources as a}from"./step-projection.js";import{positiveFinite as o,positiveSafeInteger as s}from"./config.js";import{messageOf as c,modelAbortedFinish as ee,modelFailureFinish as l,now as u,serializedBytes as d}from"./common.js";import{StreamAbortError as te,closeIterator as ne,nextWithAbort as re}from"./cancellation.js";import{runOptionalHook as f}from"./hooks.js";import{classifyTextPhases as p,contentTiming as m,createAssistant as ie,dropDuplicateToolCalls as ae,invalidHostToolCall as oe,recentToolResultIds as se,systemText as ce,textOf as h}from"./content.js";import{validateStreamChunk as le}from"./validation.js";import{accountingUsageStop as ue}from"./usage-stop.js";function g(e){let t=e.replace(/\s+/gu,` `).trim();return t.length>600?`${t.slice(0,600)}…`:t}function _(e){let t=[],n=[];for(let r of e.content)r.type===`text`||r.type===`reasoning`?t.push(r.text):r.type===`tool-call`?n.push(`${r.name}(${g(r.arguments)})`):r.type===`tool-result`?t.push(h(r.content)):t.push(`[${r.type}]`);return{role:e.role,producer:e.source.kind,...t.length===0?{}:{text:g(t.join(` `))},...n.length===0?{}:{toolCalls:n}}}function de(e){let t=e.messages??[],n=Math.max(0,t.length-8);return{messageCount:t.length,...t.at(-1)===void 0?{}:{lastMessageId:t.at(-1)?.id},...e.system===void 0?{}:{system:g(e.system)},...e.tools===void 0||e.tools.length===0?{}:{tools:e.tools.map(e=>e.name)},...e.toolChoice===void 0?{}:{toolChoice:e.toolChoice},...n===0?{}:{earlierMessagesOmitted:n},messages:t.slice(-8).map(_)}}async function v(g,_,v,pe,me,y,b,x){let S=x===`forced-final`,C=x===`final`||S,w={traceId:me.traceId,spanId:e(),parentSpanId:me.spanId},T=r(g.history.messages()),he=T,E=se(g.history),ge=g.history.generation(),_e=g.history.entries().length,D=()=>{let e=ue(g.accounting);if(!(e===void 0&&!_.aborted))return{trace:w,finish:_.aborted?{kind:`aborted`,failure:{code:`ABORTED`,message:c(_.reason)}}:{kind:`stop`},calls:[],afterToolCallIds:E,timing:m(!1,E.length>0),...e?.kind===`error`?{usageRequired:!0}:{},...e?.kind===`usage-unavailable`?{usageUnavailable:!0}:{}}},O=D();if(O!==void 0)return O;let k=await f(g.hooks?.beforeStep,[{turn:y,step:b,messages:T,snapshot:g.history.snapshot(),signal:_,...g.logger===void 0?{}:{logger:g.logger},emit:pe}],g,_,`beforeStep`),A=D();if(A!==void 0)return A;if(k?.kind===`reject`)return{trace:w,finish:{kind:`error`,failure:{message:k.reason,code:`STEP_REJECTED`}},calls:[],afterToolCallIds:E,timing:m(!1,E.length>0)};if((g.history.generation()!==ge||g.history.entries().length!==_e)&&(T=r(g.history.messages())),k?.messages!==void 0){let e=a(k)??new Set(he.map(e=>e.id)),t=new Set(k.messages.map(e=>e.id)),n=T.filter(n=>!e.has(n.id)&&!t.has(n.id)),i=new Set(T.map(e=>e.id)),o=[...e].some(e=>!i.has(e)),s=new Map(k.messages.map(e=>[e.id,e]));T=r(o?[...k.messages.filter(t=>!e.has(t.id)&&!i.has(t.id)&&t.source.kind===`app`),...T.flatMap(t=>{let n=s.get(t.id);return n===void 0?e.has(t.id)?[]:[t]:[n]})]:[...k.messages,...n])}k?.prepend!==void 0&&(T=Object.freeze([...k.prepend,...T]));let j=ce(g,S),M=[...g.tools?.schemas()??[],...g.nativeTools??[]],N=!C&&g.outputFormat?.type===`json_schema`?{type:`text`}:g.outputFormat,P={...g.config,messages:T,...j.length===0?{}:{system:j},...M.length===0?{}:{tools:M},...C&&M.length>0?{toolChoice:`none`}:g.toolChoice===void 0?{}:{toolChoice:g.toolChoice},...g.imagePolicy===void 0?{}:{imagePolicy:g.imagePolicy},...g.documentPolicy===void 0?{}:{documentPolicy:g.documentPolicy},...N===void 0?{}:{outputFormat:N}},ve={...P,signal:_};i(g.history,!0);try{await f(g.hooks?.checkpoint,[{kind:`before-model-request`,request:ve,snapshot:g.history.snapshot(),signal:_,...g.logger===void 0?{}:{logger:g.logger}}],g,_,`checkpoint`)}catch(e){return i(g.history,!1),{trace:w,finish:{kind:`error`,failure:{message:`history checkpoint failed: ${c(e)}`,code:`CHECKPOINT_FAILED`}},calls:[],afterToolCallIds:E,timing:m(!1,E.length>0)}}let F=D();if(F!==void 0)return i(g.history,!1),F;await v({type:`span-start`,trace:w,at:u(),name:`chat ${g.config.model}`,kind:`chat`,attributes:{"gen_ai.operation.name":`chat`,"gen_ai.request.model":g.config.model,...g.config.reasoningEffort===void 0?{}:{"gen_ai.request.reasoning_effort":g.config.reasoningEffort},turn:y,step:b,forcedFinal:S,phase:x},input:de(P)}),await v({type:`step-start`,turn:y,step:b,trace:w,...S?{forcedFinal:!0}:{}});let I=new n,L=new Set,R=o(g.modelTimeoutMs??6e5,`modelTimeoutMs`),z=s(g.maxModelRequestBytes??33554432,`maxModelRequestBytes`),B=s(g.maxModelResponseBytes??33554432,`maxModelResponseBytes`),V=s(g.maxModelStreamEvents??1e5,`maxModelStreamEvents`),ye=o(g.teardownTimeoutMs??3e4,`teardownTimeoutMs`),H,be=!1,xe=!1,U;try{U=d(P)}catch(e){U=-1,I.push(l(`model request is not serializable: ${c(e)}`,`INVALID_MODEL_REQUEST`))}if(U>z)I.push(l(`model request exceeds the ${z}-byte limit`,`MODEL_REQUEST_TOO_LARGE`));else if(U>=0){let e=new AbortController,t=AbortSignal.any([_,e.signal]),n=!1,r=setTimeout(()=>{n=!0,e.abort(Error(`model stream exceeded ${R}ms`))},R),i=g.registry.stream({...P,signal:t},g.accounting?.modelInvocation),a=i[Symbol.asyncIterator](),o=!1,s=0,u=0,f=!1;try{for(;;){let n=await re(a.next(),t);if(n.done===!0){o=!0;break}let r=n.value;try{le(r,V)}catch(t){I.push(l(`model adapter emitted an invalid stream chunk: ${c(t)}`,`INVALID_MODEL_STREAM`)),e.abort(Error(`invalid model stream chunk`));break}s++;try{u+=d(r)}catch(t){I.push(l(`model adapter emitted a non-serializable stream chunk: ${c(t)}`,`INVALID_MODEL_STREAM`)),e.abort(Error(`non-serializable model stream chunk`));break}if(s>V||u>B){let t=s>V?`${V}-event`:`${B}-byte`;I.push(l(`model response exceeds the ${t} limit`,`MODEL_RESPONSE_TOO_LARGE`)),e.abort(Error(`model response resource limit exceeded`));break}if(f){I.push(l(`model adapter emitted data after its terminal finish chunk`,`INVALID_MODEL_STREAM`)),e.abort(Error(`invalid model stream`));break}I.push(r),!((r.type===`text-delta`||r.type===`reasoning-delta`||r.type===`tool-call-delta`)&&L.has(r.index))&&(r.type===`block-end`&&L.add(r.index),r.type===`finish`&&(f=!0),r.type===`text-delta`?await v({type:`text-delta`,index:r.index,text:r.text,phase:x===`process`?`commentary`:r.phase??`unknown`,trace:w}):r.type===`reasoning-delta`?await v({type:`reasoning-delta`,index:r.index,text:r.text,trace:w}):r.type===`image-delta`?await v({type:`image-delta`,itemId:r.itemId,data:r.data,mediaType:r.mediaType,...r.partialIndex===void 0?{}:{partialIndex:r.partialIndex},trace:w}):r.type===`usage`?await v({type:`usage`,usage:r.usage,trace:w}):r.type===`usage-progress`&&await v({...r,trace:w}))}}catch(e){if(!(e instanceof te))throw e;I.push(_.aborted?ee(c(_.reason??e)):n?l(`model stream exceeded ${R}ms`,`MODEL_TIMEOUT`):l(c(e.cause??e),`MODEL_STREAM_ABORTED`))}finally{clearTimeout(r),o||(e.abort(Error(`model stream closed before exhaustion`)),await ne(a,ye)||I.push(l(`model stream ignored cancellation for more than ${ye}ms; the adapter operation may still be running`,`MODEL_TEARDOWN_TIMEOUT`)))}let p=await i.report,m=g.accounting===void 0?void 0:await g.accounting.recordModelCall(p,{...P,signal:_});H=m?.report??p,be=m?.usageRequired??!1,xe=m?.usageUnavailable??!1}let W=I.finish,G=W.kind===`aborted`,K;try{K=W.kind===`aborted`?I.interruptedBlocks():I.blocks()}catch(e){W={kind:`error`,failure:{message:`model adapter emitted an invalid block sequence: ${c(e)}`,code:`INVALID_MODEL_STREAM`}},G=!0,K=I.interruptedBlocks()}let Se=I.textBlocks().filter(({block:e})=>!G||e.text.trim()!==``),Ce=0,we=p(K.map(e=>{if(e.type!==`text`)return e;let t=Se[Ce++];return e.phase===void 0&&t?.beforeNativeCall?{...e,phase:`commentary`}:e}),x===`process`,I.hasToolCalls),q=ae(we,g.history),J=q.blocks,Te=C&&g.outputFormat?.type===`json_schema`&&W.kind===`stop`&&!fe(h(J),g.validateOutput)?{message:`model returned invalid JSON or failed the structured output validator`,code:t.MALFORMED_RESPONSE}:void 0,Y=(C&&J.some(e=>e.type===`tool-call`)?{message:`model emitted a host tool call during the final output phase`,code:`INVALID_TOOL_CALL`}:void 0)??Te??oe(J,g.history),X=Y===void 0?J:J.filter(e=>e.type!==`tool-call`),Z=Y===void 0?W:{kind:`error`,failure:Y},Q=X.length===0&&(Z.kind===`error`||Z.kind===`aborted`)?void 0:ie(g,X,G?void 0:I.replayState),$=Q?.content.filter(e=>e.type===`tool-call`).map(e=>({callId:e.id,toolName:e.name,rawArguments:e.arguments}))??[],Ee=m($.length>0,E.length>0);if(X.some(e=>e.type===`text`)){let e=Se.map(({index:e})=>e),t=Z.kind===`error`||Z.kind===`aborted`||Z.kind===`max-tokens`,n=0;for(let r of X){if(r.type!==`text`)continue;let i=e[n++];i!==void 0&&await v({type:`text-end`,index:i,text:r.text,phase:r.phase??`final-answer`,trace:w,...t?{incomplete:!0}:{}})}}return await v({type:`span-end`,trace:w,at:u(),status:Z.kind===`error`?`error`:Z.kind===`aborted`?`aborted`:`success`,output:Q===void 0?void 0:{text:h(Q.content),commentary:Q.content.flatMap(e=>e.type===`text`&&e.phase===`commentary`?[e.text]:[]),reasoning:Q.content.flatMap(e=>e.type===`reasoning`?[e.text]:[]),toolCalls:$.length},...I.usage===void 0?{}:{usage:I.usage},...Z.kind===`error`||Z.kind===`aborted`?{error:{type:`ModelError`,message:Z.failure.message,code:Z.failure.code}}:{}}),($.length===0||C)&&await v({type:`step-end`,turn:y,step:b,trace:w}),{trace:w,...Q===void 0?{}:{message:Q},finish:Z,...I.usage===void 0?{}:{usage:I.usage},...H===void 0?{}:{report:H},...be?{usageRequired:!0}:{},...xe?{usageUnavailable:!0}:{},calls:$,afterToolCallIds:E,timing:Ee,...q.dropped.length===0?{}:{droppedDuplicateCalls:q.dropped}}}function fe(e,t){try{let n=JSON.parse(e);return t?.(n),!0}catch{return!1}}export{v as modelRound};
|
|
2
2
|
//# sourceMappingURL=model-round.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-round.js","names":[],"sources":["../../../../src/agent/loop/turn/model-round.ts"],"sourcesContent":["import type { GenerateOptions } from '../../../contract/index.ts'\nimport { MODEL_ERROR_CODES, type ModelFailure } from '../../../errors/index.ts'\nimport type { ContentBlock, Message, ToolCallBlock } from '../../../message/index.ts'\nimport { BlockAssembler } from '../../../stream/index.ts'\nimport type { FinishReason } from '../../../stream/index.ts'\nimport type { ModelCallReport } from '../../../observation/index.ts'\nimport { normalizeToolPairing } from '../../history/normalize.ts'\nimport { createSpanId, type TraceRef } from '../../trace/trace.ts'\nimport type { AgentEvent, AgentMaintenanceEvent } from '../types.ts'\nimport { type ModelRoundPhase, type RunTurnOptions, type RoundResult } from './types.ts'\nimport { positiveFinite, positiveSafeInteger } from './config.ts'\nimport { serializedBytes, modelFailureFinish, modelAbortedFinish, messageOf, now } from './common.ts'\nimport { validateStreamChunk } from './validation.ts'\nimport { StreamAbortError, nextWithAbort, closeIterator } from './cancellation.ts'\nimport { runOptionalHook } from './hooks.ts'\nimport { accountingUsageStop } from './usage-stop.ts'\nimport {\n classifyTextPhases, dropDuplicateToolCalls, invalidHostToolCall, contentTiming,\n recentToolResultIds, systemText,\n createAssistant, textOf,\n} from './content.ts'\n\n/** How much of one message the trace keeps. */\nconst PREVIEW_CHARS = 600\n\n/** How many trailing messages the trace keeps. */\nconst PREVIEW_MESSAGES = 8\n\nfunction clip(text: string): string {\n const flattened = text.replace(/\\s+/gu, ' ').trim()\n return flattened.length > PREVIEW_CHARS ? `${flattened.slice(0, PREVIEW_CHARS)}…` : flattened\n}\n\n/** One message as a line: who spoke, and what the model could read of it. */\nfunction previewMessage(message: Message): Record<string, unknown> {\n const parts: string[] = []\n const calls: string[] = []\n for (const block of message.content) {\n if (block.type === 'text' || block.type === 'reasoning') parts.push(block.text)\n else if (block.type === 'tool-call') calls.push(`${block.name}(${clip(block.arguments)})`)\n else if (block.type === 'tool-result') parts.push(textOf(block.content))\n else parts.push(`[${block.type}]`)\n }\n return {\n role: message.role,\n producer: message.source.kind,\n ...parts.length === 0 ? {} : { text: clip(parts.join(' ')) },\n ...calls.length === 0 ? {} : { toolCalls: calls },\n }\n}\n\n/**\n * What this round sent the model, in a form a person can read.\n *\n * A summary, not a copy: the request can be the whole conversation plus every\n * tool schema, and a trace that stored it verbatim would be larger than the\n * work it describes. What survives is the part that explains the round — which\n * tools were on offer, how long the context was, and the tail of the\n * conversation the model was actually answering, each message clipped.\n * @param request - The request about to be dispatched.\n * @returns The summary for the span.\n */\nfunction requestSummary(request: GenerateOptions): Record<string, unknown> {\n const messages = request.messages ?? []\n const dropped = Math.max(0, messages.length - PREVIEW_MESSAGES)\n return {\n messageCount: messages.length,\n // The identity of the request, for a host that records the provider call\n // separately and has to say WHICH round each recording belongs to. Message\n // ids are stable across every representation boundary, so the last one plus\n // the count names one round of one agent without ambiguity.\n ...messages.at(-1) === undefined ? {} : { lastMessageId: messages.at(-1)?.id },\n ...request.system === undefined ? {} : { system: clip(request.system) },\n ...request.tools === undefined || request.tools.length === 0\n ? {}\n : { tools: request.tools.map(tool => tool.name) },\n ...request.toolChoice === undefined ? {} : { toolChoice: request.toolChoice },\n ...dropped === 0 ? {} : { earlierMessagesOmitted: dropped },\n messages: messages.slice(-PREVIEW_MESSAGES).map(previewMessage),\n }\n}\n\nexport async function modelRound(\n options: RunTurnOptions,\n signal: AbortSignal,\n emit: (event: AgentEvent) => Promise<void>,\n emitMaintenance: (event: AgentMaintenanceEvent) => Promise<void>,\n root: TraceRef,\n turn: number,\n step: number,\n phase: ModelRoundPhase,\n): Promise<RoundResult> {\n const forcedFinal = phase === 'forced-final'\n const finalOutput = phase === 'final' || forcedFinal\n const trace: TraceRef = { traceId: root.traceId, spanId: createSpanId(), parentSpanId: root.spanId }\n let messages = normalizeToolPairing(options.history.messages())\n const afterToolCallIds = recentToolResultIds(options.history)\n const generation = options.history.generation()\n const entries = options.history.entries().length\n const stopped = (): RoundResult | undefined => {\n const stop = accountingUsageStop(options.accounting)\n if (stop === undefined && !signal.aborted) return undefined\n return {\n trace, finish: signal.aborted\n ? { kind: 'aborted', failure: { code: 'ABORTED', message: messageOf(signal.reason) } }\n : { kind: 'stop' },\n calls: [], afterToolCallIds, timing: contentTiming(false, afterToolCallIds.length > 0),\n ...(stop?.kind === 'error' ? { usageRequired: true } : {}),\n ...(stop?.kind === 'usage-unavailable' ? { usageUnavailable: true } : {}),\n }\n }\n const beforeMaintenance = stopped()\n if (beforeMaintenance !== undefined) return beforeMaintenance\n const decision = await runOptionalHook(options.hooks?.beforeStep, [{\n turn, step, messages, snapshot: options.history.snapshot(), signal,\n ...(options.logger === undefined ? {} : { logger: options.logger }),\n emit: emitMaintenance,\n }], options, signal, 'beforeStep')\n const afterMaintenance = stopped()\n if (afterMaintenance !== undefined) return afterMaintenance\n if (decision?.kind === 'reject') {\n const failure: ModelFailure = { message: decision.reason, code: 'STEP_REJECTED' }\n return {\n trace, finish: { kind: 'error', failure }, calls: [], afterToolCallIds,\n timing: contentTiming(false, afterToolCallIds.length > 0),\n }\n }\n // Hooks may append live steering as well as replacing compacted history.\n // Refresh for either mutation so new user input stays at the chronological\n // tail instead of needing to be prepended ahead of older history.\n if (options.history.generation() !== generation\n || options.history.entries().length !== entries) {\n messages = normalizeToolPairing(options.history.messages())\n }\n if (decision?.prepend !== undefined) messages = Object.freeze([...decision.prepend, ...messages])\n const system = systemText(options, forcedFinal)\n const availableTools = [\n ...options.tools?.schemas() ?? [],\n ...options.nativeTools ?? [],\n ]\n const tools = availableTools\n const outputFormat = !finalOutput && options.outputFormat?.type === 'json_schema'\n ? { type: 'text' as const }\n : options.outputFormat\n const requestBase: GenerateOptions = {\n ...options.config,\n messages,\n ...system.length === 0 ? {} : { system },\n ...tools.length === 0 ? {} : { tools },\n ...finalOutput && tools.length > 0\n ? { toolChoice: 'none' as const }\n : options.toolChoice === undefined ? {} : { toolChoice: options.toolChoice },\n ...options.imagePolicy === undefined ? {} : { imagePolicy: options.imagePolicy },\n ...options.documentPolicy === undefined ? {} : { documentPolicy: options.documentPolicy },\n ...outputFormat === undefined ? {} : { outputFormat },\n }\n const checkpointRequest: GenerateOptions = { ...requestBase, signal }\n try {\n await runOptionalHook(options.hooks?.checkpoint, [{\n kind: 'before-model-request', request: checkpointRequest,\n snapshot: options.history.snapshot(), signal,\n ...(options.logger === undefined ? {} : { logger: options.logger }),\n }], options, signal, 'checkpoint')\n } catch (error: unknown) {\n const failure: ModelFailure = { message: `history checkpoint failed: ${messageOf(error)}`, code: 'CHECKPOINT_FAILED' }\n return {\n trace, finish: { kind: 'error', failure }, calls: [], afterToolCallIds,\n timing: contentTiming(false, afterToolCallIds.length > 0),\n }\n }\n const beforeDispatch = stopped()\n if (beforeDispatch !== undefined) return beforeDispatch\n await emit({ type: 'span-start', trace, at: now(), name: `chat ${options.config.model}`, kind: 'chat', attributes: {\n 'gen_ai.operation.name': 'chat', 'gen_ai.request.model': options.config.model,\n // The effort is part of WHICH call this was: the same model at minimal and\n // at high is two different requests, priced and paced differently, and a\n // trace that omits it cannot explain either. Absent when the route has no\n // effort ladder, rather than reported as a default nobody chose.\n ...options.config.reasoningEffort === undefined\n ? {}\n : { 'gen_ai.request.reasoning_effort': options.config.reasoningEffort },\n turn, step, forcedFinal, phase,\n }, input: requestSummary(requestBase) })\n await emit({ type: 'step-start', turn, step, trace, ...forcedFinal ? { forcedFinal: true as const } : {} })\n const assembler = new BlockAssembler()\n const closedBlockIndexes = new Set<number>()\n const modelTimeoutMs = positiveFinite(options.modelTimeoutMs ?? 10 * 60_000, 'modelTimeoutMs')\n const maxRequestBytes = positiveSafeInteger(options.maxModelRequestBytes ?? 32 * 1024 * 1024, 'maxModelRequestBytes')\n const maxResponseBytes = positiveSafeInteger(options.maxModelResponseBytes ?? 32 * 1024 * 1024, 'maxModelResponseBytes')\n const maxStreamEvents = positiveSafeInteger(options.maxModelStreamEvents ?? 100_000, 'maxModelStreamEvents')\n const teardownTimeoutMs = positiveFinite(options.teardownTimeoutMs ?? 30_000, 'teardownTimeoutMs')\n let modelCallReport: ModelCallReport | undefined\n let usageRequired = false\n let usageUnavailable = false\n let requestBytes: number\n try {\n requestBytes = serializedBytes(requestBase)\n } catch (error: unknown) {\n requestBytes = -1\n assembler.push(modelFailureFinish(`model request is not serializable: ${messageOf(error)}`, 'INVALID_MODEL_REQUEST'))\n }\n if (requestBytes > maxRequestBytes) {\n assembler.push(modelFailureFinish(\n `model request exceeds the ${maxRequestBytes}-byte limit`,\n 'MODEL_REQUEST_TOO_LARGE',\n ))\n } else if (requestBytes >= 0) {\n const owned = new AbortController()\n const roundSignal = AbortSignal.any([signal, owned.signal])\n let timedOut = false\n const timeout = setTimeout(() => {\n timedOut = true\n owned.abort(new Error(`model stream exceeded ${modelTimeoutMs}ms`))\n }, modelTimeoutMs)\n const handle = options.registry.stream(\n { ...requestBase, signal: roundSignal },\n options.accounting?.modelInvocation,\n )\n const iterator = handle[Symbol.asyncIterator]()\n let exhausted = false\n let events = 0\n let responseBytes = 0\n let sawFinish = false\n try {\n while (true) {\n const next = await nextWithAbort(iterator.next(), roundSignal)\n if (next.done === true) {\n exhausted = true\n break\n }\n const chunk = next.value\n try {\n validateStreamChunk(chunk, maxStreamEvents)\n } catch (error: unknown) {\n assembler.push(modelFailureFinish(\n `model adapter emitted an invalid stream chunk: ${messageOf(error)}`,\n 'INVALID_MODEL_STREAM',\n ))\n owned.abort(new Error('invalid model stream chunk'))\n break\n }\n events++\n try {\n responseBytes += serializedBytes(chunk)\n } catch (error: unknown) {\n assembler.push(modelFailureFinish(\n `model adapter emitted a non-serializable stream chunk: ${messageOf(error)}`,\n 'INVALID_MODEL_STREAM',\n ))\n owned.abort(new Error('non-serializable model stream chunk'))\n break\n }\n if (events > maxStreamEvents || responseBytes > maxResponseBytes) {\n const dimension = events > maxStreamEvents\n ? `${maxStreamEvents}-event`\n : `${maxResponseBytes}-byte`\n assembler.push(modelFailureFinish(\n `model response exceeds the ${dimension} limit`,\n 'MODEL_RESPONSE_TOO_LARGE',\n ))\n owned.abort(new Error('model response resource limit exceeded'))\n break\n }\n if (sawFinish) {\n assembler.push(modelFailureFinish(\n 'model adapter emitted data after its terminal finish chunk',\n 'INVALID_MODEL_STREAM',\n ))\n owned.abort(new Error('invalid model stream'))\n break\n }\n assembler.push(chunk)\n // The assembler ignores deltas after the first close. The visible\n // stream must do the same or UI text can disagree with stored history.\n if ((chunk.type === 'text-delta' || chunk.type === 'reasoning-delta' || chunk.type === 'tool-call-delta')\n && closedBlockIndexes.has(chunk.index)) continue\n if (chunk.type === 'block-end') closedBlockIndexes.add(chunk.index)\n if (chunk.type === 'finish') sawFinish = true\n if (chunk.type === 'text-delta') await emit({\n type: 'text-delta', index: chunk.index, text: chunk.text,\n phase: phase === 'process' ? 'commentary' : chunk.phase ?? 'unknown', trace,\n })\n else if (chunk.type === 'reasoning-delta') await emit({\n type: 'reasoning-delta', index: chunk.index, text: chunk.text, trace,\n })\n else if (chunk.type === 'image-delta') await emit({\n type: 'image-delta', itemId: chunk.itemId, data: chunk.data,\n mediaType: chunk.mediaType, ...chunk.partialIndex === undefined ? {} : { partialIndex: chunk.partialIndex }, trace,\n })\n else if (chunk.type === 'usage') await emit({ type: 'usage', usage: chunk.usage, trace })\n else if (chunk.type === 'usage-progress') await emit({ ...chunk, trace })\n }\n } catch (error: unknown) {\n if (!(error instanceof StreamAbortError)) throw error\n assembler.push(signal.aborted\n ? modelAbortedFinish(messageOf(signal.reason ?? error))\n : timedOut\n ? modelFailureFinish(`model stream exceeded ${modelTimeoutMs}ms`, 'MODEL_TIMEOUT')\n : modelFailureFinish(messageOf(error.cause ?? error), 'MODEL_STREAM_ABORTED'))\n } finally {\n clearTimeout(timeout)\n if (!exhausted) {\n owned.abort(new Error('model stream closed before exhaustion'))\n const settled = await closeIterator(iterator, teardownTimeoutMs)\n if (!settled) {\n assembler.push(modelFailureFinish(\n `model stream ignored cancellation for more than ${teardownTimeoutMs}ms; the adapter operation may still be running`,\n 'MODEL_TEARDOWN_TIMEOUT',\n ))\n }\n }\n }\n const report = await handle.report\n const decision = options.accounting === undefined\n ? undefined\n : await options.accounting.recordModelCall(report, { ...requestBase, signal })\n modelCallReport = decision?.report ?? report\n usageRequired = decision?.usageRequired ?? false\n usageUnavailable = decision?.usageUnavailable ?? false\n }\n let providerFinish = assembler.finish\n let retainedPrefix = providerFinish.kind === 'aborted'\n let rawBlocks: ContentBlock[]\n try {\n rawBlocks = providerFinish.kind === 'aborted' ? assembler.interruptedBlocks() : assembler.blocks()\n } catch (error: unknown) {\n providerFinish = {\n kind: 'error',\n failure: {\n message: `model adapter emitted an invalid block sequence: ${messageOf(error)}`,\n code: 'INVALID_MODEL_STREAM',\n },\n }\n // A broken extension must not discard text already delivered to the user.\n // This prefix deliberately excludes tools and unassembled extension data.\n retainedPrefix = true\n rawBlocks = assembler.interruptedBlocks()\n }\n // Interrupted/truncated assembly drops unfinished calls, but the text that\n // introduced those calls is still process narration, not a final answer.\n const canonicalTexts = assembler.textBlocks()\n .filter(({ block }) => !retainedPrefix || block.text.trim() !== '')\n let textPosition = 0\n const classifiedRaw = classifyTextPhases(rawBlocks.map(block => {\n if (block.type !== 'text') return block\n const canonical = canonicalTexts[textPosition++]\n // Safe-prefix recovery removes native calls too. Retain their position so\n // search narration cannot become an answer merely because a call was removed.\n return block.phase === undefined && canonical?.beforeNativeCall\n ? { ...block, phase: 'commentary' as const } : block\n }), phase === 'process', assembler.hasToolCalls)\n // A repeated id costs the model that one call, not its whole turn.\n const deduped = dropDuplicateToolCalls(classifiedRaw, options.history)\n const classified = deduped.blocks\n const structuredOutputFailure = finalOutput\n && options.outputFormat?.type === 'json_schema'\n && providerFinish.kind === 'stop'\n && !isJsonText(textOf(classified), options.validateOutput)\n ? {\n message: 'model returned invalid JSON or failed the structured output validator',\n code: MODEL_ERROR_CODES.MALFORMED_RESPONSE,\n }\n : undefined\n const disabledCall = finalOutput && classified.some(block => block.type === 'tool-call')\n ? { message: 'model emitted a host tool call during the final output phase', code: 'INVALID_TOOL_CALL' }\n : undefined\n const invalidCall = disabledCall\n ?? structuredOutputFailure\n ?? invalidHostToolCall(classified, options.history)\n const blocks = invalidCall === undefined\n ? classified\n : classified.filter(block => block.type !== 'tool-call')\n const finish: FinishReason = invalidCall === undefined\n ? providerFinish\n : { kind: 'error', failure: invalidCall }\n const message = blocks.length === 0 && (finish.kind === 'error' || finish.kind === 'aborted')\n ? undefined\n : createAssistant(options, blocks, retainedPrefix ? undefined : assembler.replayState)\n const calls = message?.content\n .filter((block): block is ToolCallBlock => block.type === 'tool-call')\n .map(block => ({ callId: block.id, toolName: block.name, rawArguments: block.arguments })) ?? []\n const timing = contentTiming(calls.length > 0, afterToolCallIds.length > 0)\n // Deltas may have no phase, or block-end may correct it. Publish the\n // canonical snapshot before consumers close this step, including providers\n // that only emit block-end. Keep provider indexes (which may be sparse).\n if (blocks.some(block => block.type === 'text')) {\n const indexes = canonicalTexts.map(({ index }) => index)\n const incomplete = finish.kind === 'error' || finish.kind === 'aborted' || finish.kind === 'max-tokens'\n let position = 0\n for (const block of blocks) {\n if (block.type !== 'text') continue\n const index = indexes[position++]\n if (index !== undefined) await emit({\n type: 'text-end', index, text: block.text,\n phase: block.phase ?? 'final-answer', trace,\n ...incomplete ? { incomplete: true as const } : {},\n })\n }\n }\n await emit({\n type: 'span-end', trace, at: now(),\n status: finish.kind === 'error' ? 'error' : finish.kind === 'aborted' ? 'aborted' : 'success',\n output: message === undefined ? undefined : {\n text: textOf(message.content),\n commentary: message.content.flatMap(block => block.type === 'text' && block.phase === 'commentary' ? [block.text] : []),\n reasoning: message.content.flatMap(block => block.type === 'reasoning' ? [block.text] : []),\n toolCalls: calls.length,\n },\n ...assembler.usage === undefined ? {} : { usage: assembler.usage },\n ...finish.kind === 'error' || finish.kind === 'aborted'\n ? { error: { type: 'ModelError', message: finish.failure.message, code: finish.failure.code } }\n : {},\n })\n if (calls.length === 0 || finalOutput) await emit({ type: 'step-end', turn, step, trace })\n return {\n trace, ...message === undefined ? {} : { message }, finish,\n ...assembler.usage === undefined ? {} : { usage: assembler.usage },\n ...modelCallReport === undefined ? {} : { report: modelCallReport },\n ...usageRequired ? { usageRequired: true as const } : {},\n ...usageUnavailable ? { usageUnavailable: true as const } : {},\n calls, afterToolCallIds, timing,\n ...deduped.dropped.length === 0 ? {} : { droppedDuplicateCalls: deduped.dropped },\n }\n}\n\nfunction isJsonText(value: string, validate?: (value: unknown) => void): boolean {\n try {\n const parsed: unknown = JSON.parse(value)\n validate?.(parsed)\n return true\n } catch {\n return false\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"model-round.js","names":[],"sources":["../../../../src/agent/loop/turn/model-round.ts"],"sourcesContent":["import type { GenerateOptions } from '../../../contract/index.ts'\nimport { MODEL_ERROR_CODES, type ModelFailure } from '../../../errors/index.ts'\nimport type { ContentBlock, Message, ToolCallBlock } from '../../../message/index.ts'\nimport { BlockAssembler } from '../../../stream/index.ts'\nimport type { FinishReason } from '../../../stream/index.ts'\nimport type { ModelCallReport } from '../../../observation/index.ts'\nimport { normalizeToolPairing } from '../../history/normalize.ts'\nimport { createSpanId, type TraceRef } from '../../trace/trace.ts'\nimport type { AgentEvent, AgentMaintenanceEvent } from '../types.ts'\nimport { type ModelRoundPhase, type RunTurnOptions, type RoundResult } from './types.ts'\nimport { positiveFinite, positiveSafeInteger } from './config.ts'\nimport { serializedBytes, modelFailureFinish, modelAbortedFinish, messageOf, now } from './common.ts'\nimport { validateStreamChunk } from './validation.ts'\nimport { StreamAbortError, nextWithAbort, closeIterator } from './cancellation.ts'\nimport { runOptionalHook } from './hooks.ts'\nimport { stepProjectionSources } from './step-projection.ts'\nimport { observeModelRequestBoundary } from './model-request-boundary.ts'\nimport { accountingUsageStop } from './usage-stop.ts'\nimport {\n classifyTextPhases, dropDuplicateToolCalls, invalidHostToolCall, contentTiming,\n recentToolResultIds, systemText,\n createAssistant, textOf,\n} from './content.ts'\n\n/** How much of one message the trace keeps. */\nconst PREVIEW_CHARS = 600\n\n/** How many trailing messages the trace keeps. */\nconst PREVIEW_MESSAGES = 8\n\nfunction clip(text: string): string {\n const flattened = text.replace(/\\s+/gu, ' ').trim()\n return flattened.length > PREVIEW_CHARS ? `${flattened.slice(0, PREVIEW_CHARS)}…` : flattened\n}\n\n/** One message as a line: who spoke, and what the model could read of it. */\nfunction previewMessage(message: Message): Record<string, unknown> {\n const parts: string[] = []\n const calls: string[] = []\n for (const block of message.content) {\n if (block.type === 'text' || block.type === 'reasoning') parts.push(block.text)\n else if (block.type === 'tool-call') calls.push(`${block.name}(${clip(block.arguments)})`)\n else if (block.type === 'tool-result') parts.push(textOf(block.content))\n else parts.push(`[${block.type}]`)\n }\n return {\n role: message.role,\n producer: message.source.kind,\n ...parts.length === 0 ? {} : { text: clip(parts.join(' ')) },\n ...calls.length === 0 ? {} : { toolCalls: calls },\n }\n}\n\n/**\n * What this round sent the model, in a form a person can read.\n *\n * A summary, not a copy: the request can be the whole conversation plus every\n * tool schema, and a trace that stored it verbatim would be larger than the\n * work it describes. What survives is the part that explains the round — which\n * tools were on offer, how long the context was, and the tail of the\n * conversation the model was actually answering, each message clipped.\n * @param request - The request about to be dispatched.\n * @returns The summary for the span.\n */\nfunction requestSummary(request: GenerateOptions): Record<string, unknown> {\n const messages = request.messages ?? []\n const dropped = Math.max(0, messages.length - PREVIEW_MESSAGES)\n return {\n messageCount: messages.length,\n // The identity of the request, for a host that records the provider call\n // separately and has to say WHICH round each recording belongs to. Message\n // ids are stable across every representation boundary, so the last one plus\n // the count names one round of one agent without ambiguity.\n ...messages.at(-1) === undefined ? {} : { lastMessageId: messages.at(-1)?.id },\n ...request.system === undefined ? {} : { system: clip(request.system) },\n ...request.tools === undefined || request.tools.length === 0\n ? {}\n : { tools: request.tools.map(tool => tool.name) },\n ...request.toolChoice === undefined ? {} : { toolChoice: request.toolChoice },\n ...dropped === 0 ? {} : { earlierMessagesOmitted: dropped },\n messages: messages.slice(-PREVIEW_MESSAGES).map(previewMessage),\n }\n}\n\nexport async function modelRound(\n options: RunTurnOptions,\n signal: AbortSignal,\n emit: (event: AgentEvent) => Promise<void>,\n emitMaintenance: (event: AgentMaintenanceEvent) => Promise<void>,\n root: TraceRef,\n turn: number,\n step: number,\n phase: ModelRoundPhase,\n): Promise<RoundResult> {\n const forcedFinal = phase === 'forced-final'\n const finalOutput = phase === 'final' || forcedFinal\n const trace: TraceRef = { traceId: root.traceId, spanId: createSpanId(), parentSpanId: root.spanId }\n let messages = normalizeToolPairing(options.history.messages())\n const initialMessages = messages\n const afterToolCallIds = recentToolResultIds(options.history)\n const generation = options.history.generation()\n const entries = options.history.entries().length\n const stopped = (): RoundResult | undefined => {\n const stop = accountingUsageStop(options.accounting)\n if (stop === undefined && !signal.aborted) return undefined\n return {\n trace, finish: signal.aborted\n ? { kind: 'aborted', failure: { code: 'ABORTED', message: messageOf(signal.reason) } }\n : { kind: 'stop' },\n calls: [], afterToolCallIds, timing: contentTiming(false, afterToolCallIds.length > 0),\n ...(stop?.kind === 'error' ? { usageRequired: true } : {}),\n ...(stop?.kind === 'usage-unavailable' ? { usageUnavailable: true } : {}),\n }\n }\n const beforeMaintenance = stopped()\n if (beforeMaintenance !== undefined) return beforeMaintenance\n const decision = await runOptionalHook(options.hooks?.beforeStep, [{\n turn, step, messages, snapshot: options.history.snapshot(), signal,\n ...(options.logger === undefined ? {} : { logger: options.logger }),\n emit: emitMaintenance,\n }], options, signal, 'beforeStep')\n const afterMaintenance = stopped()\n if (afterMaintenance !== undefined) return afterMaintenance\n if (decision?.kind === 'reject') {\n const failure: ModelFailure = { message: decision.reason, code: 'STEP_REJECTED' }\n return {\n trace, finish: { kind: 'error', failure }, calls: [], afterToolCallIds,\n timing: contentTiming(false, afterToolCallIds.length > 0),\n }\n }\n // Hooks may append live steering as well as replacing compacted history.\n // Refresh for either mutation so new user input stays at the chronological\n // tail instead of needing to be prepended ahead of older history.\n if (options.history.generation() !== generation\n || options.history.entries().length !== entries) {\n messages = normalizeToolPairing(options.history.messages())\n }\n if (decision?.messages !== undefined) {\n const originalIds = stepProjectionSources(decision) ?? new Set(initialMessages.map(message => message.id))\n const projectedIds = new Set(decision.messages.map(message => message.id))\n const introduced = messages.filter(message => !originalIds.has(message.id) && !projectedIds.has(message.id))\n const currentIds = new Set(messages.map(message => message.id))\n const replaced = [...originalIds].some(id => !currentIds.has(id))\n // A hook can await external I/O while live steering or replacement arrives.\n // Replacements invalidate a stale projection; append-only steering stays at the tail.\n const projectedById = new Map(decision.messages.map(message => [message.id, message]))\n messages = normalizeToolPairing(replaced\n ? [...decision.messages.filter(message => !originalIds.has(message.id) && !currentIds.has(message.id) && message.source.kind === 'app'),\n ...messages.flatMap(message => {\n const projected = projectedById.get(message.id)\n return projected === undefined ? originalIds.has(message.id) ? [] : [message] : [projected]\n })]\n : [...decision.messages, ...introduced])\n }\n if (decision?.prepend !== undefined) messages = Object.freeze([...decision.prepend, ...messages])\n const system = systemText(options, forcedFinal)\n const availableTools = [\n ...options.tools?.schemas() ?? [],\n ...options.nativeTools ?? [],\n ]\n const tools = availableTools\n const outputFormat = !finalOutput && options.outputFormat?.type === 'json_schema'\n ? { type: 'text' as const }\n : options.outputFormat\n const requestBase: GenerateOptions = {\n ...options.config,\n messages,\n ...system.length === 0 ? {} : { system },\n ...tools.length === 0 ? {} : { tools },\n ...finalOutput && tools.length > 0\n ? { toolChoice: 'none' as const }\n : options.toolChoice === undefined ? {} : { toolChoice: options.toolChoice },\n ...options.imagePolicy === undefined ? {} : { imagePolicy: options.imagePolicy },\n ...options.documentPolicy === undefined ? {} : { documentPolicy: options.documentPolicy },\n ...outputFormat === undefined ? {} : { outputFormat },\n }\n const checkpointRequest: GenerateOptions = { ...requestBase, signal }\n observeModelRequestBoundary(options.history, true)\n try {\n await runOptionalHook(options.hooks?.checkpoint, [{\n kind: 'before-model-request', request: checkpointRequest,\n snapshot: options.history.snapshot(), signal,\n ...(options.logger === undefined ? {} : { logger: options.logger }),\n }], options, signal, 'checkpoint')\n } catch (error: unknown) {\n // No output will be recorded for this request. Release queued input before\n // recovery hooks or the retry's beforeStep can read or inject into history.\n observeModelRequestBoundary(options.history, false)\n const failure: ModelFailure = { message: `history checkpoint failed: ${messageOf(error)}`, code: 'CHECKPOINT_FAILED' }\n return {\n trace, finish: { kind: 'error', failure }, calls: [], afterToolCallIds,\n timing: contentTiming(false, afterToolCallIds.length > 0),\n }\n }\n const beforeDispatch = stopped()\n if (beforeDispatch !== undefined) {\n observeModelRequestBoundary(options.history, false)\n return beforeDispatch\n }\n await emit({ type: 'span-start', trace, at: now(), name: `chat ${options.config.model}`, kind: 'chat', attributes: {\n 'gen_ai.operation.name': 'chat', 'gen_ai.request.model': options.config.model,\n // The effort is part of WHICH call this was: the same model at minimal and\n // at high is two different requests, priced and paced differently, and a\n // trace that omits it cannot explain either. Absent when the route has no\n // effort ladder, rather than reported as a default nobody chose.\n ...options.config.reasoningEffort === undefined\n ? {}\n : { 'gen_ai.request.reasoning_effort': options.config.reasoningEffort },\n turn, step, forcedFinal, phase,\n }, input: requestSummary(requestBase) })\n await emit({ type: 'step-start', turn, step, trace, ...forcedFinal ? { forcedFinal: true as const } : {} })\n const assembler = new BlockAssembler()\n const closedBlockIndexes = new Set<number>()\n const modelTimeoutMs = positiveFinite(options.modelTimeoutMs ?? 10 * 60_000, 'modelTimeoutMs')\n const maxRequestBytes = positiveSafeInteger(options.maxModelRequestBytes ?? 32 * 1024 * 1024, 'maxModelRequestBytes')\n const maxResponseBytes = positiveSafeInteger(options.maxModelResponseBytes ?? 32 * 1024 * 1024, 'maxModelResponseBytes')\n const maxStreamEvents = positiveSafeInteger(options.maxModelStreamEvents ?? 100_000, 'maxModelStreamEvents')\n const teardownTimeoutMs = positiveFinite(options.teardownTimeoutMs ?? 30_000, 'teardownTimeoutMs')\n let modelCallReport: ModelCallReport | undefined\n let usageRequired = false\n let usageUnavailable = false\n let requestBytes: number\n try {\n requestBytes = serializedBytes(requestBase)\n } catch (error: unknown) {\n requestBytes = -1\n assembler.push(modelFailureFinish(`model request is not serializable: ${messageOf(error)}`, 'INVALID_MODEL_REQUEST'))\n }\n if (requestBytes > maxRequestBytes) {\n assembler.push(modelFailureFinish(\n `model request exceeds the ${maxRequestBytes}-byte limit`,\n 'MODEL_REQUEST_TOO_LARGE',\n ))\n } else if (requestBytes >= 0) {\n const owned = new AbortController()\n const roundSignal = AbortSignal.any([signal, owned.signal])\n let timedOut = false\n const timeout = setTimeout(() => {\n timedOut = true\n owned.abort(new Error(`model stream exceeded ${modelTimeoutMs}ms`))\n }, modelTimeoutMs)\n const handle = options.registry.stream(\n { ...requestBase, signal: roundSignal },\n options.accounting?.modelInvocation,\n )\n const iterator = handle[Symbol.asyncIterator]()\n let exhausted = false\n let events = 0\n let responseBytes = 0\n let sawFinish = false\n try {\n while (true) {\n const next = await nextWithAbort(iterator.next(), roundSignal)\n if (next.done === true) {\n exhausted = true\n break\n }\n const chunk = next.value\n try {\n validateStreamChunk(chunk, maxStreamEvents)\n } catch (error: unknown) {\n assembler.push(modelFailureFinish(\n `model adapter emitted an invalid stream chunk: ${messageOf(error)}`,\n 'INVALID_MODEL_STREAM',\n ))\n owned.abort(new Error('invalid model stream chunk'))\n break\n }\n events++\n try {\n responseBytes += serializedBytes(chunk)\n } catch (error: unknown) {\n assembler.push(modelFailureFinish(\n `model adapter emitted a non-serializable stream chunk: ${messageOf(error)}`,\n 'INVALID_MODEL_STREAM',\n ))\n owned.abort(new Error('non-serializable model stream chunk'))\n break\n }\n if (events > maxStreamEvents || responseBytes > maxResponseBytes) {\n const dimension = events > maxStreamEvents\n ? `${maxStreamEvents}-event`\n : `${maxResponseBytes}-byte`\n assembler.push(modelFailureFinish(\n `model response exceeds the ${dimension} limit`,\n 'MODEL_RESPONSE_TOO_LARGE',\n ))\n owned.abort(new Error('model response resource limit exceeded'))\n break\n }\n if (sawFinish) {\n assembler.push(modelFailureFinish(\n 'model adapter emitted data after its terminal finish chunk',\n 'INVALID_MODEL_STREAM',\n ))\n owned.abort(new Error('invalid model stream'))\n break\n }\n assembler.push(chunk)\n // The assembler ignores deltas after the first close. The visible\n // stream must do the same or UI text can disagree with stored history.\n if ((chunk.type === 'text-delta' || chunk.type === 'reasoning-delta' || chunk.type === 'tool-call-delta')\n && closedBlockIndexes.has(chunk.index)) continue\n if (chunk.type === 'block-end') closedBlockIndexes.add(chunk.index)\n if (chunk.type === 'finish') sawFinish = true\n if (chunk.type === 'text-delta') await emit({\n type: 'text-delta', index: chunk.index, text: chunk.text,\n phase: phase === 'process' ? 'commentary' : chunk.phase ?? 'unknown', trace,\n })\n else if (chunk.type === 'reasoning-delta') await emit({\n type: 'reasoning-delta', index: chunk.index, text: chunk.text, trace,\n })\n else if (chunk.type === 'image-delta') await emit({\n type: 'image-delta', itemId: chunk.itemId, data: chunk.data,\n mediaType: chunk.mediaType, ...chunk.partialIndex === undefined ? {} : { partialIndex: chunk.partialIndex }, trace,\n })\n else if (chunk.type === 'usage') await emit({ type: 'usage', usage: chunk.usage, trace })\n else if (chunk.type === 'usage-progress') await emit({ ...chunk, trace })\n }\n } catch (error: unknown) {\n if (!(error instanceof StreamAbortError)) throw error\n assembler.push(signal.aborted\n ? modelAbortedFinish(messageOf(signal.reason ?? error))\n : timedOut\n ? modelFailureFinish(`model stream exceeded ${modelTimeoutMs}ms`, 'MODEL_TIMEOUT')\n : modelFailureFinish(messageOf(error.cause ?? error), 'MODEL_STREAM_ABORTED'))\n } finally {\n clearTimeout(timeout)\n if (!exhausted) {\n owned.abort(new Error('model stream closed before exhaustion'))\n const settled = await closeIterator(iterator, teardownTimeoutMs)\n if (!settled) {\n assembler.push(modelFailureFinish(\n `model stream ignored cancellation for more than ${teardownTimeoutMs}ms; the adapter operation may still be running`,\n 'MODEL_TEARDOWN_TIMEOUT',\n ))\n }\n }\n }\n const report = await handle.report\n const decision = options.accounting === undefined\n ? undefined\n : await options.accounting.recordModelCall(report, { ...requestBase, signal })\n modelCallReport = decision?.report ?? report\n usageRequired = decision?.usageRequired ?? false\n usageUnavailable = decision?.usageUnavailable ?? false\n }\n let providerFinish = assembler.finish\n let retainedPrefix = providerFinish.kind === 'aborted'\n let rawBlocks: ContentBlock[]\n try {\n rawBlocks = providerFinish.kind === 'aborted' ? assembler.interruptedBlocks() : assembler.blocks()\n } catch (error: unknown) {\n providerFinish = {\n kind: 'error',\n failure: {\n message: `model adapter emitted an invalid block sequence: ${messageOf(error)}`,\n code: 'INVALID_MODEL_STREAM',\n },\n }\n // A broken extension must not discard text already delivered to the user.\n // This prefix deliberately excludes tools and unassembled extension data.\n retainedPrefix = true\n rawBlocks = assembler.interruptedBlocks()\n }\n // Interrupted/truncated assembly drops unfinished calls, but the text that\n // introduced those calls is still process narration, not a final answer.\n const canonicalTexts = assembler.textBlocks()\n .filter(({ block }) => !retainedPrefix || block.text.trim() !== '')\n let textPosition = 0\n const classifiedRaw = classifyTextPhases(rawBlocks.map(block => {\n if (block.type !== 'text') return block\n const canonical = canonicalTexts[textPosition++]\n // Safe-prefix recovery removes native calls too. Retain their position so\n // search narration cannot become an answer merely because a call was removed.\n return block.phase === undefined && canonical?.beforeNativeCall\n ? { ...block, phase: 'commentary' as const } : block\n }), phase === 'process', assembler.hasToolCalls)\n // A repeated id costs the model that one call, not its whole turn.\n const deduped = dropDuplicateToolCalls(classifiedRaw, options.history)\n const classified = deduped.blocks\n const structuredOutputFailure = finalOutput\n && options.outputFormat?.type === 'json_schema'\n && providerFinish.kind === 'stop'\n && !isJsonText(textOf(classified), options.validateOutput)\n ? {\n message: 'model returned invalid JSON or failed the structured output validator',\n code: MODEL_ERROR_CODES.MALFORMED_RESPONSE,\n }\n : undefined\n const disabledCall = finalOutput && classified.some(block => block.type === 'tool-call')\n ? { message: 'model emitted a host tool call during the final output phase', code: 'INVALID_TOOL_CALL' }\n : undefined\n const invalidCall = disabledCall\n ?? structuredOutputFailure\n ?? invalidHostToolCall(classified, options.history)\n const blocks = invalidCall === undefined\n ? classified\n : classified.filter(block => block.type !== 'tool-call')\n const finish: FinishReason = invalidCall === undefined\n ? providerFinish\n : { kind: 'error', failure: invalidCall }\n const message = blocks.length === 0 && (finish.kind === 'error' || finish.kind === 'aborted')\n ? undefined\n : createAssistant(options, blocks, retainedPrefix ? undefined : assembler.replayState)\n const calls = message?.content\n .filter((block): block is ToolCallBlock => block.type === 'tool-call')\n .map(block => ({ callId: block.id, toolName: block.name, rawArguments: block.arguments })) ?? []\n const timing = contentTiming(calls.length > 0, afterToolCallIds.length > 0)\n // Deltas may have no phase, or block-end may correct it. Publish the\n // canonical snapshot before consumers close this step, including providers\n // that only emit block-end. Keep provider indexes (which may be sparse).\n if (blocks.some(block => block.type === 'text')) {\n const indexes = canonicalTexts.map(({ index }) => index)\n const incomplete = finish.kind === 'error' || finish.kind === 'aborted' || finish.kind === 'max-tokens'\n let position = 0\n for (const block of blocks) {\n if (block.type !== 'text') continue\n const index = indexes[position++]\n if (index !== undefined) await emit({\n type: 'text-end', index, text: block.text,\n phase: block.phase ?? 'final-answer', trace,\n ...incomplete ? { incomplete: true as const } : {},\n })\n }\n }\n await emit({\n type: 'span-end', trace, at: now(),\n status: finish.kind === 'error' ? 'error' : finish.kind === 'aborted' ? 'aborted' : 'success',\n output: message === undefined ? undefined : {\n text: textOf(message.content),\n commentary: message.content.flatMap(block => block.type === 'text' && block.phase === 'commentary' ? [block.text] : []),\n reasoning: message.content.flatMap(block => block.type === 'reasoning' ? [block.text] : []),\n toolCalls: calls.length,\n },\n ...assembler.usage === undefined ? {} : { usage: assembler.usage },\n ...finish.kind === 'error' || finish.kind === 'aborted'\n ? { error: { type: 'ModelError', message: finish.failure.message, code: finish.failure.code } }\n : {},\n })\n if (calls.length === 0 || finalOutput) await emit({ type: 'step-end', turn, step, trace })\n return {\n trace, ...message === undefined ? {} : { message }, finish,\n ...assembler.usage === undefined ? {} : { usage: assembler.usage },\n ...modelCallReport === undefined ? {} : { report: modelCallReport },\n ...usageRequired ? { usageRequired: true as const } : {},\n ...usageUnavailable ? { usageUnavailable: true as const } : {},\n calls, afterToolCallIds, timing,\n ...deduped.dropped.length === 0 ? {} : { droppedDuplicateCalls: deduped.dropped },\n }\n}\n\nfunction isJsonText(value: string, validate?: (value: unknown) => void): boolean {\n try {\n const parsed: unknown = JSON.parse(value)\n validate?.(parsed)\n return true\n } catch {\n return false\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=new WeakMap;function t(t,n){if(t.kind===`proceed`&&t.messages!==void 0){let r={...t};return e.set(r,new Set(n.map(e=>e.id))),r}return t}function n(t){return e.get(t)}export{t as bindStepProjectionSources,n as stepProjectionSources};
|
|
2
|
+
//# sourceMappingURL=step-projection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"step-projection.js","names":[],"sources":["../../../../src/agent/loop/turn/step-projection.ts"],"sourcesContent":["import type { Message } from '../../../message/index.ts'\nimport type { StepDecision } from '../events.ts'\n\n// Session maintenance may refresh the hook's context after queue delivery or\n// compaction. Keep that projection's input identity out of the public decision\n// and provider payload; otherwise a deliberately filtered input can be mistaken\n// for unseen late steering and reinserted by model-round.\nconst sources = new WeakMap<StepDecision, ReadonlySet<Message['id']>>()\n\nexport function bindStepProjectionSources<T extends StepDecision>(decision: T, messages: readonly Message[]): T {\n if (decision.kind === 'proceed' && decision.messages !== undefined) {\n // Applications may reuse a frozen decision across concurrent sessions.\n // Each preparation owns its identity baseline independently.\n const bound = { ...decision } as T\n sources.set(bound, new Set(messages.map(message => message.id)))\n return bound\n }\n return decision\n}\n\nexport function stepProjectionSources(decision: StepDecision): ReadonlySet<Message['id']> | undefined {\n return sources.get(decision)\n}\n"],"mappings":"AAOA,MAAM,EAAU,IAAI,QAEpB,SAAgB,EAAkD,EAAa,EAAiC,CAC9G,GAAI,EAAS,OAAS,WAAa,EAAS,WAAa,IAAA,GAAW,CAGlE,IAAM,EAAQ,CAAE,GAAG,CAAS,EAE5B,OADA,EAAQ,IAAI,EAAO,IAAI,IAAI,EAAS,IAAI,GAAW,EAAQ,EAAE,CAAC,CAAC,EACxD,CACT,CACA,OAAO,CACT,CAEA,SAAgB,EAAsB,EAAgE,CACpG,OAAO,EAAQ,IAAI,CAAQ,CAC7B"}
|
|
@@ -10,6 +10,7 @@ import { ToolCatalog } from "../../tool/registry.js";
|
|
|
10
10
|
import { ToolInterceptor } from "../../tool/pipeline.js";
|
|
11
11
|
import { TurnHooks } from "../events.js";
|
|
12
12
|
import { TurnBounds } from "../types.js";
|
|
13
|
+
import { ExperimentalProgramGrant } from "../../tool/nested.js";
|
|
13
14
|
import { ModelRegistry } from "../../../runtime/registry.js";
|
|
14
15
|
import { RunAccountingPort } from "../../accounting/contracts.js";
|
|
15
16
|
import { ContextSection } from "../../context/types.js";
|
|
@@ -71,6 +72,11 @@ interface RunTurnOptions {
|
|
|
71
72
|
* retrieves the rest.
|
|
72
73
|
*/
|
|
73
74
|
readonly spillStore?: SpillStore;
|
|
75
|
+
/**
|
|
76
|
+
* Experimental: program tools and the tools each may call. Off by default.
|
|
77
|
+
* Child calls spend this turn's tool budget and never enter model history.
|
|
78
|
+
*/
|
|
79
|
+
readonly experimentalPrograms?: readonly ExperimentalProgramGrant[];
|
|
74
80
|
/**
|
|
75
81
|
* Model-visible context recomputed before every model round.
|
|
76
82
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","names":[],"sources":["../../../../src/agent/loop/turn/types.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.d.ts","names":[],"sources":["../../../../src/agent/loop/turn/types.ts"],"mappings":";;;;;;;;;;;;;;;;;UAmBiB;WACN,kBAAkB;;WAElB;;WAEA;WACA,UAAU;WACV,QAAQ;WACR,SAAS;WACT,QAAQ;;WAER,uBAAuB;;WAEvB,aAAa;;WAEb,eAAe;WACf;WACA,wBAAwB;WACxB,YAAY;WACZ,SAAS,QAAQ;WACjB,QAAQ;WACR,SAAS;;WAET,SAAS;;WAET;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;WACA;aACE,UAAU;aACV,eAAe;aACf;aACA;aACA;;;WAGF,aAAa;;;;;;;;WAQb,aAAa;;;;;WAKb,gCAAgC;;;;;;;WAOhC,2BAA2B"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { ToolDefinition } from "../tool/definition.js";
|
|
2
|
+
import { SpillStore } from "../tool/output-budget.js";
|
|
3
|
+
import { HistorySnapshot } from "../history/types.js";
|
|
4
|
+
import { TurnHooks } from "../loop/events.js";
|
|
5
|
+
import { EvidenceReducer } from "../tool/evidence-reducer.js";
|
|
6
|
+
//#region src/agent/memory/context-optimizer.d.ts
|
|
7
|
+
interface ContextMilestone {
|
|
8
|
+
readonly id: string;
|
|
9
|
+
/** Inclusive append-only sequence boundary, captured after the completed step. */
|
|
10
|
+
readonly throughSeq: number;
|
|
11
|
+
/** Host-verified state, including decisions and unresolved constraints needed later. */
|
|
12
|
+
readonly summary: string;
|
|
13
|
+
readonly remainingTurns: number;
|
|
14
|
+
/** Same cost units as historyTokenCost; include input/output and summary-model price. */
|
|
15
|
+
readonly compactionCost: number;
|
|
16
|
+
readonly historyTokenCost?: number;
|
|
17
|
+
}
|
|
18
|
+
interface ContextOptimizerOptions {
|
|
19
|
+
/** Mount the returned retrievalTool in the same session. Use a store scoped to that session. */
|
|
20
|
+
readonly store: SpillStore;
|
|
21
|
+
readonly observationThresholdBytes?: number;
|
|
22
|
+
readonly summaryBytes?: number;
|
|
23
|
+
readonly fullRequests?: number;
|
|
24
|
+
readonly maxObservations?: number;
|
|
25
|
+
readonly maxMilestones?: number;
|
|
26
|
+
/** Persist raw snapshots locally/durably before a milestone projection is accepted. */
|
|
27
|
+
readonly archive?: (snapshot: HistorySnapshot, milestone: ContextMilestone, signal: AbortSignal) => Promise<void>;
|
|
28
|
+
readonly reducer?: EvidenceReducer;
|
|
29
|
+
/** Explicitly identify logs and their authoritative outcome; core does not guess exit status. */
|
|
30
|
+
readonly log?: (toolName: string, text: string) => {
|
|
31
|
+
readonly status: 'pass' | 'fail' | 'unknown';
|
|
32
|
+
readonly requiredLines?: readonly number[];
|
|
33
|
+
} | undefined;
|
|
34
|
+
readonly reductionThresholdBytes?: number;
|
|
35
|
+
}
|
|
36
|
+
interface ContextOptimizationMetrics {
|
|
37
|
+
readonly packedObservations: number;
|
|
38
|
+
readonly verifiedReductions: number;
|
|
39
|
+
readonly rejectedReductions: number;
|
|
40
|
+
readonly compactedMilestones: number;
|
|
41
|
+
readonly skippedMilestones: number;
|
|
42
|
+
/** Estimate of payload tokens avoided across prepared requests; never billing usage. */
|
|
43
|
+
readonly estimatedTokensSaved: number;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* One controller per session. Projection affects requests only; snapshots retain raw history.
|
|
47
|
+
* Compose its hooks with application hooks using wrapHooks(), and register retrievalTool.
|
|
48
|
+
* Disposal drops projection state, never tool receipts or host-owned storage.
|
|
49
|
+
*/
|
|
50
|
+
declare function createContextOptimizer(options: ContextOptimizerOptions): Readonly<{
|
|
51
|
+
hooks: TurnHooks;
|
|
52
|
+
retrievalTool: ToolDefinition<unknown>;
|
|
53
|
+
completeMilestone(milestone: ContextMilestone): void;
|
|
54
|
+
/** Application hook runs first; checkpoints count only after host persistence succeeds. */
|
|
55
|
+
wrapHooks(application?: TurnHooks): TurnHooks;
|
|
56
|
+
metrics(): ContextOptimizationMetrics;
|
|
57
|
+
dispose(): void;
|
|
58
|
+
}>;
|
|
59
|
+
//#endregion
|
|
60
|
+
export { ContextMilestone, ContextOptimizationMetrics, ContextOptimizerOptions, createContextOptimizer };
|
|
61
|
+
//# sourceMappingURL=context-optimizer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-optimizer.d.ts","names":[],"sources":["../../../src/agent/memory/context-optimizer.ts"],"mappings":";;;;;;UAUiB;WACN;;WAEA;;WAEA;WACA;;WAEA;WACA;;UAEM;;WAEN,OAAO;WACP;WACA;WACA;WACA;WACA;;WAEA,WAAW,UAAU,iBAAiB,WAAW,kBAAkB,QAAQ,gBAAgB;WAC3F,UAAU;;WAEV,OAAO,kBAAkB;aACvB;aAA8C;;WAEhD;;UAEM;WACN;WACA;WACA;WACA;WACA;;WAEA;;;;;;;iBAkBK,uBAAuB,SAAS,0BAAuB;;;EAoMtC,kBAAA,WAAA;;EAaN,UAAA,cAAA,YAAiB;EAY7B,WAAA;EACA"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{createMessage as e}from"../../message/message.js";import{estimateMessageTokens as t}from"./token-estimator.js";import{projectHistorySurface as n}from"../history/project.js";import{readSpillTool as r}from"../tool/output-budget.js";import{diagnosticLineNumbers as i,reduceEvidence as a}from"../tool/evidence-reducer.js";function o(o){let f=s(o.observationThresholdBytes??10240,`observationThresholdBytes`),p=s(o.summaryBytes??1024,`summaryBytes`);if(p<256)throw RangeError(`summaryBytes must be at least 256`);let m=s(o.fullRequests??2,`fullRequests`),h=s(o.maxObservations??64,`maxObservations`),g=s(o.maxMilestones??128,`maxMilestones`),_=s(o.reductionThresholdBytes??4096,`reductionThresholdBytes`),v=o.store,y=o.archive,b=o.reducer,x=o.log,S=new Map,C=[],w=[],T=new Set,E={packedObservations:0,verifiedReductions:0,rejectedReductions:0,compactedMilestones:0,skippedMilestones:0,estimatedTokensSaved:0},D=!1,O=!1,k,A=new AbortController,j=0,M=r(v),N=async(r,i)=>{let a=new Map((w.length||C.length?i:[]).map(e=>[String(e.id),JSON.stringify({content:e.content,source:e.source,role:e.role})]));for(let i of C.splice(0)){if(i.throughSeq>(r.snapshot.entries.at(-1)?.seq??0)){E.skippedMilestones++;continue}let o=n(r.snapshot.entries),s=new Set(r.messages.map(e=>e.id)),c=o.slice(1).filter(e=>e.seq>j&&e.seq<=i.throughSeq&&e.message.source.kind!==`app`&&s.has(e.message.id)),l=new Map(r.messages.map(e=>[e.id,e])),u=c.map(e=>l.get(e.message.id)),f=new Set(w.flatMap(e=>[...e.targets]));if(c.length===0||c.some(e=>f.has(e.message.id))||!d(u)){E.skippedMilestones++;continue}let p=e({role:`user`,source:{kind:`user`},content:[{type:`text`,text:`[Completed ${i.id}: ${i.summary}]`}]}),m=u.reduce((e,n)=>e+t(n),0)-t(p);if(m<=0||i.compactionCost>=m*i.remainingTurns*(i.historyTokenCost??1)){E.skippedMilestones++;continue}if(y===void 0){E.skippedMilestones++;continue}try{if(await y(r.snapshot,i,r.signal),D)break;r.signal.throwIfAborted()}catch{if(D)break;r.signal.throwIfAborted(),E.skippedMilestones++;continue}w.push({targets:new Set(u.map(e=>e.id)),inputs:new Map(u.map(e=>[String(e.id),a.get(e.id)])),message:p}),j=i.throughSeq,E.compactedMilestones++}let o=[...r.messages];for(let e=w.length-1;e>=0;e--){let t=w[e],n=new Set(o.map(e=>String(e.id)));if(![...t.targets].every(e=>n.has(e)&&a.get(e)===t.inputs.get(e))){w.splice(e,1);continue}let r=o.findIndex(e=>t.targets.has(e.id)),i=o.filter(e=>!t.targets.has(e.id));i.splice(r,0,t.message),o=i}return o},P=async e=>{let n=e.messages,r=new Map(e.snapshot.entries.flatMap(e=>e.event.kind===`tool-call`?[[String(e.event.callId),e.event.name]]:[]));if(n=await l(n,async t=>{let n=await l(t.content,async n=>{if(n.type!==`tool-result`)return n;let o=r.get(String(n.toolCallId))??``;if(o===M.name)return n;let s=await l(n.content,async(r,s)=>{if(D||(e.signal.throwIfAborted(),r.type!==`text`))return r;let l=`${t.id}:${n.toolCallId}:${s}`,d=S.get(l);if(d!==void 0&&d.text!==r.text)return r;let g=d?.record.bytes??c(r.text),y=d===void 0?g>_?x?.(o,r.text):void 0:d.log,C=y===void 0?void 0:Object.freeze({status:y.status,...y.requiredLines===void 0?{}:{requiredLines:Object.freeze([...y.requiredLines])}}),w=g>f,T=b!==void 0&&C!==void 0&&g>_;if(!w&&!T)return r;if(d===void 0){if(S.size>=h)return r;try{let t=await v.save(r.text,{toolName:o,callId:String(n.toolCallId)});if(D)return r;e.signal.throwIfAborted();let i=Object.freeze({locator:t.locator,retrieval:t.retrieval,bytes:t.bytes});if(typeof i.locator!=`string`||!i.locator||typeof i.retrieval!=`string`||!i.retrieval||i.bytes!==g)return r;d={text:r.text,record:i,exposures:0,reductionAttempted:!1,...C===void 0?{}:{log:{...C,...C.requiredLines===void 0?{}:{requiredLines:Object.freeze([...C.requiredLines])}}}},S.set(l,d)}catch{return r}}if(w&&d.exposures<m)return r;try{if(await v.read(d.record.locator,{offset:0,limit:1})===void 0)return r}catch{return r}if(D)return r;if(e.signal.throwIfAborted(),d.packed!==void 0)return E.packedObservations++,{...r,text:d.packed};if(T&&!d.reductionAttempted){d.reductionAttempted=!0;let t=await a({text:r.text,status:C.status,signal:e.signal},b,C.requiredLines);if(D)return r;if(e.signal.throwIfAborted(),t.accepted)d.reduced=t.text,E.verifiedReductions++;else return E.rejectedReductions++,r}if(T&&d.reduced===void 0)return r;let O=`[Observation stored. ID: ${d.record.locator}; bytes: ${g}]\n${d.record.retrieval}\n`,k=d.reduced;if(k===void 0){let e=r.text.split(`
|
|
2
|
+
`),t=i(r.text).map(t=>`${t}: ${e[t-1]}`).join(`
|
|
3
|
+
`),n=p-c(O)-c(t)-40;if(n<=0)return r;k=`${u(r.text,n)}\n[Preview; omitted text is retrievable]\n${t}`}let A=O+k;return c(A)>=g?r:(d.packed=A,E.packedObservations++,{...r,text:A})});return{...n,content:s}});return{...t,content:n}}),D||(e.signal.throwIfAborted(),n=await N({...e,messages:n},e.messages),D))return{kind:`proceed`,messages:e.messages};e.signal.throwIfAborted();let o=e.messages.reduce((e,n)=>e+t(n),0),s=n.reduce((e,n)=>e+t(n),0);return E.estimatedTokensSaved+=Math.max(0,o-s),{kind:`proceed`,messages:Object.freeze(n)}},F=async e=>{if(D)return{kind:`proceed`,messages:e.messages};if(O)return{kind:`reject`,reason:`Context optimizer already has a prepare in flight; use one controller per session.`};let t=e.snapshot.entries.find(e=>`message`in e.event),n=t!==void 0&&`message`in t.event?String(t.event.message.id):void 0;if(k!==void 0&&n!==k)return{kind:`reject`,reason:`Context optimizer belongs to a different history; create a fresh controller after reset/resume.`};k??=n,O=!0;try{return await P({...e,signal:AbortSignal.any([e.signal,A.signal])})}finally{O=!1}},I=e=>{if(!(D||e.kind!==`before-model-request`))for(let t of e.request.messages??[])for(let e of t.content)e.type===`tool-result`&&e.content.forEach((n,r)=>{let i=S.get(`${t.id}:${e.toolCallId}:${r}`);i!==void 0&&i.exposures++})},L=Object.freeze({beforeStep:F,checkpoint:I});return Object.freeze({hooks:L,retrievalTool:M,completeMilestone(e){if(D)throw Error(`context optimizer is disposed`);if(T.size>=g)throw RangeError(`context optimizer milestone capacity reached`);if(!e.id.trim()||!e.summary.trim()||T.has(e.id))throw TypeError(`milestone requires a unique id and non-empty summary`);if(c(e.id)>128||c(e.summary)>8192)throw RangeError(`milestone id/summary exceeds 128/8192 bytes`);if(s(e.throughSeq,`throughSeq`),s(e.remainingTurns,`remainingTurns`),!Number.isFinite(e.compactionCost)||e.compactionCost<0||!Number.isFinite(e.historyTokenCost??1)||(e.historyTokenCost??1)<=0)throw RangeError(`milestone costs must be finite and non-negative; token cost must be positive`);T.add(e.id),C.push(Object.freeze({...e}))},wrapHooks(e={}){return{...e,async beforeStep(t){let n=await e.beforeStep?.(t);return n?.kind===`reject`?n:{...await F({...t,messages:n?.messages??t.messages}),...n?.prepend===void 0?{}:{prepend:n.prepend}}},async checkpoint(t){await e.checkpoint?.(t),await I(t)}}},metrics(){return Object.freeze({...E})},dispose(){D=!0,A.abort(Error(`Context optimizer disposed`)),S.clear(),C.length=0,w.length=0,T.clear()}})}function s(e,t){if(!Number.isSafeInteger(e)||e<1)throw RangeError(`${t} must be a positive safe integer`);return e}function c(e){return new TextEncoder().encode(e).byteLength}async function l(e,t){let n=[];for(let r=0;r<e.length;r++)n.push(await t(e[r],r));return n}function u(e,t){let n=``,r=0;for(let i of e){if(r+=c(i),r>t)break;n+=i}return n}function d(e){let t=new Set,n=new Set;for(let r of e)for(let e of r.content)e.type===`tool-call`&&t.add(String(e.id)),e.type===`tool-result`&&n.add(String(e.toolCallId));return t.size===n.size&&[...t].every(e=>n.has(e))}export{o as createContextOptimizer};
|
|
4
|
+
//# sourceMappingURL=context-optimizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-optimizer.js","names":[],"sources":["../../../src/agent/memory/context-optimizer.ts"],"sourcesContent":["import { createMessage, type Message } from '../../message/index.ts'\nimport { projectHistorySurface } from '../history/project.ts'\nimport type { HistorySnapshot } from '../history/types.ts'\nimport type { BeforeStepContext, TurnHooks } from '../loop/events.ts'\nimport type { SpillRecord, SpillStore } from '../tool/output-budget.ts'\nimport type { ToolDefinition } from '../tool/definition.ts'\nimport { readSpillTool } from '../tool/output-budget.ts'\nimport { diagnosticLineNumbers, reduceEvidence, type EvidenceReducer } from '../tool/evidence-reducer.ts'\nimport { estimateMessageTokens } from './token-estimator.ts'\n\nexport interface ContextMilestone {\n readonly id: string\n /** Inclusive append-only sequence boundary, captured after the completed step. */\n readonly throughSeq: number\n /** Host-verified state, including decisions and unresolved constraints needed later. */\n readonly summary: string\n readonly remainingTurns: number\n /** Same cost units as historyTokenCost; include input/output and summary-model price. */\n readonly compactionCost: number\n readonly historyTokenCost?: number\n}\nexport interface ContextOptimizerOptions {\n /** Mount the returned retrievalTool in the same session. Use a store scoped to that session. */\n readonly store: SpillStore\n readonly observationThresholdBytes?: number\n readonly summaryBytes?: number\n readonly fullRequests?: number\n readonly maxObservations?: number\n readonly maxMilestones?: number\n /** Persist raw snapshots locally/durably before a milestone projection is accepted. */\n readonly archive?: (snapshot: HistorySnapshot, milestone: ContextMilestone, signal: AbortSignal) => Promise<void>\n readonly reducer?: EvidenceReducer\n /** Explicitly identify logs and their authoritative outcome; core does not guess exit status. */\n readonly log?: (toolName: string, text: string) => {\n readonly status: 'pass' | 'fail' | 'unknown'; readonly requiredLines?: readonly number[]\n } | undefined\n readonly reductionThresholdBytes?: number\n}\nexport interface ContextOptimizationMetrics {\n readonly packedObservations: number\n readonly verifiedReductions: number\n readonly rejectedReductions: number\n readonly compactedMilestones: number\n readonly skippedMilestones: number\n /** Estimate of payload tokens avoided across prepared requests; never billing usage. */\n readonly estimatedTokensSaved: number\n}\ninterface Observation {\n readonly text: string\n readonly record: SpillRecord\n exposures: number\n reduced?: string\n reductionAttempted: boolean\n readonly log?: { readonly status: 'pass' | 'fail' | 'unknown'; readonly requiredLines?: readonly number[] }\n packed?: string\n}\ninterface Completed { readonly targets: ReadonlySet<string>; readonly inputs: ReadonlyMap<string, string>; readonly message: Message }\n\n/**\n * One controller per session. Projection affects requests only; snapshots retain raw history.\n * Compose its hooks with application hooks using wrapHooks(), and register retrievalTool.\n * Disposal drops projection state, never tool receipts or host-owned storage.\n */\nexport function createContextOptimizer(options: ContextOptimizerOptions) {\n const threshold = positive(options.observationThresholdBytes ?? 10 * 1024, 'observationThresholdBytes')\n const summaryBytes = positive(options.summaryBytes ?? 1024, 'summaryBytes')\n if (summaryBytes < 256) throw new RangeError('summaryBytes must be at least 256')\n const fullRequests = positive(options.fullRequests ?? 2, 'fullRequests')\n const maxObservations = positive(options.maxObservations ?? 64, 'maxObservations')\n const maxMilestones = positive(options.maxMilestones ?? 128, 'maxMilestones')\n const reductionThreshold = positive(options.reductionThresholdBytes ?? 4 * 1024, 'reductionThresholdBytes')\n const store = options.store\n const archive = options.archive\n const reducer = options.reducer\n const log = options.log\n const observations = new Map<string, Observation>()\n const pending: ContextMilestone[] = []\n const completed: Completed[] = []\n const milestoneIds = new Set<string>()\n const metrics = { packedObservations: 0, verifiedReductions: 0, rejectedReductions: 0,\n compactedMilestones: 0, skippedMilestones: 0, estimatedTokensSaved: 0 }\n let disposed = false\n let active = false\n let scope: string | undefined\n const lifetime = new AbortController()\n let completedThroughSeq = 0\n const retrievalTool: ToolDefinition = readSpillTool(store)\n\n const projectMilestones = async (context: BeforeStepContext, sourceMessages: readonly Message[]): Promise<readonly Message[]> => {\n const fingerprints = new Map((completed.length || pending.length ? sourceMessages : []).map(message => [String(message.id), JSON.stringify({ content: message.content, source: message.source, role: message.role })]))\n for (const milestone of pending.splice(0)) {\n if (milestone.throughSeq > (context.snapshot.entries.at(-1)?.seq ?? 0)) {\n metrics.skippedMilestones++; continue\n }\n const surface = projectHistorySurface(context.snapshot.entries)\n const visible = new Set(context.messages.map(message => message.id))\n // Preserve the initial request and injected state. Only completed, balanced ranges.\n const nodes = surface.slice(1).filter(node => node.seq > completedThroughSeq\n && node.seq <= milestone.throughSeq && node.message.source.kind !== 'app' && visible.has(node.message.id))\n const byId = new Map(context.messages.map(message => [message.id, message]))\n const messages = nodes.map(node => byId.get(node.message.id)!)\n const covered = new Set(completed.flatMap(group => [...group.targets]))\n if (nodes.length === 0 || nodes.some(node => covered.has(node.message.id)) || !balanced(messages)) {\n metrics.skippedMilestones++; continue\n }\n const summary = createMessage({ role: 'user', source: { kind: 'user' },\n content: [{ type: 'text', text: `[Completed ${milestone.id}: ${milestone.summary}]` }] })\n const saved = messages.reduce((sum, message) => sum + estimateMessageTokens(message), 0) - estimateMessageTokens(summary)\n if (saved <= 0 || milestone.compactionCost >= saved * milestone.remainingTurns * (milestone.historyTokenCost ?? 1)) {\n metrics.skippedMilestones++; continue\n }\n if (archive === undefined) { metrics.skippedMilestones++; continue }\n try {\n await archive(context.snapshot, milestone, context.signal)\n if (disposed) break\n context.signal.throwIfAborted()\n } catch {\n if (disposed) break\n context.signal.throwIfAborted()\n metrics.skippedMilestones++; continue\n }\n completed.push({ targets: new Set(messages.map(message => message.id)),\n inputs: new Map(messages.map(message => [String(message.id), fingerprints.get(message.id)!])), message: summary })\n completedThroughSeq = milestone.throughSeq\n metrics.compactedMilestones++\n }\n let messages = [...context.messages]\n // If regular pressure compaction has shadowed a target, its summary now owns that range.\n for (let i = completed.length - 1; i >= 0; i--) {\n const group = completed[i]!\n const visible = new Set(messages.map(message => String(message.id)))\n if (![...group.targets].every(id => visible.has(id) && fingerprints.get(id) === group.inputs.get(id))) {\n completed.splice(i, 1); continue\n }\n const first = messages.findIndex(message => group.targets.has(message.id))\n const retained = messages.filter(message => !group.targets.has(message.id))\n retained.splice(first, 0, group.message)\n messages = retained\n }\n return messages\n }\n\n const prepare: NonNullable<TurnHooks['beforeStep']> = async context => {\n let messages = context.messages\n const toolNames = new Map(context.snapshot.entries.flatMap(entry => entry.event.kind === 'tool-call'\n ? [[String(entry.event.callId), entry.event.name] as const] : []))\n messages = await mapSequential(messages, async message => {\n const content = await mapSequential(message.content, async block => {\n if (block.type !== 'tool-result') return block\n const toolName = toolNames.get(String(block.toolCallId)) ?? ''\n if (toolName === retrievalTool.name) return block\n const children = await mapSequential(block.content, async (child, index) => {\n if (disposed) return child\n context.signal.throwIfAborted()\n if (child.type !== 'text') return child\n const key = `${message.id}:${block.toolCallId}:${index}`\n let observation = observations.get(key)\n if (observation !== undefined && observation.text !== child.text) return child\n const bytes = observation?.record.bytes ?? byteLength(child.text)\n const identifiedLog = observation === undefined\n ? bytes > reductionThreshold ? log?.(toolName, child.text) : undefined\n : observation.log\n // Save may await a host backend. Capture its authority before that await.\n const logInfo = identifiedLog === undefined ? undefined : Object.freeze({ status: identifiedLog.status,\n ...identifiedLog.requiredLines === undefined ? {} : { requiredLines: Object.freeze([...identifiedLog.requiredLines]) } })\n const large = bytes > threshold\n const reducible = reducer !== undefined && logInfo !== undefined && bytes > reductionThreshold\n if (!large && !reducible) return child\n if (observation === undefined) {\n if (observations.size >= maxObservations) return child\n try {\n const record = await store.save(child.text, { toolName, callId: String(block.toolCallId) })\n if (disposed) return child\n context.signal.throwIfAborted()\n const captured = Object.freeze({ locator: record.locator, retrieval: record.retrieval, bytes: record.bytes })\n if (typeof captured.locator !== 'string' || !captured.locator || typeof captured.retrieval !== 'string'\n || !captured.retrieval || captured.bytes !== bytes) return child\n observation = { text: child.text, record: captured, exposures: 0, reductionAttempted: false,\n ...logInfo === undefined ? {} : { log: { ...logInfo,\n ...logInfo.requiredLines === undefined ? {} : { requiredLines: Object.freeze([...logInfo.requiredLines]) } } } }\n observations.set(key, observation)\n } catch { return child }\n }\n if (large && observation.exposures < fullRequests) return child\n // Retention is owned by the store. Do not emit a pointer known to be expired.\n try {\n if (await store.read(observation.record.locator, { offset: 0, limit: 1 }) === undefined) return child\n } catch { return child }\n if (disposed) return child\n context.signal.throwIfAborted()\n if (observation.packed !== undefined) {\n metrics.packedObservations++\n return { ...child, text: observation.packed }\n }\n if (reducible && !observation.reductionAttempted) {\n observation.reductionAttempted = true\n const result = await reduceEvidence({ text: child.text, status: logInfo.status, signal: context.signal }, reducer, logInfo.requiredLines)\n if (disposed) return child\n context.signal.throwIfAborted()\n if (result.accepted) { observation.reduced = result.text; metrics.verifiedReductions++ }\n else { metrics.rejectedReductions++; return child }\n }\n // Invalid reductions always preserve the original, including on later requests.\n if (reducible && observation.reduced === undefined) return child\n const header = `[Observation stored. ID: ${observation.record.locator}; bytes: ${bytes}]\\n${observation.record.retrieval}\\n`\n let body = observation.reduced\n if (body === undefined) {\n const lines = child.text.split('\\n')\n const evidence = diagnosticLineNumbers(child.text).map(line => `${line}: ${lines[line - 1]}`).join('\\n')\n const room = summaryBytes - byteLength(header) - byteLength(evidence) - 40\n if (room <= 0) return child\n body = `${utf8Prefix(child.text, room)}\\n[Preview; omitted text is retrievable]\\n${evidence}`\n }\n const text = header + body\n if (byteLength(text) >= bytes) return child\n observation.packed = text\n metrics.packedObservations++\n return { ...child, text }\n })\n return { ...block, content: children }\n })\n return { ...message, content }\n })\n if (disposed) return { kind: 'proceed', messages: context.messages }\n context.signal.throwIfAborted()\n messages = await projectMilestones({ ...context, messages }, context.messages)\n if (disposed) return { kind: 'proceed', messages: context.messages }\n context.signal.throwIfAborted()\n const before = context.messages.reduce((sum, message) => sum + estimateMessageTokens(message), 0)\n const after = messages.reduce((sum, message) => sum + estimateMessageTokens(message), 0)\n metrics.estimatedTokensSaved += Math.max(0, before - after)\n return { kind: 'proceed', messages: Object.freeze(messages) }\n }\n const beforeStep: NonNullable<TurnHooks['beforeStep']> = async context => {\n if (disposed) return { kind: 'proceed', messages: context.messages }\n if (active) return { kind: 'reject', reason: 'Context optimizer already has a prepare in flight; use one controller per session.' }\n const first = context.snapshot.entries.find(entry => 'message' in entry.event)\n const identity = first !== undefined && 'message' in first.event ? String(first.event.message.id) : undefined\n if (scope !== undefined && identity !== scope) return { kind: 'reject', reason: 'Context optimizer belongs to a different history; create a fresh controller after reset/resume.' }\n scope ??= identity\n active = true\n try { return await prepare({ ...context, signal: AbortSignal.any([context.signal, lifetime.signal]) }) }\n finally { active = false }\n }\n const checkpoint: NonNullable<TurnHooks['checkpoint']> = context => {\n if (disposed || context.kind !== 'before-model-request') return\n for (const message of context.request.messages ?? []) {\n for (const block of message.content) {\n if (block.type !== 'tool-result') continue\n block.content.forEach((_child, index) => {\n const observation = observations.get(`${message.id}:${block.toolCallId}:${index}`)\n if (observation !== undefined) observation.exposures++\n })\n }\n }\n }\n const hooks: TurnHooks = Object.freeze({ beforeStep, checkpoint })\n return Object.freeze({\n hooks, retrievalTool,\n completeMilestone(milestone: ContextMilestone): void {\n if (disposed) throw new Error('context optimizer is disposed')\n if (milestoneIds.size >= maxMilestones) throw new RangeError('context optimizer milestone capacity reached')\n if (!milestone.id.trim() || !milestone.summary.trim() || milestoneIds.has(milestone.id)) throw new TypeError('milestone requires a unique id and non-empty summary')\n if (byteLength(milestone.id) > 128 || byteLength(milestone.summary) > 8192) throw new RangeError('milestone id/summary exceeds 128/8192 bytes')\n positive(milestone.throughSeq, 'throughSeq')\n positive(milestone.remainingTurns, 'remainingTurns')\n if (!Number.isFinite(milestone.compactionCost) || milestone.compactionCost < 0\n || !Number.isFinite(milestone.historyTokenCost ?? 1) || (milestone.historyTokenCost ?? 1) <= 0) throw new RangeError('milestone costs must be finite and non-negative; token cost must be positive')\n milestoneIds.add(milestone.id)\n pending.push(Object.freeze({ ...milestone }))\n },\n /** Application hook runs first; checkpoints count only after host persistence succeeds. */\n wrapHooks(application: TurnHooks = {}): TurnHooks {\n return {\n ...application,\n async beforeStep(context) {\n const decision = await application.beforeStep?.(context)\n if (decision?.kind === 'reject') return decision\n const optimized = await beforeStep({ ...context, messages: decision?.messages ?? context.messages })\n return { ...optimized, ...decision?.prepend === undefined ? {} : { prepend: decision.prepend } }\n },\n async checkpoint(context) { await application.checkpoint?.(context); await checkpoint(context) },\n }\n },\n metrics(): ContextOptimizationMetrics { return Object.freeze({ ...metrics }) },\n dispose(): void { disposed = true; lifetime.abort(new Error('Context optimizer disposed')); observations.clear(); pending.length = 0; completed.length = 0; milestoneIds.clear() },\n })\n}\n\nfunction positive(value: number, name: string): number {\n if (!Number.isSafeInteger(value) || value < 1) throw new RangeError(`${name} must be a positive safe integer`)\n return value\n}\nfunction byteLength(text: string): number { return new TextEncoder().encode(text).byteLength }\nasync function mapSequential<T, U>(items: readonly T[], map: (item: T, index: number) => Promise<U>): Promise<U[]> {\n const result: U[] = []\n for (let index = 0; index < items.length; index++) result.push(await map(items[index]!, index))\n return result\n}\nfunction utf8Prefix(text: string, bytes: number): string {\n let result = '', used = 0\n for (const point of text) { used += byteLength(point); if (used > bytes) break; result += point }\n return result\n}\nfunction balanced(messages: readonly Message[]): boolean {\n const calls = new Set<string>(), results = new Set<string>()\n for (const message of messages) for (const block of message.content) {\n if (block.type === 'tool-call') calls.add(String(block.id))\n if (block.type === 'tool-result') results.add(String(block.toolCallId))\n }\n return calls.size === results.size && [...calls].every(id => results.has(id))\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const e=Object.freeze({objective:0,constraint:1,decision:2,"next-step":3,progress:4,fact:5});var t=class t{records=new Map;limits;nextId=1;storedChars=0;constructor(e=[],t={}){this.limits=i(t);for(let t of e)this.remember(t)}static fromSnapshot(e,n={}){if(typeof e!=`object`||!e||e.version!==1||!Array.isArray(e.items))throw TypeError(`unsupported agent memory snapshot`);let r=new t([],n);if(e.items.length>r.limits.maxItems)throw RangeError(`agent memory snapshot exceeds the ${r.limits.maxItems}-item limit`);for(let t of e.items){if(o(t),r.records.has(t.id))throw TypeError(`duplicate agent memory id '${t.id}'`);if(t.content.length>r.limits.maxItemChars)throw RangeError(`agent memory item '${t.id}' exceeds the ${r.limits.maxItemChars}-character limit`);if(r.storedChars+t.content.length>r.limits.maxStoredChars)throw RangeError(`agent memory snapshot exceeds the ${r.limits.maxStoredChars}-character limit`);r.records.set(t.id,Object.freeze({id:t.id,kind:t.kind,content:t.content,createdAt:t.createdAt,updatedAt:t.updatedAt})),r.storedChars+=t.content.length;let e=c(t.id);e!==void 0&&(r.nextId=Math.max(r.nextId,e))}return r}remember(e){r(e);let t=e.id??`memory-${this.nextId}`,n=e.content.trim();if(n.length>this.limits.maxItemChars)throw RangeError(`agent memory item '${t}' exceeds the ${this.limits.maxItemChars}-character limit`);let i=c(t),a=this.records.get(t);if(a===void 0&&this.records.size>=this.limits.maxItems)throw RangeError(`agent memory reached its ${this.limits.maxItems}-item limit`);let o=this.storedChars-(a?.content.length??0)+n.length;if(o>this.limits.maxStoredChars)throw RangeError(`agent memory reached its ${this.limits.maxStoredChars}-character limit`);let s=new Date().toISOString(),l=Object.freeze({id:t,kind:e.kind,content:n,createdAt:a?.createdAt??s,updatedAt:s});return this.records.set(t,l),this.storedChars=o,i!==void 0&&(this.nextId=Math.max(this.nextId,i)),l}forget(e){let t=this.records.get(e);return t!==void 0&&(this.records.delete(e),this.storedChars-=t.content.length,!0)}items(){return Object.freeze([...this.records.values()])}snapshot(){return Object.freeze({version:1,items:Object.freeze(this.items().map(e=>Object.freeze(structuredClone(e))))})}captureOriginalObjective(e){if([...this.records.values()].some(e=>e.kind===`objective`))return;let t=l(e,this.limits.maxItemChars).trim();return this.remember({id:`original-objective`,kind:`objective`,content:t.length>0?t:`[The original request contained non-text input.]`})}render(t=12e3){if(!Number.isInteger(t)||t<256)throw RangeError(`agent memory render maxChars must be an integer >= 256`);let n=[...this.records.values()].sort((t,n)=>e[t.kind]-e[n.kind]||t.createdAt.localeCompare(n.createdAt));if(n.length===0)return``;let r=[`<task-memory>`,`
|
|
1
|
+
const e=Object.freeze({objective:0,constraint:1,decision:2,"next-step":3,progress:4,fact:5});var t=class t{records=new Map;limits;nextId=1;storedChars=0;constructor(e=[],t={}){this.limits=i(t);for(let t of e)this.remember(t)}static fromSnapshot(e,n={}){if(typeof e!=`object`||!e||e.version!==1||!Array.isArray(e.items))throw TypeError(`unsupported agent memory snapshot`);let r=new t([],n);if(e.items.length>r.limits.maxItems)throw RangeError(`agent memory snapshot exceeds the ${r.limits.maxItems}-item limit`);for(let t of e.items){if(o(t),r.records.has(t.id))throw TypeError(`duplicate agent memory id '${t.id}'`);if(t.content.length>r.limits.maxItemChars)throw RangeError(`agent memory item '${t.id}' exceeds the ${r.limits.maxItemChars}-character limit`);if(r.storedChars+t.content.length>r.limits.maxStoredChars)throw RangeError(`agent memory snapshot exceeds the ${r.limits.maxStoredChars}-character limit`);r.records.set(t.id,Object.freeze({id:t.id,kind:t.kind,content:t.content,createdAt:t.createdAt,updatedAt:t.updatedAt})),r.storedChars+=t.content.length;let e=c(t.id);e!==void 0&&(r.nextId=Math.max(r.nextId,e))}return r}remember(e){r(e);let t=e.id??`memory-${this.nextId}`,n=e.content.trim();if(n.length>this.limits.maxItemChars)throw RangeError(`agent memory item '${t}' exceeds the ${this.limits.maxItemChars}-character limit`);let i=c(t),a=this.records.get(t);if(a===void 0&&this.records.size>=this.limits.maxItems)throw RangeError(`agent memory reached its ${this.limits.maxItems}-item limit`);let o=this.storedChars-(a?.content.length??0)+n.length;if(o>this.limits.maxStoredChars)throw RangeError(`agent memory reached its ${this.limits.maxStoredChars}-character limit`);let s=new Date().toISOString(),l=Object.freeze({id:t,kind:e.kind,content:n,createdAt:a?.createdAt??s,updatedAt:s});return this.records.set(t,l),this.storedChars=o,i!==void 0&&(this.nextId=Math.max(this.nextId,i)),l}forget(e){let t=this.records.get(e);return t!==void 0&&(this.records.delete(e),this.storedChars-=t.content.length,!0)}items(){return Object.freeze([...this.records.values()])}snapshot(){return Object.freeze({version:1,items:Object.freeze(this.items().map(e=>Object.freeze(structuredClone(e))))})}captureOriginalObjective(e){if([...this.records.values()].some(e=>e.kind===`objective`))return;let t=l(e,this.limits.maxItemChars).trim();return this.remember({id:`original-objective`,kind:`objective`,content:t.length>0?t:`[The original request contained non-text input.]`})}render(t=12e3){if(!Number.isInteger(t)||t<256)throw RangeError(`agent memory render maxChars must be an integer >= 256`);let n=[...this.records.values()].sort((t,n)=>e[t.kind]-e[n.kind]||t.createdAt.localeCompare(n.createdAt));if(n.length===0)return``;let r=[`<task-memory>`,`Retained context is background from earlier work. Newer user messages supersede conflicting objectives or constraints below. Follow the latest request and its output format.`],i=t-r.join(`
|
|
2
2
|
`).length-14-2,a=[];for(let e of n){if(i<=0)break;let t=`- ${e.kind} [${e.id}]: `,n=Math.max(0,i-t.length-1),r=t+u(JSON.stringify(e.content),n);a.push(r),i-=r.length+1}return[...r,...a,`</task-memory>`].join(`
|
|
3
3
|
`)}};function n(e){let t=e?.maxInjectedChars??12e3;if(!Number.isInteger(t)||t<256)throw RangeError(`agent memory maxInjectedChars must be an integer >= 256`);let n=i(e??{}),a=new Set,o=1,s=0,l=Object.freeze([...e?.seed??[]].map(e=>{r(e);let t=e.id??`memory-${o++}`,i=c(t);if(i!==void 0&&(o=Math.max(o,i)),a.has(t))throw TypeError(`duplicate agent memory seed id '${t}'`);a.add(t);let l=e.content.trim().length;if(l>n.maxItemChars)throw RangeError(`agent memory seed '${t}' exceeds the ${n.maxItemChars}-character limit`);if(s+=l,s>n.maxStoredChars)throw RangeError(`agent memory seed exceeds the ${n.maxStoredChars}-character limit`);return Object.freeze({...e.id===void 0?{}:{id:e.id},kind:e.kind,content:e.content})}));if(l.length>n.maxItems)throw RangeError(`agent memory seed exceeds the ${n.maxItems}-item limit`);return Object.freeze({autoCaptureObjective:e?.autoCaptureObjective??!0,maxInjectedChars:t,...n,seed:l})}function r(t){if(t.id!==void 0&&(typeof t.id!=`string`||t.id.trim().length===0||t.id.length>256||/\s/.test(t.id)))throw TypeError(`agent memory id must be non-empty, at most 256 characters, and contain no whitespace`);if(!Object.hasOwn(e,t.kind))throw TypeError(`unsupported agent memory kind '${String(t.kind)}'`);if(typeof t.content!=`string`||t.content.trim().length===0)throw TypeError(`agent memory content must be a non-empty string`)}function i(e){let t=a(e.maxItems??1024,`maxItems`),n=a(e.maxItemChars??65536,`maxItemChars`),r=a(e.maxStoredChars??1048576,`maxStoredChars`);if(n>r)throw RangeError(`agent memory maxItemChars must not exceed maxStoredChars`);return Object.freeze({maxItems:t,maxItemChars:n,maxStoredChars:r})}function a(e,t){if(!Number.isSafeInteger(e)||e<1)throw RangeError(`agent memory ${t} must be a positive safe integer`);return e}function o(e){if(r(e),!s(e.createdAt)||!s(e.updatedAt))throw TypeError(`agent memory timestamps must be bounded ISO date strings`)}function s(e){if(typeof e!=`string`||e.length===0||e.length>64)return!1;let t=Date.parse(e);if(!Number.isFinite(t))return!1;try{return new Date(t).toISOString()===e}catch{return!1}}function c(e){let t=/^memory-(\d+)$/.exec(e)?.[1];if(t===void 0)return;let n=Number(t);return Number.isSafeInteger(n)&&n>=1&&n<2**53-1?n+1:void 0}function l(e,t){let n=``,r=!1;for(let i of e.content){if(i.type!==`text`)continue;let e=n.length===0?``:`
|
|
4
4
|
`,a=t-n.length;if(e.length+i.text.length<=a){n+=e+i.text;continue}let o=Math.max(0,a-e.length-22);n+=e+i.text.slice(0,o)+`…[objective truncated]`.slice(0,Math.max(0,a-e.length-o)),r=!0;break}return r?n.slice(0,t):n}function u(e,t){if(e.length<=t)return e;if(t<=1)return e.slice(0,t);let n=Math.ceil((t-1)/2),r=Math.floor((t-1)/2);return e.slice(0,n)+`…`+e.slice(e.length-r)}export{t as AgentMemory,n as resolveMemoryConfig};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory.js","names":[],"sources":["../../../src/agent/memory/memory.ts"],"sourcesContent":["/** Durable, explicitly inspectable task memory kept outside compactable history. */\n\nimport type { UserMessage } from '../../message/index.ts'\n\nexport type AgentMemoryKind =\n | 'objective'\n | 'constraint'\n | 'decision'\n | 'fact'\n | 'progress'\n | 'next-step'\n\nexport interface AgentMemorySeed {\n readonly id?: string\n readonly kind: AgentMemoryKind\n readonly content: string\n}\n\nexport interface AgentMemoryItem {\n readonly id: string\n readonly kind: AgentMemoryKind\n readonly content: string\n readonly createdAt: string\n readonly updatedAt: string\n}\n\nexport interface AgentMemorySnapshot {\n readonly version: 1\n readonly items: readonly AgentMemoryItem[]\n}\n\nexport interface AgentMemoryConfigInput {\n /** Pin the first real user message as the original objective. Defaults to true. */\n readonly autoCaptureObjective?: boolean\n /** Maximum rendered memory characters injected into every model request. Defaults to 12,000. */\n readonly maxInjectedChars?: number\n /** Maximum retained memory records. Defaults to 1,024. */\n readonly maxItems?: number\n /** Maximum characters retained by one record. Defaults to 65,536. */\n readonly maxItemChars?: number\n /** Maximum cumulative retained content characters. Defaults to 1 MiB. */\n readonly maxStoredChars?: number\n readonly seed?: readonly AgentMemorySeed[]\n}\n\nexport interface AgentMemoryConfig {\n readonly autoCaptureObjective: boolean\n readonly maxInjectedChars: number\n readonly maxItems: number\n readonly maxItemChars: number\n readonly maxStoredChars: number\n readonly seed: readonly AgentMemorySeed[]\n}\n\nconst PRIORITY: Readonly<Record<AgentMemoryKind, number>> = Object.freeze({\n objective: 0,\n constraint: 1,\n decision: 2,\n 'next-step': 3,\n progress: 4,\n fact: 5,\n})\n\nexport class AgentMemory {\n private readonly records = new Map<string, AgentMemoryItem>()\n private readonly limits: Pick<AgentMemoryConfig, 'maxItems' | 'maxItemChars' | 'maxStoredChars'>\n private nextId = 1\n private storedChars = 0\n\n constructor(\n seed: readonly AgentMemorySeed[] = [],\n limits: Partial<Pick<AgentMemoryConfig, 'maxItems' | 'maxItemChars' | 'maxStoredChars'>> = {},\n ) {\n this.limits = resolveMemoryLimits(limits)\n for (const item of seed) this.remember(item)\n }\n\n static fromSnapshot(\n snapshot: AgentMemorySnapshot,\n limits: Partial<Pick<AgentMemoryConfig, 'maxItems' | 'maxItemChars' | 'maxStoredChars'>> = {},\n ): AgentMemory {\n if (typeof snapshot !== 'object' || snapshot === null\n || snapshot.version !== 1 || !Array.isArray(snapshot.items)) {\n throw new TypeError('unsupported agent memory snapshot')\n }\n const memory = new AgentMemory([], limits)\n if (snapshot.items.length > memory.limits.maxItems) {\n throw new RangeError(`agent memory snapshot exceeds the ${memory.limits.maxItems}-item limit`)\n }\n for (const item of snapshot.items) {\n validateItem(item)\n if (memory.records.has(item.id)) throw new TypeError(`duplicate agent memory id '${item.id}'`)\n if (item.content.length > memory.limits.maxItemChars) {\n throw new RangeError(`agent memory item '${item.id}' exceeds the ${memory.limits.maxItemChars}-character limit`)\n }\n if (memory.storedChars + item.content.length > memory.limits.maxStoredChars) {\n throw new RangeError(`agent memory snapshot exceeds the ${memory.limits.maxStoredChars}-character limit`)\n }\n memory.records.set(item.id, Object.freeze({\n id: item.id,\n kind: item.kind,\n content: item.content,\n createdAt: item.createdAt,\n updatedAt: item.updatedAt,\n }))\n memory.storedChars += item.content.length\n const successor = memoryIdSuccessor(item.id)\n if (successor !== undefined) memory.nextId = Math.max(memory.nextId, successor)\n }\n return memory\n }\n\n /** Add or update one stable memory item. Supplying the same id is an upsert. */\n remember(input: AgentMemorySeed): AgentMemoryItem {\n validateSeed(input)\n const id = input.id ?? `memory-${this.nextId}`\n const content = input.content.trim()\n if (content.length > this.limits.maxItemChars) {\n throw new RangeError(`agent memory item '${id}' exceeds the ${this.limits.maxItemChars}-character limit`)\n }\n const successor = memoryIdSuccessor(id)\n const previous = this.records.get(id)\n if (previous === undefined && this.records.size >= this.limits.maxItems) {\n throw new RangeError(`agent memory reached its ${this.limits.maxItems}-item limit`)\n }\n const nextStoredChars = this.storedChars - (previous?.content.length ?? 0) + content.length\n if (nextStoredChars > this.limits.maxStoredChars) {\n throw new RangeError(`agent memory reached its ${this.limits.maxStoredChars}-character limit`)\n }\n const at = new Date().toISOString()\n const item = Object.freeze({\n id,\n kind: input.kind,\n content,\n createdAt: previous?.createdAt ?? at,\n updatedAt: at,\n })\n this.records.set(id, item)\n this.storedChars = nextStoredChars\n if (successor !== undefined) this.nextId = Math.max(this.nextId, successor)\n return item\n }\n\n forget(id: string): boolean {\n const item = this.records.get(id)\n if (item === undefined) return false\n this.records.delete(id)\n this.storedChars -= item.content.length\n return true\n }\n\n items(): readonly AgentMemoryItem[] {\n return Object.freeze([...this.records.values()])\n }\n\n snapshot(): AgentMemorySnapshot {\n return Object.freeze({\n version: 1,\n items: Object.freeze(this.items().map(item => Object.freeze(structuredClone(item)))),\n })\n }\n\n /** Capture the first user request verbatim enough to survive every later compaction. */\n captureOriginalObjective(message: UserMessage): AgentMemoryItem | undefined {\n if ([...this.records.values()].some(item => item.kind === 'objective')) return undefined\n const text = collectTextPrefix(message, this.limits.maxItemChars).trim()\n return this.remember({\n id: 'original-objective',\n kind: 'objective',\n content: text.length > 0 ? text : '[The original request contained non-text input.]',\n })\n }\n\n /** Render bounded pinned context, ordered by importance rather than insertion accident. */\n render(maxChars = 12_000): string {\n if (!Number.isInteger(maxChars) || maxChars < 256) {\n throw new RangeError('agent memory render maxChars must be an integer >= 256')\n }\n const ordered = [...this.records.values()].sort((a, b) =>\n PRIORITY[a.kind] - PRIORITY[b.kind] || a.createdAt.localeCompare(b.createdAt))\n if (ordered.length === 0) return ''\n const preamble = [\n '<task-memory>',\n 'Durable task facts follow. Preserve the original objective and obey active constraints even when older conversation turns were compacted.',\n ]\n const closing = '</task-memory>'\n let remaining = maxChars - preamble.join('\\n').length - closing.length - 2\n const lines: string[] = []\n for (const item of ordered) {\n if (remaining <= 0) break\n const prefix = `- ${item.kind} [${item.id}]: `\n const contentBudget = Math.max(0, remaining - prefix.length - 1)\n const content = truncateMiddle(JSON.stringify(item.content), contentBudget)\n const line = prefix + content\n lines.push(line)\n remaining -= line.length + 1\n }\n return [...preamble, ...lines, closing].join('\\n')\n }\n}\n\nexport function resolveMemoryConfig(input: AgentMemoryConfigInput | undefined): AgentMemoryConfig {\n const maxInjectedChars = input?.maxInjectedChars ?? 12_000\n if (!Number.isInteger(maxInjectedChars) || maxInjectedChars < 256) {\n throw new RangeError('agent memory maxInjectedChars must be an integer >= 256')\n }\n const limits = resolveMemoryLimits(input ?? {})\n const ids = new Set<string>()\n let nextId = 1\n let storedChars = 0\n const seed = Object.freeze([...(input?.seed ?? [])].map(item => {\n validateSeed(item)\n const id = item.id ?? `memory-${nextId++}`\n const successor = memoryIdSuccessor(id)\n if (successor !== undefined) nextId = Math.max(nextId, successor)\n if (ids.has(id)) throw new TypeError(`duplicate agent memory seed id '${id}'`)\n ids.add(id)\n const chars = item.content.trim().length\n if (chars > limits.maxItemChars) {\n throw new RangeError(`agent memory seed '${id}' exceeds the ${limits.maxItemChars}-character limit`)\n }\n storedChars += chars\n if (storedChars > limits.maxStoredChars) {\n throw new RangeError(`agent memory seed exceeds the ${limits.maxStoredChars}-character limit`)\n }\n return Object.freeze({\n ...(item.id === undefined ? {} : { id: item.id }),\n kind: item.kind,\n content: item.content,\n })\n }))\n if (seed.length > limits.maxItems) {\n throw new RangeError(`agent memory seed exceeds the ${limits.maxItems}-item limit`)\n }\n return Object.freeze({\n autoCaptureObjective: input?.autoCaptureObjective ?? true,\n maxInjectedChars,\n ...limits,\n seed,\n })\n}\n\nfunction validateSeed(input: AgentMemorySeed): void {\n if (input.id !== undefined && (typeof input.id !== 'string'\n || input.id.trim().length === 0 || input.id.length > 256 || /\\s/.test(input.id))) {\n throw new TypeError('agent memory id must be non-empty, at most 256 characters, and contain no whitespace')\n }\n if (!Object.hasOwn(PRIORITY, input.kind)) throw new TypeError(`unsupported agent memory kind '${String(input.kind)}'`)\n if (typeof input.content !== 'string' || input.content.trim().length === 0) {\n throw new TypeError('agent memory content must be a non-empty string')\n }\n}\n\nfunction resolveMemoryLimits(\n input: Partial<Pick<AgentMemoryConfig, 'maxItems' | 'maxItemChars' | 'maxStoredChars'>>,\n): Pick<AgentMemoryConfig, 'maxItems' | 'maxItemChars' | 'maxStoredChars'> {\n const maxItems = positiveInteger(input.maxItems ?? 1_024, 'maxItems')\n const maxItemChars = positiveInteger(input.maxItemChars ?? 65_536, 'maxItemChars')\n const maxStoredChars = positiveInteger(input.maxStoredChars ?? 1024 * 1024, 'maxStoredChars')\n if (maxItemChars > maxStoredChars) {\n throw new RangeError('agent memory maxItemChars must not exceed maxStoredChars')\n }\n return Object.freeze({ maxItems, maxItemChars, maxStoredChars })\n}\n\nfunction positiveInteger(value: number, name: string): number {\n if (!Number.isSafeInteger(value) || value < 1) {\n throw new RangeError(`agent memory ${name} must be a positive safe integer`)\n }\n return value\n}\n\nfunction validateItem(item: AgentMemoryItem): void {\n validateSeed(item)\n if (!validTimestamp(item.createdAt) || !validTimestamp(item.updatedAt)) {\n throw new TypeError('agent memory timestamps must be bounded ISO date strings')\n }\n}\n\nfunction validTimestamp(value: unknown): value is string {\n if (typeof value !== 'string' || value.length === 0 || value.length > 64) return false\n const parsed = Date.parse(value)\n if (!Number.isFinite(parsed)) return false\n try { return new Date(parsed).toISOString() === value } catch { return false }\n}\n\nfunction memoryIdSuccessor(id: string): number | undefined {\n const suffix = /^memory-(\\d+)$/.exec(id)?.[1]\n if (suffix === undefined) return undefined\n const numeric = Number(suffix)\n return Number.isSafeInteger(numeric) && numeric >= 1 && numeric < Number.MAX_SAFE_INTEGER\n ? numeric + 1\n : undefined\n}\n\nfunction collectTextPrefix(message: UserMessage, maxChars: number): string {\n const marker = '…[objective truncated]'\n let text = ''\n let truncated = false\n for (const block of message.content) {\n if (block.type !== 'text') continue\n const separator = text.length === 0 ? '' : '\\n'\n const remaining = maxChars - text.length\n if (separator.length + block.text.length <= remaining) {\n text += separator + block.text\n continue\n }\n const available = Math.max(0, remaining - separator.length - marker.length)\n text += separator + block.text.slice(0, available) + marker.slice(0, Math.max(0, remaining - separator.length - available))\n truncated = true\n break\n }\n return truncated ? text.slice(0, maxChars) : text\n}\n\nfunction truncateMiddle(value: string, maxChars: number): string {\n if (value.length <= maxChars) return value\n if (maxChars <= 1) return value.slice(0, maxChars)\n const marker = '…'\n const head = Math.ceil((maxChars - marker.length) / 2)\n const tail = Math.floor((maxChars - marker.length) / 2)\n return value.slice(0, head) + marker + value.slice(value.length - tail)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"memory.js","names":[],"sources":["../../../src/agent/memory/memory.ts"],"sourcesContent":["/** Durable, explicitly inspectable task memory kept outside compactable history. */\n\nimport type { UserMessage } from '../../message/index.ts'\n\nexport type AgentMemoryKind =\n | 'objective'\n | 'constraint'\n | 'decision'\n | 'fact'\n | 'progress'\n | 'next-step'\n\nexport interface AgentMemorySeed {\n readonly id?: string\n readonly kind: AgentMemoryKind\n readonly content: string\n}\n\nexport interface AgentMemoryItem {\n readonly id: string\n readonly kind: AgentMemoryKind\n readonly content: string\n readonly createdAt: string\n readonly updatedAt: string\n}\n\nexport interface AgentMemorySnapshot {\n readonly version: 1\n readonly items: readonly AgentMemoryItem[]\n}\n\nexport interface AgentMemoryConfigInput {\n /** Pin the first real user message as the original objective. Defaults to true. */\n readonly autoCaptureObjective?: boolean\n /** Maximum rendered memory characters injected into every model request. Defaults to 12,000. */\n readonly maxInjectedChars?: number\n /** Maximum retained memory records. Defaults to 1,024. */\n readonly maxItems?: number\n /** Maximum characters retained by one record. Defaults to 65,536. */\n readonly maxItemChars?: number\n /** Maximum cumulative retained content characters. Defaults to 1 MiB. */\n readonly maxStoredChars?: number\n readonly seed?: readonly AgentMemorySeed[]\n}\n\nexport interface AgentMemoryConfig {\n readonly autoCaptureObjective: boolean\n readonly maxInjectedChars: number\n readonly maxItems: number\n readonly maxItemChars: number\n readonly maxStoredChars: number\n readonly seed: readonly AgentMemorySeed[]\n}\n\nconst PRIORITY: Readonly<Record<AgentMemoryKind, number>> = Object.freeze({\n objective: 0,\n constraint: 1,\n decision: 2,\n 'next-step': 3,\n progress: 4,\n fact: 5,\n})\n\nexport class AgentMemory {\n private readonly records = new Map<string, AgentMemoryItem>()\n private readonly limits: Pick<AgentMemoryConfig, 'maxItems' | 'maxItemChars' | 'maxStoredChars'>\n private nextId = 1\n private storedChars = 0\n\n constructor(\n seed: readonly AgentMemorySeed[] = [],\n limits: Partial<Pick<AgentMemoryConfig, 'maxItems' | 'maxItemChars' | 'maxStoredChars'>> = {},\n ) {\n this.limits = resolveMemoryLimits(limits)\n for (const item of seed) this.remember(item)\n }\n\n static fromSnapshot(\n snapshot: AgentMemorySnapshot,\n limits: Partial<Pick<AgentMemoryConfig, 'maxItems' | 'maxItemChars' | 'maxStoredChars'>> = {},\n ): AgentMemory {\n if (typeof snapshot !== 'object' || snapshot === null\n || snapshot.version !== 1 || !Array.isArray(snapshot.items)) {\n throw new TypeError('unsupported agent memory snapshot')\n }\n const memory = new AgentMemory([], limits)\n if (snapshot.items.length > memory.limits.maxItems) {\n throw new RangeError(`agent memory snapshot exceeds the ${memory.limits.maxItems}-item limit`)\n }\n for (const item of snapshot.items) {\n validateItem(item)\n if (memory.records.has(item.id)) throw new TypeError(`duplicate agent memory id '${item.id}'`)\n if (item.content.length > memory.limits.maxItemChars) {\n throw new RangeError(`agent memory item '${item.id}' exceeds the ${memory.limits.maxItemChars}-character limit`)\n }\n if (memory.storedChars + item.content.length > memory.limits.maxStoredChars) {\n throw new RangeError(`agent memory snapshot exceeds the ${memory.limits.maxStoredChars}-character limit`)\n }\n memory.records.set(item.id, Object.freeze({\n id: item.id,\n kind: item.kind,\n content: item.content,\n createdAt: item.createdAt,\n updatedAt: item.updatedAt,\n }))\n memory.storedChars += item.content.length\n const successor = memoryIdSuccessor(item.id)\n if (successor !== undefined) memory.nextId = Math.max(memory.nextId, successor)\n }\n return memory\n }\n\n /** Add or update one stable memory item. Supplying the same id is an upsert. */\n remember(input: AgentMemorySeed): AgentMemoryItem {\n validateSeed(input)\n const id = input.id ?? `memory-${this.nextId}`\n const content = input.content.trim()\n if (content.length > this.limits.maxItemChars) {\n throw new RangeError(`agent memory item '${id}' exceeds the ${this.limits.maxItemChars}-character limit`)\n }\n const successor = memoryIdSuccessor(id)\n const previous = this.records.get(id)\n if (previous === undefined && this.records.size >= this.limits.maxItems) {\n throw new RangeError(`agent memory reached its ${this.limits.maxItems}-item limit`)\n }\n const nextStoredChars = this.storedChars - (previous?.content.length ?? 0) + content.length\n if (nextStoredChars > this.limits.maxStoredChars) {\n throw new RangeError(`agent memory reached its ${this.limits.maxStoredChars}-character limit`)\n }\n const at = new Date().toISOString()\n const item = Object.freeze({\n id,\n kind: input.kind,\n content,\n createdAt: previous?.createdAt ?? at,\n updatedAt: at,\n })\n this.records.set(id, item)\n this.storedChars = nextStoredChars\n if (successor !== undefined) this.nextId = Math.max(this.nextId, successor)\n return item\n }\n\n forget(id: string): boolean {\n const item = this.records.get(id)\n if (item === undefined) return false\n this.records.delete(id)\n this.storedChars -= item.content.length\n return true\n }\n\n items(): readonly AgentMemoryItem[] {\n return Object.freeze([...this.records.values()])\n }\n\n snapshot(): AgentMemorySnapshot {\n return Object.freeze({\n version: 1,\n items: Object.freeze(this.items().map(item => Object.freeze(structuredClone(item)))),\n })\n }\n\n /** Capture the first user request verbatim enough to survive every later compaction. */\n captureOriginalObjective(message: UserMessage): AgentMemoryItem | undefined {\n if ([...this.records.values()].some(item => item.kind === 'objective')) return undefined\n const text = collectTextPrefix(message, this.limits.maxItemChars).trim()\n return this.remember({\n id: 'original-objective',\n kind: 'objective',\n content: text.length > 0 ? text : '[The original request contained non-text input.]',\n })\n }\n\n /** Render bounded pinned context, ordered by importance rather than insertion accident. */\n render(maxChars = 12_000): string {\n if (!Number.isInteger(maxChars) || maxChars < 256) {\n throw new RangeError('agent memory render maxChars must be an integer >= 256')\n }\n const ordered = [...this.records.values()].sort((a, b) =>\n PRIORITY[a.kind] - PRIORITY[b.kind] || a.createdAt.localeCompare(b.createdAt))\n if (ordered.length === 0) return ''\n const preamble = [\n '<task-memory>',\n 'Retained context is background from earlier work. Newer user messages supersede conflicting objectives or constraints below. Follow the latest request and its output format.',\n ]\n const closing = '</task-memory>'\n let remaining = maxChars - preamble.join('\\n').length - closing.length - 2\n const lines: string[] = []\n for (const item of ordered) {\n if (remaining <= 0) break\n const prefix = `- ${item.kind} [${item.id}]: `\n const contentBudget = Math.max(0, remaining - prefix.length - 1)\n const content = truncateMiddle(JSON.stringify(item.content), contentBudget)\n const line = prefix + content\n lines.push(line)\n remaining -= line.length + 1\n }\n return [...preamble, ...lines, closing].join('\\n')\n }\n}\n\nexport function resolveMemoryConfig(input: AgentMemoryConfigInput | undefined): AgentMemoryConfig {\n const maxInjectedChars = input?.maxInjectedChars ?? 12_000\n if (!Number.isInteger(maxInjectedChars) || maxInjectedChars < 256) {\n throw new RangeError('agent memory maxInjectedChars must be an integer >= 256')\n }\n const limits = resolveMemoryLimits(input ?? {})\n const ids = new Set<string>()\n let nextId = 1\n let storedChars = 0\n const seed = Object.freeze([...(input?.seed ?? [])].map(item => {\n validateSeed(item)\n const id = item.id ?? `memory-${nextId++}`\n const successor = memoryIdSuccessor(id)\n if (successor !== undefined) nextId = Math.max(nextId, successor)\n if (ids.has(id)) throw new TypeError(`duplicate agent memory seed id '${id}'`)\n ids.add(id)\n const chars = item.content.trim().length\n if (chars > limits.maxItemChars) {\n throw new RangeError(`agent memory seed '${id}' exceeds the ${limits.maxItemChars}-character limit`)\n }\n storedChars += chars\n if (storedChars > limits.maxStoredChars) {\n throw new RangeError(`agent memory seed exceeds the ${limits.maxStoredChars}-character limit`)\n }\n return Object.freeze({\n ...(item.id === undefined ? {} : { id: item.id }),\n kind: item.kind,\n content: item.content,\n })\n }))\n if (seed.length > limits.maxItems) {\n throw new RangeError(`agent memory seed exceeds the ${limits.maxItems}-item limit`)\n }\n return Object.freeze({\n autoCaptureObjective: input?.autoCaptureObjective ?? true,\n maxInjectedChars,\n ...limits,\n seed,\n })\n}\n\nfunction validateSeed(input: AgentMemorySeed): void {\n if (input.id !== undefined && (typeof input.id !== 'string'\n || input.id.trim().length === 0 || input.id.length > 256 || /\\s/.test(input.id))) {\n throw new TypeError('agent memory id must be non-empty, at most 256 characters, and contain no whitespace')\n }\n if (!Object.hasOwn(PRIORITY, input.kind)) throw new TypeError(`unsupported agent memory kind '${String(input.kind)}'`)\n if (typeof input.content !== 'string' || input.content.trim().length === 0) {\n throw new TypeError('agent memory content must be a non-empty string')\n }\n}\n\nfunction resolveMemoryLimits(\n input: Partial<Pick<AgentMemoryConfig, 'maxItems' | 'maxItemChars' | 'maxStoredChars'>>,\n): Pick<AgentMemoryConfig, 'maxItems' | 'maxItemChars' | 'maxStoredChars'> {\n const maxItems = positiveInteger(input.maxItems ?? 1_024, 'maxItems')\n const maxItemChars = positiveInteger(input.maxItemChars ?? 65_536, 'maxItemChars')\n const maxStoredChars = positiveInteger(input.maxStoredChars ?? 1024 * 1024, 'maxStoredChars')\n if (maxItemChars > maxStoredChars) {\n throw new RangeError('agent memory maxItemChars must not exceed maxStoredChars')\n }\n return Object.freeze({ maxItems, maxItemChars, maxStoredChars })\n}\n\nfunction positiveInteger(value: number, name: string): number {\n if (!Number.isSafeInteger(value) || value < 1) {\n throw new RangeError(`agent memory ${name} must be a positive safe integer`)\n }\n return value\n}\n\nfunction validateItem(item: AgentMemoryItem): void {\n validateSeed(item)\n if (!validTimestamp(item.createdAt) || !validTimestamp(item.updatedAt)) {\n throw new TypeError('agent memory timestamps must be bounded ISO date strings')\n }\n}\n\nfunction validTimestamp(value: unknown): value is string {\n if (typeof value !== 'string' || value.length === 0 || value.length > 64) return false\n const parsed = Date.parse(value)\n if (!Number.isFinite(parsed)) return false\n try { return new Date(parsed).toISOString() === value } catch { return false }\n}\n\nfunction memoryIdSuccessor(id: string): number | undefined {\n const suffix = /^memory-(\\d+)$/.exec(id)?.[1]\n if (suffix === undefined) return undefined\n const numeric = Number(suffix)\n return Number.isSafeInteger(numeric) && numeric >= 1 && numeric < Number.MAX_SAFE_INTEGER\n ? numeric + 1\n : undefined\n}\n\nfunction collectTextPrefix(message: UserMessage, maxChars: number): string {\n const marker = '…[objective truncated]'\n let text = ''\n let truncated = false\n for (const block of message.content) {\n if (block.type !== 'text') continue\n const separator = text.length === 0 ? '' : '\\n'\n const remaining = maxChars - text.length\n if (separator.length + block.text.length <= remaining) {\n text += separator + block.text\n continue\n }\n const available = Math.max(0, remaining - separator.length - marker.length)\n text += separator + block.text.slice(0, available) + marker.slice(0, Math.max(0, remaining - separator.length - available))\n truncated = true\n break\n }\n return truncated ? text.slice(0, maxChars) : text\n}\n\nfunction truncateMiddle(value: string, maxChars: number): string {\n if (value.length <= maxChars) return value\n if (maxChars <= 1) return value.slice(0, maxChars)\n const marker = '…'\n const head = Math.ceil((maxChars - marker.length) / 2)\n const tail = Math.floor((maxChars - marker.length) / 2)\n return value.slice(0, head) + marker + value.slice(value.length - tail)\n}\n"],"mappings":"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"}
|
|
@@ -9,6 +9,20 @@ declare const AGENT_CONTROL_TOOLS: Readonly<{
|
|
|
9
9
|
readonly complete: 'submit_result';
|
|
10
10
|
readonly requestUserInput: 'request_user_input';
|
|
11
11
|
}>;
|
|
12
|
+
/**
|
|
13
|
+
* What the model replies with, after an accepted self-check, when the answer
|
|
14
|
+
* it already gave the user earlier in the run still stands.
|
|
15
|
+
*
|
|
16
|
+
* Deep modes gate on an accepted `submit_result`, so a model that answered
|
|
17
|
+
* before checking is asked to check, then told to "deliver the answer"
|
|
18
|
+
* again — even when the check found nothing to add. Without this marker that
|
|
19
|
+
* second pass either pads an already-adequate answer or, worse, quietly
|
|
20
|
+
* changes it while claiming to only be confirming it. `driveAgent` treats a
|
|
21
|
+
* reply that is exactly this marker as "no change": the run's `text` (and,
|
|
22
|
+
* where the earlier answer is still the history's current surface, the
|
|
23
|
+
* transcript) keep the answer already given instead of this marker.
|
|
24
|
+
*/
|
|
25
|
+
declare const UNCHANGED_ANSWER_MARKER = "<<deep-mode:answer-unchanged>>";
|
|
12
26
|
/** Default high-level runtime requested by this SDK; `runTurn` remains provider-neutral. */
|
|
13
27
|
type AgentMode = 'basic' | 'deep' | 'deep-human-in-loop';
|
|
14
28
|
interface AgentRunCommon extends Omit<RunTurnOptions, 'bounds' | 'commentary' | 'config' | 'hooks' | 'system' | 'tools'> {
|
|
@@ -75,5 +89,5 @@ type AgentRunEvent = AgentEvent | AgentModeStartEvent | UserInputRequestEvent |
|
|
|
75
89
|
*/
|
|
76
90
|
declare function runAgent(options: RunAgentOptions): AsyncIterable<AgentRunEvent>;
|
|
77
91
|
//#endregion
|
|
78
|
-
export { AGENT_CONTROL_TOOLS, AgentMode, AgentRunEvent, AgentRunOutcome, BasicAgentOptions, CompletionSubmission, DeepAgentOptions, HumanInLoopAgentOptions, RunAgentOptions, runAgent };
|
|
92
|
+
export { AGENT_CONTROL_TOOLS, AgentMode, AgentRunEvent, AgentRunOutcome, BasicAgentOptions, CompletionSubmission, DeepAgentOptions, HumanInLoopAgentOptions, RunAgentOptions, UNCHANGED_ANSWER_MARKER, runAgent };
|
|
79
93
|
//# sourceMappingURL=run-agent.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-agent.d.ts","names":[],"sources":["../../../src/agent/mode/run-agent.ts"],"mappings":";;;;;;;cAkBa,qBAAmB;WACpB;WACQ
|
|
1
|
+
{"version":3,"file":"run-agent.d.ts","names":[],"sources":["../../../src/agent/mode/run-agent.ts"],"mappings":";;;;;;;cAkBa,qBAAmB;WACpB;WACQ;;;;;;;;;;;;;;;cAgBP;;KAGD;UAEF,uBAAuB,KAAK;;WAE3B,QAAQ;WACR,QAAQ;WACR;;;WAGA;WACA,SAAS,KAAK,QAAQ;WACtB,aAAa;WACb,QAAQ;;UAGF,0BAA0B;WAChC;;UAGM,yBAAyB;WAC/B;;WAEA,YAAY;;UAGN,gCAAgC;WACtC;;WAEA,WAAW;;KAGV,kBAAkB,oBAAoB,mBAAmB;UAEpD;WACN;WACA;;UAGM,wBAAwB;WAC9B,MAAM;;WAEN;WACA,aAAa;;UAGd;WACC;WACA,MAAM;WACN;;UAED;WACC;WACA,SAAS;;UAEV;WACC;WACA,SAAS;WACT,UAAU;;UAEX;WACC;WACA,SAAS;;KAGR,gBAAgB,aACxB,sBACA,wBACA,yBACA;;;;;;;;iBA4CY,SAAS,SAAS,kBAAkB,cAAc"}
|