replicas-engine 0.1.400 → 0.1.403

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.
Files changed (2) hide show
  1. package/dist/src/index.js +327 -135
  2. package/package.json +1 -1
package/dist/src/index.js CHANGED
@@ -484,7 +484,7 @@ var WORKSPACE_SIZES = ["small", "large"];
484
484
  var INVALID_WORKSPACE_SIZE_ERROR = `Invalid size: must be one of ${WORKSPACE_SIZES.join(", ")}`;
485
485
 
486
486
  // ../shared/src/e2b.ts
487
- var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-07-06-v2";
487
+ var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-07-06-v5";
488
488
 
489
489
  // ../shared/src/runtime-env.ts
490
490
  function parsePosixEnvFile(content) {
@@ -647,8 +647,8 @@ var SLASH_COMMANDS = [
647
647
  },
648
648
  {
649
649
  command: "/goal",
650
- description: "Set or clear a task goal.",
651
- argumentHint: "<objective | clear>",
650
+ description: "Set, pause, resume, or clear a task goal.",
651
+ argumentHint: "<objective | pause | resume | clear>",
652
652
  providers: ["codex"]
653
653
  }
654
654
  ];
@@ -667,19 +667,23 @@ function getSlashCommandsForProvider(provider, commands = SLASH_COMMANDS) {
667
667
  return commands.filter((command) => command.providers.includes(provider));
668
668
  }
669
669
  var MAX_CODEX_GOAL_OBJECTIVE_CHARS = 4e3;
670
+ function classifyGoalValue(value) {
671
+ if (/^(clear|reset|unset)$/i.test(value)) return { type: "clear" };
672
+ if (/^pause$/i.test(value)) return { type: "pause" };
673
+ if (/^resume$/i.test(value)) return { type: "resume" };
674
+ return { type: "set", objective: value };
675
+ }
670
676
  function parseGoalCommand(message) {
671
677
  const match = message.trim().match(/^\/goal(?:\s+([\s\S]*))?$/i);
672
678
  const value = match?.[1]?.trim();
673
679
  if (!value) return null;
674
- if (/^(clear|reset|unset)$/i.test(value)) return { type: "clear" };
675
- return { type: "set", objective: value };
680
+ return classifyGoalValue(value);
676
681
  }
677
682
  function getGoalCommand(message, goalMode) {
678
683
  if (!goalMode) return parseGoalCommand(message);
679
684
  const value = message.trim();
680
685
  if (!value) return null;
681
- if (/^(clear|reset|unset)$/i.test(value)) return { type: "clear" };
682
- return { type: "set", objective: value };
686
+ return classifyGoalValue(value);
683
687
  }
