@deepagents/context 0.37.1 → 0.38.1
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 +1 -1
- package/dist/browser.js +8 -1
- package/dist/browser.js.map +2 -2
- package/dist/index.js +298 -156
- package/dist/index.js.map +3 -3
- 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 +4 -2
- package/dist/lib/fragments/message/user.d.ts.map +1 -1
- package/dist/lib/fragments/reminders/temporal/predicates.d.ts +1 -0
- package/dist/lib/fragments/reminders/temporal/predicates.d.ts.map +1 -1
- package/dist/lib/fragments/reminders/temporal/reminder.d.ts +1 -5
- package/dist/lib/fragments/reminders/temporal/reminder.d.ts.map +1 -1
- package/dist/lib/fragments.d.ts +2 -0
- package/dist/lib/fragments.d.ts.map +1 -1
- package/dist/lib/sandbox/routing-sandbox.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 +8 -7
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(),
|
|
@@ -1313,7 +1319,7 @@ function reminder(textOrFragment, options) {
|
|
|
1313
1319
|
if (typeof text === "string") {
|
|
1314
1320
|
assertReminderText(text);
|
|
1315
1321
|
}
|
|
1316
|
-
const asPart = options?.asPart ??
|
|
1322
|
+
const asPart = options?.asPart ?? false;
|
|
1317
1323
|
if (options && "when" in options && options.when) {
|
|
1318
1324
|
return {
|
|
1319
1325
|
name: "reminder",
|
|
@@ -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 = [];
|
|
@@ -2766,6 +2777,120 @@ var ContextEngine = class _ContextEngine {
|
|
|
2766
2777
|
const { turn } = await this.#getChainContext();
|
|
2767
2778
|
return turn;
|
|
2768
2779
|
}
|
|
2780
|
+
async firstUserMessage() {
|
|
2781
|
+
await this.#ensureInitialized();
|
|
2782
|
+
if (this.#branch?.headMessageId) {
|
|
2783
|
+
const chain = await this.#store.getMessageChain(
|
|
2784
|
+
this.#branch.headMessageId
|
|
2785
|
+
);
|
|
2786
|
+
for (const msg of chain) {
|
|
2787
|
+
if (msg.name === "user") return msg.data;
|
|
2788
|
+
}
|
|
2789
|
+
}
|
|
2790
|
+
for (const fragment2 of this.#pendingMessages) {
|
|
2791
|
+
if (fragment2.name !== "user") continue;
|
|
2792
|
+
if (!fragment2.codec) {
|
|
2793
|
+
throw new Error(`Fragment "${fragment2.name}" is missing codec.`);
|
|
2794
|
+
}
|
|
2795
|
+
return fragment2.codec.encode();
|
|
2796
|
+
}
|
|
2797
|
+
return void 0;
|
|
2798
|
+
}
|
|
2799
|
+
/**
|
|
2800
|
+
* Return the head of the conversation — pending tail or persisted branch head.
|
|
2801
|
+
*
|
|
2802
|
+
* Includes empty assistant placeholders (use this for id-lookup, not for
|
|
2803
|
+
* building model prompts — see `getMessages()` for prompt-ready output).
|
|
2804
|
+
*
|
|
2805
|
+
* @throws if the pending tail is missing an id (programming error).
|
|
2806
|
+
*/
|
|
2807
|
+
async headMessage() {
|
|
2808
|
+
await this.#ensureInitialized();
|
|
2809
|
+
if (this.#pendingMessages.length > 0) {
|
|
2810
|
+
const tail = this.#pendingMessages[this.#pendingMessages.length - 1];
|
|
2811
|
+
if (!tail.id) {
|
|
2812
|
+
throw new Error(
|
|
2813
|
+
`headMessage: pending fragment "${tail.name}" is missing id`
|
|
2814
|
+
);
|
|
2815
|
+
}
|
|
2816
|
+
return { id: tail.id, name: tail.name };
|
|
2817
|
+
}
|
|
2818
|
+
if (this.#branch?.headMessageId) {
|
|
2819
|
+
const msg = await this.#store.getMessage(this.#branch.headMessageId);
|
|
2820
|
+
if (msg) return { id: msg.id, name: msg.name };
|
|
2821
|
+
}
|
|
2822
|
+
return void 0;
|
|
2823
|
+
}
|
|
2824
|
+
/**
|
|
2825
|
+
* Return the model-ready conversation: persisted chain plus pending fragments,
|
|
2826
|
+
* with empty assistant placeholders filtered out.
|
|
2827
|
+
*
|
|
2828
|
+
* For id-lookup use `headMessage()` instead — that one keeps placeholders.
|
|
2829
|
+
*/
|
|
2830
|
+
async getMessages() {
|
|
2831
|
+
await this.#ensureInitialized();
|
|
2832
|
+
const messages = [];
|
|
2833
|
+
if (this.#branch?.headMessageId) {
|
|
2834
|
+
const chain = await this.#store.getMessageChain(
|
|
2835
|
+
this.#branch.headMessageId
|
|
2836
|
+
);
|
|
2837
|
+
for (const msg of chain) {
|
|
2838
|
+
const data = msg.data;
|
|
2839
|
+
if (isEmptyAssistantPlaceholder(data)) continue;
|
|
2840
|
+
messages.push(data);
|
|
2841
|
+
}
|
|
2842
|
+
}
|
|
2843
|
+
for (const fragment2 of this.#pendingMessages) {
|
|
2844
|
+
if (!fragment2.codec) {
|
|
2845
|
+
throw new Error(`Fragment "${fragment2.name}" is missing codec.`);
|
|
2846
|
+
}
|
|
2847
|
+
const encoded = fragment2.codec.encode();
|
|
2848
|
+
if (isEmptyAssistantPlaceholder(encoded)) continue;
|
|
2849
|
+
messages.push(encoded);
|
|
2850
|
+
}
|
|
2851
|
+
return messages.length === 0 ? [] : validateUIMessages({ messages });
|
|
2852
|
+
}
|
|
2853
|
+
/**
|
|
2854
|
+
* Advance the conversation by one turn. Required setup before `chat()`.
|
|
2855
|
+
*
|
|
2856
|
+
* - User input → persists the message AND appends an empty assistant
|
|
2857
|
+
* placeholder reserving the id of the next streamed response.
|
|
2858
|
+
* - Assistant input (tool-resume / continuation) → persists in-place
|
|
2859
|
+
* (`branch: false`), reusing the input's id.
|
|
2860
|
+
*
|
|
2861
|
+
* Always leaves the chain head as an assistant fragment, satisfying chat()'s
|
|
2862
|
+
* precondition.
|
|
2863
|
+
*
|
|
2864
|
+
* @returns the assistant id that will receive the streamed response — useful
|
|
2865
|
+
* for telemetry, optimistic UI, or correlating logs before the stream starts.
|
|
2866
|
+
* @throws if assistant input is missing an id.
|
|
2867
|
+
*
|
|
2868
|
+
* @example
|
|
2869
|
+
* ```ts
|
|
2870
|
+
* const assistantId = await context.continue(user('hi'));
|
|
2871
|
+
* const stream = await chat(agent); // streams into assistantId
|
|
2872
|
+
* ```
|
|
2873
|
+
*/
|
|
2874
|
+
async continue(input) {
|
|
2875
|
+
const fragment2 = toMessageFragment(input);
|
|
2876
|
+
const isAssistantUpdate = fragment2.name === "assistant";
|
|
2877
|
+
let assistantId;
|
|
2878
|
+
if (isAssistantUpdate) {
|
|
2879
|
+
if (!fragment2.id) {
|
|
2880
|
+
throw new Error("continue: assistant input is missing id");
|
|
2881
|
+
}
|
|
2882
|
+
assistantId = fragment2.id;
|
|
2883
|
+
this.set(fragment2);
|
|
2884
|
+
} else {
|
|
2885
|
+
assistantId = generateId4();
|
|
2886
|
+
this.set(
|
|
2887
|
+
fragment2,
|
|
2888
|
+
assistant({ id: assistantId, role: "assistant", parts: [] })
|
|
2889
|
+
);
|
|
2890
|
+
}
|
|
2891
|
+
await this.save({ branch: !isAssistantUpdate });
|
|
2892
|
+
return assistantId;
|
|
2893
|
+
}
|
|
2769
2894
|
/**
|
|
2770
2895
|
* Add fragments to the context.
|
|
2771
2896
|
*
|
|
@@ -2812,25 +2937,8 @@ var ContextEngine = class _ContextEngine {
|
|
|
2812
2937
|
async resolve(options) {
|
|
2813
2938
|
await this.#ensureInitialized();
|
|
2814
2939
|
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
|
-
};
|
|
2940
|
+
const messages = await this.getMessages();
|
|
2941
|
+
return { systemPrompt, messages };
|
|
2834
2942
|
}
|
|
2835
2943
|
/**
|
|
2836
2944
|
* Save pending messages to the graph.
|
|
@@ -2893,6 +3001,7 @@ var ContextEngine = class _ContextEngine {
|
|
|
2893
3001
|
content: plainText,
|
|
2894
3002
|
lastMessageAt,
|
|
2895
3003
|
lastMessage,
|
|
3004
|
+
currentMessage: original,
|
|
2896
3005
|
chat: this.#chatData,
|
|
2897
3006
|
usage,
|
|
2898
3007
|
branch: this.#branchName,
|
|
@@ -3745,55 +3854,75 @@ Examples:
|
|
|
3745
3854
|
- "hi" -> New Conversation
|
|
3746
3855
|
- "debug my python code" -> Python Debugging`;
|
|
3747
3856
|
var titleSchema = z2.object({ title: z2.string() });
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
}
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
}
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3857
|
+
var TitleGenerator = class {
|
|
3858
|
+
#context;
|
|
3859
|
+
constructor(options) {
|
|
3860
|
+
this.#context = options.context;
|
|
3861
|
+
}
|
|
3862
|
+
async ensure(options) {
|
|
3863
|
+
const msg = await this.#firstUntitledUser();
|
|
3864
|
+
if (!msg) return null;
|
|
3865
|
+
try {
|
|
3866
|
+
const title = await this.#generateTitle(msg, options);
|
|
3867
|
+
return this.#applyTitle(title, "llm");
|
|
3868
|
+
} catch (error) {
|
|
3869
|
+
console.warn(
|
|
3870
|
+
"TitleGenerator: LLM title generation failed, falling back to static.",
|
|
3871
|
+
error
|
|
3872
|
+
);
|
|
3873
|
+
return this.#applyTitle(this.#staticTitle(msg), "static");
|
|
3874
|
+
}
|
|
3875
|
+
}
|
|
3876
|
+
async ensureStatic() {
|
|
3877
|
+
const msg = await this.#firstUntitledUser();
|
|
3878
|
+
if (!msg) return null;
|
|
3879
|
+
return this.#applyTitle(this.#staticTitle(msg), "static");
|
|
3880
|
+
}
|
|
3881
|
+
#staticTitle(message2) {
|
|
3882
|
+
return this.#truncateTitle(this.#extractText(message2));
|
|
3883
|
+
}
|
|
3884
|
+
async #generateTitle(message2, options) {
|
|
3885
|
+
const text = this.#extractText(message2);
|
|
3886
|
+
if (!text) {
|
|
3887
|
+
throw new Error(
|
|
3888
|
+
"Cannot generate chat title: message has no text content."
|
|
3889
|
+
);
|
|
3890
|
+
}
|
|
3891
|
+
const store2 = new InMemoryContextStore();
|
|
3892
|
+
const context = new ContextEngine({
|
|
3893
|
+
store: store2,
|
|
3894
|
+
chatId: crypto.randomUUID(),
|
|
3895
|
+
userId: "system"
|
|
3896
|
+
});
|
|
3897
|
+
context.set(role(TITLE_PROMPT), user(text));
|
|
3772
3898
|
const { title } = await structuredOutput({
|
|
3773
3899
|
context,
|
|
3774
3900
|
model: options.model,
|
|
3775
3901
|
schema: titleSchema
|
|
3776
3902
|
}).generate({}, { abortSignal: options.abortSignal });
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
return
|
|
3903
|
+
if (!title) {
|
|
3904
|
+
throw new Error("Title generation returned an empty string.");
|
|
3905
|
+
}
|
|
3906
|
+
return title;
|
|
3781
3907
|
}
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
// packages/context/src/lib/chat.ts
|
|
3785
|
-
function toMessageFragment(item) {
|
|
3786
|
-
if (isFragment(item) && isMessageFragment(item)) {
|
|
3787
|
-
return item;
|
|
3908
|
+
#extractText(message2) {
|
|
3909
|
+
return extractPlainText(stripReminders(message2));
|
|
3788
3910
|
}
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
if (isFragment(item) && isMessageFragment(item)) {
|
|
3793
|
-
return item.codec.encode();
|
|
3911
|
+
#truncateTitle(text) {
|
|
3912
|
+
if (!text) return "New Chat";
|
|
3913
|
+
return text.length > 100 ? text.slice(0, 100) + "..." : text;
|
|
3794
3914
|
}
|
|
3795
|
-
|
|
3796
|
-
}
|
|
3915
|
+
async #applyTitle(title, source) {
|
|
3916
|
+
await this.#context.updateChat({ title });
|
|
3917
|
+
return { title, source };
|
|
3918
|
+
}
|
|
3919
|
+
async #firstUntitledUser() {
|
|
3920
|
+
if (this.#context.chat?.title) return void 0;
|
|
3921
|
+
return this.#context.firstUserMessage();
|
|
3922
|
+
}
|
|
3923
|
+
};
|
|
3924
|
+
|
|
3925
|
+
// packages/context/src/lib/chat.ts
|
|
3797
3926
|
var defaultChatMessageMetadata = ({
|
|
3798
3927
|
part
|
|
3799
3928
|
}) => {
|
|
@@ -3805,7 +3934,7 @@ var defaultChatMessageMetadata = ({
|
|
|
3805
3934
|
}
|
|
3806
3935
|
return void 0;
|
|
3807
3936
|
};
|
|
3808
|
-
async function chat(agent2,
|
|
3937
|
+
async function chat(agent2, options = {}) {
|
|
3809
3938
|
const context = agent2.context;
|
|
3810
3939
|
const sandbox = agent2.sandbox;
|
|
3811
3940
|
if (!context) {
|
|
@@ -3813,53 +3942,36 @@ async function chat(agent2, messages, options) {
|
|
|
3813
3942
|
"Agent is missing a context. Provide context when creating the agent."
|
|
3814
3943
|
);
|
|
3815
3944
|
}
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
const lastUIMessage = chatMessageToUIMessage(lastItem);
|
|
3822
|
-
let assistantMsgId;
|
|
3823
|
-
if (lastUIMessage.role === "assistant") {
|
|
3824
|
-
context.set(lastFragment);
|
|
3825
|
-
await context.save({ branch: false });
|
|
3826
|
-
assistantMsgId = lastUIMessage.id;
|
|
3827
|
-
} else {
|
|
3828
|
-
context.set(lastFragment);
|
|
3829
|
-
await context.save();
|
|
3830
|
-
assistantMsgId = generateId4();
|
|
3831
|
-
}
|
|
3832
|
-
const uiMessages = messages.map(chatMessageToUIMessage);
|
|
3833
|
-
let title = null;
|
|
3834
|
-
if (!context.chat?.title) {
|
|
3835
|
-
const firstUserMsg = uiMessages.find((m) => m.role === "user");
|
|
3836
|
-
if (firstUserMsg) {
|
|
3837
|
-
if (options?.generateTitle && agent2.model) {
|
|
3838
|
-
title = await generateChatTitle({
|
|
3839
|
-
message: firstUserMsg,
|
|
3840
|
-
model: agent2.model,
|
|
3841
|
-
abortSignal: options?.abortSignal
|
|
3842
|
-
});
|
|
3843
|
-
} else {
|
|
3844
|
-
title = staticChatTitle(firstUserMsg);
|
|
3845
|
-
}
|
|
3846
|
-
await context.updateChat({ title });
|
|
3847
|
-
}
|
|
3945
|
+
const head = await context.headMessage();
|
|
3946
|
+
if (head?.name !== "assistant") {
|
|
3947
|
+
throw new Error(
|
|
3948
|
+
"chat: expected an assistant message at head. Call context.continue(input) before chat()."
|
|
3949
|
+
);
|
|
3848
3950
|
}
|
|
3849
|
-
const
|
|
3850
|
-
const
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3951
|
+
const assistantMsgId = head.id;
|
|
3952
|
+
const uiMessages = await context.getMessages();
|
|
3953
|
+
const streamContextVariables = options.contextVariables === void 0 ? {} : options.contextVariables;
|
|
3954
|
+
const [title, result] = await Promise.all([
|
|
3955
|
+
makeTitle({
|
|
3956
|
+
context,
|
|
3957
|
+
model: agent2.model,
|
|
3958
|
+
generateTitle: options.generateTitle,
|
|
3959
|
+
abortSignal: options.abortSignal
|
|
3960
|
+
}),
|
|
3961
|
+
agent2.stream(streamContextVariables, {
|
|
3962
|
+
transform: options.transform,
|
|
3963
|
+
abortSignal: options.abortSignal
|
|
3964
|
+
})
|
|
3965
|
+
]);
|
|
3854
3966
|
const uiStream = result.toUIMessageStream({
|
|
3855
|
-
onError: options
|
|
3967
|
+
onError: options.onError ?? formatChatError,
|
|
3856
3968
|
sendStart: true,
|
|
3857
3969
|
sendFinish: true,
|
|
3858
3970
|
sendReasoning: true,
|
|
3859
3971
|
sendSources: true,
|
|
3860
3972
|
originalMessages: uiMessages,
|
|
3861
3973
|
generateMessageId: () => assistantMsgId,
|
|
3862
|
-
messageMetadata: options
|
|
3974
|
+
messageMetadata: options.messageMetadata ?? defaultChatMessageMetadata
|
|
3863
3975
|
});
|
|
3864
3976
|
return createUIMessageStream2({
|
|
3865
3977
|
originalMessages: uiMessages,
|
|
@@ -3882,7 +3994,7 @@ async function chat(agent2, messages, options) {
|
|
|
3882
3994
|
}
|
|
3883
3995
|
const drained = sandbox.drainFileEvents?.() ?? [];
|
|
3884
3996
|
const fileEvents = isAborted ? [] : drained;
|
|
3885
|
-
const finalMetadata = await options
|
|
3997
|
+
const finalMetadata = await options.finalAssistantMetadata?.(normalizedMessage);
|
|
3886
3998
|
const mergedMetadata = {
|
|
3887
3999
|
...normalizedMessage.metadata ?? {},
|
|
3888
4000
|
...fileEvents.length > 0 ? { fileEvents } : {},
|
|
@@ -3919,9 +4031,7 @@ function sanitizeAbortedParts(parts) {
|
|
|
3919
4031
|
sanitized.push(part);
|
|
3920
4032
|
continue;
|
|
3921
4033
|
}
|
|
3922
|
-
if (part.state === "input-streaming")
|
|
3923
|
-
continue;
|
|
3924
|
-
}
|
|
4034
|
+
if (part.state === "input-streaming") continue;
|
|
3925
4035
|
sanitized.push({
|
|
3926
4036
|
...part,
|
|
3927
4037
|
state: "output-error",
|
|
@@ -3946,6 +4056,19 @@ function formatChatError(error) {
|
|
|
3946
4056
|
}
|
|
3947
4057
|
return JSON.stringify(error);
|
|
3948
4058
|
}
|
|
4059
|
+
async function makeTitle(options) {
|
|
4060
|
+
const titler = new TitleGenerator({ context: options.context });
|
|
4061
|
+
if (options.generateTitle && !options.model) {
|
|
4062
|
+
console.warn(
|
|
4063
|
+
"chat: generateTitle=true but agent.model is unset; using static title."
|
|
4064
|
+
);
|
|
4065
|
+
}
|
|
4066
|
+
const result = options.generateTitle && options.model ? await titler.ensure({
|
|
4067
|
+
model: options.model,
|
|
4068
|
+
abortSignal: options.abortSignal
|
|
4069
|
+
}) : await titler.ensureStatic();
|
|
4070
|
+
return result?.title ?? null;
|
|
4071
|
+
}
|
|
3949
4072
|
|
|
3950
4073
|
// packages/context/src/lib/fragments/user.ts
|
|
3951
4074
|
function identity(input) {
|
|
@@ -4432,8 +4555,7 @@ function getLocaleFromMessage(message2) {
|
|
|
4432
4555
|
}
|
|
4433
4556
|
function resolveTz(options, ctx) {
|
|
4434
4557
|
if (options?.tz) return options.tz;
|
|
4435
|
-
|
|
4436
|
-
return locale?.timeZone ?? "UTC";
|
|
4558
|
+
return getLocaleFromMessage(ctx.currentMessage)?.timeZone ?? getLocaleFromMessage(ctx.lastMessage)?.timeZone ?? "UTC";
|
|
4437
4559
|
}
|
|
4438
4560
|
function toDateParts(date, tz) {
|
|
4439
4561
|
const parts = new Intl.DateTimeFormat("en-CA", {
|
|
@@ -4581,7 +4703,7 @@ function dateReminder(options) {
|
|
|
4581
4703
|
return `${diff}Date: ${currentDate}
|
|
4582
4704
|
Day of Week: ${currentDay}`;
|
|
4583
4705
|
},
|
|
4584
|
-
{ when: dayChanged(options), asPart:
|
|
4706
|
+
{ when: dayChanged(options), asPart: false }
|
|
4585
4707
|
);
|
|
4586
4708
|
}
|
|
4587
4709
|
function timeReminder(options) {
|
|
@@ -4599,7 +4721,7 @@ function timeReminder(options) {
|
|
|
4599
4721
|
}
|
|
4600
4722
|
return `${diff}Time: ${currentTime}`;
|
|
4601
4723
|
},
|
|
4602
|
-
{ when: hourChanged(options), asPart:
|
|
4724
|
+
{ when: hourChanged(options), asPart: false }
|
|
4603
4725
|
);
|
|
4604
4726
|
}
|
|
4605
4727
|
function monthReminder(options) {
|
|
@@ -4617,7 +4739,7 @@ function monthReminder(options) {
|
|
|
4617
4739
|
}
|
|
4618
4740
|
return `${diff}Month: ${currentMonth}`;
|
|
4619
4741
|
},
|
|
4620
|
-
{ when: monthChanged(options), asPart:
|
|
4742
|
+
{ when: monthChanged(options), asPart: false }
|
|
4621
4743
|
);
|
|
4622
4744
|
}
|
|
4623
4745
|
function yearReminder(options) {
|
|
@@ -4635,7 +4757,7 @@ function yearReminder(options) {
|
|
|
4635
4757
|
}
|
|
4636
4758
|
return `${diff}Year: ${currentYear}`;
|
|
4637
4759
|
},
|
|
4638
|
-
{ when: yearChanged(options), asPart:
|
|
4760
|
+
{ when: yearChanged(options), asPart: false }
|
|
4639
4761
|
);
|
|
4640
4762
|
}
|
|
4641
4763
|
function seasonReminder(options) {
|
|
@@ -4653,36 +4775,30 @@ function seasonReminder(options) {
|
|
|
4653
4775
|
}
|
|
4654
4776
|
return `${diff}Season: ${currentSeason}`;
|
|
4655
4777
|
},
|
|
4656
|
-
{ when: seasonChanged(options), asPart:
|
|
4778
|
+
{ when: seasonChanged(options), asPart: false }
|
|
4657
4779
|
);
|
|
4658
4780
|
}
|
|
4659
|
-
function localeReminder(
|
|
4660
|
-
const language = options?.language ?? "English (US)";
|
|
4661
|
-
const timeZone = options?.timeZone ?? "UTC";
|
|
4781
|
+
function localeReminder() {
|
|
4662
4782
|
const whenFn = (ctx) => {
|
|
4663
|
-
const
|
|
4664
|
-
if (!
|
|
4665
|
-
|
|
4783
|
+
const current = getLocaleFromMessage(ctx.currentMessage);
|
|
4784
|
+
if (!current) return false;
|
|
4785
|
+
const last = getLocaleFromMessage(ctx.lastMessage);
|
|
4786
|
+
if (!last) return true;
|
|
4787
|
+
return current.language !== last.language || current.timeZone !== last.timeZone;
|
|
4666
4788
|
};
|
|
4667
4789
|
return reminder(
|
|
4668
4790
|
(ctx) => {
|
|
4669
|
-
const
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
}
|
|
4677
|
-
|
|
4678
|
-
text: `${diff}Language: ${language}
|
|
4679
|
-
Timezone: ${timeZone}`,
|
|
4680
|
-
metadata: {
|
|
4681
|
-
[LOCALE_METADATA_KEY]: { language, timeZone }
|
|
4682
|
-
}
|
|
4683
|
-
};
|
|
4791
|
+
const current = getLocaleFromMessage(ctx.currentMessage);
|
|
4792
|
+
if (!current) return "";
|
|
4793
|
+
const last = getLocaleFromMessage(ctx.lastMessage);
|
|
4794
|
+
const diff = last ? formatDiff([
|
|
4795
|
+
diffLine("language", last.language, current.language),
|
|
4796
|
+
diffLine("timezone", last.timeZone, current.timeZone)
|
|
4797
|
+
]) : "";
|
|
4798
|
+
return `${diff}Language: ${current.language}
|
|
4799
|
+
Timezone: ${current.timeZone}`;
|
|
4684
4800
|
},
|
|
4685
|
-
{ when: whenFn, asPart:
|
|
4801
|
+
{ when: whenFn, asPart: false }
|
|
4686
4802
|
);
|
|
4687
4803
|
}
|
|
4688
4804
|
function temporalReminder(options) {
|
|
@@ -6332,11 +6448,10 @@ function createShallowRouter(backend, ext, cwd) {
|
|
|
6332
6448
|
executeCommand: async (raw) => {
|
|
6333
6449
|
const preHook = ext.onBeforeBashCall ? (await ext.onBeforeBashCall({ command: raw })).command : raw;
|
|
6334
6450
|
let transformed;
|
|
6335
|
-
let
|
|
6451
|
+
let dispatch;
|
|
6336
6452
|
try {
|
|
6337
6453
|
transformed = ext.plugins.length > 0 ? transformer.transform(preHook).script : preHook;
|
|
6338
|
-
|
|
6339
|
-
tokens = firstCmd?.type === "SimpleCommand" ? tokenizeFirstCommand(transformed) : [];
|
|
6454
|
+
dispatch = extractDispatchTarget(parse(transformed), cwd);
|
|
6340
6455
|
} catch (err) {
|
|
6341
6456
|
return {
|
|
6342
6457
|
stdout: "",
|
|
@@ -6345,12 +6460,12 @@ function createShallowRouter(backend, ext, cwd) {
|
|
|
6345
6460
|
exitCode: 2
|
|
6346
6461
|
};
|
|
6347
6462
|
}
|
|
6348
|
-
const [name, ...args] = tokens;
|
|
6463
|
+
const [name, ...args] = dispatch?.tokens ?? [];
|
|
6349
6464
|
const cmd = name ? byName.get(name) : void 0;
|
|
6350
6465
|
if (cmd) {
|
|
6351
6466
|
return cmd.handler(args, {
|
|
6352
6467
|
sandbox: backend,
|
|
6353
|
-
cwd,
|
|
6468
|
+
cwd: dispatch?.cwd ?? cwd,
|
|
6354
6469
|
env: ext.env,
|
|
6355
6470
|
stdin: ""
|
|
6356
6471
|
});
|
|
@@ -6359,20 +6474,49 @@ function createShallowRouter(backend, ext, cwd) {
|
|
|
6359
6474
|
}
|
|
6360
6475
|
};
|
|
6361
6476
|
}
|
|
6362
|
-
function
|
|
6363
|
-
|
|
6364
|
-
const
|
|
6365
|
-
if (
|
|
6366
|
-
if (
|
|
6367
|
-
|
|
6368
|
-
|
|
6369
|
-
|
|
6370
|
-
|
|
6371
|
-
|
|
6477
|
+
function extractDispatchTarget(ast, defaultCwd) {
|
|
6478
|
+
if (ast.statements.length !== 1) return null;
|
|
6479
|
+
const statement = ast.statements[0];
|
|
6480
|
+
if (statement.background) return null;
|
|
6481
|
+
if (statement.operators.length === 0 && statement.pipelines.length === 1) {
|
|
6482
|
+
const tokens = tokenizeSimplePipeline(statement.pipelines[0]);
|
|
6483
|
+
return tokens ? { tokens, cwd: defaultCwd } : null;
|
|
6484
|
+
}
|
|
6485
|
+
if (statement.operators.length === 1 && statement.operators[0] === "&&" && statement.pipelines.length === 2) {
|
|
6486
|
+
const cd = simpleCommandFromPipeline(statement.pipelines[0]);
|
|
6487
|
+
const cdCwd = readCdTarget(cd);
|
|
6488
|
+
if (!cdCwd) return null;
|
|
6489
|
+
const tokens = tokenizeSimplePipeline(statement.pipelines[1]);
|
|
6490
|
+
return tokens ? { tokens, cwd: cdCwd } : null;
|
|
6491
|
+
}
|
|
6492
|
+
return null;
|
|
6493
|
+
}
|
|
6494
|
+
function simpleCommandFromPipeline(pipeline) {
|
|
6495
|
+
if (!pipeline) return null;
|
|
6496
|
+
if (pipeline.negated || pipeline.timed || pipeline.commands.length !== 1) {
|
|
6497
|
+
return null;
|
|
6498
|
+
}
|
|
6499
|
+
const command = pipeline.commands[0];
|
|
6500
|
+
return command?.type === "SimpleCommand" ? command : null;
|
|
6501
|
+
}
|
|
6502
|
+
function readCdTarget(command) {
|
|
6503
|
+
if (!command) return null;
|
|
6504
|
+
if (command.redirections.length > 0 || command.args.length !== 1) {
|
|
6505
|
+
return null;
|
|
6506
|
+
}
|
|
6507
|
+
const name = asStaticWordText(command.name);
|
|
6508
|
+
if (name !== "cd") return null;
|
|
6509
|
+
return asStaticWordText(command.args[0]);
|
|
6510
|
+
}
|
|
6511
|
+
function tokenizeSimplePipeline(pipeline) {
|
|
6512
|
+
const command = simpleCommandFromPipeline(pipeline);
|
|
6513
|
+
if (!command || command.redirections.length > 0) return null;
|
|
6514
|
+
const name = asStaticWordText(command.name);
|
|
6515
|
+
if (!name) return null;
|
|
6372
6516
|
const args = [];
|
|
6373
|
-
for (const arg of
|
|
6517
|
+
for (const arg of command.args) {
|
|
6374
6518
|
const text = asStaticWordText(arg);
|
|
6375
|
-
if (text == null) return
|
|
6519
|
+
if (text == null) return null;
|
|
6376
6520
|
args.push(text);
|
|
6377
6521
|
}
|
|
6378
6522
|
return [name, ...args];
|
|
@@ -9189,6 +9333,7 @@ export {
|
|
|
9189
9333
|
SqliteStreamStore,
|
|
9190
9334
|
StreamManager,
|
|
9191
9335
|
StreamStore,
|
|
9336
|
+
TitleGenerator,
|
|
9192
9337
|
TomlRenderer,
|
|
9193
9338
|
ToonRenderer,
|
|
9194
9339
|
XmlRenderer,
|
|
@@ -9212,7 +9357,6 @@ export {
|
|
|
9212
9357
|
buildSubcommandRepair,
|
|
9213
9358
|
chainHooks,
|
|
9214
9359
|
chat,
|
|
9215
|
-
chatMessageToUIMessage,
|
|
9216
9360
|
clarification,
|
|
9217
9361
|
classifies,
|
|
9218
9362
|
contentIncludes,
|
|
@@ -9250,7 +9394,6 @@ export {
|
|
|
9250
9394
|
formatResponse,
|
|
9251
9395
|
fragment,
|
|
9252
9396
|
fromFragment,
|
|
9253
|
-
generateChatTitle,
|
|
9254
9397
|
getFragmentData,
|
|
9255
9398
|
getLocaleFromMessage,
|
|
9256
9399
|
getModelsRegistry,
|
|
@@ -9313,7 +9456,6 @@ export {
|
|
|
9313
9456
|
skillsReminder,
|
|
9314
9457
|
socraticPrompting,
|
|
9315
9458
|
soul,
|
|
9316
|
-
staticChatTitle,
|
|
9317
9459
|
stop,
|
|
9318
9460
|
stripQuoteArtifacts,
|
|
9319
9461
|
stripReminders,
|