@robota-sdk/agent-sdk 3.0.0-beta.55 → 3.0.0-beta.57

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.
@@ -39,19 +39,23 @@ __export(index_exports, {
39
39
  BundlePluginInstaller: () => BundlePluginInstaller,
40
40
  BundlePluginLoader: () => BundlePluginLoader,
41
41
  CommandRegistry: () => CommandRegistry,
42
+ EditCheckpointStore: () => EditCheckpointStore,
42
43
  InteractiveSession: () => InteractiveSession,
44
+ MEMORY_INDEX_MAX_BYTES: () => MEMORY_INDEX_MAX_BYTES,
45
+ MEMORY_INDEX_MAX_LINES: () => MEMORY_INDEX_MAX_LINES,
43
46
  MarketplaceClient: () => MarketplaceClient,
44
47
  PluginCommandSource: () => PluginCommandSource,
45
48
  PluginSettingsStore: () => PluginSettingsStore,
49
+ ProjectMemoryStore: () => ProjectMemoryStore,
46
50
  PromptExecutor: () => PromptExecutor,
47
51
  SkillCommandSource: () => SkillCommandSource,
48
52
  SubagentManager: () => import_agent_runtime7.SubagentManager,
49
53
  SystemCommandExecutor: () => SystemCommandExecutor,
50
- TRUST_TO_MODE: () => import_agent_core4.TRUST_TO_MODE,
54
+ TRUST_TO_MODE: () => import_agent_core5.TRUST_TO_MODE,
51
55
  WorktreeSubagentRunner: () => import_agent_runtime8.WorktreeSubagentRunner,
52
56
  assembleSubagentPrompt: () => assembleSubagentPrompt,
53
57
  buildSkillPrompt: () => buildSkillPrompt,
54
- chatEntryToMessage: () => import_agent_core5.chatEntryToMessage,
58
+ chatEntryToMessage: () => import_agent_core6.chatEntryToMessage,
55
59
  createAgentTool: () => createAgentTool,
56
60
  createBackgroundProcessTool: () => createBackgroundProcessTool,
57
61
  createCommandExecutionTool: () => createCommandExecutionTool,
@@ -61,33 +65,44 @@ __export(index_exports, {
61
65
  createSubagentSession: () => createSubagentSession,
62
66
  createSystemCommands: () => createSystemCommands,
63
67
  createWorktreeSubagentRunner: () => import_agent_runtime8.createWorktreeSubagentRunner,
64
- evaluatePermission: () => import_agent_core6.evaluatePermission,
68
+ discoverTaskFiles: () => discoverTaskFiles,
69
+ evaluatePermission: () => import_agent_core7.evaluatePermission,
65
70
  executeSkill: () => executeSkill,
71
+ formatTaskContext: () => formatTaskContext,
66
72
  getBackgroundTaskTransitions: () => import_agent_runtime4.getBackgroundTaskTransitions,
67
73
  getBuiltInAgent: () => getBuiltInAgent,
68
74
  getForkWorkerSuffix: () => getForkWorkerSuffix,
69
- getMessagesForAPI: () => import_agent_core5.getMessagesForAPI,
75
+ getMessagesForAPI: () => import_agent_core6.getMessagesForAPI,
70
76
  getSubagentSuffix: () => getSubagentSuffix,
71
- isChatEntry: () => import_agent_core5.isChatEntry,
77
+ isChatEntry: () => import_agent_core6.isChatEntry,
78
+ isMemoryType: () => isMemoryType,
72
79
  isTerminalBackgroundTaskStatus: () => import_agent_runtime4.isTerminalBackgroundTaskStatus,
73
- messageToHistoryEntry: () => import_agent_core5.messageToHistoryEntry,
80
+ loadTaskContext: () => loadTaskContext,
81
+ messageToHistoryEntry: () => import_agent_core6.messageToHistoryEntry,
74
82
  parseFrontmatter: () => parseFrontmatter,
83
+ parseTaskFile: () => parseTaskFile,
84
+ planSelfHostingVerification: () => planSelfHostingVerification,
75
85
  preprocessShellCommands: () => preprocessShellCommands,
76
86
  projectPaths: () => projectPaths,
77
87
  promptForApproval: () => promptForApproval,
88
+ readCurrentGitBranch: () => readCurrentGitBranch,
78
89
  resolveSubagentLogDir: () => resolveSubagentLogDir,
79
90
  retrieveAgentToolDeps: () => retrieveAgentToolDeps,
80
- runHooks: () => import_agent_core7.runHooks,
91
+ runHooks: () => import_agent_core8.runHooks,
92
+ selectRelevantTasks: () => selectRelevantTasks,
81
93
  storeAgentToolDeps: () => storeAgentToolDeps,
82
94
  substituteVariables: () => substituteVariables,
83
95
  summarizeBackgroundJobGroup: () => summarizeBackgroundJobGroup,
84
96
  transitionBackgroundTaskStatus: () => import_agent_runtime4.transitionBackgroundTaskStatus,
85
- userPaths: () => userPaths
97
+ transitionSelfHostingLoop: () => transitionSelfHostingLoop,
98
+ updateTaskFileStatus: () => updateTaskFileStatus,
99
+ userPaths: () => userPaths,
100
+ wrapEditCheckpointTools: () => wrapEditCheckpointTools
86
101
  });
87
102
  module.exports = __toCommonJS(index_exports);
88
103
 
89
104
  // src/interactive/interactive-session.ts
90
- var import_agent_core3 = require("@robota-sdk/agent-core");
105
+ var import_agent_core4 = require("@robota-sdk/agent-core");
91
106
 
92
107
  // src/commands/capability-descriptors.ts
93
108
  function inferKind(command) {
@@ -186,6 +201,28 @@ function buildBackgroundSubcommands() {
186
201
  { name: "close", description: "Dismiss a terminal background task", source: "builtin" }
187
202
  ];
188
203
  }
204
+ function buildRewindSubcommands() {
205
+ return [
206
+ { name: "list", description: "List edit checkpoints", source: "builtin" },
207
+ { name: "restore", description: "Restore code to a checkpoint", source: "builtin" },
208
+ { name: "code", description: "Restore code to a checkpoint", source: "builtin" }
209
+ ];
210
+ }
211
+ function buildMemorySubcommands() {
212
+ return [
213
+ { name: "list", description: "List project memory topics", source: "builtin" },
214
+ { name: "show", description: "Show project memory index or a topic", source: "builtin" },
215
+ { name: "add", description: "Save durable project memory", source: "builtin" },
216
+ { name: "pending", description: "List pending memory candidates", source: "builtin" },
217
+ { name: "approve", description: "Approve a pending memory candidate", source: "builtin" },
218
+ { name: "reject", description: "Reject a pending memory candidate", source: "builtin" },
219
+ {
220
+ name: "used",
221
+ description: "Show memory references used in the current turn",
222
+ source: "builtin"
223
+ }
224
+ ];
225
+ }
189
226
  function createBuiltinCommands() {
190
227
  return [
191
228
  { name: "help", description: "Show available commands", source: "builtin" },
@@ -222,6 +259,18 @@ function createBuiltinCommands() {
222
259
  { name: "cost", description: "Show session info", source: "builtin" },
223
260
  { name: "context", description: "Context window info", source: "builtin" },
224
261
  { name: "permissions", description: "Permission rules", source: "builtin" },
262
+ {
263
+ name: "memory",
264
+ description: "Inspect, save, review, and audit project memory",
265
+ source: "builtin",
266
+ subcommands: buildMemorySubcommands()
267
+ },
268
+ {
269
+ name: "rewind",
270
+ description: "List and restore edit checkpoints",
271
+ source: "builtin",
272
+ subcommands: buildRewindSubcommands()
273
+ },
225
274
  {
226
275
  name: "provider",
227
276
  description: "Manage provider profiles",
@@ -468,8 +517,502 @@ Next offset: ${page.nextCursor.offset}` : "";
468
517
  return { message: `Unknown background action: ${action}`, success: false };
469
518
  }
470
519
 
520
+ // src/memory/pending-memory-store.ts
521
+ var import_node_fs2 = require("fs");
522
+ var import_node_path2 = require("path");
523
+ var PENDING_FILENAME = "pending.json";
524
+ function memoryRoot(cwd) {
525
+ return (0, import_node_path2.join)(cwd, ".robota", "memory");
526
+ }
527
+ function emptyDocument() {
528
+ return { version: 1, records: [] };
529
+ }
530
+ var PendingMemoryStore = class {
531
+ path;
532
+ now;
533
+ constructor(cwd, now = () => /* @__PURE__ */ new Date()) {
534
+ this.path = (0, import_node_path2.join)(memoryRoot(cwd), PENDING_FILENAME);
535
+ this.now = now;
536
+ }
537
+ getPath() {
538
+ return this.path;
539
+ }
540
+ list(status) {
541
+ const records = this.read().records;
542
+ return status ? records.filter((record) => record.status === status) : records;
543
+ }
544
+ get(id) {
545
+ return this.read().records.find((record) => record.id === id);
546
+ }
547
+ upsert(candidate, status, reason) {
548
+ const document = this.read();
549
+ const updatedAt = this.now().toISOString();
550
+ const existingIndex = document.records.findIndex((record2) => record2.id === candidate.id);
551
+ const record = {
552
+ ...candidate,
553
+ status,
554
+ updatedAt,
555
+ decisionReason: reason
556
+ };
557
+ if (existingIndex >= 0) {
558
+ document.records[existingIndex] = { ...document.records[existingIndex], ...record };
559
+ } else {
560
+ document.records.push(record);
561
+ }
562
+ this.write(document);
563
+ }
564
+ mark(id, status, reason) {
565
+ const document = this.read();
566
+ const index = document.records.findIndex((record2) => record2.id === id);
567
+ if (index < 0) throw new Error(`Memory candidate not found: ${id}`);
568
+ const record = {
569
+ ...document.records[index],
570
+ status,
571
+ updatedAt: this.now().toISOString(),
572
+ decisionReason: reason
573
+ };
574
+ document.records[index] = record;
575
+ this.write(document);
576
+ return record;
577
+ }
578
+ read() {
579
+ if (!(0, import_node_fs2.existsSync)(this.path)) return emptyDocument();
580
+ try {
581
+ const parsed = JSON.parse((0, import_node_fs2.readFileSync)(this.path, "utf8"));
582
+ return { version: 1, records: parsed.records ?? [] };
583
+ } catch {
584
+ return emptyDocument();
585
+ }
586
+ }
587
+ write(document) {
588
+ (0, import_node_fs2.mkdirSync)((0, import_node_path2.dirname)(this.path), { recursive: true });
589
+ (0, import_node_fs2.writeFileSync)(this.path, JSON.stringify(document, null, 2), "utf8");
590
+ }
591
+ };
592
+
593
+ // src/memory/project-memory-store.ts
594
+ var import_fs = require("fs");
595
+ var import_path = require("path");
596
+ var MEMORY_INDEX_MAX_LINES = 200;
597
+ var MEMORY_INDEX_MAX_BYTES = Number("25600");
598
+ var INDEX_FILENAME = "MEMORY.md";
599
+ var TOPICS_DIRNAME = "topics";
600
+ var DATE_LENGTH = 10;
601
+ var MAX_TOPIC_LENGTH = 80;
602
+ var DEFAULT_TOPIC = "general";
603
+ var TOPIC_EXTENSION = ".md";
604
+ var VALID_TYPES = ["user", "feedback", "project", "reference"];
605
+ function isMemoryType(value) {
606
+ return VALID_TYPES.includes(value);
607
+ }
608
+ function memoryRoot2(cwd) {
609
+ return (0, import_path.join)(cwd, ".robota", "memory");
610
+ }
611
+ function truncateToUtf8Bytes(value, maxBytes) {
612
+ const buffer = Buffer.from(value, "utf8");
613
+ if (buffer.byteLength <= maxBytes) return value;
614
+ return buffer.subarray(0, maxBytes).toString("utf8");
615
+ }
616
+ function limitLines(value, maxLines) {
617
+ const lines = value.split(/\r?\n/);
618
+ const limited = lines.slice(0, maxLines);
619
+ return {
620
+ content: limited.join("\n").trimEnd(),
621
+ truncated: lines.length > maxLines
622
+ };
623
+ }
624
+ function sanitizeTopic(topic) {
625
+ const normalized = topic.trim().toLowerCase().replace(/[^a-z0-9가-힣_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, MAX_TOPIC_LENGTH);
626
+ return normalized || DEFAULT_TOPIC;
627
+ }
628
+ function formatEntry(date, input, topic) {
629
+ const day = date.toISOString().slice(0, DATE_LENGTH);
630
+ const text = input.text.trim().replace(/\s+/g, " ");
631
+ return `[${day}] (${input.type}/${topic}) ${text}`;
632
+ }
633
+ function normalizeMemoryText(text) {
634
+ return text.trim().replace(/\s+/g, " ");
635
+ }
636
+ var ProjectMemoryStore = class {
637
+ cwd;
638
+ now;
639
+ constructor(cwd, now = () => /* @__PURE__ */ new Date()) {
640
+ this.cwd = cwd;
641
+ this.now = now;
642
+ }
643
+ getIndexPath() {
644
+ return (0, import_path.join)(memoryRoot2(this.cwd), INDEX_FILENAME);
645
+ }
646
+ getTopicsPath() {
647
+ return (0, import_path.join)(memoryRoot2(this.cwd), TOPICS_DIRNAME);
648
+ }
649
+ loadStartupMemory() {
650
+ const path = this.getIndexPath();
651
+ if (!(0, import_fs.existsSync)(path)) {
652
+ return { content: "", path, lineCount: 0, truncated: false };
653
+ }
654
+ const raw = (0, import_fs.readFileSync)(path, "utf8");
655
+ const byBytes = truncateToUtf8Bytes(raw, MEMORY_INDEX_MAX_BYTES);
656
+ const byteTruncated = Buffer.byteLength(raw, "utf8") > MEMORY_INDEX_MAX_BYTES;
657
+ const byLines = limitLines(byBytes, MEMORY_INDEX_MAX_LINES);
658
+ return {
659
+ content: byLines.content,
660
+ path,
661
+ lineCount: byLines.content.length === 0 ? 0 : byLines.content.split(/\r?\n/).length,
662
+ truncated: byteTruncated || byLines.truncated
663
+ };
664
+ }
665
+ list() {
666
+ const topicsPath = this.getTopicsPath();
667
+ const topics = (0, import_fs.existsSync)(topicsPath) ? (0, import_fs.readdirSync)(topicsPath, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(TOPIC_EXTENSION)).map((entry) => ({
668
+ name: (0, import_path.basename)(entry.name, TOPIC_EXTENSION),
669
+ path: (0, import_path.join)(topicsPath, entry.name)
670
+ })).sort((a, b) => a.name.localeCompare(b.name)) : [];
671
+ return {
672
+ indexPath: this.getIndexPath(),
673
+ topicsPath,
674
+ topics
675
+ };
676
+ }
677
+ readTopic(topic) {
678
+ const normalized = sanitizeTopic(topic);
679
+ const path = (0, import_path.join)(this.getTopicsPath(), `${normalized}${TOPIC_EXTENSION}`);
680
+ if (!(0, import_fs.existsSync)(path)) return "";
681
+ return (0, import_fs.readFileSync)(path, "utf8").trimEnd();
682
+ }
683
+ append(input) {
684
+ const topic = sanitizeTopic(input.topic);
685
+ const root = memoryRoot2(this.cwd);
686
+ const topicsPath = this.getTopicsPath();
687
+ (0, import_fs.mkdirSync)(topicsPath, { recursive: true });
688
+ const indexPath = this.getIndexPath();
689
+ const topicPath = (0, import_path.join)(topicsPath, `${topic}${TOPIC_EXTENSION}`);
690
+ const entry = formatEntry(this.now(), input, topic);
691
+ const topicHeader = (0, import_fs.existsSync)(topicPath) ? "" : `# ${topic}
692
+
693
+ `;
694
+ const normalizedText = normalizeMemoryText(input.text);
695
+ if ((0, import_fs.existsSync)(topicPath) && (0, import_fs.readFileSync)(topicPath, "utf8").includes(`) ${normalizedText}`)) {
696
+ return { indexPath, topicPath, topic, deduplicated: true };
697
+ }
698
+ if (!(0, import_fs.existsSync)(indexPath)) {
699
+ (0, import_fs.mkdirSync)(root, { recursive: true });
700
+ (0, import_fs.writeFileSync)(indexPath, "# Project Memory\n\n", "utf8");
701
+ }
702
+ (0, import_fs.appendFileSync)(indexPath, `- ${entry}
703
+ `, "utf8");
704
+ (0, import_fs.appendFileSync)(topicPath, `${topicHeader}- ${entry}
705
+ `, "utf8");
706
+ return { indexPath, topicPath, topic, deduplicated: false };
707
+ }
708
+ };
709
+
710
+ // src/memory/memory-policy-evaluator.ts
711
+ var SENSITIVE_PATTERNS = [
712
+ /\b(api[_-]?key|secret|token|password|private key)\b/i,
713
+ /\b\d{3}-\d{2}-\d{4}\b/,
714
+ /\b\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}\b/,
715
+ /주민등록|비밀번호|시크릿|토큰/u
716
+ ];
717
+ function containsSensitiveMemoryContent(text) {
718
+ return SENSITIVE_PATTERNS.some((pattern) => pattern.test(text));
719
+ }
720
+
721
+ // src/commands/memory-command.ts
722
+ var SUBCOMMAND_INDEX = 0;
723
+ var TYPE_INDEX = 1;
724
+ var TOPIC_INDEX = 2;
725
+ var TEXT_START_INDEX = 3;
726
+ function usage() {
727
+ return {
728
+ message: "Usage: memory list | memory show [topic] | memory add <user|feedback|project|reference> <topic> <text> | memory pending | memory approve <id> | memory reject <id> | memory used",
729
+ success: false
730
+ };
731
+ }
732
+ function formatList(store) {
733
+ const summary = store.list();
734
+ const topics = summary.topics.length > 0 ? summary.topics.map((topic) => `- ${topic.name}: ${topic.path}`).join("\n") : "(none)";
735
+ return {
736
+ message: [
737
+ `Memory index: ${summary.indexPath}`,
738
+ `Topics directory: ${summary.topicsPath}`,
739
+ "Topics:",
740
+ topics
741
+ ].join("\n"),
742
+ success: true,
743
+ data: {
744
+ indexPath: summary.indexPath,
745
+ topicsPath: summary.topicsPath,
746
+ topicCount: summary.topics.length
747
+ }
748
+ };
749
+ }
750
+ function formatShow(store, topic) {
751
+ if (!topic || topic === "index") {
752
+ const memory = store.loadStartupMemory();
753
+ return {
754
+ message: memory.content || "(empty memory index)",
755
+ success: true,
756
+ data: {
757
+ path: memory.path,
758
+ lineCount: memory.lineCount,
759
+ truncated: memory.truncated
760
+ }
761
+ };
762
+ }
763
+ const content = store.readTopic(topic);
764
+ return {
765
+ message: content || `(empty memory topic: ${topic})`,
766
+ success: true,
767
+ data: { topic }
768
+ };
769
+ }
770
+ function parseAdd(args) {
771
+ const type = args[TYPE_INDEX];
772
+ const topic = args[TOPIC_INDEX];
773
+ const text = args.slice(TEXT_START_INDEX).join(" ").trim();
774
+ if (!type || !isMemoryType(type) || !topic || text.length === 0) return void 0;
775
+ return { type, topic, text };
776
+ }
777
+ function formatPending(store) {
778
+ const records = store.list("pending");
779
+ const lines = records.length > 0 ? records.map(
780
+ (record) => `- ${record.id} ${record.type}/${record.topic} confidence=${record.confidence}: ${record.text}`
781
+ ) : ["(no pending memory candidates)"];
782
+ return {
783
+ message: ["Pending memory candidates:", ...lines].join("\n"),
784
+ success: true,
785
+ data: { count: records.length }
786
+ };
787
+ }
788
+ function recordEvent(session, event) {
789
+ session.recordMemoryEvent({
790
+ ...event,
791
+ at: (/* @__PURE__ */ new Date()).toISOString()
792
+ });
793
+ }
794
+ function approvePending(session, pendingStore, memoryStore, id) {
795
+ if (!id) return usage();
796
+ try {
797
+ const approved = pendingStore.mark(id, "approved", "approved-by-user");
798
+ const saved = memoryStore.append(approved);
799
+ const record = pendingStore.mark(id, "saved", "approved-and-saved");
800
+ recordEvent(session, {
801
+ type: "memory_candidate_approved",
802
+ candidateId: record.id,
803
+ topic: record.topic,
804
+ reason: "approved-by-user"
805
+ });
806
+ recordEvent(session, {
807
+ type: "memory_candidate_saved",
808
+ candidateId: record.id,
809
+ topic: record.topic,
810
+ reason: saved.deduplicated ? "deduplicated" : "approved-and-saved"
811
+ });
812
+ return {
813
+ message: saved.deduplicated ? `Saved memory candidate ${id} was already present in ${saved.topicPath}` : `Saved memory candidate ${id} to ${saved.topicPath}`,
814
+ success: true,
815
+ data: {
816
+ id,
817
+ status: record.status,
818
+ topic: saved.topic,
819
+ topicPath: saved.topicPath,
820
+ deduplicated: saved.deduplicated
821
+ }
822
+ };
823
+ } catch (error) {
824
+ return {
825
+ message: error instanceof Error ? error.message : String(error),
826
+ success: false
827
+ };
828
+ }
829
+ }
830
+ function rejectPending(session, pendingStore, id) {
831
+ if (!id) return usage();
832
+ try {
833
+ const record = pendingStore.mark(id, "rejected", "rejected-by-user");
834
+ recordEvent(session, {
835
+ type: "memory_candidate_rejected",
836
+ candidateId: record.id,
837
+ topic: record.topic,
838
+ reason: "rejected-by-user"
839
+ });
840
+ return {
841
+ message: `Rejected memory candidate ${id}`,
842
+ success: true,
843
+ data: { id, status: record.status }
844
+ };
845
+ } catch (error) {
846
+ return {
847
+ message: error instanceof Error ? error.message : String(error),
848
+ success: false
849
+ };
850
+ }
851
+ }
852
+ function formatUsed(session) {
853
+ const references = session.getUsedMemoryReferences();
854
+ const lines = references.length > 0 ? references.map((reference) => {
855
+ const suffix = reference.truncated ? " truncated=true" : "";
856
+ return `- ${reference.topic} score=${reference.score}${suffix}: ${reference.path}`;
857
+ }) : ["(no memory used in current turn)"];
858
+ return {
859
+ message: ["Used memory references:", ...lines].join("\n"),
860
+ success: true,
861
+ data: { count: references.length, references }
862
+ };
863
+ }
864
+ function executeMemoryCommand(session, rawArgs) {
865
+ const args = rawArgs.trim().split(/\s+/).filter(Boolean);
866
+ const subcommand = args[SUBCOMMAND_INDEX] ?? "list";
867
+ const store = new ProjectMemoryStore(session.getCwd());
868
+ const pendingStore = new PendingMemoryStore(session.getCwd());
869
+ if (subcommand === "list") return formatList(store);
870
+ if (subcommand === "show") return formatShow(store, args[TYPE_INDEX]);
871
+ if (subcommand === "pending") return formatPending(pendingStore);
872
+ if (subcommand === "approve")
873
+ return approvePending(session, pendingStore, store, args[TYPE_INDEX]);
874
+ if (subcommand === "reject") return rejectPending(session, pendingStore, args[TYPE_INDEX]);
875
+ if (subcommand === "used") return formatUsed(session);
876
+ if (subcommand === "add") {
877
+ const input = parseAdd(args);
878
+ if (!input) return usage();
879
+ if (containsSensitiveMemoryContent(input.text)) {
880
+ return {
881
+ message: "Refusing to save sensitive memory content.",
882
+ success: false
883
+ };
884
+ }
885
+ const result = store.append(input);
886
+ return {
887
+ message: result.deduplicated ? `${input.type} memory already exists in ${result.topicPath}` : `Saved ${input.type} memory to ${result.topicPath}`,
888
+ success: true,
889
+ data: {
890
+ indexPath: result.indexPath,
891
+ topicPath: result.topicPath,
892
+ topic: result.topic,
893
+ deduplicated: result.deduplicated
894
+ }
895
+ };
896
+ }
897
+ return usage();
898
+ }
899
+
900
+ // src/commands/rewind-command.ts
901
+ var SUBCOMMAND_INDEX2 = 0;
902
+ var CHECKPOINT_ID_INDEX = 1;
903
+ var PROMPT_PREVIEW_LENGTH = 120;
904
+ var ELLIPSIS_LENGTH = 3;
905
+ function usage2() {
906
+ return {
907
+ message: "Usage: rewind [list] | rewind restore <checkpoint-id> | rewind code <checkpoint-id>",
908
+ success: false
909
+ };
910
+ }
911
+ function formatPrompt(prompt) {
912
+ const compact = prompt.replace(/\s+/g, " ").trim();
913
+ if (compact.length <= PROMPT_PREVIEW_LENGTH) return compact;
914
+ return `${compact.slice(0, PROMPT_PREVIEW_LENGTH - ELLIPSIS_LENGTH)}...`;
915
+ }
916
+ function formatList2(checkpoints) {
917
+ const lines = checkpoints.length > 0 ? checkpoints.map(
918
+ (checkpoint) => `- ${checkpoint.id} files=${checkpoint.fileCount} ${checkpoint.createdAt} ${formatPrompt(
919
+ checkpoint.prompt
920
+ )}`
921
+ ) : ["(no edit checkpoints)"];
922
+ return {
923
+ message: ["Edit checkpoints:", ...lines].join("\n"),
924
+ success: true,
925
+ data: { count: checkpoints.length, checkpoints: [...checkpoints] }
926
+ };
927
+ }
928
+ async function restore(session, checkpointId) {
929
+ if (!checkpointId) return usage2();
930
+ try {
931
+ const result = await session.restoreEditCheckpoint(checkpointId);
932
+ return {
933
+ message: [
934
+ `Restored code to ${result.target.id}.`,
935
+ `Restored files: ${result.restoredFileCount}`,
936
+ `Rolled back checkpoints: ${result.restoredCheckpointCount}`
937
+ ].join("\n"),
938
+ success: true,
939
+ data: {
940
+ target: result.target,
941
+ restoredCheckpointCount: result.restoredCheckpointCount,
942
+ restoredFileCount: result.restoredFileCount,
943
+ removedCheckpointCount: result.removedCheckpointCount
944
+ }
945
+ };
946
+ } catch (error) {
947
+ return {
948
+ message: error instanceof Error ? error.message : String(error),
949
+ success: false
950
+ };
951
+ }
952
+ }
953
+ async function executeRewindCommand(session, rawArgs) {
954
+ const args = rawArgs.trim().split(/\s+/).filter(Boolean);
955
+ const subcommand = args[SUBCOMMAND_INDEX2] ?? "list";
956
+ if (subcommand === "list") {
957
+ return formatList2(session.listEditCheckpoints());
958
+ }
959
+ if (subcommand === "restore" || subcommand === "code") {
960
+ return restore(session, args[CHECKPOINT_ID_INDEX]);
961
+ }
962
+ return usage2();
963
+ }
964
+
965
+ // src/commands/system-command-executor.ts
966
+ var SystemCommandExecutor = class {
967
+ commands;
968
+ constructor(commands) {
969
+ this.commands = /* @__PURE__ */ new Map();
970
+ for (const cmd of commands ?? createSystemCommands()) {
971
+ this.commands.set(cmd.name, cmd);
972
+ }
973
+ }
974
+ /** Register an additional command. */
975
+ register(command) {
976
+ this.commands.set(command.name, command);
977
+ }
978
+ /** Execute a command by name. Returns null if command not found. */
979
+ async execute(name, session, args) {
980
+ const cmd = this.commands.get(name);
981
+ if (!cmd) return null;
982
+ return await cmd.execute(session, args);
983
+ }
984
+ /** List all registered commands. */
985
+ listCommands() {
986
+ return [...this.commands.values()];
987
+ }
988
+ listModelInvocableCommands() {
989
+ return this.listCommands().filter((command) => command.modelInvocable === true).map((command) => ({
990
+ name: `/${command.name}`,
991
+ kind: "builtin-command",
992
+ description: command.description,
993
+ userInvocable: command.userInvocable !== false,
994
+ modelInvocable: true,
995
+ ...command.argumentHint ? { argumentHint: command.argumentHint } : {},
996
+ ...command.safety ? { safety: command.safety } : {}
997
+ }));
998
+ }
999
+ isModelInvocable(name) {
1000
+ return this.commands.get(name)?.modelInvocable === true;
1001
+ }
1002
+ async executeModelInvocable(name, session, args) {
1003
+ if (!this.isModelInvocable(name)) return null;
1004
+ return this.execute(name, session, args);
1005
+ }
1006
+ /** Check if a command exists. */
1007
+ hasCommand(name) {
1008
+ return this.commands.has(name);
1009
+ }
1010
+ };
1011
+
471
1012
  // src/commands/system-command.ts
472
1013
  var VALID_MODES = ["plan", "default", "acceptEdits", "bypassPermissions"];
1014
+ var MEMORY_COMMAND_DESCRIPTION = "Project memory command. Use it to inspect project memory when stored context may help, save durable preferences, project conventions, feedback, or references worth reusing across sessions, review pending candidates, and report memory provenance. Do not store secrets, credentials, or transient facts.";
1015
+ var MEMORY_COMMAND_ARGUMENT_HINT = "list | show [topic] | add <user|feedback|project|reference> <topic> <text> | pending | approve <id> | reject <id> | used";
473
1016
  function createSystemCommands() {
474
1017
  return [
475
1018
  {
@@ -487,6 +1030,8 @@ function createSystemCommands() {
487
1030
  " cost \u2014 Show session info",
488
1031
  " context \u2014 Context window info",
489
1032
  " permissions \u2014 Permission rules",
1033
+ " memory \u2014 Manage project memory and pending candidates",
1034
+ " rewind \u2014 List or restore edit checkpoints",
490
1035
  " provider \u2014 Provider profile status and switching",
491
1036
  " resume \u2014 Resume a previous session",
492
1037
  " background \u2014 List/cancel/close background tasks",
@@ -629,6 +1174,21 @@ Messages: ${messageCount}`,
629
1174
  };
630
1175
  }
631
1176
  },
1177
+ {
1178
+ name: "memory",
1179
+ description: MEMORY_COMMAND_DESCRIPTION,
1180
+ modelInvocable: true,
1181
+ argumentHint: MEMORY_COMMAND_ARGUMENT_HINT,
1182
+ safety: "write",
1183
+ execute: executeMemoryCommand
1184
+ },
1185
+ {
1186
+ name: "rewind",
1187
+ description: "List edit checkpoints or restore code to a previous checkpoint.",
1188
+ argumentHint: "list | restore CHECKPOINT_ID | code CHECKPOINT_ID",
1189
+ safety: "write",
1190
+ execute: executeRewindCommand
1191
+ },
632
1192
  {
633
1193
  name: "resume",
634
1194
  description: "Resume a previous session",
@@ -671,51 +1231,6 @@ Messages: ${messageCount}`,
671
1231
  }
672
1232
  ];
673
1233
  }
674
- var SystemCommandExecutor = class {
675
- commands;
676
- constructor(commands) {
677
- this.commands = /* @__PURE__ */ new Map();
678
- for (const cmd of commands ?? createSystemCommands()) {
679
- this.commands.set(cmd.name, cmd);
680
- }
681
- }
682
- /** Register an additional command. */
683
- register(command) {
684
- this.commands.set(command.name, command);
685
- }
686
- /** Execute a command by name. Returns null if command not found. */
687
- async execute(name, session, args) {
688
- const cmd = this.commands.get(name);
689
- if (!cmd) return null;
690
- return await cmd.execute(session, args);
691
- }
692
- /** List all registered commands. */
693
- listCommands() {
694
- return [...this.commands.values()];
695
- }
696
- listModelInvocableCommands() {
697
- return this.listCommands().filter((command) => command.modelInvocable === true).map((command) => ({
698
- name: `/${command.name}`,
699
- kind: "builtin-command",
700
- description: command.description,
701
- userInvocable: command.userInvocable !== false,
702
- modelInvocable: true,
703
- ...command.argumentHint ? { argumentHint: command.argumentHint } : {},
704
- ...command.safety ? { safety: command.safety } : {}
705
- }));
706
- }
707
- isModelInvocable(name) {
708
- return this.commands.get(name)?.modelInvocable === true;
709
- }
710
- async executeModelInvocable(name, session, args) {
711
- if (!this.isModelInvocable(name)) return null;
712
- return this.execute(name, session, args);
713
- }
714
- /** Check if a command exists. */
715
- hasCommand(name) {
716
- return this.commands.has(name);
717
- }
718
- };
719
1234
 
720
1235
  // src/utils/skill-prompt.ts
721
1236
  var import_node_child_process = require("child_process");
@@ -1048,11 +1563,11 @@ function createInProcessSubagentRunner(deps) {
1048
1563
 
1049
1564
  // src/tools/agent-tool.ts
1050
1565
  var AGENT_TOOL_DESCRIPTION = [
1051
- "Creates one subagent job for delegated work in an isolated context.",
1052
- "One tool call creates one subagent job.",
1566
+ "Creates delegated subagent jobs in isolated contexts.",
1567
+ "Without jobs, one tool call creates one subagent job from prompt.",
1568
+ "For explicit multi-agent or parallel-agent requests, use one Agent tool call with jobs containing one entry per requested role.",
1053
1569
  "When the user explicitly asks to create, run, spawn, delegate to, or use agents/subagents, start the requested subagent job immediately.",
1054
1570
  "Do not ask a follow-up question unless execution is impossible or unsafe.",
1055
- "For multiple or parallel agents, create one Agent tool call per requested role in the current turn.",
1056
1571
  "Subagent jobs run as background tasks by default.",
1057
1572
  "The tool waits for a terminal result and returns completed, failed, or timed-out outcome data to the parent conversation.",
1058
1573
  "Execution is represented by a real tool call and runtime background task event."
@@ -1060,11 +1575,11 @@ var AGENT_TOOL_DESCRIPTION = [
1060
1575
  function createAgentToolPromptDescription(agentDefinitions = []) {
1061
1576
  const availableAgents = agentDefinitions.length > 0 ? ` Available agent types: ${agentDefinitions.map((agent) => `${agent.name} (${agent.description})`).join(", ")}.` : "";
1062
1577
  return [
1063
- "Agent \u2014 creates one isolated subagent job.",
1064
- "One Agent tool call corresponds to one subagent job.",
1578
+ "Agent \u2014 creates isolated subagent jobs.",
1579
+ "Without jobs, one Agent tool call corresponds to one subagent job.",
1580
+ "For explicit multi-agent or parallel-agent requests, use one Agent tool call with jobs containing one entry per requested role.",
1065
1581
  "When the user explicitly asks to create, run, spawn, delegate to, or use agents/subagents, start the requested subagent job immediately.",
1066
1582
  "Do not ask a follow-up question unless execution is impossible or unsafe.",
1067
- "For multiple or parallel agents, create one Agent tool call per requested role in the current turn.",
1068
1583
  "The tool returns terminal result data.",
1069
1584
  "Runtime mode is background.",
1070
1585
  availableAgents
@@ -1074,10 +1589,18 @@ function asZodSchema(schema) {
1074
1589
  return schema;
1075
1590
  }
1076
1591
  var AgentSchema = import_zod.z.object({
1077
- prompt: import_zod.z.string().describe("The task for the subagent to perform"),
1592
+ prompt: import_zod.z.string().optional().describe("The task for a single subagent to perform. Required when jobs is omitted."),
1078
1593
  subagent_type: import_zod.z.string().optional().describe('Agent type: "general-purpose", "Explore", "Plan", or a custom agent name'),
1079
1594
  model: import_zod.z.string().optional().describe("Optional model override"),
1080
- isolation: import_zod.z.enum(["none", "worktree"]).optional().describe('Optional runtime isolation mode. "worktree" runs in a Git worktree.')
1595
+ isolation: import_zod.z.enum(["none", "worktree"]).optional().describe('Optional runtime isolation mode. "worktree" runs in a Git worktree.'),
1596
+ jobs: import_zod.z.array(
1597
+ import_zod.z.object({
1598
+ prompt: import_zod.z.string().describe("The task for this subagent to perform"),
1599
+ subagent_type: import_zod.z.string().optional().describe("Agent type for this job"),
1600
+ model: import_zod.z.string().optional().describe("Optional model override for this job"),
1601
+ isolation: import_zod.z.enum(["none", "worktree"]).optional().describe("Isolation for this job")
1602
+ }).passthrough()
1603
+ ).optional().describe("Batch of subagent jobs to start in one Agent tool call")
1081
1604
  }).passthrough();
1082
1605
  var sessionDepsStore = /* @__PURE__ */ new WeakMap();
1083
1606
  function storeAgentToolDeps(key, deps) {
@@ -1140,7 +1663,22 @@ function stringifyAgentError(message, agentId) {
1140
1663
  agentId
1141
1664
  });
1142
1665
  }
1666
+ function stringifyAgentBatchResult(input) {
1667
+ const successfulJobs = input.jobs.filter((job) => job.success);
1668
+ const agentIds = input.jobs.map((job) => job.agentId).filter((agentId) => typeof agentId === "string" && agentId.length > 0);
1669
+ return JSON.stringify({
1670
+ success: input.jobs.every((job) => job.success),
1671
+ output: successfulJobs.map((job) => job.output ?? "").filter(Boolean).join("\n\n"),
1672
+ groupId: input.groupId,
1673
+ agentIds,
1674
+ jobs: input.jobs
1675
+ });
1676
+ }
1143
1677
  async function runManagedAgent(args, deps, manager) {
1678
+ if (typeof args.prompt !== "string" || args.prompt.length === 0) {
1679
+ return stringifyAgentError("prompt is required when jobs is omitted");
1680
+ }
1681
+ const singleArgs = { ...args, prompt: args.prompt };
1144
1682
  const agentType = args.subagent_type ?? "general-purpose";
1145
1683
  const agentDef = resolveAgentDefinition2(agentType, deps.customAgentRegistry);
1146
1684
  if (!agentDef) {
@@ -1148,7 +1686,7 @@ async function runManagedAgent(args, deps, manager) {
1148
1686
  }
1149
1687
  let agentId;
1150
1688
  try {
1151
- const state = await manager.spawn(createSpawnRequest(args, agentType, agentDef, deps));
1689
+ const state = await manager.spawn(createSpawnRequest(singleArgs, agentType, agentDef, deps));
1152
1690
  agentId = state.id;
1153
1691
  const response = await manager.wait(state.id);
1154
1692
  return stringifyAgentSuccess(response);
@@ -1157,14 +1695,86 @@ async function runManagedAgent(args, deps, manager) {
1157
1695
  return stringifyAgentError(message, agentId);
1158
1696
  }
1159
1697
  }
1160
- function createAgentTool(deps) {
1161
- const manager = createSubagentManager(deps);
1162
- return (0, import_agent_tools.createZodFunctionTool)(
1163
- "Agent",
1164
- AGENT_TOOL_DESCRIPTION,
1165
- asZodSchema(AgentSchema),
1166
- async (params) => {
1167
- const args = params;
1698
+ function createBatchGroupId() {
1699
+ return `agent_group_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
1700
+ }
1701
+ async function runManagedAgentBatch(jobs, deps, manager) {
1702
+ const groupId = createBatchGroupId();
1703
+ const resolvedJobs = jobs.map((job, index) => {
1704
+ const agentType = job.subagent_type ?? "general-purpose";
1705
+ const agentDef = resolveAgentDefinition2(agentType, deps.customAgentRegistry);
1706
+ return { index, job, agentType, agentDef };
1707
+ });
1708
+ const invalidJobs = resolvedJobs.filter((job) => job.agentDef === void 0).map((job) => ({
1709
+ index: job.index,
1710
+ success: false,
1711
+ subagent_type: job.agentType,
1712
+ error: `Unknown agent type: ${job.agentType}`
1713
+ }));
1714
+ const validJobs = resolvedJobs.filter(
1715
+ (job) => job.agentDef !== void 0
1716
+ );
1717
+ const startedJobs = await Promise.all(
1718
+ validJobs.map(async (job) => {
1719
+ try {
1720
+ const state = await manager.spawn(
1721
+ createSpawnRequest(job.job, job.agentType, job.agentDef, deps)
1722
+ );
1723
+ return { ...job, agentId: state.id, spawnError: void 0 };
1724
+ } catch (error) {
1725
+ const message = error instanceof Error ? error.message : String(error);
1726
+ return { ...job, agentId: void 0, spawnError: message };
1727
+ }
1728
+ })
1729
+ );
1730
+ const terminalJobs = await Promise.all(
1731
+ startedJobs.map(async (job) => {
1732
+ if (job.spawnError || !job.agentId) {
1733
+ return {
1734
+ index: job.index,
1735
+ success: false,
1736
+ subagent_type: job.agentType,
1737
+ error: `Sub-agent error: ${job.spawnError ?? "missing agent id"}`
1738
+ };
1739
+ }
1740
+ try {
1741
+ const result = await manager.wait(job.agentId);
1742
+ return {
1743
+ index: job.index,
1744
+ success: true,
1745
+ agentId: result.jobId,
1746
+ subagent_type: job.agentType,
1747
+ output: result.output,
1748
+ metadata: result.metadata
1749
+ };
1750
+ } catch (error) {
1751
+ const message = error instanceof Error ? error.message : String(error);
1752
+ return {
1753
+ index: job.index,
1754
+ success: false,
1755
+ agentId: job.agentId,
1756
+ subagent_type: job.agentType,
1757
+ error: `Sub-agent error: ${message}`
1758
+ };
1759
+ }
1760
+ })
1761
+ );
1762
+ const batchJobs = [...invalidJobs, ...terminalJobs].sort(
1763
+ (left, right) => left.index - right.index
1764
+ );
1765
+ return stringifyAgentBatchResult({ groupId, jobs: batchJobs });
1766
+ }
1767
+ function createAgentTool(deps) {
1768
+ const manager = createSubagentManager(deps);
1769
+ return (0, import_agent_tools.createZodFunctionTool)(
1770
+ "Agent",
1771
+ AGENT_TOOL_DESCRIPTION,
1772
+ asZodSchema(AgentSchema),
1773
+ async (params) => {
1774
+ const args = params;
1775
+ if (Array.isArray(args.jobs) && args.jobs.length > 0) {
1776
+ return runManagedAgentBatch(args.jobs, deps, manager);
1777
+ }
1168
1778
  return runManagedAgent(
1169
1779
  {
1170
1780
  prompt: args.prompt,
@@ -1255,8 +1865,8 @@ var BackgroundJobOrchestrator = class {
1255
1865
  createRecord(state) {
1256
1866
  let resolveGroup = () => {
1257
1867
  };
1258
- const completion = new Promise((resolve2) => {
1259
- resolveGroup = resolve2;
1868
+ const completion = new Promise((resolve4) => {
1869
+ resolveGroup = resolve4;
1260
1870
  });
1261
1871
  return { state, completion, resolve: resolveGroup };
1262
1872
  }
@@ -1381,6 +1991,8 @@ function retrieveSessionBackgroundTaskManager(key) {
1381
1991
  }
1382
1992
 
1383
1993
  // src/interactive/interactive-session-execution.ts
1994
+ var import_node_crypto = require("crypto");
1995
+ var import_agent_core2 = require("@robota-sdk/agent-core");
1384
1996
  function isAbortError(err) {
1385
1997
  return err instanceof DOMException && err.name === "AbortError" || err instanceof Error && (err.message.includes("aborted") || err.message.includes("abort"));
1386
1998
  }
@@ -1398,11 +2010,13 @@ function extractToolSummaries(history, historyBefore) {
1398
2010
  }
1399
2011
  function buildResult(response, sessionHistory, interactiveHistory, historyBefore, contextState) {
1400
2012
  const toolSummaries = extractToolSummaries(sessionHistory, historyBefore);
2013
+ const usage3 = extractTurnUsage(sessionHistory, historyBefore, contextState);
1401
2014
  return {
1402
2015
  response,
1403
2016
  history: interactiveHistory,
1404
2017
  toolSummaries,
1405
- contextState
2018
+ contextState,
2019
+ ...usage3 && { usage: usage3 }
1406
2020
  };
1407
2021
  }
1408
2022
  function buildInterruptedResult(sessionHistory, interactiveHistory, historyBefore, contextState) {
@@ -1412,14 +2026,51 @@ function buildInterruptedResult(sessionHistory, interactiveHistory, historyBefor
1412
2026
  const msg = sessionHistory[i];
1413
2027
  if (msg?.role === "assistant" && msg.content) parts.push(msg.content);
1414
2028
  }
2029
+ const usage3 = extractTurnUsage(sessionHistory, historyBefore, contextState);
1415
2030
  return {
1416
2031
  response: parts.join("\n\n"),
1417
2032
  history: interactiveHistory,
1418
2033
  toolSummaries,
1419
- contextState
2034
+ contextState,
2035
+ ...usage3 && { usage: usage3 }
2036
+ };
2037
+ }
2038
+ function createUsageSummaryEntry(usage3) {
2039
+ return {
2040
+ id: `usage_${(0, import_node_crypto.randomUUID)()}`,
2041
+ timestamp: /* @__PURE__ */ new Date(),
2042
+ category: "event",
2043
+ type: "usage-summary",
2044
+ data: usage3
2045
+ };
2046
+ }
2047
+ function extractTurnUsage(sessionHistory, historyBefore, contextState) {
2048
+ const turnMessages = sessionHistory.slice(historyBefore);
2049
+ let promptTokens = 0;
2050
+ let completionTokens = 0;
2051
+ let foundUsage = false;
2052
+ for (const message of turnMessages) {
2053
+ if (message.role !== "assistant") continue;
2054
+ const usage3 = (0, import_agent_core2.collectAssistantUsageMetadata)(message);
2055
+ if (!usage3) continue;
2056
+ foundUsage = true;
2057
+ promptTokens += usage3.inputTokens;
2058
+ completionTokens += usage3.outputTokens;
2059
+ }
2060
+ if (!foundUsage) return void 0;
2061
+ return {
2062
+ kind: "exact",
2063
+ scope: "turn",
2064
+ promptTokens,
2065
+ completionTokens,
2066
+ totalTokens: promptTokens + completionTokens,
2067
+ contextUsedTokens: contextState.usedTokens,
2068
+ contextMaxTokens: contextState.maxTokens,
2069
+ contextUsedPercentage: contextState.usedPercentage,
2070
+ costStatus: "unknown"
1420
2071
  };
1421
2072
  }
1422
- function persistSession(sessionStore, session, sessionName, cwd, history, backgroundState) {
2073
+ function persistSession(sessionStore, session, sessionName, cwd, history, backgroundState, memoryState) {
1423
2074
  try {
1424
2075
  const sessionId = session.getSessionId();
1425
2076
  const existing = sessionStore.load(sessionId);
@@ -1438,6 +2089,10 @@ function persistSession(sessionStore, session, sessionName, cwd, history, backgr
1438
2089
  backgroundTaskEvents: [...backgroundState.events],
1439
2090
  backgroundJobGroups: [...backgroundState.groups ?? []],
1440
2091
  backgroundJobGroupEvents: [...backgroundState.groupEvents ?? []]
2092
+ } : {},
2093
+ ...memoryState ? {
2094
+ memoryEvents: [...memoryState.events],
2095
+ usedMemoryReferences: [...memoryState.usedReferences]
1441
2096
  } : {}
1442
2097
  });
1443
2098
  } catch {
@@ -1460,17 +2115,97 @@ var NOOP_TERMINAL = {
1460
2115
  };
1461
2116
 
1462
2117
  // src/interactive/interactive-session-streaming.ts
1463
- var import_node_crypto = require("crypto");
2118
+ var import_node_crypto2 = require("crypto");
2119
+ var import_node_fs3 = require("fs");
1464
2120
  var TOOL_ARG_DISPLAY_MAX = 80;
1465
2121
  var TAIL_KEEP = 30;
1466
2122
  var MAX_COMPLETED_TOOLS = 50;
1467
2123
  var STREAMING_FLUSH_INTERVAL_MS = 16;
2124
+ var DEFAULT_START_LINE = 1;
2125
+ var EDIT_DIFF_CONTEXT_LINES = 3;
1468
2126
  function extractFirstArg2(toolArgs) {
1469
2127
  if (!toolArgs) return "";
1470
2128
  const firstVal = Object.values(toolArgs)[0];
1471
2129
  const raw = typeof firstVal === "string" ? firstVal : JSON.stringify(firstVal ?? "");
1472
2130
  return raw.length > TOOL_ARG_DISPLAY_MAX ? raw.slice(0, TOOL_ARG_DISPLAY_MAX - TAIL_KEEP - 3) + "..." + raw.slice(-TAIL_KEEP) : raw;
1473
2131
  }
2132
+ function getStringArg(args, snake, camel) {
2133
+ const value = args?.[snake] ?? args?.[camel];
2134
+ return typeof value === "string" ? value : null;
2135
+ }
2136
+ function parseStartLine(toolResultData) {
2137
+ if (!toolResultData) return DEFAULT_START_LINE;
2138
+ try {
2139
+ const parsed = JSON.parse(toolResultData);
2140
+ return typeof parsed.startLine === "number" && Number.isFinite(parsed.startLine) ? parsed.startLine : DEFAULT_START_LINE;
2141
+ } catch {
2142
+ return DEFAULT_START_LINE;
2143
+ }
2144
+ }
2145
+ function buildEditDiffState(event) {
2146
+ if (event.toolName !== "Edit") return {};
2147
+ const filePath = getStringArg(event.toolArgs, "file_path", "filePath");
2148
+ const oldString = getStringArg(event.toolArgs, "old_string", "oldString");
2149
+ const newString = getStringArg(event.toolArgs, "new_string", "newString");
2150
+ if (!filePath || oldString === null || newString === null || oldString === newString) return {};
2151
+ const startLine = parseStartLine(event.toolResultData);
2152
+ return {
2153
+ diffFile: filePath,
2154
+ diffLines: buildEditDiffLinesWithContext(oldString, newString, startLine, filePath)
2155
+ };
2156
+ }
2157
+ function buildEditDiffLines(oldString, newString, startLine) {
2158
+ return [
2159
+ ...oldString.split("\n").map((text, index) => ({
2160
+ type: "remove",
2161
+ text,
2162
+ lineNumber: startLine + index
2163
+ })),
2164
+ ...newString.split("\n").map((text, index) => ({
2165
+ type: "add",
2166
+ text,
2167
+ lineNumber: startLine + index
2168
+ }))
2169
+ ];
2170
+ }
2171
+ function buildEditDiffLinesWithContext(oldString, newString, startLine, filePath) {
2172
+ const diffLines = buildEditDiffLines(oldString, newString, startLine);
2173
+ let fileLines;
2174
+ try {
2175
+ fileLines = (0, import_node_fs3.readFileSync)(filePath, "utf8").split("\n");
2176
+ } catch {
2177
+ return diffLines;
2178
+ }
2179
+ const beforeContext = [];
2180
+ const contextStart = Math.max(0, startLine - 1 - EDIT_DIFF_CONTEXT_LINES);
2181
+ for (let index = contextStart; index < startLine - 1; index++) {
2182
+ if (index < fileLines.length) {
2183
+ beforeContext.push({ type: "context", text: fileLines[index], lineNumber: index + 1 });
2184
+ }
2185
+ }
2186
+ const afterContext = [];
2187
+ const afterStart = startLine - 1 + newString.split("\n").length;
2188
+ for (let index = afterStart; index < afterStart + EDIT_DIFF_CONTEXT_LINES; index++) {
2189
+ if (index < fileLines.length) {
2190
+ afterContext.push({ type: "context", text: fileLines[index], lineNumber: index + 1 });
2191
+ }
2192
+ }
2193
+ const hunkStart = beforeContext[0]?.lineNumber ?? diffLines[0]?.lineNumber ?? afterContext[0]?.lineNumber ?? startLine;
2194
+ const oldLineCount = oldString.split("\n").length;
2195
+ const newLineCount = newString.split("\n").length;
2196
+ const oldHunkLineCount = beforeContext.length + oldLineCount + afterContext.length;
2197
+ const newHunkLineCount = beforeContext.length + newLineCount + afterContext.length;
2198
+ return [
2199
+ {
2200
+ type: "hunk",
2201
+ text: `@@ -${hunkStart},${oldHunkLineCount} +${hunkStart},${newHunkLineCount} @@`,
2202
+ lineNumber: hunkStart
2203
+ },
2204
+ ...beforeContext,
2205
+ ...diffLines,
2206
+ ...afterContext
2207
+ ];
2208
+ }
1474
2209
  function pushToolSummaryToHistory(state) {
1475
2210
  if (state.activeTools.length === 0) return;
1476
2211
  const summary = state.activeTools.map((t) => {
@@ -1478,7 +2213,7 @@ function pushToolSummaryToHistory(state) {
1478
2213
  return `${status} ${t.toolName}${t.firstArg ? `(${t.firstArg})` : ""}`;
1479
2214
  }).join("\n");
1480
2215
  state.history.push({
1481
- id: (0, import_node_crypto.randomUUID)(),
2216
+ id: (0, import_node_crypto2.randomUUID)(),
1482
2217
  timestamp: /* @__PURE__ */ new Date(),
1483
2218
  category: "event",
1484
2219
  type: "tool-summary",
@@ -1487,7 +2222,10 @@ function pushToolSummaryToHistory(state) {
1487
2222
  toolName: t.toolName,
1488
2223
  firstArg: t.firstArg,
1489
2224
  isRunning: t.isRunning,
1490
- result: t.result
2225
+ result: t.result,
2226
+ diffFile: t.diffFile,
2227
+ diffLines: t.diffLines,
2228
+ toolResultData: t.toolResultData
1491
2229
  })),
1492
2230
  summary
1493
2231
  }
@@ -1511,7 +2249,7 @@ function applyToolStart(state, event) {
1511
2249
  const toolState = { toolName: event.toolName, firstArg, isRunning: true };
1512
2250
  state.activeTools.push(toolState);
1513
2251
  state.history.push({
1514
- id: (0, import_node_crypto.randomUUID)(),
2252
+ id: (0, import_node_crypto2.randomUUID)(),
1515
2253
  timestamp: /* @__PURE__ */ new Date(),
1516
2254
  category: "event",
1517
2255
  type: "tool-start",
@@ -1523,11 +2261,17 @@ function applyToolEnd(state, event) {
1523
2261
  const result = event.denied ? "denied" : event.success === false ? "error" : "success";
1524
2262
  const idx = state.activeTools.findIndex((t) => t.toolName === event.toolName && t.isRunning);
1525
2263
  if (idx === -1) return null;
1526
- const finished = { ...state.activeTools[idx], isRunning: false, result };
2264
+ const finished = {
2265
+ ...state.activeTools[idx],
2266
+ ...buildEditDiffState(event),
2267
+ isRunning: false,
2268
+ result,
2269
+ toolResultData: event.toolResultData
2270
+ };
1527
2271
  state.activeTools[idx] = finished;
1528
2272
  state.activeTools = trimCompletedTools(state.activeTools);
1529
2273
  state.history.push({
1530
- id: (0, import_node_crypto.randomUUID)(),
2274
+ id: (0, import_node_crypto2.randomUUID)(),
1531
2275
  timestamp: /* @__PURE__ */ new Date(),
1532
2276
  category: "event",
1533
2277
  type: "tool-end",
@@ -1535,12 +2279,307 @@ function applyToolEnd(state, event) {
1535
2279
  toolName: finished.toolName,
1536
2280
  firstArg: finished.firstArg,
1537
2281
  isRunning: false,
1538
- result
2282
+ result,
2283
+ toolResultData: event.toolResultData
1539
2284
  }
1540
2285
  });
1541
2286
  return finished;
1542
2287
  }
1543
2288
 
2289
+ // src/config/config-loader.ts
2290
+ var import_fs2 = require("fs");
2291
+ var import_path2 = require("path");
2292
+
2293
+ // src/config/config-types.ts
2294
+ var import_zod2 = require("zod");
2295
+ var UniversalValueSchema = import_zod2.z.lazy(
2296
+ () => import_zod2.z.union([
2297
+ import_zod2.z.string(),
2298
+ import_zod2.z.number(),
2299
+ import_zod2.z.boolean(),
2300
+ import_zod2.z.null(),
2301
+ import_zod2.z.undefined(),
2302
+ import_zod2.z.date(),
2303
+ import_zod2.z.array(UniversalValueSchema),
2304
+ import_zod2.z.record(UniversalValueSchema)
2305
+ ])
2306
+ );
2307
+ var ProviderSchema = import_zod2.z.object({
2308
+ name: import_zod2.z.string().optional(),
2309
+ model: import_zod2.z.string().optional(),
2310
+ apiKey: import_zod2.z.string().optional(),
2311
+ baseURL: import_zod2.z.string().optional(),
2312
+ timeout: import_zod2.z.number().optional(),
2313
+ options: import_zod2.z.record(UniversalValueSchema).optional()
2314
+ });
2315
+ var ProviderProfileSchema = import_zod2.z.object({
2316
+ type: import_zod2.z.string().optional(),
2317
+ model: import_zod2.z.string().optional(),
2318
+ apiKey: import_zod2.z.string().optional(),
2319
+ baseURL: import_zod2.z.string().optional(),
2320
+ timeout: import_zod2.z.number().optional(),
2321
+ options: import_zod2.z.record(UniversalValueSchema).optional()
2322
+ });
2323
+ var PermissionsSchema = import_zod2.z.object({
2324
+ /** Patterns that are always approved without prompting */
2325
+ allow: import_zod2.z.array(import_zod2.z.string()).optional(),
2326
+ /** Patterns that are always denied */
2327
+ deny: import_zod2.z.array(import_zod2.z.string()).optional()
2328
+ });
2329
+ var EnvSchema = import_zod2.z.record(import_zod2.z.string()).optional();
2330
+ var CommandHookDefinitionSchema = import_zod2.z.object({
2331
+ type: import_zod2.z.literal("command"),
2332
+ command: import_zod2.z.string(),
2333
+ timeout: import_zod2.z.number().optional()
2334
+ });
2335
+ var HttpHookDefinitionSchema = import_zod2.z.object({
2336
+ type: import_zod2.z.literal("http"),
2337
+ url: import_zod2.z.string(),
2338
+ headers: import_zod2.z.record(import_zod2.z.string()).optional(),
2339
+ timeout: import_zod2.z.number().optional()
2340
+ });
2341
+ var PromptHookDefinitionSchema = import_zod2.z.object({
2342
+ type: import_zod2.z.literal("prompt"),
2343
+ prompt: import_zod2.z.string(),
2344
+ model: import_zod2.z.string().optional()
2345
+ });
2346
+ var AgentHookDefinitionSchema = import_zod2.z.object({
2347
+ type: import_zod2.z.literal("agent"),
2348
+ agent: import_zod2.z.string(),
2349
+ maxTurns: import_zod2.z.number().optional(),
2350
+ timeout: import_zod2.z.number().optional()
2351
+ });
2352
+ var HookDefinitionSchema = import_zod2.z.discriminatedUnion("type", [
2353
+ CommandHookDefinitionSchema,
2354
+ HttpHookDefinitionSchema,
2355
+ PromptHookDefinitionSchema,
2356
+ AgentHookDefinitionSchema
2357
+ ]);
2358
+ var HookGroupSchema = import_zod2.z.object({
2359
+ matcher: import_zod2.z.string(),
2360
+ hooks: import_zod2.z.array(HookDefinitionSchema)
2361
+ });
2362
+ var HooksSchema = import_zod2.z.object({
2363
+ PreToolUse: import_zod2.z.array(HookGroupSchema).optional(),
2364
+ PostToolUse: import_zod2.z.array(HookGroupSchema).optional(),
2365
+ SessionStart: import_zod2.z.array(HookGroupSchema).optional(),
2366
+ SessionEnd: import_zod2.z.array(HookGroupSchema).optional(),
2367
+ Stop: import_zod2.z.array(HookGroupSchema).optional(),
2368
+ StopFailure: import_zod2.z.array(HookGroupSchema).optional(),
2369
+ PreCompact: import_zod2.z.array(HookGroupSchema).optional(),
2370
+ PostCompact: import_zod2.z.array(HookGroupSchema).optional(),
2371
+ UserPromptSubmit: import_zod2.z.array(HookGroupSchema).optional(),
2372
+ SubagentStart: import_zod2.z.array(HookGroupSchema).optional(),
2373
+ SubagentStop: import_zod2.z.array(HookGroupSchema).optional(),
2374
+ WorktreeCreate: import_zod2.z.array(HookGroupSchema).optional(),
2375
+ WorktreeRemove: import_zod2.z.array(HookGroupSchema).optional()
2376
+ }).optional();
2377
+ var EnabledPluginsSchema = import_zod2.z.record(import_zod2.z.boolean()).optional();
2378
+ var MarketplaceSourceSchema = import_zod2.z.object({
2379
+ source: import_zod2.z.object({
2380
+ type: import_zod2.z.enum(["github", "git", "local", "url"]),
2381
+ repo: import_zod2.z.string().optional(),
2382
+ url: import_zod2.z.string().optional(),
2383
+ path: import_zod2.z.string().optional(),
2384
+ ref: import_zod2.z.string().optional()
2385
+ })
2386
+ });
2387
+ var ExtraKnownMarketplacesSchema = import_zod2.z.record(MarketplaceSourceSchema).optional().catch(void 0);
2388
+ var SettingsSchema = import_zod2.z.object({
2389
+ /** Trust level used when no --permission-mode flag is given */
2390
+ defaultTrustLevel: import_zod2.z.enum(["safe", "moderate", "full"]).optional(),
2391
+ /** Response language (e.g., "ko", "en", "ja"). Injected into system prompt. */
2392
+ language: import_zod2.z.string().optional(),
2393
+ /** Active provider profile key from providers. */
2394
+ currentProvider: import_zod2.z.string().optional(),
2395
+ /** Provider profiles keyed by user-facing profile name. */
2396
+ providers: import_zod2.z.record(ProviderProfileSchema).optional(),
2397
+ /** Legacy single-provider settings. Prefer currentProvider + providers for new config. */
2398
+ provider: ProviderSchema.optional(),
2399
+ permissions: PermissionsSchema.optional(),
2400
+ env: EnvSchema,
2401
+ hooks: HooksSchema,
2402
+ /** Plugin enablement map: plugin name -> enabled/disabled */
2403
+ enabledPlugins: EnabledPluginsSchema,
2404
+ /** Extra marketplace URLs for BundlePlugin discovery */
2405
+ extraKnownMarketplaces: ExtraKnownMarketplacesSchema
2406
+ });
2407
+
2408
+ // src/config/config-loader.ts
2409
+ function getHomeDir() {
2410
+ return process.env.HOME ?? process.env.USERPROFILE ?? "/";
2411
+ }
2412
+ var DEFAULTS = {
2413
+ defaultTrustLevel: "moderate",
2414
+ provider: {
2415
+ name: "anthropic",
2416
+ model: "claude-opus-4-5",
2417
+ apiKey: void 0
2418
+ },
2419
+ permissions: {
2420
+ allow: [],
2421
+ deny: []
2422
+ },
2423
+ env: {}
2424
+ };
2425
+ function readJsonFile(filePath) {
2426
+ if (!(0, import_fs2.existsSync)(filePath)) {
2427
+ return void 0;
2428
+ }
2429
+ const raw = (0, import_fs2.readFileSync)(filePath, "utf-8").trim();
2430
+ if (raw.length === 0) {
2431
+ return void 0;
2432
+ }
2433
+ try {
2434
+ return JSON.parse(raw);
2435
+ } catch {
2436
+ return void 0;
2437
+ }
2438
+ }
2439
+ function resolveEnvRef(value) {
2440
+ const ENV_PREFIX = "$ENV:";
2441
+ if (value.startsWith(ENV_PREFIX)) {
2442
+ const varName = value.slice(ENV_PREFIX.length);
2443
+ return process.env[varName] ?? value;
2444
+ }
2445
+ return value;
2446
+ }
2447
+ function resolveEnvRefs(settings) {
2448
+ const provider = settings.provider?.apiKey !== void 0 ? {
2449
+ ...settings.provider,
2450
+ apiKey: resolveEnvRef(settings.provider.apiKey)
2451
+ } : settings.provider;
2452
+ if (settings.providers !== void 0) {
2453
+ const providers = Object.fromEntries(
2454
+ Object.entries(settings.providers).map(([name, profile]) => [
2455
+ name,
2456
+ {
2457
+ ...profile,
2458
+ ...profile.apiKey !== void 0 && { apiKey: resolveEnvRef(profile.apiKey) }
2459
+ }
2460
+ ])
2461
+ );
2462
+ return {
2463
+ ...settings,
2464
+ provider,
2465
+ providers
2466
+ };
2467
+ }
2468
+ return {
2469
+ ...settings,
2470
+ provider
2471
+ };
2472
+ }
2473
+ function mergeSettings(layers) {
2474
+ return layers.reduce((merged, layer) => {
2475
+ return {
2476
+ ...merged,
2477
+ ...layer,
2478
+ provider: merged.provider !== void 0 || layer.provider !== void 0 ? { ...merged.provider, ...layer.provider } : void 0,
2479
+ permissions: merged.permissions !== void 0 || layer.permissions !== void 0 ? {
2480
+ allow: layer.permissions?.allow ?? merged.permissions?.allow,
2481
+ deny: layer.permissions?.deny ?? merged.permissions?.deny
2482
+ } : void 0,
2483
+ env: {
2484
+ ...merged.env ?? {},
2485
+ ...layer.env ?? {}
2486
+ },
2487
+ providers: merged.providers !== void 0 || layer.providers !== void 0 ? mergeProviders(merged.providers, layer.providers) : void 0,
2488
+ enabledPlugins: merged.enabledPlugins !== void 0 || layer.enabledPlugins !== void 0 ? { ...merged.enabledPlugins ?? {}, ...layer.enabledPlugins ?? {} } : void 0,
2489
+ extraKnownMarketplaces: layer.extraKnownMarketplaces ?? merged.extraKnownMarketplaces
2490
+ };
2491
+ }, {});
2492
+ }
2493
+ function mergeProviders(base, override) {
2494
+ const result = { ...base ?? {} };
2495
+ for (const [name, profile] of Object.entries(override ?? {})) {
2496
+ result[name] = {
2497
+ ...result[name],
2498
+ ...profile
2499
+ };
2500
+ }
2501
+ return result;
2502
+ }
2503
+ function resolveProvider(merged) {
2504
+ if (merged.currentProvider !== void 0) {
2505
+ const profile = merged.providers?.[merged.currentProvider];
2506
+ if (profile === void 0) {
2507
+ throw new Error(`currentProvider "${merged.currentProvider}" was not found in providers`);
2508
+ }
2509
+ if (profile.type === void 0) {
2510
+ throw new Error(`Provider profile "${merged.currentProvider}" is missing type`);
2511
+ }
2512
+ return {
2513
+ name: profile.type,
2514
+ model: profile.model ?? DEFAULTS.provider.model,
2515
+ apiKey: profile.apiKey ?? DEFAULTS.provider.apiKey,
2516
+ ...profile.baseURL !== void 0 && { baseURL: profile.baseURL },
2517
+ ...profile.timeout !== void 0 && { timeout: profile.timeout },
2518
+ ...profile.options !== void 0 && { options: profile.options }
2519
+ };
2520
+ }
2521
+ return {
2522
+ name: merged.provider?.name ?? DEFAULTS.provider.name,
2523
+ model: merged.provider?.model ?? DEFAULTS.provider.model,
2524
+ apiKey: merged.provider?.apiKey ?? DEFAULTS.provider.apiKey,
2525
+ ...merged.provider?.baseURL !== void 0 && { baseURL: merged.provider.baseURL },
2526
+ ...merged.provider?.timeout !== void 0 && { timeout: merged.provider.timeout },
2527
+ ...merged.provider?.options !== void 0 && { options: merged.provider.options }
2528
+ };
2529
+ }
2530
+ function toResolvedConfig(merged) {
2531
+ return {
2532
+ defaultTrustLevel: merged.defaultTrustLevel ?? DEFAULTS.defaultTrustLevel,
2533
+ language: merged.language,
2534
+ currentProvider: merged.currentProvider,
2535
+ provider: resolveProvider(merged),
2536
+ permissions: {
2537
+ allow: merged.permissions?.allow ?? DEFAULTS.permissions.allow,
2538
+ deny: merged.permissions?.deny ?? DEFAULTS.permissions.deny
2539
+ },
2540
+ env: merged.env ?? DEFAULTS.env,
2541
+ hooks: merged.hooks ?? void 0,
2542
+ enabledPlugins: merged.enabledPlugins ?? void 0,
2543
+ extraKnownMarketplaces: merged.extraKnownMarketplaces ?? void 0
2544
+ };
2545
+ }
2546
+ function getSettingsPaths(cwd) {
2547
+ const home = getHomeDir();
2548
+ return [
2549
+ (0, import_path2.join)(home, ".robota", "settings.json"),
2550
+ // 1. user (lowest)
2551
+ (0, import_path2.join)(home, ".claude", "settings.json"),
2552
+ // 1b. user (Claude Code compat)
2553
+ (0, import_path2.join)(cwd, ".robota", "settings.json"),
2554
+ // 2. project
2555
+ (0, import_path2.join)(cwd, ".robota", "settings.local.json"),
2556
+ // 3. project-local
2557
+ (0, import_path2.join)(cwd, ".claude", "settings.json"),
2558
+ // 4. project, Claude Code compat
2559
+ (0, import_path2.join)(cwd, ".claude", "settings.local.json")
2560
+ // 5. project-local (highest)
2561
+ ];
2562
+ }
2563
+ async function loadConfig(cwd) {
2564
+ const allPaths = getSettingsPaths(cwd);
2565
+ const rawEntries = [];
2566
+ for (const filePath of allPaths) {
2567
+ const raw = readJsonFile(filePath);
2568
+ if (raw !== void 0) {
2569
+ rawEntries.push({ raw, path: filePath });
2570
+ }
2571
+ }
2572
+ const parsedLayers = rawEntries.map(({ raw, path }) => {
2573
+ const result = SettingsSchema.safeParse(raw);
2574
+ if (!result.success) {
2575
+ throw new Error(`Invalid settings in ${path}: ${result.error.message}`);
2576
+ }
2577
+ return resolveEnvRefs(result.data);
2578
+ });
2579
+ const merged = mergeSettings(parsedLayers);
2580
+ return toResolvedConfig(merged);
2581
+ }
2582
+
1544
2583
  // src/hooks/prompt-executor.ts
1545
2584
  function extractJson(raw) {
1546
2585
  const codeBlockMatch = /```(?:json)?\s*\n?([\s\S]*?)\n?\s*```/.exec(raw);
@@ -1667,6 +2706,8 @@ var TRUST_LEVEL_LABELS = {
1667
2706
  moderate: "moderate",
1668
2707
  full: "full"
1669
2708
  };
2709
+ var PROJECT_MEMORY_PRIORITY = Number("25");
2710
+ var TASK_CONTEXT_PRIORITY = Number("27");
1670
2711
  function createSection(id, title, priority, content, source) {
1671
2712
  return { id, title, priority, content, source };
1672
2713
  }
@@ -1717,6 +2758,26 @@ function createClaudeMdSection(claudeMd) {
1717
2758
  if (claudeMd.trim().length === 0) return void 0;
1718
2759
  return createSection("project-claude-md", "Project Notes", 20, claudeMd, "project-instructions");
1719
2760
  }
2761
+ function createProjectMemorySection(memoryMd) {
2762
+ if (memoryMd === void 0 || memoryMd.trim().length === 0) return void 0;
2763
+ return createSection(
2764
+ "project-memory",
2765
+ "Project Memory",
2766
+ PROJECT_MEMORY_PRIORITY,
2767
+ memoryMd,
2768
+ "project-instructions"
2769
+ );
2770
+ }
2771
+ function createTaskContextSection(taskContext) {
2772
+ if (taskContext === void 0 || taskContext.trim().length === 0) return void 0;
2773
+ return createSection(
2774
+ "active-task-context",
2775
+ "Active Task Context",
2776
+ TASK_CONTEXT_PRIORITY,
2777
+ taskContext,
2778
+ "project-instructions"
2779
+ );
2780
+ }
1720
2781
  function createToolDescriptionSection(descriptions) {
1721
2782
  if (descriptions.length === 0) return void 0;
1722
2783
  return createSection(
@@ -1789,6 +2850,8 @@ function buildSystemPrompt(params) {
1789
2850
  const sections = [];
1790
2851
  appendOptionalSection(sections, createAgentsMdSection(params.agentsMd));
1791
2852
  appendOptionalSection(sections, createClaudeMdSection(params.claudeMd));
2853
+ appendOptionalSection(sections, createProjectMemorySection(params.memoryMd));
2854
+ appendOptionalSection(sections, createTaskContextSection(params.taskContext));
1792
2855
  appendOptionalSection(sections, createWorkingDirectorySection(params.cwd));
1793
2856
  sections.push(createProjectSection(params.projectInfo));
1794
2857
  appendOptionalSection(sections, createResponseLanguageSection(params.language));
@@ -1823,18 +2886,18 @@ function createDefaultTools() {
1823
2886
  }
1824
2887
 
1825
2888
  // src/tools/background-process-tool.ts
1826
- var import_zod2 = require("zod");
2889
+ var import_zod3 = require("zod");
1827
2890
  var import_agent_tools3 = require("@robota-sdk/agent-tools");
1828
2891
  var DEFAULT_PROCESS_TIMEOUT_MS = 12e4;
1829
2892
  function asZodSchema2(schema) {
1830
2893
  return schema;
1831
2894
  }
1832
- var BackgroundProcessSchema = import_zod2.z.object({
1833
- command: import_zod2.z.string().describe("The shell command to start in the background"),
1834
- timeout: import_zod2.z.number().optional().describe("Optional timeout in milliseconds. Default is 120000."),
1835
- workingDirectory: import_zod2.z.string().optional().describe("Working directory for the command. Defaults to the current project directory."),
1836
- stdin: import_zod2.z.string().optional().describe("Optional stdin to write after the process starts."),
1837
- outputLimitBytes: import_zod2.z.number().optional().describe("Maximum captured output bytes kept in the task result.")
2895
+ var BackgroundProcessSchema = import_zod3.z.object({
2896
+ command: import_zod3.z.string().describe("The shell command to start in the background"),
2897
+ timeout: import_zod3.z.number().optional().describe("Optional timeout in milliseconds. Default is 120000."),
2898
+ workingDirectory: import_zod3.z.string().optional().describe("Working directory for the command. Defaults to the current project directory."),
2899
+ stdin: import_zod3.z.string().optional().describe("Optional stdin to write after the process starts."),
2900
+ outputLimitBytes: import_zod3.z.number().optional().describe("Maximum captured output bytes kept in the task result.")
1838
2901
  });
1839
2902
  function stringifyStarted(taskId, status, command) {
1840
2903
  return JSON.stringify({
@@ -1883,11 +2946,11 @@ function createBackgroundProcessTool(deps) {
1883
2946
  }
1884
2947
 
1885
2948
  // src/tools/command-execution-tool.ts
1886
- var import_zod3 = require("zod");
2949
+ var import_zod4 = require("zod");
1887
2950
  var import_agent_tools4 = require("@robota-sdk/agent-tools");
1888
- var CommandExecutionSchema = import_zod3.z.object({
1889
- command: import_zod3.z.string().describe("Command name to execute, with or without a leading slash"),
1890
- args: import_zod3.z.string().optional().describe("Arguments to pass to the command")
2951
+ var CommandExecutionSchema = import_zod4.z.object({
2952
+ command: import_zod4.z.string().describe("Command name to execute, with or without a leading slash"),
2953
+ args: import_zod4.z.string().optional().describe("Arguments to pass to the command")
1891
2954
  });
1892
2955
  function asZodSchema3(schema) {
1893
2956
  return schema;
@@ -1930,12 +2993,55 @@ function createCommandExecutionTool(deps) {
1930
2993
  );
1931
2994
  }
1932
2995
 
2996
+ // src/checkpoints/edit-checkpoint-tools.ts
2997
+ var CHECKPOINTED_TOOL_NAMES = /* @__PURE__ */ new Set(["Write", "Edit"]);
2998
+ function wrapEditCheckpointTools(tools, recorder) {
2999
+ return tools.map(
3000
+ (tool) => CHECKPOINTED_TOOL_NAMES.has(tool.getName()) ? new EditCheckpointToolWrapper(tool, recorder) : tool
3001
+ );
3002
+ }
3003
+ var EditCheckpointToolWrapper = class {
3004
+ constructor(delegate, recorder) {
3005
+ this.delegate = delegate;
3006
+ this.recorder = recorder;
3007
+ this.schema = delegate.schema;
3008
+ }
3009
+ schema;
3010
+ setEventService(eventService) {
3011
+ this.delegate.setEventService(eventService);
3012
+ }
3013
+ async execute(parameters, context) {
3014
+ const filePath = extractFilePath(parameters);
3015
+ if (filePath) {
3016
+ await this.recorder.captureFile(filePath);
3017
+ }
3018
+ return this.delegate.execute(parameters, context);
3019
+ }
3020
+ validate(parameters) {
3021
+ return this.delegate.validate(parameters);
3022
+ }
3023
+ validateParameters(parameters) {
3024
+ return this.delegate.validateParameters(parameters);
3025
+ }
3026
+ getDescription() {
3027
+ return this.delegate.getDescription();
3028
+ }
3029
+ getName() {
3030
+ return this.delegate.getName();
3031
+ }
3032
+ };
3033
+ function extractFilePath(parameters) {
3034
+ if (!parameters || typeof parameters !== "object") return void 0;
3035
+ const value = parameters.filePath;
3036
+ return typeof value === "string" && value.length > 0 ? value : void 0;
3037
+ }
3038
+
1933
3039
  // src/assembly/create-session.ts
1934
3040
  var import_agent_runtime6 = require("@robota-sdk/agent-runtime");
1935
3041
 
1936
3042
  // src/agents/agent-definition-loader.ts
1937
- var import_node_fs2 = require("fs");
1938
- var import_node_path2 = require("path");
3043
+ var import_node_fs4 = require("fs");
3044
+ var import_node_path3 = require("path");
1939
3045
  var import_node_os2 = require("os");
1940
3046
  var LIST_KEYS2 = /* @__PURE__ */ new Set(["tools", "disallowedTools"]);
1941
3047
  var NUMBER_KEYS = /* @__PURE__ */ new Set(["maxTurns"]);
@@ -1980,20 +3086,20 @@ function parseFrontmatter2(content) {
1980
3086
  };
1981
3087
  }
1982
3088
  function scanAgentsDir(dir) {
1983
- if (!(0, import_node_fs2.existsSync)(dir)) return [];
3089
+ if (!(0, import_node_fs4.existsSync)(dir)) return [];
1984
3090
  const agents = [];
1985
3091
  let entries;
1986
3092
  try {
1987
- entries = (0, import_node_fs2.readdirSync)(dir, { withFileTypes: true });
3093
+ entries = (0, import_node_fs4.readdirSync)(dir, { withFileTypes: true });
1988
3094
  } catch {
1989
3095
  return [];
1990
3096
  }
1991
3097
  for (const entry of entries) {
1992
3098
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
1993
- const filePath = (0, import_node_path2.join)(dir, entry.name);
1994
- const content = (0, import_node_fs2.readFileSync)(filePath, "utf-8");
3099
+ const filePath = (0, import_node_path3.join)(dir, entry.name);
3100
+ const content = (0, import_node_fs4.readFileSync)(filePath, "utf-8");
1995
3101
  const { frontmatter, body } = parseFrontmatter2(content);
1996
- const fallbackName = (0, import_node_path2.basename)(entry.name, ".md");
3102
+ const fallbackName = (0, import_node_path3.basename)(entry.name, ".md");
1997
3103
  const agent = {
1998
3104
  name: frontmatter?.name ?? fallbackName,
1999
3105
  description: frontmatter?.description ?? "",
@@ -2018,11 +3124,11 @@ var AgentDefinitionLoader = class {
2018
3124
  /** Load all agent definitions, merged with built-in agents. Custom overrides built-in on name collision. */
2019
3125
  loadAll() {
2020
3126
  const sources = [
2021
- scanAgentsDir((0, import_node_path2.join)(this.cwd, ".robota", "agents")),
2022
- scanAgentsDir((0, import_node_path2.join)(this.cwd, ".agents", "agents")),
2023
- scanAgentsDir((0, import_node_path2.join)(this.cwd, ".claude", "agents")),
2024
- scanAgentsDir((0, import_node_path2.join)(this.home, ".robota", "agents")),
2025
- scanAgentsDir((0, import_node_path2.join)(this.home, ".claude", "agents"))
3127
+ scanAgentsDir((0, import_node_path3.join)(this.cwd, ".robota", "agents")),
3128
+ scanAgentsDir((0, import_node_path3.join)(this.cwd, ".agents", "agents")),
3129
+ scanAgentsDir((0, import_node_path3.join)(this.cwd, ".claude", "agents")),
3130
+ scanAgentsDir((0, import_node_path3.join)(this.home, ".robota", "agents")),
3131
+ scanAgentsDir((0, import_node_path3.join)(this.home, ".claude", "agents"))
2026
3132
  ];
2027
3133
  const seen = /* @__PURE__ */ new Set();
2028
3134
  const customAgents = [];
@@ -2049,7 +3155,7 @@ var AgentDefinitionLoader = class {
2049
3155
  };
2050
3156
 
2051
3157
  // src/assembly/background-task-hooks.ts
2052
- var import_agent_core2 = require("@robota-sdk/agent-core");
3158
+ var import_agent_core3 = require("@robota-sdk/agent-core");
2053
3159
  function getSubagentHookEvent(event) {
2054
3160
  if (event.type === "background_task_started" && event.task.kind === "agent") {
2055
3161
  return "SubagentStart";
@@ -2084,7 +3190,7 @@ function fireSubagentLifecycleHook(event, cwd, hooks, hookTypeExecutors) {
2084
3190
  ROBOTA_AGENT_TYPE: event.task.agentType ?? event.task.label
2085
3191
  }
2086
3192
  };
2087
- void (0, import_agent_core2.runHooks)(hooks, hookEventName, input, hookTypeExecutors).catch(() => void 0);
3193
+ void (0, import_agent_core3.runHooks)(hooks, hookEventName, input, hookTypeExecutors).catch(() => void 0);
2088
3194
  }
2089
3195
 
2090
3196
  // src/assembly/create-session.ts
@@ -2100,7 +3206,7 @@ function createSession(options) {
2100
3206
  const provider = options.provider;
2101
3207
  const cwd = options.cwd ?? process.cwd();
2102
3208
  const sessionId = options.sessionId ?? createSessionId();
2103
- const defaultTools = createDefaultTools();
3209
+ const defaultTools = options.editCheckpointRecorder ? wrapEditCheckpointTools(createDefaultTools(), options.editCheckpointRecorder) : createDefaultTools();
2104
3210
  const tools = [...defaultTools, ...options.additionalTools ?? []];
2105
3211
  if (options.modelCommandExecutor && options.isModelCommandInvocable) {
2106
3212
  tools.push(
@@ -2201,6 +3307,8 @@ function createSession(options) {
2201
3307
  const systemMessage = buildPrompt({
2202
3308
  agentsMd: options.context.agentsMd,
2203
3309
  claudeMd: options.context.claudeMd,
3310
+ memoryMd: options.context.memoryMd,
3311
+ taskContext: options.context.taskContext,
2204
3312
  toolDescriptions: options.toolDescriptions ?? (backgroundProcessToolDeps ? [
2205
3313
  ...defaultToolDescriptions,
2206
3314
  "BackgroundProcess \u2014 start long-running shell commands as managed background tasks"
@@ -2254,6 +3362,7 @@ ${options.appendSystemPrompt}` : systemMessage;
2254
3362
  sessionId,
2255
3363
  permissionHandler: options.permissionHandler,
2256
3364
  onTextDelta: options.onTextDelta,
3365
+ onContextUpdate: options.onContextUpdate,
2257
3366
  onToolExecution: options.onToolExecution,
2258
3367
  promptForApproval: options.promptForApproval,
2259
3368
  onCompact: options.onCompact,
@@ -2278,334 +3387,229 @@ function logBackgroundTaskEvent(logger, sessionId, event) {
2278
3387
  }
2279
3388
 
2280
3389
  // src/assembly/subagent-logger.ts
2281
- var import_node_fs3 = require("fs");
2282
- var import_node_path3 = require("path");
3390
+ var import_node_fs5 = require("fs");
3391
+ var import_node_path4 = require("path");
2283
3392
  var import_agent_sessions3 = require("@robota-sdk/agent-sessions");
2284
3393
  function createSubagentLogger(parentSessionId, _agentId, baseLogsDir) {
2285
- const subagentDir = (0, import_node_path3.join)(baseLogsDir, parentSessionId, "subagents");
2286
- (0, import_node_fs3.mkdirSync)(subagentDir, { recursive: true });
3394
+ const subagentDir = (0, import_node_path4.join)(baseLogsDir, parentSessionId, "subagents");
3395
+ (0, import_node_fs5.mkdirSync)(subagentDir, { recursive: true });
2287
3396
  return new import_agent_sessions3.FileSessionLogger(subagentDir);
2288
3397
  }
2289
- function resolveSubagentLogDir(parentSessionId, baseLogsDir) {
2290
- return (0, import_node_path3.join)(baseLogsDir, parentSessionId, "subagents");
2291
- }
2292
-
2293
- // src/interactive/interactive-session-init.ts
2294
- var import_agent_sessions4 = require("@robota-sdk/agent-sessions");
2295
-
2296
- // src/paths.ts
2297
- var import_node_path4 = require("path");
2298
- var import_node_os3 = require("os");
2299
- function projectPaths(cwd) {
2300
- const base = (0, import_node_path4.join)(cwd, ".robota");
2301
- return {
2302
- settings: (0, import_node_path4.join)(base, "settings.json"),
2303
- settingsLocal: (0, import_node_path4.join)(base, "settings.local.json"),
2304
- logs: (0, import_node_path4.join)(base, "logs"),
2305
- sessions: (0, import_node_path4.join)(base, "sessions")
2306
- };
2307
- }
2308
- function userPaths() {
2309
- const base = (0, import_node_path4.join)((0, import_node_os3.homedir)(), ".robota");
2310
- return {
2311
- settings: (0, import_node_path4.join)(base, "settings.json"),
2312
- sessions: (0, import_node_path4.join)(base, "sessions")
2313
- };
2314
- }
2315
-
2316
- // src/config/config-loader.ts
2317
- var import_fs = require("fs");
2318
- var import_path = require("path");
2319
-
2320
- // src/config/config-types.ts
2321
- var import_zod4 = require("zod");
2322
- var ProviderSchema = import_zod4.z.object({
2323
- name: import_zod4.z.string().optional(),
2324
- model: import_zod4.z.string().optional(),
2325
- apiKey: import_zod4.z.string().optional(),
2326
- baseURL: import_zod4.z.string().optional(),
2327
- timeout: import_zod4.z.number().optional()
2328
- });
2329
- var ProviderProfileSchema = import_zod4.z.object({
2330
- type: import_zod4.z.string().optional(),
2331
- model: import_zod4.z.string().optional(),
2332
- apiKey: import_zod4.z.string().optional(),
2333
- baseURL: import_zod4.z.string().optional(),
2334
- timeout: import_zod4.z.number().optional()
2335
- });
2336
- var PermissionsSchema = import_zod4.z.object({
2337
- /** Patterns that are always approved without prompting */
2338
- allow: import_zod4.z.array(import_zod4.z.string()).optional(),
2339
- /** Patterns that are always denied */
2340
- deny: import_zod4.z.array(import_zod4.z.string()).optional()
2341
- });
2342
- var EnvSchema = import_zod4.z.record(import_zod4.z.string()).optional();
2343
- var CommandHookDefinitionSchema = import_zod4.z.object({
2344
- type: import_zod4.z.literal("command"),
2345
- command: import_zod4.z.string(),
2346
- timeout: import_zod4.z.number().optional()
2347
- });
2348
- var HttpHookDefinitionSchema = import_zod4.z.object({
2349
- type: import_zod4.z.literal("http"),
2350
- url: import_zod4.z.string(),
2351
- headers: import_zod4.z.record(import_zod4.z.string()).optional(),
2352
- timeout: import_zod4.z.number().optional()
2353
- });
2354
- var PromptHookDefinitionSchema = import_zod4.z.object({
2355
- type: import_zod4.z.literal("prompt"),
2356
- prompt: import_zod4.z.string(),
2357
- model: import_zod4.z.string().optional()
2358
- });
2359
- var AgentHookDefinitionSchema = import_zod4.z.object({
2360
- type: import_zod4.z.literal("agent"),
2361
- agent: import_zod4.z.string(),
2362
- maxTurns: import_zod4.z.number().optional(),
2363
- timeout: import_zod4.z.number().optional()
2364
- });
2365
- var HookDefinitionSchema = import_zod4.z.discriminatedUnion("type", [
2366
- CommandHookDefinitionSchema,
2367
- HttpHookDefinitionSchema,
2368
- PromptHookDefinitionSchema,
2369
- AgentHookDefinitionSchema
2370
- ]);
2371
- var HookGroupSchema = import_zod4.z.object({
2372
- matcher: import_zod4.z.string(),
2373
- hooks: import_zod4.z.array(HookDefinitionSchema)
2374
- });
2375
- var HooksSchema = import_zod4.z.object({
2376
- PreToolUse: import_zod4.z.array(HookGroupSchema).optional(),
2377
- PostToolUse: import_zod4.z.array(HookGroupSchema).optional(),
2378
- SessionStart: import_zod4.z.array(HookGroupSchema).optional(),
2379
- SessionEnd: import_zod4.z.array(HookGroupSchema).optional(),
2380
- Stop: import_zod4.z.array(HookGroupSchema).optional(),
2381
- StopFailure: import_zod4.z.array(HookGroupSchema).optional(),
2382
- PreCompact: import_zod4.z.array(HookGroupSchema).optional(),
2383
- PostCompact: import_zod4.z.array(HookGroupSchema).optional(),
2384
- UserPromptSubmit: import_zod4.z.array(HookGroupSchema).optional(),
2385
- SubagentStart: import_zod4.z.array(HookGroupSchema).optional(),
2386
- SubagentStop: import_zod4.z.array(HookGroupSchema).optional(),
2387
- WorktreeCreate: import_zod4.z.array(HookGroupSchema).optional(),
2388
- WorktreeRemove: import_zod4.z.array(HookGroupSchema).optional()
2389
- }).optional();
2390
- var EnabledPluginsSchema = import_zod4.z.record(import_zod4.z.boolean()).optional();
2391
- var MarketplaceSourceSchema = import_zod4.z.object({
2392
- source: import_zod4.z.object({
2393
- type: import_zod4.z.enum(["github", "git", "local", "url"]),
2394
- repo: import_zod4.z.string().optional(),
2395
- url: import_zod4.z.string().optional(),
2396
- path: import_zod4.z.string().optional(),
2397
- ref: import_zod4.z.string().optional()
2398
- })
2399
- });
2400
- var ExtraKnownMarketplacesSchema = import_zod4.z.record(MarketplaceSourceSchema).optional().catch(void 0);
2401
- var SettingsSchema = import_zod4.z.object({
2402
- /** Trust level used when no --permission-mode flag is given */
2403
- defaultTrustLevel: import_zod4.z.enum(["safe", "moderate", "full"]).optional(),
2404
- /** Response language (e.g., "ko", "en", "ja"). Injected into system prompt. */
2405
- language: import_zod4.z.string().optional(),
2406
- /** Active provider profile key from providers. */
2407
- currentProvider: import_zod4.z.string().optional(),
2408
- /** Provider profiles keyed by user-facing profile name. */
2409
- providers: import_zod4.z.record(ProviderProfileSchema).optional(),
2410
- /** Legacy single-provider settings. Prefer currentProvider + providers for new config. */
2411
- provider: ProviderSchema.optional(),
2412
- permissions: PermissionsSchema.optional(),
2413
- env: EnvSchema,
2414
- hooks: HooksSchema,
2415
- /** Plugin enablement map: plugin name -> enabled/disabled */
2416
- enabledPlugins: EnabledPluginsSchema,
2417
- /** Extra marketplace URLs for BundlePlugin discovery */
2418
- extraKnownMarketplaces: ExtraKnownMarketplacesSchema
2419
- });
2420
-
2421
- // src/config/config-loader.ts
2422
- function getHomeDir() {
2423
- return process.env.HOME ?? process.env.USERPROFILE ?? "/";
2424
- }
2425
- var DEFAULTS = {
2426
- defaultTrustLevel: "moderate",
2427
- provider: {
2428
- name: "anthropic",
2429
- model: "claude-opus-4-5",
2430
- apiKey: void 0
2431
- },
2432
- permissions: {
2433
- allow: [],
2434
- deny: []
2435
- },
2436
- env: {}
2437
- };
2438
- function readJsonFile(filePath) {
2439
- if (!(0, import_fs.existsSync)(filePath)) {
2440
- return void 0;
2441
- }
2442
- const raw = (0, import_fs.readFileSync)(filePath, "utf-8").trim();
2443
- if (raw.length === 0) {
2444
- return void 0;
2445
- }
2446
- try {
2447
- return JSON.parse(raw);
2448
- } catch {
2449
- return void 0;
2450
- }
2451
- }
2452
- function resolveEnvRef(value) {
2453
- const ENV_PREFIX = "$ENV:";
2454
- if (value.startsWith(ENV_PREFIX)) {
2455
- const varName = value.slice(ENV_PREFIX.length);
2456
- return process.env[varName] ?? value;
2457
- }
2458
- return value;
2459
- }
2460
- function resolveEnvRefs(settings) {
2461
- const provider = settings.provider?.apiKey !== void 0 ? {
2462
- ...settings.provider,
2463
- apiKey: resolveEnvRef(settings.provider.apiKey)
2464
- } : settings.provider;
2465
- if (settings.providers !== void 0) {
2466
- const providers = Object.fromEntries(
2467
- Object.entries(settings.providers).map(([name, profile]) => [
2468
- name,
2469
- {
2470
- ...profile,
2471
- ...profile.apiKey !== void 0 && { apiKey: resolveEnvRef(profile.apiKey) }
2472
- }
2473
- ])
2474
- );
2475
- return {
2476
- ...settings,
2477
- provider,
2478
- providers
2479
- };
2480
- }
2481
- return {
2482
- ...settings,
2483
- provider
2484
- };
2485
- }
2486
- function mergeSettings(layers) {
2487
- return layers.reduce((merged, layer) => {
2488
- return {
2489
- ...merged,
2490
- ...layer,
2491
- provider: merged.provider !== void 0 || layer.provider !== void 0 ? { ...merged.provider, ...layer.provider } : void 0,
2492
- permissions: merged.permissions !== void 0 || layer.permissions !== void 0 ? {
2493
- allow: layer.permissions?.allow ?? merged.permissions?.allow,
2494
- deny: layer.permissions?.deny ?? merged.permissions?.deny
2495
- } : void 0,
2496
- env: {
2497
- ...merged.env ?? {},
2498
- ...layer.env ?? {}
2499
- },
2500
- providers: merged.providers !== void 0 || layer.providers !== void 0 ? mergeProviders(merged.providers, layer.providers) : void 0,
2501
- enabledPlugins: merged.enabledPlugins !== void 0 || layer.enabledPlugins !== void 0 ? { ...merged.enabledPlugins ?? {}, ...layer.enabledPlugins ?? {} } : void 0,
2502
- extraKnownMarketplaces: layer.extraKnownMarketplaces ?? merged.extraKnownMarketplaces
2503
- };
2504
- }, {});
2505
- }
2506
- function mergeProviders(base, override) {
2507
- const result = { ...base ?? {} };
2508
- for (const [name, profile] of Object.entries(override ?? {})) {
2509
- result[name] = {
2510
- ...result[name],
2511
- ...profile
2512
- };
2513
- }
2514
- return result;
3398
+ function resolveSubagentLogDir(parentSessionId, baseLogsDir) {
3399
+ return (0, import_node_path4.join)(baseLogsDir, parentSessionId, "subagents");
2515
3400
  }
2516
- function resolveProvider(merged) {
2517
- if (merged.currentProvider !== void 0) {
2518
- const profile = merged.providers?.[merged.currentProvider];
2519
- if (profile === void 0) {
2520
- throw new Error(`currentProvider "${merged.currentProvider}" was not found in providers`);
2521
- }
2522
- if (profile.type === void 0) {
2523
- throw new Error(`Provider profile "${merged.currentProvider}" is missing type`);
2524
- }
2525
- return {
2526
- name: profile.type,
2527
- model: profile.model ?? DEFAULTS.provider.model,
2528
- apiKey: profile.apiKey ?? DEFAULTS.provider.apiKey,
2529
- ...profile.baseURL !== void 0 && { baseURL: profile.baseURL },
2530
- ...profile.timeout !== void 0 && { timeout: profile.timeout }
2531
- };
2532
- }
3401
+
3402
+ // src/interactive/interactive-session-init.ts
3403
+ var import_agent_sessions4 = require("@robota-sdk/agent-sessions");
3404
+
3405
+ // src/paths.ts
3406
+ var import_node_path5 = require("path");
3407
+ var import_node_os3 = require("os");
3408
+ function projectPaths(cwd) {
3409
+ const base = (0, import_node_path5.join)(cwd, ".robota");
2533
3410
  return {
2534
- name: merged.provider?.name ?? DEFAULTS.provider.name,
2535
- model: merged.provider?.model ?? DEFAULTS.provider.model,
2536
- apiKey: merged.provider?.apiKey ?? DEFAULTS.provider.apiKey,
2537
- ...merged.provider?.baseURL !== void 0 && { baseURL: merged.provider.baseURL },
2538
- ...merged.provider?.timeout !== void 0 && { timeout: merged.provider.timeout }
3411
+ settings: (0, import_node_path5.join)(base, "settings.json"),
3412
+ settingsLocal: (0, import_node_path5.join)(base, "settings.local.json"),
3413
+ logs: (0, import_node_path5.join)(base, "logs"),
3414
+ sessions: (0, import_node_path5.join)(base, "sessions"),
3415
+ memory: (0, import_node_path5.join)(base, "memory"),
3416
+ checkpoints: (0, import_node_path5.join)(base, "checkpoints")
2539
3417
  };
2540
3418
  }
2541
- function toResolvedConfig(merged) {
3419
+ function userPaths() {
3420
+ const base = (0, import_node_path5.join)((0, import_node_os3.homedir)(), ".robota");
2542
3421
  return {
2543
- defaultTrustLevel: merged.defaultTrustLevel ?? DEFAULTS.defaultTrustLevel,
2544
- language: merged.language,
2545
- currentProvider: merged.currentProvider,
2546
- provider: resolveProvider(merged),
2547
- permissions: {
2548
- allow: merged.permissions?.allow ?? DEFAULTS.permissions.allow,
2549
- deny: merged.permissions?.deny ?? DEFAULTS.permissions.deny
2550
- },
2551
- env: merged.env ?? DEFAULTS.env,
2552
- hooks: merged.hooks ?? void 0,
2553
- enabledPlugins: merged.enabledPlugins ?? void 0,
2554
- extraKnownMarketplaces: merged.extraKnownMarketplaces ?? void 0
3422
+ settings: (0, import_node_path5.join)(base, "settings.json"),
3423
+ sessions: (0, import_node_path5.join)(base, "sessions")
2555
3424
  };
2556
3425
  }
2557
- function getSettingsPaths(cwd) {
2558
- const home = getHomeDir();
2559
- return [
2560
- (0, import_path.join)(home, ".robota", "settings.json"),
2561
- // 1. user (lowest)
2562
- (0, import_path.join)(home, ".claude", "settings.json"),
2563
- // 1b. user (Claude Code compat)
2564
- (0, import_path.join)(cwd, ".robota", "settings.json"),
2565
- // 2. project
2566
- (0, import_path.join)(cwd, ".robota", "settings.local.json"),
2567
- // 3. project-local
2568
- (0, import_path.join)(cwd, ".claude", "settings.json"),
2569
- // 4. project, Claude Code compat
2570
- (0, import_path.join)(cwd, ".claude", "settings.local.json")
2571
- // 5. project-local (highest)
2572
- ];
2573
- }
2574
- async function loadConfig(cwd) {
2575
- const allPaths = getSettingsPaths(cwd);
2576
- const rawEntries = [];
2577
- for (const filePath of allPaths) {
2578
- const raw = readJsonFile(filePath);
2579
- if (raw !== void 0) {
2580
- rawEntries.push({ raw, path: filePath });
3426
+
3427
+ // src/context/context-loader.ts
3428
+ var import_fs3 = require("fs");
3429
+ var import_path3 = require("path");
3430
+
3431
+ // src/context/task-context.ts
3432
+ var import_node_fs6 = require("fs");
3433
+ var import_node_path6 = require("path");
3434
+ var TASKS_DIR = (0, import_node_path6.join)(".agents", "tasks");
3435
+ var README_FILENAME = "README.md";
3436
+ var MARKDOWN_EXTENSION = ".md";
3437
+ var DEFAULT_MAX_TASKS = Number("3");
3438
+ var STATUS_PRIORITIES = {
3439
+ "in-progress": Number("1"),
3440
+ todo: Number("2"),
3441
+ blocked: Number("3"),
3442
+ unknown: Number("4"),
3443
+ completed: Number("5")
3444
+ };
3445
+ function normalizeStatus(value) {
3446
+ const normalized = value?.trim().toLowerCase();
3447
+ if (normalized === "todo" || normalized === "in-progress" || normalized === "blocked" || normalized === "completed") {
3448
+ return normalized;
3449
+ }
3450
+ return "unknown";
3451
+ }
3452
+ function extractTitle(content, taskPath) {
3453
+ const heading = content.split(/\r?\n/).find((line) => /^#\s+/.test(line));
3454
+ return heading?.replace(/^#\s+/, "").trim() || (0, import_node_path6.basename)(taskPath, MARKDOWN_EXTENSION);
3455
+ }
3456
+ function extractMetadata(content, key) {
3457
+ const matcher = new RegExp(`^- \\*\\*${key}\\*\\*:\\s*(.+)$`, "im");
3458
+ return matcher.exec(content)?.[1]?.trim();
3459
+ }
3460
+ function extractSection(content, title) {
3461
+ const lines = content.split(/\r?\n/);
3462
+ const heading = new RegExp(`^(#{2,6})\\s+${title}\\b`, "i");
3463
+ const startIndex = lines.findIndex((line) => heading.test(line));
3464
+ if (startIndex < 0) {
3465
+ return void 0;
3466
+ }
3467
+ const collected = [];
3468
+ for (const line of lines.slice(startIndex + Number("1"))) {
3469
+ if (/^##\s+/.test(line)) {
3470
+ break;
2581
3471
  }
3472
+ collected.push(line);
3473
+ }
3474
+ const result = collected.join("\n").trim();
3475
+ return result.length > 0 ? result : void 0;
3476
+ }
3477
+ function extractOpenItems(content) {
3478
+ return content.split(/\r?\n/).map((line) => /^- \[ \]\s+(.+)$/.exec(line)?.[1]?.trim()).filter((item) => item !== void 0 && item.length > 0);
3479
+ }
3480
+ function taskSortScore(task, currentBranch) {
3481
+ if (currentBranch && task.branch === currentBranch) {
3482
+ return Number("0");
3483
+ }
3484
+ return STATUS_PRIORITIES[task.status];
3485
+ }
3486
+ function formatTask(task) {
3487
+ const lines = [`### ${task.title}`, `- **Path:** \`${task.relativePath}\``];
3488
+ lines.push(`- **Status:** ${task.status}`);
3489
+ if (task.branch) lines.push(`- **Branch:** ${task.branch}`);
3490
+ if (task.scope) lines.push(`- **Scope:** ${task.scope}`);
3491
+ if (task.objective) lines.push(`- **Objective:** ${task.objective}`);
3492
+ if (task.openItems.length > 0) {
3493
+ lines.push("- **Open items:**");
3494
+ lines.push(...task.openItems.map((item) => ` - ${item}`));
3495
+ }
3496
+ return lines.join("\n");
3497
+ }
3498
+ function formatDate(date) {
3499
+ return date.toISOString().slice(Number("0"), Number("10"));
3500
+ }
3501
+ function upsertStatusLine(content, status) {
3502
+ const lines = content.split(/\r?\n/);
3503
+ const statusLine = `- **Status**: ${status}`;
3504
+ const statusIndex = lines.findIndex((line) => /^- \*\*Status\*\*:\s*/.test(line));
3505
+ if (statusIndex >= Number("0")) {
3506
+ lines[statusIndex] = statusLine;
3507
+ return lines.join("\n");
3508
+ }
3509
+ const hasTopHeading = lines.length > Number("0") && /^#\s+/.test(lines[Number("0")]);
3510
+ if (hasTopHeading) {
3511
+ lines.splice(Number("1"), Number("0"), "", statusLine);
3512
+ } else {
3513
+ lines.unshift(statusLine, "");
2582
3514
  }
2583
- const parsedLayers = rawEntries.map(({ raw, path }) => {
2584
- const result = SettingsSchema.safeParse(raw);
2585
- if (!result.success) {
2586
- throw new Error(`Invalid settings in ${path}: ${result.error.message}`);
3515
+ return lines.join("\n");
3516
+ }
3517
+ function appendProgressEntry(content, now, progressMessage) {
3518
+ const entryLines = [`### ${formatDate(now)}`, `- ${progressMessage.trim()}`];
3519
+ const lines = content.replace(/\s+$/u, "").split(/\r?\n/);
3520
+ const progressIndex = lines.findIndex((line) => /^## Progress\s*$/.test(line));
3521
+ if (progressIndex < Number("0")) {
3522
+ return [...lines, "", "## Progress", "", ...entryLines, ""].join("\n");
3523
+ }
3524
+ const nextHeadingIndex = lines.findIndex(
3525
+ (line, index) => index > progressIndex && /^##\s+/.test(line)
3526
+ );
3527
+ if (nextHeadingIndex < Number("0")) {
3528
+ return [...lines, "", ...entryLines, ""].join("\n");
3529
+ }
3530
+ lines.splice(nextHeadingIndex, Number("0"), "", ...entryLines, "");
3531
+ return lines.join("\n");
3532
+ }
3533
+ function resolveGitDirectory(cwd) {
3534
+ let current = (0, import_node_path6.resolve)(cwd);
3535
+ let reachedRoot = false;
3536
+ while (!reachedRoot) {
3537
+ const gitPath = (0, import_node_path6.join)(current, ".git");
3538
+ if ((0, import_node_fs6.existsSync)(gitPath)) {
3539
+ const stats = (0, import_node_fs6.statSync)(gitPath);
3540
+ if (stats.isDirectory()) return gitPath;
3541
+ const content = (0, import_node_fs6.readFileSync)(gitPath, "utf8").trim();
3542
+ const gitdir = content.match(/^gitdir:\s*(.+)$/)?.[1];
3543
+ if (gitdir) return (0, import_node_path6.isAbsolute)(gitdir) ? gitdir : (0, import_node_path6.resolve)(current, gitdir);
2587
3544
  }
2588
- return resolveEnvRefs(result.data);
2589
- });
2590
- const merged = mergeSettings(parsedLayers);
2591
- return toResolvedConfig(merged);
3545
+ const parent = (0, import_node_path6.dirname)(current);
3546
+ reachedRoot = parent === current;
3547
+ current = parent;
3548
+ }
3549
+ return void 0;
3550
+ }
3551
+ function readCurrentGitBranch(cwd) {
3552
+ const gitDir = resolveGitDirectory(cwd);
3553
+ if (!gitDir) return void 0;
3554
+ const headPath = (0, import_node_path6.join)(gitDir, "HEAD");
3555
+ if (!(0, import_node_fs6.existsSync)(headPath)) return void 0;
3556
+ const head = (0, import_node_fs6.readFileSync)(headPath, "utf8").trim();
3557
+ const branch = head.match(/^ref:\s+refs\/heads\/(.+)$/)?.[1];
3558
+ return branch?.trim();
3559
+ }
3560
+ function discoverTaskFiles(cwd) {
3561
+ const tasksDir = (0, import_node_path6.join)(cwd, TASKS_DIR);
3562
+ if (!(0, import_node_fs6.existsSync)(tasksDir)) {
3563
+ return [];
3564
+ }
3565
+ return (0, import_node_fs6.readdirSync)(tasksDir, { withFileTypes: true }).filter((entry) => entry.isFile()).map((entry) => entry.name).filter((name) => name !== README_FILENAME && name.endsWith(MARKDOWN_EXTENSION)).sort((a, b) => a.localeCompare(b)).map((name) => (0, import_node_path6.join)(tasksDir, name));
3566
+ }
3567
+ function parseTaskFile(taskPath, cwd) {
3568
+ const content = (0, import_node_fs6.readFileSync)(taskPath, "utf8");
3569
+ return {
3570
+ path: taskPath,
3571
+ relativePath: (0, import_node_path6.relative)(cwd, taskPath),
3572
+ title: extractTitle(content, taskPath),
3573
+ status: normalizeStatus(extractMetadata(content, "Status")),
3574
+ branch: extractMetadata(content, "Branch"),
3575
+ scope: extractMetadata(content, "Scope"),
3576
+ objective: extractSection(content, "Objective"),
3577
+ openItems: extractOpenItems(content)
3578
+ };
3579
+ }
3580
+ function selectRelevantTasks(tasks, options = {}) {
3581
+ const maxTasks = options.maxTasks ?? DEFAULT_MAX_TASKS;
3582
+ return [...tasks].filter((task) => task.status !== "completed").sort(
3583
+ (left, right) => taskSortScore(left, options.currentBranch) - taskSortScore(right, options.currentBranch) || left.relativePath.localeCompare(right.relativePath)
3584
+ ).slice(Number("0"), maxTasks);
3585
+ }
3586
+ function formatTaskContext(tasks) {
3587
+ return tasks.map(formatTask).join("\n\n");
3588
+ }
3589
+ function loadTaskContext(cwd, options = {}) {
3590
+ const currentBranch = options.currentBranch ?? readCurrentGitBranch(cwd);
3591
+ const tasks = discoverTaskFiles(cwd).map((path) => parseTaskFile(path, cwd));
3592
+ return formatTaskContext(selectRelevantTasks(tasks, { ...options, currentBranch }));
3593
+ }
3594
+ function updateTaskFileStatus(taskPath, status, options = {}) {
3595
+ const updated = upsertStatusLine((0, import_node_fs6.readFileSync)(taskPath, "utf8"), status);
3596
+ const withProgress = options.progressMessage ? appendProgressEntry(updated, options.now ?? /* @__PURE__ */ new Date(), options.progressMessage) : updated;
3597
+ (0, import_node_fs6.writeFileSync)(taskPath, withProgress, "utf8");
2592
3598
  }
2593
3599
 
2594
3600
  // src/context/context-loader.ts
2595
- var import_fs2 = require("fs");
2596
- var import_path2 = require("path");
2597
3601
  var AGENTS_FILENAME = "AGENTS.md";
2598
3602
  var CLAUDE_FILENAME = "CLAUDE.md";
2599
3603
  function collectFilesWalkingUp(startDir, filename) {
2600
3604
  const found = [];
2601
- let current = (0, import_path2.resolve)(startDir);
3605
+ let current = (0, import_path3.resolve)(startDir);
2602
3606
  let atRoot = false;
2603
3607
  while (!atRoot) {
2604
- const candidate = (0, import_path2.join)(current, filename);
2605
- if ((0, import_fs2.existsSync)(candidate)) {
3608
+ const candidate = (0, import_path3.join)(current, filename);
3609
+ if ((0, import_fs3.existsSync)(candidate)) {
2606
3610
  found.push(candidate);
2607
3611
  }
2608
- const parent = (0, import_path2.dirname)(current);
3612
+ const parent = (0, import_path3.dirname)(current);
2609
3613
  atRoot = parent === current;
2610
3614
  if (!atRoot) {
2611
3615
  current = parent;
@@ -2640,47 +3644,51 @@ function extractCompactInstructions(content) {
2640
3644
  async function loadContext(cwd) {
2641
3645
  const agentsPaths = collectFilesWalkingUp(cwd, AGENTS_FILENAME);
2642
3646
  const claudePaths = collectFilesWalkingUp(cwd, CLAUDE_FILENAME);
2643
- const agentsMd = agentsPaths.map((p) => (0, import_fs2.readFileSync)(p, "utf-8")).join("\n\n");
2644
- const claudeMd = claudePaths.map((p) => (0, import_fs2.readFileSync)(p, "utf-8")).join("\n\n");
3647
+ const agentsMd = agentsPaths.map((p) => (0, import_fs3.readFileSync)(p, "utf-8")).join("\n\n");
3648
+ const claudeMd = claudePaths.map((p) => (0, import_fs3.readFileSync)(p, "utf-8")).join("\n\n");
2645
3649
  const compactInstructions = extractCompactInstructions(claudeMd);
2646
- return { agentsMd, claudeMd, compactInstructions };
3650
+ const startupMemory = new ProjectMemoryStore(cwd).loadStartupMemory();
3651
+ const memoryMd = startupMemory.content || void 0;
3652
+ const loadedTaskContext = loadTaskContext(cwd);
3653
+ const taskContext = loadedTaskContext.trim().length > 0 ? loadedTaskContext : void 0;
3654
+ return { agentsMd, claudeMd, memoryMd, taskContext, compactInstructions };
2647
3655
  }
2648
3656
 
2649
3657
  // src/context/project-detector.ts
2650
- var import_fs3 = require("fs");
2651
- var import_path3 = require("path");
3658
+ var import_fs4 = require("fs");
3659
+ var import_path4 = require("path");
2652
3660
  function tryReadJson(filePath) {
2653
- if (!(0, import_fs3.existsSync)(filePath)) return void 0;
3661
+ if (!(0, import_fs4.existsSync)(filePath)) return void 0;
2654
3662
  try {
2655
- return JSON.parse((0, import_fs3.readFileSync)(filePath, "utf-8"));
3663
+ return JSON.parse((0, import_fs4.readFileSync)(filePath, "utf-8"));
2656
3664
  } catch {
2657
3665
  return void 0;
2658
3666
  }
2659
3667
  }
2660
3668
  function detectPackageManager(cwd) {
2661
- if ((0, import_fs3.existsSync)((0, import_path3.join)(cwd, "pnpm-workspace.yaml")) || (0, import_fs3.existsSync)((0, import_path3.join)(cwd, "pnpm-lock.yaml"))) {
3669
+ if ((0, import_fs4.existsSync)((0, import_path4.join)(cwd, "pnpm-workspace.yaml")) || (0, import_fs4.existsSync)((0, import_path4.join)(cwd, "pnpm-lock.yaml"))) {
2662
3670
  return "pnpm";
2663
3671
  }
2664
- if ((0, import_fs3.existsSync)((0, import_path3.join)(cwd, "yarn.lock"))) {
3672
+ if ((0, import_fs4.existsSync)((0, import_path4.join)(cwd, "yarn.lock"))) {
2665
3673
  return "yarn";
2666
3674
  }
2667
- if ((0, import_fs3.existsSync)((0, import_path3.join)(cwd, "bun.lockb"))) {
3675
+ if ((0, import_fs4.existsSync)((0, import_path4.join)(cwd, "bun.lockb"))) {
2668
3676
  return "bun";
2669
3677
  }
2670
- if ((0, import_fs3.existsSync)((0, import_path3.join)(cwd, "package-lock.json"))) {
3678
+ if ((0, import_fs4.existsSync)((0, import_path4.join)(cwd, "package-lock.json"))) {
2671
3679
  return "npm";
2672
3680
  }
2673
3681
  return void 0;
2674
3682
  }
2675
3683
  async function detectProject(cwd) {
2676
- const pkgJsonPath = (0, import_path3.join)(cwd, "package.json");
2677
- const tsconfigPath = (0, import_path3.join)(cwd, "tsconfig.json");
2678
- const pyprojectPath = (0, import_path3.join)(cwd, "pyproject.toml");
2679
- const cargoPath = (0, import_path3.join)(cwd, "Cargo.toml");
2680
- const goModPath = (0, import_path3.join)(cwd, "go.mod");
2681
- if ((0, import_fs3.existsSync)(pkgJsonPath)) {
3684
+ const pkgJsonPath = (0, import_path4.join)(cwd, "package.json");
3685
+ const tsconfigPath = (0, import_path4.join)(cwd, "tsconfig.json");
3686
+ const pyprojectPath = (0, import_path4.join)(cwd, "pyproject.toml");
3687
+ const cargoPath = (0, import_path4.join)(cwd, "Cargo.toml");
3688
+ const goModPath = (0, import_path4.join)(cwd, "go.mod");
3689
+ if ((0, import_fs4.existsSync)(pkgJsonPath)) {
2682
3690
  const pkgJson = tryReadJson(pkgJsonPath);
2683
- const language = (0, import_fs3.existsSync)(tsconfigPath) ? "typescript" : "javascript";
3691
+ const language = (0, import_fs4.existsSync)(tsconfigPath) ? "typescript" : "javascript";
2684
3692
  const packageManager = detectPackageManager(cwd);
2685
3693
  return {
2686
3694
  type: "node",
@@ -2689,19 +3697,19 @@ async function detectProject(cwd) {
2689
3697
  language
2690
3698
  };
2691
3699
  }
2692
- if ((0, import_fs3.existsSync)(pyprojectPath) || (0, import_fs3.existsSync)((0, import_path3.join)(cwd, "setup.py"))) {
3700
+ if ((0, import_fs4.existsSync)(pyprojectPath) || (0, import_fs4.existsSync)((0, import_path4.join)(cwd, "setup.py"))) {
2693
3701
  return {
2694
3702
  type: "python",
2695
3703
  language: "python"
2696
3704
  };
2697
3705
  }
2698
- if ((0, import_fs3.existsSync)(cargoPath)) {
3706
+ if ((0, import_fs4.existsSync)(cargoPath)) {
2699
3707
  return {
2700
3708
  type: "rust",
2701
3709
  language: "rust"
2702
3710
  };
2703
3711
  }
2704
- if ((0, import_fs3.existsSync)(goModPath)) {
3712
+ if ((0, import_fs4.existsSync)(goModPath)) {
2705
3713
  return {
2706
3714
  type: "go",
2707
3715
  language: "go"
@@ -2714,8 +3722,8 @@ async function detectProject(cwd) {
2714
3722
  }
2715
3723
 
2716
3724
  // src/plugins/plugin-settings-store.ts
2717
- var import_node_fs4 = require("fs");
2718
- var import_node_path5 = require("path");
3725
+ var import_node_fs7 = require("fs");
3726
+ var import_node_path7 = require("path");
2719
3727
  var PluginSettingsStore = class {
2720
3728
  settingsPath;
2721
3729
  constructor(settingsPath) {
@@ -2723,11 +3731,11 @@ var PluginSettingsStore = class {
2723
3731
  }
2724
3732
  /** Read the full settings file from disk. */
2725
3733
  readAll() {
2726
- if (!(0, import_node_fs4.existsSync)(this.settingsPath)) {
3734
+ if (!(0, import_node_fs7.existsSync)(this.settingsPath)) {
2727
3735
  return {};
2728
3736
  }
2729
3737
  try {
2730
- const raw = (0, import_node_fs4.readFileSync)(this.settingsPath, "utf-8");
3738
+ const raw = (0, import_node_fs7.readFileSync)(this.settingsPath, "utf-8");
2731
3739
  const data = JSON.parse(raw);
2732
3740
  if (typeof data === "object" && data !== null) {
2733
3741
  return data;
@@ -2739,11 +3747,11 @@ var PluginSettingsStore = class {
2739
3747
  }
2740
3748
  /** Write the full settings file to disk. */
2741
3749
  writeAll(settings) {
2742
- const dir = (0, import_node_path5.dirname)(this.settingsPath);
2743
- if (!(0, import_node_fs4.existsSync)(dir)) {
2744
- (0, import_node_fs4.mkdirSync)(dir, { recursive: true });
3750
+ const dir = (0, import_node_path7.dirname)(this.settingsPath);
3751
+ if (!(0, import_node_fs7.existsSync)(dir)) {
3752
+ (0, import_node_fs7.mkdirSync)(dir, { recursive: true });
2745
3753
  }
2746
- (0, import_node_fs4.writeFileSync)(this.settingsPath, JSON.stringify(settings, null, 2), "utf-8");
3754
+ (0, import_node_fs7.writeFileSync)(this.settingsPath, JSON.stringify(settings, null, 2), "utf-8");
2747
3755
  }
2748
3756
  // --- enabledPlugins ---
2749
3757
  /** Get the enabledPlugins map. */
@@ -2815,11 +3823,11 @@ var PluginSettingsStore = class {
2815
3823
  };
2816
3824
 
2817
3825
  // src/plugins/bundle-plugin-loader.ts
2818
- var import_node_fs6 = require("fs");
2819
- var import_node_path6 = require("path");
3826
+ var import_node_fs9 = require("fs");
3827
+ var import_node_path8 = require("path");
2820
3828
 
2821
3829
  // src/plugins/bundle-plugin-utils.ts
2822
- var import_node_fs5 = require("fs");
3830
+ var import_node_fs8 = require("fs");
2823
3831
  function parseSkillFrontmatter(raw) {
2824
3832
  const trimmed = raw.trimStart();
2825
3833
  if (!trimmed.startsWith("---")) {
@@ -2868,9 +3876,9 @@ function validateManifest(data) {
2868
3876
  };
2869
3877
  }
2870
3878
  function getSortedSubdirs(dirPath) {
2871
- if (!(0, import_node_fs5.existsSync)(dirPath)) return [];
3879
+ if (!(0, import_node_fs8.existsSync)(dirPath)) return [];
2872
3880
  try {
2873
- const entries = (0, import_node_fs5.readdirSync)(dirPath, { withFileTypes: true });
3881
+ const entries = (0, import_node_fs8.readdirSync)(dirPath, { withFileTypes: true });
2874
3882
  return entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();
2875
3883
  } catch {
2876
3884
  return [];
@@ -2900,23 +3908,23 @@ var BundlePluginLoader = class {
2900
3908
  * For each marketplace/plugin pair, the latest version (lexicographically last) is loaded.
2901
3909
  */
2902
3910
  discoverAndLoad() {
2903
- const cacheDir = (0, import_node_path6.join)(this.pluginsDir, "cache");
2904
- if (!(0, import_node_fs6.existsSync)(cacheDir)) {
3911
+ const cacheDir = (0, import_node_path8.join)(this.pluginsDir, "cache");
3912
+ if (!(0, import_node_fs9.existsSync)(cacheDir)) {
2905
3913
  return [];
2906
3914
  }
2907
3915
  const results = [];
2908
3916
  const marketplaces = getSortedSubdirs(cacheDir);
2909
3917
  for (const marketplace of marketplaces) {
2910
- const marketplaceDir = (0, import_node_path6.join)(cacheDir, marketplace);
3918
+ const marketplaceDir = (0, import_node_path8.join)(cacheDir, marketplace);
2911
3919
  const plugins = getSortedSubdirs(marketplaceDir);
2912
3920
  for (const pluginName of plugins) {
2913
- const pluginDir = (0, import_node_path6.join)(marketplaceDir, pluginName);
3921
+ const pluginDir = (0, import_node_path8.join)(marketplaceDir, pluginName);
2914
3922
  const versions = getSortedSubdirs(pluginDir);
2915
3923
  if (versions.length === 0) continue;
2916
3924
  const latestVersion = versions[versions.length - 1];
2917
- const versionDir = (0, import_node_path6.join)(pluginDir, latestVersion);
2918
- const manifestPath = (0, import_node_path6.join)(versionDir, ".claude-plugin", "plugin.json");
2919
- if (!(0, import_node_fs6.existsSync)(manifestPath)) continue;
3925
+ const versionDir = (0, import_node_path8.join)(pluginDir, latestVersion);
3926
+ const manifestPath = (0, import_node_path8.join)(versionDir, ".claude-plugin", "plugin.json");
3927
+ if (!(0, import_node_fs9.existsSync)(manifestPath)) continue;
2920
3928
  const manifest = this.readManifest(manifestPath);
2921
3929
  if (!manifest) continue;
2922
3930
  const pluginId = `${manifest.name}@${marketplace}`;
@@ -2930,7 +3938,7 @@ var BundlePluginLoader = class {
2930
3938
  /** Read and validate a plugin.json manifest. Returns null on failure. */
2931
3939
  readManifest(path) {
2932
3940
  try {
2933
- const raw = (0, import_node_fs6.readFileSync)(path, "utf-8");
3941
+ const raw = (0, import_node_fs9.readFileSync)(path, "utf-8");
2934
3942
  const data = JSON.parse(raw);
2935
3943
  return validateManifest(data);
2936
3944
  } catch {
@@ -2965,15 +3973,15 @@ var BundlePluginLoader = class {
2965
3973
  }
2966
3974
  /** Load skills from the plugin's skills/ directory. */
2967
3975
  loadSkills(pluginDir, pluginName) {
2968
- const skillsDir = (0, import_node_path6.join)(pluginDir, "skills");
2969
- if (!(0, import_node_fs6.existsSync)(skillsDir)) return [];
2970
- const entries = (0, import_node_fs6.readdirSync)(skillsDir, { withFileTypes: true });
3976
+ const skillsDir = (0, import_node_path8.join)(pluginDir, "skills");
3977
+ if (!(0, import_node_fs9.existsSync)(skillsDir)) return [];
3978
+ const entries = (0, import_node_fs9.readdirSync)(skillsDir, { withFileTypes: true });
2971
3979
  const skills = [];
2972
3980
  for (const entry of entries) {
2973
3981
  if (!entry.isDirectory()) continue;
2974
- const skillFile = (0, import_node_path6.join)(skillsDir, entry.name, "SKILL.md");
2975
- if (!(0, import_node_fs6.existsSync)(skillFile)) continue;
2976
- const raw = (0, import_node_fs6.readFileSync)(skillFile, "utf-8");
3982
+ const skillFile = (0, import_node_path8.join)(skillsDir, entry.name, "SKILL.md");
3983
+ if (!(0, import_node_fs9.existsSync)(skillFile)) continue;
3984
+ const raw = (0, import_node_fs9.readFileSync)(skillFile, "utf-8");
2977
3985
  const { metadata, content } = parseSkillFrontmatter(raw);
2978
3986
  const description = typeof metadata.description === "string" ? metadata.description : "";
2979
3987
  const skill = {
@@ -2988,13 +3996,13 @@ var BundlePluginLoader = class {
2988
3996
  }
2989
3997
  /** Load commands from the plugin's commands/ directory (flat .md files). */
2990
3998
  loadCommands(pluginDir, pluginName) {
2991
- const commandsDir = (0, import_node_path6.join)(pluginDir, "commands");
2992
- if (!(0, import_node_fs6.existsSync)(commandsDir)) return [];
2993
- const entries = (0, import_node_fs6.readdirSync)(commandsDir, { withFileTypes: true });
3999
+ const commandsDir = (0, import_node_path8.join)(pluginDir, "commands");
4000
+ if (!(0, import_node_fs9.existsSync)(commandsDir)) return [];
4001
+ const entries = (0, import_node_fs9.readdirSync)(commandsDir, { withFileTypes: true });
2994
4002
  const commands = [];
2995
4003
  for (const entry of entries) {
2996
4004
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
2997
- const raw = (0, import_node_fs6.readFileSync)((0, import_node_path6.join)(commandsDir, entry.name), "utf-8");
4005
+ const raw = (0, import_node_fs9.readFileSync)((0, import_node_path8.join)(commandsDir, entry.name), "utf-8");
2998
4006
  const { metadata, content } = parseSkillFrontmatter(raw);
2999
4007
  const name = typeof metadata.name === "string" ? metadata.name : entry.name.replace(/\.md$/, "");
3000
4008
  const description = typeof metadata.description === "string" ? metadata.description : "";
@@ -3009,10 +4017,10 @@ var BundlePluginLoader = class {
3009
4017
  }
3010
4018
  /** Load hooks from hooks/hooks.json if present. */
3011
4019
  loadHooks(pluginDir) {
3012
- const hooksPath = (0, import_node_path6.join)(pluginDir, "hooks", "hooks.json");
3013
- if (!(0, import_node_fs6.existsSync)(hooksPath)) return {};
4020
+ const hooksPath = (0, import_node_path8.join)(pluginDir, "hooks", "hooks.json");
4021
+ if (!(0, import_node_fs9.existsSync)(hooksPath)) return {};
3014
4022
  try {
3015
- const raw = (0, import_node_fs6.readFileSync)(hooksPath, "utf-8");
4023
+ const raw = (0, import_node_fs9.readFileSync)(hooksPath, "utf-8");
3016
4024
  const data = JSON.parse(raw);
3017
4025
  if (typeof data === "object" && data !== null) {
3018
4026
  return data;
@@ -3024,12 +4032,12 @@ var BundlePluginLoader = class {
3024
4032
  }
3025
4033
  /** Load MCP server configuration if present. Checks `.mcp.json` at plugin root first. */
3026
4034
  loadMcpConfig(pluginDir) {
3027
- const primaryPath = (0, import_node_path6.join)(pluginDir, ".mcp.json");
3028
- const fallbackPath = (0, import_node_path6.join)(pluginDir, ".claude-plugin", "mcp.json");
3029
- const mcpPath = (0, import_node_fs6.existsSync)(primaryPath) ? primaryPath : fallbackPath;
3030
- if (!(0, import_node_fs6.existsSync)(mcpPath)) return void 0;
4035
+ const primaryPath = (0, import_node_path8.join)(pluginDir, ".mcp.json");
4036
+ const fallbackPath = (0, import_node_path8.join)(pluginDir, ".claude-plugin", "mcp.json");
4037
+ const mcpPath = (0, import_node_fs9.existsSync)(primaryPath) ? primaryPath : fallbackPath;
4038
+ if (!(0, import_node_fs9.existsSync)(mcpPath)) return void 0;
3031
4039
  try {
3032
- const raw = (0, import_node_fs6.readFileSync)(mcpPath, "utf-8");
4040
+ const raw = (0, import_node_fs9.readFileSync)(mcpPath, "utf-8");
3033
4041
  return JSON.parse(raw);
3034
4042
  } catch {
3035
4043
  return void 0;
@@ -3037,10 +4045,10 @@ var BundlePluginLoader = class {
3037
4045
  }
3038
4046
  /** Load agent definitions from agents/ directory if present. */
3039
4047
  loadAgents(pluginDir) {
3040
- const agentsDir = (0, import_node_path6.join)(pluginDir, "agents");
3041
- if (!(0, import_node_fs6.existsSync)(agentsDir)) return [];
4048
+ const agentsDir = (0, import_node_path8.join)(pluginDir, "agents");
4049
+ if (!(0, import_node_fs9.existsSync)(agentsDir)) return [];
3042
4050
  try {
3043
- const entries = (0, import_node_fs6.readdirSync)(agentsDir, { withFileTypes: true });
4051
+ const entries = (0, import_node_fs9.readdirSync)(agentsDir, { withFileTypes: true });
3044
4052
  return entries.filter((e) => e.isDirectory() || e.name.endsWith(".md")).map((e) => e.name.replace(/\.md$/, ""));
3045
4053
  } catch {
3046
4054
  return [];
@@ -3050,8 +4058,8 @@ var BundlePluginLoader = class {
3050
4058
 
3051
4059
  // src/plugins/bundle-plugin-installer.ts
3052
4060
  var import_node_child_process2 = require("child_process");
3053
- var import_node_fs7 = require("fs");
3054
- var import_node_path7 = require("path");
4061
+ var import_node_fs10 = require("fs");
4062
+ var import_node_path9 = require("path");
3055
4063
  var GIT_CLONE_TIMEOUT_MS = 6e4;
3056
4064
  var BundlePluginInstaller = class {
3057
4065
  pluginsDir;
@@ -3062,8 +4070,8 @@ var BundlePluginInstaller = class {
3062
4070
  exec;
3063
4071
  constructor(options) {
3064
4072
  this.pluginsDir = options.pluginsDir;
3065
- this.cacheDir = (0, import_node_path7.join)(this.pluginsDir, "cache");
3066
- this.registryPath = (0, import_node_path7.join)(this.pluginsDir, "installed_plugins.json");
4073
+ this.cacheDir = (0, import_node_path9.join)(this.pluginsDir, "cache");
4074
+ this.registryPath = (0, import_node_path9.join)(this.pluginsDir, "installed_plugins.json");
3067
4075
  this.settingsStore = options.settingsStore;
3068
4076
  this.marketplaceClient = options.marketplaceClient;
3069
4077
  this.exec = options.exec ?? this.defaultExec;
@@ -3083,8 +4091,8 @@ var BundlePluginInstaller = class {
3083
4091
  throw new Error(`Plugin "${pluginName}" not found in marketplace "${marketplaceName}"`);
3084
4092
  }
3085
4093
  const version = this.resolveVersion(entry, marketplaceName);
3086
- const targetDir = (0, import_node_path7.join)(this.cacheDir, marketplaceName, pluginName, version);
3087
- if ((0, import_node_fs7.existsSync)(targetDir)) {
4094
+ const targetDir = (0, import_node_path9.join)(this.cacheDir, marketplaceName, pluginName, version);
4095
+ if ((0, import_node_fs10.existsSync)(targetDir)) {
3088
4096
  throw new Error(
3089
4097
  `Plugin "${pluginName}" version "${version}" is already installed from "${marketplaceName}"`
3090
4098
  );
@@ -3111,8 +4119,8 @@ var BundlePluginInstaller = class {
3111
4119
  if (!record) {
3112
4120
  throw new Error(`Plugin "${pluginId}" is not installed`);
3113
4121
  }
3114
- if ((0, import_node_fs7.existsSync)(record.installPath)) {
3115
- (0, import_node_fs7.rmSync)(record.installPath, { recursive: true, force: true });
4122
+ if ((0, import_node_fs10.existsSync)(record.installPath)) {
4123
+ (0, import_node_fs10.rmSync)(record.installPath, { recursive: true, force: true });
3116
4124
  }
3117
4125
  delete registry[pluginId];
3118
4126
  this.writeRegistry(registry);
@@ -3158,18 +4166,18 @@ var BundlePluginInstaller = class {
3158
4166
  }
3159
4167
  /** Resolve the source and install the plugin. */
3160
4168
  resolveAndInstall(rawSource, marketplaceName, pluginName, targetDir) {
3161
- (0, import_node_fs7.mkdirSync)(targetDir, { recursive: true });
4169
+ (0, import_node_fs10.mkdirSync)(targetDir, { recursive: true });
3162
4170
  const source = this.normalizeSource(rawSource);
3163
4171
  try {
3164
4172
  if (typeof source === "string") {
3165
4173
  const marketplaceDir = this.marketplaceClient.getMarketplaceDir(marketplaceName);
3166
- const sourcePath = (0, import_node_path7.join)(marketplaceDir, source);
3167
- if (!(0, import_node_fs7.existsSync)(sourcePath)) {
4174
+ const sourcePath = (0, import_node_path9.join)(marketplaceDir, source);
4175
+ if (!(0, import_node_fs10.existsSync)(sourcePath)) {
3168
4176
  throw new Error(
3169
4177
  `Plugin source path "${source}" not found in marketplace "${marketplaceName}"`
3170
4178
  );
3171
4179
  }
3172
- (0, import_node_fs7.cpSync)(sourcePath, targetDir, { recursive: true });
4180
+ (0, import_node_fs10.cpSync)(sourcePath, targetDir, { recursive: true });
3173
4181
  } else if (source.type === "github") {
3174
4182
  const repoUrl = `https://github.com/${source.repo}.git`;
3175
4183
  this.cloneToDir(repoUrl, targetDir, pluginName);
@@ -3181,15 +4189,15 @@ var BundlePluginInstaller = class {
3181
4189
  throw new Error(`Unknown source type: ${JSON.stringify(source)}`);
3182
4190
  }
3183
4191
  } catch (err) {
3184
- if ((0, import_node_fs7.existsSync)(targetDir)) {
3185
- (0, import_node_fs7.rmSync)(targetDir, { recursive: true, force: true });
4192
+ if ((0, import_node_fs10.existsSync)(targetDir)) {
4193
+ (0, import_node_fs10.rmSync)(targetDir, { recursive: true, force: true });
3186
4194
  }
3187
4195
  throw err;
3188
4196
  }
3189
4197
  }
3190
4198
  /** Clone a git repository to the target directory. */
3191
4199
  cloneToDir(repoUrl, targetDir, pluginName) {
3192
- (0, import_node_fs7.rmSync)(targetDir, { recursive: true, force: true });
4200
+ (0, import_node_fs10.rmSync)(targetDir, { recursive: true, force: true });
3193
4201
  const command = `git clone --depth 1 ${repoUrl} ${targetDir}`;
3194
4202
  try {
3195
4203
  this.exec(command, { timeout: GIT_CLONE_TIMEOUT_MS, stdio: "pipe" });
@@ -3200,11 +4208,11 @@ var BundlePluginInstaller = class {
3200
4208
  }
3201
4209
  /** Read the installed_plugins.json registry. */
3202
4210
  readRegistry() {
3203
- if (!(0, import_node_fs7.existsSync)(this.registryPath)) {
4211
+ if (!(0, import_node_fs10.existsSync)(this.registryPath)) {
3204
4212
  return {};
3205
4213
  }
3206
4214
  try {
3207
- const raw = (0, import_node_fs7.readFileSync)(this.registryPath, "utf-8");
4215
+ const raw = (0, import_node_fs10.readFileSync)(this.registryPath, "utf-8");
3208
4216
  const data = JSON.parse(raw);
3209
4217
  if (typeof data === "object" && data !== null) {
3210
4218
  return data;
@@ -3216,11 +4224,11 @@ var BundlePluginInstaller = class {
3216
4224
  }
3217
4225
  /** Write the installed_plugins.json registry. */
3218
4226
  writeRegistry(registry) {
3219
- const dir = (0, import_node_path7.dirname)(this.registryPath);
3220
- if (!(0, import_node_fs7.existsSync)(dir)) {
3221
- (0, import_node_fs7.mkdirSync)(dir, { recursive: true });
4227
+ const dir = (0, import_node_path9.dirname)(this.registryPath);
4228
+ if (!(0, import_node_fs10.existsSync)(dir)) {
4229
+ (0, import_node_fs10.mkdirSync)(dir, { recursive: true });
3222
4230
  }
3223
- (0, import_node_fs7.writeFileSync)(this.registryPath, JSON.stringify(registry, null, 2), "utf-8");
4231
+ (0, import_node_fs10.writeFileSync)(this.registryPath, JSON.stringify(registry, null, 2), "utf-8");
3224
4232
  }
3225
4233
  /** Default exec implementation using child_process. */
3226
4234
  defaultExec(command, options) {
@@ -3230,18 +4238,18 @@ var BundlePluginInstaller = class {
3230
4238
 
3231
4239
  // src/plugins/marketplace-client.ts
3232
4240
  var import_node_child_process3 = require("child_process");
3233
- var import_node_fs9 = require("fs");
3234
- var import_node_path9 = require("path");
4241
+ var import_node_fs12 = require("fs");
4242
+ var import_node_path11 = require("path");
3235
4243
 
3236
4244
  // src/plugins/marketplace-registry.ts
3237
- var import_node_fs8 = require("fs");
3238
- var import_node_path8 = require("path");
4245
+ var import_node_fs11 = require("fs");
4246
+ var import_node_path10 = require("path");
3239
4247
  function readRegistry(registryPath) {
3240
- if (!(0, import_node_fs8.existsSync)(registryPath)) {
4248
+ if (!(0, import_node_fs11.existsSync)(registryPath)) {
3241
4249
  return {};
3242
4250
  }
3243
4251
  try {
3244
- const raw = (0, import_node_fs8.readFileSync)(registryPath, "utf-8");
4252
+ const raw = (0, import_node_fs11.readFileSync)(registryPath, "utf-8");
3245
4253
  const data = JSON.parse(raw);
3246
4254
  if (typeof data === "object" && data !== null) {
3247
4255
  return data;
@@ -3252,18 +4260,18 @@ function readRegistry(registryPath) {
3252
4260
  }
3253
4261
  }
3254
4262
  function writeRegistry(registryPath, registry) {
3255
- const dir = (0, import_node_path8.dirname)(registryPath);
3256
- if (!(0, import_node_fs8.existsSync)(dir)) {
3257
- (0, import_node_fs8.mkdirSync)(dir, { recursive: true });
4263
+ const dir = (0, import_node_path10.dirname)(registryPath);
4264
+ if (!(0, import_node_fs11.existsSync)(dir)) {
4265
+ (0, import_node_fs11.mkdirSync)(dir, { recursive: true });
3258
4266
  }
3259
- (0, import_node_fs8.writeFileSync)(registryPath, JSON.stringify(registry, null, 2), "utf-8");
4267
+ (0, import_node_fs11.writeFileSync)(registryPath, JSON.stringify(registry, null, 2), "utf-8");
3260
4268
  }
3261
4269
  function removeInstalledPluginsForMarketplace(pluginsDir, marketplaceName) {
3262
- const installedPath = (0, import_node_path8.join)(pluginsDir, "installed_plugins.json");
3263
- if (!(0, import_node_fs8.existsSync)(installedPath)) return;
4270
+ const installedPath = (0, import_node_path10.join)(pluginsDir, "installed_plugins.json");
4271
+ if (!(0, import_node_fs11.existsSync)(installedPath)) return;
3264
4272
  let registry;
3265
4273
  try {
3266
- const raw = (0, import_node_fs8.readFileSync)(installedPath, "utf-8");
4274
+ const raw = (0, import_node_fs11.readFileSync)(installedPath, "utf-8");
3267
4275
  const data = JSON.parse(raw);
3268
4276
  if (typeof data !== "object" || data === null) return;
3269
4277
  registry = data;
@@ -3273,19 +4281,19 @@ function removeInstalledPluginsForMarketplace(pluginsDir, marketplaceName) {
3273
4281
  let changed = false;
3274
4282
  for (const [pluginId, record] of Object.entries(registry)) {
3275
4283
  if (record.marketplace === marketplaceName) {
3276
- if (record.installPath && (0, import_node_fs8.existsSync)(record.installPath)) {
3277
- (0, import_node_fs8.rmSync)(record.installPath, { recursive: true, force: true });
4284
+ if (record.installPath && (0, import_node_fs11.existsSync)(record.installPath)) {
4285
+ (0, import_node_fs11.rmSync)(record.installPath, { recursive: true, force: true });
3278
4286
  }
3279
4287
  delete registry[pluginId];
3280
4288
  changed = true;
3281
4289
  }
3282
4290
  }
3283
4291
  if (changed) {
3284
- const dir = (0, import_node_path8.dirname)(installedPath);
3285
- if (!(0, import_node_fs8.existsSync)(dir)) {
3286
- (0, import_node_fs8.mkdirSync)(dir, { recursive: true });
4292
+ const dir = (0, import_node_path10.dirname)(installedPath);
4293
+ if (!(0, import_node_fs11.existsSync)(dir)) {
4294
+ (0, import_node_fs11.mkdirSync)(dir, { recursive: true });
3287
4295
  }
3288
- (0, import_node_fs8.writeFileSync)(installedPath, JSON.stringify(registry, null, 2), "utf-8");
4296
+ (0, import_node_fs11.writeFileSync)(installedPath, JSON.stringify(registry, null, 2), "utf-8");
3289
4297
  }
3290
4298
  }
3291
4299
 
@@ -3299,8 +4307,8 @@ var MarketplaceClient = class {
3299
4307
  constructor(options) {
3300
4308
  this.pluginsDir = options.pluginsDir;
3301
4309
  this.exec = options.exec ?? this.defaultExec;
3302
- this.marketplacesDir = (0, import_node_path9.join)(this.pluginsDir, "marketplaces");
3303
- this.registryPath = (0, import_node_path9.join)(this.pluginsDir, "known_marketplaces.json");
4310
+ this.marketplacesDir = (0, import_node_path11.join)(this.pluginsDir, "marketplaces");
4311
+ this.registryPath = (0, import_node_path11.join)(this.pluginsDir, "known_marketplaces.json");
3304
4312
  }
3305
4313
  /**
3306
4314
  * Add a marketplace by cloning its repository.
@@ -3313,13 +4321,13 @@ var MarketplaceClient = class {
3313
4321
  */
3314
4322
  addMarketplace(source) {
3315
4323
  const tempName = "temp-" + Date.now().toString(36);
3316
- const tempDir = (0, import_node_path9.join)(this.marketplacesDir, tempName);
3317
- (0, import_node_fs9.mkdirSync)(this.marketplacesDir, { recursive: true });
4324
+ const tempDir = (0, import_node_path11.join)(this.marketplacesDir, tempName);
4325
+ (0, import_node_fs12.mkdirSync)(this.marketplacesDir, { recursive: true });
3318
4326
  if (source.type === "local") {
3319
- if (!(0, import_node_fs9.existsSync)(source.path)) {
4327
+ if (!(0, import_node_fs12.existsSync)(source.path)) {
3320
4328
  throw new Error(`Local marketplace path does not exist: ${source.path}`);
3321
4329
  }
3322
- (0, import_node_fs9.cpSync)(source.path, tempDir, { recursive: true });
4330
+ (0, import_node_fs12.cpSync)(source.path, tempDir, { recursive: true });
3323
4331
  } else {
3324
4332
  const cloneUrl = this.resolveCloneUrl(source);
3325
4333
  const command = `git clone --depth 1 ${cloneUrl} ${tempDir}`;
@@ -3330,9 +4338,9 @@ var MarketplaceClient = class {
3330
4338
  throw new Error(`Failed to clone marketplace: ${message}`);
3331
4339
  }
3332
4340
  }
3333
- const manifestPath = (0, import_node_path9.join)(tempDir, ".claude-plugin", "marketplace.json");
3334
- if (!(0, import_node_fs9.existsSync)(manifestPath)) {
3335
- (0, import_node_fs9.rmSync)(tempDir, { recursive: true, force: true });
4341
+ const manifestPath = (0, import_node_path11.join)(tempDir, ".claude-plugin", "marketplace.json");
4342
+ if (!(0, import_node_fs12.existsSync)(manifestPath)) {
4343
+ (0, import_node_fs12.rmSync)(tempDir, { recursive: true, force: true });
3336
4344
  throw new Error(
3337
4345
  source.type === "local" ? "Local directory does not contain .claude-plugin/marketplace.json" : "Cloned repository does not contain .claude-plugin/marketplace.json"
3338
4346
  );
@@ -3340,16 +4348,16 @@ var MarketplaceClient = class {
3340
4348
  const manifest = this.readManifestFromPath(manifestPath);
3341
4349
  const name = manifest.name;
3342
4350
  if (!name) {
3343
- (0, import_node_fs9.rmSync)(tempDir, { recursive: true, force: true });
4351
+ (0, import_node_fs12.rmSync)(tempDir, { recursive: true, force: true });
3344
4352
  throw new Error('Marketplace manifest does not contain a "name" field');
3345
4353
  }
3346
4354
  const registry = readRegistry(this.registryPath);
3347
4355
  if (registry[name]) {
3348
- (0, import_node_fs9.rmSync)(tempDir, { recursive: true, force: true });
4356
+ (0, import_node_fs12.rmSync)(tempDir, { recursive: true, force: true });
3349
4357
  throw new Error(`Marketplace "${name}" already exists`);
3350
4358
  }
3351
- const finalDir = (0, import_node_path9.join)(this.marketplacesDir, name);
3352
- (0, import_node_fs9.renameSync)(tempDir, finalDir);
4359
+ const finalDir = (0, import_node_path11.join)(this.marketplacesDir, name);
4360
+ (0, import_node_fs12.renameSync)(tempDir, finalDir);
3353
4361
  registry[name] = {
3354
4362
  source,
3355
4363
  installLocation: finalDir,
@@ -3370,8 +4378,8 @@ var MarketplaceClient = class {
3370
4378
  throw new Error(`Marketplace "${name}" not found`);
3371
4379
  }
3372
4380
  removeInstalledPluginsForMarketplace(this.pluginsDir, name);
3373
- if ((0, import_node_fs9.existsSync)(entry.installLocation)) {
3374
- (0, import_node_fs9.rmSync)(entry.installLocation, { recursive: true, force: true });
4381
+ if ((0, import_node_fs12.existsSync)(entry.installLocation)) {
4382
+ (0, import_node_fs12.rmSync)(entry.installLocation, { recursive: true, force: true });
3375
4383
  }
3376
4384
  delete registry[name];
3377
4385
  writeRegistry(this.registryPath, registry);
@@ -3387,16 +4395,16 @@ var MarketplaceClient = class {
3387
4395
  if (!entry) {
3388
4396
  throw new Error(`Marketplace "${name}" not found`);
3389
4397
  }
3390
- if (!(0, import_node_fs9.existsSync)(entry.installLocation)) {
4398
+ if (!(0, import_node_fs12.existsSync)(entry.installLocation)) {
3391
4399
  throw new Error(`Marketplace directory for "${name}" does not exist`);
3392
4400
  }
3393
4401
  if (entry.source.type === "local") {
3394
4402
  const localSource = entry.source;
3395
- if (!(0, import_node_fs9.existsSync)(localSource.path)) {
4403
+ if (!(0, import_node_fs12.existsSync)(localSource.path)) {
3396
4404
  throw new Error(`Local marketplace path does not exist: ${localSource.path}`);
3397
4405
  }
3398
- (0, import_node_fs9.rmSync)(entry.installLocation, { recursive: true, force: true });
3399
- (0, import_node_fs9.cpSync)(localSource.path, entry.installLocation, { recursive: true });
4406
+ (0, import_node_fs12.rmSync)(entry.installLocation, { recursive: true, force: true });
4407
+ (0, import_node_fs12.cpSync)(localSource.path, entry.installLocation, { recursive: true });
3400
4408
  } else {
3401
4409
  const command = `git -C ${entry.installLocation} pull`;
3402
4410
  try {
@@ -3425,8 +4433,8 @@ var MarketplaceClient = class {
3425
4433
  if (!entry) {
3426
4434
  throw new Error(`Marketplace "${marketplaceName}" not found`);
3427
4435
  }
3428
- const manifestPath = (0, import_node_path9.join)(entry.installLocation, ".claude-plugin", "marketplace.json");
3429
- if (!(0, import_node_fs9.existsSync)(manifestPath)) {
4436
+ const manifestPath = (0, import_node_path11.join)(entry.installLocation, ".claude-plugin", "marketplace.json");
4437
+ if (!(0, import_node_fs12.existsSync)(manifestPath)) {
3430
4438
  throw new Error(
3431
4439
  `Marketplace "${marketplaceName}" does not contain .claude-plugin/marketplace.json`
3432
4440
  );
@@ -3489,7 +4497,7 @@ var MarketplaceClient = class {
3489
4497
  }
3490
4498
  /** Read and parse a marketplace.json from a file path. */
3491
4499
  readManifestFromPath(path) {
3492
- const raw = (0, import_node_fs9.readFileSync)(path, "utf-8");
4500
+ const raw = (0, import_node_fs12.readFileSync)(path, "utf-8");
3493
4501
  const data = JSON.parse(raw);
3494
4502
  if (typeof data !== "object" || data === null) {
3495
4503
  throw new Error("Invalid marketplace manifest: not an object");
@@ -3507,9 +4515,9 @@ var MarketplaceClient = class {
3507
4515
  };
3508
4516
 
3509
4517
  // src/plugins/plugin-hooks-merger.ts
3510
- var import_node_path10 = require("path");
4518
+ var import_node_path12 = require("path");
3511
4519
  function buildPluginEnv(plugin) {
3512
- const dataDir = (0, import_node_path10.join)((0, import_node_path10.dirname)((0, import_node_path10.dirname)(plugin.pluginDir)), "data", plugin.manifest.name);
4520
+ const dataDir = (0, import_node_path12.join)((0, import_node_path12.dirname)((0, import_node_path12.dirname)(plugin.pluginDir)), "data", plugin.manifest.name);
3513
4521
  return {
3514
4522
  CLAUDE_PLUGIN_ROOT: plugin.pluginDir,
3515
4523
  CLAUDE_PLUGIN_PATH: plugin.pluginDir,
@@ -3572,15 +4580,15 @@ function mergeHooksIntoConfig(configHooks, pluginHooks) {
3572
4580
 
3573
4581
  // src/interactive/interactive-session-init.ts
3574
4582
  var import_node_os4 = require("os");
3575
- var import_node_path11 = require("path");
4583
+ var import_node_path13 = require("path");
3576
4584
  async function createInteractiveSession(options) {
3577
4585
  const cwd = options.cwd;
3578
4586
  const [config, context, projectInfo] = await Promise.all([
3579
- loadConfig(cwd),
4587
+ options.config ? Promise.resolve(options.config) : loadConfig(cwd),
3580
4588
  options.bare ? Promise.resolve({ agentsMd: "", claudeMd: "" }) : loadContext(cwd),
3581
4589
  options.bare ? Promise.resolve({ type: "unknown", language: "unknown" }) : detectProject(cwd)
3582
4590
  ]);
3583
- const pluginsDir = (0, import_node_path11.join)((0, import_node_os4.homedir)(), ".robota", "plugins");
4591
+ const pluginsDir = (0, import_node_path13.join)((0, import_node_os4.homedir)(), ".robota", "plugins");
3584
4592
  const pluginLoader = new BundlePluginLoader(pluginsDir);
3585
4593
  let mergedConfig = config;
3586
4594
  if (!options.bare) {
@@ -3613,6 +4621,7 @@ async function createInteractiveSession(options) {
3613
4621
  permissionHandler: options.permissionHandler,
3614
4622
  provider: options.provider,
3615
4623
  onTextDelta: options.onTextDelta,
4624
+ onContextUpdate: options.onContextUpdate,
3616
4625
  onToolExecution: options.onToolExecution,
3617
4626
  sessionId,
3618
4627
  allowedTools: options.allowedTools,
@@ -3629,7 +4638,8 @@ async function createInteractiveSession(options) {
3629
4638
  ]
3630
4639
  } : {},
3631
4640
  modelCommandExecutor: options.modelCommandExecutor,
3632
- isModelCommandInvocable: options.isModelCommandInvocable
4641
+ isModelCommandInvocable: options.isModelCommandInvocable,
4642
+ editCheckpointRecorder: options.editCheckpointRecorder
3633
4643
  });
3634
4644
  }
3635
4645
  function injectSavedMessage(session, msg) {
@@ -3655,7 +4665,9 @@ function loadSessionRecord(sessionStore, resumeSessionId, forkSession, existingS
3655
4665
  backgroundTasks: [],
3656
4666
  backgroundTaskEvents: [],
3657
4667
  backgroundJobGroups: [],
3658
- backgroundJobGroupEvents: []
4668
+ backgroundJobGroupEvents: [],
4669
+ memoryEvents: [],
4670
+ usedMemoryReferences: []
3659
4671
  };
3660
4672
  }
3661
4673
  const history = record.history ?? [];
@@ -3663,6 +4675,8 @@ function loadSessionRecord(sessionStore, resumeSessionId, forkSession, existingS
3663
4675
  const restoredBackgroundTaskEvents = record.backgroundTaskEvents ?? [];
3664
4676
  const backgroundJobGroups = record.backgroundJobGroups ?? [];
3665
4677
  const backgroundJobGroupEvents = record.backgroundJobGroupEvents ?? [];
4678
+ const memoryEvents = record.memoryEvents ?? [];
4679
+ const usedMemoryReferences = record.usedMemoryReferences ?? [];
3666
4680
  const { backgroundTasks, backgroundTaskEvents } = reconcileRestoredBackgroundTasks(
3667
4681
  restoredBackgroundTasks,
3668
4682
  restoredBackgroundTaskEvents
@@ -3685,7 +4699,9 @@ function loadSessionRecord(sessionStore, resumeSessionId, forkSession, existingS
3685
4699
  backgroundTasks,
3686
4700
  backgroundTaskEvents,
3687
4701
  backgroundJobGroups,
3688
- backgroundJobGroupEvents
4702
+ backgroundJobGroupEvents,
4703
+ memoryEvents,
4704
+ usedMemoryReferences
3689
4705
  };
3690
4706
  }
3691
4707
  function reconcileRestoredBackgroundTasks(tasks, events) {
@@ -3718,6 +4734,189 @@ function isRestoredTerminalStatus(status) {
3718
4734
  return status === "completed" || status === "failed" || status === "cancelled";
3719
4735
  }
3720
4736
 
4737
+ // src/checkpoints/edit-checkpoint-store.ts
4738
+ var import_promises = require("fs/promises");
4739
+ var import_node_fs13 = require("fs");
4740
+ var import_node_path14 = require("path");
4741
+ var MANIFEST_FILE = "manifest.json";
4742
+ var SNAPSHOT_DIR = "files";
4743
+ var ID_PAD = 4;
4744
+ var SNAPSHOT_PAD = 6;
4745
+ var EditCheckpointStore = class {
4746
+ cwd;
4747
+ rootDir;
4748
+ now;
4749
+ activeTurn = null;
4750
+ constructor(options) {
4751
+ this.cwd = (0, import_node_path14.resolve)(options.cwd);
4752
+ this.rootDir = projectPaths(this.cwd).checkpoints;
4753
+ this.now = options.now ?? (() => /* @__PURE__ */ new Date());
4754
+ }
4755
+ async beginTurn(input) {
4756
+ if (this.activeTurn) {
4757
+ await this.finalizeTurn();
4758
+ }
4759
+ const nextSequence = this.nextSequence(input.sessionId);
4760
+ const id = `turn-${String(nextSequence).padStart(ID_PAD, "0")}`;
4761
+ const dir = (0, import_node_path14.join)(this.sessionDir(input.sessionId), id);
4762
+ await (0, import_promises.mkdir)((0, import_node_path14.join)(dir, SNAPSHOT_DIR), { recursive: true });
4763
+ const manifest = {
4764
+ version: 1,
4765
+ id,
4766
+ sessionId: input.sessionId,
4767
+ sequence: nextSequence,
4768
+ prompt: input.prompt,
4769
+ createdAt: this.now().toISOString(),
4770
+ fileCount: 0,
4771
+ files: []
4772
+ };
4773
+ this.activeTurn = {
4774
+ manifest,
4775
+ dir,
4776
+ capturedPaths: /* @__PURE__ */ new Set()
4777
+ };
4778
+ return toSummary(manifest);
4779
+ }
4780
+ async captureFile(filePath) {
4781
+ if (!this.activeTurn) return;
4782
+ const originalPath = (0, import_node_path14.resolve)(this.cwd, filePath);
4783
+ if (this.activeTurn.capturedPaths.has(originalPath)) return;
4784
+ if (isInside(this.rootDir, originalPath)) return;
4785
+ const record = await this.createFileRecord(originalPath, this.activeTurn);
4786
+ this.activeTurn.manifest.files.push(record);
4787
+ this.activeTurn.manifest.fileCount = this.activeTurn.manifest.files.length;
4788
+ this.activeTurn.capturedPaths.add(originalPath);
4789
+ }
4790
+ async finalizeTurn() {
4791
+ if (!this.activeTurn) return void 0;
4792
+ const active = this.activeTurn;
4793
+ this.activeTurn = null;
4794
+ await this.writeManifest(active.dir, active.manifest);
4795
+ return toSummary(active.manifest);
4796
+ }
4797
+ list(sessionId) {
4798
+ return this.loadManifests(sessionId).map(toSummary);
4799
+ }
4800
+ async restoreToCheckpoint(sessionId, checkpointId) {
4801
+ const manifests = this.loadManifests(sessionId);
4802
+ const target = manifests.find((manifest) => manifest.id === checkpointId);
4803
+ if (!target) {
4804
+ throw new Error(`Unknown edit checkpoint: ${checkpointId}`);
4805
+ }
4806
+ const later = manifests.filter((manifest) => manifest.sequence > target.sequence).sort((a, b) => b.sequence - a.sequence);
4807
+ let restoredFileCount = 0;
4808
+ for (const manifest of later) {
4809
+ for (const file of manifest.files) {
4810
+ await this.restoreFile(sessionId, manifest.id, file);
4811
+ restoredFileCount += 1;
4812
+ }
4813
+ }
4814
+ for (const manifest of later) {
4815
+ await (0, import_promises.rm)(this.checkpointDir(sessionId, manifest.id), { recursive: true, force: true });
4816
+ }
4817
+ return {
4818
+ target: toSummary(target),
4819
+ restoredCheckpointCount: later.length,
4820
+ restoredFileCount,
4821
+ removedCheckpointCount: later.length
4822
+ };
4823
+ }
4824
+ async createFileRecord(originalPath, active) {
4825
+ const existed = await pathExists(originalPath);
4826
+ if (!existed) {
4827
+ return {
4828
+ originalPath,
4829
+ existed: false
4830
+ };
4831
+ }
4832
+ const snapshotFile = (0, import_node_path14.join)(
4833
+ SNAPSHOT_DIR,
4834
+ `${String(active.manifest.files.length + 1).padStart(SNAPSHOT_PAD, "0")}.content`
4835
+ );
4836
+ await (0, import_promises.copyFile)(originalPath, (0, import_node_path14.join)(active.dir, snapshotFile));
4837
+ return {
4838
+ originalPath,
4839
+ existed: true,
4840
+ snapshotFile
4841
+ };
4842
+ }
4843
+ async restoreFile(sessionId, checkpointId, record) {
4844
+ if (!record.existed) {
4845
+ await (0, import_promises.rm)(record.originalPath, { force: true });
4846
+ return;
4847
+ }
4848
+ if (!record.snapshotFile) {
4849
+ throw new Error(`Checkpoint file record is missing a snapshot: ${record.originalPath}`);
4850
+ }
4851
+ await (0, import_promises.mkdir)((0, import_node_path14.dirname)(record.originalPath), { recursive: true });
4852
+ await (0, import_promises.copyFile)(
4853
+ (0, import_node_path14.join)(this.checkpointDir(sessionId, checkpointId), record.snapshotFile),
4854
+ record.originalPath
4855
+ );
4856
+ }
4857
+ loadManifests(sessionId) {
4858
+ const dir = this.sessionDir(sessionId);
4859
+ return readDirSyncSafe(dir).map((entry) => (0, import_node_path14.join)(dir, entry, MANIFEST_FILE)).map((manifestPath) => readJsonManifest(manifestPath)).filter((manifest) => manifest !== void 0).sort((a, b) => a.sequence - b.sequence);
4860
+ }
4861
+ nextSequence(sessionId) {
4862
+ const last = this.list(sessionId).at(-1);
4863
+ return (last?.sequence ?? 0) + 1;
4864
+ }
4865
+ async writeManifest(dir, manifest) {
4866
+ await (0, import_promises.mkdir)(dir, { recursive: true });
4867
+ const path = (0, import_node_path14.join)(dir, MANIFEST_FILE);
4868
+ const tmp = `${path}.tmp`;
4869
+ await (0, import_promises.writeFile)(tmp, JSON.stringify(manifest, null, 2), "utf8");
4870
+ await (0, import_promises.rename)(tmp, path);
4871
+ }
4872
+ sessionDir(sessionId) {
4873
+ return (0, import_node_path14.join)(this.rootDir, safePathSegment(sessionId));
4874
+ }
4875
+ checkpointDir(sessionId, checkpointId) {
4876
+ return (0, import_node_path14.join)(this.sessionDir(sessionId), safePathSegment(checkpointId));
4877
+ }
4878
+ };
4879
+ function toSummary(manifest) {
4880
+ return {
4881
+ id: manifest.id,
4882
+ sessionId: manifest.sessionId,
4883
+ sequence: manifest.sequence,
4884
+ prompt: manifest.prompt,
4885
+ createdAt: manifest.createdAt,
4886
+ fileCount: manifest.fileCount
4887
+ };
4888
+ }
4889
+ function safePathSegment(value) {
4890
+ return value.replace(/[^a-zA-Z0-9._-]/g, "_");
4891
+ }
4892
+ function isInside(parent, child) {
4893
+ const rel = (0, import_node_path14.relative)(parent, child);
4894
+ return rel.length === 0 || !rel.startsWith("..") && !rel.startsWith("/");
4895
+ }
4896
+ async function pathExists(path) {
4897
+ try {
4898
+ await (0, import_promises.access)(path, import_node_fs13.constants.F_OK);
4899
+ return true;
4900
+ } catch {
4901
+ return false;
4902
+ }
4903
+ }
4904
+ function readDirSyncSafe(dir) {
4905
+ try {
4906
+ return (0, import_node_fs13.readdirSync)(dir);
4907
+ } catch {
4908
+ return [];
4909
+ }
4910
+ }
4911
+ function readJsonManifest(path) {
4912
+ try {
4913
+ const raw = (0, import_node_fs13.readFileSync)(path, "utf8");
4914
+ return JSON.parse(raw);
4915
+ } catch {
4916
+ return void 0;
4917
+ }
4918
+ }
4919
+
3721
4920
  // src/interactive/interactive-session.ts
3722
4921
  var InteractiveSession = class {
3723
4922
  session = null;
@@ -3741,6 +4940,9 @@ var InteractiveSession = class {
3741
4940
  backgroundTaskEvents = [];
3742
4941
  backgroundJobGroups = [];
3743
4942
  backgroundJobGroupEvents = [];
4943
+ memoryEvents = [];
4944
+ usedMemoryReferences = [];
4945
+ editCheckpointStore = null;
3744
4946
  resumeSessionId;
3745
4947
  forkSession;
3746
4948
  backgroundTaskUnsubscribe = null;
@@ -3758,6 +4960,9 @@ var InteractiveSession = class {
3758
4960
  this.sessionStore = options.sessionStore;
3759
4961
  this.sessionName = options.sessionName;
3760
4962
  this.cwd = ("cwd" in options ? options.cwd : void 0) ?? "";
4963
+ if ("session" in options && options.session && this.cwd) {
4964
+ this.editCheckpointStore = new EditCheckpointStore({ cwd: this.cwd });
4965
+ }
3761
4966
  this.resumeSessionId = options.resumeSessionId;
3762
4967
  this.forkSession = options.forkSession ?? false;
3763
4968
  if ("session" in options && options.session) {
@@ -3780,21 +4985,27 @@ var InteractiveSession = class {
3780
4985
  this.backgroundTaskEvents = restored.backgroundTaskEvents;
3781
4986
  this.backgroundJobGroups = restored.backgroundJobGroups;
3782
4987
  this.backgroundJobGroupEvents = restored.backgroundJobGroupEvents;
4988
+ this.memoryEvents = restored.memoryEvents;
4989
+ this.usedMemoryReferences = restored.usedMemoryReferences;
3783
4990
  this.pendingRestoreMessages = restored.pendingRestoreMessages;
3784
4991
  }
3785
4992
  if (this.initialized) this.subscribeBackgroundTaskEvents();
3786
4993
  if (this.initialized) this.persistCurrentSession();
3787
4994
  }
3788
4995
  async initializeAsync(options) {
4996
+ const config = await loadConfig(options.cwd);
4997
+ this.editCheckpointStore = new EditCheckpointStore({ cwd: options.cwd });
3789
4998
  this.session = await createInteractiveSession({
3790
4999
  cwd: options.cwd,
3791
5000
  provider: options.provider,
5001
+ config,
3792
5002
  permissionMode: options.permissionMode,
3793
5003
  maxTurns: options.maxTurns,
3794
5004
  permissionHandler: options.permissionHandler,
3795
5005
  resumeSessionId: this.resumeSessionId,
3796
5006
  forkSession: this.forkSession,
3797
5007
  onTextDelta: (delta) => this.handleTextDelta(delta),
5008
+ onContextUpdate: (state) => this.emit("context_update", state),
3798
5009
  onToolExecution: (event) => this.handleToolExecution(event),
3799
5010
  bare: options.bare,
3800
5011
  allowedTools: options.allowedTools,
@@ -3802,6 +5013,7 @@ var InteractiveSession = class {
3802
5013
  backgroundTaskRunners: options.backgroundTaskRunners,
3803
5014
  subagentRunnerFactory: options.subagentRunnerFactory,
3804
5015
  ...options.commandModules ? { commandModules: options.commandModules } : {},
5016
+ editCheckpointRecorder: this.editCheckpointStore,
3805
5017
  commandDescriptors: this.commandExecutor.listModelInvocableCommands(),
3806
5018
  ...this.commandExecutor.listModelInvocableCommands().length > 0 ? {
3807
5019
  modelCommandExecutor: (command, args) => this.executeModelCommand(command, args),
@@ -3944,9 +5156,38 @@ var InteractiveSession = class {
3944
5156
  getName() {
3945
5157
  return this.sessionName;
3946
5158
  }
5159
+ getCwd() {
5160
+ return this.cwd ?? process.cwd();
5161
+ }
3947
5162
  getSession() {
3948
5163
  return this.getSessionOrThrow();
3949
5164
  }
5165
+ listEditCheckpoints() {
5166
+ const sessionId = this.getSessionOrThrow().getSessionId();
5167
+ return this.getEditCheckpointStore().list(sessionId);
5168
+ }
5169
+ async restoreEditCheckpoint(checkpointId) {
5170
+ await this.ensureInitialized();
5171
+ if (this.executing) {
5172
+ throw new Error("Cannot restore edit checkpoint while a prompt is running.");
5173
+ }
5174
+ const result = await this.getEditCheckpointStore().restoreToCheckpoint(
5175
+ this.getSessionOrThrow().getSessionId(),
5176
+ checkpointId
5177
+ );
5178
+ this.history.push(
5179
+ (0, import_agent_core4.messageToHistoryEntry)((0, import_agent_core4.createSystemMessage)(`Restored edit checkpoint: ${checkpointId}`))
5180
+ );
5181
+ this.persistCurrentSession();
5182
+ return result;
5183
+ }
5184
+ getUsedMemoryReferences() {
5185
+ return [...this.usedMemoryReferences];
5186
+ }
5187
+ recordMemoryEvent(event) {
5188
+ this.memoryEvents.push(event);
5189
+ this.persistCurrentSession();
5190
+ }
3950
5191
  listBackgroundTasks(filter) {
3951
5192
  return this.getBackgroundTaskManagerOrThrow().list(filter);
3952
5193
  }
@@ -4169,6 +5410,10 @@ var InteractiveSession = class {
4169
5410
  events: this.backgroundTaskEvents,
4170
5411
  groups: this.backgroundJobGroups,
4171
5412
  groupEvents: this.backgroundJobGroupEvents
5413
+ },
5414
+ {
5415
+ events: this.memoryEvents,
5416
+ usedReferences: this.usedMemoryReferences
4172
5417
  }
4173
5418
  );
4174
5419
  }
@@ -4176,7 +5421,7 @@ var InteractiveSession = class {
4176
5421
  this.executing = true;
4177
5422
  this.clearStreaming();
4178
5423
  this.emit("thinking", true);
4179
- this.history.push((0, import_agent_core3.messageToHistoryEntry)((0, import_agent_core3.createUserMessage)(displayInput)));
5424
+ this.history.push((0, import_agent_core4.messageToHistoryEntry)((0, import_agent_core4.createUserMessage)(displayInput)));
4180
5425
  }
4181
5426
  finishForkSkillExecution() {
4182
5427
  this.executing = false;
@@ -4191,7 +5436,7 @@ var InteractiveSession = class {
4191
5436
  }
4192
5437
  }
4193
5438
  recordForkSkillError(err) {
4194
- this.history.push((0, import_agent_core3.messageToHistoryEntry)((0, import_agent_core3.createSystemMessage)(`Error: ${err.message}`)));
5439
+ this.history.push((0, import_agent_core4.messageToHistoryEntry)((0, import_agent_core4.createSystemMessage)(`Error: ${err.message}`)));
4195
5440
  this.emit("error", err);
4196
5441
  }
4197
5442
  resolveForkAgentDefinition(agentType, options) {
@@ -4240,7 +5485,7 @@ var InteractiveSession = class {
4240
5485
  toolSummaries: [],
4241
5486
  contextState: this.getContextState()
4242
5487
  };
4243
- this.history.push((0, import_agent_core3.messageToHistoryEntry)((0, import_agent_core3.createAssistantMessage)(result)));
5488
+ this.history.push((0, import_agent_core4.messageToHistoryEntry)((0, import_agent_core4.createAssistantMessage)(result)));
4244
5489
  this.emit("complete", executionResult);
4245
5490
  this.emit("context_update", this.getContextState());
4246
5491
  }
@@ -4248,9 +5493,11 @@ var InteractiveSession = class {
4248
5493
  this.executing = true;
4249
5494
  this.clearStreaming();
4250
5495
  this.emit("thinking", true);
4251
- this.history.push((0, import_agent_core3.messageToHistoryEntry)((0, import_agent_core3.createUserMessage)(displayInput ?? input)));
5496
+ this.history.push((0, import_agent_core4.messageToHistoryEntry)((0, import_agent_core4.createUserMessage)(displayInput ?? input)));
4252
5497
  const historyBefore = this.getSessionOrThrow().getHistory().length;
5498
+ this.usedMemoryReferences = [];
4253
5499
  try {
5500
+ await this.beginEditCheckpointTurn(displayInput ?? input);
4254
5501
  const response = await this.getSessionOrThrow().run(input, rawInput);
4255
5502
  this.flushStreaming();
4256
5503
  pushToolSummaryToHistory({ activeTools: this.activeTools, history: this.history });
@@ -4262,7 +5509,8 @@ var InteractiveSession = class {
4262
5509
  historyBefore,
4263
5510
  this.getContextState()
4264
5511
  );
4265
- this.history.push((0, import_agent_core3.messageToHistoryEntry)((0, import_agent_core3.createAssistantMessage)(result.response)));
5512
+ this.history.push((0, import_agent_core4.messageToHistoryEntry)((0, import_agent_core4.createAssistantMessage)(result.response)));
5513
+ if (result.usage) this.history.push(createUsageSummaryEntry(result.usage));
4266
5514
  this.emit("complete", result);
4267
5515
  this.emit("context_update", this.getContextState());
4268
5516
  } catch (err) {
@@ -4277,17 +5525,19 @@ var InteractiveSession = class {
4277
5525
  pushToolSummaryToHistory({ activeTools: this.activeTools, history: this.history });
4278
5526
  this.clearStreaming();
4279
5527
  if (result.response)
4280
- this.history.push((0, import_agent_core3.messageToHistoryEntry)((0, import_agent_core3.createAssistantMessage)(result.response)));
4281
- this.history.push((0, import_agent_core3.messageToHistoryEntry)((0, import_agent_core3.createSystemMessage)("Interrupted by user.")));
5528
+ this.history.push((0, import_agent_core4.messageToHistoryEntry)((0, import_agent_core4.createAssistantMessage)(result.response)));
5529
+ if (result.usage) this.history.push(createUsageSummaryEntry(result.usage));
5530
+ this.history.push((0, import_agent_core4.messageToHistoryEntry)((0, import_agent_core4.createSystemMessage)("Interrupted by user.")));
4282
5531
  this.emit("interrupted", result);
4283
5532
  } else {
4284
5533
  pushToolSummaryToHistory({ activeTools: this.activeTools, history: this.history });
4285
5534
  this.clearStreaming();
4286
5535
  const errMsg = err instanceof Error ? err.message : String(err);
4287
- this.history.push((0, import_agent_core3.messageToHistoryEntry)((0, import_agent_core3.createSystemMessage)(`Error: ${errMsg}`)));
5536
+ this.history.push((0, import_agent_core4.messageToHistoryEntry)((0, import_agent_core4.createSystemMessage)(`Error: ${errMsg}`)));
4288
5537
  this.emit("error", err instanceof Error ? err : new Error(errMsg));
4289
5538
  }
4290
5539
  } finally {
5540
+ await this.finalizeEditCheckpointTurn();
4291
5541
  this.executing = false;
4292
5542
  this.emit("thinking", false);
4293
5543
  this.persistCurrentSession();
@@ -4300,6 +5550,31 @@ var InteractiveSession = class {
4300
5550
  }
4301
5551
  }
4302
5552
  }
5553
+ getEditCheckpointStore() {
5554
+ if (!this.editCheckpointStore) {
5555
+ this.editCheckpointStore = new EditCheckpointStore({ cwd: this.getCwd() });
5556
+ }
5557
+ return this.editCheckpointStore;
5558
+ }
5559
+ async beginEditCheckpointTurn(prompt) {
5560
+ if (!this.editCheckpointStore) return;
5561
+ await this.editCheckpointStore.beginTurn({
5562
+ sessionId: this.getSessionOrThrow().getSessionId(),
5563
+ prompt
5564
+ });
5565
+ }
5566
+ async finalizeEditCheckpointTurn() {
5567
+ if (!this.editCheckpointStore) return;
5568
+ try {
5569
+ await this.editCheckpointStore.finalizeTurn();
5570
+ } catch (error) {
5571
+ const err = error instanceof Error ? error : new Error(String(error));
5572
+ this.history.push(
5573
+ (0, import_agent_core4.messageToHistoryEntry)((0, import_agent_core4.createSystemMessage)(`Checkpoint error: ${err.message}`))
5574
+ );
5575
+ this.emit("error", err);
5576
+ }
5577
+ }
4303
5578
  handleTextDelta(delta) {
4304
5579
  this.streamingText += delta;
4305
5580
  this.emit("text_delta", delta);
@@ -4350,14 +5625,14 @@ function createQuery(options) {
4350
5625
  session.on("text_delta", options.onTextDelta);
4351
5626
  }
4352
5627
  return async (prompt) => {
4353
- return new Promise((resolve2, reject) => {
5628
+ return new Promise((resolve4, reject) => {
4354
5629
  const onComplete = (result) => {
4355
5630
  cleanup();
4356
- resolve2(result.response);
5631
+ resolve4(result.response);
4357
5632
  };
4358
5633
  const onInterrupted = (result) => {
4359
5634
  cleanup();
4360
- resolve2(result.response);
5635
+ resolve4(result.response);
4361
5636
  };
4362
5637
  const onError = (error) => {
4363
5638
  cleanup();
@@ -4380,17 +5655,132 @@ function createQuery(options) {
4380
5655
  }
4381
5656
 
4382
5657
  // src/types.ts
4383
- var import_agent_core4 = require("@robota-sdk/agent-core");
5658
+ var import_agent_core5 = require("@robota-sdk/agent-core");
4384
5659
 
4385
5660
  // src/index.ts
4386
- var import_agent_core5 = require("@robota-sdk/agent-core");
5661
+ var import_agent_core6 = require("@robota-sdk/agent-core");
5662
+
5663
+ // src/self-hosting/self-hosting-verification.ts
5664
+ var DEFAULT_BASE_REF = "origin/develop";
5665
+ var PACKAGE_VERIFY_COMMANDS = ["test", "typecheck", "build"];
5666
+ var TRANSITIONS = {
5667
+ idle: {
5668
+ checkpoint_created: "checkpointed",
5669
+ cancelled: "cancelled"
5670
+ },
5671
+ checkpointed: {
5672
+ edits_started: "editing",
5673
+ cancelled: "cancelled"
5674
+ },
5675
+ editing: {
5676
+ edits_applied: "verifying",
5677
+ verify_failed: "failed",
5678
+ cancelled: "cancelled"
5679
+ },
5680
+ verifying: {
5681
+ verify_passed: "passed",
5682
+ verify_failed: "failed",
5683
+ cancelled: "cancelled"
5684
+ },
5685
+ passed: {},
5686
+ failed: {
5687
+ rollback_completed: "rolled_back",
5688
+ cancelled: "cancelled"
5689
+ },
5690
+ rolled_back: {},
5691
+ cancelled: {}
5692
+ };
5693
+ function normalizePackageScopes(packageScopes) {
5694
+ if (!packageScopes) {
5695
+ return [];
5696
+ }
5697
+ return Array.from(new Set(packageScopes.map((scope) => scope.trim()).filter(Boolean)));
5698
+ }
5699
+ function packageVerificationSteps(packageScopes) {
5700
+ return packageScopes.flatMap(
5701
+ (scope) => PACKAGE_VERIFY_COMMANDS.map(
5702
+ (commandName) => ({
5703
+ id: `package-${commandName}:${scope}`,
5704
+ phase: "verify",
5705
+ description: `Run ${commandName} for ${scope} in a child process against the new on-disk tree.`,
5706
+ required: true,
5707
+ command: `pnpm --filter ${scope} ${commandName}`
5708
+ })
5709
+ )
5710
+ );
5711
+ }
5712
+ function preVerificationSteps() {
5713
+ return [
5714
+ {
5715
+ id: "checkpoint",
5716
+ phase: "checkpoint",
5717
+ description: "Create a recoverable turn-level checkpoint before the first mutation.",
5718
+ required: true
5719
+ },
5720
+ {
5721
+ id: "atomic-edit",
5722
+ phase: "edit",
5723
+ description: "Apply Write/Edit mutations through same-directory temp files and atomic rename.",
5724
+ required: true
5725
+ },
5726
+ {
5727
+ id: "handoff",
5728
+ phase: "handoff",
5729
+ description: "Keep the current process on already-loaded code and run verification child processes against disk.",
5730
+ required: true
5731
+ }
5732
+ ];
5733
+ }
5734
+ function harnessVerificationStep(baseRef) {
5735
+ return {
5736
+ id: "harness-verify",
5737
+ phase: "verify",
5738
+ description: "Run Robota harness verification as the local CI-like gate.",
5739
+ required: true,
5740
+ command: `pnpm harness:verify -- --base-ref ${baseRef} --skip-record-check`
5741
+ };
5742
+ }
5743
+ function rollbackRecoveryStep() {
5744
+ return {
5745
+ id: "rollback-on-failure",
5746
+ phase: "recover",
5747
+ description: "Use the existing edit checkpoint restore path if verification fails.",
5748
+ required: true
5749
+ };
5750
+ }
5751
+ function planSelfHostingVerification(input) {
5752
+ if (input.changedFiles.length === 0) {
5753
+ throw new Error("Self-hosting verification requires at least one changed file.");
5754
+ }
5755
+ const baseRef = input.baseRef ?? DEFAULT_BASE_REF;
5756
+ const packageScopes = normalizePackageScopes(input.packageScopes);
5757
+ const steps = [
5758
+ ...preVerificationSteps(),
5759
+ ...packageVerificationSteps(packageScopes),
5760
+ harnessVerificationStep(baseRef),
5761
+ rollbackRecoveryStep()
5762
+ ];
5763
+ return {
5764
+ changedFiles: [...input.changedFiles],
5765
+ packageScopes,
5766
+ baseRef,
5767
+ steps
5768
+ };
5769
+ }
5770
+ function transitionSelfHostingLoop(state, event) {
5771
+ const nextState = TRANSITIONS[state][event];
5772
+ if (!nextState) {
5773
+ throw new Error(`Invalid self-hosting loop transition: ${state} -> ${event}`);
5774
+ }
5775
+ return nextState;
5776
+ }
4387
5777
 
4388
5778
  // src/subagents/index.ts
4389
5779
  var import_agent_runtime7 = require("@robota-sdk/agent-runtime");
4390
5780
  var import_agent_runtime8 = require("@robota-sdk/agent-runtime");
4391
5781
 
4392
5782
  // src/index.ts
4393
- var import_agent_core6 = require("@robota-sdk/agent-core");
5783
+ var import_agent_core7 = require("@robota-sdk/agent-core");
4394
5784
 
4395
5785
  // src/permissions/permission-prompt.ts
4396
5786
  var import_chalk = __toESM(require("chalk"), 1);
@@ -4413,7 +5803,7 @@ async function promptForApproval(terminal, toolName, toolArgs) {
4413
5803
  }
4414
5804
 
4415
5805
  // src/index.ts
4416
- var import_agent_core7 = require("@robota-sdk/agent-core");
5806
+ var import_agent_core8 = require("@robota-sdk/agent-core");
4417
5807
  // Annotate the CommonJS export names for ESM import in node:
4418
5808
  0 && (module.exports = {
4419
5809
  AgentExecutor,
@@ -4425,10 +5815,14 @@ var import_agent_core7 = require("@robota-sdk/agent-core");
4425
5815
  BundlePluginInstaller,
4426
5816
  BundlePluginLoader,
4427
5817
  CommandRegistry,
5818
+ EditCheckpointStore,
4428
5819
  InteractiveSession,
5820
+ MEMORY_INDEX_MAX_BYTES,
5821
+ MEMORY_INDEX_MAX_LINES,
4429
5822
  MarketplaceClient,
4430
5823
  PluginCommandSource,
4431
5824
  PluginSettingsStore,
5825
+ ProjectMemoryStore,
4432
5826
  PromptExecutor,
4433
5827
  SkillCommandSource,
4434
5828
  SubagentManager,
@@ -4447,26 +5841,37 @@ var import_agent_core7 = require("@robota-sdk/agent-core");
4447
5841
  createSubagentSession,
4448
5842
  createSystemCommands,
4449
5843
  createWorktreeSubagentRunner,
5844
+ discoverTaskFiles,
4450
5845
  evaluatePermission,
4451
5846
  executeSkill,
5847
+ formatTaskContext,
4452
5848
  getBackgroundTaskTransitions,
4453
5849
  getBuiltInAgent,
4454
5850
  getForkWorkerSuffix,
4455
5851
  getMessagesForAPI,
4456
5852
  getSubagentSuffix,
4457
5853
  isChatEntry,
5854
+ isMemoryType,
4458
5855
  isTerminalBackgroundTaskStatus,
5856
+ loadTaskContext,
4459
5857
  messageToHistoryEntry,
4460
5858
  parseFrontmatter,
5859
+ parseTaskFile,
5860
+ planSelfHostingVerification,
4461
5861
  preprocessShellCommands,
4462
5862
  projectPaths,
4463
5863
  promptForApproval,
5864
+ readCurrentGitBranch,
4464
5865
  resolveSubagentLogDir,
4465
5866
  retrieveAgentToolDeps,
4466
5867
  runHooks,
5868
+ selectRelevantTasks,
4467
5869
  storeAgentToolDeps,
4468
5870
  substituteVariables,
4469
5871
  summarizeBackgroundJobGroup,
4470
5872
  transitionBackgroundTaskStatus,
4471
- userPaths
5873
+ transitionSelfHostingLoop,
5874
+ updateTaskFileStatus,
5875
+ userPaths,
5876
+ wrapEditCheckpointTools
4472
5877
  });