684
688
  function getGoalCommandObjectiveValidationError(message, goalMode) {
685
689
  const command = getGoalCommand(message, goalMode);
@@ -1415,8 +1419,67 @@ var GOOGLE_ABILITY = {
1415
1419
  referenceFile: { name: "GOOGLE.md", content: REFERENCE5 }
1416
1420
  };
1417
1421
 
1422
+ // ../shared/src/default-skills/replicas-agent/abilities/learnings.ts
1423
+ var SECTION6 = `### Learnings
1424
+ Curated, human-approved organization knowledge that helps you execute correctly \u2014 conventions, gotchas, environment facts, and past user corrections that the codebase alone can't tell you.
1425
+
1426
+ **Reference:** \`references/LEARNINGS.md\`
1427
+
1428
+ Use this when:
1429
+ - You are starting a substantive task and have gathered enough context to write a meaningful query
1430
+ - A user corrects you about a durable fact, preference, or process worth keeping
1431
+ - You find an existing learning contradicted by the current code or docs`;
1432
+ var REFERENCE6 = `# Learnings
1433
+
1434
+ Learnings are curated, human-approved knowledge entries your organization maintains: conventions, gotchas, environment facts, and process rules. Some entries always apply; contextual entries are retrieved by matching your query against their trigger descriptions.
1435
+
1436
+ ## Reading Learnings
1437
+
1438
+ \`\`\`bash
1439
+ replicas learnings read --query "<enriched query>"
1440
+ \`\`\`
1441
+
1442
+ **When to read:** once you've collected enough context to write a meaningful query \u2014 typically after your first look at the relevant code. Don't wait until you're deep into execution; learnings are most valuable before you commit to an approach.
1443
+
1444
+ **Enrich the query.** Use codebase specifics (feature area, module/component names), not the raw user request. Triggers describe *situations*, so a query grounded in what the task actually touches matches far better.
1445
+
1446
+ Example: the user asks "remove the PRs chart projections". After locating the chart you know it lives in the platform admin dashboard, so query:
1447
+
1448
+ \`\`\`bash
1449
+ replicas learnings read --query "Remove the PRs chart in the platform admin dashboard"
1450
+ \`\`\`
1451
+
1452
+ This matches a learning triggered on "UI/UX work on the admin panel" that the raw request would have missed.
1453
+
1454
+ Output is a list of blocks \u2014 id, name, and content. Use the ids to propose updates or deletions.
1455
+
1456
+ ## Proposing changes
1457
+
1458
+ All writes are proposals: a human reviews them in the dashboard, and nothing takes effect until approved.
1459
+
1460
+ \`\`\`bash
1461
+ replicas learnings add --name "<short name>" --content "<the knowledge>" [--trigger "<when to surface it>"] [--always] [--global]
1462
+ replicas learnings update <id> [--name "..."] [--content "..."] [--trigger "..."] [--always true|false]
1463
+ replicas learnings delete <id>
1464
+ \`\`\`
1465
+
1466
+ \`--always\` marks an entry as always-returned (no trigger needed); \`--global\` proposes org-wide scope instead of this workspace's environment.
1467
+
1468
+ **Be very conservative:**
1469
+ - Propose \`add\` only for durable, org-useful facts: explicit user corrections, hard-won environment or process knowledge.
1470
+ - Never store task-specific details, anything derivable by reading the repo, or secrets/credentials.
1471
+ - Propose \`update\` or \`delete\` when a learning is contradicted by the current code or docs \u2014 flag outdated knowledge instead of silently ignoring it.
1472
+ - Write narrow triggers describing when the entry should surface (e.g. "UI/UX work on the admin panel"), not vague topics.`;
1473
+ var LEARNINGS_ABILITY = {
1474
+ label: "Learnings",
1475
+ description: "Fetch curated org knowledge and propose new learnings for human review.",
1476
+ bullet: "- Fetching organization Learnings (curated, human-approved knowledge) before executing, or proposing new ones",
1477
+ section: SECTION6,
1478
+ referenceFile: { name: "LEARNINGS.md", content: REFERENCE6 }
1479
+ };
1480
+
1418
1481
  // ../shared/src/default-skills/replicas-agent/abilities/linear.ts
1419
- var SECTION6 = `### Linear
1482
+ var SECTION7 = `### Linear
1420
1483
  Fetch issues, update state, add comments, and search via the Linear GraphQL API.
1421
1484
 
1422
1485
  **Reference:** \`references/LINEAR.md\`
@@ -1425,7 +1488,7 @@ Use this when:
1425
1488
  - You encounter a Linear issue link and need to understand the task
1426
1489
  - You need to update an issue's state (e.g. mark as done)
1427
1490
  - You need to comment on or search for Linear issues`;
1428
- var REFERENCE6 = `# Linear Integration
1491
+ var REFERENCE7 = `# Linear Integration
1429
1492
 
1430
1493
  This guide covers how to interact with Linear from within your Replicas workspace.
1431
1494
 
@@ -1514,12 +1577,12 @@ var LINEAR_ABILITY = {
1514
1577
  label: "Linear",
1515
1578
  description: "Fetch issues, post comments, update states via the Linear GraphQL API.",
1516
1579
  bullet: "- Interacting with Linear (fetching issues, updating state, commenting, etc.)",
1517
- section: SECTION6,
1518
- referenceFile: { name: "LINEAR.md", content: REFERENCE6 }
1580
+ section: SECTION7,
1581
+ referenceFile: { name: "LINEAR.md", content: REFERENCE7 }
1519
1582
  };
1520
1583
 
1521
1584
  // ../shared/src/default-skills/replicas-agent/abilities/media.ts
1522
- var SECTION7 = `### Media
1585
+ var SECTION8 = `### Media
1523
1586
  Share screenshots, screen recordings, generated diagrams, and audio clips inline in the Replicas chat and natively embedded in external messages.
1524
1587
 
1525
1588
  **Reference:** \`references/MEDIA.md\`
@@ -1528,7 +1591,7 @@ Use this when:
1528
1591
  - You produce a screenshot, recording, generated image, or audio clip the user should see
1529
1592
  - You record video output (browser automation, screen capture) \u2014 including the recommended aspect ratio and FPS
1530
1593
  - You need to embed media in a Slack/Linear/GitHub message AND keep a referenceable copy in the Replicas dashboard`;
1531
- var REFERENCE7 = `# Media (Screenshots, Recordings, Audio)
1594
+ var REFERENCE8 = `# Media (Screenshots, Recordings, Audio)
1532
1595
 
1533
1596
  This guide covers how to share screenshots, screen recordings, generated diagrams, and audio clips \u2014 both inline in the Replicas chat and natively embedded in external surfaces (Slack, Linear, GitHub).
1534
1597
 
@@ -1780,12 +1843,12 @@ var MEDIA_ABILITY = {
1780
1843
  label: "Media",
1781
1844
  description: "Share screenshots, recordings, generated images, and audio clips.",
1782
1845
  bullet: "- Producing or showing the user any media \u2014 screenshots, screen recordings, generated images or diagrams, audio clips \u2014 including in your Replicas chat reply, PR descriptions/comments, and other external platforms",
1783
- section: SECTION7,
1784
- referenceFile: { name: "MEDIA.md", content: REFERENCE7 }
1846
+ section: SECTION8,
1847
+ referenceFile: { name: "MEDIA.md", content: REFERENCE8 }
1785
1848
  };
1786
1849
 
1787
1850
  // ../shared/src/default-skills/replicas-agent/abilities/previews.ts
1788
- var SECTION8 = `### Previews
1851
+ var SECTION9 = `### Previews
1789
1852
  Expose locally running services (web apps, APIs, databases) as public preview URLs so humans can interact with them directly.
1790
1853
 
1791
1854
  **Reference:** \`references/PREVIEWS.md\`
@@ -1794,7 +1857,7 @@ Use this when:
1794
1857
  - You need to start a service that a human should view or interact with
1795
1858
  - The task involves UI work that benefits from human review
1796
1859
  - You are verifying frontend/backend integrations visually`;
1797
- var REFERENCE8 = `# Preview URLs
1860
+ var REFERENCE9 = `# Preview URLs
1798
1861
 
1799
1862
  When you run services on ports \u2014 such as a web app, API server, or database \u2014 humans may want to interact with them directly. You can expose your locally running services as public preview URLs.
1800
1863
 
@@ -1876,12 +1939,12 @@ var PREVIEWS_ABILITY = {
1876
1939
  label: "Previews",
1877
1940
  description: "Expose locally running services on public preview URLs for humans.",
1878
1941
  bullet: "- Creating preview URLs for locally running services",
1879
- section: SECTION8,
1880
- referenceFile: { name: "PREVIEWS.md", content: REFERENCE8 }
1942
+ section: SECTION9,
1943
+ referenceFile: { name: "PREVIEWS.md", content: REFERENCE9 }
1881
1944
  };
1882
1945
 
1883
1946
  // ../shared/src/default-skills/replicas-agent/abilities/replicas.ts
1884
- var SECTION9 = `### Replicas (in-workspace CLI)
1947
+ var SECTION10 = `### Replicas (in-workspace CLI)
1885
1948
  Take action *with* Replicas itself \u2014 manage automations, environments (variables, files), repos, and \`replicas.json\` config \u2014 using the pre-installed, pre-authenticated \`replicas\` CLI.
1886
1949
 
1887
1950
  **Reference:** \`references/REPLICAS.md\`
@@ -1891,7 +1954,7 @@ Use this when:
1891
1954
  - The user asks you to manage environments, environment variables, or environment files
1892
1955
  - The user asks "what envs / repos / automations do I have?"
1893
1956
  - The user asks you to scaffold a \`replicas.json\` / \`replicas.yaml\` in a repo`;
1894
- var REFERENCE9 = `# Replicas (in-workspace CLI)
1957
+ var REFERENCE10 = `# Replicas (in-workspace CLI)
1895
1958
 
1896
1959
  This guide covers how to take action *with* Replicas itself from inside a Replicas workspace \u2014 managing automations, environments (and their variables/files), repos, previews, and the user's \`replicas.json\` config \u2014 using the pre-installed \`replicas\` CLI.
1897
1960
 
@@ -2093,13 +2156,13 @@ var REPLICAS_ABILITY = {
2093
2156
  description: "Teach the agent about Replicas itself \u2014 automations, environments, the in-workspace CLI.",
2094
2157
  // No bullet — help_instructions covers the `replicas` CLI surface in detail.
2095
2158
  bullet: "",
2096
- section: SECTION9,
2097
- referenceFile: { name: "REPLICAS.md", content: REFERENCE9 },
2159
+ section: SECTION10,
2160
+ referenceFile: { name: "REPLICAS.md", content: REFERENCE10 },
2098
2161
  locked: true
2099
2162
  };
2100
2163
 
2101
2164
  // ../shared/src/default-skills/replicas-agent/abilities/slack.ts
2102
- var SECTION10 = `### Slack
2165
+ var SECTION11 = `### Slack
2103
2166
  Send messages, read threads, search conversations, and upload files via the Slack Web API.
2104
2167
 
2105
2168
  **Reference:** \`references/SLACK.md\`
@@ -2109,7 +2172,7 @@ Use this when:
2109
2172
  - You need to read or fetch a Slack conversation
2110
2173
  - You encounter a Slack message link and need to retrieve its content
2111
2174
  - The task asks you to notify, update, or communicate via Slack`;
2112
- var REFERENCE10 = `# Slack Integration
2175
+ var REFERENCE11 = `# Slack Integration
2113
2176
 
2114
2177
  This guide covers how to interact with Slack from within your Replicas workspace.
2115
2178
 
@@ -2210,8 +2273,8 @@ var SLACK_ABILITY = {
2210
2273
  label: "Slack",
2211
2274
  description: "Send messages, read threads, search conversations, upload files.",
2212
2275
  bullet: "- Interacting with Slack (sending messages, reading threads, etc.)",
2213
- section: SECTION10,
2214
- referenceFile: { name: "SLACK.md", content: REFERENCE10 }
2276
+ section: SECTION11,
2277
+ referenceFile: { name: "SLACK.md", content: REFERENCE11 }
2215
2278
  };
2216
2279
 
2217
2280
  // ../shared/src/default-skills/replicas-agent/registry.ts
@@ -2222,6 +2285,7 @@ var REPLICAS_AGENT_ABILITY_REGISTRY = {
2222
2285
  github: GITHUB_ABILITY,
2223
2286
  gitlab: GITLAB_ABILITY,
2224
2287
  google: GOOGLE_ABILITY,
2288
+ learnings: LEARNINGS_ABILITY,
2225
2289
  linear: LINEAR_ABILITY,
2226
2290
  media: MEDIA_ABILITY,
2227
2291
  previews: PREVIEWS_ABILITY,
@@ -2970,6 +3034,7 @@ function parseAgentEventJsonl(content, options = {}) {
2970
3034
  function parseAgentEventJsonlWithCodexAspTranscript(content, options = {}) {
2971
3035
  const events = [];
2972
3036
  let transcript = null;
3037
+ const transcriptsByThreadId = /* @__PURE__ */ new Map();
2973
3038
  for (const event of parseAgentEventJsonl(content, options)) {
2974
3039
  if (event.type !== CODEX_ASP_TRANSCRIPT_UPDATED_EVENT_TYPE) {
2975
3040
  events.push(event);
@@ -2977,12 +3042,16 @@ function parseAgentEventJsonlWithCodexAspTranscript(content, options = {}) {
2977
3042
  }
2978
3043
  const delta = event.payload.transcriptDelta;
2979
3044
  if (isCodexAspTranscriptDelta(delta)) {
2980
- transcript = applyCodexAspTranscriptDelta(transcript, delta);
3045
+ const previous = transcriptsByThreadId.get(delta.threadId) ?? null;
3046
+ transcript = applyCodexAspTranscriptDelta(previous, delta);
2981
3047
  } else if (isCodexAspTranscript(event.payload.transcript)) {
2982
3048
  transcript = event.payload.transcript;
2983
3049
  }
3050
+ if (transcript) {
3051
+ transcriptsByThreadId.set(transcript.threadId, transcript);
3052
+ }
2984
3053
  }
2985
- return { events, transcript };
3054
+ return { events, transcript, transcriptsByThreadId };
2986
3055
  }
2987
3056
 
2988
3057
  // ../shared/src/display-message/parsers/codex-asp-parser.ts
@@ -4681,6 +4750,7 @@ var EnvironmentDetailsService = class {
4681
4750
  ]);
4682
4751
  details.engineVersion = E2B_TEMPLATE_NAME;
4683
4752
  details.supportsMidTurnSteering = true;
4753
+ details.supportsGoalUpdates = true;
4684
4754
  details.claudeAuthMethod = detectClaudeAuthMethod();
4685
4755
  details.codexAuthMethod = detectCodexAuthMethod();
4686
4756
  details.cursorAuthMethod = detectCursorAuthMethod();
@@ -5436,8 +5506,8 @@ async function registerDesktopPreview() {
5436
5506
  // src/services/chat/chat-service.ts
5437
5507
  import { existsSync as existsSync7 } from "fs";
5438
5508
  import { appendFile as appendFile3, copyFile, mkdir as mkdir13, readFile as readFile14, rename as rename2, rm } from "fs/promises";
5439
- import { homedir as homedir14 } from "os";
5440
- import { join as join19 } from "path";
5509
+ import { homedir as homedir15 } from "os";
5510
+ import { join as join21 } from "path";
5441
5511
  import { randomUUID as randomUUID5 } from "crypto";
5442
5512
 
5443
5513
  // src/managers/claude-manager.ts
@@ -6639,7 +6709,7 @@ var CodexHistoryFile = class {
6639
6709
  if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) {
6640
6710
  console.error("[CodexHistoryFile] Failed to load history file:", error);
6641
6711
  }
6642
- return { events: [], transcript: null };
6712
+ return { events: [], transcript: null, transcriptsByThreadId: /* @__PURE__ */ new Map() };
6643
6713
  }
6644
6714
  }
6645
6715
  };
@@ -7744,6 +7814,10 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
7744
7814
  }
