@poncho-ai/harness 0.37.1 → 0.38.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/.turbo/turbo-build.log +5 -5
- package/CHANGELOG.md +32 -0
- package/dist/index.d.ts +66 -34
- package/dist/index.js +382 -59
- package/package.json +1 -1
- package/src/harness.ts +63 -13
- package/src/prompt-cache.ts +13 -4
- package/src/reminder-store.ts +183 -16
- package/src/reminder-tools.ts +102 -6
- package/src/state.ts +29 -3
- package/src/storage/engine.ts +10 -10
- package/src/storage/memory-engine.ts +25 -10
- package/src/storage/schema.ts +11 -0
- package/src/storage/sql-dialect.ts +80 -15
- package/src/storage/store-adapters.ts +11 -11
- package/src/vfs/bash-manager.ts +16 -2
- package/src/vfs/edit-file-tool.ts +9 -8
- package/src/vfs/read-file-tool.ts +14 -15
- package/src/vfs/write-file-tool.ts +6 -5
- package/test/harness.test.ts +1 -1
- package/test/reminder-store.test.ts +193 -4
- package/test/storage-engine.test.ts +25 -0
package/dist/index.js
CHANGED
|
@@ -2663,16 +2663,19 @@ var InMemoryEngine = class {
|
|
|
2663
2663
|
get: async (conversationId) => {
|
|
2664
2664
|
return this.convs.get(conversationId);
|
|
2665
2665
|
},
|
|
2666
|
-
create: async (ownerId, title, tenantId) => {
|
|
2666
|
+
create: async (ownerId, title, tenantId, init) => {
|
|
2667
2667
|
const now2 = Date.now();
|
|
2668
2668
|
const conv = {
|
|
2669
2669
|
conversationId: randomUUID3(),
|
|
2670
2670
|
title: normalizeTitle(title),
|
|
2671
|
-
messages: [],
|
|
2671
|
+
messages: init?.messages ?? [],
|
|
2672
2672
|
ownerId: ownerId ?? DEFAULT_OWNER,
|
|
2673
2673
|
tenantId: tenantId === void 0 ? null : tenantId,
|
|
2674
2674
|
createdAt: now2,
|
|
2675
|
-
updatedAt: now2
|
|
2675
|
+
updatedAt: now2,
|
|
2676
|
+
...init?.parentConversationId !== void 0 ? { parentConversationId: init.parentConversationId } : {},
|
|
2677
|
+
...init?.subagentMeta !== void 0 ? { subagentMeta: init.subagentMeta } : {},
|
|
2678
|
+
...init?.channelMeta !== void 0 ? { channelMeta: init.channelMeta } : {}
|
|
2676
2679
|
};
|
|
2677
2680
|
this.convs.set(conv.conversationId, conv);
|
|
2678
2681
|
return conv;
|
|
@@ -2776,11 +2779,21 @@ var InMemoryEngine = class {
|
|
|
2776
2779
|
createdAt: Date.now(),
|
|
2777
2780
|
conversationId: input.conversationId,
|
|
2778
2781
|
ownerId: input.ownerId,
|
|
2779
|
-
tenantId: input.tenantId
|
|
2782
|
+
tenantId: input.tenantId,
|
|
2783
|
+
recurrence: input.recurrence ?? null,
|
|
2784
|
+
occurrenceCount: 0
|
|
2780
2785
|
};
|
|
2781
2786
|
this.reminderData.set(r.id, r);
|
|
2782
2787
|
return r;
|
|
2783
2788
|
},
|
|
2789
|
+
update: async (id, fields) => {
|
|
2790
|
+
const r = this.reminderData.get(id);
|
|
2791
|
+
if (!r) throw new Error(`Reminder ${id} not found`);
|
|
2792
|
+
if (fields.scheduledAt !== void 0) r.scheduledAt = fields.scheduledAt;
|
|
2793
|
+
if (fields.occurrenceCount !== void 0) r.occurrenceCount = fields.occurrenceCount;
|
|
2794
|
+
if (fields.status !== void 0) r.status = fields.status;
|
|
2795
|
+
return r;
|
|
2796
|
+
},
|
|
2784
2797
|
cancel: async (id) => {
|
|
2785
2798
|
const r = this.reminderData.get(id);
|
|
2786
2799
|
if (!r) throw new Error(`Reminder ${id} not found`);
|
|
@@ -3207,6 +3220,17 @@ var migrations = [
|
|
|
3207
3220
|
]
|
|
3208
3221
|
];
|
|
3209
3222
|
}
|
|
3223
|
+
},
|
|
3224
|
+
{
|
|
3225
|
+
version: 5,
|
|
3226
|
+
name: "add_reminder_recurrence",
|
|
3227
|
+
up: (d) => {
|
|
3228
|
+
const jsonType = d === "sqlite" ? "TEXT" : "JSONB";
|
|
3229
|
+
return [
|
|
3230
|
+
`ALTER TABLE reminders ADD COLUMN recurrence ${jsonType}`,
|
|
3231
|
+
`ALTER TABLE reminders ADD COLUMN occurrence_count INTEGER NOT NULL DEFAULT 0`
|
|
3232
|
+
];
|
|
3233
|
+
}
|
|
3210
3234
|
}
|
|
3211
3235
|
];
|
|
3212
3236
|
|
|
@@ -3338,19 +3362,23 @@ var SqlStorageEngine = class {
|
|
|
3338
3362
|
}
|
|
3339
3363
|
return conv;
|
|
3340
3364
|
},
|
|
3341
|
-
create: async (ownerId, title, tenantId) => {
|
|
3365
|
+
create: async (ownerId, title, tenantId, init) => {
|
|
3342
3366
|
const id = randomUUID4();
|
|
3343
3367
|
const now2 = Date.now();
|
|
3344
3368
|
const conv = {
|
|
3345
3369
|
conversationId: id,
|
|
3346
3370
|
title: normalizeTitle2(title),
|
|
3347
|
-
messages: [],
|
|
3371
|
+
messages: init?.messages ?? [],
|
|
3348
3372
|
ownerId: ownerId ?? DEFAULT_OWNER2,
|
|
3349
3373
|
tenantId: tenantId === void 0 ? null : tenantId,
|
|
3350
3374
|
createdAt: now2,
|
|
3351
|
-
updatedAt: now2
|
|
3375
|
+
updatedAt: now2,
|
|
3376
|
+
...init?.parentConversationId !== void 0 ? { parentConversationId: init.parentConversationId } : {},
|
|
3377
|
+
...init?.subagentMeta !== void 0 ? { subagentMeta: init.subagentMeta } : {},
|
|
3378
|
+
...init?.channelMeta !== void 0 ? { channelMeta: init.channelMeta } : {}
|
|
3352
3379
|
};
|
|
3353
3380
|
const data = JSON.stringify(conv);
|
|
3381
|
+
const channelMetaJson = conv.channelMeta ? JSON.stringify(conv.channelMeta) : null;
|
|
3354
3382
|
await this.executor.run(
|
|
3355
3383
|
rewrite(
|
|
3356
3384
|
`INSERT INTO conversations (id, agent_id, tenant_id, owner_id, title, data, message_count, created_at, updated_at,
|
|
@@ -3365,12 +3393,12 @@ var SqlStorageEngine = class {
|
|
|
3365
3393
|
conv.ownerId,
|
|
3366
3394
|
conv.title,
|
|
3367
3395
|
data,
|
|
3368
|
-
|
|
3396
|
+
conv.messages.length,
|
|
3369
3397
|
new Date(now2).toISOString(),
|
|
3370
3398
|
new Date(now2).toISOString(),
|
|
3371
|
-
null,
|
|
3399
|
+
conv.parentConversationId ?? null,
|
|
3372
3400
|
0,
|
|
3373
|
-
|
|
3401
|
+
channelMetaJson
|
|
3374
3402
|
]
|
|
3375
3403
|
);
|
|
3376
3404
|
return conv;
|
|
@@ -3567,10 +3595,11 @@ var SqlStorageEngine = class {
|
|
|
3567
3595
|
const id = randomUUID4();
|
|
3568
3596
|
const now2 = Date.now();
|
|
3569
3597
|
const tid = normalizeTenant2(input.tenantId);
|
|
3598
|
+
const recurrenceJson = input.recurrence ? JSON.stringify(input.recurrence) : null;
|
|
3570
3599
|
await this.executor.run(
|
|
3571
3600
|
rewrite(
|
|
3572
|
-
`INSERT INTO reminders (id, agent_id, tenant_id, owner_id, conversation_id, task, status, scheduled_at, timezone, created_at)
|
|
3573
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)`,
|
|
3601
|
+
`INSERT INTO reminders (id, agent_id, tenant_id, owner_id, conversation_id, task, status, scheduled_at, timezone, created_at, recurrence, occurrence_count)
|
|
3602
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)`,
|
|
3574
3603
|
this.dialect
|
|
3575
3604
|
),
|
|
3576
3605
|
[
|
|
@@ -3583,7 +3612,9 @@ var SqlStorageEngine = class {
|
|
|
3583
3612
|
"pending",
|
|
3584
3613
|
input.scheduledAt,
|
|
3585
3614
|
input.timezone ?? null,
|
|
3586
|
-
new Date(now2).toISOString()
|
|
3615
|
+
new Date(now2).toISOString(),
|
|
3616
|
+
recurrenceJson,
|
|
3617
|
+
0
|
|
3587
3618
|
]
|
|
3588
3619
|
);
|
|
3589
3620
|
return {
|
|
@@ -3595,9 +3626,52 @@ var SqlStorageEngine = class {
|
|
|
3595
3626
|
createdAt: now2,
|
|
3596
3627
|
conversationId: input.conversationId,
|
|
3597
3628
|
ownerId: input.ownerId,
|
|
3598
|
-
tenantId: input.tenantId
|
|
3629
|
+
tenantId: input.tenantId,
|
|
3630
|
+
recurrence: input.recurrence ?? null,
|
|
3631
|
+
occurrenceCount: 0
|
|
3599
3632
|
};
|
|
3600
3633
|
},
|
|
3634
|
+
update: async (id, fields) => {
|
|
3635
|
+
const setClauses = [];
|
|
3636
|
+
const params = [];
|
|
3637
|
+
let idx = 1;
|
|
3638
|
+
if (fields.scheduledAt !== void 0) {
|
|
3639
|
+
setClauses.push(`scheduled_at = $${idx++}`);
|
|
3640
|
+
params.push(fields.scheduledAt);
|
|
3641
|
+
}
|
|
3642
|
+
if (fields.occurrenceCount !== void 0) {
|
|
3643
|
+
setClauses.push(`occurrence_count = $${idx++}`);
|
|
3644
|
+
params.push(fields.occurrenceCount);
|
|
3645
|
+
}
|
|
3646
|
+
if (fields.status !== void 0) {
|
|
3647
|
+
setClauses.push(`status = $${idx++}`);
|
|
3648
|
+
params.push(fields.status);
|
|
3649
|
+
}
|
|
3650
|
+
if (setClauses.length === 0) {
|
|
3651
|
+
const row2 = await this.executor.get(
|
|
3652
|
+
rewrite("SELECT * FROM reminders WHERE id = $1 AND agent_id = $2", this.dialect),
|
|
3653
|
+
[id, this.agentId]
|
|
3654
|
+
);
|
|
3655
|
+
if (!row2) throw new Error(`Reminder ${id} not found`);
|
|
3656
|
+
return this.rowToReminder(row2);
|
|
3657
|
+
}
|
|
3658
|
+
params.push(id, this.agentId);
|
|
3659
|
+
const idIdx = idx++;
|
|
3660
|
+
const agentIdx = idx++;
|
|
3661
|
+
await this.executor.run(
|
|
3662
|
+
rewrite(
|
|
3663
|
+
`UPDATE reminders SET ${setClauses.join(", ")} WHERE id = $${idIdx} AND agent_id = $${agentIdx}`,
|
|
3664
|
+
this.dialect
|
|
3665
|
+
),
|
|
3666
|
+
params
|
|
3667
|
+
);
|
|
3668
|
+
const row = await this.executor.get(
|
|
3669
|
+
rewrite("SELECT * FROM reminders WHERE id = $1 AND agent_id = $2", this.dialect),
|
|
3670
|
+
[id, this.agentId]
|
|
3671
|
+
);
|
|
3672
|
+
if (!row) throw new Error(`Reminder ${id} not found`);
|
|
3673
|
+
return this.rowToReminder(row);
|
|
3674
|
+
},
|
|
3601
3675
|
cancel: async (id) => {
|
|
3602
3676
|
await this.executor.run(
|
|
3603
3677
|
rewrite(
|
|
@@ -3904,6 +3978,14 @@ var SqlStorageEngine = class {
|
|
|
3904
3978
|
}
|
|
3905
3979
|
rowToReminder(row) {
|
|
3906
3980
|
const tid = row.tenant_id;
|
|
3981
|
+
let recurrence = null;
|
|
3982
|
+
if (row.recurrence) {
|
|
3983
|
+
try {
|
|
3984
|
+
recurrence = typeof row.recurrence === "string" ? JSON.parse(row.recurrence) : row.recurrence;
|
|
3985
|
+
} catch {
|
|
3986
|
+
recurrence = null;
|
|
3987
|
+
}
|
|
3988
|
+
}
|
|
3907
3989
|
return {
|
|
3908
3990
|
id: row.id,
|
|
3909
3991
|
task: row.task,
|
|
@@ -3913,7 +3995,9 @@ var SqlStorageEngine = class {
|
|
|
3913
3995
|
createdAt: new Date(row.created_at).getTime(),
|
|
3914
3996
|
conversationId: row.conversation_id,
|
|
3915
3997
|
ownerId: row.owner_id ?? void 0,
|
|
3916
|
-
tenantId: tid === DEFAULT_TENANT2 ? null : tid
|
|
3998
|
+
tenantId: tid === DEFAULT_TENANT2 ? null : tid,
|
|
3999
|
+
recurrence,
|
|
4000
|
+
occurrenceCount: row.occurrence_count ?? 0
|
|
3917
4001
|
};
|
|
3918
4002
|
}
|
|
3919
4003
|
toUint8Array(value) {
|
|
@@ -4170,7 +4254,7 @@ function createConversationStoreFromEngine(engine) {
|
|
|
4170
4254
|
}),
|
|
4171
4255
|
listSummaries: (ownerId, tenantId) => engine.conversations.list(ownerId, tenantId),
|
|
4172
4256
|
get: (conversationId) => engine.conversations.get(conversationId),
|
|
4173
|
-
create: (ownerId, title, tenantId) => engine.conversations.create(ownerId, title, tenantId),
|
|
4257
|
+
create: (ownerId, title, tenantId, init) => engine.conversations.create(ownerId, title, tenantId, init),
|
|
4174
4258
|
update: (conversation) => engine.conversations.update(conversation),
|
|
4175
4259
|
rename: (conversationId, title) => engine.conversations.rename(conversationId, title),
|
|
4176
4260
|
delete: (conversationId) => engine.conversations.delete(conversationId),
|
|
@@ -4194,6 +4278,7 @@ function createReminderStoreFromEngine(engine) {
|
|
|
4194
4278
|
return {
|
|
4195
4279
|
list: () => engine.reminders.list(),
|
|
4196
4280
|
create: (input) => engine.reminders.create(input),
|
|
4281
|
+
update: (id, fields) => engine.reminders.update(id, fields),
|
|
4197
4282
|
cancel: (id) => engine.reminders.cancel(id),
|
|
4198
4283
|
delete: (id) => engine.reminders.delete(id)
|
|
4199
4284
|
};
|
|
@@ -4591,13 +4676,23 @@ var BashEnvironmentManager = class {
|
|
|
4591
4676
|
this.bashOptions = toBashOptions(bashConfig, network);
|
|
4592
4677
|
}
|
|
4593
4678
|
environments = /* @__PURE__ */ new Map();
|
|
4679
|
+
filesystems = /* @__PURE__ */ new Map();
|
|
4594
4680
|
workingDir;
|
|
4595
4681
|
bashOptions;
|
|
4682
|
+
/** Return the combined IFileSystem (VFS + optional /project mount) for a tenant. */
|
|
4683
|
+
getFs(tenantId) {
|
|
4684
|
+
let fs = this.filesystems.get(tenantId);
|
|
4685
|
+
if (!fs) {
|
|
4686
|
+
const adapter = new PonchoFsAdapter(this.engine, tenantId, this.limits);
|
|
4687
|
+
fs = createBashFs(adapter, this.workingDir);
|
|
4688
|
+
this.filesystems.set(tenantId, fs);
|
|
4689
|
+
}
|
|
4690
|
+
return fs;
|
|
4691
|
+
}
|
|
4596
4692
|
getOrCreate(tenantId) {
|
|
4597
4693
|
let bash = this.environments.get(tenantId);
|
|
4598
4694
|
if (!bash) {
|
|
4599
|
-
const
|
|
4600
|
-
const fs = createBashFs(adapter, this.workingDir);
|
|
4695
|
+
const fs = this.getFs(tenantId);
|
|
4601
4696
|
bash = new Bash({
|
|
4602
4697
|
fs,
|
|
4603
4698
|
cwd: "/",
|
|
@@ -4621,9 +4716,11 @@ var BashEnvironmentManager = class {
|
|
|
4621
4716
|
}
|
|
4622
4717
|
destroy(tenantId) {
|
|
4623
4718
|
this.environments.delete(tenantId);
|
|
4719
|
+
this.filesystems.delete(tenantId);
|
|
4624
4720
|
}
|
|
4625
4721
|
destroyAll() {
|
|
4626
4722
|
this.environments.clear();
|
|
4723
|
+
this.filesystems.clear();
|
|
4627
4724
|
}
|
|
4628
4725
|
};
|
|
4629
4726
|
|
|
@@ -4701,7 +4798,7 @@ var mimeFromPath = (path) => {
|
|
|
4701
4798
|
return MIME_MAP2[path.slice(dot).toLowerCase()];
|
|
4702
4799
|
};
|
|
4703
4800
|
var isTextMime = (mime) => mime.startsWith("text/") || mime === "application/json" || mime === "application/xml" || mime === "application/sql" || mime === "application/javascript" || mime === "application/x-sh";
|
|
4704
|
-
var createReadFileTool = (
|
|
4801
|
+
var createReadFileTool = (getFs) => defineTool3({
|
|
4705
4802
|
name: "read_file",
|
|
4706
4803
|
description: "Read a file from the virtual filesystem. Returns text content for text-based files, or sends images and PDFs directly to the model for visual analysis.",
|
|
4707
4804
|
inputSchema: {
|
|
@@ -4721,23 +4818,24 @@ var createReadFileTool = (engine) => defineTool3({
|
|
|
4721
4818
|
throw new Error("path is required");
|
|
4722
4819
|
}
|
|
4723
4820
|
const tenantId = context.tenantId ?? "__default__";
|
|
4724
|
-
const
|
|
4725
|
-
if (!
|
|
4821
|
+
const fs = getFs(tenantId);
|
|
4822
|
+
if (!await fs.exists(filePath)) {
|
|
4726
4823
|
throw new Error(`File not found: ${filePath}`);
|
|
4727
4824
|
}
|
|
4728
|
-
|
|
4825
|
+
const stat3 = await fs.stat(filePath);
|
|
4826
|
+
if (stat3.isDirectory) {
|
|
4729
4827
|
throw new Error(`${filePath} is a directory, not a file`);
|
|
4730
4828
|
}
|
|
4731
|
-
const mediaType =
|
|
4829
|
+
const mediaType = mimeFromPath(filePath) ?? "application/octet-stream";
|
|
4732
4830
|
const filename = filePath.split("/").pop() ?? filePath;
|
|
4733
4831
|
if (isTextMime(mediaType)) {
|
|
4734
|
-
const
|
|
4735
|
-
const text = Buffer.from(buf).toString("utf8");
|
|
4832
|
+
const text = await fs.readFile(filePath);
|
|
4736
4833
|
return { filename, mediaType, content: text };
|
|
4737
4834
|
}
|
|
4835
|
+
const buf = await fs.readFileBuffer(filePath);
|
|
4738
4836
|
return {
|
|
4739
4837
|
type: "file",
|
|
4740
|
-
data:
|
|
4838
|
+
data: Buffer.from(buf).toString("base64"),
|
|
4741
4839
|
mediaType,
|
|
4742
4840
|
filename
|
|
4743
4841
|
};
|
|
@@ -4746,7 +4844,7 @@ var createReadFileTool = (engine) => defineTool3({
|
|
|
4746
4844
|
|
|
4747
4845
|
// src/vfs/edit-file-tool.ts
|
|
4748
4846
|
import { defineTool as defineTool4 } from "@poncho-ai/sdk";
|
|
4749
|
-
var createEditFileTool = (
|
|
4847
|
+
var createEditFileTool = (getFs) => defineTool4({
|
|
4750
4848
|
name: "edit_file",
|
|
4751
4849
|
description: "Edit a file by replacing an exact string match with new content. The old_str must match exactly one location in the file. Use an empty new_str to delete matched content. Use read_file first to see current content before editing.",
|
|
4752
4850
|
inputSchema: {
|
|
@@ -4775,11 +4873,11 @@ var createEditFileTool = (engine) => defineTool4({
|
|
|
4775
4873
|
if (!filePath) throw new Error("path is required");
|
|
4776
4874
|
if (!oldStr) throw new Error("old_str must not be empty");
|
|
4777
4875
|
const tenantId = context.tenantId ?? "__default__";
|
|
4778
|
-
const
|
|
4779
|
-
if (!
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
const content =
|
|
4876
|
+
const fs = getFs(tenantId);
|
|
4877
|
+
if (!await fs.exists(filePath)) throw new Error(`File not found: ${filePath}`);
|
|
4878
|
+
const stat3 = await fs.stat(filePath);
|
|
4879
|
+
if (stat3.isDirectory) throw new Error(`${filePath} is a directory`);
|
|
4880
|
+
const content = await fs.readFile(filePath);
|
|
4783
4881
|
const first = content.indexOf(oldStr);
|
|
4784
4882
|
if (first === -1) {
|
|
4785
4883
|
throw new Error(
|
|
@@ -4793,14 +4891,14 @@ var createEditFileTool = (engine) => defineTool4({
|
|
|
4793
4891
|
);
|
|
4794
4892
|
}
|
|
4795
4893
|
const updated = content.slice(0, first) + newStr + content.slice(first + oldStr.length);
|
|
4796
|
-
await
|
|
4894
|
+
await fs.writeFile(filePath, updated);
|
|
4797
4895
|
return { ok: true, path: filePath };
|
|
4798
4896
|
}
|
|
4799
4897
|
});
|
|
4800
4898
|
|
|
4801
4899
|
// src/vfs/write-file-tool.ts
|
|
4802
4900
|
import { defineTool as defineTool5 } from "@poncho-ai/sdk";
|
|
4803
|
-
var createWriteFileTool = (
|
|
4901
|
+
var createWriteFileTool = (getFs) => defineTool5({
|
|
4804
4902
|
name: "write_file",
|
|
4805
4903
|
description: "Create a new file or overwrite an existing file in the virtual filesystem. Parent directories are created automatically. Prefer edit_file for targeted changes to existing files.",
|
|
4806
4904
|
inputSchema: {
|
|
@@ -4823,11 +4921,12 @@ var createWriteFileTool = (engine) => defineTool5({
|
|
|
4823
4921
|
const content = typeof input.content === "string" ? input.content : "";
|
|
4824
4922
|
if (!filePath) throw new Error("path is required");
|
|
4825
4923
|
const tenantId = context.tenantId ?? "__default__";
|
|
4924
|
+
const fs = getFs(tenantId);
|
|
4826
4925
|
const dir = filePath.slice(0, filePath.lastIndexOf("/"));
|
|
4827
4926
|
if (dir) {
|
|
4828
|
-
await
|
|
4927
|
+
await fs.mkdir(dir, { recursive: true });
|
|
4829
4928
|
}
|
|
4830
|
-
await
|
|
4929
|
+
await fs.writeFile(filePath, content);
|
|
4831
4930
|
return { ok: true, path: filePath };
|
|
4832
4931
|
}
|
|
4833
4932
|
});
|
|
@@ -5370,10 +5469,11 @@ async function resolveEnv(secretsStore, tenantId, envName) {
|
|
|
5370
5469
|
// src/reminder-tools.ts
|
|
5371
5470
|
import { defineTool as defineTool8 } from "@poncho-ai/sdk";
|
|
5372
5471
|
var VALID_STATUSES2 = ["pending", "cancelled"];
|
|
5472
|
+
var VALID_RECURRENCE_TYPES = ["daily", "weekly", "monthly", "cron"];
|
|
5373
5473
|
var createReminderTools = (store) => [
|
|
5374
5474
|
defineTool8({
|
|
5375
5475
|
name: "set_reminder",
|
|
5376
|
-
description: "Set a
|
|
5476
|
+
description: "Set a reminder that will fire at the specified date and time. Use this when the user asks to be reminded about something. The datetime must be an ISO 8601 string in the future. When the reminder fires, the task message will be delivered to the user. Supports optional recurrence for recurring reminders (daily, weekly, monthly, or cron).",
|
|
5377
5477
|
inputSchema: {
|
|
5378
5478
|
type: "object",
|
|
5379
5479
|
properties: {
|
|
@@ -5383,11 +5483,45 @@ var createReminderTools = (store) => [
|
|
|
5383
5483
|
},
|
|
5384
5484
|
datetime: {
|
|
5385
5485
|
type: "string",
|
|
5386
|
-
description: "ISO 8601 datetime for when the reminder should fire (e.g. '2026-03-23T09:00:00Z')"
|
|
5486
|
+
description: "ISO 8601 datetime for when the reminder should first fire (e.g. '2026-03-23T09:00:00Z')"
|
|
5387
5487
|
},
|
|
5388
5488
|
timezone: {
|
|
5389
5489
|
type: "string",
|
|
5390
5490
|
description: "IANA timezone for interpreting the datetime if it lacks an offset (e.g. 'America/New_York'). Defaults to UTC."
|
|
5491
|
+
},
|
|
5492
|
+
recurrence: {
|
|
5493
|
+
type: "object",
|
|
5494
|
+
description: "Optional. Set this to make the reminder repeat. Omit for a one-time reminder.",
|
|
5495
|
+
properties: {
|
|
5496
|
+
type: {
|
|
5497
|
+
type: "string",
|
|
5498
|
+
enum: VALID_RECURRENCE_TYPES,
|
|
5499
|
+
description: "How often to repeat: 'daily', 'weekly', 'monthly', or 'cron'."
|
|
5500
|
+
},
|
|
5501
|
+
interval: {
|
|
5502
|
+
type: "number",
|
|
5503
|
+
description: "Repeat every N units (e.g. 2 = every 2 days/weeks/months). Defaults to 1."
|
|
5504
|
+
},
|
|
5505
|
+
daysOfWeek: {
|
|
5506
|
+
type: "array",
|
|
5507
|
+
items: { type: "number" },
|
|
5508
|
+
description: "For weekly: which days to fire (0=Sunday, 1=Monday, ..., 6=Saturday)."
|
|
5509
|
+
},
|
|
5510
|
+
expression: {
|
|
5511
|
+
type: "string",
|
|
5512
|
+
description: "For type 'cron': a 5-field cron expression (e.g. '0 9 * * 1-5' for weekdays at 9am)."
|
|
5513
|
+
},
|
|
5514
|
+
endsAt: {
|
|
5515
|
+
type: "string",
|
|
5516
|
+
description: "ISO 8601 datetime after which the recurrence should stop."
|
|
5517
|
+
},
|
|
5518
|
+
maxOccurrences: {
|
|
5519
|
+
type: "number",
|
|
5520
|
+
description: "Maximum number of times the reminder should fire before stopping."
|
|
5521
|
+
}
|
|
5522
|
+
},
|
|
5523
|
+
required: ["type"],
|
|
5524
|
+
additionalProperties: false
|
|
5391
5525
|
}
|
|
5392
5526
|
},
|
|
5393
5527
|
required: ["task", "datetime"],
|
|
@@ -5434,13 +5568,56 @@ var createReminderTools = (store) => [
|
|
|
5434
5568
|
if (scheduledAt <= Date.now()) {
|
|
5435
5569
|
throw new Error("Reminder datetime must be in the future");
|
|
5436
5570
|
}
|
|
5571
|
+
let recurrence = null;
|
|
5572
|
+
if (input.recurrence && typeof input.recurrence === "object") {
|
|
5573
|
+
const rec = input.recurrence;
|
|
5574
|
+
const recType = rec.type;
|
|
5575
|
+
if (!VALID_RECURRENCE_TYPES.includes(recType)) {
|
|
5576
|
+
throw new Error(`Invalid recurrence type: "${recType}". Must be one of: ${VALID_RECURRENCE_TYPES.join(", ")}`);
|
|
5577
|
+
}
|
|
5578
|
+
recurrence = { type: recType };
|
|
5579
|
+
if (rec.interval !== void 0) {
|
|
5580
|
+
const interval = Number(rec.interval);
|
|
5581
|
+
if (!Number.isInteger(interval) || interval < 1) {
|
|
5582
|
+
throw new Error("recurrence.interval must be a positive integer");
|
|
5583
|
+
}
|
|
5584
|
+
recurrence.interval = interval;
|
|
5585
|
+
}
|
|
5586
|
+
if (rec.daysOfWeek !== void 0) {
|
|
5587
|
+
if (!Array.isArray(rec.daysOfWeek)) throw new Error("recurrence.daysOfWeek must be an array");
|
|
5588
|
+
const days = rec.daysOfWeek.map(Number);
|
|
5589
|
+
if (days.some((d) => !Number.isInteger(d) || d < 0 || d > 6)) {
|
|
5590
|
+
throw new Error("recurrence.daysOfWeek values must be integers 0-6");
|
|
5591
|
+
}
|
|
5592
|
+
recurrence.daysOfWeek = days;
|
|
5593
|
+
}
|
|
5594
|
+
if (rec.expression !== void 0) {
|
|
5595
|
+
if (typeof rec.expression !== "string") throw new Error("recurrence.expression must be a string");
|
|
5596
|
+
recurrence.expression = rec.expression;
|
|
5597
|
+
}
|
|
5598
|
+
if (rec.endsAt !== void 0) {
|
|
5599
|
+
const endsAtDate = new Date(rec.endsAt);
|
|
5600
|
+
if (isNaN(endsAtDate.getTime())) {
|
|
5601
|
+
throw new Error(`Invalid recurrence.endsAt: "${rec.endsAt}"`);
|
|
5602
|
+
}
|
|
5603
|
+
recurrence.endsAt = endsAtDate.getTime();
|
|
5604
|
+
}
|
|
5605
|
+
if (rec.maxOccurrences !== void 0) {
|
|
5606
|
+
const max = Number(rec.maxOccurrences);
|
|
5607
|
+
if (!Number.isInteger(max) || max < 1) {
|
|
5608
|
+
throw new Error("recurrence.maxOccurrences must be a positive integer");
|
|
5609
|
+
}
|
|
5610
|
+
recurrence.maxOccurrences = max;
|
|
5611
|
+
}
|
|
5612
|
+
}
|
|
5437
5613
|
const conversationId = context.conversationId || context.runId;
|
|
5438
5614
|
const reminder = await store.create({
|
|
5439
5615
|
task,
|
|
5440
5616
|
scheduledAt,
|
|
5441
5617
|
timezone,
|
|
5442
5618
|
conversationId,
|
|
5443
|
-
tenantId: context.tenantId
|
|
5619
|
+
tenantId: context.tenantId,
|
|
5620
|
+
recurrence
|
|
5444
5621
|
});
|
|
5445
5622
|
return {
|
|
5446
5623
|
ok: true,
|
|
@@ -5449,14 +5626,16 @@ var createReminderTools = (store) => [
|
|
|
5449
5626
|
task: reminder.task,
|
|
5450
5627
|
scheduledAt: new Date(reminder.scheduledAt).toISOString(),
|
|
5451
5628
|
timezone: reminder.timezone ?? "UTC",
|
|
5452
|
-
status: reminder.status
|
|
5629
|
+
status: reminder.status,
|
|
5630
|
+
recurrence: reminder.recurrence ?? void 0,
|
|
5631
|
+
occurrenceCount: reminder.occurrenceCount ?? 0
|
|
5453
5632
|
}
|
|
5454
5633
|
};
|
|
5455
5634
|
}
|
|
5456
5635
|
}),
|
|
5457
5636
|
defineTool8({
|
|
5458
5637
|
name: "list_reminders",
|
|
5459
|
-
description: "List reminders for this agent. Returns all reminders by default; use the status filter to show only pending or cancelled ones. Fired reminders are automatically deleted after delivery.",
|
|
5638
|
+
description: "List reminders for this agent. Returns all reminders by default; use the status filter to show only pending or cancelled ones. Fired one-time reminders are automatically deleted after delivery. Recurring reminders stay active and show their recurrence config and fire count.",
|
|
5460
5639
|
inputSchema: {
|
|
5461
5640
|
type: "object",
|
|
5462
5641
|
properties: {
|
|
@@ -5484,7 +5663,9 @@ var createReminderTools = (store) => [
|
|
|
5484
5663
|
scheduledAt: new Date(r.scheduledAt).toISOString(),
|
|
5485
5664
|
timezone: r.timezone ?? "UTC",
|
|
5486
5665
|
status: r.status,
|
|
5487
|
-
createdAt: new Date(r.createdAt).toISOString()
|
|
5666
|
+
createdAt: new Date(r.createdAt).toISOString(),
|
|
5667
|
+
recurrence: r.recurrence ?? void 0,
|
|
5668
|
+
occurrenceCount: r.occurrenceCount ?? 0
|
|
5488
5669
|
})),
|
|
5489
5670
|
count: reminders.length
|
|
5490
5671
|
};
|
|
@@ -5492,7 +5673,7 @@ var createReminderTools = (store) => [
|
|
|
5492
5673
|
}),
|
|
5493
5674
|
defineTool8({
|
|
5494
5675
|
name: "cancel_reminder",
|
|
5495
|
-
description: "Cancel a pending reminder by its ID.",
|
|
5676
|
+
description: "Cancel a pending reminder by its ID. This works for both one-time and recurring reminders \u2014 cancelling a recurring reminder stops all future occurrences.",
|
|
5496
5677
|
inputSchema: {
|
|
5497
5678
|
type: "object",
|
|
5498
5679
|
properties: {
|
|
@@ -6659,15 +6840,19 @@ function isAnthropicModel(model) {
|
|
|
6659
6840
|
}
|
|
6660
6841
|
return model.provider === "anthropic" || model.provider.includes("anthropic") || model.modelId.includes("anthropic") || model.modelId.includes("claude");
|
|
6661
6842
|
}
|
|
6662
|
-
function addPromptCacheBreakpoints(messages, model) {
|
|
6843
|
+
function addPromptCacheBreakpoints(messages, model, targetIndex) {
|
|
6663
6844
|
if (messages.length === 0 || !isAnthropicModel(model)) {
|
|
6664
6845
|
return messages;
|
|
6665
6846
|
}
|
|
6847
|
+
const index = targetIndex ?? messages.length - 1;
|
|
6848
|
+
if (index < 0 || index >= messages.length) {
|
|
6849
|
+
return messages;
|
|
6850
|
+
}
|
|
6666
6851
|
const cacheDirective = {
|
|
6667
6852
|
anthropic: { cacheControl: { type: "ephemeral" } }
|
|
6668
6853
|
};
|
|
6669
|
-
return messages.map((message,
|
|
6670
|
-
if (
|
|
6854
|
+
return messages.map((message, i) => {
|
|
6855
|
+
if (i === index) {
|
|
6671
6856
|
return {
|
|
6672
6857
|
...message,
|
|
6673
6858
|
providerOptions: {
|
|
@@ -7800,6 +7985,25 @@ var hasUntruncatedToolResults = (messages) => {
|
|
|
7800
7985
|
}
|
|
7801
7986
|
return false;
|
|
7802
7987
|
};
|
|
7988
|
+
var findLastStableCacheIndex = (messages) => {
|
|
7989
|
+
for (let i = 0; i < messages.length; i += 1) {
|
|
7990
|
+
const msg = messages[i];
|
|
7991
|
+
if (msg.role !== "tool") continue;
|
|
7992
|
+
if (!Array.isArray(msg.content)) continue;
|
|
7993
|
+
for (const part of msg.content) {
|
|
7994
|
+
if (!part || typeof part !== "object") continue;
|
|
7995
|
+
const p = part;
|
|
7996
|
+
if (p.type !== "tool-result" || !p.output) continue;
|
|
7997
|
+
if (p.output.type === "json") return i - 1;
|
|
7998
|
+
if (p.output.type === "text" && typeof p.output.value === "string") {
|
|
7999
|
+
if (!p.output.value.startsWith(TOOL_RESULT_TRUNCATED_PREFIX)) {
|
|
8000
|
+
return i - 1;
|
|
8001
|
+
}
|
|
8002
|
+
}
|
|
8003
|
+
}
|
|
8004
|
+
}
|
|
8005
|
+
return messages.length - 1;
|
|
8006
|
+
};
|
|
7803
8007
|
var DEVELOPMENT_MODE_CONTEXT = `## Development Mode Context
|
|
7804
8008
|
|
|
7805
8009
|
You are running locally in development mode. Treat this as an editable agent workspace.
|
|
@@ -8771,9 +8975,10 @@ var AgentHarness = class _AgentHarness {
|
|
|
8771
8975
|
config?.network
|
|
8772
8976
|
);
|
|
8773
8977
|
this.registerIfMissing(createBashTool(this.bashManager));
|
|
8774
|
-
this.
|
|
8775
|
-
this.registerIfMissing(
|
|
8776
|
-
this.registerIfMissing(
|
|
8978
|
+
const getFs = (tenantId) => this.bashManager.getFs(tenantId);
|
|
8979
|
+
this.registerIfMissing(createReadFileTool(getFs));
|
|
8980
|
+
this.registerIfMissing(createEditFileTool(getFs));
|
|
8981
|
+
this.registerIfMissing(createWriteFileTool(getFs));
|
|
8777
8982
|
if (config?.isolate) {
|
|
8778
8983
|
const { createRunCodeTool, buildRunCodeDescription, bundleLibraries } = await import("./isolate-TCWTUVG4.js");
|
|
8779
8984
|
let libraryPreamble = null;
|
|
@@ -9072,14 +9277,13 @@ var AgentHarness = class _AgentHarness {
|
|
|
9072
9277
|
);
|
|
9073
9278
|
}
|
|
9074
9279
|
const hasFullToolResults = hasUntruncatedToolResults(messages);
|
|
9075
|
-
|
|
9076
|
-
if (!enablePromptCache) {
|
|
9280
|
+
if (hasFullToolResults) {
|
|
9077
9281
|
console.info(
|
|
9078
|
-
`[poncho][cost] Prompt cache
|
|
9282
|
+
`[poncho][cost] Prompt cache breakpoint will be placed before untruncated tool results for run "${runId}" (stable prefix only).`
|
|
9079
9283
|
);
|
|
9080
9284
|
} else {
|
|
9081
9285
|
console.info(
|
|
9082
|
-
`[poncho][cost] Prompt cache
|
|
9286
|
+
`[poncho][cost] Prompt cache breakpoint will be placed at history tail for run "${runId}" (no untruncated tool results).`
|
|
9083
9287
|
);
|
|
9084
9288
|
}
|
|
9085
9289
|
const inputMessageCount = messages.length;
|
|
@@ -9174,9 +9378,14 @@ Code is wrapped in an async IIFE \u2014 use \`return\` to return a value to the
|
|
|
9174
9378
|
const promptWithSkills = this.skillContextWindow ? `${agentPrompt}${developmentContext}
|
|
9175
9379
|
|
|
9176
9380
|
${this.skillContextWindow}${browserContext}${fsContext}${isolateContext}` : `${agentPrompt}${developmentContext}${browserContext}${fsContext}${isolateContext}`;
|
|
9381
|
+
const hourlyTime = (() => {
|
|
9382
|
+
const d = /* @__PURE__ */ new Date();
|
|
9383
|
+
d.setUTCMinutes(0, 0, 0);
|
|
9384
|
+
return d.toISOString();
|
|
9385
|
+
})();
|
|
9177
9386
|
const timeContext = this.reminderStore ? `
|
|
9178
9387
|
|
|
9179
|
-
Current UTC time: ${
|
|
9388
|
+
Current UTC time (hour precision): ${hourlyTime}` : "";
|
|
9180
9389
|
return `${promptWithSkills}${memoryContext}${todoContext}${timeContext}`;
|
|
9181
9390
|
};
|
|
9182
9391
|
let systemPrompt = buildSystemPrompt();
|
|
@@ -9615,7 +9824,12 @@ ${textContent}` };
|
|
|
9615
9824
|
const coreMessages = cachedCoreMessages;
|
|
9616
9825
|
const temperature = agent.frontmatter.model?.temperature ?? 0.2;
|
|
9617
9826
|
const maxTokens = agent.frontmatter.model?.maxTokens;
|
|
9618
|
-
const
|
|
9827
|
+
const breakpointIndex = hasFullToolResults ? findLastStableCacheIndex(coreMessages) : coreMessages.length - 1;
|
|
9828
|
+
const cachedMessages = addPromptCacheBreakpoints(
|
|
9829
|
+
coreMessages,
|
|
9830
|
+
modelInstance,
|
|
9831
|
+
breakpointIndex
|
|
9832
|
+
);
|
|
9619
9833
|
const telemetryEnabled = this.loadedConfig?.telemetry?.enabled !== false;
|
|
9620
9834
|
const result = await streamText({
|
|
9621
9835
|
model: modelInstance,
|
|
@@ -10399,12 +10613,22 @@ var InMemoryReminderStore = class {
|
|
|
10399
10613
|
createdAt: Date.now(),
|
|
10400
10614
|
conversationId: input.conversationId,
|
|
10401
10615
|
ownerId: input.ownerId,
|
|
10402
|
-
tenantId: input.tenantId
|
|
10616
|
+
tenantId: input.tenantId,
|
|
10617
|
+
recurrence: input.recurrence ?? null,
|
|
10618
|
+
occurrenceCount: 0
|
|
10403
10619
|
};
|
|
10404
10620
|
this.reminders = pruneStale(this.reminders);
|
|
10405
10621
|
this.reminders.push(reminder);
|
|
10406
10622
|
return reminder;
|
|
10407
10623
|
}
|
|
10624
|
+
async update(id, fields) {
|
|
10625
|
+
const reminder = this.reminders.find((r) => r.id === id);
|
|
10626
|
+
if (!reminder) throw new Error(`Reminder "${id}" not found`);
|
|
10627
|
+
if (fields.scheduledAt !== void 0) reminder.scheduledAt = fields.scheduledAt;
|
|
10628
|
+
if (fields.occurrenceCount !== void 0) reminder.occurrenceCount = fields.occurrenceCount;
|
|
10629
|
+
if (fields.status !== void 0) reminder.status = fields.status;
|
|
10630
|
+
return reminder;
|
|
10631
|
+
}
|
|
10408
10632
|
async cancel(id) {
|
|
10409
10633
|
const reminder = this.reminders.find((r) => r.id === id);
|
|
10410
10634
|
if (!reminder) throw new Error(`Reminder "${id}" not found`);
|
|
@@ -10418,6 +10642,101 @@ var InMemoryReminderStore = class {
|
|
|
10418
10642
|
this.reminders = this.reminders.filter((r) => r.id !== id);
|
|
10419
10643
|
}
|
|
10420
10644
|
};
|
|
10645
|
+
var computeNextOccurrence = (reminder) => {
|
|
10646
|
+
const rec = reminder.recurrence;
|
|
10647
|
+
if (!rec) return null;
|
|
10648
|
+
const fired = (reminder.occurrenceCount ?? 0) + 1;
|
|
10649
|
+
if (rec.maxOccurrences && fired >= rec.maxOccurrences) return null;
|
|
10650
|
+
const interval = rec.interval ?? 1;
|
|
10651
|
+
const prev = reminder.scheduledAt;
|
|
10652
|
+
let next;
|
|
10653
|
+
switch (rec.type) {
|
|
10654
|
+
case "daily": {
|
|
10655
|
+
next = prev + interval * 24 * 60 * 60 * 1e3;
|
|
10656
|
+
break;
|
|
10657
|
+
}
|
|
10658
|
+
case "weekly": {
|
|
10659
|
+
if (rec.daysOfWeek && rec.daysOfWeek.length > 0) {
|
|
10660
|
+
const d = new Date(prev);
|
|
10661
|
+
const days = [...rec.daysOfWeek].sort((a, b) => a - b);
|
|
10662
|
+
const currentDay = d.getUTCDay();
|
|
10663
|
+
let nextDay = days.find((day) => day > currentDay);
|
|
10664
|
+
if (nextDay !== void 0) {
|
|
10665
|
+
const delta = nextDay - currentDay;
|
|
10666
|
+
next = prev + delta * 24 * 60 * 60 * 1e3;
|
|
10667
|
+
} else {
|
|
10668
|
+
const delta = 7 * interval - currentDay + days[0];
|
|
10669
|
+
next = prev + delta * 24 * 60 * 60 * 1e3;
|
|
10670
|
+
}
|
|
10671
|
+
} else {
|
|
10672
|
+
next = prev + interval * 7 * 24 * 60 * 60 * 1e3;
|
|
10673
|
+
}
|
|
10674
|
+
break;
|
|
10675
|
+
}
|
|
10676
|
+
case "monthly": {
|
|
10677
|
+
const d = new Date(prev);
|
|
10678
|
+
d.setUTCMonth(d.getUTCMonth() + interval);
|
|
10679
|
+
next = d.getTime();
|
|
10680
|
+
break;
|
|
10681
|
+
}
|
|
10682
|
+
case "cron": {
|
|
10683
|
+
if (!rec.expression) return null;
|
|
10684
|
+
const parsed = parseCronExpression(rec.expression);
|
|
10685
|
+
if (!parsed) return null;
|
|
10686
|
+
next = nextCronOccurrence(prev, parsed);
|
|
10687
|
+
if (next <= prev) return null;
|
|
10688
|
+
break;
|
|
10689
|
+
}
|
|
10690
|
+
default:
|
|
10691
|
+
return null;
|
|
10692
|
+
}
|
|
10693
|
+
if (rec.endsAt && next > rec.endsAt) return null;
|
|
10694
|
+
return next;
|
|
10695
|
+
};
|
|
10696
|
+
var expandField = (field, min, max) => {
|
|
10697
|
+
const values = /* @__PURE__ */ new Set();
|
|
10698
|
+
for (const part of field.split(",")) {
|
|
10699
|
+
const stepMatch = part.match(/^(.+)\/(\d+)$/);
|
|
10700
|
+
const step = stepMatch ? parseInt(stepMatch[2], 10) : 1;
|
|
10701
|
+
const range = stepMatch ? stepMatch[1] : part;
|
|
10702
|
+
if (range === "*") {
|
|
10703
|
+
for (let i = min; i <= max; i += step) values.add(i);
|
|
10704
|
+
} else if (range.includes("-")) {
|
|
10705
|
+
const [lo, hi] = range.split("-").map(Number);
|
|
10706
|
+
if (isNaN(lo) || isNaN(hi)) return null;
|
|
10707
|
+
for (let i = lo; i <= hi; i += step) values.add(i);
|
|
10708
|
+
} else {
|
|
10709
|
+
const n = parseInt(range, 10);
|
|
10710
|
+
if (isNaN(n)) return null;
|
|
10711
|
+
values.add(n);
|
|
10712
|
+
}
|
|
10713
|
+
}
|
|
10714
|
+
return values;
|
|
10715
|
+
};
|
|
10716
|
+
var parseCronExpression = (expr) => {
|
|
10717
|
+
const parts = expr.trim().split(/\s+/);
|
|
10718
|
+
if (parts.length !== 5) return null;
|
|
10719
|
+
const minutes = expandField(parts[0], 0, 59);
|
|
10720
|
+
const hours = expandField(parts[1], 0, 23);
|
|
10721
|
+
const daysOfMonth = expandField(parts[2], 1, 31);
|
|
10722
|
+
const months = expandField(parts[3], 1, 12);
|
|
10723
|
+
const daysOfWeek = expandField(parts[4], 0, 6);
|
|
10724
|
+
if (!minutes || !hours || !daysOfMonth || !months || !daysOfWeek) return null;
|
|
10725
|
+
return { minutes, hours, daysOfMonth, months, daysOfWeek };
|
|
10726
|
+
};
|
|
10727
|
+
var nextCronOccurrence = (afterMs, fields) => {
|
|
10728
|
+
const d = new Date(afterMs);
|
|
10729
|
+
d.setUTCSeconds(0, 0);
|
|
10730
|
+
d.setUTCMinutes(d.getUTCMinutes() + 1);
|
|
10731
|
+
const limit = afterMs + 366 * 24 * 60 * 60 * 1e3;
|
|
10732
|
+
while (d.getTime() < limit) {
|
|
10733
|
+
if (fields.months.has(d.getUTCMonth() + 1) && fields.daysOfMonth.has(d.getUTCDate()) && fields.daysOfWeek.has(d.getUTCDay()) && fields.hours.has(d.getUTCHours()) && fields.minutes.has(d.getUTCMinutes())) {
|
|
10734
|
+
return d.getTime();
|
|
10735
|
+
}
|
|
10736
|
+
d.setUTCMinutes(d.getUTCMinutes() + 1);
|
|
10737
|
+
}
|
|
10738
|
+
return afterMs;
|
|
10739
|
+
};
|
|
10421
10740
|
var createReminderStore = (_agentId, _config, _options) => {
|
|
10422
10741
|
return new InMemoryReminderStore();
|
|
10423
10742
|
};
|
|
@@ -10491,16 +10810,19 @@ var InMemoryConversationStore = class {
|
|
|
10491
10810
|
this.purgeExpired();
|
|
10492
10811
|
return this.conversations.get(conversationId);
|
|
10493
10812
|
}
|
|
10494
|
-
async create(ownerId = DEFAULT_OWNER3, title, tenantId = null) {
|
|
10813
|
+
async create(ownerId = DEFAULT_OWNER3, title, tenantId = null, init) {
|
|
10495
10814
|
const now2 = Date.now();
|
|
10496
10815
|
const conversation = {
|
|
10497
10816
|
conversationId: globalThis.crypto?.randomUUID?.() ?? `${now2}-${Math.random()}`,
|
|
10498
10817
|
title: normalizeTitle3(title),
|
|
10499
|
-
messages: [],
|
|
10818
|
+
messages: init?.messages ?? [],
|
|
10500
10819
|
ownerId,
|
|
10501
10820
|
tenantId,
|
|
10502
10821
|
createdAt: now2,
|
|
10503
|
-
updatedAt: now2
|
|
10822
|
+
updatedAt: now2,
|
|
10823
|
+
...init?.parentConversationId !== void 0 ? { parentConversationId: init.parentConversationId } : {},
|
|
10824
|
+
...init?.subagentMeta !== void 0 ? { subagentMeta: init.subagentMeta } : {},
|
|
10825
|
+
...init?.channelMeta !== void 0 ? { channelMeta: init.channelMeta } : {}
|
|
10504
10826
|
};
|
|
10505
10827
|
this.conversations.set(conversation.conversationId, conversation);
|
|
10506
10828
|
return conversation;
|
|
@@ -10600,6 +10922,7 @@ export {
|
|
|
10600
10922
|
buildSkillContextWindow,
|
|
10601
10923
|
compactMessages,
|
|
10602
10924
|
completeOpenAICodexDeviceAuth,
|
|
10925
|
+
computeNextOccurrence,
|
|
10603
10926
|
createBashTool,
|
|
10604
10927
|
createConversationStore,
|
|
10605
10928
|
createConversationStoreFromEngine,
|