zeitlich 0.2.9 → 0.2.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,35 +1,23 @@
1
- import { setHandler, defineUpdate, condition, proxyActivities, defineQuery, uuid4, workflowInfo, executeChild } from '@temporalio/workflow';
2
- import z13, { z } from 'zod';
1
+ import { setHandler, defineUpdate, ApplicationFailure, condition, proxyActivities, defineQuery, uuid4, workflowInfo, executeChild } from '@temporalio/workflow';
2
+ import z14, { z } from 'zod';
3
3
  import { SimplePlugin } from '@temporalio/plugin';
4
4
  import { mapStoredMessagesToChatMessages, ToolMessage, AIMessage, SystemMessage, HumanMessage } from '@langchain/core/messages';
5
- import crypto from 'crypto';
5
+ import { randomUUID, randomFillSync } from 'crypto';
6
6
  import { Context } from '@temporalio/activity';
7
7
  import { Bash } from 'just-bash';
8
+ import { readFile, readdir } from 'fs/promises';
9
+ import { join } from 'path';
8
10
 
9
11
  // src/lib/session.ts
10
12
  var SUBAGENT_TOOL_NAME = "Subagent";
11
13
  function buildSubagentDescription(subagents) {
12
- const subagentList = subagents.map((s) => `- **${s.agentName}**: ${s.description}`).join("\n");
13
- return `Launch a new agent to handle complex tasks autonomously.
14
-
15
- The ${SUBAGENT_TOOL_NAME} tool launches specialized agents (subprocesses) that autonomously handle complex tasks. Each agent type has specific capabilities and tools available to it.
16
-
17
- Available agent types:
14
+ const subagentList = subagents.map((s) => `## ${s.agentName}
15
+ ${s.description}`).join("\n\n");
16
+ return `The ${SUBAGENT_TOOL_NAME} tool launches specialized agents (subagents) that autonomously handle complex work. Each agent type has specific capabilities and tools available to it.
18
17
 
18
+ # Available subagents:
19
19
  ${subagentList}
20
-
21
- When using the ${SUBAGENT_TOOL_NAME} tool, you must specify a subagent parameter to select which agent type to use.
22
-
23
- Usage notes:
24
-
25
- - Always include a short description (3-5 words) summarizing what the agent will do
26
- - Launch multiple agents concurrently whenever possible, to maximize performance; to do that, use a single message with multiple tool uses
27
- - When the agent is done, it will return a single message back to you.
28
- - Each invocation starts fresh - provide a detailed task description with all necessary context.
29
- - Provide clear, detailed prompts so the agent can work autonomously and return exactly the information you need.
30
- - The agent's outputs should generally be trusted
31
- - Clearly tell the agent what type of work you expect since it is not aware of the user's intent
32
- - If the agent description mentions that it should be used proactively, then you should try your best to use it without the user having to ask for it first. Use your judgement.`;
20
+ `;
33
21
  }
