@letta-ai/letta-code 0.30.0 → 0.30.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.
@@ -0,0 +1,189 @@
1
+ // src/cron/scheduled-task-prompt.ts
2
+ var SYSTEM_REMINDER_OPEN = "<system-reminder>";
3
+ var SYSTEM_REMINDER_CLOSE = "</system-reminder>";
4
+ var AUTONOMOUS_NOTICE_PREFIX = "You are running autonomously:";
5
+ var AUTONOMOUS_NOTICE = "You are running autonomously: no user is watching this turn and questions will not be answered. Deliver results through your available channels or record them in memory, and work until the task is done or genuinely blocked.";
6
+ function pad(value, width) {
7
+ return String(value).padStart(width, "0");
8
+ }
9
+ function formatOffset(minutes) {
10
+ const sign = minutes >= 0 ? "+" : "-";
11
+ const abs = Math.abs(minutes);
12
+ return `${sign}${pad(Math.floor(abs / 60), 2)}:${pad(abs % 60, 2)}`;
13
+ }
14
+ function getLocalDateTimeParts(date) {
15
+ return {
16
+ year: date.getFullYear(),
17
+ month: date.getMonth() + 1,
18
+ day: date.getDate(),
19
+ hour: date.getHours(),
20
+ minute: date.getMinutes(),
21
+ second: date.getSeconds()
22
+ };
23
+ }
24
+ function isValidTimezone(timezone) {
25
+ try {
26
+ new Intl.DateTimeFormat("en-US", { timeZone: timezone });
27
+ return true;
28
+ } catch {
29
+ return false;
30
+ }
31
+ }
32
+ function getSystemTimezone() {
33
+ const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
34
+ if (!timezone || !isValidTimezone(timezone)) {
35
+ return null;
36
+ }
37
+ return timezone;
38
+ }
39
+ function getEffectiveTimezone(timezone) {
40
+ const trimmed = timezone.trim();
41
+ if (trimmed && isValidTimezone(trimmed)) {
42
+ return trimmed;
43
+ }
44
+ return getSystemTimezone();
45
+ }
46
+ function formatTimezoneDisplay(timezone) {
47
+ const trimmed = timezone.trim();
48
+ if (!trimmed) {
49
+ return "local time";
50
+ }
51
+ if (isValidTimezone(trimmed)) {
52
+ return trimmed;
53
+ }
54
+ return `${trimmed} (invalid; using local time)`;
55
+ }
56
+ function getZonedDateTimeParts(date, timezone) {
57
+ const formatter = new Intl.DateTimeFormat("en-US", {
58
+ timeZone: timezone,
59
+ calendar: "iso8601",
60
+ numberingSystem: "latn",
61
+ hourCycle: "h23",
62
+ year: "numeric",
63
+ month: "2-digit",
64
+ day: "2-digit",
65
+ hour: "2-digit",
66
+ minute: "2-digit",
67
+ second: "2-digit"
68
+ });
69
+ const parts = new Map(formatter.formatToParts(date).map((part) => [part.type, part.value]));
70
+ return {
71
+ year: Number.parseInt(parts.get("year") ?? "0", 10),
72
+ month: Number.parseInt(parts.get("month") ?? "1", 10),
73
+ day: Number.parseInt(parts.get("day") ?? "1", 10),
74
+ hour: Number.parseInt(parts.get("hour") ?? "0", 10),
75
+ minute: Number.parseInt(parts.get("minute") ?? "0", 10),
76
+ second: Number.parseInt(parts.get("second") ?? "0", 10)
77
+ };
78
+ }
79
+ function formatTimezoneQualifiedIso(date, timezone) {
80
+ const effectiveTimezone = getEffectiveTimezone(timezone);
81
+ const parts = effectiveTimezone ? getZonedDateTimeParts(date, effectiveTimezone) : getLocalDateTimeParts(date);
82
+ const millis = date.getMilliseconds();
83
+ const zonedAsUtcMs = Date.UTC(parts.year, parts.month - 1, parts.day, parts.hour, parts.minute, parts.second, millis);
84
+ const offsetMinutes = Math.round((zonedAsUtcMs - date.getTime()) / 60000);
85
+ return `${pad(parts.year, 4)}-${pad(parts.month, 2)}-${pad(parts.day, 2)}T${pad(parts.hour, 2)}:${pad(parts.minute, 2)}:${pad(parts.second, 2)}.${pad(millis, 3)}${formatOffset(offsetMinutes)}[${effectiveTimezone ?? "local"}]`;
86
+ }
87
+ function formatRecurrence(recurrence) {
88
+ if (recurrence.type === "one-off") {
89
+ return "This is a one-off scheduled task.";
90
+ }
91
+ if (recurrence.fireNumber !== undefined) {
92
+ return `This is fire #${recurrence.fireNumber} (cron: ${recurrence.cron}).`;
93
+ }
94
+ return `This is a recurring scheduled task (cron: ${recurrence.cron}).`;
95
+ }
96
+ function formatScheduledTaskPrompt(input) {
97
+ const lines = [
98
+ `Scheduled task "${input.name}" is firing.`,
99
+ ...input.description ? [`Description: ${input.description}`] : [],
100
+ `Timezone: ${formatTimezoneDisplay(input.timezone)}`,
101
+ `Scheduled for: ${formatTimezoneQualifiedIso(input.scheduledFor, input.timezone)}`,
102
+ `Current time: ${formatTimezoneQualifiedIso(input.currentTime, input.timezone)}`,
103
+ formatRecurrence(input.recurrence),
104
+ "",
105
+ AUTONOMOUS_NOTICE,
106
+ "",
107
+ `Prompt: ${input.prompt}`
108
+ ];
109
+ return lines.join(`
110
+ `);
111
+ }
112
+ function unwrapSystemReminder(text) {
113
+ const trimmed = text.trim();
114
+ if (trimmed.startsWith(SYSTEM_REMINDER_OPEN) && trimmed.endsWith(SYSTEM_REMINDER_CLOSE)) {
115
+ return trimmed.slice(SYSTEM_REMINDER_OPEN.length, -SYSTEM_REMINDER_CLOSE.length).trim();
116
+ }
117
+ return trimmed;
118
+ }
119
+ function getField(lines, prefix) {
120
+ const line = lines.find((candidate) => candidate.startsWith(prefix));
121
+ const value = line?.slice(prefix.length).trim();
122
+ return value || null;
123
+ }
124
+ function getPrompt(text, recurrenceLineIndex) {
125
+ const promptMarker = `
126
+ Prompt: `;
127
+ const promptIndex = text.indexOf(promptMarker);
128
+ if (promptIndex >= 0) {
129
+ return text.slice(promptIndex + promptMarker.length).trim() || null;
130
+ }
131
+ const trailingLines = text.split(`
132
+ `).slice(recurrenceLineIndex + 1);
133
+ while (trailingLines[0]?.trim() === "")
134
+ trailingLines.shift();
135
+ if (trailingLines[0]?.startsWith(AUTONOMOUS_NOTICE_PREFIX)) {
136
+ trailingLines.shift();
137
+ while (trailingLines[0]?.trim() === "")
138
+ trailingLines.shift();
139
+ }
140
+ return trailingLines.join(`
141
+ `).trim() || null;
142
+ }
143
+ function parseScheduledTaskPrompt(rawText) {
144
+ const text = unwrapSystemReminder(rawText).replace(/\r\n/g, `
145
+ `);
146
+ const lines = text.split(`
147
+ `);
148
+ const titleMatch = /^Scheduled task ["“](.+)["”] is firing\.$/.exec(lines[0]?.trim() ?? "");
149
+ if (!titleMatch?.[1])
150
+ return null;
151
+ const recurrenceLineIndex = lines.findIndex((line) => {
152
+ const trimmed = line.trim();
153
+ return trimmed === "This is a one-off scheduled task." || /^This is fire #\d+ \(cron: .+\)\.$/.test(trimmed) || /^This is a recurring scheduled task \(cron: .+\)\.$/.test(trimmed);
154
+ });
155
+ if (recurrenceLineIndex < 0)
156
+ return null;
157
+ const recurrenceLine = lines[recurrenceLineIndex]?.trim() ?? "";
158
+ const countedRecurringMatch = /^This is fire #(\d+) \(cron: (.+)\)\.$/.exec(recurrenceLine);
159
+ const recurringMatch = /^This is a recurring scheduled task \(cron: (.+)\)\.$/.exec(recurrenceLine);
160
+ let recurrence;
161
+ if (countedRecurringMatch) {
162
+ recurrence = {
163
+ type: "recurring",
164
+ fireNumber: Number.parseInt(countedRecurringMatch[1] ?? "0", 10),
165
+ cron: countedRecurringMatch[2] ?? ""
166
+ };
167
+ } else if (recurringMatch) {
168
+ recurrence = { type: "recurring", cron: recurringMatch[1] ?? "" };
169
+ } else {
170
+ recurrence = { type: "one-off" };
171
+ }
172
+ const prompt = getPrompt(text, recurrenceLineIndex);
173
+ if (!prompt)
174
+ return null;
175
+ return {
176
+ name: titleMatch[1],
177
+ description: getField(lines, "Description:"),
178
+ timezone: getField(lines, "Timezone:"),
179
+ scheduledFor: getField(lines, "Scheduled for:"),
180
+ recurrence,
181
+ prompt
182
+ };
183
+ }
184
+ export {
185
+ parseScheduledTaskPrompt,
186
+ formatScheduledTaskPrompt
187
+ };
188
+
189
+ //# debugId=07276DD08883835A64756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/cron/scheduled-task-prompt.ts"],
4
+ "sourcesContent": [
5
+ "export type ScheduledTaskRecurrence =\n | { type: \"one-off\" }\n | { type: \"recurring\"; cron: string; fireNumber?: number };\n\nexport interface ScheduledTaskPromptInput {\n name: string;\n description?: string | null;\n timezone: string;\n scheduledFor: Date;\n currentTime: Date;\n recurrence: ScheduledTaskRecurrence;\n prompt: string;\n}\n\nexport interface ScheduledTaskPromptInfo {\n name: string;\n description: string | null;\n timezone: string | null;\n scheduledFor: string | null;\n recurrence: ScheduledTaskRecurrence;\n prompt: string;\n}\n\ninterface ZonedDateTimeParts {\n year: number;\n month: number;\n day: number;\n hour: number;\n minute: number;\n second: number;\n}\n\nconst SYSTEM_REMINDER_OPEN = \"<system-reminder>\";\nconst SYSTEM_REMINDER_CLOSE = \"</system-reminder>\";\nconst AUTONOMOUS_NOTICE_PREFIX = \"You are running autonomously:\";\nconst AUTONOMOUS_NOTICE =\n \"You are running autonomously: no user is watching this turn and questions will not be answered. Deliver results through your available channels or record them in memory, and work until the task is done or genuinely blocked.\";\n\nfunction pad(value: number, width: number): string {\n return String(value).padStart(width, \"0\");\n}\n\nfunction formatOffset(minutes: number): string {\n const sign = minutes >= 0 ? \"+\" : \"-\";\n const abs = Math.abs(minutes);\n return `${sign}${pad(Math.floor(abs / 60), 2)}:${pad(abs % 60, 2)}`;\n}\n\nfunction getLocalDateTimeParts(date: Date): ZonedDateTimeParts {\n return {\n year: date.getFullYear(),\n month: date.getMonth() + 1,\n day: date.getDate(),\n hour: date.getHours(),\n minute: date.getMinutes(),\n second: date.getSeconds(),\n };\n}\n\nfunction isValidTimezone(timezone: string): boolean {\n try {\n new Intl.DateTimeFormat(\"en-US\", { timeZone: timezone });\n return true;\n } catch {\n return false;\n }\n}\n\nfunction getSystemTimezone(): string | null {\n const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;\n if (!timezone || !isValidTimezone(timezone)) {\n return null;\n }\n return timezone;\n}\n\nfunction getEffectiveTimezone(timezone: string): string | null {\n const trimmed = timezone.trim();\n if (trimmed && isValidTimezone(trimmed)) {\n return trimmed;\n }\n return getSystemTimezone();\n}\n\nfunction formatTimezoneDisplay(timezone: string): string {\n const trimmed = timezone.trim();\n if (!trimmed) {\n return \"local time\";\n }\n if (isValidTimezone(trimmed)) {\n return trimmed;\n }\n return `${trimmed} (invalid; using local time)`;\n}\n\nfunction getZonedDateTimeParts(\n date: Date,\n timezone: string,\n): ZonedDateTimeParts {\n const formatter = new Intl.DateTimeFormat(\"en-US\", {\n timeZone: timezone,\n calendar: \"iso8601\",\n numberingSystem: \"latn\",\n hourCycle: \"h23\",\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n });\n const parts = new Map(\n formatter.formatToParts(date).map((part) => [part.type, part.value]),\n );\n\n return {\n year: Number.parseInt(parts.get(\"year\") ?? \"0\", 10),\n month: Number.parseInt(parts.get(\"month\") ?? \"1\", 10),\n day: Number.parseInt(parts.get(\"day\") ?? \"1\", 10),\n hour: Number.parseInt(parts.get(\"hour\") ?? \"0\", 10),\n minute: Number.parseInt(parts.get(\"minute\") ?? \"0\", 10),\n second: Number.parseInt(parts.get(\"second\") ?? \"0\", 10),\n };\n}\n\nexport function formatTimezoneQualifiedIso(\n date: Date,\n timezone: string,\n): string {\n const effectiveTimezone = getEffectiveTimezone(timezone);\n const parts = effectiveTimezone\n ? getZonedDateTimeParts(date, effectiveTimezone)\n : getLocalDateTimeParts(date);\n const millis = date.getMilliseconds();\n const zonedAsUtcMs = Date.UTC(\n parts.year,\n parts.month - 1,\n parts.day,\n parts.hour,\n parts.minute,\n parts.second,\n millis,\n );\n const offsetMinutes = Math.round((zonedAsUtcMs - date.getTime()) / 60_000);\n\n return `${pad(parts.year, 4)}-${pad(parts.month, 2)}-${pad(parts.day, 2)}T${pad(parts.hour, 2)}:${pad(parts.minute, 2)}:${pad(parts.second, 2)}.${pad(millis, 3)}${formatOffset(offsetMinutes)}[${effectiveTimezone ?? \"local\"}]`;\n}\n\nfunction formatRecurrence(recurrence: ScheduledTaskRecurrence): string {\n if (recurrence.type === \"one-off\") {\n return \"This is a one-off scheduled task.\";\n }\n if (recurrence.fireNumber !== undefined) {\n return `This is fire #${recurrence.fireNumber} (cron: ${recurrence.cron}).`;\n }\n return `This is a recurring scheduled task (cron: ${recurrence.cron}).`;\n}\n\n/**\n * Build the canonical prompt envelope for a scheduled agent turn.\n *\n * Every schedule runner should call this immediately before dispatch while\n * keeping its stored authored prompt separate from runtime metadata.\n */\nexport function formatScheduledTaskPrompt(\n input: ScheduledTaskPromptInput,\n): string {\n const lines = [\n `Scheduled task \"${input.name}\" is firing.`,\n ...(input.description ? [`Description: ${input.description}`] : []),\n `Timezone: ${formatTimezoneDisplay(input.timezone)}`,\n `Scheduled for: ${formatTimezoneQualifiedIso(input.scheduledFor, input.timezone)}`,\n `Current time: ${formatTimezoneQualifiedIso(input.currentTime, input.timezone)}`,\n formatRecurrence(input.recurrence),\n \"\",\n AUTONOMOUS_NOTICE,\n \"\",\n `Prompt: ${input.prompt}`,\n ];\n return lines.join(\"\\n\");\n}\n\nfunction unwrapSystemReminder(text: string): string {\n const trimmed = text.trim();\n if (\n trimmed.startsWith(SYSTEM_REMINDER_OPEN) &&\n trimmed.endsWith(SYSTEM_REMINDER_CLOSE)\n ) {\n return trimmed\n .slice(SYSTEM_REMINDER_OPEN.length, -SYSTEM_REMINDER_CLOSE.length)\n .trim();\n }\n return trimmed;\n}\n\nfunction getField(lines: string[], prefix: string): string | null {\n const line = lines.find((candidate) => candidate.startsWith(prefix));\n const value = line?.slice(prefix.length).trim();\n return value || null;\n}\n\nfunction getPrompt(text: string, recurrenceLineIndex: number): string | null {\n const promptMarker = \"\\nPrompt: \";\n const promptIndex = text.indexOf(promptMarker);\n if (promptIndex >= 0) {\n return text.slice(promptIndex + promptMarker.length).trim() || null;\n }\n\n const trailingLines = text.split(\"\\n\").slice(recurrenceLineIndex + 1);\n while (trailingLines[0]?.trim() === \"\") trailingLines.shift();\n if (trailingLines[0]?.startsWith(AUTONOMOUS_NOTICE_PREFIX)) {\n trailingLines.shift();\n while (trailingLines[0]?.trim() === \"\") trailingLines.shift();\n }\n return trailingLines.join(\"\\n\").trim() || null;\n}\n\n/** Parse canonical and legacy scheduled-task envelopes from persisted messages. */\nexport function parseScheduledTaskPrompt(\n rawText: string,\n): ScheduledTaskPromptInfo | null {\n const text = unwrapSystemReminder(rawText).replace(/\\r\\n/g, \"\\n\");\n const lines = text.split(\"\\n\");\n const titleMatch = /^Scheduled task [\"“](.+)[\"”] is firing\\.$/.exec(\n lines[0]?.trim() ?? \"\",\n );\n if (!titleMatch?.[1]) return null;\n\n const recurrenceLineIndex = lines.findIndex((line) => {\n const trimmed = line.trim();\n return (\n trimmed === \"This is a one-off scheduled task.\" ||\n /^This is fire #\\d+ \\(cron: .+\\)\\.$/.test(trimmed) ||\n /^This is a recurring scheduled task \\(cron: .+\\)\\.$/.test(trimmed)\n );\n });\n if (recurrenceLineIndex < 0) return null;\n\n const recurrenceLine = lines[recurrenceLineIndex]?.trim() ?? \"\";\n const countedRecurringMatch = /^This is fire #(\\d+) \\(cron: (.+)\\)\\.$/.exec(\n recurrenceLine,\n );\n const recurringMatch =\n /^This is a recurring scheduled task \\(cron: (.+)\\)\\.$/.exec(\n recurrenceLine,\n );\n let recurrence: ScheduledTaskRecurrence;\n if (countedRecurringMatch) {\n recurrence = {\n type: \"recurring\",\n fireNumber: Number.parseInt(countedRecurringMatch[1] ?? \"0\", 10),\n cron: countedRecurringMatch[2] ?? \"\",\n };\n } else if (recurringMatch) {\n recurrence = { type: \"recurring\", cron: recurringMatch[1] ?? \"\" };\n } else {\n recurrence = { type: \"one-off\" };\n }\n\n const prompt = getPrompt(text, recurrenceLineIndex);\n if (!prompt) return null;\n\n return {\n name: titleMatch[1],\n description: getField(lines, \"Description:\"),\n timezone: getField(lines, \"Timezone:\"),\n scheduledFor: getField(lines, \"Scheduled for:\"),\n recurrence,\n prompt,\n };\n}\n"
6
+ ],
7
+ "mappings": "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",
8
+ "debugId": "07276DD08883835A64756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,35 @@
1
+ export type ScheduledTaskRecurrence = {
2
+ type: "one-off";
3
+ } | {
4
+ type: "recurring";
5
+ cron: string;
6
+ fireNumber?: number;
7
+ };
8
+ export interface ScheduledTaskPromptInput {
9
+ name: string;
10
+ description?: string | null;
11
+ timezone: string;
12
+ scheduledFor: Date;
13
+ currentTime: Date;
14
+ recurrence: ScheduledTaskRecurrence;
15
+ prompt: string;
16
+ }
17
+ export interface ScheduledTaskPromptInfo {
18
+ name: string;
19
+ description: string | null;
20
+ timezone: string | null;
21
+ scheduledFor: string | null;
22
+ recurrence: ScheduledTaskRecurrence;
23
+ prompt: string;
24
+ }
25
+ export declare function formatTimezoneQualifiedIso(date: Date, timezone: string): string;
26
+ /**
27
+ * Build the canonical prompt envelope for a scheduled agent turn.
28
+ *
29
+ * Every schedule runner should call this immediately before dispatch while
30
+ * keeping its stored authored prompt separate from runtime metadata.
31
+ */
32
+ export declare function formatScheduledTaskPrompt(input: ScheduledTaskPromptInput): string;
33
+ /** Parse canonical and legacy scheduled-task envelopes from persisted messages. */
34
+ export declare function parseScheduledTaskPrompt(rawText: string): ScheduledTaskPromptInfo | null;
35
+ //# sourceMappingURL=scheduled-task-prompt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduled-task-prompt.d.ts","sourceRoot":"","sources":["../../../src/cron/scheduled-task-prompt.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,uBAAuB,GAC/B;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7D,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,IAAI,CAAC;IACnB,WAAW,EAAE,IAAI,CAAC;IAClB,UAAU,EAAE,uBAAuB,CAAC;IACpC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,uBAAuB,CAAC;IACpC,MAAM,EAAE,MAAM,CAAC;CAChB;AAwGD,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,MAAM,GACf,MAAM,CAkBR;AAYD;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,wBAAwB,GAC9B,MAAM,CAcR;AAqCD,mFAAmF;AACnF,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,GACd,uBAAuB,GAAG,IAAI,CAkDhC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Package export: `@letta-ai/letta-code/schedules`
3
+ *
4
+ * Pure scheduled-turn envelope contract shared by every scheduler producer and
5
+ * transcript consumer. This entry must remain free of Node and backend imports.
6
+ */
7
+ export { formatScheduledTaskPrompt, parseScheduledTaskPrompt, type ScheduledTaskPromptInfo, type ScheduledTaskPromptInput, type ScheduledTaskRecurrence, } from "./cron/scheduled-task-prompt";
8
+ //# sourceMappingURL=schedules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schedules.d.ts","sourceRoot":"","sources":["../../src/schedules.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,yBAAyB,EACzB,wBAAwB,EACxB,KAAK,uBAAuB,EAC5B,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,GAC7B,MAAM,8BAA8B,CAAC"}
package/letta.js CHANGED
@@ -5462,7 +5462,7 @@ var package_default;
5462
5462
  var init_package = __esm(() => {
5463
5463
  package_default = {
5464
5464
  name: "@letta-ai/letta-code",
5465
- version: "0.30.0",
5465
+ version: "0.30.1",
5466
5466
  description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
5467
5467
  type: "module",
5468
5468
  packageManager: "bun@1.3.0",
@@ -5488,6 +5488,8 @@ var init_package = __esm(() => {
5488
5488
  "dist/mcp-client.js.map",
5489
5489
  "dist/agent-presets.js",
5490
5490
  "dist/agent-presets.js.map",
5491
+ "dist/schedules.js",
5492
+ "dist/schedules.js.map",
5491
5493
  "dist/channels-public.js",
5492
5494
  "dist/channels-public.js.map",
5493
5495
  "dist/channels-slack.js",
@@ -5532,6 +5534,12 @@ var init_package = __esm(() => {
5532
5534
  import: "./dist/agent-presets.js",
5533
5535
  default: "./dist/agent-presets.js"
5534
5536
  },
5537
+ "./schedules": {
5538
+ types: "./dist/types/schedules.d.ts",
5539
+ browser: "./dist/schedules.js",
5540
+ import: "./dist/schedules.js",
5541
+ default: "./dist/schedules.js"
5542
+ },
5535
5543
  "./channels": {
5536
5544
  types: "./dist/types/channels-public.d.ts",
5537
5545
  browser: "./dist/channels-public.js",
@@ -5634,6 +5642,9 @@ var init_package = __esm(() => {
5634
5642
  "agent-presets": [
5635
5643
  "./dist/types/agent-presets.d.ts"
5636
5644
  ],
5645
+ schedules: [
5646
+ "./dist/types/schedules.d.ts"
5647
+ ],
5637
5648
  "app-server-protocol": [
5638
5649
  "./dist/types/types/app-server-protocol.d.ts"
5639
5650
  ],
@@ -437141,7 +437152,7 @@ var init_conversation_runtime = __esm(async () => {
437141
437152
  ]);
437142
437153
  });
437143
437154
 
437144
- // src/cron/prompt.ts
437155
+ // src/cron/scheduled-task-prompt.ts
437145
437156
  function pad(value, width) {
437146
437157
  return String(value).padStart(width, "0");
437147
437158
  }
@@ -437223,6 +437234,34 @@ function formatTimezoneQualifiedIso(date6, timezone) {
437223
437234
  const offsetMinutes = Math.round((zonedAsUtcMs - date6.getTime()) / 60000);
437224
437235
  return `${pad(parts.year, 4)}-${pad(parts.month, 2)}-${pad(parts.day, 2)}T${pad(parts.hour, 2)}:${pad(parts.minute, 2)}:${pad(parts.second, 2)}.${pad(millis, 3)}${formatOffset(offsetMinutes)}[${effectiveTimezone ?? "local"}]`;
437225
437236
  }
437237
+ function formatRecurrence(recurrence) {
437238
+ if (recurrence.type === "one-off") {
437239
+ return "This is a one-off scheduled task.";
437240
+ }
437241
+ if (recurrence.fireNumber !== undefined) {
437242
+ return `This is fire #${recurrence.fireNumber} (cron: ${recurrence.cron}).`;
437243
+ }
437244
+ return `This is a recurring scheduled task (cron: ${recurrence.cron}).`;
437245
+ }
437246
+ function formatScheduledTaskPrompt(input) {
437247
+ const lines = [
437248
+ `Scheduled task "${input.name}" is firing.`,
437249
+ ...input.description ? [`Description: ${input.description}`] : [],
437250
+ `Timezone: ${formatTimezoneDisplay(input.timezone)}`,
437251
+ `Scheduled for: ${formatTimezoneQualifiedIso(input.scheduledFor, input.timezone)}`,
437252
+ `Current time: ${formatTimezoneQualifiedIso(input.currentTime, input.timezone)}`,
437253
+ formatRecurrence(input.recurrence),
437254
+ "",
437255
+ AUTONOMOUS_NOTICE,
437256
+ "",
437257
+ `Prompt: ${input.prompt}`
437258
+ ];
437259
+ return lines.join(`
437260
+ `);
437261
+ }
437262
+ var AUTONOMOUS_NOTICE = "You are running autonomously: no user is watching this turn and questions will not be answered. Deliver results through your available channels or record them in memory, and work until the task is done or genuinely blocked.";
437263
+
437264
+ // src/cron/prompt.ts
437226
437265
  function getIntendedCronOccurrence(task2, matchedAt) {
437227
437266
  if (!task2.recurring && task2.scheduled_for) {
437228
437267
  const scheduledFor = new Date(task2.scheduled_for);
@@ -437235,22 +437274,21 @@ function getIntendedCronOccurrence(task2, matchedAt) {
437235
437274
  return occurrence;
437236
437275
  }
437237
437276
  function formatCronPrompt(task2, timing) {
437238
- const timezone = typeof task2.timezone === "string" ? task2.timezone : "";
437239
- const lines = [
437240
- `Scheduled task "${task2.name}" is firing.`,
437241
- `Description: ${task2.description}`,
437242
- `Timezone: ${formatTimezoneDisplay(timezone)}`,
437243
- `Scheduled for: ${formatTimezoneQualifiedIso(timing.intendedOccurrence, timezone)}`,
437244
- `Current time: ${formatTimezoneQualifiedIso(timing.schedulerNow, timezone)}`,
437245
- task2.recurring ? `This is fire #${task2.fire_count + 1} (cron: ${task2.cron}).` : "This is a one-off scheduled task.",
437246
- "",
437247
- "You are running autonomously: no user is watching this turn and questions will not be answered. Deliver results through your available channels or record them in memory, and work until the task is done or genuinely blocked.",
437248
- "",
437249
- `Prompt: ${task2.prompt}`
437250
- ];
437251
- return lines.join(`
437252
- `);
437253
- }
437277
+ return formatScheduledTaskPrompt({
437278
+ name: task2.name,
437279
+ description: task2.description,
437280
+ timezone: typeof task2.timezone === "string" ? task2.timezone : "",
437281
+ scheduledFor: timing.intendedOccurrence,
437282
+ currentTime: timing.schedulerNow,
437283
+ recurrence: task2.recurring ? {
437284
+ type: "recurring",
437285
+ cron: task2.cron,
437286
+ fireNumber: task2.fire_count + 1
437287
+ } : { type: "one-off" },
437288
+ prompt: task2.prompt
437289
+ });
437290
+ }
437291
+ var init_prompt = () => {};
437254
437292
 
437255
437293
  // src/cron/scheduler.ts
437256
437294
  function minuteKey(date6) {
@@ -437671,7 +437709,9 @@ var init_scheduler = __esm(async () => {
437671
437709
  init_runtime6();
437672
437710
  init_cron_file();
437673
437711
  init_parse_interval();
437712
+ init_prompt();
437674
437713
  init_run_log();
437714
+ init_prompt();
437675
437715
  await __promiseAll([
437676
437716
  init_conversation_runtime(),
437677
437717
  init_protocol_outbound(),
@@ -488523,7 +488563,7 @@ var init_mcp_client = __esm(() => {
488523
488563
  init_streamableHttp();
488524
488564
  DEFAULT_CLIENT_INFO = {
488525
488565
  name: "letta-code",
488526
- version: "0.30.0"
488566
+ version: "0.30.1"
488527
488567
  };
488528
488568
  });
488529
488569
 
@@ -555546,4 +555586,4 @@ function registerBunOAuthFlows() {
555546
555586
  registerBunOAuthFlows();
555547
555587
  await init_src5().then(() => exports_src2);
555548
555588
 
555549
- //# debugId=B9D60EB55EA6EE2C64756E2164756E21
555589
+ //# debugId=176DD519C90983F764756E2164756E21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letta-ai/letta-code",
3
- "version": "0.30.0",
3
+ "version": "0.30.1",
4
4
  "description": "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
5
5
  "type": "module",
6
6
  "packageManager": "bun@1.3.0",
@@ -26,6 +26,8 @@
26
26
  "dist/mcp-client.js.map",
27
27
  "dist/agent-presets.js",
28
28
  "dist/agent-presets.js.map",
29
+ "dist/schedules.js",
30
+ "dist/schedules.js.map",
29
31
  "dist/channels-public.js",
30
32
  "dist/channels-public.js.map",
31
33
  "dist/channels-slack.js",
@@ -70,6 +72,12 @@
70
72
  "import": "./dist/agent-presets.js",
71
73
  "default": "./dist/agent-presets.js"
72
74
  },
75
+ "./schedules": {
76
+ "types": "./dist/types/schedules.d.ts",
77
+ "browser": "./dist/schedules.js",
78
+ "import": "./dist/schedules.js",
79
+ "default": "./dist/schedules.js"
80
+ },
73
81
  "./channels": {
74
82
  "types": "./dist/types/channels-public.d.ts",
75
83
  "browser": "./dist/channels-public.js",
@@ -172,6 +180,9 @@
172
180
  "agent-presets": [
173
181
  "./dist/types/agent-presets.d.ts"
174
182
  ],
183
+ "schedules": [
184
+ "./dist/types/schedules.d.ts"
185
+ ],
175
186
  "app-server-protocol": [
176
187
  "./dist/types/types/app-server-protocol.d.ts"
177
188
  ],