zeitlich 0.2.8 → 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.cjs CHANGED
@@ -1,42 +1,29 @@
1
1
  'use strict';
2
2
 
3
3
  var workflow = require('@temporalio/workflow');
4
- var z13 = require('zod');
4
+ var z14 = require('zod');
5
5
  var plugin = require('@temporalio/plugin');
6
6
  var messages = require('@langchain/core/messages');
7
7
  var crypto = require('crypto');
8
8
  var activity = require('@temporalio/activity');
9
9
  var justBash = require('just-bash');
10
+ var promises = require('fs/promises');
11
+ var path = require('path');
10
12
 
11
13
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
12
14
 
13
- var z13__default = /*#__PURE__*/_interopDefault(z13);
14
- var crypto__default = /*#__PURE__*/_interopDefault(crypto);
15
+ var z14__default = /*#__PURE__*/_interopDefault(z14);
15
16
 
16
17
  // src/lib/session.ts
17
- var SUBAGENT_TOOL = "Subagent";
18
+ var SUBAGENT_TOOL_NAME = "Subagent";
18
19
  function buildSubagentDescription(subagents) {
19
- const subagentList = subagents.map((s) => `- **${s.agentName}**: ${s.description}`).join("\n");
20
- return `Launch a new agent to handle complex tasks autonomously.
21
-
22
- The ${SUBAGENT_TOOL} tool launches specialized agents (subprocesses) that autonomously handle complex tasks. Each agent type has specific capabilities and tools available to it.
23
-
24
- Available agent types:
20
+ const subagentList = subagents.map((s) => `## ${s.agentName}
21
+ ${s.description}`).join("\n\n");
22
+ 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.
25
23
 
24
+ # Available subagents:
26
25
  ${subagentList}
27
-
28
- When using the ${SUBAGENT_TOOL} tool, you must specify a subagent parameter to select which agent type to use.
29
-
30
- Usage notes:
31
-
32
- - Always include a short description (3-5 words) summarizing what the agent will do
33
- - Launch multiple agents concurrently whenever possible, to maximize performance; to do that, use a single message with multiple tool uses
34
- - When the agent is done, it will return a single message back to you.
35
- - Each invocation starts fresh - provide a detailed task description with all necessary context.
36
- - Provide clear, detailed prompts so the agent can work autonomously and return exactly the information you need.
37
- - The agent's outputs should generally be trusted
38
- - Clearly tell the agent what type of work you expect since it is not aware of the user's intent
39
- - 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.`;
26
+ `;
40
27
  }
41
28
  function createSubagentTool(subagents) {
42
29
  if (subagents.length === 0) {
@@ -44,12 +31,12 @@ function createSubagentTool(subagents) {
44
31
  }
45
32
  const names = subagents.map((s) => s.agentName);
46
33
  return {
47
- name: SUBAGENT_TOOL,
34
+ name: SUBAGENT_TOOL_NAME,
48
35
  description: buildSubagentDescription(subagents),
49
- schema: z13__default.default.object({
50
- subagent: z13__default.default.enum(names).describe("The type of subagent to launch"),
51
- description: z13__default.default.string().describe("A short (3-5 word) description of the task"),
52
- prompt: z13__default.default.string().describe("The task for the agent to perform")
36
+ schema: z14__default.default.object({
37
+ subagent: z14__default.default.enum(names).describe("The type of subagent to launch"),
38
+ description: z14__default.default.string().describe("A short (3-5 word) description of the task"),
39
+ prompt: z14__default.default.string().describe("The task for the agent to perform")
53
40
  })
54
41
  };
55
42
  }
@@ -73,23 +60,77 @@ function createSubagentHandler(subagents) {
73
60
  taskQueue: config.taskQueue ?? parentTaskQueue
74
61
  };
75
62
  const { toolResponse, data, usage } = typeof config.workflow === "string" ? await workflow.executeChild(config.workflow, childOpts) : await workflow.executeChild(config.workflow, childOpts);
76
- const validated = config.resultSchema ? config.resultSchema.parse(data) : null;
63
+ if (!toolResponse) {
64
+ return {
65
+ toolResponse: "Subagent workflow returned no response",
66
+ data: null,
67
+ ...usage && { usage }
68
+ };
69
+ }
70
+ const validated = config.resultSchema ? config.resultSchema.safeParse(data) : null;
71
+ if (validated && !validated.success) {
72
+ return {
73
+ toolResponse: `Subagent workflow returned invalid data: ${validated.error.message}`,
74
+ data: null,
75
+ ...usage && { usage }
76
+ };
77
+ }
77
78
  return {
78
79
  toolResponse,
79
- data: validated,
80
+ data: validated ? validated.data : data,
80
81
  ...usage && { usage }
81
82
  };
82
83
  };
83
84
  }
85
+ var READ_SKILL_TOOL_NAME = "ReadSkill";
86
+ function buildReadSkillDescription(skills) {
87
+ const skillList = skills.map((s) => `- **${s.name}**: ${s.description}`).join("\n");
88
+ return `Load the full instructions for a skill. Read the skill before following its instructions.
84
89
 
85
- // src/lib/tool-router.ts
90
+ # Available skills:
91
+ ${skillList}
92
+ `;
93
+ }
94
+ function createReadSkillTool(skills) {
95
+ if (skills.length === 0) {
96
+ throw new Error("createReadSkillTool requires at least one skill");
97
+ }
98
+ const names = skills.map((s) => s.name);
99
+ return {
100
+ name: READ_SKILL_TOOL_NAME,
101
+ description: buildReadSkillDescription(skills),
102
+ schema: z14__default.default.object({
103
+ skill_name: z14__default.default.enum(names).describe("The name of the skill to load")
104
+ })
105
+ };
106
+ }
107
+
108
+ // src/tools/read-skill/handler.ts
109
+ function createReadSkillHandler(skills) {
110
+ const skillMap = new Map(skills.map((s) => [s.name, s]));
111
+ return (args) => {
112
+ const skill = skillMap.get(args.skill_name);
113
+ if (!skill) {
114
+ return {
115
+ toolResponse: JSON.stringify({
116
+ error: `Skill "${args.skill_name}" not found`
117
+ }),
118
+ data: null
119
+ };
120
+ }
121
+ return {
122
+ toolResponse: skill.instructions,
123
+ data: null
124
+ };
125
+ };
126
+ }
86
127
  function createToolRouter(options) {
87
128
  const { appendToolResult } = options;
88
129
  const toolMap = /* @__PURE__ */ new Map();
89
130
  for (const [_key, tool] of Object.entries(options.tools)) {
90
131
  toolMap.set(tool.name, tool);
91
132
  }
92
- const isEnabled = (tool) => tool.enabled?.() ?? true;
133
+ const isEnabled = (tool) => tool.enabled ?? true;
93
134
  if (options.subagents) {
94
135
  if (options.subagents.length > 0) {
95
136
  const subagentHooksMap = /* @__PURE__ */ new Map();
@@ -97,7 +138,7 @@ function createToolRouter(options) {
97
138
  if (s.hooks) subagentHooksMap.set(s.agentName, s.hooks);
98
139
  }
99
140
  const resolveSubagentName = (args) => args.subagent;
100
- toolMap.set("Subagent", {
141
+ toolMap.set(SUBAGENT_TOOL_NAME, {
101
142
  ...createSubagentTool(options.subagents),
102
143
  handler: createSubagentHandler(options.subagents),
103
144
  ...subagentHooksMap.size > 0 && {
@@ -119,6 +160,12 @@ function createToolRouter(options) {
119
160
  });
120
161
  }
121
162
  }
163
+ if (options.skills && options.skills.length > 0) {
164
+ toolMap.set(READ_SKILL_TOOL_NAME, {
165
+ ...createReadSkillTool(options.skills),
166
+ handler: createReadSkillHandler(options.skills)
167
+ });
168
+ }
122
169
  async function processToolCall(toolCall, turn, handlerContext) {
123
170
  const startTime = Date.now();
124
171
  const tool = toolMap.get(toolCall.name);
@@ -228,7 +275,9 @@ function createToolRouter(options) {
228
275
  }
229
276
  }
230
277
  if (!recovered) {
231
- throw error;
278
+ throw workflow.ApplicationFailure.fromError(error, {
279
+ nonRetryable: true
280
+ });
232
281
  }
233
282
  }
234
283
  if (!resultAppended) {
@@ -290,13 +339,21 @@ function createToolRouter(options) {
290
339
  return Array.from(toolMap.entries()).filter(([, tool]) => isEnabled(tool)).map(([name]) => name);
291
340
  },
292
341
  getToolDefinitions() {
293
- return Array.from(toolMap).filter(([, tool]) => isEnabled(tool)).map(([name, tool]) => ({
294
- name,
295
- description: tool.description,
296
- schema: tool.schema,
297
- strict: tool.strict,
298
- max_uses: tool.max_uses
299
- }));
342
+ const activeSubagents = options.subagents?.filter((subagent) => isEnabled(subagent)) ?? [];
343
+ const activeSkills = options.skills ?? [];
344
+ return [
345
+ ...Array.from(toolMap).filter(
346
+ ([, tool]) => isEnabled(tool) && tool.name !== SUBAGENT_TOOL_NAME && tool.name !== READ_SKILL_TOOL_NAME
347
+ ).map(([name, tool]) => ({
348
+ name,
349
+ description: tool.description,
350
+ schema: tool.schema,
351
+ strict: tool.strict,
352
+ max_uses: tool.max_uses
353
+ })),
354
+ ...activeSubagents.length > 0 ? [createSubagentTool(activeSubagents)] : [],
355
+ ...activeSkills.length > 0 ? [createReadSkillTool(activeSkills)] : []
356
+ ];
300
357
  },
301
358
  // --- Methods for processing tool calls ---
302
359
  async processToolCalls(toolCalls, context) {
@@ -409,18 +466,17 @@ function hasNoOtherToolCalls(toolCalls, excludeName) {
409
466
  var createSession = async ({
410
467
  threadId,
411
468
  agentName,
412
- description,
413
469
  maxTurns = 50,
414
470
  metadata = {},
415
471
  runAgent,
416
472
  threadOps,
417
473
  buildContextMessage,
418
474
  subagents,
475
+ skills,
419
476
  tools = {},
420
477
  processToolsInParallel = true,
421
478
  hooks = {},
422
479
  appendSystemPrompt = true,
423
- systemPrompt,
424
480
  waitForInputTimeout = "48h"
425
481
  }) => {
426
482
  const {
@@ -435,6 +491,7 @@ var createSession = async ({
435
491
  threadId,
436
492
  hooks,
437
493
  subagents,
494
+ skills,
438
495
  parallel: processToolsInParallel
439
496
  });
440
497
  const callSessionEnd = async (exitReason, turns) => {
@@ -478,8 +535,15 @@ var createSession = async ({
478
535
  metadata
479
536
  });
480
537
  }
538
+ const systemPrompt = stateManager.getSystemPrompt();
481
539
  await initializeThread(threadId);
482
- if (appendSystemPrompt && systemPrompt && systemPrompt.trim() !== "") {
540
+ if (appendSystemPrompt) {
541
+ if (!systemPrompt || systemPrompt.trim() === "") {
542
+ throw workflow.ApplicationFailure.create({
543
+ message: "No system prompt in state",
544
+ nonRetryable: true
545
+ });
546
+ }
483
547
  await appendSystemMessage(threadId, systemPrompt);
484
548
  }
485
549
  await appendHumanMessage(threadId, await buildContextMessage());
@@ -492,9 +556,7 @@ var createSession = async ({
492
556
  const { message, rawToolCalls, usage } = await runAgent({
493
557
  threadId,
494
558
  agentName,
495
- metadata,
496
- systemPrompt,
497
- description
559
+ metadata
498
560
  });
499
561
  if (usage) {
500
562
  stateManager.updateUsage(usage);
@@ -551,7 +613,7 @@ var createSession = async ({
551
613
  }
552
614
  } catch (error) {
553
615
  exitReason = "failed";
554
- throw error;
616
+ throw workflow.ApplicationFailure.fromError(error);
555
617
  } finally {
556
618
  await callSessionEnd(exitReason, stateManager.getTurns());
557
619
  }
@@ -566,14 +628,13 @@ var createSession = async ({
566
628
  function proxyDefaultThreadOps(options) {
567
629
  const activities = workflow.proxyActivities(
568
630
  options ?? {
569
- startToCloseTimeout: "30m",
631
+ startToCloseTimeout: "10s",
570
632
  retry: {
571
633
  maximumAttempts: 6,
572
634
  initialInterval: "5s",
573
635
  maximumInterval: "15m",
574
636
  backoffCoefficient: 4
575
- },
576
- heartbeatTimeout: "5m"
637
+ }
577
638
  }
578
639
  );
579
640
  return {
@@ -585,12 +646,14 @@ function proxyDefaultThreadOps(options) {
585
646
  }
586
647
 
587
648
  // src/lib/types.ts
649
+ var agentQueryName = (agentName) => `get${agentName}State`;
650
+ var agentStateChangeUpdateName = (agentName) => `waitFor${agentName}StateChange`;
588
651
  function isTerminalStatus(status) {
589
652
  return status === "COMPLETED" || status === "FAILED" || status === "CANCELLED";
590
653
  }
591
654
  function createAgentStateManager({
592
655
  initialState,
593
- agentConfig
656
+ agentName
594
657
  }) {
595
658
  let status = initialState?.status ?? "RUNNING";
596
659
  let version = initialState?.version ?? 0;
@@ -601,6 +664,7 @@ function createAgentStateManager({
601
664
  let totalCachedWriteTokens = 0;
602
665
  let totalCachedReadTokens = 0;
603
666
  let totalReasonTokens = 0;
667
+ let systemPrompt = initialState?.systemPrompt;
604
668
  const tasks = new Map(initialState?.tasks);
605
669
  const {
606
670
  status: _,
@@ -620,28 +684,32 @@ function createAgentStateManager({
620
684
  ...customState
621
685
  };
622
686
  }
623
- workflow.setHandler(workflow.defineQuery(`get${agentConfig.agentName}State`), () => {
687
+ const stateQuery = workflow.defineQuery(
688
+ agentQueryName(agentName)
689
+ );
690
+ const stateChangeUpdate = workflow.defineUpdate(
691
+ agentStateChangeUpdateName(agentName)
692
+ );
693
+ workflow.setHandler(stateQuery, () => buildState());
694
+ workflow.setHandler(stateChangeUpdate, async (lastKnownVersion) => {
695
+ await workflow.condition(
696
+ () => version > lastKnownVersion || isTerminalStatus(status),
697
+ "55s"
698
+ );
624
699
  return buildState();
625
700
  });
626
- workflow.setHandler(
627
- workflow.defineUpdate(
628
- `waitFor${agentConfig.agentName}StateChange`
629
- ),
630
- async (lastKnownVersion) => {
631
- await workflow.condition(
632
- () => version > lastKnownVersion || isTerminalStatus(status),
633
- "55s"
634
- );
635
- return buildState();
636
- }
637
- );
638
701
  return {
702
+ stateQuery,
703
+ stateChangeUpdate,
639
704
  getStatus() {
640
705
  return status;
641
706
  },
642
707
  isRunning() {
643
708
  return status === "RUNNING";
644
709
  },
710
+ getSystemPrompt() {
711
+ return systemPrompt;
712
+ },
645
713
  isTerminal() {
646
714
  return isTerminalStatus(status);
647
715
  },
@@ -704,11 +772,14 @@ function createAgentStateManager({
704
772
  tools = newTools.map((tool) => ({
705
773
  name: tool.name,
706
774
  description: tool.description,
707
- schema: z13.z.toJSONSchema(tool.schema),
775
+ schema: z14.z.toJSONSchema(tool.schema),
708
776
  strict: tool.strict,
709
777
  max_uses: tool.max_uses
710
778
  }));
711
779
  },
780
+ setSystemPrompt(newSystemPrompt) {
781
+ systemPrompt = newSystemPrompt;
782
+ },
712
783
  deleteTask(id) {
713
784
  const deleted = tasks.delete(id);
714
785
  if (deleted) {
@@ -735,11 +806,79 @@ function createAgentStateManager({
735
806
  }
736
807
  };
737
808
  }
738
- var AGENT_HANDLER_NAMES = {
739
- getAgentState: "getAgentState",
740
- waitForStateChange: "waitForStateChange",
741
- addMessage: "addMessage"
742
- };
809
+
810
+ // src/lib/skills/parse.ts
811
+ function parseSkillFile(raw) {
812
+ const trimmed = raw.replace(/^\uFEFF/, "");
813
+ const match = trimmed.match(/^---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*\r?\n?([\s\S]*)$/);
814
+ if (!match) {
815
+ throw new Error(
816
+ "SKILL.md must start with YAML frontmatter delimited by ---"
817
+ );
818
+ }
819
+ const [, yamlBlock, body] = match;
820
+ const frontmatter = parseSimpleYaml(yamlBlock);
821
+ if (!frontmatter.name || typeof frontmatter.name !== "string") {
822
+ throw new Error("SKILL.md frontmatter must include a 'name' field");
823
+ }
824
+ if (!frontmatter.description || typeof frontmatter.description !== "string") {
825
+ throw new Error("SKILL.md frontmatter must include a 'description' field");
826
+ }
827
+ const result = {
828
+ name: frontmatter.name,
829
+ description: frontmatter.description
830
+ };
831
+ if (frontmatter.license) result.license = String(frontmatter.license);
832
+ if (frontmatter.compatibility)
833
+ result.compatibility = String(frontmatter.compatibility);
834
+ if (frontmatter["allowed-tools"]) {
835
+ result.allowedTools = String(frontmatter["allowed-tools"]).split(/\s+/).filter(Boolean);
836
+ }
837
+ if (frontmatter.metadata && typeof frontmatter.metadata === "object" && !Array.isArray(frontmatter.metadata)) {
838
+ result.metadata = frontmatter.metadata;
839
+ }
840
+ return { frontmatter: result, body: body.trim() };
841
+ }
842
+ function parseSimpleYaml(yaml) {
843
+ const result = {};
844
+ const lines = yaml.split(/\r?\n/);
845
+ let currentMapKey = null;
846
+ let currentMap = null;
847
+ for (const line of lines) {
848
+ if (line.trim() === "" || line.trim().startsWith("#")) continue;
849
+ const nestedMatch = line.match(/^(\s{2,}|\t+)(\S+)\s*:\s*(.*)$/);
850
+ if (nestedMatch && currentMapKey && currentMap) {
851
+ const [, , key2, rawVal2] = nestedMatch;
852
+ currentMap[key2] = unquote(rawVal2.trim());
853
+ continue;
854
+ }
855
+ if (currentMapKey && currentMap) {
856
+ result[currentMapKey] = currentMap;
857
+ currentMapKey = null;
858
+ currentMap = null;
859
+ }
860
+ const topMatch = line.match(/^(\S+)\s*:\s*(.*)$/);
861
+ if (!topMatch) continue;
862
+ const [, key, rawVal] = topMatch;
863
+ const val = rawVal.trim();
864
+ if (val === "" || val === "|" || val === ">") {
865
+ currentMapKey = key;
866
+ currentMap = {};
867
+ } else {
868
+ result[key] = unquote(val);
869
+ }
870
+ }
871
+ if (currentMapKey && currentMap) {
872
+ result[currentMapKey] = currentMap;
873
+ }
874
+ return result;
875
+ }
876
+ function unquote(s) {
877
+ if (s.startsWith('"') && s.endsWith('"') || s.startsWith("'") && s.endsWith("'")) {
878
+ return s.slice(1, -1);
879
+ }
880
+ return s;
881
+ }
743
882
  var globTool = {
744
883
  name: "Glob",
745
884
  description: `Search for files matching a glob pattern within the available file system.
@@ -754,9 +893,9 @@ Examples:
754
893
  - "**/*.test.ts" - Find all test files recursively
755
894
  - "src/**/*.ts" - Find all TypeScript files in src directory
756
895
  `,
757
- schema: z13.z.object({
758
- pattern: z13.z.string().describe("Glob pattern to match files against"),
759
- root: z13.z.string().optional().describe("Optional root directory to search from")
896
+ schema: z14.z.object({
897
+ pattern: z14.z.string().describe("Glob pattern to match files against"),
898
+ root: z14.z.string().optional().describe("Optional root directory to search from")
760
899
  }),
761
900
  strict: true
762
901
  };
@@ -774,17 +913,17 @@ Examples:
774
913
  - Search for function definitions with "function.*handleClick"
775
914
  - Search case-insensitively with ignoreCase: true
776
915
  `,
777
- schema: z13.z.object({
778
- pattern: z13.z.string().describe("Regex pattern to search for in file contents"),
779
- ignoreCase: z13.z.boolean().optional().describe("Case-insensitive search (default: false)"),
780
- maxMatches: z13.z.number().optional().describe("Maximum number of matches to return (default: 50)"),
781
- includePatterns: z13.z.array(z13.z.string()).optional().describe("Glob patterns to include (e.g., ['*.ts', '*.js'])"),
782
- excludePatterns: z13.z.array(z13.z.string()).optional().describe("Glob patterns to exclude (e.g., ['*.test.ts'])"),
783
- contextLines: z13.z.number().optional().describe("Number of context lines to show around matches")
916
+ schema: z14.z.object({
917
+ pattern: z14.z.string().describe("Regex pattern to search for in file contents"),
918
+ ignoreCase: z14.z.boolean().optional().describe("Case-insensitive search (default: false)"),
919
+ maxMatches: z14.z.number().optional().describe("Maximum number of matches to return (default: 50)"),
920
+ includePatterns: z14.z.array(z14.z.string()).optional().describe("Glob patterns to include (e.g., ['*.ts', '*.js'])"),
921
+ excludePatterns: z14.z.array(z14.z.string()).optional().describe("Glob patterns to exclude (e.g., ['*.test.ts'])"),
922
+ contextLines: z14.z.number().optional().describe("Number of context lines to show around matches")
784
923
  }),
785
924
  strict: true
786
925
  };
787
- var readTool = {
926
+ var readFileTool = {
788
927
  name: "FileRead",
789
928
  description: `Read file contents with optional pagination.
790
929
 
@@ -798,32 +937,31 @@ The tool returns the file content in an appropriate format:
798
937
  - Images: Base64-encoded image data
799
938
  - PDFs: Extracted text content
800
939
  `,
801
- schema: z13.z.object({
802
- path: z13.z.string().describe("Virtual path to the file to read"),
803
- offset: z13.z.number().optional().describe(
940
+ schema: z14.z.object({
941
+ path: z14.z.string().describe("Virtual path to the file to read"),
942
+ offset: z14.z.number().optional().describe(
804
943
  "Line number to start reading from (1-indexed, for text files)"
805
944
  ),
806
- limit: z13.z.number().optional().describe("Maximum number of lines to read (for text files)")
945
+ limit: z14.z.number().optional().describe("Maximum number of lines to read (for text files)")
807
946
  }),
808
947
  strict: true
809
948
  };
810
- var writeTool = {
949
+ var writeFileTool = {
811
950
  name: "FileWrite",
812
951
  description: `Create or overwrite a file with new content.
813
952
 
814
953
  Usage:
815
- - Provide the absolute path to the file
816
954
  - The file will be created if it doesn't exist
817
955
  - If the file exists, it will be completely overwritten
818
956
 
819
957
  IMPORTANT:
820
958
  - You must read the file first (in this session) before writing to it
821
959
  - This is an atomic write operation - the entire file is replaced
822
- - Path must be absolute (e.g., "/docs/readme.md", not "docs/readme.md")
960
+ - Path must be relative to the root of the file system (e.g., "docs/readme.md", not "/docs/readme.md")
823
961
  `,
824
- schema: z13.z.object({
825
- file_path: z13.z.string().describe("The absolute path to the file to write"),
826
- content: z13.z.string().describe("The content to write to the file")
962
+ schema: z14.z.object({
963
+ file_path: z14.z.string().describe("The path to the file to write"),
964
+ content: z14.z.string().describe("The content to write to the file")
827
965
  }),
828
966
  strict: true
829
967
  };
@@ -843,13 +981,13 @@ IMPORTANT:
843
981
  - The operation fails if old_string is not found
844
982
  - old_string and new_string must be different
845
983
  `,
846
- schema: z13.z.object({
847
- file_path: z13.z.string().describe("The absolute virtual path to the file to modify"),
848
- old_string: z13.z.string().describe("The exact text to replace"),
849
- new_string: z13.z.string().describe(
984
+ schema: z14.z.object({
985
+ file_path: z14.z.string().describe("The absolute virtual path to the file to modify"),
986
+ old_string: z14.z.string().describe("The exact text to replace"),
987
+ new_string: z14.z.string().describe(
850
988
  "The text to replace it with (must be different from old_string)"
851
989
  ),
852
- replace_all: z13.z.boolean().optional().describe(
990
+ replace_all: z14.z.boolean().optional().describe(
853
991
  "If true, replace all occurrences of old_string (default: false)"
854
992
  )
855
993
  }),
@@ -857,7 +995,7 @@ IMPORTANT:
857
995
  };
858
996
  var taskCreateTool = {
859
997
  name: "TaskCreate",
860
- 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.
998
+ description: `Use this tool to create a structured task list. This helps you track progress, organize complex tasks, and demonstrate thoroughness to the user.
861
999
  It also helps the user understand the progress of the task and overall progress of their requests.
862
1000
 
863
1001
  ## When to Use This Tool
@@ -896,17 +1034,17 @@ var taskCreateTool = {
896
1034
  - Include enough detail in the description for another agent to understand and complete the task
897
1035
  - After creating tasks, use TaskUpdate to set up dependencies (blocks/blockedBy) if needed
898
1036
  - Check TaskList first to avoid creating duplicate tasks`,
899
- schema: z13__default.default.object({
900
- subject: z13__default.default.string().describe(
1037
+ schema: z14__default.default.object({
1038
+ subject: z14__default.default.string().describe(
901
1039
  'A brief, actionable title in imperative form (e.g., "Fix authentication bug in login flow")'
902
1040
  ),
903
- description: z13__default.default.string().describe(
1041
+ description: z14__default.default.string().describe(
904
1042
  "Detailed description of what needs to be done, including context and acceptance criteria"
905
1043
  ),
906
- activeForm: z13__default.default.string().describe(
1044
+ activeForm: z14__default.default.string().describe(
907
1045
  '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.'
908
1046
  ),
909
- metadata: z13__default.default.record(z13__default.default.string(), z13__default.default.string()).describe("Arbitrary key-value pairs for tracking")
1047
+ metadata: z14__default.default.record(z14__default.default.string(), z14__default.default.string()).describe("Arbitrary key-value pairs for tracking")
910
1048
  })
911
1049
  };
912
1050
  function createTaskCreateHandler(stateManager) {
@@ -931,8 +1069,8 @@ function createTaskCreateHandler(stateManager) {
931
1069
  var taskGetTool = {
932
1070
  name: "TaskGet",
933
1071
  description: `Retrieve full task details including dependencies.`,
934
- schema: z13__default.default.object({
935
- taskId: z13__default.default.string().describe("The ID of the task to get")
1072
+ schema: z14__default.default.object({
1073
+ taskId: z14__default.default.string().describe("The ID of the task to get")
936
1074
  })
937
1075
  };
938
1076
 
@@ -955,7 +1093,7 @@ function createTaskGetHandler(stateManager) {
955
1093
  var taskListTool = {
956
1094
  name: "TaskList",
957
1095
  description: `List all tasks with current state.`,
958
- schema: z13__default.default.object({})
1096
+ schema: z14__default.default.object({})
959
1097
  };
960
1098
 
961
1099
  // src/tools/task-list/handler.ts
@@ -971,11 +1109,11 @@ function createTaskListHandler(stateManager) {
971
1109
  var taskUpdateTool = {
972
1110
  name: "TaskUpdate",
973
1111
  description: `Update status, add blockers, modify details.`,
974
- schema: z13__default.default.object({
975
- taskId: z13__default.default.string().describe("The ID of the task to get"),
976
- status: z13__default.default.enum(["pending", "in_progress", "completed"]).describe("The status of the task"),
977
- addBlockedBy: z13__default.default.array(z13__default.default.string()).describe("The IDs of the tasks that are blocking this task"),
978
- addBlocks: z13__default.default.array(z13__default.default.string()).describe("The IDs of the tasks that this task is blocking")
1112
+ schema: z14__default.default.object({
1113
+ taskId: z14__default.default.string().describe("The ID of the task to get"),
1114
+ status: z14__default.default.enum(["pending", "in_progress", "completed"]).describe("The status of the task"),
1115
+ addBlockedBy: z14__default.default.array(z14__default.default.string()).describe("The IDs of the tasks that are blocking this task"),
1116
+ addBlocks: z14__default.default.array(z14__default.default.string()).describe("The IDs of the tasks that this task is blocking")
979
1117
  })
980
1118
  };
981
1119
 
@@ -1043,8 +1181,8 @@ Use this tool to:
1043
1181
  - Execute scripts and chain commands with pipes (|) or logical operators (&&, ||)
1044
1182
  - Inspect files and directories
1045
1183
  `,
1046
- schema: z13__default.default.object({
1047
- command: z13__default.default.string().describe(
1184
+ schema: z14__default.default.object({
1185
+ command: z14__default.default.string().describe(
1048
1186
  "The bash command to execute. Can include pipes (|), redirects (>, >>), logical operators (&&, ||), and shell features like command substitution $(...)."
1049
1187
  )
1050
1188
  }),
@@ -1065,18 +1203,18 @@ Usage notes:
1065
1203
  * Use multiSelect: true to allow multiple answers to be selected for a question
1066
1204
  * If you recommend a specific option, make that the first option in the list and add "(Recommended)" at the end of the label
1067
1205
  `,
1068
- schema: z13__default.default.object({
1069
- questions: z13__default.default.array(
1070
- z13__default.default.object({
1071
- question: z13__default.default.string().describe("The full question text to display"),
1072
- header: z13__default.default.string().describe("Short label for the question (max 12 characters)"),
1073
- options: z13__default.default.array(
1074
- z13__default.default.object({
1075
- label: z13__default.default.string(),
1076
- description: z13__default.default.string()
1206
+ schema: z14__default.default.object({
1207
+ questions: z14__default.default.array(
1208
+ z14__default.default.object({
1209
+ question: z14__default.default.string().describe("The full question text to display"),
1210
+ header: z14__default.default.string().describe("Short label for the question (max 12 characters)"),
1211
+ options: z14__default.default.array(
1212
+ z14__default.default.object({
1213
+ label: z14__default.default.string(),
1214
+ description: z14__default.default.string()
1077
1215
  })
1078
1216
  ).min(0).max(4).describe("Array of 0-4 choices, each with label and description"),
1079
- multiSelect: z13__default.default.boolean().describe("If true, users can select multiple options")
1217
+ multiSelect: z14__default.default.boolean().describe("If true, users can select multiple options")
1080
1218
  })
1081
1219
  )
1082
1220
  }),
@@ -1084,14 +1222,14 @@ Usage notes:
1084
1222
  };
1085
1223
 
1086
1224
  // src/tools/ask-user-question/handler.ts
1087
- var createAskUserQuestionHandler = () => (args) => {
1225
+ var createAskUserQuestionHandler = () => async (args) => {
1088
1226
  return {
1089
1227
  toolResponse: "Question submitted",
1090
1228
  data: { questions: args.questions }
1091
1229
  };
1092
1230
  };
1093
1231
 
1094
- // node_modules/uuid/dist/esm-node/stringify.js
1232
+ // node_modules/uuid/dist/esm/stringify.js
1095
1233
  var byteToHex = [];
1096
1234
  for (let i = 0; i < 256; ++i) {
1097
1235
  byteToHex.push((i + 256).toString(16).slice(1));
@@ -1103,26 +1241,30 @@ var rnds8Pool = new Uint8Array(256);
1103
1241
  var poolPtr = rnds8Pool.length;
1104
1242
  function rng() {
1105
1243
  if (poolPtr > rnds8Pool.length - 16) {
1106
- crypto__default.default.randomFillSync(rnds8Pool);
1244
+ crypto.randomFillSync(rnds8Pool);
1107
1245
  poolPtr = 0;
1108
1246
  }
1109
1247
  return rnds8Pool.slice(poolPtr, poolPtr += 16);
1110
1248
  }
1111
- var native_default = {
1112
- randomUUID: crypto__default.default.randomUUID
1113
- };
1249
+ var native_default = { randomUUID: crypto.randomUUID };
1114
1250
 
1115
- // node_modules/uuid/dist/esm-node/v4.js
1251
+ // node_modules/uuid/dist/esm/v4.js
1116
1252
  function v4(options, buf, offset) {
1117
1253
  if (native_default.randomUUID && !buf && !options) {
1118
1254
  return native_default.randomUUID();
1119
1255
  }
1120
1256
  options = options || {};
1121
- const rnds = options.random || (options.rng || rng)();
1257
+ const rnds = options.random ?? options.rng?.() ?? rng();
1258
+ if (rnds.length < 16) {
1259
+ throw new Error("Random bytes length must be >= 16");
1260
+ }
1122
1261
  rnds[6] = rnds[6] & 15 | 64;
1123
1262
  rnds[8] = rnds[8] & 63 | 128;
1124
1263
  if (buf) {
1125
1264
  offset = offset || 0;
1265
+ if (offset < 0 || offset + 16 > buf.length) {
1266
+ throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`);
1267
+ }
1126
1268
  for (let i = 0; i < 16; ++i) {
1127
1269
  buf[offset + i] = rnds[i];
1128
1270
  }
@@ -1134,9 +1276,23 @@ var v4_default = v4;
1134
1276
 
1135
1277
  // src/lib/thread-manager.ts
1136
1278
  var THREAD_TTL_SECONDS = 60 * 60 * 24 * 90;
1279
+ var APPEND_IDEMPOTENT_SCRIPT = `
1280
+ if redis.call('EXISTS', KEYS[1]) == 1 then
1281
+ return 0
1282
+ end
1283
+ for i = 2, #ARGV do
1284
+ redis.call('RPUSH', KEYS[2], ARGV[i])
1285
+ end
1286
+ redis.call('EXPIRE', KEYS[2], tonumber(ARGV[1]))
1287
+ redis.call('SET', KEYS[1], '1', 'EX', tonumber(ARGV[1]))
1288
+ return 1
1289
+ `;
1137
1290
  function getThreadKey(threadId, key) {
1138
1291
  return `thread:${threadId}:${key}`;
1139
1292
  }
1293
+ function storedMessageId(msg) {
1294
+ return msg.data.id ?? "";
1295
+ }
1140
1296
  function createThreadManager(config) {
1141
1297
  const {
1142
1298
  redis,
@@ -1146,6 +1302,7 @@ function createThreadManager(config) {
1146
1302
  deserialize = (raw) => JSON.parse(raw)
1147
1303
  } = config;
1148
1304
  const redisKey = getThreadKey(threadId, key);
1305
+ const idOf = config.idOf ?? (!config.serialize ? storedMessageId : void 0);
1149
1306
  const base = {
1150
1307
  async initialize() {
1151
1308
  await redis.del(redisKey);
@@ -1155,7 +1312,19 @@ function createThreadManager(config) {
1155
1312
  return data.map(deserialize);
1156
1313
  },
1157
1314
  async append(messages) {
1158
- if (messages.length > 0) {
1315
+ if (messages.length === 0) return;
1316
+ if (idOf) {
1317
+ const dedupId = messages.map(idOf).join(":");
1318
+ const dedupKey = getThreadKey(threadId, `dedup:${dedupId}`);
1319
+ await redis.eval(
1320
+ APPEND_IDEMPOTENT_SCRIPT,
1321
+ 2,
1322
+ dedupKey,
1323
+ redisKey,
1324
+ String(THREAD_TTL_SECONDS),
1325
+ ...messages.map(serialize)
1326
+ );
1327
+ } else {
1159
1328
  await redis.rpush(redisKey, ...messages.map(serialize));
1160
1329
  await redis.expire(redisKey, THREAD_TTL_SECONDS);
1161
1330
  }
@@ -1190,6 +1359,7 @@ function createThreadManager(config) {
1190
1359
  },
1191
1360
  createToolMessage(content, toolCallId) {
1192
1361
  return new messages.ToolMessage({
1362
+ id: v4_default(),
1193
1363
  content,
1194
1364
  tool_call_id: toolCallId
1195
1365
  }).toDict();
@@ -1262,7 +1432,7 @@ async function invokeModel({
1262
1432
  const parentWorkflowId = info.workflowExecution.workflowId;
1263
1433
  const parentRunId = info.workflowExecution.runId;
1264
1434
  const handle = client.getHandle(parentWorkflowId, parentRunId);
1265
- const { tools } = await handle.query(`get${agentName}State`);
1435
+ const { tools } = await handle.query(agentQueryName(agentName));
1266
1436
  const messages$1 = await thread.load();
1267
1437
  const response = await model.invoke(
1268
1438
  [...messages.mapStoredMessagesToChatMessages(messages$1)],
@@ -1459,9 +1629,66 @@ var toTree = async (fs, opts = {}) => {
1459
1629
  const base = basename(dir, separator) + separator;
1460
1630
  return base + subtree;
1461
1631
  };
1632
+ var FileSystemSkillProvider = class {
1633
+ constructor(baseDir) {
1634
+ this.baseDir = baseDir;
1635
+ }
1636
+ async listSkills() {
1637
+ const dirs = await this.discoverSkillDirs();
1638
+ const skills = [];
1639
+ for (const dir of dirs) {
1640
+ const raw = await promises.readFile(path.join(this.baseDir, dir, "SKILL.md"), "utf-8");
1641
+ const { frontmatter } = parseSkillFile(raw);
1642
+ skills.push(frontmatter);
1643
+ }
1644
+ return skills;
1645
+ }
1646
+ async getSkill(name) {
1647
+ const raw = await promises.readFile(
1648
+ path.join(this.baseDir, name, "SKILL.md"),
1649
+ "utf-8"
1650
+ );
1651
+ const { frontmatter, body } = parseSkillFile(raw);
1652
+ if (frontmatter.name !== name) {
1653
+ throw new Error(
1654
+ `Skill directory "${name}" contains SKILL.md with mismatched name "${frontmatter.name}"`
1655
+ );
1656
+ }
1657
+ return { ...frontmatter, instructions: body };
1658
+ }
1659
+ /**
1660
+ * Convenience method to load all skills with full instructions.
1661
+ * Returns `Skill[]` ready to pass into a workflow.
1662
+ */
1663
+ async loadAll() {
1664
+ const dirs = await this.discoverSkillDirs();
1665
+ const skills = [];
1666
+ for (const dir of dirs) {
1667
+ const raw = await promises.readFile(path.join(this.baseDir, dir, "SKILL.md"), "utf-8");
1668
+ const { frontmatter, body } = parseSkillFile(raw);
1669
+ skills.push({ ...frontmatter, instructions: body });
1670
+ }
1671
+ return skills;
1672
+ }
1673
+ async discoverSkillDirs() {
1674
+ const entries = await promises.readdir(this.baseDir, { withFileTypes: true });
1675
+ const dirs = [];
1676
+ for (const entry of entries) {
1677
+ if (!entry.isDirectory()) continue;
1678
+ try {
1679
+ await promises.readFile(path.join(this.baseDir, entry.name, "SKILL.md"), "utf-8");
1680
+ dirs.push(entry.name);
1681
+ } catch {
1682
+ }
1683
+ }
1684
+ return dirs;
1685
+ }
1686
+ };
1462
1687
 
1463
- exports.AGENT_HANDLER_NAMES = AGENT_HANDLER_NAMES;
1688
+ exports.FileSystemSkillProvider = FileSystemSkillProvider;
1464
1689
  exports.ZeitlichPlugin = ZeitlichPlugin;
1690
+ exports.agentQueryName = agentQueryName;
1691
+ exports.agentStateChangeUpdateName = agentStateChangeUpdateName;
1465
1692
  exports.askUserQuestionTool = askUserQuestionTool;
1466
1693
  exports.bashTool = bashTool;
1467
1694
  exports.createAgentStateManager = createAgentStateManager;
@@ -1470,6 +1697,8 @@ exports.createBashHandler = createBashHandler;
1470
1697
  exports.createBashToolDescription = createBashToolDescription;
1471
1698
  exports.createEditHandler = createEditHandler;
1472
1699
  exports.createGlobHandler = createGlobHandler;
1700
+ exports.createReadSkillHandler = createReadSkillHandler;
1701
+ exports.createReadSkillTool = createReadSkillTool;
1473
1702
  exports.createSession = createSession;
1474
1703
  exports.createSharedActivities = createSharedActivities;
1475
1704
  exports.createSubagentTool = createSubagentTool;
@@ -1487,14 +1716,15 @@ exports.grepTool = grepTool;
1487
1716
  exports.hasNoOtherToolCalls = hasNoOtherToolCalls;
1488
1717
  exports.invokeModel = invokeModel;
1489
1718
  exports.isTerminalStatus = isTerminalStatus;
1719
+ exports.parseSkillFile = parseSkillFile;
1490
1720
  exports.proxyDefaultThreadOps = proxyDefaultThreadOps;
1491
- exports.readTool = readTool;
1721
+ exports.readFileTool = readFileTool;
1492
1722
  exports.taskCreateTool = taskCreateTool;
1493
1723
  exports.taskGetTool = taskGetTool;
1494
1724
  exports.taskListTool = taskListTool;
1495
1725
  exports.taskUpdateTool = taskUpdateTool;
1496
1726
  exports.toTree = toTree;
1497
1727
  exports.withAutoAppend = withAutoAppend;
1498
- exports.writeTool = writeTool;
1728
+ exports.writeFileTool = writeFileTool;
1499
1729
  //# sourceMappingURL=index.cjs.map
1500
1730
  //# sourceMappingURL=index.cjs.map