7745
7815
  };
7746
7816
 
7817
+ // src/managers/codex-asp/codex-asp-manager.ts
7818
+ import { readdir as readdir4 } from "fs/promises";
7819
+ import { join as join16 } from "path";
7820
+
7747
7821
  // src/managers/codex-asp/app-server-process.ts
7748
7822
  import { spawn as spawn3 } from "child_process";
7749
7823
  import { EventEmitter as EventEmitter2 } from "events";
@@ -7940,7 +8014,7 @@ var AspClient = class {
7940
8014
  // src/managers/codex-asp/app-server-process.ts
7941
8015
  var DEFAULT_CODEX_BINARY = "codex";
7942
8016
  var DEFAULT_CODEX_ARGS = ["app-server", "--listen", "stdio://"];
7943
- var ENGINE_PACKAGE_VERSION = "0.1.400";
8017
+ var ENGINE_PACKAGE_VERSION = "0.1.403";
7944
8018
  var INITIALIZE_METHOD = "initialize";
7945
8019
  var INITIALIZED_NOTIFICATION = "initialized";
7946
8020
  var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
@@ -8374,6 +8448,7 @@ function itemToTranscriptItem(item, timestamp, status) {
8374
8448
  description: item.receiverThreadIds.length === 1 ? "Codex subagent" : `Codex subagents (${item.receiverThreadIds.length})`,
8375
8449
  prompt: item.prompt ?? "",
8376
8450
  subagentType: "codex",
8451
+ receiverThreadIds: item.receiverThreadIds,
8377
8452
  ...item.model ? { model: item.model } : {},
8378
8453
  output: stringifyCollabAgentStates(item.agentsStates),
8379
8454
  timestamp,
@@ -8772,9 +8847,84 @@ var TranscriptUpdateCoalescer = class {
8772
8847
  }
8773
8848
  };
8774
8849
 
8850
+ // src/services/chat/history-paths.ts
8851
+ import { homedir as homedir12 } from "os";
8852
+ import { join as join15 } from "path";
8853
+ var ENGINE_DIR2 = join15(homedir12(), ".replicas", "engine");
8854
+ var CHATS_FILE = join15(ENGINE_DIR2, "chats.json");
8855
+ var CLAUDE_HISTORY_DIR = join15(ENGINE_DIR2, "claude-histories");
8856
+ var RELAY_HISTORY_DIR = join15(ENGINE_DIR2, "relay-histories");
8857
+ var CODEX_HISTORY_DIR = join15(ENGINE_DIR2, "codex-histories");
8858
+ var CURSOR_HISTORY_DIR = join15(ENGINE_DIR2, "cursor-histories");
8859
+ var OPENCODE_HISTORY_DIR = join15(ENGINE_DIR2, "opencode-histories");
8860
+ var HISTORY_DIR_BY_PROVIDER = {
8861
+ claude: CLAUDE_HISTORY_DIR,
8862
+ relay: RELAY_HISTORY_DIR,
8863
+ codex: CODEX_HISTORY_DIR,
8864
+ cursor: CURSOR_HISTORY_DIR,
8865
+ opencode: OPENCODE_HISTORY_DIR
8866
+ };
8867
+
8868
+ // src/services/chat/errors.ts
8869
+ var ChatNotFoundError = class extends Error {
8870
+ constructor(chatId) {
8871
+ super(`Chat not found: ${chatId}`);
8872
+ this.name = "ChatNotFoundError";
8873
+ }
8874
+ };
8875
+ var CodexThreadNotFoundError = class extends Error {
8876
+ constructor(threadId) {
8877
+ super(`Codex thread history not found: ${threadId}`);
8878
+ this.name = "CodexThreadNotFoundError";
8879
+ }
8880
+ };
8881
+ var DefaultChatDeletionError = class extends Error {
8882
+ constructor() {
8883
+ super("Default chats cannot be deleted");
8884
+ this.name = "DefaultChatDeletionError";
8885
+ }
8886
+ };
8887
+ var ChatProcessingDeletionError = class extends Error {
8888
+ constructor() {
8889
+ super("Cannot delete a chat while it is processing");
8890
+ this.name = "ChatProcessingDeletionError";
8891
+ }
8892
+ };
8893
+ var DuplicateDefaultChatError = class extends Error {
8894
+ constructor(provider) {
8895
+ super(`Default chat already exists for provider: ${provider}`);
8896
+ this.name = "DuplicateDefaultChatError";
8897
+ }
8898
+ };
8899
+
8775
8900
  // src/managers/codex-asp/codex-asp-manager.ts
8776
8901
  var GOAL_TURN_CONTINUATION_GRACE_MS = 5e3;
8777
8902
  var CODEX_SLASH_COMMANDS_CACHE_MS = 3e4;
8903
+ async function readCodexAspThreadHistory(threadId) {
8904
+ let entries;
8905
+ try {
8906
+ entries = await readdir4(CODEX_HISTORY_DIR, { withFileTypes: true });
8907
+ } catch (error) {
8908
+ if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
8909
+ throw new CodexThreadNotFoundError(threadId);
8910
+ }
8911
+ throw error;
8912
+ }
8913
+ for (const entry of entries) {
8914
+ if (!entry.isFile() || !entry.name.endsWith(".jsonl")) continue;
8915
+ const history = await new CodexHistoryFile(join16(CODEX_HISTORY_DIR, entry.name)).load();
8916
+ const transcript = history.transcriptsByThreadId.get(threadId);
8917
+ if (transcript) {
8918
+ return {
8919
+ thread_id: threadId,
8920
+ events: [],
8921
+ codexAspTranscript: transcript,
8922
+ goal: null
8923
+ };
8924
+ }
8925
+ }
8926
+ throw new CodexThreadNotFoundError(threadId);
8927
+ }
8778
8928
  function skillToSlashCommand(skill) {
8779
8929
  if (!skill.enabled) return null;
8780
8930
  const description = skill.interface?.shortDescription ?? skill.shortDescription ?? skill.description;
@@ -8959,6 +9109,21 @@ var CodexAspManager = class extends CodingAgentManager {
8959
9109
  this.recordGoalChange(null, true);
8960
9110
  return null;
8961
9111
  }
9112
+ async updateGoal(request) {
9113
+ await this.initialized;
9114
+ if (!this.currentThreadId) return null;
9115
+ const host = await getCodexAspHost();
9116
+ const response = await host.client.request(
9117
+ THREAD_GOAL_SET_METHOD,
9118
+ {
9119
+ threadId: this.currentThreadId,
9120
+ ...request.objective !== void 0 ? { objective: request.objective } : {},
9121
+ ...request.status !== void 0 ? { status: request.status } : {}
9122
+ }
9123
+ );
9124
+ this.recordGoalChange(response.goal, true);
9125
+ return this.currentGoal;
9126
+ }
8962
9127
  recordUserMessageEvent(request, extraPayload = {}) {
8963
9128
  const images = imageContentToUserMessageImages(request.images);
8964
9129
  this.recordHistoryEvent("event_msg", {
@@ -8981,6 +9146,14 @@ var CodexAspManager = class extends CodingAgentManager {
8981
9146
  await this.executeGoalClearCommand(request, recordUserMessage);
8982
9147
  return;
8983
9148
  }
9149
+ if (goalCommand?.type === "pause" || goalCommand?.type === "resume") {
9150
+ await this.executeGoalStatusCommand(
9151
+ request,
9152
+ recordUserMessage,
9153
+ goalCommand.type === "pause" ? "paused" : "active"
9154
+ );
9155
+ return;
9156
+ }
8984
9157
  if (goalCommand?.type === "set") {
8985
9158
  await this.executeAspTurn(request, recordUserMessage, {
8986
9159
  runTurn: (host, threadId) => this.runGoalTurn(host, threadId, request, goalCommand.objective),
@@ -9013,16 +9186,28 @@ var CodexAspManager = class extends CodingAgentManager {
9013
9186
  }
9014
9187
  }
9015
9188
  async executeGoalClearCommand(request, recordUserMessage) {
9016
- const host = await getCodexAspHost();
9017
- const developerInstructions = this.buildCombinedInstructions(request.customInstructions);
9018
- recordUserMessage({ command: "goal" });
9019
- const threadId = await this.ensureThread(host, request, developerInstructions);
9189
+ const { host, threadId } = await this.prepareGoalCommand(request, recordUserMessage);
9020
9190
  await host.client.request(
9021
9191
  THREAD_GOAL_CLEAR_METHOD,
9022
9192
  { threadId }
9023
9193
  );
9024
9194
  this.recordGoalChange(null, true);
9025
9195
  }
9196
+ async executeGoalStatusCommand(request, recordUserMessage, status) {
9197
+ const { host, threadId } = await this.prepareGoalCommand(request, recordUserMessage);
9198
+ const response = await host.client.request(
9199
+ THREAD_GOAL_SET_METHOD,
9200
+ { threadId, status }
9201
+ );
9202
+ this.recordGoalChange(response.goal, true);
9203
+ }
9204
+ async prepareGoalCommand(request, recordUserMessage) {
9205
+ const host = await getCodexAspHost();
9206
+ const developerInstructions = this.buildCombinedInstructions(request.customInstructions);
9207
+ recordUserMessage({ command: "goal" });
9208
+ const threadId = await this.ensureThread(host, request, developerInstructions);
9209
+ return { host, threadId };
9210
+ }
9026
9211
  async executeAspTurn(request, recordUserMessage, options = {}) {
9027
9212
  const host = await getCodexAspHost();
9028
9213
  if (this.quotaStatus.blocked && this.quotaStatus.latestSnapshot) {
@@ -9920,8 +10105,8 @@ var CodexAspManager = class extends CodingAgentManager {
9920
10105
  };
9921
10106
 
9922
10107
  // src/managers/cursor-manager.ts
9923
- import { mkdir as mkdir11, readFile as readFile10, readdir as readdir4 } from "fs/promises";
9924
- import { basename, dirname as dirname5, extname, join as join15 } from "path";
10108
+ import { mkdir as mkdir11, readFile as readFile10, readdir as readdir5 } from "fs/promises";
10109
+ import { basename, dirname as dirname5, extname, join as join17 } from "path";
9925
10110
  import { parse as parseYaml2 } from "yaml";
9926
10111
  import { Agent as CursorAgent } from "@cursor/sdk";
9927
10112
  var CURSOR_SLASH_COMMANDS_CACHE_MS = 3e4;
@@ -9948,7 +10133,7 @@ function extractCursorCommandDescription(content) {
9948
10133
  async function listCursorCommandsInDirectory(directory) {
9949
10134
  let entries;
9950
10135
  try {
9951
- entries = await readdir4(directory, { withFileTypes: true });
10136
+ entries = await readdir5(directory, { withFileTypes: true });
9952
10137
  } catch (error) {
9953
10138
  if (isRecord4(error) && error.code === "ENOENT") return [];
9954
10139
  console.warn("[CursorManager] Failed to read slash command directory:", error);
@@ -9958,7 +10143,7 @@ async function listCursorCommandsInDirectory(directory) {
9958
10143
  const name = basename(entry.name, ".md");
9959
10144
  let description;
9960
10145
  try {
9961
- description = extractCursorCommandDescription(await readFile10(join15(directory, entry.name), "utf8"));
10146
+ description = extractCursorCommandDescription(await readFile10(join17(directory, entry.name), "utf8"));
9962
10147
  } catch (error) {
9963
10148
  console.warn("[CursorManager] Failed to read slash command file:", error);
9964
10149
  }
@@ -9976,7 +10161,7 @@ var CursorManager = class extends CodingAgentManager {
9976
10161
  slashCommandsRequest = null;
9977
10162
  constructor(options) {
9978
10163
  super(options);
9979
- this.historyFilePath = options.historyFilePath ?? join15(ENGINE_ENV.HOME_DIR, ".replicas", "cursor", "history.jsonl");
10164
+ this.historyFilePath = options.historyFilePath ?? join17(ENGINE_ENV.HOME_DIR, ".replicas", "cursor", "history.jsonl");
9980
10165
  this.historyFile = new CodexHistoryFile(this.historyFilePath);
9981
10166
  this.initializeManager(this.processMessageInternal.bind(this));
9982
10167
  }
@@ -10004,8 +10189,8 @@ var CursorManager = class extends CodingAgentManager {
10004
10189
  this.slashCommandsRequest ??= (async () => {
10005
10190
  try {
10006
10191
  const commands = mergeSlashCommands(
10007
- await listCursorCommandsInDirectory(join15(this.workingDirectory, ".cursor", "commands")),
10008
- await listCursorCommandsInDirectory(join15(ENGINE_ENV.HOME_DIR, ".cursor", "commands"))
10192
+ await listCursorCommandsInDirectory(join17(this.workingDirectory, ".cursor", "commands")),
10193
+ await listCursorCommandsInDirectory(join17(ENGINE_ENV.HOME_DIR, ".cursor", "commands"))
10009
10194
  );
10010
10195
  this.slashCommandsCache = { commands, expiresAt: Date.now() + CURSOR_SLASH_COMMANDS_CACHE_MS };
10011
10196
  return commands;
@@ -10155,7 +10340,7 @@ var CursorManager = class extends CodingAgentManager {
10155
10340
 
10156
10341
  // src/managers/opencode-manager.ts
10157
10342
  import { mkdir as mkdir12, readFile as readFile11 } from "fs/promises";
10158
- import { delimiter, dirname as dirname6, join as join16 } from "path";
10343
+ import { delimiter, dirname as dirname6, join as join18 } from "path";
10159
10344
  import { randomBytes as randomBytes2 } from "crypto";
10160
10345
  import { fileURLToPath } from "url";
10161
10346
  import { Agent } from "undici";
@@ -10164,7 +10349,7 @@ import {
10164
10349
  createOpencodeServer
10165
10350
  } from "@opencode-ai/sdk/v2";
10166
10351
  var OPENCODE_SHIM_DIR = dirname6(fileURLToPath(new URL("../../scripts/opencode", import.meta.url)));
10167
- var OPENCODE_CONFIG_PATH = join16(ENGINE_ENV.HOME_DIR, ".config", "opencode", "opencode.json");
10352
+ var OPENCODE_CONFIG_PATH = join18(ENGINE_ENV.HOME_DIR, ".config", "opencode", "opencode.json");
10168
10353
  var OPENCODE_FETCH_DISPATCHER = new Agent({ headersTimeout: 0, bodyTimeout: 0 });
10169
10354
  var OPENCODE_SERVER_STARTUP_TIMEOUT_MS = 3e4;
10170
10355
  var OPENCODE_WORKSPACE_PERMISSION = "allow";
@@ -10319,7 +10504,7 @@ var OpencodeManager = class extends CodingAgentManager {
10319
10504
  constructor(options) {
10320
10505
  super(options);
10321
10506
  this.sessionId = options.initialSessionId;
10322
- this.historyFilePath = options.historyFilePath ?? join16(ENGINE_ENV.HOME_DIR, ".replicas", "opencode", "history.jsonl");
10507
+ this.historyFilePath = options.historyFilePath ?? join18(ENGINE_ENV.HOME_DIR, ".replicas", "opencode", "history.jsonl");
10323
10508
  this.historyFile = new CodexHistoryFile(this.historyFilePath);
10324
10509
  this.initializeManager(this.processMessageInternal.bind(this));
10325
10510
  }
@@ -11257,12 +11442,12 @@ var KeepAliveService = class _KeepAliveService {
11257
11442
  var keepAliveService = new KeepAliveService();
11258
11443
 
11259
11444
  // src/services/canvas-service.ts
11260
- import { readdir as readdir5, readFile as readFile12, stat as stat3 } from "fs/promises";
11261
- import { homedir as homedir12 } from "os";
11262
- import { join as join17 } from "path";
11445
+ import { readdir as readdir6, readFile as readFile12, stat as stat3 } from "fs/promises";
11446
+ import { homedir as homedir13 } from "os";
11447
+ import { join as join19 } from "path";
11263
11448
  var CANVAS_DIRECTORIES = [
11264
- join17(homedir12(), ".claude", "plans"),
11265
- join17(homedir12(), ".replicas", "canvas")
11449
+ join19(homedir13(), ".claude", "plans"),
11450
+ join19(homedir13(), ".replicas", "canvas")
11266
11451
  ];
11267
11452
  var CanvasService = class {
11268
11453
  async listItems() {
@@ -11270,7 +11455,7 @@ var CanvasService = class {
11270
11455
  for (const directory of CANVAS_DIRECTORIES) {
11271
11456
  let entries;
11272
11457
  try {
11273
- entries = await readdir5(directory, { withFileTypes: true });
11458
+ entries = await readdir6(directory, { withFileTypes: true });
11274
11459
  } catch {
11275
11460
  continue;
11276
11461
  }
@@ -11281,7 +11466,7 @@ var CanvasService = class {
11281
11466
  const { kind } = classifyCanvasFilename(entry.name);
11282
11467
  let sizeBytes = 0;
11283
11468
  try {
11284
- const s = await stat3(join17(directory, entry.name));
11469
+ const s = await stat3(join19(directory, entry.name));
11285
11470
  sizeBytes = s.size;
11286
11471
  } catch {
11287
11472
  continue;
@@ -11296,7 +11481,7 @@ var CanvasService = class {
11296
11481
  if (!safe) return null;
11297
11482
  const { kind, mimeType } = classifyCanvasFilename(safe);
11298
11483
  for (const directory of CANVAS_DIRECTORIES) {
11299
- const filePath = join17(directory, safe);
11484
+ const filePath = join19(directory, safe);
11300
11485
  let sizeBytes = 0;
11301
11486
  let updatedAt = "";
11302
11487
  try {
@@ -11432,14 +11617,14 @@ async function reconcileCanvasItems(filenames) {
11432
11617
  }
11433
11618
 
11434
11619
  // src/services/upload-chat-transcripts.ts
11435
- import { readdir as readdir6, readFile as readFile13 } from "fs/promises";
11436
- import { basename as basename2, join as join18 } from "path";
11437
- import { homedir as homedir13 } from "os";
11438
- var ENGINE_DIR2 = join18(homedir13(), ".replicas", "engine");
11620
+ import { readdir as readdir7, readFile as readFile13 } from "fs/promises";
11621
+ import { basename as basename2, join as join20 } from "path";
11622
+ import { homedir as homedir14 } from "os";
11623
+ var ENGINE_DIR3 = join20(homedir14(), ".replicas", "engine");
11439
11624
  var HISTORY_DIRS = [
11440
- join18(ENGINE_DIR2, "claude-histories"),
11441
- join18(ENGINE_DIR2, "relay-histories"),
11442
- join18(ENGINE_DIR2, "codex-histories")
11625
+ join20(ENGINE_DIR3, "claude-histories"),
11626
+ join20(ENGINE_DIR3, "relay-histories"),
11627
+ join20(ENGINE_DIR3, "codex-histories")
11443
11628
  ];
11444
11629
  async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map()) {
11445
11630
  let flushed = 0;
@@ -11448,7 +11633,7 @@ async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map()) {
11448
11633
  for (const dir of HISTORY_DIRS) {
11449
11634
  let entries;
11450
11635
  try {
11451
- entries = await readdir6(dir);
11636
+ entries = await readdir7(dir);
11452
11637
  } catch {
11453
11638
  continue;
11454
11639
  }
@@ -11456,7 +11641,7 @@ async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map()) {
11456
11641
  if (!entry.endsWith(".jsonl")) continue;
11457
11642
  const chatId = basename2(entry, ".jsonl");
11458
11643
  tasks.push(
11459
- uploadChatTranscript(chatId, join18(dir, entry), chatsById.get(chatId)).then(() => {
11644
+ uploadChatTranscript(chatId, join20(dir, entry), chatsById.get(chatId)).then(() => {
11460
11645
  flushed++;
11461
11646
  }).catch((err) => {
11462
11647
  failed++;
@@ -11499,50 +11684,10 @@ async function uploadChatTranscript(chatId, filePath, chat) {
11499
11684
  }
11500
11685
  }
11501
11686
 
11502
- // src/services/chat/errors.ts
11503
- var ChatNotFoundError = class extends Error {
11504
- constructor(chatId) {
11505
- super(`Chat not found: ${chatId}`);
11506
- this.name = "ChatNotFoundError";
11507
- }
11508
- };
11509
- var DefaultChatDeletionError = class extends Error {
11510
- constructor() {
11511
- super("Default chats cannot be deleted");
11512
- this.name = "DefaultChatDeletionError";
11513
- }
11514
- };
11515
- var ChatProcessingDeletionError = class extends Error {
11516
- constructor() {
11517
- super("Cannot delete a chat while it is processing");
11518
- this.name = "ChatProcessingDeletionError";
11519
- }
11520
- };
11521
- var DuplicateDefaultChatError = class extends Error {
11522
- constructor(provider) {
11523
- super(`Default chat already exists for provider: ${provider}`);
11524
- this.name = "DuplicateDefaultChatError";
11525
- }
11526
- };
11527
-
11528
11687
  // src/services/chat/chat-service.ts
11529
- var ENGINE_DIR3 = join19(homedir14(), ".replicas", "engine");
11530
- var CHATS_FILE = join19(ENGINE_DIR3, "chats.json");
11531
- var CLAUDE_HISTORY_DIR = join19(ENGINE_DIR3, "claude-histories");
11532
- var RELAY_HISTORY_DIR = join19(ENGINE_DIR3, "relay-histories");
11533
- var CODEX_HISTORY_DIR = join19(ENGINE_DIR3, "codex-histories");
11534
- var CURSOR_HISTORY_DIR = join19(ENGINE_DIR3, "cursor-histories");
11535
- var OPENCODE_HISTORY_DIR = join19(ENGINE_DIR3, "opencode-histories");
11536
- var HISTORY_DIR_BY_PROVIDER = {
11537
- claude: CLAUDE_HISTORY_DIR,
11538
- relay: RELAY_HISTORY_DIR,
11539
- codex: CODEX_HISTORY_DIR,
11540
- cursor: CURSOR_HISTORY_DIR,
11541
- opencode: OPENCODE_HISTORY_DIR
11542
- };
11543
- var CHAT_SENDERS_DIR = join19(ENGINE_DIR3, "chat-senders");
11544
- var CODEX_AUTH_PATH2 = join19(homedir14(), ".codex", "auth.json");
11545
- var OPENCODE_AUTH_PATH2 = join19(homedir14(), ".local", "share", "opencode", "auth.json");
11688
+ var CHAT_SENDERS_DIR = join21(ENGINE_DIR2, "chat-senders");
11689
+ var CODEX_AUTH_PATH2 = join21(homedir15(), ".codex", "auth.json");
11690
+ var OPENCODE_AUTH_PATH2 = join21(homedir15(), ".local", "share", "opencode", "auth.json");
11546
11691
  var CHATS_BACKUP_FILE = `${CHATS_FILE}.bak`;
11547
11692
  function isChatMessageSender(value) {
11548
11693
  if (!isRecord4(value)) return false;
@@ -11646,7 +11791,7 @@ var ChatService = class {
11646
11791
  persistInFlight = false;
11647
11792
  persistQueued = false;
11648
11793
  async initialize() {
11649
- await mkdir13(ENGINE_DIR3, { recursive: true });
11794
+ await mkdir13(ENGINE_DIR2, { recursive: true });
11650
11795
  await mkdir13(CLAUDE_HISTORY_DIR, { recursive: true });
11651
11796
  await mkdir13(RELAY_HISTORY_DIR, { recursive: true });
11652
11797
  await mkdir13(CODEX_HISTORY_DIR, { recursive: true });
@@ -11781,7 +11926,7 @@ var ChatService = class {
11781
11926
  };
11782
11927
  }
11783
11928
  senderFilePath(chatId) {
11784
- return join19(CHAT_SENDERS_DIR, `${chatId}.jsonl`);
11929
+ return join21(CHAT_SENDERS_DIR, `${chatId}.jsonl`);
11785
11930
  }
11786
11931
  async appendSender(chatId, sender) {
11787
11932
  try {
@@ -11863,6 +12008,16 @@ var ChatService = class {
11863
12008
  goal
11864
12009
  };
11865
12010
  }
12011
+ async updateGoal(chatId, request) {
12012
+ const chat = this.requireChat(chatId);
12013
+ const goal = chat.provider.updateGoal ? await chat.provider.updateGoal(request) : null;
12014
+ this.touch(chat);
12015
+ await this.publish({
12016
+ type: "chat.updated",
12017
+ payload: { chat: this.toSummary(chat) }
12018
+ });
12019
+ return { goal };
12020
+ }
11866
12021
  getChatQueue(chatId) {
11867
12022
  const chat = this.requireChat(chatId);
11868
12023
  return {
@@ -11941,7 +12096,7 @@ var ChatService = class {
11941
12096
  return descendants;
11942
12097
  }
11943
12098
  async deleteHistoryFile(persisted) {
11944
- await rm(join19(HISTORY_DIR_BY_PROVIDER[persisted.provider], `${persisted.id}.jsonl`), { force: true });
12099
+ await rm(join21(HISTORY_DIR_BY_PROVIDER[persisted.provider], `${persisted.id}.jsonl`), { force: true });
11945
12100
  await rm(this.senderFilePath(persisted.id), { force: true });
11946
12101
  }
11947
12102
  async getChatHistory(chatId) {
@@ -12012,7 +12167,7 @@ var ChatService = class {
12012
12167
  if (persisted.provider === "claude") {
12013
12168
  provider = new ClaudeManager({
12014
12169
  workingDirectory: this.workingDirectory,
12015
- historyFilePath: join19(CLAUDE_HISTORY_DIR, `${persisted.id}.jsonl`),
12170
+ historyFilePath: join21(CLAUDE_HISTORY_DIR, `${persisted.id}.jsonl`),
12016
12171
  initialSessionId: persisted.providerSessionId,
12017
12172
  onSaveSessionId: saveSession,
12018
12173
  onTurnComplete: onProviderTurnComplete,
@@ -12021,7 +12176,7 @@ var ChatService = class {
12021
12176
  } else if (persisted.provider === "relay") {
12022
12177
  provider = new RelayManager({
12023
12178
  workingDirectory: this.workingDirectory,
12024
- historyFilePath: join19(RELAY_HISTORY_DIR, `${persisted.id}.jsonl`),
12179
+ historyFilePath: join21(RELAY_HISTORY_DIR, `${persisted.id}.jsonl`),
12025
12180
  initialSessionId: persisted.providerSessionId,
12026
12181
  onSaveSessionId: saveSession,
12027
12182
  onTurnComplete: onProviderTurnComplete,
@@ -12034,7 +12189,7 @@ var ChatService = class {
12034
12189
  } else if (persisted.provider === "cursor") {
12035
12190
  provider = new CursorManager({
12036
12191
  workingDirectory: this.workingDirectory,
12037
- historyFilePath: join19(CURSOR_HISTORY_DIR, `${persisted.id}.jsonl`),
12192
+ historyFilePath: join21(CURSOR_HISTORY_DIR, `${persisted.id}.jsonl`),
12038
12193
  initialSessionId: persisted.providerSessionId,
12039
12194
  onSaveSessionId: saveSession,
12040
12195
  onTurnComplete: onProviderTurnComplete,
@@ -12043,7 +12198,7 @@ var ChatService = class {
12043
12198
  } else if (persisted.provider === "opencode") {
12044
12199
  provider = new OpencodeManager({
12045
12200
  workingDirectory: this.workingDirectory,
12046
- historyFilePath: join19(OPENCODE_HISTORY_DIR, `${persisted.id}.jsonl`),
12201
+ historyFilePath: join21(OPENCODE_HISTORY_DIR, `${persisted.id}.jsonl`),
12047
12202
  initialSessionId: persisted.providerSessionId,
12048
12203
  onSaveSessionId: saveSession,
12049
12204
  onTurnComplete: onProviderTurnComplete,
@@ -12052,7 +12207,7 @@ var ChatService = class {
12052
12207
  } else {
12053
12208
  provider = new CodexAspManager({
12054
12209
  workingDirectory: this.workingDirectory,
12055
- historyFilePath: join19(CODEX_HISTORY_DIR, `${persisted.id}.jsonl`),
12210
+ historyFilePath: join21(CODEX_HISTORY_DIR, `${persisted.id}.jsonl`),
12056
12211
  initialSessionId: persisted.providerSessionId,
12057
12212
  onSaveSessionId: saveSession,
12058
12213
  onTurnComplete: onProviderTurnComplete,
@@ -12191,7 +12346,7 @@ var ChatService = class {
12191
12346
  });
12192
12347
  uploadChatTranscript(
12193
12348
  chatId,
12194
- join19(HISTORY_DIR_BY_PROVIDER[chat.persisted.provider], `${chatId}.jsonl`),
12349
+ join21(HISTORY_DIR_BY_PROVIDER[chat.persisted.provider], `${chatId}.jsonl`),
12195
12350
  this.toSummary(chat)
12196
12351
  ).catch((err) => {
12197
12352
  console.error("[ChatService] Failed to upload chat transcript:", { chatId, err });
@@ -12309,7 +12464,7 @@ var ChatService = class {
12309
12464
  // src/services/repo-file-service.ts
12310
12465
  import { execFile as execFile2 } from "child_process";
12311
12466
  import { readFile as readFile15, realpath, stat as stat4 } from "fs/promises";
12312
- import { join as join20, resolve as resolve2, extname as extname2 } from "path";
12467
+ import { join as join22, resolve as resolve2, extname as extname2 } from "path";
12313
12468
  var CACHE_TTL_MS = 3e4;
12314
12469
  var SEARCH_TIMEOUT_MS = 15e3;
12315
12470
  var MAX_CONTENT_BYTES = 256 * 1024;
@@ -12469,7 +12624,7 @@ var RepoFileService = class {
12469
12624
  const repo = repos.find((r) => r.name === repoName);
12470
12625
  if (!repo) return null;
12471
12626
  try {
12472
- const fullPath = await realpath(resolve2(join20(repo.path, filePath)));
12627
+ const fullPath = await realpath(resolve2(join22(repo.path, filePath)));
12473
12628
  const repoRoot = await realpath(repo.path);
12474
12629
  const repoPrefix = repoRoot.endsWith("/") ? repoRoot : repoRoot + "/";
12475
12630
  if (!fullPath.startsWith(repoPrefix) && fullPath !== repoRoot) return null;
@@ -12576,21 +12731,21 @@ var RepoFileService = class {
12576
12731
  // src/v1-routes.ts
12577
12732
  import { Hono } from "hono";
12578
12733
  import { z as z2 } from "zod";
12579
- import { readdir as readdir8, stat as stat5, readFile as readFile18 } from "fs/promises";
12580
- import { join as join23, resolve as resolve3 } from "path";
12734
+ import { readdir as readdir9, stat as stat5, readFile as readFile18 } from "fs/promises";
12735
+ import { join as join25, resolve as resolve3 } from "path";
12581
12736
 
12582
12737
  // src/services/warm-hooks-service.ts
12583
12738
  import { spawn as spawn4 } from "child_process";
12584
12739
  import { readFile as readFile17 } from "fs/promises";
12585
12740
  import { existsSync as existsSync8 } from "fs";
12586
- import { join as join22 } from "path";
12741
+ import { join as join24 } from "path";
12587
12742
 
12588
12743
  // src/services/warm-hook-logs-service.ts
12589
- import { mkdir as mkdir14, readFile as readFile16, writeFile as writeFile6, readdir as readdir7, appendFile as appendFile4, unlink as unlink3 } from "fs/promises";
12590
- import { homedir as homedir15 } from "os";
12591
- import { join as join21 } from "path";
12592
- var LOGS_DIR2 = join21(homedir15(), ".replicas", "warm-hook-logs");
12593
- var CURRENT_RUN_LOG = join21(LOGS_DIR2, "current-run.log");
12744
+ import { mkdir as mkdir14, readFile as readFile16, writeFile as writeFile6, readdir as readdir8, appendFile as appendFile4, unlink as unlink3 } from "fs/promises";
12745
+ import { homedir as homedir16 } from "os";
12746
+ import { join as join23 } from "path";
12747
+ var LOGS_DIR2 = join23(homedir16(), ".replicas", "warm-hook-logs");
12748
+ var CURRENT_RUN_LOG = join23(LOGS_DIR2, "current-run.log");
12594
12749
  var GLOBAL_FILENAME = "global.json";
12595
12750
  function withPreview2(stored) {
12596
12751
  const preview = buildHookOutputPreview(stored.output);
@@ -12607,7 +12762,7 @@ var WarmHookLogsService = class {
12607
12762
  hookName: "organization",
12608
12763
  ...entry
12609
12764
  };
12610
- await writeFile6(join21(LOGS_DIR2, GLOBAL_FILENAME), `${JSON.stringify(log, null, 2)}
12765
+ await writeFile6(join23(LOGS_DIR2, GLOBAL_FILENAME), `${JSON.stringify(log, null, 2)}
12611
12766
  `, "utf-8");
12612
12767
  }
12613
12768
  async saveEnvironmentHookLog(entry) {
@@ -12617,7 +12772,7 @@ var WarmHookLogsService = class {
12617
12772
  hookName: "environment",
12618
12773
  ...entry
12619
12774
  };
12620
- await writeFile6(join21(LOGS_DIR2, ENVIRONMENT_HOOK_LOG_FILENAME), `${JSON.stringify(log, null, 2)}
12775
+ await writeFile6(join23(LOGS_DIR2, ENVIRONMENT_HOOK_LOG_FILENAME), `${JSON.stringify(log, null, 2)}
12621
12776
  `, "utf-8");
12622
12777
  }
12623
12778
  async saveRepoHookLog(repoName, entry) {
@@ -12627,13 +12782,13 @@ var WarmHookLogsService = class {
12627
12782
  hookName: repoName,
12628
12783
  ...entry
12629
12784
  };
12630
- await writeFile6(join21(LOGS_DIR2, repoHookLogFilename(repoName)), `${JSON.stringify(log, null, 2)}
12785
+ await writeFile6(join23(LOGS_DIR2, repoHookLogFilename(repoName)), `${JSON.stringify(log, null, 2)}
12631
12786
  `, "utf-8");
12632
12787
  }
12633
12788
  async getAllLogs() {
12634
12789
  let files;
12635
12790
  try {
12636
- files = await readdir7(LOGS_DIR2);
12791
+ files = await readdir8(LOGS_DIR2);
12637
12792
  } catch (err) {
12638
12793
  if (err.code === "ENOENT") {
12639
12794
  return [];
@@ -12646,7 +12801,7 @@ var WarmHookLogsService = class {
12646
12801
  continue;
12647
12802
  }
12648
12803
  try {
12649
- const raw = await readFile16(join21(LOGS_DIR2, file), "utf-8");
12804
+ const raw = await readFile16(join23(LOGS_DIR2, file), "utf-8");
12650
12805
  const stored = JSON.parse(raw);
12651
12806
  logs.push(withPreview2(stored));
12652
12807
  } catch {
@@ -12684,7 +12839,7 @@ var WarmHookLogsService = class {
12684
12839
  async getFullOutput(hookType, hookName) {
12685
12840
  const filename = hookType === "global" ? GLOBAL_FILENAME : hookType === "environment" ? ENVIRONMENT_HOOK_LOG_FILENAME : repoHookLogFilename(hookName);
12686
12841
  try {
12687
- const raw = await readFile16(join21(LOGS_DIR2, filename), "utf-8");
12842
+ const raw = await readFile16(join23(LOGS_DIR2, filename), "utf-8");
12688
12843
  const stored = JSON.parse(raw);
12689
12844
  if (stored.hookType !== hookType || stored.hookName !== hookName) {
12690
12845
  return null;
@@ -12703,7 +12858,7 @@ var warmHookLogsService = new WarmHookLogsService();
12703
12858
  // src/services/warm-hooks-service.ts
12704
12859
  async function readRepoWarmHook(repoPath) {
12705
12860
  for (const filename of REPLICAS_CONFIG_FILENAMES) {
12706
- const configPath = join22(repoPath, filename);
12861
+ const configPath = join24(repoPath, filename);
12707
12862
  if (!existsSync8(configPath)) {
12708
12863
  continue;
12709
12864
  }
@@ -13011,6 +13166,12 @@ var respondToolInputSchema = z2.object({
13011
13166
  requestId: z2.string().min(1),
13012
13167
  selectionId: z2.string().min(1)
13013
13168
  });
13169
+ var updateGoalSchema = z2.object({
13170
+ objective: z2.string().trim().min(1).max(MAX_CODEX_GOAL_OBJECTIVE_CHARS).optional(),
13171
+ status: z2.enum(["active", "paused"]).optional()
13172
+ }).refine((body) => body.objective !== void 0 || body.status !== void 0, {
13173
+ message: "Goal objective or status required"
13174
+ });
13014
13175
  function jsonError(message, details) {
13015
13176
  return { error: message, details };
13016
13177
  }
@@ -13187,6 +13348,21 @@ function createV1Routes(deps) {
13187
13348
  return c.json(jsonError("Failed to interrupt chat", error instanceof Error ? error.message : "Unknown error"), 404);
13188
13349
  }
13189
13350
  });
13351
+ app2.patch("/chats/:chatId/goal", async (c) => {
13352
+ try {
13353
+ const body = updateGoalSchema.parse(await c.req.json());
13354
+ const result = await deps.chatService.updateGoal(c.req.param("chatId"), body);
13355
+ return c.json(result);
13356
+ } catch (error) {
13357
+ if (error instanceof z2.ZodError) {
13358
+ return c.json(jsonError(error.issues[0]?.message || "Invalid goal update"), 400);
13359
+ }
13360
+ if (error instanceof ChatNotFoundError) {
13361
+ return c.json(jsonError("Failed to update goal", error.message), 404);
13362
+ }
13363
+ return c.json(jsonError("Failed to update goal", error instanceof Error ? error.message : "Unknown error"), 500);
13364
+ }
13365
+ });
13190
13366
  app2.post("/chats/:chatId/goal/clear", async (c) => {
13191
13367
  try {
13192
13368
  const result = await deps.chatService.clearGoal(c.req.param("chatId"));
@@ -13397,6 +13573,22 @@ function createV1Routes(deps) {
13397
13573
  );
13398
13574
  }
13399
13575
  });
13576
+ app2.get("/codex/threads/:threadId/history", async (c) => {
13577
+ try {
13578
+ return c.json(await readCodexAspThreadHistory(c.req.param("threadId")));
13579
+ } catch (error) {
13580
+ if (error instanceof CodexThreadNotFoundError) {
13581
+ return c.json(
13582
+ jsonError("Failed to load Codex thread history", error.message),
13583
+ 404
13584
+ );
13585
+ }
13586
+ return c.json(
13587
+ jsonError("Failed to load Codex thread history", error instanceof Error ? error.message : "Unknown error"),
13588
+ 500
13589
+ );
13590
+ }
13591
+ });
13400
13592
  app2.post("/environment/track", async (c) => {
13401
13593
  try {
13402
13594
  const body = await c.req.json();
@@ -13677,11 +13869,11 @@ function createV1Routes(deps) {
13677
13869
  });
13678
13870
  app2.get("/logs", async (c) => {
13679
13871
  try {
13680
- const files = await readdir8(LOG_DIR).catch(() => []);
13872
+ const files = await readdir9(LOG_DIR).catch(() => []);
13681
13873
  const logFiles = files.filter((f) => f.endsWith(".log"));
13682
13874
  const sessions = await Promise.all(
13683
13875
  logFiles.map(async (filename) => {
13684
- const filePath = join23(LOG_DIR, filename);
13876
+ const filePath = join25(LOG_DIR, filename);
13685
13877
  const fileStat = await stat5(filePath);
13686
13878
  const sessionId = filename.replace(/\.log$/, "");
13687
13879
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-engine",
3
- "version": "0.1.400",
3
+ "version": "0.1.403",
4
4
  "description": "Lightweight API server for Replicas workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",