@deepagents/context 0.38.0 → 0.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.js +7 -0
- package/dist/browser.js.map +2 -2
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +393 -126
- package/dist/index.js.map +4 -4
- package/dist/lib/chat.d.ts +19 -5
- package/dist/lib/chat.d.ts.map +1 -1
- package/dist/lib/engine.d.ts +44 -2
- package/dist/lib/engine.d.ts.map +1 -1
- package/dist/lib/fragments/message/user.d.ts +1 -0
- package/dist/lib/fragments/message/user.d.ts.map +1 -1
- package/dist/lib/fragments/reminders/combinators.d.ts +23 -0
- package/dist/lib/fragments/reminders/combinators.d.ts.map +1 -1
- package/dist/lib/fragments/reminders/message-predicates.d.ts +10 -0
- package/dist/lib/fragments/reminders/message-predicates.d.ts.map +1 -0
- package/dist/lib/fragments/reminders/temporal/elapsed.d.ts +3 -0
- package/dist/lib/fragments/reminders/temporal/elapsed.d.ts.map +1 -0
- package/dist/lib/fragments/reminders/temporal/index.d.ts +1 -0
- package/dist/lib/fragments/reminders/temporal/index.d.ts.map +1 -1
- package/dist/lib/fragments/reminders/tool-predicates.d.ts +17 -0
- package/dist/lib/fragments/reminders/tool-predicates.d.ts.map +1 -0
- package/dist/lib/fragments/reminders/usage-predicates.d.ts +3 -0
- package/dist/lib/fragments/reminders/usage-predicates.d.ts.map +1 -0
- package/dist/lib/fragments.d.ts +2 -0
- package/dist/lib/fragments.d.ts.map +1 -1
- package/dist/lib/title.d.ts +16 -7
- package/dist/lib/title.d.ts.map +1 -1
- package/dist/lib/tracing/exporter.d.ts.map +1 -1
- package/dist/lib/tracing/index.js +16 -1
- package/dist/lib/tracing/index.js.map +2 -2
- package/package.json +14 -13
package/dist/index.js
CHANGED
|
@@ -199,6 +199,12 @@ function assistant(message2) {
|
|
|
199
199
|
}
|
|
200
200
|
};
|
|
201
201
|
}
|
|
202
|
+
function toMessageFragment(item) {
|
|
203
|
+
if (isFragment(item) && isMessageFragment(item)) {
|
|
204
|
+
return item;
|
|
205
|
+
}
|
|
206
|
+
return message(item);
|
|
207
|
+
}
|
|
202
208
|
function message(content) {
|
|
203
209
|
const message2 = typeof content === "string" ? {
|
|
204
210
|
id: generateId(),
|
|
@@ -1396,27 +1402,27 @@ import {
|
|
|
1396
1402
|
} from "ai";
|
|
1397
1403
|
import chalk from "chalk";
|
|
1398
1404
|
function createRepairToolCall(model, abortSignal) {
|
|
1399
|
-
return async ({ toolCall, tools, inputSchema, error }) => {
|
|
1405
|
+
return async ({ toolCall: toolCall2, tools, inputSchema, error }) => {
|
|
1400
1406
|
if (NoSuchToolError.isInstance(error)) {
|
|
1401
1407
|
return null;
|
|
1402
1408
|
}
|
|
1403
1409
|
console.log(
|
|
1404
|
-
`Debug: ${chalk.yellow("RepairingToolCall")}: ${
|
|
1410
|
+
`Debug: ${chalk.yellow("RepairingToolCall")}: ${toolCall2.toolName}`
|
|
1405
1411
|
);
|
|
1406
|
-
const tool3 = tools[
|
|
1412
|
+
const tool3 = tools[toolCall2.toolName];
|
|
1407
1413
|
const { output } = await generateText({
|
|
1408
1414
|
abortSignal,
|
|
1409
1415
|
model,
|
|
1410
1416
|
output: Output.object({ schema: tool3.inputSchema }),
|
|
1411
1417
|
prompt: [
|
|
1412
|
-
`The model tried to call the tool "${
|
|
1413
|
-
JSON.stringify(
|
|
1418
|
+
`The model tried to call the tool "${toolCall2.toolName}" with the following inputs:`,
|
|
1419
|
+
JSON.stringify(toolCall2.input),
|
|
1414
1420
|
`The tool accepts the following schema:`,
|
|
1415
|
-
JSON.stringify(inputSchema(
|
|
1421
|
+
JSON.stringify(inputSchema(toolCall2)),
|
|
1416
1422
|
"Please fix the inputs."
|
|
1417
1423
|
].join("\n")
|
|
1418
1424
|
});
|
|
1419
|
-
return { ...
|
|
1425
|
+
return { ...toolCall2, input: JSON.stringify(output) };
|
|
1420
1426
|
};
|
|
1421
1427
|
}
|
|
1422
1428
|
|
|
@@ -1464,10 +1470,10 @@ var Agent = class _Agent {
|
|
|
1464
1470
|
),
|
|
1465
1471
|
toolChoice: this.#options.toolChoice,
|
|
1466
1472
|
onStepFinish: (step) => {
|
|
1467
|
-
const
|
|
1468
|
-
if (
|
|
1473
|
+
const toolCall2 = step.toolCalls.at(-1);
|
|
1474
|
+
if (toolCall2) {
|
|
1469
1475
|
console.log(
|
|
1470
|
-
`Debug: ${chalk2.yellow("ToolCalled")}: ${
|
|
1476
|
+
`Debug: ${chalk2.yellow("ToolCalled")}: ${toolCall2.toolName}(${JSON.stringify(toolCall2.input)})`
|
|
1471
1477
|
);
|
|
1472
1478
|
}
|
|
1473
1479
|
}
|
|
@@ -1538,10 +1544,10 @@ var Agent = class _Agent {
|
|
|
1538
1544
|
experimental_context: contextVariables,
|
|
1539
1545
|
toolChoice: this.#options.toolChoice,
|
|
1540
1546
|
onStepFinish: (step) => {
|
|
1541
|
-
const
|
|
1542
|
-
if (
|
|
1547
|
+
const toolCall2 = step.toolCalls.at(-1);
|
|
1548
|
+
if (toolCall2) {
|
|
1543
1549
|
console.log(
|
|
1544
|
-
`Debug: (${runId}) ${chalk2.bold.yellow("ToolCalled")}: ${
|
|
1550
|
+
`Debug: (${runId}) ${chalk2.bold.yellow("ToolCalled")}: ${toolCall2.toolName}(${JSON.stringify(toolCall2.input)})`
|
|
1545
1551
|
);
|
|
1546
1552
|
}
|
|
1547
1553
|
}
|
|
@@ -1869,7 +1875,6 @@ import {
|
|
|
1869
1875
|
NoSuchToolError as NoSuchToolError2,
|
|
1870
1876
|
ToolCallRepairError,
|
|
1871
1877
|
createUIMessageStream as createUIMessageStream2,
|
|
1872
|
-
generateId as generateId4,
|
|
1873
1878
|
isToolUIPart
|
|
1874
1879
|
} from "ai";
|
|
1875
1880
|
|
|
@@ -1877,7 +1882,10 @@ import {
|
|
|
1877
1882
|
import { z as z2 } from "zod";
|
|
1878
1883
|
|
|
1879
1884
|
// packages/context/src/lib/engine.ts
|
|
1880
|
-
import {
|
|
1885
|
+
import {
|
|
1886
|
+
generateId as generateId4,
|
|
1887
|
+
validateUIMessages
|
|
1888
|
+
} from "ai";
|
|
1881
1889
|
import { mergeWith } from "lodash-es";
|
|
1882
1890
|
|
|
1883
1891
|
// packages/context/src/lib/estimate.ts
|
|
@@ -2585,6 +2593,9 @@ function estimateMessageContent(data) {
|
|
|
2585
2593
|
function isLanguageModelUsage(value) {
|
|
2586
2594
|
return typeof value === "object" && value !== null && "totalTokens" in value;
|
|
2587
2595
|
}
|
|
2596
|
+
function isEmptyAssistantPlaceholder(message2) {
|
|
2597
|
+
return message2.role === "assistant" && message2.parts.length === 0;
|
|
2598
|
+
}
|
|
2588
2599
|
var ContextEngine = class _ContextEngine {
|
|
2589
2600
|
/** Non-message fragments (role, hints, etc.) - not persisted in graph */
|
|
2590
2601
|
#fragments = [];
|
|
@@ -2733,6 +2744,7 @@ var ContextEngine = class _ContextEngine {
|
|
|
2733
2744
|
let lastMessageAt;
|
|
2734
2745
|
let lastMessage;
|
|
2735
2746
|
let lastAssistantMessage;
|
|
2747
|
+
const lastAssistantMessages = [];
|
|
2736
2748
|
if (this.#branch?.headMessageId) {
|
|
2737
2749
|
const chain = await this.#store.getMessageChain(
|
|
2738
2750
|
this.#branch.headMessageId
|
|
@@ -2740,7 +2752,9 @@ var ContextEngine = class _ContextEngine {
|
|
|
2740
2752
|
for (const msg of chain) {
|
|
2741
2753
|
messageCount++;
|
|
2742
2754
|
if (msg.name === "assistant") {
|
|
2743
|
-
|
|
2755
|
+
const assistantMsg = msg.data;
|
|
2756
|
+
lastAssistantMessage = assistantMsg;
|
|
2757
|
+
lastAssistantMessages.push(assistantMsg);
|
|
2744
2758
|
}
|
|
2745
2759
|
if (msg.name !== "user") {
|
|
2746
2760
|
continue;
|
|
@@ -2759,13 +2773,128 @@ var ContextEngine = class _ContextEngine {
|
|
|
2759
2773
|
messageCount,
|
|
2760
2774
|
lastMessageAt,
|
|
2761
2775
|
lastMessage,
|
|
2762
|
-
lastAssistantMessage
|
|
2776
|
+
lastAssistantMessage,
|
|
2777
|
+
lastAssistantMessages
|
|
2763
2778
|
};
|
|
2764
2779
|
}
|
|
2765
2780
|
async getTurnCount() {
|
|
2766
2781
|
const { turn } = await this.#getChainContext();
|
|
2767
2782
|
return turn;
|
|
2768
2783
|
}
|
|
2784
|
+
async firstUserMessage() {
|
|
2785
|
+
await this.#ensureInitialized();
|
|
2786
|
+
if (this.#branch?.headMessageId) {
|
|
2787
|
+
const chain = await this.#store.getMessageChain(
|
|
2788
|
+
this.#branch.headMessageId
|
|
2789
|
+
);
|
|
2790
|
+
for (const msg of chain) {
|
|
2791
|
+
if (msg.name === "user") return msg.data;
|
|
2792
|
+
}
|
|
2793
|
+
}
|
|
2794
|
+
for (const fragment2 of this.#pendingMessages) {
|
|
2795
|
+
if (fragment2.name !== "user") continue;
|
|
2796
|
+
if (!fragment2.codec) {
|
|
2797
|
+
throw new Error(`Fragment "${fragment2.name}" is missing codec.`);
|
|
2798
|
+
}
|
|
2799
|
+
return fragment2.codec.encode();
|
|
2800
|
+
}
|
|
2801
|
+
return void 0;
|
|
2802
|
+
}
|
|
2803
|
+
/**
|
|
2804
|
+
* Return the head of the conversation — pending tail or persisted branch head.
|
|
2805
|
+
*
|
|
2806
|
+
* Includes empty assistant placeholders (use this for id-lookup, not for
|
|
2807
|
+
* building model prompts — see `getMessages()` for prompt-ready output).
|
|
2808
|
+
*
|
|
2809
|
+
* @throws if the pending tail is missing an id (programming error).
|
|
2810
|
+
*/
|
|
2811
|
+
async headMessage() {
|
|
2812
|
+
await this.#ensureInitialized();
|
|
2813
|
+
if (this.#pendingMessages.length > 0) {
|
|
2814
|
+
const tail = this.#pendingMessages[this.#pendingMessages.length - 1];
|
|
2815
|
+
if (!tail.id) {
|
|
2816
|
+
throw new Error(
|
|
2817
|
+
`headMessage: pending fragment "${tail.name}" is missing id`
|
|
2818
|
+
);
|
|
2819
|
+
}
|
|
2820
|
+
return { id: tail.id, name: tail.name };
|
|
2821
|
+
}
|
|
2822
|
+
if (this.#branch?.headMessageId) {
|
|
2823
|
+
const msg = await this.#store.getMessage(this.#branch.headMessageId);
|
|
2824
|
+
if (msg) return { id: msg.id, name: msg.name };
|
|
2825
|
+
}
|
|
2826
|
+
return void 0;
|
|
2827
|
+
}
|
|
2828
|
+
/**
|
|
2829
|
+
* Return the model-ready conversation: persisted chain plus pending fragments,
|
|
2830
|
+
* with empty assistant placeholders filtered out.
|
|
2831
|
+
*
|
|
2832
|
+
* For id-lookup use `headMessage()` instead — that one keeps placeholders.
|
|
2833
|
+
*/
|
|
2834
|
+
async getMessages() {
|
|
2835
|
+
await this.#ensureInitialized();
|
|
2836
|
+
const messages = [];
|
|
2837
|
+
if (this.#branch?.headMessageId) {
|
|
2838
|
+
const chain = await this.#store.getMessageChain(
|
|
2839
|
+
this.#branch.headMessageId
|
|
2840
|
+
);
|
|
2841
|
+
for (const msg of chain) {
|
|
2842
|
+
const data = msg.data;
|
|
2843
|
+
if (isEmptyAssistantPlaceholder(data)) continue;
|
|
2844
|
+
messages.push(data);
|
|
2845
|
+
}
|
|
2846
|
+
}
|
|
2847
|
+
for (const fragment2 of this.#pendingMessages) {
|
|
2848
|
+
if (!fragment2.codec) {
|
|
2849
|
+
throw new Error(`Fragment "${fragment2.name}" is missing codec.`);
|
|
2850
|
+
}
|
|
2851
|
+
const encoded = fragment2.codec.encode();
|
|
2852
|
+
if (isEmptyAssistantPlaceholder(encoded)) continue;
|
|
2853
|
+
messages.push(encoded);
|
|
2854
|
+
}
|
|
2855
|
+
return messages.length === 0 ? [] : validateUIMessages({ messages });
|
|
2856
|
+
}
|
|
2857
|
+
/**
|
|
2858
|
+
* Advance the conversation by one turn. Required setup before `chat()`.
|
|
2859
|
+
*
|
|
2860
|
+
* - User input → persists the message AND appends an empty assistant
|
|
2861
|
+
* placeholder reserving the id of the next streamed response.
|
|
2862
|
+
* - Assistant input (tool-resume / continuation) → persists in-place
|
|
2863
|
+
* (`branch: false`), reusing the input's id.
|
|
2864
|
+
*
|
|
2865
|
+
* Always leaves the chain head as an assistant fragment, satisfying chat()'s
|
|
2866
|
+
* precondition.
|
|
2867
|
+
*
|
|
2868
|
+
* @returns the assistant id that will receive the streamed response — useful
|
|
2869
|
+
* for telemetry, optimistic UI, or correlating logs before the stream starts.
|
|
2870
|
+
* @throws if assistant input is missing an id.
|
|
2871
|
+
*
|
|
2872
|
+
* @example
|
|
2873
|
+
* ```ts
|
|
2874
|
+
* const assistantId = await context.continue(user('hi'));
|
|
2875
|
+
* const stream = await chat(agent); // streams into assistantId
|
|
2876
|
+
* ```
|
|
2877
|
+
*/
|
|
2878
|
+
async continue(input) {
|
|
2879
|
+
const fragment2 = toMessageFragment(input);
|
|
2880
|
+
const isAssistantUpdate = fragment2.name === "assistant";
|
|
2881
|
+
let assistantId;
|
|
2882
|
+
if (isAssistantUpdate) {
|
|
2883
|
+
if (!fragment2.id) {
|
|
2884
|
+
throw new Error("continue: assistant input is missing id");
|
|
2885
|
+
}
|
|
2886
|
+
assistantId = fragment2.id;
|
|
2887
|
+
this.set(fragment2);
|
|
2888
|
+
} else {
|
|
2889
|
+
assistantId = generateId4();
|
|
2890
|
+
this.set(
|
|
2891
|
+
fragment2,
|
|
2892
|
+
assistant({ id: assistantId, role: "assistant", parts: [] })
|
|
2893
|
+
);
|
|
2894
|
+
}
|
|
2895
|
+
await this.save({ branch: !isAssistantUpdate });
|
|
2896
|
+
return assistantId;
|
|
2897
|
+
}
|
|
2769
2898
|
/**
|
|
2770
2899
|
* Add fragments to the context.
|
|
2771
2900
|
*
|
|
@@ -2812,25 +2941,8 @@ var ContextEngine = class _ContextEngine {
|
|
|
2812
2941
|
async resolve(options) {
|
|
2813
2942
|
await this.#ensureInitialized();
|
|
2814
2943
|
const systemPrompt = options.renderer.render(this.#renderableFragments);
|
|
2815
|
-
const messages =
|
|
2816
|
-
|
|
2817
|
-
const chain = await this.#store.getMessageChain(
|
|
2818
|
-
this.#branch.headMessageId
|
|
2819
|
-
);
|
|
2820
|
-
for (const msg of chain) {
|
|
2821
|
-
messages.push(msg.data);
|
|
2822
|
-
}
|
|
2823
|
-
}
|
|
2824
|
-
for (const fragment2 of this.#pendingMessages) {
|
|
2825
|
-
if (!fragment2.codec) {
|
|
2826
|
-
throw new Error(`Fragment "${fragment2.name}" is missing codec.`);
|
|
2827
|
-
}
|
|
2828
|
-
messages.push(fragment2.codec.encode());
|
|
2829
|
-
}
|
|
2830
|
-
return {
|
|
2831
|
-
systemPrompt,
|
|
2832
|
-
messages: messages.length === 0 ? [] : await validateUIMessages({ messages })
|
|
2833
|
-
};
|
|
2944
|
+
const messages = await this.getMessages();
|
|
2945
|
+
return { systemPrompt, messages };
|
|
2834
2946
|
}
|
|
2835
2947
|
/**
|
|
2836
2948
|
* Save pending messages to the graph.
|
|
@@ -2881,7 +2993,8 @@ var ContextEngine = class _ContextEngine {
|
|
|
2881
2993
|
messageCount,
|
|
2882
2994
|
lastMessageAt,
|
|
2883
2995
|
lastMessage,
|
|
2884
|
-
lastAssistantMessage
|
|
2996
|
+
lastAssistantMessage,
|
|
2997
|
+
lastAssistantMessages
|
|
2885
2998
|
} = await this.#getChainContext();
|
|
2886
2999
|
const original = lastUserFragment.codec.encode();
|
|
2887
3000
|
const plainText = extractPlainText(original);
|
|
@@ -2899,7 +3012,8 @@ var ContextEngine = class _ContextEngine {
|
|
|
2899
3012
|
branch: this.#branchName,
|
|
2900
3013
|
elapsed,
|
|
2901
3014
|
messageCount,
|
|
2902
|
-
lastAssistantMessage
|
|
3015
|
+
lastAssistantMessage,
|
|
3016
|
+
lastAssistantMessages
|
|
2903
3017
|
};
|
|
2904
3018
|
const configs = conditionalReminders.map(
|
|
2905
3019
|
(it) => it.metadata.reminder
|
|
@@ -3746,55 +3860,75 @@ Examples:
|
|
|
3746
3860
|
- "hi" -> New Conversation
|
|
3747
3861
|
- "debug my python code" -> Python Debugging`;
|
|
3748
3862
|
var titleSchema = z2.object({ title: z2.string() });
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
}
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
}
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3863
|
+
var TitleGenerator = class {
|
|
3864
|
+
#context;
|
|
3865
|
+
constructor(options) {
|
|
3866
|
+
this.#context = options.context;
|
|
3867
|
+
}
|
|
3868
|
+
async ensure(options) {
|
|
3869
|
+
const msg = await this.#firstUntitledUser();
|
|
3870
|
+
if (!msg) return null;
|
|
3871
|
+
try {
|
|
3872
|
+
const title = await this.#generateTitle(msg, options);
|
|
3873
|
+
return this.#applyTitle(title, "llm");
|
|
3874
|
+
} catch (error) {
|
|
3875
|
+
console.warn(
|
|
3876
|
+
"TitleGenerator: LLM title generation failed, falling back to static.",
|
|
3877
|
+
error
|
|
3878
|
+
);
|
|
3879
|
+
return this.#applyTitle(this.#staticTitle(msg), "static");
|
|
3880
|
+
}
|
|
3881
|
+
}
|
|
3882
|
+
async ensureStatic() {
|
|
3883
|
+
const msg = await this.#firstUntitledUser();
|
|
3884
|
+
if (!msg) return null;
|
|
3885
|
+
return this.#applyTitle(this.#staticTitle(msg), "static");
|
|
3886
|
+
}
|
|
3887
|
+
#staticTitle(message2) {
|
|
3888
|
+
return this.#truncateTitle(this.#extractText(message2));
|
|
3889
|
+
}
|
|
3890
|
+
async #generateTitle(message2, options) {
|
|
3891
|
+
const text = this.#extractText(message2);
|
|
3892
|
+
if (!text) {
|
|
3893
|
+
throw new Error(
|
|
3894
|
+
"Cannot generate chat title: message has no text content."
|
|
3895
|
+
);
|
|
3896
|
+
}
|
|
3897
|
+
const store2 = new InMemoryContextStore();
|
|
3898
|
+
const context = new ContextEngine({
|
|
3899
|
+
store: store2,
|
|
3900
|
+
chatId: crypto.randomUUID(),
|
|
3901
|
+
userId: "system"
|
|
3902
|
+
});
|
|
3903
|
+
context.set(role(TITLE_PROMPT), user(text));
|
|
3773
3904
|
const { title } = await structuredOutput({
|
|
3774
3905
|
context,
|
|
3775
3906
|
model: options.model,
|
|
3776
3907
|
schema: titleSchema
|
|
3777
3908
|
}).generate({}, { abortSignal: options.abortSignal });
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
return
|
|
3909
|
+
if (!title) {
|
|
3910
|
+
throw new Error("Title generation returned an empty string.");
|
|
3911
|
+
}
|
|
3912
|
+
return title;
|
|
3782
3913
|
}
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
// packages/context/src/lib/chat.ts
|
|
3786
|
-
function toMessageFragment(item) {
|
|
3787
|
-
if (isFragment(item) && isMessageFragment(item)) {
|
|
3788
|
-
return item;
|
|
3914
|
+
#extractText(message2) {
|
|
3915
|
+
return extractPlainText(stripReminders(message2));
|
|
3789
3916
|
}
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
if (isFragment(item) && isMessageFragment(item)) {
|
|
3794
|
-
return item.codec.encode();
|
|
3917
|
+
#truncateTitle(text) {
|
|
3918
|
+
if (!text) return "New Chat";
|
|
3919
|
+
return text.length > 100 ? text.slice(0, 100) + "..." : text;
|
|
3795
3920
|
}
|
|
3796
|
-
|
|
3797
|
-
}
|
|
3921
|
+
async #applyTitle(title, source) {
|
|
3922
|
+
await this.#context.updateChat({ title });
|
|
3923
|
+
return { title, source };
|
|
3924
|
+
}
|
|
3925
|
+
async #firstUntitledUser() {
|
|
3926
|
+
if (this.#context.chat?.title) return void 0;
|
|
3927
|
+
return this.#context.firstUserMessage();
|
|
3928
|
+
}
|
|
3929
|
+
};
|
|
3930
|
+
|
|
3931
|
+
// packages/context/src/lib/chat.ts
|
|
3798
3932
|
var defaultChatMessageMetadata = ({
|
|
3799
3933
|
part
|
|
3800
3934
|
}) => {
|
|
@@ -3806,7 +3940,7 @@ var defaultChatMessageMetadata = ({
|
|
|
3806
3940
|
}
|
|
3807
3941
|
return void 0;
|
|
3808
3942
|
};
|
|
3809
|
-
async function chat(agent2,
|
|
3943
|
+
async function chat(agent2, options = {}) {
|
|
3810
3944
|
const context = agent2.context;
|
|
3811
3945
|
const sandbox = agent2.sandbox;
|
|
3812
3946
|
if (!context) {
|
|
@@ -3814,53 +3948,36 @@ async function chat(agent2, messages, options) {
|
|
|
3814
3948
|
"Agent is missing a context. Provide context when creating the agent."
|
|
3815
3949
|
);
|
|
3816
3950
|
}
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
const lastUIMessage = chatMessageToUIMessage(lastItem);
|
|
3823
|
-
let assistantMsgId;
|
|
3824
|
-
if (lastUIMessage.role === "assistant") {
|
|
3825
|
-
context.set(lastFragment);
|
|
3826
|
-
await context.save({ branch: false });
|
|
3827
|
-
assistantMsgId = lastUIMessage.id;
|
|
3828
|
-
} else {
|
|
3829
|
-
context.set(lastFragment);
|
|
3830
|
-
await context.save();
|
|
3831
|
-
assistantMsgId = generateId4();
|
|
3832
|
-
}
|
|
3833
|
-
const uiMessages = messages.map(chatMessageToUIMessage);
|
|
3834
|
-
let title = null;
|
|
3835
|
-
if (!context.chat?.title) {
|
|
3836
|
-
const firstUserMsg = uiMessages.find((m) => m.role === "user");
|
|
3837
|
-
if (firstUserMsg) {
|
|
3838
|
-
if (options?.generateTitle && agent2.model) {
|
|
3839
|
-
title = await generateChatTitle({
|
|
3840
|
-
message: firstUserMsg,
|
|
3841
|
-
model: agent2.model,
|
|
3842
|
-
abortSignal: options?.abortSignal
|
|
3843
|
-
});
|
|
3844
|
-
} else {
|
|
3845
|
-
title = staticChatTitle(firstUserMsg);
|
|
3846
|
-
}
|
|
3847
|
-
await context.updateChat({ title });
|
|
3848
|
-
}
|
|
3951
|
+
const head = await context.headMessage();
|
|
3952
|
+
if (head?.name !== "assistant") {
|
|
3953
|
+
throw new Error(
|
|
3954
|
+
"chat: expected an assistant message at head. Call context.continue(input) before chat()."
|
|
3955
|
+
);
|
|
3849
3956
|
}
|
|
3850
|
-
const
|
|
3851
|
-
const
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3957
|
+
const assistantMsgId = head.id;
|
|
3958
|
+
const uiMessages = await context.getMessages();
|
|
3959
|
+
const streamContextVariables = options.contextVariables === void 0 ? {} : options.contextVariables;
|
|
3960
|
+
const [title, result] = await Promise.all([
|
|
3961
|
+
makeTitle({
|
|
3962
|
+
context,
|
|
3963
|
+
model: agent2.model,
|
|
3964
|
+
generateTitle: options.generateTitle,
|
|
3965
|
+
abortSignal: options.abortSignal
|
|
3966
|
+
}),
|
|
3967
|
+
agent2.stream(streamContextVariables, {
|
|
3968
|
+
transform: options.transform,
|
|
3969
|
+
abortSignal: options.abortSignal
|
|
3970
|
+
})
|
|
3971
|
+
]);
|
|
3855
3972
|
const uiStream = result.toUIMessageStream({
|
|
3856
|
-
onError: options
|
|
3973
|
+
onError: options.onError ?? formatChatError,
|
|
3857
3974
|
sendStart: true,
|
|
3858
3975
|
sendFinish: true,
|
|
3859
3976
|
sendReasoning: true,
|
|
3860
3977
|
sendSources: true,
|
|
3861
3978
|
originalMessages: uiMessages,
|
|
3862
3979
|
generateMessageId: () => assistantMsgId,
|
|
3863
|
-
messageMetadata: options
|
|
3980
|
+
messageMetadata: options.messageMetadata ?? defaultChatMessageMetadata
|
|
3864
3981
|
});
|
|
3865
3982
|
return createUIMessageStream2({
|
|
3866
3983
|
originalMessages: uiMessages,
|
|
@@ -3883,7 +4000,7 @@ async function chat(agent2, messages, options) {
|
|
|
3883
4000
|
}
|
|
3884
4001
|
const drained = sandbox.drainFileEvents?.() ?? [];
|
|
3885
4002
|
const fileEvents = isAborted ? [] : drained;
|
|
3886
|
-
const finalMetadata = await options
|
|
4003
|
+
const finalMetadata = await options.finalAssistantMetadata?.(normalizedMessage);
|
|
3887
4004
|
const mergedMetadata = {
|
|
3888
4005
|
...normalizedMessage.metadata ?? {},
|
|
3889
4006
|
...fileEvents.length > 0 ? { fileEvents } : {},
|
|
@@ -3920,9 +4037,7 @@ function sanitizeAbortedParts(parts) {
|
|
|
3920
4037
|
sanitized.push(part);
|
|
3921
4038
|
continue;
|
|
3922
4039
|
}
|
|
3923
|
-
if (part.state === "input-streaming")
|
|
3924
|
-
continue;
|
|
3925
|
-
}
|
|
4040
|
+
if (part.state === "input-streaming") continue;
|
|
3926
4041
|
sanitized.push({
|
|
3927
4042
|
...part,
|
|
3928
4043
|
state: "output-error",
|
|
@@ -3947,6 +4062,19 @@ function formatChatError(error) {
|
|
|
3947
4062
|
}
|
|
3948
4063
|
return JSON.stringify(error);
|
|
3949
4064
|
}
|
|
4065
|
+
async function makeTitle(options) {
|
|
4066
|
+
const titler = new TitleGenerator({ context: options.context });
|
|
4067
|
+
if (options.generateTitle && !options.model) {
|
|
4068
|
+
console.warn(
|
|
4069
|
+
"chat: generateTitle=true but agent.model is unset; using static title."
|
|
4070
|
+
);
|
|
4071
|
+
}
|
|
4072
|
+
const result = options.generateTitle && options.model ? await titler.ensure({
|
|
4073
|
+
model: options.model,
|
|
4074
|
+
abortSignal: options.abortSignal
|
|
4075
|
+
}) : await titler.ensureStatic();
|
|
4076
|
+
return result?.title ?? null;
|
|
4077
|
+
}
|
|
3950
4078
|
|
|
3951
4079
|
// packages/context/src/lib/fragments/user.ts
|
|
3952
4080
|
function identity(input) {
|
|
@@ -4393,6 +4521,32 @@ function or(...predicates) {
|
|
|
4393
4521
|
function not(predicate) {
|
|
4394
4522
|
return async (ctx) => !await predicate(ctx);
|
|
4395
4523
|
}
|
|
4524
|
+
function withinLastN(n, predicate) {
|
|
4525
|
+
return async (ctx) => {
|
|
4526
|
+
if (n <= 0) return false;
|
|
4527
|
+
const candidates = (ctx.lastAssistantMessages ?? []).slice(-n);
|
|
4528
|
+
for (const message2 of candidates) {
|
|
4529
|
+
if (await predicate({ ...ctx, lastAssistantMessage: message2 })) {
|
|
4530
|
+
return true;
|
|
4531
|
+
}
|
|
4532
|
+
}
|
|
4533
|
+
return false;
|
|
4534
|
+
};
|
|
4535
|
+
}
|
|
4536
|
+
function everyOfLastN(n, predicate) {
|
|
4537
|
+
return async (ctx) => {
|
|
4538
|
+
if (n <= 0) return false;
|
|
4539
|
+
const history = ctx.lastAssistantMessages ?? [];
|
|
4540
|
+
if (history.length < n) return false;
|
|
4541
|
+
const candidates = history.slice(-n);
|
|
4542
|
+
for (const message2 of candidates) {
|
|
4543
|
+
if (!await predicate({ ...ctx, lastAssistantMessage: message2 })) {
|
|
4544
|
+
return false;
|
|
4545
|
+
}
|
|
4546
|
+
}
|
|
4547
|
+
return true;
|
|
4548
|
+
};
|
|
4549
|
+
}
|
|
4396
4550
|
|
|
4397
4551
|
// packages/context/src/lib/fragments/reminders/content-predicates.ts
|
|
4398
4552
|
function contentMatches(topics, options) {
|
|
@@ -4418,6 +4572,37 @@ function contentPattern(pattern) {
|
|
|
4418
4572
|
};
|
|
4419
4573
|
}
|
|
4420
4574
|
|
|
4575
|
+
// packages/context/src/lib/fragments/reminders/message-predicates.ts
|
|
4576
|
+
function assertCountSpec(spec) {
|
|
4577
|
+
const hasEq = spec.eq !== void 0;
|
|
4578
|
+
const hasRange = spec.gte !== void 0 || spec.lte !== void 0;
|
|
4579
|
+
if (!hasEq && !hasRange) {
|
|
4580
|
+
throw new Error("CountSpec must include at least one of gte/lte/eq");
|
|
4581
|
+
}
|
|
4582
|
+
if (hasEq && hasRange) {
|
|
4583
|
+
throw new Error("CountSpec.eq cannot be combined with gte/lte");
|
|
4584
|
+
}
|
|
4585
|
+
}
|
|
4586
|
+
function checkCount(count, spec) {
|
|
4587
|
+
if (spec.eq !== void 0) return count === spec.eq;
|
|
4588
|
+
if (spec.gte !== void 0 && count < spec.gte) return false;
|
|
4589
|
+
if (spec.lte !== void 0 && count > spec.lte) return false;
|
|
4590
|
+
return true;
|
|
4591
|
+
}
|
|
4592
|
+
function lastAssistantLength(spec) {
|
|
4593
|
+
assertCountSpec(spec);
|
|
4594
|
+
return (ctx) => {
|
|
4595
|
+
const message2 = ctx.lastAssistantMessage;
|
|
4596
|
+
const text = message2 ? extractPlainText(message2) : "";
|
|
4597
|
+
return checkCount(text.length, spec);
|
|
4598
|
+
};
|
|
4599
|
+
}
|
|
4600
|
+
|
|
4601
|
+
// packages/context/src/lib/fragments/reminders/temporal/elapsed.ts
|
|
4602
|
+
function elapsedExceeds(ms) {
|
|
4603
|
+
return (ctx) => (ctx.elapsed ?? 0) >= ms;
|
|
4604
|
+
}
|
|
4605
|
+
|
|
4421
4606
|
// packages/context/src/lib/fragments/reminders/temporal/predicates.ts
|
|
4422
4607
|
var LOCALE_METADATA_KEY = "locale";
|
|
4423
4608
|
function getLocaleFromMessage(message2) {
|
|
@@ -4689,6 +4874,73 @@ function temporalReminder(options) {
|
|
|
4689
4874
|
];
|
|
4690
4875
|
}
|
|
4691
4876
|
|
|
4877
|
+
// packages/context/src/lib/fragments/reminders/tool-predicates.ts
|
|
4878
|
+
import { isStaticToolUIPart } from "ai";
|
|
4879
|
+
var COMPLETED_STATES = /* @__PURE__ */ new Set([
|
|
4880
|
+
"input-available",
|
|
4881
|
+
"output-available",
|
|
4882
|
+
"output-error"
|
|
4883
|
+
]);
|
|
4884
|
+
function matchesName(spec, name) {
|
|
4885
|
+
return typeof spec === "function" ? spec(name) : spec === name;
|
|
4886
|
+
}
|
|
4887
|
+
function toolNameOf(part) {
|
|
4888
|
+
return part.type.slice("tool-".length);
|
|
4889
|
+
}
|
|
4890
|
+
function toolPartsOf(message2) {
|
|
4891
|
+
if (!message2) return [];
|
|
4892
|
+
return message2.parts.filter(isStaticToolUIPart);
|
|
4893
|
+
}
|
|
4894
|
+
function toolCall(options) {
|
|
4895
|
+
return (ctx) => {
|
|
4896
|
+
const parts = toolPartsOf(ctx.lastAssistantMessage);
|
|
4897
|
+
return parts.some((part) => {
|
|
4898
|
+
if (options.state) {
|
|
4899
|
+
if (part.state !== options.state) return false;
|
|
4900
|
+
} else if (!COMPLETED_STATES.has(part.state)) {
|
|
4901
|
+
return false;
|
|
4902
|
+
}
|
|
4903
|
+
if (options.name !== void 0 && !matchesName(options.name, toolNameOf(part))) {
|
|
4904
|
+
return false;
|
|
4905
|
+
}
|
|
4906
|
+
if (options.input && !options.input(part.input)) {
|
|
4907
|
+
return false;
|
|
4908
|
+
}
|
|
4909
|
+
if (options.output) {
|
|
4910
|
+
if (part.state !== "output-available") return false;
|
|
4911
|
+
if (!options.output(part.output))
|
|
4912
|
+
return false;
|
|
4913
|
+
}
|
|
4914
|
+
if (options.errorText) {
|
|
4915
|
+
if (part.state !== "output-error") return false;
|
|
4916
|
+
const text = part.errorText ?? "";
|
|
4917
|
+
if (!options.errorText(text)) return false;
|
|
4918
|
+
}
|
|
4919
|
+
return true;
|
|
4920
|
+
});
|
|
4921
|
+
};
|
|
4922
|
+
}
|
|
4923
|
+
function toolCalled(name) {
|
|
4924
|
+
return toolCall({ name });
|
|
4925
|
+
}
|
|
4926
|
+
function toolFailed(name) {
|
|
4927
|
+
return toolCall({ name, state: "output-error" });
|
|
4928
|
+
}
|
|
4929
|
+
function anyToolCalled() {
|
|
4930
|
+
return (ctx) => toolPartsOf(ctx.lastAssistantMessage).some(
|
|
4931
|
+
(part) => COMPLETED_STATES.has(part.state)
|
|
4932
|
+
);
|
|
4933
|
+
}
|
|
4934
|
+
function toolCallCount(name, spec) {
|
|
4935
|
+
assertCountSpec(spec);
|
|
4936
|
+
return (ctx) => {
|
|
4937
|
+
const count = toolPartsOf(ctx.lastAssistantMessage).filter(
|
|
4938
|
+
(part) => COMPLETED_STATES.has(part.state) && matchesName(name, toolNameOf(part))
|
|
4939
|
+
).length;
|
|
4940
|
+
return checkCount(count, spec);
|
|
4941
|
+
};
|
|
4942
|
+
}
|
|
4943
|
+
|
|
4692
4944
|
// packages/context/src/lib/fragments/reminders/turn-predicates.ts
|
|
4693
4945
|
function everyNTurns(n) {
|
|
4694
4946
|
return ({ turn }) => turn % n === 0;
|
|
@@ -4703,6 +4955,11 @@ function afterTurn(n) {
|
|
|
4703
4955
|
return ({ turn }) => turn > n;
|
|
4704
4956
|
}
|
|
4705
4957
|
|
|
4958
|
+
// packages/context/src/lib/fragments/reminders/usage-predicates.ts
|
|
4959
|
+
function usageExceeds(totalTokens) {
|
|
4960
|
+
return (ctx) => (ctx.usage?.totalTokens ?? 0) >= totalTokens;
|
|
4961
|
+
}
|
|
4962
|
+
|
|
4706
4963
|
// packages/context/src/lib/fragments/socratic.ts
|
|
4707
4964
|
function socraticPrompting() {
|
|
4708
4965
|
return [
|
|
@@ -9211,6 +9468,7 @@ export {
|
|
|
9211
9468
|
SqliteStreamStore,
|
|
9212
9469
|
StreamManager,
|
|
9213
9470
|
StreamStore,
|
|
9471
|
+
TitleGenerator,
|
|
9214
9472
|
TomlRenderer,
|
|
9215
9473
|
ToonRenderer,
|
|
9216
9474
|
XmlRenderer,
|
|
@@ -9223,18 +9481,20 @@ export {
|
|
|
9223
9481
|
alias,
|
|
9224
9482
|
analogy,
|
|
9225
9483
|
and,
|
|
9484
|
+
anyToolCalled,
|
|
9226
9485
|
applyInlineReminder,
|
|
9227
9486
|
applyPartReminder,
|
|
9228
9487
|
applyReminderToMessage,
|
|
9229
9488
|
asStaticWordPartText,
|
|
9230
9489
|
asStaticWordText,
|
|
9490
|
+
assertCountSpec,
|
|
9231
9491
|
assistant,
|
|
9232
9492
|
assistantText,
|
|
9233
9493
|
buildSchemaSubcommand,
|
|
9234
9494
|
buildSubcommandRepair,
|
|
9235
9495
|
chainHooks,
|
|
9236
9496
|
chat,
|
|
9237
|
-
|
|
9497
|
+
checkCount,
|
|
9238
9498
|
clarification,
|
|
9239
9499
|
classifies,
|
|
9240
9500
|
contentIncludes,
|
|
@@ -9257,10 +9517,12 @@ export {
|
|
|
9257
9517
|
defaultTokenizer,
|
|
9258
9518
|
defineSubcommandGroup,
|
|
9259
9519
|
discoverSkillsInDirectory,
|
|
9520
|
+
elapsedExceeds,
|
|
9260
9521
|
encodeSerializedValue,
|
|
9261
9522
|
errorRecoveryGuardrail,
|
|
9262
9523
|
estimate,
|
|
9263
9524
|
everyNTurns,
|
|
9525
|
+
everyOfLastN,
|
|
9264
9526
|
example,
|
|
9265
9527
|
executorContext,
|
|
9266
9528
|
explain,
|
|
@@ -9272,7 +9534,6 @@ export {
|
|
|
9272
9534
|
formatResponse,
|
|
9273
9535
|
fragment,
|
|
9274
9536
|
fromFragment,
|
|
9275
|
-
generateChatTitle,
|
|
9276
9537
|
getFragmentData,
|
|
9277
9538
|
getLocaleFromMessage,
|
|
9278
9539
|
getModelsRegistry,
|
|
@@ -9291,6 +9552,7 @@ export {
|
|
|
9291
9552
|
isInstallable,
|
|
9292
9553
|
isMessageFragment,
|
|
9293
9554
|
isRecord,
|
|
9555
|
+
lastAssistantLength,
|
|
9294
9556
|
loadSkillMetadata,
|
|
9295
9557
|
localeReminder,
|
|
9296
9558
|
mapGenerateErrorToCode,
|
|
@@ -9335,7 +9597,6 @@ export {
|
|
|
9335
9597
|
skillsReminder,
|
|
9336
9598
|
socraticPrompting,
|
|
9337
9599
|
soul,
|
|
9338
|
-
staticChatTitle,
|
|
9339
9600
|
stop,
|
|
9340
9601
|
stripQuoteArtifacts,
|
|
9341
9602
|
stripReminders,
|
|
@@ -9347,13 +9608,19 @@ export {
|
|
|
9347
9608
|
timeReminder,
|
|
9348
9609
|
toFragment,
|
|
9349
9610
|
toMessageFragment,
|
|
9611
|
+
toolCall,
|
|
9612
|
+
toolCallCount,
|
|
9613
|
+
toolCalled,
|
|
9614
|
+
toolFailed,
|
|
9350
9615
|
uploadSkills,
|
|
9616
|
+
usageExceeds,
|
|
9351
9617
|
useAgentOsSandbox,
|
|
9352
9618
|
useBashMeta,
|
|
9353
9619
|
useSandbox,
|
|
9354
9620
|
user,
|
|
9355
9621
|
visualizeGraph,
|
|
9356
9622
|
weekChanged,
|
|
9623
|
+
withinLastN,
|
|
9357
9624
|
workflow,
|
|
9358
9625
|
yearChanged,
|
|
9359
9626
|
yearReminder
|