34
22
  function createSubagentTool(subagents) {
35
23
  if (subagents.length === 0) {
@@ -39,10 +27,10 @@ function createSubagentTool(subagents) {
39
27
  return {
40
28
  name: SUBAGENT_TOOL_NAME,
41
29
  description: buildSubagentDescription(subagents),
42
- schema: z13.object({
43
- subagent: z13.enum(names).describe("The type of subagent to launch"),
44
- description: z13.string().describe("A short (3-5 word) description of the task"),
45
- prompt: z13.string().describe("The task for the agent to perform")
30
+ schema: z14.object({
31
+ subagent: z14.enum(names).describe("The type of subagent to launch"),
32
+ description: z14.string().describe("A short (3-5 word) description of the task"),
33
+ prompt: z14.string().describe("The task for the agent to perform")
46
34
  })
47
35
  };
48
36
  }
@@ -66,23 +54,77 @@ function createSubagentHandler(subagents) {
66
54
  taskQueue: config.taskQueue ?? parentTaskQueue
67
55
  };
68
56
  const { toolResponse, data, usage } = typeof config.workflow === "string" ? await executeChild(config.workflow, childOpts) : await executeChild(config.workflow, childOpts);
69
- const validated = config.resultSchema ? config.resultSchema.parse(data) : null;
57
+ if (!toolResponse) {
58
+ return {
59
+ toolResponse: "Subagent workflow returned no response",
60
+ data: null,
61
+ ...usage && { usage }
62
+ };
63
+ }
64
+ const validated = config.resultSchema ? config.resultSchema.safeParse(data) : null;
65
+ if (validated && !validated.success) {
66
+ return {
67
+ toolResponse: `Subagent workflow returned invalid data: ${validated.error.message}`,
68
+ data: null,
69
+ ...usage && { usage }
70
+ };
71
+ }
70
72
  return {
71
73
  toolResponse,
72
- data: validated,
74
+ data: validated ? validated.data : data,
73
75
  ...usage && { usage }
74
76
  };
75
77
  };
76
78
  }
79
+ var READ_SKILL_TOOL_NAME = "ReadSkill";
80
+ function buildReadSkillDescription(skills) {
81
+ const skillList = skills.map((s) => `- **${s.name}**: ${s.description}`).join("\n");
82
+ return `Load the full instructions for a skill. Read the skill before following its instructions.
77
83
 
78
- // src/lib/tool-router.ts
84
+ # Available skills:
85
+ ${skillList}
86
+ `;
87
+ }
88
+ function createReadSkillTool(skills) {
89
+ if (skills.length === 0) {
90
+ throw new Error("createReadSkillTool requires at least one skill");
91
+ }
92
+ const names = skills.map((s) => s.name);
93
+ return {
94
+ name: READ_SKILL_TOOL_NAME,
95
+ description: buildReadSkillDescription(skills),
96
+ schema: z14.object({
97
+ skill_name: z14.enum(names).describe("The name of the skill to load")
98
+ })
99
+ };
100
+ }
101
+
102
+ // src/tools/read-skill/handler.ts
103
+ function createReadSkillHandler(skills) {
104
+ const skillMap = new Map(skills.map((s) => [s.name, s]));
105
+ return (args) => {
106
+ const skill = skillMap.get(args.skill_name);
107
+ if (!skill) {
108
+ return {
109
+ toolResponse: JSON.stringify({
110
+ error: `Skill "${args.skill_name}" not found`
111
+ }),
112
+ data: null
113
+ };
114
+ }
115
+ return {
116
+ toolResponse: skill.instructions,
117
+ data: null
118
+ };
119
+ };
120
+ }
79
121
  function createToolRouter(options) {
80
122
  const { appendToolResult } = options;
81
123
  const toolMap = /* @__PURE__ */ new Map();
82
124
  for (const [_key, tool] of Object.entries(options.tools)) {
83
125
  toolMap.set(tool.name, tool);
84
126
  }
85
- const isEnabled = (tool) => tool.enabled?.() ?? true;
127
+ const isEnabled = (tool) => tool.enabled ?? true;
86
128
  if (options.subagents) {
87
129
  if (options.subagents.length > 0) {
88
130
  const subagentHooksMap = /* @__PURE__ */ new Map();
@@ -112,6 +154,12 @@ function createToolRouter(options) {
112
154
  });
113
155
  }
114
156
  }
157
+ if (options.skills && options.skills.length > 0) {
158
+ toolMap.set(READ_SKILL_TOOL_NAME, {
159
+ ...createReadSkillTool(options.skills),
160
+ handler: createReadSkillHandler(options.skills)
161
+ });
162
+ }
115
163
  async function processToolCall(toolCall, turn, handlerContext) {
116
164
  const startTime = Date.now();
117
165
  const tool = toolMap.get(toolCall.name);
@@ -221,7 +269,9 @@ function createToolRouter(options) {
221
269
  }
222
270
  }
223
271
  if (!recovered) {
224
- throw error;
272
+ throw ApplicationFailure.fromError(error, {
273
+ nonRetryable: true
274
+ });
225
275
  }
226
276
  }
227
277
  if (!resultAppended) {
@@ -284,9 +334,10 @@ function createToolRouter(options) {
284
334
  },
285
335
  getToolDefinitions() {
286
336
  const activeSubagents = options.subagents?.filter((subagent) => isEnabled(subagent)) ?? [];
337
+ const activeSkills = options.skills ?? [];
287
338
  return [
288
339
  ...Array.from(toolMap).filter(
289
- ([, tool]) => isEnabled(tool) && tool.name !== SUBAGENT_TOOL_NAME
340
+ ([, tool]) => isEnabled(tool) && tool.name !== SUBAGENT_TOOL_NAME && tool.name !== READ_SKILL_TOOL_NAME
290
341
  ).map(([name, tool]) => ({
291
342
  name,
292
343
  description: tool.description,
@@ -294,7 +345,8 @@ function createToolRouter(options) {
294
345
  strict: tool.strict,
295
346
  max_uses: tool.max_uses
296
347
  })),
297
- ...activeSubagents.length > 0 ? [createSubagentTool(activeSubagents)] : []
348
+ ...activeSubagents.length > 0 ? [createSubagentTool(activeSubagents)] : [],
349
+ ...activeSkills.length > 0 ? [createReadSkillTool(activeSkills)] : []
298
350
  ];
299
351
  },
300
352
  // --- Methods for processing tool calls ---
@@ -408,18 +460,17 @@ function hasNoOtherToolCalls(toolCalls, excludeName) {
408
460
  var createSession = async ({
409
461
  threadId,
410
462
  agentName,
411
- description,
412
463
  maxTurns = 50,
413
464
  metadata = {},
414
465
  runAgent,
415
466
  threadOps,
416
467
  buildContextMessage,
417
468
  subagents,
469
+ skills,
418
470
  tools = {},
419
471
  processToolsInParallel = true,
420
472
  hooks = {},
421
473
  appendSystemPrompt = true,
422
- systemPrompt,
423
474
  waitForInputTimeout = "48h"
424
475
  }) => {
425
476
  const {
@@ -434,6 +485,7 @@ var createSession = async ({
434
485
  threadId,
435
486
  hooks,
436
487
  subagents,
488
+ skills,
437
489
  parallel: processToolsInParallel
438
490
  });
439
491
  const callSessionEnd = async (exitReason, turns) => {
@@ -477,8 +529,15 @@ var createSession = async ({
477
529
  metadata
478
530
  });
479
531
  }
532
+ const systemPrompt = stateManager.getSystemPrompt();
480
533
  await initializeThread(threadId);
481
- if (appendSystemPrompt && systemPrompt && systemPrompt.trim() !== "") {
534
+ if (appendSystemPrompt) {
535
+ if (!systemPrompt || systemPrompt.trim() === "") {
536
+ throw ApplicationFailure.create({
537
+ message: "No system prompt in state",
538
+ nonRetryable: true
539
+ });
540
+ }
482
541
  await appendSystemMessage(threadId, systemPrompt);
483
542
  }
484
543
  await appendHumanMessage(threadId, await buildContextMessage());
@@ -491,9 +550,7 @@ var createSession = async ({
491
550
  const { message, rawToolCalls, usage } = await runAgent({
492
551
  threadId,
493
552
  agentName,
494
- metadata,
495
- systemPrompt,
496
- description
553
+ metadata
497
554
  });
498
555
  if (usage) {
499
556
  stateManager.updateUsage(usage);
@@ -550,7 +607,7 @@ var createSession = async ({
550
607
  }
551
608
  } catch (error) {
552
609
  exitReason = "failed";
553
- throw error;
610
+ throw ApplicationFailure.fromError(error);
554
611
  } finally {
555
612
  await callSessionEnd(exitReason, stateManager.getTurns());
556
613
  }
@@ -565,14 +622,13 @@ var createSession = async ({
565
622
  function proxyDefaultThreadOps(options) {
566
623
  const activities = proxyActivities(
567
624
  options ?? {
568
- startToCloseTimeout: "30m",
625
+ startToCloseTimeout: "10s",
569
626
  retry: {
570
627
  maximumAttempts: 6,
571
628
  initialInterval: "5s",
572
629
  maximumInterval: "15m",
573
630
  backoffCoefficient: 4
574
- },
575
- heartbeatTimeout: "5m"
631
+ }
576
632
  }
577
633
  );
578
634
  return {
@@ -584,12 +640,14 @@ function proxyDefaultThreadOps(options) {
584
640
  }
585
641
 
586
642
  // src/lib/types.ts
643
+ var agentQueryName = (agentName) => `get${agentName}State`;
644
+ var agentStateChangeUpdateName = (agentName) => `waitFor${agentName}StateChange`;
587
645
  function isTerminalStatus(status) {
588
646
  return status === "COMPLETED" || status === "FAILED" || status === "CANCELLED";
589
647
  }
590
648
  function createAgentStateManager({
591
649
  initialState,
592
- agentConfig
650
+ agentName
593
651
  }) {
594
652
  let status = initialState?.status ?? "RUNNING";
595
653
  let version = initialState?.version ?? 0;
@@ -600,6 +658,7 @@ function createAgentStateManager({
600
658
  let totalCachedWriteTokens = 0;
601
659
  let totalCachedReadTokens = 0;
602
660
  let totalReasonTokens = 0;
661
+ let systemPrompt = initialState?.systemPrompt;
603
662
  const tasks = new Map(initialState?.tasks);
604
663
  const {
605
664
  status: _,
@@ -619,28 +678,32 @@ function createAgentStateManager({
619
678
  ...customState
620
679
  };
621
680
  }
622
- setHandler(defineQuery(`get${agentConfig.agentName}State`), () => {
681
+ const stateQuery = defineQuery(
682
+ agentQueryName(agentName)
683
+ );
684
+ const stateChangeUpdate = defineUpdate(
685
+ agentStateChangeUpdateName(agentName)
686
+ );
687
+ setHandler(stateQuery, () => buildState());
688
+ setHandler(stateChangeUpdate, async (lastKnownVersion) => {
689
+ await condition(
690
+ () => version > lastKnownVersion || isTerminalStatus(status),
691
+ "55s"
692
+ );
623
693
  return buildState();
624
694
  });
625
- setHandler(
626
- defineUpdate(
627
- `waitFor${agentConfig.agentName}StateChange`
628
- ),
629
- async (lastKnownVersion) => {
630
- await condition(
631
- () => version > lastKnownVersion || isTerminalStatus(status),
632
- "55s"
633
- );
634
- return buildState();
635
- }
636
- );
637
695
  return {
696
+ stateQuery,
697
+ stateChangeUpdate,
638
698
  getStatus() {
639
699
  return status;
640
700
  },
641
701
  isRunning() {
642
702
  return status === "RUNNING";
643
703
  },
704
+ getSystemPrompt() {
705
+ return systemPrompt;
706
+ },
644
707
  isTerminal() {
645
708
  return isTerminalStatus(status);
646
709
  },
@@ -708,6 +771,9 @@ function createAgentStateManager({
708
771
  max_uses: tool.max_uses
709
772
  }));
710
773
  },
774
+ setSystemPrompt(newSystemPrompt) {
775
+ systemPrompt = newSystemPrompt;
776
+ },
711
777
  deleteTask(id) {
712
778
  const deleted = tasks.delete(id);
713
779
  if (deleted) {
@@ -734,11 +800,79 @@ function createAgentStateManager({
734
800
  }
735
801
  };
736
802
  }
737
- var AGENT_HANDLER_NAMES = {
738
- getAgentState: "getAgentState",
739
- waitForStateChange: "waitForStateChange",
740
- addMessage: "addMessage"
741
- };
803
+
804
+ // src/lib/skills/parse.ts
805
+ function parseSkillFile(raw) {
806
+ const trimmed = raw.replace(/^\uFEFF/, "");
807
+ const match = trimmed.match(/^---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*\r?\n?([\s\S]*)$/);
808
+ if (!match) {
809
+ throw new Error(
810
+ "SKILL.md must start with YAML frontmatter delimited by ---"
811
+ );
812
+ }
813
+ const [, yamlBlock, body] = match;
814
+ const frontmatter = parseSimpleYaml(yamlBlock);
815
+ if (!frontmatter.name || typeof frontmatter.name !== "string") {
816
+ throw new Error("SKILL.md frontmatter must include a 'name' field");
817
+ }
818
+ if (!frontmatter.description || typeof frontmatter.description !== "string") {
819
+ throw new Error("SKILL.md frontmatter must include a 'description' field");
820
+ }
821
+ const result = {
822
+ name: frontmatter.name,
823
+ description: frontmatter.description
824
+ };
825
+ if (frontmatter.license) result.license = String(frontmatter.license);
826
+ if (frontmatter.compatibility)
827
+ result.compatibility = String(frontmatter.compatibility);
828
+ if (frontmatter["allowed-tools"]) {
829
+ result.allowedTools = String(frontmatter["allowed-tools"]).split(/\s+/).filter(Boolean);
830
+ }
831
+ if (frontmatter.metadata && typeof frontmatter.metadata === "object" && !Array.isArray(frontmatter.metadata)) {
832
+ result.metadata = frontmatter.metadata;
833
+ }
834
+ return { frontmatter: result, body: body.trim() };
835
+ }
836
+ function parseSimpleYaml(yaml) {
837
+ const result = {};
838
+ const lines = yaml.split(/\r?\n/);
839
+ let currentMapKey = null;
840
+ let currentMap = null;
841
+ for (const line of lines) {
842
+ if (line.trim() === "" || line.trim().startsWith("#")) continue;
843
+ const nestedMatch = line.match(/^(\s{2,}|\t+)(\S+)\s*:\s*(.*)$/);
844
+ if (nestedMatch && currentMapKey && currentMap) {
845
+ const [, , key2, rawVal2] = nestedMatch;
846
+ currentMap[key2] = unquote(rawVal2.trim());
847
+ continue;
848
+ }
849
+ if (currentMapKey && currentMap) {
850
+ result[currentMapKey] = currentMap;
851
+ currentMapKey = null;
852
+ currentMap = null;
853
+ }
854
+ const topMatch = line.match(/^(\S+)\s*:\s*(.*)$/);
855
+ if (!topMatch) continue;
856
+ const [, key, rawVal] = topMatch;
857
+ const val = rawVal.trim();
858
+ if (val === "" || val === "|" || val === ">") {
859
+ currentMapKey = key;
860
+ currentMap = {};
861
+ } else {
862
+ result[key] = unquote(val);
863
+ }
864
+ }
865
+ if (currentMapKey && currentMap) {
866
+ result[currentMapKey] = currentMap;
867
+ }
868
+ return result;
869
+ }
870
+ function unquote(s) {
871
+ if (s.startsWith('"') && s.endsWith('"') || s.startsWith("'") && s.endsWith("'")) {
872
+ return s.slice(1, -1);
873
+ }
874
+ return s;
875
+ }
742
876
  var globTool = {
743
877
  name: "Glob",
744
878
  description: `Search for files matching a glob pattern within the available file system.
@@ -855,7 +989,7 @@ IMPORTANT:
855
989
  };
856
990
  var taskCreateTool = {
857
991
  name: "TaskCreate",
858
- description: `Use this tool to create a structured task list for the control test. This helps you track progress, organize complex tasks, and demonstrate thoroughness to the user.
992
+ description: `Use this tool to create a structured task list. This helps you track progress, organize complex tasks, and demonstrate thoroughness to the user.
859
993
  It also helps the user understand the progress of the task and overall progress of their requests.
860
994
 
861
995
  ## When to Use This Tool
@@ -894,17 +1028,17 @@ var taskCreateTool = {
894
1028
  - Include enough detail in the description for another agent to understand and complete the task
895
1029
  - After creating tasks, use TaskUpdate to set up dependencies (blocks/blockedBy) if needed
896
1030
  - Check TaskList first to avoid creating duplicate tasks`,
897
- schema: z13.object({
898
- subject: z13.string().describe(
1031
+ schema: z14.object({
1032
+ subject: z14.string().describe(
899
1033
  'A brief, actionable title in imperative form (e.g., "Fix authentication bug in login flow")'
900
1034
  ),
901
- description: z13.string().describe(
1035
+ description: z14.string().describe(
902
1036
  "Detailed description of what needs to be done, including context and acceptance criteria"
903
1037
  ),
904
- activeForm: z13.string().describe(
1038
+ activeForm: z14.string().describe(
905
1039
  'Present continuous form shown in spinner when task is in_progress (e.g., "Fixing authentication bug"). This is displayed to the user while you work on the task.'
906
1040
  ),
907
- metadata: z13.record(z13.string(), z13.string()).describe("Arbitrary key-value pairs for tracking")
1041
+ metadata: z14.record(z14.string(), z14.string()).describe("Arbitrary key-value pairs for tracking")
908
1042
  })
909
1043
  };
910
1044
  function createTaskCreateHandler(stateManager) {
@@ -929,8 +1063,8 @@ function createTaskCreateHandler(stateManager) {
929
1063
  var taskGetTool = {
930
1064
  name: "TaskGet",
931
1065
  description: `Retrieve full task details including dependencies.`,
932
- schema: z13.object({
933
- taskId: z13.string().describe("The ID of the task to get")
1066
+ schema: z14.object({
1067
+ taskId: z14.string().describe("The ID of the task to get")
934
1068
  })
935
1069
  };
936
1070
 
@@ -953,7 +1087,7 @@ function createTaskGetHandler(stateManager) {
953
1087
  var taskListTool = {
954
1088
  name: "TaskList",
955
1089
  description: `List all tasks with current state.`,
956
- schema: z13.object({})
1090
+ schema: z14.object({})
957
1091
  };
958
1092
 
959
1093
  // src/tools/task-list/handler.ts
@@ -969,11 +1103,11 @@ function createTaskListHandler(stateManager) {
969
1103
  var taskUpdateTool = {
970
1104
  name: "TaskUpdate",
971
1105
  description: `Update status, add blockers, modify details.`,
972
- schema: z13.object({
973
- taskId: z13.string().describe("The ID of the task to get"),
974
- status: z13.enum(["pending", "in_progress", "completed"]).describe("The status of the task"),
975
- addBlockedBy: z13.array(z13.string()).describe("The IDs of the tasks that are blocking this task"),
976
- addBlocks: z13.array(z13.string()).describe("The IDs of the tasks that this task is blocking")
1106
+ schema: z14.object({
1107
+ taskId: z14.string().describe("The ID of the task to get"),
1108
+ status: z14.enum(["pending", "in_progress", "completed"]).describe("The status of the task"),
1109
+ addBlockedBy: z14.array(z14.string()).describe("The IDs of the tasks that are blocking this task"),
1110
+ addBlocks: z14.array(z14.string()).describe("The IDs of the tasks that this task is blocking")
977
1111
  })
978
1112
  };
979
1113
 
@@ -1041,8 +1175,8 @@ Use this tool to:
1041
1175
  - Execute scripts and chain commands with pipes (|) or logical operators (&&, ||)
1042
1176
  - Inspect files and directories
1043
1177
  `,
1044
- schema: z13.object({
1045
- command: z13.string().describe(
1178
+ schema: z14.object({
1179
+ command: z14.string().describe(
1046
1180
  "The bash command to execute. Can include pipes (|), redirects (>, >>), logical operators (&&, ||), and shell features like command substitution $(...)."
1047
1181
  )
1048
1182
  }),
@@ -1063,18 +1197,18 @@ Usage notes:
1063
1197
  * Use multiSelect: true to allow multiple answers to be selected for a question
1064
1198
  * If you recommend a specific option, make that the first option in the list and add "(Recommended)" at the end of the label
1065
1199
  `,
1066
- schema: z13.object({
1067
- questions: z13.array(
1068
- z13.object({
1069
- question: z13.string().describe("The full question text to display"),
1070
- header: z13.string().describe("Short label for the question (max 12 characters)"),
1071
- options: z13.array(
1072
- z13.object({
1073
- label: z13.string(),
1074
- description: z13.string()
1200
+ schema: z14.object({
1201
+ questions: z14.array(
1202
+ z14.object({
1203
+ question: z14.string().describe("The full question text to display"),
1204
+ header: z14.string().describe("Short label for the question (max 12 characters)"),
1205
+ options: z14.array(
1206
+ z14.object({
1207
+ label: z14.string(),
1208
+ description: z14.string()
1075
1209
  })
1076
1210
  ).min(0).max(4).describe("Array of 0-4 choices, each with label and description"),
1077
- multiSelect: z13.boolean().describe("If true, users can select multiple options")
1211
+ multiSelect: z14.boolean().describe("If true, users can select multiple options")
1078
1212
  })
1079
1213
  )
1080
1214
  }),
@@ -1089,7 +1223,7 @@ var createAskUserQuestionHandler = () => async (args) => {
1089
1223
  };
1090
1224
  };
1091
1225
 
1092
- // node_modules/uuid/dist/esm-node/stringify.js
1226
+ // node_modules/uuid/dist/esm/stringify.js
1093
1227
  var byteToHex = [];
1094
1228
  for (let i = 0; i < 256; ++i) {
1095
1229
  byteToHex.push((i + 256).toString(16).slice(1));
@@ -1101,26 +1235,30 @@ var rnds8Pool = new Uint8Array(256);
1101
1235
  var poolPtr = rnds8Pool.length;
1102
1236
  function rng() {
1103
1237
  if (poolPtr > rnds8Pool.length - 16) {
1104
- crypto.randomFillSync(rnds8Pool);
1238
+ randomFillSync(rnds8Pool);
1105
1239
  poolPtr = 0;
1106
1240
  }
1107
1241
  return rnds8Pool.slice(poolPtr, poolPtr += 16);
1108
1242
  }
1109
- var native_default = {
1110
- randomUUID: crypto.randomUUID
1111
- };
1243
+ var native_default = { randomUUID };
1112
1244
 
1113
- // node_modules/uuid/dist/esm-node/v4.js
1245
+ // node_modules/uuid/dist/esm/v4.js
1114
1246
  function v4(options, buf, offset) {
1115
1247
  if (native_default.randomUUID && !buf && !options) {
1116
1248
  return native_default.randomUUID();
1117
1249
  }
1118
1250
  options = options || {};
1119
- const rnds = options.random || (options.rng || rng)();
1251
+ const rnds = options.random ?? options.rng?.() ?? rng();
1252
+ if (rnds.length < 16) {
1253
+ throw new Error("Random bytes length must be >= 16");
1254
+ }
1120
1255
  rnds[6] = rnds[6] & 15 | 64;
1121
1256
  rnds[8] = rnds[8] & 63 | 128;
1122
1257
  if (buf) {
1123
1258
  offset = offset || 0;
1259
+ if (offset < 0 || offset + 16 > buf.length) {
1260
+ throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`);
1261
+ }
1124
1262
  for (let i = 0; i < 16; ++i) {
1125
1263
  buf[offset + i] = rnds[i];
1126
1264
  }
@@ -1132,9 +1270,23 @@ var v4_default = v4;
1132
1270
 
1133
1271
  // src/lib/thread-manager.ts
1134
1272
  var THREAD_TTL_SECONDS = 60 * 60 * 24 * 90;
1273
+ var APPEND_IDEMPOTENT_SCRIPT = `
1274
+ if redis.call('EXISTS', KEYS[1]) == 1 then
1275
+ return 0
1276
+ end
1277
+ for i = 2, #ARGV do
1278
+ redis.call('RPUSH', KEYS[2], ARGV[i])
1279
+ end
1280
+ redis.call('EXPIRE', KEYS[2], tonumber(ARGV[1]))
1281
+ redis.call('SET', KEYS[1], '1', 'EX', tonumber(ARGV[1]))
1282
+ return 1
1283
+ `;
1135
1284
  function getThreadKey(threadId, key) {
1136
1285
  return `thread:${threadId}:${key}`;
1137
1286
  }
1287
+ function storedMessageId(msg) {
1288
+ return msg.data.id ?? "";
1289
+ }
1138
1290
  function createThreadManager(config) {
1139
1291
  const {
1140
1292
  redis,
@@ -1144,6 +1296,7 @@ function createThreadManager(config) {
1144
1296
  deserialize = (raw) => JSON.parse(raw)
1145
1297
  } = config;
1146
1298
  const redisKey = getThreadKey(threadId, key);
1299
+ const idOf = config.idOf ?? (!config.serialize ? storedMessageId : void 0);
1147
1300
  const base = {
1148
1301
  async initialize() {
1149
1302
  await redis.del(redisKey);
@@ -1153,7 +1306,19 @@ function createThreadManager(config) {
1153
1306
  return data.map(deserialize);
1154
1307
  },
1155
1308
  async append(messages) {
1156
- if (messages.length > 0) {
1309
+ if (messages.length === 0) return;
1310
+ if (idOf) {
1311
+ const dedupId = messages.map(idOf).join(":");
1312
+ const dedupKey = getThreadKey(threadId, `dedup:${dedupId}`);
1313
+ await redis.eval(
1314
+ APPEND_IDEMPOTENT_SCRIPT,
1315
+ 2,
1316
+ dedupKey,
1317
+ redisKey,
1318
+ String(THREAD_TTL_SECONDS),
1319
+ ...messages.map(serialize)
1320
+ );
1321
+ } else {
1157
1322
  await redis.rpush(redisKey, ...messages.map(serialize));
1158
1323
  await redis.expire(redisKey, THREAD_TTL_SECONDS);
1159
1324
  }
@@ -1188,6 +1353,7 @@ function createThreadManager(config) {
1188
1353
  },
1189
1354
  createToolMessage(content, toolCallId) {
1190
1355
  return new ToolMessage({
1356
+ id: v4_default(),
1191
1357
  content,
1192
1358
  tool_call_id: toolCallId
1193
1359
  }).toDict();
@@ -1260,7 +1426,7 @@ async function invokeModel({
1260
1426
  const parentWorkflowId = info.workflowExecution.workflowId;
1261
1427
  const parentRunId = info.workflowExecution.runId;
1262
1428
  const handle = client.getHandle(parentWorkflowId, parentRunId);
1263
- const { tools } = await handle.query(`get${agentName}State`);
1429
+ const { tools } = await handle.query(agentQueryName(agentName));
1264
1430
  const messages = await thread.load();
1265
1431
  const response = await model.invoke(
1266
1432
  [...mapStoredMessagesToChatMessages(messages)],
@@ -1457,7 +1623,62 @@ var toTree = async (fs, opts = {}) => {
1457
1623
  const base = basename(dir, separator) + separator;
1458
1624
  return base + subtree;
1459
1625
  };
1626
+ var FileSystemSkillProvider = class {
1627
+ constructor(baseDir) {
1628
+ this.baseDir = baseDir;
1629
+ }
1630
+ async listSkills() {
1631
+ const dirs = await this.discoverSkillDirs();
1632
+ const skills = [];
1633
+ for (const dir of dirs) {
1634
+ const raw = await readFile(join(this.baseDir, dir, "SKILL.md"), "utf-8");
1635
+ const { frontmatter } = parseSkillFile(raw);
1636
+ skills.push(frontmatter);
1637
+ }
1638
+ return skills;
1639
+ }
1640
+ async getSkill(name) {
1641
+ const raw = await readFile(
1642
+ join(this.baseDir, name, "SKILL.md"),
1643
+ "utf-8"
1644
+ );
1645
+ const { frontmatter, body } = parseSkillFile(raw);
1646
+ if (frontmatter.name !== name) {
1647
+ throw new Error(
1648
+ `Skill directory "${name}" contains SKILL.md with mismatched name "${frontmatter.name}"`
1649
+ );
1650
+ }
1651
+ return { ...frontmatter, instructions: body };
1652
+ }
1653
+ /**
1654
+ * Convenience method to load all skills with full instructions.
1655
+ * Returns `Skill[]` ready to pass into a workflow.
1656
+ */
1657
+ async loadAll() {
1658
+ const dirs = await this.discoverSkillDirs();
1659
+ const skills = [];
1660
+ for (const dir of dirs) {
1661
+ const raw = await readFile(join(this.baseDir, dir, "SKILL.md"), "utf-8");
1662
+ const { frontmatter, body } = parseSkillFile(raw);
1663
+ skills.push({ ...frontmatter, instructions: body });
1664
+ }
1665
+ return skills;
1666
+ }
1667
+ async discoverSkillDirs() {
1668
+ const entries = await readdir(this.baseDir, { withFileTypes: true });
1669
+ const dirs = [];
1670
+ for (const entry of entries) {
1671
+ if (!entry.isDirectory()) continue;
1672
+ try {
1673
+ await readFile(join(this.baseDir, entry.name, "SKILL.md"), "utf-8");
1674
+ dirs.push(entry.name);
1675
+ } catch {
1676
+ }
1677
+ }
1678
+ return dirs;
1679
+ }
1680
+ };
1460
1681
 
1461
- export { AGENT_HANDLER_NAMES, ZeitlichPlugin, askUserQuestionTool, bashTool, createAgentStateManager, createAskUserQuestionHandler, createBashHandler, createBashToolDescription, createEditHandler, createGlobHandler, createSession, createSharedActivities, createSubagentTool, createTaskCreateHandler, createTaskGetHandler, createTaskListHandler, createTaskUpdateHandler, createThreadManager, createToolRouter, defineSubagent, defineTool, editTool, globTool, grepTool, hasNoOtherToolCalls, invokeModel, isTerminalStatus, proxyDefaultThreadOps, readFileTool, taskCreateTool, taskGetTool, taskListTool, taskUpdateTool, toTree, withAutoAppend, writeFileTool };
1682
+ export { FileSystemSkillProvider, ZeitlichPlugin, agentQueryName, agentStateChangeUpdateName, askUserQuestionTool, bashTool, createAgentStateManager, createAskUserQuestionHandler, createBashHandler, createBashToolDescription, createEditHandler, createGlobHandler, createReadSkillHandler, createReadSkillTool, createSession, createSharedActivities, createSubagentTool, createTaskCreateHandler, createTaskGetHandler, createTaskListHandler, createTaskUpdateHandler, createThreadManager, createToolRouter, defineSubagent, defineTool, editTool, globTool, grepTool, hasNoOtherToolCalls, invokeModel, isTerminalStatus, parseSkillFile, proxyDefaultThreadOps, readFileTool, taskCreateTool, taskGetTool, taskListTool, taskUpdateTool, toTree, withAutoAppend, writeFileTool };
1462
1683
  //# sourceMappingURL=index.js.map
1463
1684
  //# sourceMappingURL=index.js.map