@poncho-ai/harness 0.31.3 → 0.32.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/.turbo/turbo-build.log +5 -5
- package/.turbo/turbo-lint.log +6 -0
- package/.turbo/turbo-test.log +34 -0
- package/CHANGELOG.md +12 -0
- package/dist/index.d.ts +37 -1
- package/dist/index.js +492 -82
- package/package.json +1 -1
- package/src/config.ts +6 -0
- package/src/harness.ts +16 -1
- package/src/index.ts +2 -0
- package/src/reminder-store.ts +343 -0
- package/src/reminder-tools.ts +168 -0
- package/test/reminder-store.test.ts +159 -0
package/dist/index.js
CHANGED
|
@@ -1052,6 +1052,23 @@ The system auto-discovers all Telegram chats the bot has interacted with and sen
|
|
|
1052
1052
|
|
|
1053
1053
|
The bot must have received at least one message from a user before it can send proactive messages to that chat (Telegram API requirement).
|
|
1054
1054
|
|
|
1055
|
+
#### One-off reminders
|
|
1056
|
+
|
|
1057
|
+
Unlike cron jobs (which are recurring and static), reminders are one-off and dynamic \u2014 created by the agent during conversations. Enable in \`poncho.config.js\`:
|
|
1058
|
+
|
|
1059
|
+
\`\`\`javascript
|
|
1060
|
+
export default {
|
|
1061
|
+
reminders: {
|
|
1062
|
+
enabled: true,
|
|
1063
|
+
pollSchedule: '*/10 * * * *',
|
|
1064
|
+
},
|
|
1065
|
+
};
|
|
1066
|
+
\`\`\`
|
|
1067
|
+
|
|
1068
|
+
When enabled, the agent gets \`set_reminder\`, \`list_reminders\`, and \`cancel_reminder\` tools. If the original conversation is on a messaging channel, the reminder fires as a reply in that conversation. Otherwise, a new \`[reminder]\` conversation is created.
|
|
1069
|
+
|
|
1070
|
+
Reminders are checked on a polling interval (configured by \`pollSchedule\`). Reminders due within the next poll window fire early rather than late.
|
|
1071
|
+
|
|
1055
1072
|
### Email (Resend)
|
|
1056
1073
|
|
|
1057
1074
|
#### 1. Set up Resend
|
|
@@ -1592,6 +1609,12 @@ export default {
|
|
|
1592
1609
|
// cdpUrl: 'wss://...', // Or connect via CDP URL (alternative to provider)
|
|
1593
1610
|
// },
|
|
1594
1611
|
|
|
1612
|
+
// One-off reminders (enabled by default in new projects)
|
|
1613
|
+
reminders: {
|
|
1614
|
+
enabled: true,
|
|
1615
|
+
// pollSchedule: '*/10 * * * *', // how often to check for due reminders
|
|
1616
|
+
},
|
|
1617
|
+
|
|
1595
1618
|
// Headless mode: disable the built-in Web UI (API-only)
|
|
1596
1619
|
// webUi: false,
|
|
1597
1620
|
|
|
@@ -2110,9 +2133,9 @@ var ponchoDocsTool = defineTool({
|
|
|
2110
2133
|
|
|
2111
2134
|
// src/harness.ts
|
|
2112
2135
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
2113
|
-
import { readFile as
|
|
2114
|
-
import { resolve as
|
|
2115
|
-
import { defineTool as
|
|
2136
|
+
import { readFile as readFile10 } from "fs/promises";
|
|
2137
|
+
import { resolve as resolve12 } from "path";
|
|
2138
|
+
import { defineTool as defineTool8, getTextContent as getTextContent2 } from "@poncho-ai/sdk";
|
|
2116
2139
|
|
|
2117
2140
|
// src/upload-store.ts
|
|
2118
2141
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -3178,6 +3201,379 @@ var createTodoTools = (store) => {
|
|
|
3178
3201
|
];
|
|
3179
3202
|
};
|
|
3180
3203
|
|
|
3204
|
+
// src/reminder-store.ts
|
|
3205
|
+
import { mkdir as mkdir5, readFile as readFile7, rename as rename3, writeFile as writeFile6 } from "fs/promises";
|
|
3206
|
+
import { dirname as dirname4, resolve as resolve8 } from "path";
|
|
3207
|
+
var REMINDERS_FILE = "reminders.json";
|
|
3208
|
+
var STALE_CANCELLED_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
3209
|
+
var writeJsonAtomic3 = async (filePath, payload) => {
|
|
3210
|
+
await mkdir5(dirname4(filePath), { recursive: true });
|
|
3211
|
+
const tmpPath = `${filePath}.tmp`;
|
|
3212
|
+
await writeFile6(tmpPath, JSON.stringify(payload, null, 2), "utf8");
|
|
3213
|
+
await rename3(tmpPath, filePath);
|
|
3214
|
+
};
|
|
3215
|
+
var isValidReminder = (item) => typeof item === "object" && item !== null && typeof item.id === "string" && typeof item.task === "string" && typeof item.scheduledAt === "number" && typeof item.status === "string";
|
|
3216
|
+
var parseReminderList = (raw) => {
|
|
3217
|
+
if (!Array.isArray(raw)) return [];
|
|
3218
|
+
return raw.filter(isValidReminder);
|
|
3219
|
+
};
|
|
3220
|
+
var pruneStale = (reminders) => {
|
|
3221
|
+
const cutoff = Date.now() - STALE_CANCELLED_MS;
|
|
3222
|
+
return reminders.filter(
|
|
3223
|
+
(r) => r.status === "pending" || r.status === "cancelled" && r.createdAt > cutoff
|
|
3224
|
+
);
|
|
3225
|
+
};
|
|
3226
|
+
var generateId2 = () => (globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random()}`).slice(0, 8);
|
|
3227
|
+
var InMemoryReminderStore = class {
|
|
3228
|
+
reminders = [];
|
|
3229
|
+
async list() {
|
|
3230
|
+
this.reminders = pruneStale(this.reminders);
|
|
3231
|
+
return [...this.reminders];
|
|
3232
|
+
}
|
|
3233
|
+
async create(input) {
|
|
3234
|
+
const reminder = {
|
|
3235
|
+
id: generateId2(),
|
|
3236
|
+
task: input.task,
|
|
3237
|
+
scheduledAt: input.scheduledAt,
|
|
3238
|
+
timezone: input.timezone,
|
|
3239
|
+
status: "pending",
|
|
3240
|
+
createdAt: Date.now(),
|
|
3241
|
+
conversationId: input.conversationId,
|
|
3242
|
+
ownerId: input.ownerId
|
|
3243
|
+
};
|
|
3244
|
+
this.reminders = pruneStale(this.reminders);
|
|
3245
|
+
this.reminders.push(reminder);
|
|
3246
|
+
return reminder;
|
|
3247
|
+
}
|
|
3248
|
+
async cancel(id) {
|
|
3249
|
+
const reminder = this.reminders.find((r) => r.id === id);
|
|
3250
|
+
if (!reminder) throw new Error(`Reminder "${id}" not found`);
|
|
3251
|
+
if (reminder.status !== "pending") {
|
|
3252
|
+
throw new Error(`Reminder "${id}" is already ${reminder.status}`);
|
|
3253
|
+
}
|
|
3254
|
+
reminder.status = "cancelled";
|
|
3255
|
+
return reminder;
|
|
3256
|
+
}
|
|
3257
|
+
async delete(id) {
|
|
3258
|
+
this.reminders = this.reminders.filter((r) => r.id !== id);
|
|
3259
|
+
}
|
|
3260
|
+
};
|
|
3261
|
+
var FileReminderStore = class {
|
|
3262
|
+
workingDir;
|
|
3263
|
+
filePath = "";
|
|
3264
|
+
constructor(workingDir) {
|
|
3265
|
+
this.workingDir = workingDir;
|
|
3266
|
+
}
|
|
3267
|
+
async ensureFilePath() {
|
|
3268
|
+
if (this.filePath) return this.filePath;
|
|
3269
|
+
const identity = await ensureAgentIdentity(this.workingDir);
|
|
3270
|
+
this.filePath = resolve8(getAgentStoreDirectory(identity), REMINDERS_FILE);
|
|
3271
|
+
return this.filePath;
|
|
3272
|
+
}
|
|
3273
|
+
async readAll() {
|
|
3274
|
+
try {
|
|
3275
|
+
const fp = await this.ensureFilePath();
|
|
3276
|
+
const raw = await readFile7(fp, "utf8");
|
|
3277
|
+
return parseReminderList(JSON.parse(raw));
|
|
3278
|
+
} catch {
|
|
3279
|
+
return [];
|
|
3280
|
+
}
|
|
3281
|
+
}
|
|
3282
|
+
async writeAll(reminders) {
|
|
3283
|
+
const fp = await this.ensureFilePath();
|
|
3284
|
+
await writeJsonAtomic3(fp, reminders);
|
|
3285
|
+
}
|
|
3286
|
+
async list() {
|
|
3287
|
+
const all = await this.readAll();
|
|
3288
|
+
const pruned = pruneStale(all);
|
|
3289
|
+
if (pruned.length !== all.length) await this.writeAll(pruned);
|
|
3290
|
+
return pruned;
|
|
3291
|
+
}
|
|
3292
|
+
async create(input) {
|
|
3293
|
+
const reminder = {
|
|
3294
|
+
id: generateId2(),
|
|
3295
|
+
task: input.task,
|
|
3296
|
+
scheduledAt: input.scheduledAt,
|
|
3297
|
+
timezone: input.timezone,
|
|
3298
|
+
status: "pending",
|
|
3299
|
+
createdAt: Date.now(),
|
|
3300
|
+
conversationId: input.conversationId,
|
|
3301
|
+
ownerId: input.ownerId
|
|
3302
|
+
};
|
|
3303
|
+
let reminders = await this.readAll();
|
|
3304
|
+
reminders = pruneStale(reminders);
|
|
3305
|
+
reminders.push(reminder);
|
|
3306
|
+
await this.writeAll(reminders);
|
|
3307
|
+
return reminder;
|
|
3308
|
+
}
|
|
3309
|
+
async cancel(id) {
|
|
3310
|
+
const reminders = await this.readAll();
|
|
3311
|
+
const reminder = reminders.find((r) => r.id === id);
|
|
3312
|
+
if (!reminder) throw new Error(`Reminder "${id}" not found`);
|
|
3313
|
+
if (reminder.status !== "pending") {
|
|
3314
|
+
throw new Error(`Reminder "${id}" is already ${reminder.status}`);
|
|
3315
|
+
}
|
|
3316
|
+
reminder.status = "cancelled";
|
|
3317
|
+
await this.writeAll(reminders);
|
|
3318
|
+
return reminder;
|
|
3319
|
+
}
|
|
3320
|
+
async delete(id) {
|
|
3321
|
+
const reminders = await this.readAll();
|
|
3322
|
+
await this.writeAll(reminders.filter((r) => r.id !== id));
|
|
3323
|
+
}
|
|
3324
|
+
};
|
|
3325
|
+
var KVBackedReminderStore = class {
|
|
3326
|
+
kv;
|
|
3327
|
+
key;
|
|
3328
|
+
ttl;
|
|
3329
|
+
memoryFallback = new InMemoryReminderStore();
|
|
3330
|
+
constructor(kv, key, ttl) {
|
|
3331
|
+
this.kv = kv;
|
|
3332
|
+
this.key = key;
|
|
3333
|
+
this.ttl = ttl;
|
|
3334
|
+
}
|
|
3335
|
+
async readAll() {
|
|
3336
|
+
try {
|
|
3337
|
+
const raw = await this.kv.get(this.key);
|
|
3338
|
+
if (!raw) return [];
|
|
3339
|
+
return parseReminderList(JSON.parse(raw));
|
|
3340
|
+
} catch {
|
|
3341
|
+
return this.memoryFallback.list();
|
|
3342
|
+
}
|
|
3343
|
+
}
|
|
3344
|
+
async writeAll(reminders) {
|
|
3345
|
+
try {
|
|
3346
|
+
const serialized = JSON.stringify(reminders);
|
|
3347
|
+
if (typeof this.ttl === "number") {
|
|
3348
|
+
await this.kv.setWithTtl(this.key, serialized, Math.max(1, this.ttl));
|
|
3349
|
+
} else {
|
|
3350
|
+
await this.kv.set(this.key, serialized);
|
|
3351
|
+
}
|
|
3352
|
+
} catch {
|
|
3353
|
+
}
|
|
3354
|
+
}
|
|
3355
|
+
async list() {
|
|
3356
|
+
const all = await this.readAll();
|
|
3357
|
+
const pruned = pruneStale(all);
|
|
3358
|
+
if (pruned.length !== all.length) await this.writeAll(pruned);
|
|
3359
|
+
return pruned;
|
|
3360
|
+
}
|
|
3361
|
+
async create(input) {
|
|
3362
|
+
let reminders;
|
|
3363
|
+
try {
|
|
3364
|
+
reminders = await this.readAll();
|
|
3365
|
+
} catch {
|
|
3366
|
+
return this.memoryFallback.create(input);
|
|
3367
|
+
}
|
|
3368
|
+
const reminder = {
|
|
3369
|
+
id: generateId2(),
|
|
3370
|
+
task: input.task,
|
|
3371
|
+
scheduledAt: input.scheduledAt,
|
|
3372
|
+
timezone: input.timezone,
|
|
3373
|
+
status: "pending",
|
|
3374
|
+
createdAt: Date.now(),
|
|
3375
|
+
conversationId: input.conversationId,
|
|
3376
|
+
ownerId: input.ownerId
|
|
3377
|
+
};
|
|
3378
|
+
reminders = pruneStale(reminders);
|
|
3379
|
+
reminders.push(reminder);
|
|
3380
|
+
await this.writeAll(reminders);
|
|
3381
|
+
return reminder;
|
|
3382
|
+
}
|
|
3383
|
+
async cancel(id) {
|
|
3384
|
+
let reminders;
|
|
3385
|
+
try {
|
|
3386
|
+
reminders = await this.readAll();
|
|
3387
|
+
} catch {
|
|
3388
|
+
return this.memoryFallback.cancel(id);
|
|
3389
|
+
}
|
|
3390
|
+
const reminder = reminders.find((r) => r.id === id);
|
|
3391
|
+
if (!reminder) throw new Error(`Reminder "${id}" not found`);
|
|
3392
|
+
if (reminder.status !== "pending") {
|
|
3393
|
+
throw new Error(`Reminder "${id}" is already ${reminder.status}`);
|
|
3394
|
+
}
|
|
3395
|
+
reminder.status = "cancelled";
|
|
3396
|
+
await this.writeAll(reminders);
|
|
3397
|
+
return reminder;
|
|
3398
|
+
}
|
|
3399
|
+
async delete(id) {
|
|
3400
|
+
let reminders;
|
|
3401
|
+
try {
|
|
3402
|
+
reminders = await this.readAll();
|
|
3403
|
+
} catch {
|
|
3404
|
+
return this.memoryFallback.delete(id);
|
|
3405
|
+
}
|
|
3406
|
+
await this.writeAll(reminders.filter((r) => r.id !== id));
|
|
3407
|
+
}
|
|
3408
|
+
};
|
|
3409
|
+
var createReminderStore = (agentId, config, options) => {
|
|
3410
|
+
const provider = config?.provider ?? "local";
|
|
3411
|
+
const ttl = config?.ttl;
|
|
3412
|
+
const workingDir = options?.workingDir ?? process.cwd();
|
|
3413
|
+
if (provider === "local") {
|
|
3414
|
+
return new FileReminderStore(workingDir);
|
|
3415
|
+
}
|
|
3416
|
+
if (provider === "memory") {
|
|
3417
|
+
return new InMemoryReminderStore();
|
|
3418
|
+
}
|
|
3419
|
+
const kv = createRawKVStore(config);
|
|
3420
|
+
if (kv) {
|
|
3421
|
+
const key = `poncho:${STORAGE_SCHEMA_VERSION}:${slugifyStorageComponent(agentId)}:reminders`;
|
|
3422
|
+
return new KVBackedReminderStore(kv, key, ttl);
|
|
3423
|
+
}
|
|
3424
|
+
return new InMemoryReminderStore();
|
|
3425
|
+
};
|
|
3426
|
+
|
|
3427
|
+
// src/reminder-tools.ts
|
|
3428
|
+
import { defineTool as defineTool4 } from "@poncho-ai/sdk";
|
|
3429
|
+
var VALID_STATUSES2 = ["pending", "cancelled"];
|
|
3430
|
+
var createReminderTools = (store) => [
|
|
3431
|
+
defineTool4({
|
|
3432
|
+
name: "set_reminder",
|
|
3433
|
+
description: "Set a one-time 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.",
|
|
3434
|
+
inputSchema: {
|
|
3435
|
+
type: "object",
|
|
3436
|
+
properties: {
|
|
3437
|
+
task: {
|
|
3438
|
+
type: "string",
|
|
3439
|
+
description: "What to remind about"
|
|
3440
|
+
},
|
|
3441
|
+
datetime: {
|
|
3442
|
+
type: "string",
|
|
3443
|
+
description: "ISO 8601 datetime for when the reminder should fire (e.g. '2026-03-23T09:00:00Z')"
|
|
3444
|
+
},
|
|
3445
|
+
timezone: {
|
|
3446
|
+
type: "string",
|
|
3447
|
+
description: "IANA timezone for interpreting the datetime if it lacks an offset (e.g. 'America/New_York'). Defaults to UTC."
|
|
3448
|
+
}
|
|
3449
|
+
},
|
|
3450
|
+
required: ["task", "datetime"],
|
|
3451
|
+
additionalProperties: false
|
|
3452
|
+
},
|
|
3453
|
+
handler: async (input, context) => {
|
|
3454
|
+
const task = typeof input.task === "string" ? input.task.trim() : "";
|
|
3455
|
+
if (!task) throw new Error("task is required");
|
|
3456
|
+
const datetimeStr = typeof input.datetime === "string" ? input.datetime.trim() : "";
|
|
3457
|
+
if (!datetimeStr) throw new Error("datetime is required");
|
|
3458
|
+
const timezone = typeof input.timezone === "string" ? input.timezone.trim() : void 0;
|
|
3459
|
+
let scheduledAt;
|
|
3460
|
+
if (timezone && !datetimeStr.includes("Z") && !/[+-]\d{2}:\d{2}$/.test(datetimeStr)) {
|
|
3461
|
+
try {
|
|
3462
|
+
const formatted = new Intl.DateTimeFormat("en-US", {
|
|
3463
|
+
timeZone: timezone,
|
|
3464
|
+
year: "numeric",
|
|
3465
|
+
month: "2-digit",
|
|
3466
|
+
day: "2-digit",
|
|
3467
|
+
hour: "2-digit",
|
|
3468
|
+
minute: "2-digit",
|
|
3469
|
+
second: "2-digit",
|
|
3470
|
+
hour12: false
|
|
3471
|
+
}).format(/* @__PURE__ */ new Date());
|
|
3472
|
+
void formatted;
|
|
3473
|
+
} catch {
|
|
3474
|
+
throw new Error(`Invalid timezone: "${timezone}"`);
|
|
3475
|
+
}
|
|
3476
|
+
const baseDate = new Date(datetimeStr);
|
|
3477
|
+
if (isNaN(baseDate.getTime())) {
|
|
3478
|
+
throw new Error(`Invalid datetime: "${datetimeStr}"`);
|
|
3479
|
+
}
|
|
3480
|
+
const utcStr = baseDate.toLocaleString("en-US", { timeZone: "UTC" });
|
|
3481
|
+
const tzStr = baseDate.toLocaleString("en-US", { timeZone: timezone });
|
|
3482
|
+
const offsetMs = new Date(utcStr).getTime() - new Date(tzStr).getTime();
|
|
3483
|
+
scheduledAt = baseDate.getTime() + offsetMs;
|
|
3484
|
+
} else {
|
|
3485
|
+
const parsed = new Date(datetimeStr);
|
|
3486
|
+
if (isNaN(parsed.getTime())) {
|
|
3487
|
+
throw new Error(`Invalid datetime: "${datetimeStr}"`);
|
|
3488
|
+
}
|
|
3489
|
+
scheduledAt = parsed.getTime();
|
|
3490
|
+
}
|
|
3491
|
+
if (scheduledAt <= Date.now()) {
|
|
3492
|
+
throw new Error("Reminder datetime must be in the future");
|
|
3493
|
+
}
|
|
3494
|
+
const conversationId = context.conversationId || context.runId;
|
|
3495
|
+
const reminder = await store.create({
|
|
3496
|
+
task,
|
|
3497
|
+
scheduledAt,
|
|
3498
|
+
timezone,
|
|
3499
|
+
conversationId
|
|
3500
|
+
});
|
|
3501
|
+
return {
|
|
3502
|
+
ok: true,
|
|
3503
|
+
reminder: {
|
|
3504
|
+
id: reminder.id,
|
|
3505
|
+
task: reminder.task,
|
|
3506
|
+
scheduledAt: new Date(reminder.scheduledAt).toISOString(),
|
|
3507
|
+
timezone: reminder.timezone ?? "UTC",
|
|
3508
|
+
status: reminder.status
|
|
3509
|
+
}
|
|
3510
|
+
};
|
|
3511
|
+
}
|
|
3512
|
+
}),
|
|
3513
|
+
defineTool4({
|
|
3514
|
+
name: "list_reminders",
|
|
3515
|
+
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.",
|
|
3516
|
+
inputSchema: {
|
|
3517
|
+
type: "object",
|
|
3518
|
+
properties: {
|
|
3519
|
+
status: {
|
|
3520
|
+
type: "string",
|
|
3521
|
+
enum: VALID_STATUSES2,
|
|
3522
|
+
description: "Filter by status (omit to list all)"
|
|
3523
|
+
}
|
|
3524
|
+
},
|
|
3525
|
+
additionalProperties: false
|
|
3526
|
+
},
|
|
3527
|
+
handler: async (input) => {
|
|
3528
|
+
let reminders = await store.list();
|
|
3529
|
+
const status = typeof input.status === "string" ? input.status : void 0;
|
|
3530
|
+
if (status && VALID_STATUSES2.includes(status)) {
|
|
3531
|
+
reminders = reminders.filter((r) => r.status === status);
|
|
3532
|
+
}
|
|
3533
|
+
return {
|
|
3534
|
+
reminders: reminders.map((r) => ({
|
|
3535
|
+
id: r.id,
|
|
3536
|
+
task: r.task,
|
|
3537
|
+
scheduledAt: new Date(r.scheduledAt).toISOString(),
|
|
3538
|
+
timezone: r.timezone ?? "UTC",
|
|
3539
|
+
status: r.status,
|
|
3540
|
+
createdAt: new Date(r.createdAt).toISOString()
|
|
3541
|
+
})),
|
|
3542
|
+
count: reminders.length
|
|
3543
|
+
};
|
|
3544
|
+
}
|
|
3545
|
+
}),
|
|
3546
|
+
defineTool4({
|
|
3547
|
+
name: "cancel_reminder",
|
|
3548
|
+
description: "Cancel a pending reminder by its ID.",
|
|
3549
|
+
inputSchema: {
|
|
3550
|
+
type: "object",
|
|
3551
|
+
properties: {
|
|
3552
|
+
id: {
|
|
3553
|
+
type: "string",
|
|
3554
|
+
description: "ID of the reminder to cancel"
|
|
3555
|
+
}
|
|
3556
|
+
},
|
|
3557
|
+
required: ["id"],
|
|
3558
|
+
additionalProperties: false
|
|
3559
|
+
},
|
|
3560
|
+
handler: async (input) => {
|
|
3561
|
+
const id = typeof input.id === "string" ? input.id.trim() : "";
|
|
3562
|
+
if (!id) throw new Error("id is required");
|
|
3563
|
+
const cancelled = await store.cancel(id);
|
|
3564
|
+
return {
|
|
3565
|
+
ok: true,
|
|
3566
|
+
reminder: {
|
|
3567
|
+
id: cancelled.id,
|
|
3568
|
+
task: cancelled.task,
|
|
3569
|
+
scheduledAt: new Date(cancelled.scheduledAt).toISOString(),
|
|
3570
|
+
status: cancelled.status
|
|
3571
|
+
}
|
|
3572
|
+
};
|
|
3573
|
+
}
|
|
3574
|
+
})
|
|
3575
|
+
];
|
|
3576
|
+
|
|
3181
3577
|
// src/mcp.ts
|
|
3182
3578
|
var McpHttpError = class extends Error {
|
|
3183
3579
|
status;
|
|
@@ -3621,8 +4017,8 @@ import { createAnthropic } from "@ai-sdk/anthropic";
|
|
|
3621
4017
|
|
|
3622
4018
|
// src/openai-codex-auth.ts
|
|
3623
4019
|
import { homedir as homedir3 } from "os";
|
|
3624
|
-
import { dirname as
|
|
3625
|
-
import { mkdir as
|
|
4020
|
+
import { dirname as dirname5, resolve as resolve9 } from "path";
|
|
4021
|
+
import { mkdir as mkdir6, readFile as readFile8, chmod, writeFile as writeFile7, rm as rm3 } from "fs/promises";
|
|
3626
4022
|
var OPENAI_CODEX_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
3627
4023
|
var OPENAI_AUTH_ISSUER = "https://auth.openai.com";
|
|
3628
4024
|
var REFRESH_TOKEN_GRACE_MS = 5 * 60 * 1e3;
|
|
@@ -3654,14 +4050,14 @@ var getOpenAICodexAuthFilePath = (config) => {
|
|
|
3654
4050
|
const env = defaultedConfig(config);
|
|
3655
4051
|
const fromEnv = process.env[env.authFilePathEnv];
|
|
3656
4052
|
if (typeof fromEnv === "string" && fromEnv.trim().length > 0) {
|
|
3657
|
-
return
|
|
4053
|
+
return resolve9(fromEnv);
|
|
3658
4054
|
}
|
|
3659
|
-
return
|
|
4055
|
+
return resolve9(homedir3(), ".poncho", "auth", "openai-codex.json");
|
|
3660
4056
|
};
|
|
3661
4057
|
var readOpenAICodexSession = async (config) => {
|
|
3662
4058
|
const filePath = getOpenAICodexAuthFilePath(config);
|
|
3663
4059
|
try {
|
|
3664
|
-
const content = await
|
|
4060
|
+
const content = await readFile8(filePath, "utf8");
|
|
3665
4061
|
const parsed = JSON.parse(content);
|
|
3666
4062
|
if (typeof parsed.refreshToken !== "string" || parsed.refreshToken.length === 0) {
|
|
3667
4063
|
return void 0;
|
|
@@ -3678,12 +4074,12 @@ var readOpenAICodexSession = async (config) => {
|
|
|
3678
4074
|
};
|
|
3679
4075
|
var writeOpenAICodexSession = async (session, config) => {
|
|
3680
4076
|
const filePath = getOpenAICodexAuthFilePath(config);
|
|
3681
|
-
await
|
|
4077
|
+
await mkdir6(dirname5(filePath), { recursive: true });
|
|
3682
4078
|
const payload = {
|
|
3683
4079
|
...session,
|
|
3684
4080
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3685
4081
|
};
|
|
3686
|
-
await
|
|
4082
|
+
await writeFile7(filePath, `${JSON.stringify(payload, null, 2)}
|
|
3687
4083
|
`, "utf8");
|
|
3688
4084
|
await chmod(filePath, 384);
|
|
3689
4085
|
};
|
|
@@ -3996,8 +4392,8 @@ var createModelProvider = (provider, config) => {
|
|
|
3996
4392
|
};
|
|
3997
4393
|
|
|
3998
4394
|
// src/skill-context.ts
|
|
3999
|
-
import { readFile as
|
|
4000
|
-
import { dirname as
|
|
4395
|
+
import { readFile as readFile9, readdir as readdir2, stat } from "fs/promises";
|
|
4396
|
+
import { dirname as dirname6, resolve as resolve10, normalize } from "path";
|
|
4001
4397
|
import YAML3 from "yaml";
|
|
4002
4398
|
var DEFAULT_SKILL_DIRS = ["skills"];
|
|
4003
4399
|
var resolveSkillDirs = (workingDir, extraPaths) => {
|
|
@@ -4009,7 +4405,7 @@ var resolveSkillDirs = (workingDir, extraPaths) => {
|
|
|
4009
4405
|
}
|
|
4010
4406
|
}
|
|
4011
4407
|
}
|
|
4012
|
-
return dirs.map((d) =>
|
|
4408
|
+
return dirs.map((d) => resolve10(workingDir, d));
|
|
4013
4409
|
};
|
|
4014
4410
|
var FRONTMATTER_PATTERN3 = /^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/;
|
|
4015
4411
|
var asRecord2 = (value) => typeof value === "object" && value !== null ? value : {};
|
|
@@ -4078,7 +4474,7 @@ var collectSkillManifests = async (directory) => {
|
|
|
4078
4474
|
const entries = await readdir2(directory, { withFileTypes: true });
|
|
4079
4475
|
const files = [];
|
|
4080
4476
|
for (const entry of entries) {
|
|
4081
|
-
const fullPath =
|
|
4477
|
+
const fullPath = resolve10(directory, entry.name);
|
|
4082
4478
|
let isDir = entry.isDirectory();
|
|
4083
4479
|
let isFile = entry.isFile();
|
|
4084
4480
|
if (entry.isSymbolicLink()) {
|
|
@@ -4113,13 +4509,13 @@ var loadSkillMetadata = async (workingDir, extraSkillPaths) => {
|
|
|
4113
4509
|
const seen = /* @__PURE__ */ new Set();
|
|
4114
4510
|
for (const manifest of allManifests) {
|
|
4115
4511
|
try {
|
|
4116
|
-
const content = await
|
|
4512
|
+
const content = await readFile9(manifest, "utf8");
|
|
4117
4513
|
const parsed = parseSkillFrontmatter(content);
|
|
4118
4514
|
if (parsed && !seen.has(parsed.name)) {
|
|
4119
4515
|
seen.add(parsed.name);
|
|
4120
4516
|
skills.push({
|
|
4121
4517
|
...parsed,
|
|
4122
|
-
skillDir:
|
|
4518
|
+
skillDir: dirname6(manifest),
|
|
4123
4519
|
skillPath: manifest
|
|
4124
4520
|
});
|
|
4125
4521
|
}
|
|
@@ -4158,7 +4554,7 @@ ${xmlSkills}
|
|
|
4158
4554
|
};
|
|
4159
4555
|
var escapeXml = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
4160
4556
|
var loadSkillInstructions = async (skill) => {
|
|
4161
|
-
const content = await
|
|
4557
|
+
const content = await readFile9(skill.skillPath, "utf8");
|
|
4162
4558
|
const match = content.match(FRONTMATTER_PATTERN3);
|
|
4163
4559
|
return match ? match[2].trim() : content.trim();
|
|
4164
4560
|
};
|
|
@@ -4167,11 +4563,11 @@ var readSkillResource = async (skill, relativePath) => {
|
|
|
4167
4563
|
if (normalized.startsWith("..") || normalized.startsWith("/")) {
|
|
4168
4564
|
throw new Error("Path must be relative and within the skill directory");
|
|
4169
4565
|
}
|
|
4170
|
-
const fullPath =
|
|
4566
|
+
const fullPath = resolve10(skill.skillDir, normalized);
|
|
4171
4567
|
if (!fullPath.startsWith(skill.skillDir)) {
|
|
4172
4568
|
throw new Error("Path escapes the skill directory");
|
|
4173
4569
|
}
|
|
4174
|
-
return await
|
|
4570
|
+
return await readFile9(fullPath, "utf8");
|
|
4175
4571
|
};
|
|
4176
4572
|
var MAX_INSTRUCTIONS_PER_SKILL = 1200;
|
|
4177
4573
|
var loadSkillContext = async (workingDir) => {
|
|
@@ -4288,16 +4684,16 @@ function convertSchema(schema) {
|
|
|
4288
4684
|
}
|
|
4289
4685
|
|
|
4290
4686
|
// src/skill-tools.ts
|
|
4291
|
-
import { defineTool as
|
|
4687
|
+
import { defineTool as defineTool5 } from "@poncho-ai/sdk";
|
|
4292
4688
|
import { access as access2, readdir as readdir3, stat as stat2 } from "fs/promises";
|
|
4293
|
-
import { extname, normalize as normalize2, resolve as
|
|
4689
|
+
import { extname, normalize as normalize2, resolve as resolve11, sep as sep2 } from "path";
|
|
4294
4690
|
import { pathToFileURL } from "url";
|
|
4295
4691
|
import { createJiti as createJiti2 } from "jiti";
|
|
4296
4692
|
var createSkillTools = (skills, options) => {
|
|
4297
4693
|
const skillsByName = new Map(skills.map((skill) => [skill.name, skill]));
|
|
4298
4694
|
const knownNames = skills.length > 0 ? skills.map((skill) => skill.name).join(", ") : "(none)";
|
|
4299
4695
|
return [
|
|
4300
|
-
|
|
4696
|
+
defineTool5({
|
|
4301
4697
|
name: "activate_skill",
|
|
4302
4698
|
description: `Load the full instructions for an available skill. Use this when a user's request matches a skill's description. Available skills: ${knownNames}`,
|
|
4303
4699
|
inputSchema: {
|
|
@@ -4334,7 +4730,7 @@ var createSkillTools = (skills, options) => {
|
|
|
4334
4730
|
}
|
|
4335
4731
|
}
|
|
4336
4732
|
}),
|
|
4337
|
-
|
|
4733
|
+
defineTool5({
|
|
4338
4734
|
name: "deactivate_skill",
|
|
4339
4735
|
description: "Deactivate a previously activated skill and update scoped tool availability.",
|
|
4340
4736
|
inputSchema: {
|
|
@@ -4363,7 +4759,7 @@ var createSkillTools = (skills, options) => {
|
|
|
4363
4759
|
}
|
|
4364
4760
|
}
|
|
4365
4761
|
}),
|
|
4366
|
-
|
|
4762
|
+
defineTool5({
|
|
4367
4763
|
name: "list_active_skills",
|
|
4368
4764
|
description: "List currently active skills with scoped MCP tools.",
|
|
4369
4765
|
inputSchema: {
|
|
@@ -4375,7 +4771,7 @@ var createSkillTools = (skills, options) => {
|
|
|
4375
4771
|
activeSkills: options?.onListActiveSkills ? options.onListActiveSkills() : []
|
|
4376
4772
|
})
|
|
4377
4773
|
}),
|
|
4378
|
-
|
|
4774
|
+
defineTool5({
|
|
4379
4775
|
name: "read_skill_resource",
|
|
4380
4776
|
description: `Read a file from a skill's directory (references, scripts, assets). Use relative paths from the skill root. Available skills: ${knownNames}`,
|
|
4381
4777
|
inputSchema: {
|
|
@@ -4415,7 +4811,7 @@ var createSkillTools = (skills, options) => {
|
|
|
4415
4811
|
}
|
|
4416
4812
|
}
|
|
4417
4813
|
}),
|
|
4418
|
-
|
|
4814
|
+
defineTool5({
|
|
4419
4815
|
name: "list_skill_scripts",
|
|
4420
4816
|
description: `List JavaScript/TypeScript script files available under a skill directory (recursive). Available skills: ${knownNames}`,
|
|
4421
4817
|
inputSchema: {
|
|
@@ -4450,7 +4846,7 @@ var createSkillTools = (skills, options) => {
|
|
|
4450
4846
|
}
|
|
4451
4847
|
}
|
|
4452
4848
|
}),
|
|
4453
|
-
|
|
4849
|
+
defineTool5({
|
|
4454
4850
|
name: "run_skill_script",
|
|
4455
4851
|
description: `Run a JavaScript/TypeScript module in a skill or project directory. Uses default export function or named run/main/handler function. Available skills: ${knownNames}`,
|
|
4456
4852
|
inputSchema: {
|
|
@@ -4548,7 +4944,7 @@ var collectScriptFiles = async (directory) => {
|
|
|
4548
4944
|
if (entry.name === "node_modules") {
|
|
4549
4945
|
continue;
|
|
4550
4946
|
}
|
|
4551
|
-
const fullPath =
|
|
4947
|
+
const fullPath = resolve11(directory, entry.name);
|
|
4552
4948
|
let isDir = entry.isDirectory();
|
|
4553
4949
|
let isFile = entry.isFile();
|
|
4554
4950
|
if (entry.isSymbolicLink()) {
|
|
@@ -4587,8 +4983,8 @@ var normalizeScriptPolicyPath = (relativePath) => {
|
|
|
4587
4983
|
};
|
|
4588
4984
|
var resolveScriptPath = (baseDir, relativePath, containmentDir) => {
|
|
4589
4985
|
const normalized = normalizeScriptPolicyPath(relativePath);
|
|
4590
|
-
const fullPath =
|
|
4591
|
-
const boundary =
|
|
4986
|
+
const fullPath = resolve11(baseDir, normalized);
|
|
4987
|
+
const boundary = resolve11(containmentDir ?? baseDir);
|
|
4592
4988
|
if (!fullPath.startsWith(`${boundary}${sep2}`) && fullPath !== boundary) {
|
|
4593
4989
|
throw new Error("Script path must stay inside the allowed directory");
|
|
4594
4990
|
}
|
|
@@ -4665,7 +5061,7 @@ var extractRunnableFunction = (value) => {
|
|
|
4665
5061
|
|
|
4666
5062
|
// src/search-tools.ts
|
|
4667
5063
|
import { load as cheerioLoad } from "cheerio";
|
|
4668
|
-
import { defineTool as
|
|
5064
|
+
import { defineTool as defineTool6 } from "@poncho-ai/sdk";
|
|
4669
5065
|
var SEARCH_UA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36";
|
|
4670
5066
|
var FETCH_TIMEOUT_MS = 15e3;
|
|
4671
5067
|
var SEARCH_MAX_RETRIES = 4;
|
|
@@ -4694,8 +5090,8 @@ function applyRateLimitCooldown(retryAfterHeader) {
|
|
|
4694
5090
|
async function runWithSearchThrottle(fn) {
|
|
4695
5091
|
const previous = searchQueue;
|
|
4696
5092
|
let release;
|
|
4697
|
-
searchQueue = new Promise((
|
|
4698
|
-
release =
|
|
5093
|
+
searchQueue = new Promise((resolve14) => {
|
|
5094
|
+
release = resolve14;
|
|
4699
5095
|
});
|
|
4700
5096
|
await previous.catch(() => {
|
|
4701
5097
|
});
|
|
@@ -4787,7 +5183,7 @@ function extractReadableText($, maxLength) {
|
|
|
4787
5183
|
return { title, content };
|
|
4788
5184
|
}
|
|
4789
5185
|
var createSearchTools = () => [
|
|
4790
|
-
|
|
5186
|
+
defineTool6({
|
|
4791
5187
|
name: "web_search",
|
|
4792
5188
|
description: "Search the web and return a list of results (title, URL, snippet). Use this instead of opening a browser when you need to find information online.",
|
|
4793
5189
|
inputSchema: {
|
|
@@ -4826,7 +5222,7 @@ var createSearchTools = () => [
|
|
|
4826
5222
|
}
|
|
4827
5223
|
}
|
|
4828
5224
|
}),
|
|
4829
|
-
|
|
5225
|
+
defineTool6({
|
|
4830
5226
|
name: "web_fetch",
|
|
4831
5227
|
description: "Fetch a web page and return its text content (HTML tags stripped). Useful for reading articles, documentation, or any web page without opening a browser.",
|
|
4832
5228
|
inputSchema: {
|
|
@@ -4872,9 +5268,9 @@ var createSearchTools = () => [
|
|
|
4872
5268
|
];
|
|
4873
5269
|
|
|
4874
5270
|
// src/subagent-tools.ts
|
|
4875
|
-
import { defineTool as
|
|
5271
|
+
import { defineTool as defineTool7 } from "@poncho-ai/sdk";
|
|
4876
5272
|
var createSubagentTools = (manager) => [
|
|
4877
|
-
|
|
5273
|
+
defineTool7({
|
|
4878
5274
|
name: "spawn_subagent",
|
|
4879
5275
|
description: "Spawn a subagent to work on a task in the background. Returns immediately with a subagent ID. The subagent runs independently and its result will be delivered to you as a message in the conversation when it completes.\n\nGuidelines:\n- Spawn all needed subagents in a SINGLE response (they run concurrently), then end your turn with a brief message to the user.\n- Do NOT spawn more subagents in follow-up steps. Wait for results to be delivered before deciding if more work is needed.\n- Prefer doing work yourself for simple or quick tasks. Spawn subagents for substantial, self-contained work.\n- The subagent has no memory of your conversation -- write thorough, self-contained instructions in the task.",
|
|
4880
5276
|
inputSchema: {
|
|
@@ -4906,7 +5302,7 @@ var createSubagentTools = (manager) => [
|
|
|
4906
5302
|
return { subagentId, status: "running" };
|
|
4907
5303
|
}
|
|
4908
5304
|
}),
|
|
4909
|
-
|
|
5305
|
+
defineTool7({
|
|
4910
5306
|
name: "message_subagent",
|
|
4911
5307
|
description: "Send a follow-up message to a completed or stopped subagent. The subagent restarts in the background and its result will be delivered to you as a message when it completes. Only works when the subagent is not currently running.",
|
|
4912
5308
|
inputSchema: {
|
|
@@ -4934,7 +5330,7 @@ var createSubagentTools = (manager) => [
|
|
|
4934
5330
|
return { subagentId: id, status: "running" };
|
|
4935
5331
|
}
|
|
4936
5332
|
}),
|
|
4937
|
-
|
|
5333
|
+
defineTool7({
|
|
4938
5334
|
name: "stop_subagent",
|
|
4939
5335
|
description: "Stop a running subagent. The subagent's conversation is preserved but it will stop processing. Use this to cancel work that is no longer needed.",
|
|
4940
5336
|
inputSchema: {
|
|
@@ -4957,7 +5353,7 @@ var createSubagentTools = (manager) => [
|
|
|
4957
5353
|
return { message: `Subagent "${subagentId}" has been stopped.` };
|
|
4958
5354
|
}
|
|
4959
5355
|
}),
|
|
4960
|
-
|
|
5356
|
+
defineTool7({
|
|
4961
5357
|
name: "list_subagents",
|
|
4962
5358
|
description: "List all subagents that have been spawned in this conversation. Returns each subagent's ID, original task, current status, and message count. Use this to look up subagent IDs before calling message_subagent or stop_subagent.",
|
|
4963
5359
|
inputSchema: {
|
|
@@ -5632,6 +6028,7 @@ var AgentHarness = class _AgentHarness {
|
|
|
5632
6028
|
skillContextWindow = "";
|
|
5633
6029
|
memoryStore;
|
|
5634
6030
|
todoStore;
|
|
6031
|
+
reminderStore;
|
|
5635
6032
|
loadedConfig;
|
|
5636
6033
|
loadedSkills = [];
|
|
5637
6034
|
skillFingerprint = "";
|
|
@@ -5726,7 +6123,7 @@ var AgentHarness = class _AgentHarness {
|
|
|
5726
6123
|
}
|
|
5727
6124
|
}
|
|
5728
6125
|
createGetToolResultByIdTool() {
|
|
5729
|
-
return
|
|
6126
|
+
return defineTool8({
|
|
5730
6127
|
name: "get_tool_result_by_id",
|
|
5731
6128
|
description: "Retrieve a previously archived full tool result by id for the current conversation. Use this when older tool outputs were truncated in prompt history.",
|
|
5732
6129
|
inputSchema: {
|
|
@@ -6118,8 +6515,8 @@ var AgentHarness = class _AgentHarness {
|
|
|
6118
6515
|
return false;
|
|
6119
6516
|
}
|
|
6120
6517
|
try {
|
|
6121
|
-
const agentFilePath =
|
|
6122
|
-
const rawContent = await
|
|
6518
|
+
const agentFilePath = resolve12(this.workingDir, "AGENT.md");
|
|
6519
|
+
const rawContent = await readFile10(agentFilePath, "utf8");
|
|
6123
6520
|
if (rawContent === this.agentFileFingerprint) {
|
|
6124
6521
|
return false;
|
|
6125
6522
|
}
|
|
@@ -6188,8 +6585,8 @@ var AgentHarness = class _AgentHarness {
|
|
|
6188
6585
|
}
|
|
6189
6586
|
}
|
|
6190
6587
|
async initialize() {
|
|
6191
|
-
const agentFilePath =
|
|
6192
|
-
const agentRawContent = await
|
|
6588
|
+
const agentFilePath = resolve12(this.workingDir, "AGENT.md");
|
|
6589
|
+
const agentRawContent = await readFile10(agentFilePath, "utf8");
|
|
6193
6590
|
this.parsedAgent = parseAgentMarkdown(agentRawContent);
|
|
6194
6591
|
this.agentFileFingerprint = agentRawContent;
|
|
6195
6592
|
const identity = await ensureAgentIdentity(this.workingDir);
|
|
@@ -6232,6 +6629,14 @@ var AgentHarness = class _AgentHarness {
|
|
|
6232
6629
|
this.registerIfMissing(tool);
|
|
6233
6630
|
}
|
|
6234
6631
|
}
|
|
6632
|
+
if (config?.reminders?.enabled) {
|
|
6633
|
+
this.reminderStore = createReminderStore(agentId, stateConfig, { workingDir: this.workingDir });
|
|
6634
|
+
for (const tool of createReminderTools(this.reminderStore)) {
|
|
6635
|
+
if (this.isToolEnabled(tool.name)) {
|
|
6636
|
+
this.registerIfMissing(tool);
|
|
6637
|
+
}
|
|
6638
|
+
}
|
|
6639
|
+
}
|
|
6235
6640
|
if (config?.browser) {
|
|
6236
6641
|
await this.initBrowserTools(config).catch((e) => {
|
|
6237
6642
|
console.warn(
|
|
@@ -6296,14 +6701,14 @@ var AgentHarness = class _AgentHarness {
|
|
|
6296
6701
|
const filePath = pathResolve(stateDir, `${sessionId}.json`);
|
|
6297
6702
|
return {
|
|
6298
6703
|
async save(json) {
|
|
6299
|
-
const { mkdir:
|
|
6300
|
-
await
|
|
6301
|
-
await
|
|
6704
|
+
const { mkdir: mkdir8, writeFile: writeFile9 } = await import("fs/promises");
|
|
6705
|
+
await mkdir8(stateDir, { recursive: true });
|
|
6706
|
+
await writeFile9(filePath, json, "utf8");
|
|
6302
6707
|
},
|
|
6303
6708
|
async load() {
|
|
6304
|
-
const { readFile:
|
|
6709
|
+
const { readFile: readFile12 } = await import("fs/promises");
|
|
6305
6710
|
try {
|
|
6306
|
-
return await
|
|
6711
|
+
return await readFile12(filePath, "utf8");
|
|
6307
6712
|
} catch {
|
|
6308
6713
|
return void 0;
|
|
6309
6714
|
}
|
|
@@ -6369,7 +6774,7 @@ var AgentHarness = class _AgentHarness {
|
|
|
6369
6774
|
let browserMod;
|
|
6370
6775
|
try {
|
|
6371
6776
|
const { existsSync } = await import("fs");
|
|
6372
|
-
const { join, dirname:
|
|
6777
|
+
const { join, dirname: dirname8 } = await import("path");
|
|
6373
6778
|
const { pathToFileURL: pathToFileURL2 } = await import("url");
|
|
6374
6779
|
let searchDir = this.workingDir;
|
|
6375
6780
|
let entryPath;
|
|
@@ -6379,7 +6784,7 @@ var AgentHarness = class _AgentHarness {
|
|
|
6379
6784
|
entryPath = candidate;
|
|
6380
6785
|
break;
|
|
6381
6786
|
}
|
|
6382
|
-
const parent =
|
|
6787
|
+
const parent = dirname8(searchDir);
|
|
6383
6788
|
if (parent === searchDir) break;
|
|
6384
6789
|
searchDir = parent;
|
|
6385
6790
|
}
|
|
@@ -6484,9 +6889,9 @@ var AgentHarness = class _AgentHarness {
|
|
|
6484
6889
|
for await (const event of this.run(input)) {
|
|
6485
6890
|
eventQueue.push(event);
|
|
6486
6891
|
if (queueResolve) {
|
|
6487
|
-
const
|
|
6892
|
+
const resolve14 = queueResolve;
|
|
6488
6893
|
queueResolve = null;
|
|
6489
|
-
|
|
6894
|
+
resolve14();
|
|
6490
6895
|
}
|
|
6491
6896
|
}
|
|
6492
6897
|
} catch (error) {
|
|
@@ -6505,8 +6910,8 @@ var AgentHarness = class _AgentHarness {
|
|
|
6505
6910
|
if (eventQueue.length > 0) {
|
|
6506
6911
|
yield eventQueue.shift();
|
|
6507
6912
|
} else if (!generatorDone) {
|
|
6508
|
-
await new Promise((
|
|
6509
|
-
queueResolve =
|
|
6913
|
+
await new Promise((resolve14) => {
|
|
6914
|
+
queueResolve = resolve14;
|
|
6510
6915
|
});
|
|
6511
6916
|
}
|
|
6512
6917
|
}
|
|
@@ -6653,7 +7058,10 @@ ${boundedMainMemory.trim()}` : "";
|
|
|
6653
7058
|
const promptWithSkills = this.skillContextWindow ? `${agentPrompt}${developmentContext}
|
|
6654
7059
|
|
|
6655
7060
|
${this.skillContextWindow}${browserContext}` : `${agentPrompt}${developmentContext}${browserContext}`;
|
|
6656
|
-
|
|
7061
|
+
const timeContext = this.reminderStore ? `
|
|
7062
|
+
|
|
7063
|
+
Current UTC time: ${(/* @__PURE__ */ new Date()).toISOString()}` : "";
|
|
7064
|
+
return `${promptWithSkills}${memoryContext}${timeContext}
|
|
6657
7065
|
|
|
6658
7066
|
## Execution Integrity
|
|
6659
7067
|
|
|
@@ -7126,8 +7534,8 @@ ${textContent}` };
|
|
|
7126
7534
|
let timer;
|
|
7127
7535
|
nextPart = await Promise.race([
|
|
7128
7536
|
fullStreamIterator.next(),
|
|
7129
|
-
new Promise((
|
|
7130
|
-
timer = setTimeout(() =>
|
|
7537
|
+
new Promise((resolve14) => {
|
|
7538
|
+
timer = setTimeout(() => resolve14(null), effectiveTimeout);
|
|
7131
7539
|
})
|
|
7132
7540
|
]);
|
|
7133
7541
|
clearTimeout(timer);
|
|
@@ -7462,7 +7870,7 @@ ${textContent}` };
|
|
|
7462
7870
|
const raced = await Promise.race([
|
|
7463
7871
|
this.dispatcher.executeBatch(approvedCalls, toolContext),
|
|
7464
7872
|
new Promise(
|
|
7465
|
-
(
|
|
7873
|
+
(resolve14) => setTimeout(() => resolve14(TOOL_DEADLINE_SENTINEL), toolDeadlineRemainingMs)
|
|
7466
7874
|
)
|
|
7467
7875
|
]);
|
|
7468
7876
|
if (raced === TOOL_DEADLINE_SENTINEL) {
|
|
@@ -7853,8 +8261,8 @@ var LatitudeCapture = class {
|
|
|
7853
8261
|
|
|
7854
8262
|
// src/state.ts
|
|
7855
8263
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
7856
|
-
import { mkdir as
|
|
7857
|
-
import { dirname as
|
|
8264
|
+
import { mkdir as mkdir7, readFile as readFile11, readdir as readdir4, rename as rename4, rm as rm4, writeFile as writeFile8 } from "fs/promises";
|
|
8265
|
+
import { dirname as dirname7, resolve as resolve13 } from "path";
|
|
7858
8266
|
var DEFAULT_OWNER = "local-owner";
|
|
7859
8267
|
var LOCAL_STATE_FILE = "state.json";
|
|
7860
8268
|
var CONVERSATIONS_DIRECTORY = "conversations";
|
|
@@ -7869,11 +8277,11 @@ var toStoreIdentity = async ({
|
|
|
7869
8277
|
}
|
|
7870
8278
|
return { name: ensured.name, id: agentId };
|
|
7871
8279
|
};
|
|
7872
|
-
var
|
|
7873
|
-
await
|
|
8280
|
+
var writeJsonAtomic4 = async (filePath, payload) => {
|
|
8281
|
+
await mkdir7(dirname7(filePath), { recursive: true });
|
|
7874
8282
|
const tmpPath = `${filePath}.tmp`;
|
|
7875
|
-
await
|
|
7876
|
-
await
|
|
8283
|
+
await writeFile8(tmpPath, JSON.stringify(payload, null, 2), "utf8");
|
|
8284
|
+
await rename4(tmpPath, filePath);
|
|
7877
8285
|
};
|
|
7878
8286
|
var formatUtcTimestamp = (value) => new Date(value).toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
|
|
7879
8287
|
var normalizeTitle = (title) => {
|
|
@@ -8061,8 +8469,8 @@ var FileConversationStore = class {
|
|
|
8061
8469
|
agentId: this.agentId
|
|
8062
8470
|
});
|
|
8063
8471
|
const agentDir = getAgentStoreDirectory(identity);
|
|
8064
|
-
const conversationsDir =
|
|
8065
|
-
const indexPath =
|
|
8472
|
+
const conversationsDir = resolve13(agentDir, CONVERSATIONS_DIRECTORY);
|
|
8473
|
+
const indexPath = resolve13(conversationsDir, LOCAL_CONVERSATION_INDEX_FILE);
|
|
8066
8474
|
this.paths = { conversationsDir, indexPath };
|
|
8067
8475
|
return this.paths;
|
|
8068
8476
|
}
|
|
@@ -8072,13 +8480,13 @@ var FileConversationStore = class {
|
|
|
8072
8480
|
schemaVersion: STORAGE_SCHEMA_VERSION,
|
|
8073
8481
|
conversations: Array.from(this.conversations.values()).sort((a, b) => b.updatedAt - a.updatedAt)
|
|
8074
8482
|
};
|
|
8075
|
-
await
|
|
8483
|
+
await writeJsonAtomic4(indexPath, payload);
|
|
8076
8484
|
}
|
|
8077
8485
|
async readConversationFile(fileName) {
|
|
8078
8486
|
const { conversationsDir } = await this.resolvePaths();
|
|
8079
|
-
const filePath =
|
|
8487
|
+
const filePath = resolve13(conversationsDir, fileName);
|
|
8080
8488
|
try {
|
|
8081
|
-
const raw = await
|
|
8489
|
+
const raw = await readFile11(filePath, "utf8");
|
|
8082
8490
|
return JSON.parse(raw);
|
|
8083
8491
|
} catch {
|
|
8084
8492
|
return void 0;
|
|
@@ -8124,7 +8532,7 @@ var FileConversationStore = class {
|
|
|
8124
8532
|
this.loaded = true;
|
|
8125
8533
|
const { indexPath } = await this.resolvePaths();
|
|
8126
8534
|
try {
|
|
8127
|
-
const raw = await
|
|
8535
|
+
const raw = await readFile11(indexPath, "utf8");
|
|
8128
8536
|
const parsed = JSON.parse(raw);
|
|
8129
8537
|
for (const conversation of parsed.conversations ?? []) {
|
|
8130
8538
|
this.conversations.set(conversation.conversationId, conversation);
|
|
@@ -8147,9 +8555,9 @@ var FileConversationStore = class {
|
|
|
8147
8555
|
const { conversationsDir } = await this.resolvePaths();
|
|
8148
8556
|
const existing = this.conversations.get(conversation.conversationId);
|
|
8149
8557
|
const fileName = existing?.fileName ?? this.resolveConversationFileName(conversation);
|
|
8150
|
-
const filePath =
|
|
8558
|
+
const filePath = resolve13(conversationsDir, fileName);
|
|
8151
8559
|
this.writing = this.writing.then(async () => {
|
|
8152
|
-
await
|
|
8560
|
+
await writeJsonAtomic4(filePath, conversation);
|
|
8153
8561
|
this.conversations.set(conversation.conversationId, {
|
|
8154
8562
|
conversationId: conversation.conversationId,
|
|
8155
8563
|
title: conversation.title,
|
|
@@ -8245,7 +8653,7 @@ var FileConversationStore = class {
|
|
|
8245
8653
|
if (removed) {
|
|
8246
8654
|
this.writing = this.writing.then(async () => {
|
|
8247
8655
|
if (existing) {
|
|
8248
|
-
await rm4(
|
|
8656
|
+
await rm4(resolve13(conversationsDir, existing.fileName), { force: true });
|
|
8249
8657
|
}
|
|
8250
8658
|
await this.writeIndex();
|
|
8251
8659
|
});
|
|
@@ -8267,14 +8675,14 @@ var FileConversationStore = class {
|
|
|
8267
8675
|
const summary = this.conversations.get(conversationId);
|
|
8268
8676
|
if (!summary) return void 0;
|
|
8269
8677
|
const { conversationsDir } = await this.resolvePaths();
|
|
8270
|
-
const filePath =
|
|
8678
|
+
const filePath = resolve13(conversationsDir, summary.fileName);
|
|
8271
8679
|
let result;
|
|
8272
8680
|
this.writing = this.writing.then(async () => {
|
|
8273
8681
|
const conv = await this.readConversationFile(summary.fileName);
|
|
8274
8682
|
if (!conv) return;
|
|
8275
8683
|
conv.runningCallbackSince = void 0;
|
|
8276
8684
|
conv.updatedAt = Date.now();
|
|
8277
|
-
await
|
|
8685
|
+
await writeJsonAtomic4(filePath, conv);
|
|
8278
8686
|
this.conversations.set(conversationId, {
|
|
8279
8687
|
...summary,
|
|
8280
8688
|
updatedAt: conv.updatedAt
|
|
@@ -8306,7 +8714,7 @@ var FileStateStore = class {
|
|
|
8306
8714
|
workingDir: this.workingDir,
|
|
8307
8715
|
agentId: this.agentId
|
|
8308
8716
|
});
|
|
8309
|
-
this.filePath =
|
|
8717
|
+
this.filePath = resolve13(getAgentStoreDirectory(identity), LOCAL_STATE_FILE);
|
|
8310
8718
|
}
|
|
8311
8719
|
isExpired(state) {
|
|
8312
8720
|
return typeof this.ttlMs === "number" && Date.now() - state.updatedAt > this.ttlMs;
|
|
@@ -8318,7 +8726,7 @@ var FileStateStore = class {
|
|
|
8318
8726
|
}
|
|
8319
8727
|
this.loaded = true;
|
|
8320
8728
|
try {
|
|
8321
|
-
const raw = await
|
|
8729
|
+
const raw = await readFile11(this.filePath, "utf8");
|
|
8322
8730
|
const parsed = JSON.parse(raw);
|
|
8323
8731
|
for (const state of parsed.states ?? []) {
|
|
8324
8732
|
this.states.set(state.runId, state);
|
|
@@ -8331,7 +8739,7 @@ var FileStateStore = class {
|
|
|
8331
8739
|
states: Array.from(this.states.values())
|
|
8332
8740
|
};
|
|
8333
8741
|
this.writing = this.writing.then(async () => {
|
|
8334
|
-
await
|
|
8742
|
+
await writeJsonAtomic4(this.filePath, payload);
|
|
8335
8743
|
});
|
|
8336
8744
|
await this.writing;
|
|
8337
8745
|
}
|
|
@@ -9019,7 +9427,7 @@ var createConversationStore = (config, options) => {
|
|
|
9019
9427
|
};
|
|
9020
9428
|
|
|
9021
9429
|
// src/index.ts
|
|
9022
|
-
import { defineTool as
|
|
9430
|
+
import { defineTool as defineTool9 } from "@poncho-ai/sdk";
|
|
9023
9431
|
export {
|
|
9024
9432
|
AgentHarness,
|
|
9025
9433
|
InMemoryConversationStore,
|
|
@@ -9046,13 +9454,15 @@ export {
|
|
|
9046
9454
|
createMemoryStore,
|
|
9047
9455
|
createMemoryTools,
|
|
9048
9456
|
createModelProvider,
|
|
9457
|
+
createReminderStore,
|
|
9458
|
+
createReminderTools,
|
|
9049
9459
|
createSearchTools,
|
|
9050
9460
|
createSkillTools,
|
|
9051
9461
|
createStateStore,
|
|
9052
9462
|
createSubagentTools,
|
|
9053
9463
|
createUploadStore,
|
|
9054
9464
|
createWriteTool,
|
|
9055
|
-
|
|
9465
|
+
defineTool9 as defineTool,
|
|
9056
9466
|
deleteOpenAICodexSession,
|
|
9057
9467
|
deriveUploadKey,
|
|
9058
9468
|
ensureAgentIdentity,
|