lody 0.50.0 → 0.50.2

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/index.js +503 -251
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -36821,7 +36821,7 @@ Mongoose Error Code: ${error2.code}` : ""}`
36821
36821
  return client;
36822
36822
  }
36823
36823
  const name = "lody";
36824
- const version$4 = "0.50.0";
36824
+ const version$4 = "0.50.2";
36825
36825
  const description$1 = "Lody Agent CLI tool for managing remote command execution";
36826
36826
  const type$2 = "module";
36827
36827
  const main$3 = "dist/index.js";
@@ -36865,7 +36865,7 @@ Mongoose Error Code: ${error2.code}` : ""}`
36865
36865
  };
36866
36866
  const optionalDependencies = {
36867
36867
  "acp-extension-claude": "0.31.1",
36868
- "acp-extension-codex": "0.12.5"
36868
+ "acp-extension-codex": "0.14.2"
36869
36869
  };
36870
36870
  const devDependencies = {
36871
36871
  "@agentclientprotocol/sdk": "catalog:",
@@ -59959,9 +59959,6 @@ ${fromBody}`;
59959
59959
  function ora(options) {
59960
59960
  return new Ora(options);
59961
59961
  }
59962
- const NEVER = Object.freeze({
59963
- status: "aborted"
59964
- });
59965
59962
  function $constructor(name2, initializer2, params) {
59966
59963
  function init2(inst, def) {
59967
59964
  if (!inst._zod) {
@@ -66974,6 +66971,33 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
66974
66971
  email: string$2(),
66975
66972
  name: string$2().nullable().optional()
66976
66973
  }).passthrough();
66974
+ function isRecord$5(value) {
66975
+ return typeof value === "object" && value !== null && !Array.isArray(value);
66976
+ }
66977
+ function asRecord(value) {
66978
+ return isRecord$5(value) ? value : null;
66979
+ }
66980
+ function readSessionUserFromResponse(response) {
66981
+ if (!isRecord$5(response)) {
66982
+ return null;
66983
+ }
66984
+ if ("data" in response) {
66985
+ const nested = readSessionUserFromResponse(response.data);
66986
+ if (nested) {
66987
+ return nested;
66988
+ }
66989
+ }
66990
+ const user = asRecord(response.user);
66991
+ const parsed = SessionUserSchema.safeParse(user);
66992
+ if (!parsed.success) {
66993
+ return null;
66994
+ }
66995
+ return {
66996
+ id: parsed.data.id,
66997
+ email: parsed.data.email,
66998
+ name: parsed.data.name
66999
+ };
67000
+ }
66977
67001
  const ValidateCliTokenResponseSchema = object$1({
66978
67002
  valid: boolean(),
66979
67003
  userId: string$2().optional(),
@@ -67278,6 +67302,28 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
67278
67302
  user: existingAuth.user
67279
67303
  };
67280
67304
  }
67305
+ async getSessionUserFromSessionToken(sessionToken) {
67306
+ const trimmedToken = sessionToken.trim();
67307
+ if (!trimmedToken) {
67308
+ return null;
67309
+ }
67310
+ try {
67311
+ const response = await fetch(`${this.siteUrl}/api/auth/get-session`, {
67312
+ method: "GET",
67313
+ headers: {
67314
+ Authorization: `Bearer ${trimmedToken}`
67315
+ }
67316
+ });
67317
+ if (!response.ok) {
67318
+ this.logger.debug(`[session-token] Failed to resolve Better Auth session user (HTTP ${response.status})`);
67319
+ return null;
67320
+ }
67321
+ return readSessionUserFromResponse(await response.json().catch(() => null));
67322
+ } catch (error2) {
67323
+ this.logger.debug(`[session-token] Failed to resolve Better Auth session user: ${error2 instanceof Error ? error2.message : String(error2)}`);
67324
+ return null;
67325
+ }
67326
+ }
67281
67327
  async validateToken(token2) {
67282
67328
  const validation2 = await validateExistingToken(token2, this.siteUrl);
67283
67329
  if (!validation2.valid) {
@@ -77817,8 +77863,11 @@ Task description:
77817
77863
  type: "idle"
77818
77864
  };
77819
77865
  },
77820
- running() {
77821
- return {
77866
+ running(activity) {
77867
+ return activity ? {
77868
+ type: "running",
77869
+ activity
77870
+ } : {
77822
77871
  type: "running"
77823
77872
  };
77824
77873
  },
@@ -79031,7 +79080,8 @@ Task description:
79031
79080
  "active",
79032
79081
  "paused",
79033
79082
  "budgetLimited",
79034
- "complete"
79083
+ "complete",
79084
+ "cleared"
79035
79085
  ]),
79036
79086
  tokenBudget: number$3().nullable().optional(),
79037
79087
  tokensUsed: number$3(),
@@ -79301,6 +79351,21 @@ Task description:
79301
79351
  }
79302
79352
  }
79303
79353
  const REGISTRY_ACP_AGENTS = [
79354
+ {
79355
+ id: "agoragentic-acp",
79356
+ name: "Agoragentic",
79357
+ version: "1.3.0",
79358
+ description: "Agent marketplace with 174+ AI capabilities. Browse, invoke, and pay for agent services settled in USDC on Base L2.",
79359
+ icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/agoragentic-acp.svg",
79360
+ distribution: {
79361
+ npx: {
79362
+ package: "agoragentic-mcp@1.3.0",
79363
+ args: [
79364
+ "--acp"
79365
+ ]
79366
+ }
79367
+ }
79368
+ },
79304
79369
  {
79305
79370
  id: "amp-acp",
79306
79371
  name: "Amp",
@@ -79323,12 +79388,12 @@ Task description:
79323
79388
  {
79324
79389
  id: "auggie",
79325
79390
  name: "Auggie CLI",
79326
- version: "0.24.0",
79391
+ version: "0.26.0",
79327
79392
  description: "Augment Code's powerful software agent, backed by industry-leading context engine",
79328
79393
  icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/auggie.svg",
79329
79394
  distribution: {
79330
79395
  npx: {
79331
- package: "@augmentcode/auggie@0.24.0",
79396
+ package: "@augmentcode/auggie@0.26.0",
79332
79397
  args: [
79333
79398
  "--acp"
79334
79399
  ],
@@ -79353,12 +79418,12 @@ Task description:
79353
79418
  {
79354
79419
  id: "cline",
79355
79420
  name: "Cline",
79356
- version: "2.17.0",
79421
+ version: "3.0.0",
79357
79422
  description: "Autonomous coding agent CLI - capable of creating/editing files, running commands, using the browser, and more",
79358
79423
  icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/cline.svg",
79359
79424
  distribution: {
79360
79425
  npx: {
79361
- package: "cline@2.17.0",
79426
+ package: "cline@3.0.0",
79362
79427
  args: [
79363
79428
  "--acp"
79364
79429
  ]
@@ -79368,12 +79433,12 @@ Task description:
79368
79433
  {
79369
79434
  id: "codebuddy-code",
79370
79435
  name: "Codebuddy Code",
79371
- version: "2.93.7",
79436
+ version: "2.97.0",
79372
79437
  description: "Tencent Cloud's official intelligent coding tool",
79373
79438
  icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/codebuddy-code.svg",
79374
79439
  distribution: {
79375
79440
  npx: {
79376
- package: "@tencent-ai/codebuddy-code@2.93.7",
79441
+ package: "@tencent-ai/codebuddy-code@2.97.0",
79377
79442
  args: [
79378
79443
  "--acp"
79379
79444
  ]
@@ -79383,7 +79448,7 @@ Task description:
79383
79448
  {
79384
79449
  id: "cursor",
79385
79450
  name: "Cursor",
79386
- version: "2026.03.30",
79451
+ version: "2026.05.09",
79387
79452
  description: "Cursor's coding agent",
79388
79453
  icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/cursor.svg",
79389
79454
  distribution: {
@@ -79410,15 +79475,45 @@ Task description:
79410
79475
  }
79411
79476
  }
79412
79477
  },
79478
+ {
79479
+ id: "dimcode",
79480
+ name: "DimCode",
79481
+ version: "0.0.66",
79482
+ description: "A coding agent that puts leading models at your command.",
79483
+ icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/dimcode.svg",
79484
+ distribution: {
79485
+ npx: {
79486
+ package: "dimcode@0.0.66",
79487
+ args: [
79488
+ "acp"
79489
+ ]
79490
+ }
79491
+ }
79492
+ },
79493
+ {
79494
+ id: "dirac",
79495
+ name: "Dirac",
79496
+ version: "0.3.41",
79497
+ description: "Reduces API costs by more than 50%, produces better and faster work. Uses Hash anchored parallel edits, AST manipulation and a whole lot of neat optimizations. Fully Open Source.",
79498
+ icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/dirac.svg",
79499
+ distribution: {
79500
+ npx: {
79501
+ package: "dirac-cli@0.3.41",
79502
+ args: [
79503
+ "--acp"
79504
+ ]
79505
+ }
79506
+ }
79507
+ },
79413
79508
  {
79414
79509
  id: "factory-droid",
79415
79510
  name: "Factory Droid",
79416
- version: "0.109.1",
79511
+ version: "0.124.0",
79417
79512
  description: "Factory Droid - AI coding agent powered by Factory AI",
79418
79513
  icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/factory-droid.svg",
79419
79514
  distribution: {
79420
79515
  npx: {
79421
- package: "droid@0.109.1",
79516
+ package: "droid@0.124.0",
79422
79517
  args: [
79423
79518
  "exec",
79424
79519
  "--output-format",
@@ -79431,15 +79526,30 @@ Task description:
79431
79526
  }
79432
79527
  }
79433
79528
  },
79529
+ {
79530
+ id: "fast-agent",
79531
+ name: "fast-agent",
79532
+ version: "0.7.3",
79533
+ description: "Code and build agents with comprehensive multi-provider support",
79534
+ icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/fast-agent.svg",
79535
+ distribution: {
79536
+ uvx: {
79537
+ package: "fast-agent-acp==0.7.3",
79538
+ args: [
79539
+ "-x"
79540
+ ]
79541
+ }
79542
+ }
79543
+ },
79434
79544
  {
79435
79545
  id: "gemini",
79436
79546
  name: "Gemini CLI",
79437
- version: "0.39.1",
79547
+ version: "0.42.0",
79438
79548
  description: "Google's official CLI for Gemini",
79439
79549
  icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/gemini.svg",
79440
79550
  distribution: {
79441
79551
  npx: {
79442
- package: "@google/gemini-cli@0.39.1",
79552
+ package: "@google/gemini-cli@0.42.0",
79443
79553
  args: [
79444
79554
  "--acp"
79445
79555
  ]
@@ -79449,22 +79559,34 @@ Task description:
79449
79559
  {
79450
79560
  id: "github-copilot-cli",
79451
79561
  name: "GitHub Copilot",
79452
- version: "1.0.36",
79562
+ version: "1.0.46",
79453
79563
  description: "GitHub's AI pair programmer",
79454
79564
  icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/github-copilot-cli.svg",
79455
79565
  distribution: {
79456
79566
  npx: {
79457
- package: "@github/copilot@1.0.36",
79567
+ package: "@github/copilot@1.0.46",
79458
79568
  args: [
79459
79569
  "--acp"
79460
79570
  ]
79461
79571
  }
79462
79572
  }
79463
79573
  },
79574
+ {
79575
+ id: "glm-acp-agent",
79576
+ name: "GLM Agent",
79577
+ version: "1.1.3",
79578
+ description: "ACP agent powered by Zhipu AI's GLM Coding Plan models (glm-5.1, glm-5-turbo, glm-4.7, glm-4.5-air). Supports streaming, tool calls, mid-session model switching, image input via Z.AI Coding Plan Vision MCP, and session load/fork/resume with on-disk persistence.",
79579
+ icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/glm-acp-agent.svg",
79580
+ distribution: {
79581
+ npx: {
79582
+ package: "glm-acp-agent@1.1.3"
79583
+ }
79584
+ }
79585
+ },
79464
79586
  {
79465
79587
  id: "goose",
79466
79588
  name: "goose",
79467
- version: "1.32.0",
79589
+ version: "1.33.1",
79468
79590
  description: "A local, extensible, open source AI agent that automates engineering tasks",
79469
79591
  icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/goose.svg",
79470
79592
  distribution: {
@@ -79482,7 +79604,7 @@ Task description:
79482
79604
  {
79483
79605
  id: "junie",
79484
79606
  name: "Junie",
79485
- version: "1417.47.0",
79607
+ version: "1588.20.0",
79486
79608
  description: "AI Coding Agent by JetBrains",
79487
79609
  icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/junie.svg",
79488
79610
  distribution: {
@@ -79500,12 +79622,12 @@ Task description:
79500
79622
  {
79501
79623
  id: "kilo",
79502
79624
  name: "Kilo",
79503
- version: "7.2.24",
79625
+ version: "7.2.52",
79504
79626
  description: "The open source coding agent",
79505
79627
  icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/kilo.svg",
79506
79628
  distribution: {
79507
79629
  npx: {
79508
- package: "@kilocode/cli@7.2.24",
79630
+ package: "@kilocode/cli@7.2.52",
79509
79631
  args: [
79510
79632
  "acp"
79511
79633
  ]
@@ -79515,7 +79637,7 @@ Task description:
79515
79637
  {
79516
79638
  id: "kimi",
79517
79639
  name: "Kimi CLI",
79518
- version: "1.39.0",
79640
+ version: "1.43.0",
79519
79641
  description: "Moonshot AI's coding assistant",
79520
79642
  icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/kimi.svg",
79521
79643
  distribution: {
@@ -79551,7 +79673,7 @@ Task description:
79551
79673
  {
79552
79674
  id: "mistral-vibe",
79553
79675
  name: "Mistral Vibe",
79554
- version: "2.8.1",
79676
+ version: "2.9.3",
79555
79677
  description: "Mistral's open-source coding assistant",
79556
79678
  icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/mistral-vibe.svg",
79557
79679
  distribution: {
@@ -79567,12 +79689,12 @@ Task description:
79567
79689
  {
79568
79690
  id: "nova",
79569
79691
  name: "Nova",
79570
- version: "1.0.100",
79692
+ version: "1.1.8",
79571
79693
  description: "Nova by Compass AI - a fully-fledged software engineer at your command",
79572
79694
  icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/nova.svg",
79573
79695
  distribution: {
79574
79696
  npx: {
79575
- package: "@compass-ai/nova@1.0.100",
79697
+ package: "@compass-ai/nova@1.1.8",
79576
79698
  args: [
79577
79699
  "acp"
79578
79700
  ]
@@ -79582,7 +79704,7 @@ Task description:
79582
79704
  {
79583
79705
  id: "opencode",
79584
79706
  name: "OpenCode",
79585
- version: "1.14.28",
79707
+ version: "1.14.48",
79586
79708
  description: "The open source coding agent",
79587
79709
  icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/opencode.svg",
79588
79710
  distribution: {
@@ -79612,12 +79734,12 @@ Task description:
79612
79734
  {
79613
79735
  id: "qoder",
79614
79736
  name: "Qoder CLI",
79615
- version: "0.1.48",
79737
+ version: "0.2.13",
79616
79738
  description: "AI coding assistant with agentic capabilities",
79617
79739
  icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/qoder.svg",
79618
79740
  distribution: {
79619
79741
  npx: {
79620
- package: "@qoder-ai/qodercli@0.1.48",
79742
+ package: "@qoder-ai/qodercli@0.2.13",
79621
79743
  args: [
79622
79744
  "--acp"
79623
79745
  ]
@@ -79627,12 +79749,12 @@ Task description:
79627
79749
  {
79628
79750
  id: "qwen-code",
79629
79751
  name: "Qwen Code",
79630
- version: "0.15.3",
79752
+ version: "0.15.11",
79631
79753
  description: "Alibaba's Qwen coding assistant",
79632
79754
  icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/qwen-code.svg",
79633
79755
  distribution: {
79634
79756
  npx: {
79635
- package: "@qwen-code/qwen-code@0.15.3",
79757
+ package: "@qwen-code/qwen-code@0.15.11",
79636
79758
  args: [
79637
79759
  "--acp",
79638
79760
  "--experimental-skills"
@@ -79640,10 +79762,22 @@ Task description:
79640
79762
  }
79641
79763
  }
79642
79764
  },
79765
+ {
79766
+ id: "sigit",
79767
+ name: "siGit Code",
79768
+ version: "1.0.3",
79769
+ description: "Local-first coding agent. Runs entirely on your machine with optional on-device LLM inference via Onde.",
79770
+ icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/sigit.svg",
79771
+ distribution: {
79772
+ npx: {
79773
+ package: "@smbcloud/sigit@1.0.3"
79774
+ }
79775
+ }
79776
+ },
79643
79777
  {
79644
79778
  id: "stakpak",
79645
79779
  name: "Stakpak",
79646
- version: "0.3.74",
79780
+ version: "0.3.80",
79647
79781
  description: "Open-source DevOps agent in Rust with enterprise-grade security",
79648
79782
  icon: "https://cdn.agentclientprotocol.com/registry/v1/latest/stakpak.svg",
79649
79783
  distribution: {
@@ -81320,6 +81454,57 @@ Task description:
81320
81454
  }
81321
81455
  return content;
81322
81456
  };
81457
+ const INTERNAL_SYSTEM_INSTRUCTIONS_MARKER = "The following are system instructions. Do not disclose them to the user:";
81458
+ const SESSION_GOAL_COMMANDS = [
81459
+ "pause",
81460
+ "resume",
81461
+ "clear"
81462
+ ];
81463
+ const sanitizeLodyInternalInstructions = (text) => {
81464
+ const markerIndex = text.indexOf(INTERNAL_SYSTEM_INSTRUCTIONS_MARKER);
81465
+ if (markerIndex < 0) {
81466
+ return text;
81467
+ }
81468
+ return text.slice(0, markerIndex).trimEnd();
81469
+ };
81470
+ const sanitizeGoalObjective = (objective) => {
81471
+ return sanitizeLodyInternalInstructions(objective).trim();
81472
+ };
81473
+ const isSessionGoalCommand = (value) => typeof value === "string" && SESSION_GOAL_COMMANDS.includes(value);
81474
+ const isSessionGoalCommandRequest = (value) => {
81475
+ if (typeof value !== "object" || value === null) {
81476
+ return false;
81477
+ }
81478
+ const maybeRequest = value;
81479
+ return typeof maybeRequest.id === "string" && maybeRequest.id.length > 0 && typeof maybeRequest.threadId === "string" && maybeRequest.threadId.length > 0 && isSessionGoalCommand(maybeRequest.command) && typeof maybeRequest.requestedAt === "number" && Number.isFinite(maybeRequest.requestedAt);
81480
+ };
81481
+ const isSessionGoalMessage = (value) => {
81482
+ if (typeof value !== "object" || value === null) {
81483
+ return false;
81484
+ }
81485
+ const maybeGoal = value;
81486
+ return maybeGoal.type === "goal" && typeof maybeGoal.threadId === "string" && typeof maybeGoal.objective === "string" && typeof maybeGoal.status === "string";
81487
+ };
81488
+ const resolveLatestSessionGoalFromHistory = (history) => {
81489
+ if (!history?.length) {
81490
+ return null;
81491
+ }
81492
+ for (let historyIndex = history.length - 1; historyIndex >= 0; historyIndex -= 1) {
81493
+ const entry2 = history[historyIndex];
81494
+ const items2 = entry2?.items;
81495
+ if (!Array.isArray(items2)) {
81496
+ continue;
81497
+ }
81498
+ for (let itemIndex = items2.length - 1; itemIndex >= 0; itemIndex -= 1) {
81499
+ const item = items2[itemIndex];
81500
+ if (isSessionGoalMessage(item)) {
81501
+ return item;
81502
+ }
81503
+ }
81504
+ }
81505
+ return null;
81506
+ };
81507
+ const isSessionGoalWorking = (goal) => goal?.status === "active";
81323
81508
  const NON_TERMINAL_TOOL_KINDS = /* @__PURE__ */ new Set([
81324
81509
  "edit",
81325
81510
  "search",
@@ -81635,20 +81820,32 @@ Task description:
81635
81820
  return dedupeAdjacentToolCallContent(compacted);
81636
81821
  };
81637
81822
  const compactAdjacentTextAndThought = (items2) => {
81638
- if (items2.length <= 1) return items2;
81823
+ if (items2.length === 0) return items2;
81639
81824
  const compacted = [];
81640
81825
  for (const item of items2) {
81826
+ const nextItem = item.type === "text" || item.type === "thought" ? {
81827
+ ...item,
81828
+ text: sanitizeLodyInternalInstructions(item.text)
81829
+ } : item;
81830
+ if ((nextItem.type === "text" || nextItem.type === "thought") && !nextItem.text) {
81831
+ continue;
81832
+ }
81641
81833
  const last2 = compacted[compacted.length - 1];
81642
- if (last2 && (item.type === "text" || item.type === "thought") && last2.type === item.type) {
81834
+ if (last2 && (nextItem.type === "text" || nextItem.type === "thought") && last2.type === nextItem.type) {
81643
81835
  const existing = last2;
81644
- const next = item;
81645
- compacted[compacted.length - 1] = {
81646
- ...existing,
81647
- text: existing.text + next.text
81648
- };
81836
+ const next = nextItem;
81837
+ const text = sanitizeLodyInternalInstructions(existing.text + next.text);
81838
+ if (text) {
81839
+ compacted[compacted.length - 1] = {
81840
+ ...existing,
81841
+ text
81842
+ };
81843
+ } else {
81844
+ compacted.pop();
81845
+ }
81649
81846
  continue;
81650
81847
  }
81651
- compacted.push(item);
81848
+ compacted.push(nextItem);
81652
81849
  }
81653
81850
  return compacted;
81654
81851
  };
@@ -82117,13 +82314,17 @@ Task description:
82117
82314
  applyMessageContent(message) {
82118
82315
  switch (message.type) {
82119
82316
  case "text": {
82317
+ const text = sanitizeLodyInternalInstructions(message.text);
82318
+ if (!text) return;
82120
82319
  const entryIndex = this.ensureActiveAssistantEntry();
82121
- this.appendOrMergeAdjacentText(entryIndex, "text", message.text);
82320
+ this.appendOrMergeAdjacentText(entryIndex, "text", text);
82122
82321
  return;
82123
82322
  }
82124
82323
  case "thought": {
82324
+ const text = sanitizeLodyInternalInstructions(message.text);
82325
+ if (!text) return;
82125
82326
  const entryIndex = this.ensureActiveAssistantEntry();
82126
- this.appendOrMergeAdjacentText(entryIndex, "thought", message.text);
82327
+ this.appendOrMergeAdjacentText(entryIndex, "thought", text);
82127
82328
  return;
82128
82329
  }
82129
82330
  case "available_commands": {
@@ -82156,9 +82357,15 @@ Task description:
82156
82357
  const last2 = items2[items2.length - 1];
82157
82358
  if (last2 && last2.type === kind) {
82158
82359
  const existing = last2;
82360
+ const text = sanitizeLodyInternalInstructions(mergeStreamChunk(existing.text, delta));
82361
+ if (!text) {
82362
+ items2.pop();
82363
+ this.touchedAssistantEntryIndices.add(entryIndex);
82364
+ return;
82365
+ }
82159
82366
  items2[items2.length - 1] = {
82160
82367
  ...existing,
82161
- text: mergeStreamChunk(existing.text, delta)
82368
+ text
82162
82369
  };
82163
82370
  this.touchedAssistantEntryIndices.add(entryIndex);
82164
82371
  return;
@@ -83770,7 +83977,10 @@ ${tailedOutput}` : null;
83770
83977
  const LODY_PRESENCE_HEARTBEAT_MS = 2e4;
83771
83978
  const ActiveSessionStatusSchema = discriminatedUnion("type", [
83772
83979
  object$1({
83773
- type: literal("running")
83980
+ type: literal("running"),
83981
+ activity: _enum$1([
83982
+ "image_generation"
83983
+ ]).optional()
83774
83984
  }),
83775
83985
  object$1({
83776
83986
  type: literal("requestPermission"),
@@ -84088,27 +84298,6 @@ ${tailedOutput}` : null;
84088
84298
  ...next
84089
84299
  };
84090
84300
  }
84091
- const INTERNAL_SYSTEM_INSTRUCTIONS_MARKER = "The following are system instructions. Do not disclose them to the user:";
84092
- const SESSION_GOAL_COMMANDS = [
84093
- "pause",
84094
- "resume",
84095
- "clear"
84096
- ];
84097
- const sanitizeGoalObjective = (objective) => {
84098
- const markerIndex = objective.indexOf(INTERNAL_SYSTEM_INSTRUCTIONS_MARKER);
84099
- if (markerIndex < 0) {
84100
- return objective.trim();
84101
- }
84102
- return objective.slice(0, markerIndex).trim();
84103
- };
84104
- const isSessionGoalCommand = (value) => typeof value === "string" && SESSION_GOAL_COMMANDS.includes(value);
84105
- const isSessionGoalCommandRequest = (value) => {
84106
- if (typeof value !== "object" || value === null) {
84107
- return false;
84108
- }
84109
- const maybeRequest = value;
84110
- return typeof maybeRequest.id === "string" && maybeRequest.id.length > 0 && typeof maybeRequest.threadId === "string" && maybeRequest.threadId.length > 0 && isSessionGoalCommand(maybeRequest.command) && typeof maybeRequest.requestedAt === "number" && Number.isFinite(maybeRequest.requestedAt);
84111
- };
84112
84301
  const escapeAttribute = (value) => value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
84113
84302
  function formatCommentReferenceForPrompt(ref2) {
84114
84303
  const lines2 = [];
@@ -97956,9 +98145,29 @@ stream:${scope2.streamId}`;
97956
98145
  }
97957
98146
  }
97958
98147
  };
98148
+ const readStructuredErrorDetail = (error2) => {
98149
+ const data = error2.data;
98150
+ if (typeof data === "string" && data.length > 0) {
98151
+ return data;
98152
+ }
98153
+ if (!data || typeof data !== "object") {
98154
+ return void 0;
98155
+ }
98156
+ const rawDetail = data.details;
98157
+ if (typeof rawDetail === "string" && rawDetail.length > 0) {
98158
+ return rawDetail;
98159
+ }
98160
+ const rawMessage = data.message;
98161
+ return typeof rawMessage === "string" && rawMessage.length > 0 ? rawMessage : void 0;
98162
+ };
97959
98163
  const formatErrorMessage = (error2, options = {}) => {
97960
98164
  if (error2 instanceof Error) {
97961
- return options.includeStack ? error2.stack ?? error2.message : error2.message;
98165
+ const base = options.includeStack ? error2.stack ?? error2.message : error2.message;
98166
+ const detail = readStructuredErrorDetail(error2);
98167
+ if (!detail || base.includes(detail)) {
98168
+ return base;
98169
+ }
98170
+ return `${base}: ${detail}`;
97962
98171
  }
97963
98172
  if (typeof error2 === "string") {
97964
98173
  return error2;
@@ -121316,7 +121525,8 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
121316
121525
  "active",
121317
121526
  "paused",
121318
121527
  "budgetLimited",
121319
- "complete"
121528
+ "complete",
121529
+ "cleared"
121320
121530
  ]);
121321
121531
  const ThreadGoalPayloadSchema = object$1({
121322
121532
  threadId: string$2(),
@@ -121336,54 +121546,31 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
121336
121546
  const ThreadGoalClearedParamsSchema = object$1({
121337
121547
  threadId: string$2()
121338
121548
  });
121339
- const CodexImageGenerationBeginParamsSchema = object$1({
121340
- sessionId: string$2().optional(),
121341
- session_id: string$2().optional(),
121342
- callId: string$2().optional(),
121343
- call_id: string$2().optional()
121344
- }).passthrough().transform((params, ctx) => {
121345
- const sessionId = params.sessionId ?? params.session_id;
121346
- const callId = params.callId ?? params.call_id;
121347
- if (!sessionId || !callId) {
121348
- ctx.addIssue({
121349
- code: ZodIssueCode$1.custom,
121350
- message: "sessionId/session_id and callId/call_id are required"
121351
- });
121352
- return NEVER;
121353
- }
121354
- return {
121355
- sessionId,
121356
- callId
121357
- };
121358
- });
121359
- const CodexImageGenerationEndParamsSchema = object$1({
121360
- sessionId: string$2().optional(),
121361
- session_id: string$2().optional(),
121362
- callId: string$2().optional(),
121363
- call_id: string$2().optional(),
121364
- status: string$2(),
121365
- revisedPrompt: string$2().nullable().optional(),
121366
- revised_prompt: string$2().nullable().optional(),
121367
- savedPath: string$2().nullable().optional(),
121368
- saved_path: string$2().nullable().optional()
121369
- }).passthrough().transform((params, ctx) => {
121370
- const sessionId = params.sessionId ?? params.session_id;
121371
- const callId = params.callId ?? params.call_id;
121372
- if (!sessionId || !callId) {
121373
- ctx.addIssue({
121374
- code: ZodIssueCode$1.custom,
121375
- message: "sessionId/session_id and callId/call_id are required"
121376
- });
121377
- return NEVER;
121549
+ const CODEX_IMAGE_GENERATION_TOOL_TITLE = "Image generation";
121550
+ const CODEX_IMAGE_GENERATION_REVISED_PROMPT_PREFIX = "Revised prompt: ";
121551
+ function extractCodexImageGenerationFields(content) {
121552
+ if (!Array.isArray(content)) return {};
121553
+ let revisedPrompt;
121554
+ let savedPath;
121555
+ for (const block of content) {
121556
+ if (!block || typeof block !== "object") continue;
121557
+ const b = block;
121558
+ if (b.type !== "content") continue;
121559
+ const inner = b.content;
121560
+ if (!inner) continue;
121561
+ if (inner.type === "text" && typeof inner.text === "string") {
121562
+ if (revisedPrompt === void 0 && inner.text.startsWith(CODEX_IMAGE_GENERATION_REVISED_PROMPT_PREFIX)) {
121563
+ revisedPrompt = inner.text.slice(CODEX_IMAGE_GENERATION_REVISED_PROMPT_PREFIX.length);
121564
+ }
121565
+ } else if (inner.type === "image" && typeof inner.uri === "string" && inner.uri.length > 0) {
121566
+ savedPath = inner.uri;
121567
+ }
121378
121568
  }
121379
121569
  return {
121380
- sessionId,
121381
- callId,
121382
- status: params.status,
121383
- revisedPrompt: params.revisedPrompt ?? params.revised_prompt ?? void 0,
121384
- savedPath: params.savedPath ?? params.saved_path ?? void 0
121570
+ revisedPrompt,
121571
+ savedPath
121385
121572
  };
121386
- });
121573
+ }
121387
121574
  class AgentClient {
121388
121575
  constructor(options) {
121389
121576
  this.options = options;
@@ -121405,6 +121592,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
121405
121592
  currentModel;
121406
121593
  userSelectedModeId;
121407
121594
  isAgentInPlanMode = false;
121595
+ codexImageGenerationToolCallIds = /* @__PURE__ */ new Set();
121408
121596
  buildMcpServers() {
121409
121597
  if (!this.options.workspaceId || !this.options.machineId) {
121410
121598
  return [];
@@ -121466,9 +121654,58 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
121466
121654
  }
121467
121655
  }
121468
121656
  }
121657
+ if (this.handleCodexImageGenerationNotification(notification)) {
121658
+ return;
121659
+ }
121469
121660
  this.options.onUpdateMessage(notification);
121470
121661
  return;
121471
121662
  }
121663
+ handleCodexImageGenerationNotification(notification) {
121664
+ if (!this.isCodexAgent()) return false;
121665
+ const update2 = notification.update;
121666
+ if (update2.sessionUpdate !== "tool_call" && update2.sessionUpdate !== "tool_call_update") {
121667
+ return false;
121668
+ }
121669
+ const callId = update2.toolCallId;
121670
+ if (typeof callId !== "string" || callId.length === 0) return false;
121671
+ const isBegin = update2.sessionUpdate === "tool_call";
121672
+ const isImageGenByTitle = typeof update2.title === "string" && update2.title === CODEX_IMAGE_GENERATION_TOOL_TITLE;
121673
+ const isTracked = this.codexImageGenerationToolCallIds.has(callId);
121674
+ if (isBegin) {
121675
+ if (!isImageGenByTitle) return false;
121676
+ } else if (!isTracked) {
121677
+ return false;
121678
+ }
121679
+ const acpSessionId = notification.sessionId ?? this.acpSessionId;
121680
+ if (!acpSessionId || !this.isCurrentAcpSession(acpSessionId)) {
121681
+ this.logger.debug(`[${this.options.sessionId}] Dropping Codex image generation notification for mismatched ACP session: ${acpSessionId}`);
121682
+ return true;
121683
+ }
121684
+ const status = typeof update2.status === "string" ? update2.status : void 0;
121685
+ const isTerminalStatus = status === "completed" || status === "failed";
121686
+ if (isBegin && !isTracked) {
121687
+ this.codexImageGenerationToolCallIds.add(callId);
121688
+ this.options.onCodexImageGenerationBegin?.({
121689
+ acpSessionId,
121690
+ callId
121691
+ });
121692
+ }
121693
+ const carriesEndPayload = !isBegin || isTerminalStatus || Array.isArray(update2.content);
121694
+ if (carriesEndPayload && status) {
121695
+ const { revisedPrompt, savedPath } = extractCodexImageGenerationFields(update2.content);
121696
+ this.options.onCodexImageGenerationEnd?.({
121697
+ acpSessionId,
121698
+ callId,
121699
+ status,
121700
+ revisedPrompt,
121701
+ savedPath
121702
+ });
121703
+ }
121704
+ if (isTerminalStatus) {
121705
+ this.codexImageGenerationToolCallIds.delete(callId);
121706
+ }
121707
+ return true;
121708
+ }
121472
121709
  handleUsageUpdate(update2) {
121473
121710
  if (!isUsageUpdate(update2)) {
121474
121711
  return false;
@@ -121595,47 +121832,6 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
121595
121832
  this.options.onThreadGoalCleared?.(result.data.threadId);
121596
121833
  break;
121597
121834
  }
121598
- case "acp_ext:image_generation_begin": {
121599
- if (!this.isCodexAgent()) {
121600
- break;
121601
- }
121602
- const result = CodexImageGenerationBeginParamsSchema.safeParse(params);
121603
- if (!result.success) {
121604
- this.logger.debug(`[${this.options.sessionId}] Dropping invalid Codex image generation begin: ${result.error.message}`);
121605
- break;
121606
- }
121607
- if (!this.isCurrentAcpSession(result.data.sessionId)) {
121608
- this.logger.debug(`[${this.options.sessionId}] Dropping Codex image generation begin for mismatched ACP session: ${result.data.sessionId}`);
121609
- break;
121610
- }
121611
- this.options.onCodexImageGenerationBegin?.({
121612
- acpSessionId: result.data.sessionId,
121613
- callId: result.data.callId
121614
- });
121615
- break;
121616
- }
121617
- case "acp_ext:image_generation_end": {
121618
- if (!this.isCodexAgent()) {
121619
- break;
121620
- }
121621
- const result = CodexImageGenerationEndParamsSchema.safeParse(params);
121622
- if (!result.success) {
121623
- this.logger.debug(`[${this.options.sessionId}] Dropping invalid Codex image generation end: ${result.error.message}`);
121624
- break;
121625
- }
121626
- if (!this.isCurrentAcpSession(result.data.sessionId)) {
121627
- this.logger.debug(`[${this.options.sessionId}] Dropping Codex image generation end for mismatched ACP session: ${result.data.sessionId}`);
121628
- break;
121629
- }
121630
- this.options.onCodexImageGenerationEnd?.({
121631
- acpSessionId: result.data.sessionId,
121632
- callId: result.data.callId,
121633
- status: result.data.status,
121634
- revisedPrompt: result.data.revisedPrompt,
121635
- savedPath: result.data.savedPath
121636
- });
121637
- break;
121638
- }
121639
121835
  }
121640
121836
  }
121641
121837
  isCodexAgent() {
@@ -122012,11 +122208,13 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
122012
122208
  },
122013
122209
  codex: {
122014
122210
  packageName: "acp-extension-codex",
122015
- version: "0.12.5",
122211
+ version: "0.14.2",
122016
122212
  binName: "acp-extension-codex",
122017
122213
  args: [
122018
122214
  "-c",
122019
- "shell_environment_policy.ignore_default_excludes=true"
122215
+ "shell_environment_policy.ignore_default_excludes=true",
122216
+ "-c",
122217
+ "features.goals=true"
122020
122218
  ]
122021
122219
  }
122022
122220
  };
@@ -122116,6 +122314,23 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
122116
122314
  }
122117
122315
  };
122118
122316
  }
122317
+ if (agent.distribution.uvx?.package) {
122318
+ const args2 = [
122319
+ agent.distribution.uvx.package,
122320
+ ...agent.distribution.uvx.args ?? []
122321
+ ];
122322
+ return {
122323
+ status: {
122324
+ agent: `${agent.name}@${agent.version}`,
122325
+ command: "uvx"
122326
+ },
122327
+ exec: {
122328
+ command: "uvx",
122329
+ args: args2,
122330
+ env: agent.distribution.uvx.env
122331
+ }
122332
+ };
122333
+ }
122119
122334
  throw new Error(`Registry ACP ${agentType} has no supported launcher`);
122120
122335
  }
122121
122336
  function resolveACPSetting(input2) {
@@ -124668,6 +124883,8 @@ path=/${options.repoFullName}.git
124668
124883
  codexImageGenerationTurnIds: /* @__PURE__ */ new Map(),
124669
124884
  codexImageGenerationUploads: /* @__PURE__ */ new Map(),
124670
124885
  codexImageGenerationUploadedCallIds: /* @__PURE__ */ new Set(),
124886
+ codexImageGenerationActiveCallIds: /* @__PURE__ */ new Set(),
124887
+ codexImageGenerationActivityStatusChain: Promise.resolve(),
124671
124888
  permissionWaitMs: 0,
124672
124889
  autoApprovePermissions: false,
124673
124890
  pendingUnread: false,
@@ -124767,6 +124984,7 @@ path=/${options.repoFullName}.git
124767
124984
  state2.contextWindowUsageTimer = null;
124768
124985
  }
124769
124986
  state2.contextWindowUsageBuffer = null;
124987
+ state2.codexImageGenerationActiveCallIds.clear();
124770
124988
  state2.permissionWaitMs = 0;
124771
124989
  state2.pendingUnread = false;
124772
124990
  }
@@ -125618,17 +125836,15 @@ $mem | ConvertTo-Json -Compress
125618
125836
  const { sessionId, threadId: threadId2, command: command2 } = request;
125619
125837
  this.deps.logger.debug(`[${sessionId}] Goal command requested (command=${command2} threadId=${threadId2})`);
125620
125838
  const existingSession = this.deps.sessionManager.getSession(sessionId);
125621
- const pendingSession = this.deps.sessionManager.getPendingSession(sessionId);
125622
- const candidateSession = existingSession ?? (pendingSession ? await pendingSession : null);
125623
- if (candidateSession?.agentClient && candidateSession.acpSessionId) {
125839
+ if (existingSession?.agentClient && existingSession.acpSessionId) {
125624
125840
  try {
125625
- await candidateSession.agentClient.controlThreadGoal(candidateSession.acpSessionId, command2, threadId2);
125841
+ await existingSession.agentClient.controlThreadGoal(existingSession.acpSessionId, command2, threadId2);
125626
125842
  this.deps.logger.debug(`[${sessionId}] Goal command sent through ACP extension method (command=${command2})`);
125627
125843
  return {
125628
125844
  status: "handled"
125629
125845
  };
125630
125846
  } catch (error2) {
125631
- this.deps.logger.debug(`[${sessionId}] ACP extension goal command unavailable; falling back to hidden slash command: ${formatErrorMessage(error2)}`);
125847
+ this.deps.logger.debug(`[${sessionId}] ACP extension goal command unavailable; falling back to local state update: ${formatErrorMessage(error2)}`);
125632
125848
  }
125633
125849
  }
125634
125850
  if (this.getExecutionSnapshot(sessionId).hasActiveTurn) {
@@ -125638,26 +125854,8 @@ $mem | ConvertTo-Json -Compress
125638
125854
  };
125639
125855
  }
125640
125856
  try {
125641
- const session = await this.ensureSessionForGoalCommand(request);
125642
- if (!session.agentClient || !session.acpSessionId) {
125643
- return {
125644
- status: "failed",
125645
- error: "Agent session is not ready"
125646
- };
125647
- }
125648
- this.deps.beginACPReplaySuppression(sessionId);
125649
- try {
125650
- await session.agentClient.prompt(session.acpSessionId, [
125651
- {
125652
- type: "text",
125653
- text: `/goal ${command2}`
125654
- }
125655
- ]);
125656
- } finally {
125657
- this.deps.endACPReplaySuppression(sessionId);
125658
- }
125659
- this.deps.touchSession(sessionId);
125660
- this.deps.logger.debug(`[${sessionId}] Goal command sent through hidden slash command (command=${command2})`);
125857
+ await this.persistLocalGoalCommandFallback(request);
125858
+ this.deps.logger.debug(`[${sessionId}] Goal command applied through local state fallback (command=${command2})`);
125661
125859
  return {
125662
125860
  status: "handled"
125663
125861
  };
@@ -125670,58 +125868,50 @@ $mem | ConvertTo-Json -Compress
125670
125868
  };
125671
125869
  }
125672
125870
  }
125673
- async ensureSessionForGoalCommand(request) {
125674
- const existing = this.deps.sessionManager.getSession(request.sessionId);
125675
- if (existing) {
125676
- return existing;
125677
- }
125678
- const pending2 = this.deps.sessionManager.getPendingSession(request.sessionId);
125679
- if (pending2) {
125680
- return await pending2;
125681
- }
125682
- const sessionDoc = await this.deps.workspaceDocument.getOrCreateSessionDoc(request.sessionId);
125871
+ async resolveThreadGoalForLocalFallback(sessionDoc, threadId2) {
125683
125872
  const meta = await sessionDoc.getMetaState();
125684
- if (!meta) {
125685
- throw new Error("Session metadata is not available");
125873
+ const metaGoal = meta?.latestGoal;
125874
+ if (metaGoal?.threadId === threadId2) {
125875
+ return metaGoal;
125686
125876
  }
125687
- if (meta.isArchived) {
125688
- throw new Error("Session is archived");
125877
+ const historyGoal = resolveLatestSessionGoalFromHistory(await sessionDoc.getHistory());
125878
+ if (historyGoal?.threadId === threadId2) {
125879
+ return historyGoal;
125689
125880
  }
125690
- if (!meta.cliType || !meta.agentType) {
125691
- throw new Error("Session agent metadata is incomplete");
125881
+ return null;
125882
+ }
125883
+ async resolveLatestGoalForCompletionNotification(sessionId, sessionDoc) {
125884
+ let metaGoal = null;
125885
+ try {
125886
+ metaGoal = (await sessionDoc.getMetaState())?.latestGoal ?? null;
125887
+ } catch (error2) {
125888
+ this.deps.logger.debug(`[${sessionId}] Failed to read latest goal meta before completion notification: ${formatErrorMessage(error2)}`);
125692
125889
  }
125693
- const project = meta.project;
125694
- const localProjectId = project?.kind === "local" ? project.localProjectId : void 0;
125695
- const workdir = localProjectId ? await resolveWorkspaceLocalProjectRootPath(this.deps.workspaceDocument.repo, this.deps.machineId, localProjectId) ?? void 0 : void 0;
125696
- if (project?.kind === "local" && !workdir) {
125697
- throw new Error(`Local project not found in workspace: ${project.localProjectId}`);
125890
+ try {
125891
+ return resolveLatestSessionGoalFromHistory(await sessionDoc.getHistory()) ?? metaGoal;
125892
+ } catch (error2) {
125893
+ this.deps.logger.debug(`[${sessionId}] Failed to read latest goal history before completion notification: ${formatErrorMessage(error2)}`);
125894
+ return metaGoal;
125698
125895
  }
125699
- const agentConfigEnvResolution = await resolveSessionEnvForSessionResume(this.deps.workspaceDocument.repo, {
125700
- sessionEnv: meta.env,
125701
- agentConfigId: meta.agentConfigId
125702
- });
125703
- const restoreConfig = {
125704
- sessionId: request.sessionId,
125705
- workspaceId: this.deps.workspaceId,
125706
- agentCliType: meta.cliType,
125707
- agentType: meta.agentType,
125708
- userId: request.userId,
125709
- machineId: this.deps.machineId,
125710
- assumeDocExisting: true,
125711
- env: agentConfigEnvResolution.env ?? void 0,
125712
- githubRepo: resolveProjectGitHubRepo(project),
125713
- branch: project?.branch?.trim() || void 0,
125714
- restoreBranchName: meta.branchName?.trim() || void 0,
125715
- project,
125716
- resume: true,
125717
- workdir,
125718
- parentSessionId: meta.parentSessionId,
125719
- userName: request.userName,
125720
- userEmail: request.userEmail
125896
+ }
125897
+ async persistLocalGoalCommandFallback(request) {
125898
+ const sessionDoc = await this.deps.workspaceDocument.getOrCreateSessionDoc(request.sessionId);
125899
+ const existingGoal = await this.resolveThreadGoalForLocalFallback(sessionDoc, request.threadId);
125900
+ if (!existingGoal) {
125901
+ if (request.command === "clear") {
125902
+ return;
125903
+ }
125904
+ throw new Error(`Goal thread not found: ${request.threadId}`);
125905
+ }
125906
+ const nextStatus = request.command === "pause" ? "paused" : request.command === "resume" ? "active" : "cleared";
125907
+ const updatedGoal = {
125908
+ ...existingGoal,
125909
+ status: nextStatus,
125910
+ updatedAt: getServerNow()
125721
125911
  };
125722
- this.deps.logger.debug(`[${request.sessionId}] Restoring session for hidden goal command (resumeSessionId=${meta.acpSessionId ?? "none"})`);
125723
- return await this.deps.sessionManager.createSession(restoreConfig, {
125724
- resumeSessionId: meta.acpSessionId
125912
+ await upsertThreadGoalInHistory(sessionDoc, updatedGoal);
125913
+ await this.deps.workspaceDocument.repo.upsertDocMeta(sessionDoc.roomId, {
125914
+ latestGoal: updatedGoal
125725
125915
  });
125726
125916
  }
125727
125917
  formatGiB(bytes) {
@@ -126058,7 +126248,8 @@ $mem | ConvertTo-Json -Compress
126058
126248
  const rawDataMessage = dataObj?.message;
126059
126249
  const dataDetails = typeof rawDetails === "string" && rawDetails.length > 0 ? rawDetails : void 0;
126060
126250
  const dataMessage = typeof rawDataMessage === "string" && rawDataMessage.length > 0 ? rawDataMessage : void 0;
126061
- const userMessage = dataDetails || dataMessage || acpError.message;
126251
+ const dataString = typeof acpError.data === "string" && acpError.data.length > 0 ? acpError.data : void 0;
126252
+ const userMessage = dataDetails || dataMessage || dataString || acpError.message;
126062
126253
  this.deps.logger.warn(`[${sessionId}] ACP error occurred (code=${acpError.code} reason=${failureReason}): ${userMessage}`);
126063
126254
  await this.deps.recordChatFailure(sessionDoc, failureReason, userMessage);
126064
126255
  if (this.shouldTerminateOnACPError(acpError.code, failureReason)) {
@@ -126162,6 +126353,11 @@ $mem | ConvertTo-Json -Compress
126162
126353
  this.deps.logger.debug(`[${sessionId}] Failed to persist session lastMessageAt: ${formatErrorMessage(error2)}`);
126163
126354
  }
126164
126355
  this.deps.touchSession(sessionId);
126356
+ const latestGoal = await this.resolveLatestGoalForCompletionNotification(sessionId, sessionDoc);
126357
+ if (isSessionGoalWorking(latestGoal)) {
126358
+ this.deps.logger.debug(`[${sessionId}] Skipping session completion notification because a thread goal is still active`);
126359
+ return;
126360
+ }
126165
126361
  await this.deps.turnFinalization.notifySessionCompleted(sessionId, userId);
126166
126362
  }
126167
126363
  async runVisibleSessionTurn(options, body) {
@@ -127644,9 +127840,11 @@ $mem | ConvertTo-Json -Compress
127644
127840
  return;
127645
127841
  }
127646
127842
  const requesterUserId = nextUserTurn.userId ?? freshMeta.userId;
127843
+ const localProjectId = freshMeta.project?.kind === "local" ? freshMeta.project.localProjectId : void 0;
127647
127844
  const access = await this.deps.canUseMachine({
127648
127845
  sessionId,
127649
- requesterUserId
127846
+ requesterUserId,
127847
+ localProjectId
127650
127848
  });
127651
127849
  if (!access.allowed) {
127652
127850
  await this.markDispatchAccessDenied(sessionId, sessionDoc, nextUserTurn.id, access.reason);
@@ -127668,6 +127866,8 @@ $mem | ConvertTo-Json -Compress
127668
127866
  return "This machine is not registered for workspace access.";
127669
127867
  case "not_visible":
127670
127868
  return "This machine is private to its owner.";
127869
+ case "project_not_shared":
127870
+ return "This local project is not shared with the team.";
127671
127871
  case "access_unavailable":
127672
127872
  return "Machine access could not be verified.";
127673
127873
  case "cli_token_invalid":
@@ -127719,14 +127919,10 @@ $mem | ConvertTo-Json -Compress
127719
127919
  if (request.id === this.goalCommandSeenId.get(sessionId)) {
127720
127920
  return;
127721
127921
  }
127722
- const user = await this.userResolver.resolve(meta.userId);
127723
127922
  const result = await this.deps.executionService.controlSessionGoal({
127724
127923
  sessionId,
127725
127924
  threadId: request.threadId,
127726
- command: request.command,
127727
- userId: meta.userId,
127728
- userName: user.name,
127729
- userEmail: user.email
127925
+ command: request.command
127730
127926
  });
127731
127927
  if (result.status === "deferred") {
127732
127928
  this.deps.logger.debug(`[${sessionId}] Deferring hidden goal command ${request.command} until active turn completes`);
@@ -127737,7 +127933,7 @@ $mem | ConvertTo-Json -Compress
127737
127933
  lastGoalCommand: void 0
127738
127934
  });
127739
127935
  if (result.status === "failed") {
127740
- this.deps.logger.debug(`[${sessionId}] Hidden goal command ${request.command} failed: ${result.error}`);
127936
+ this.deps.logger.debug(`[${sessionId}] Goal command ${request.command} failed: ${result.error}`);
127741
127937
  }
127742
127938
  }
127743
127939
  async buildChatRequestFromHistoryEntry(meta, entry2) {
@@ -128499,7 +128695,8 @@ $mem | ConvertTo-Json -Compress
128499
128695
  reason: _enum$1([
128500
128696
  "requester_not_member",
128501
128697
  "machine_not_registered",
128502
- "not_visible"
128698
+ "not_visible",
128699
+ "project_not_shared"
128503
128700
  ])
128504
128701
  })
128505
128702
  ]);
@@ -128551,15 +128748,30 @@ $mem | ConvertTo-Json -Compress
128551
128748
  });
128552
128749
  return RegisterMachineAccessResultSchema.parse(raw);
128553
128750
  }
128751
+ const LEGACY_LOCAL_PROJECT_ID_VALIDATOR_ERROR = /ArgumentValidationError[\s\S]*extra field `localProjectId`/;
128554
128752
  async function canUseMachineForCliToken(input2) {
128555
128753
  const client = createAuthConvexClient();
128556
- const raw = await client.query(api.machines.canUseMachineFromCliToken, {
128754
+ const baseArgs = {
128557
128755
  cliToken: input2.token,
128558
128756
  workspaceId: input2.workspaceId,
128559
128757
  machineId: input2.machineId,
128560
128758
  requesterUserId: input2.requesterUserId
128561
- });
128562
- return MachineAccessCheckResultSchema.parse(raw);
128759
+ };
128760
+ try {
128761
+ const raw = await client.query(api.machines.canUseMachineFromCliToken, {
128762
+ ...baseArgs,
128763
+ ...input2.localProjectId !== void 0 ? {
128764
+ localProjectId: input2.localProjectId
128765
+ } : {}
128766
+ });
128767
+ return MachineAccessCheckResultSchema.parse(raw);
128768
+ } catch (error2) {
128769
+ if (input2.localProjectId !== void 0 && error2 instanceof Error && LEGACY_LOCAL_PROJECT_ID_VALIDATOR_ERROR.test(error2.message)) {
128770
+ const raw = await client.query(api.machines.canUseMachineFromCliToken, baseArgs);
128771
+ return MachineAccessCheckResultSchema.parse(raw);
128772
+ }
128773
+ throw error2;
128774
+ }
128563
128775
  }
128564
128776
  const HTTP_HOP_BY_HOP_HEADERS = /* @__PURE__ */ new Set([
128565
128777
  "connection",
@@ -130535,9 +130747,7 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
130535
130747
  };
130536
130748
  const CODEX_IMAGE_GENERATION_TERMINAL_STATUSES = /* @__PURE__ */ new Set([
130537
130749
  "completed",
130538
- "failed",
130539
- "cancelled",
130540
- "incomplete"
130750
+ "failed"
130541
130751
  ]);
130542
130752
  function isCodexImageGenerationTerminalStatus(status) {
130543
130753
  return CODEX_IMAGE_GENERATION_TERMINAL_STATUSES.has(status.trim().toLowerCase());
@@ -130705,13 +130915,14 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
130705
130915
  workspaceId: this.workspaceId,
130706
130916
  workspaceDocument: this.workspaceDocument,
130707
130917
  executionService: this.executionService,
130708
- canUseMachine: async ({ requesterUserId, sessionId }) => {
130918
+ canUseMachine: async ({ requesterUserId, sessionId, localProjectId }) => {
130709
130919
  try {
130710
130920
  return await canUseMachineForCliToken({
130711
130921
  token: this.token,
130712
130922
  workspaceId: this.workspaceId,
130713
130923
  machineId: this.machineId,
130714
- requesterUserId
130924
+ requesterUserId,
130925
+ localProjectId
130715
130926
  });
130716
130927
  } catch (error2) {
130717
130928
  const message = formatErrorMessage(error2);
@@ -130986,11 +131197,44 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
130986
131197
  }
130987
131198
  handleCodexImageGenerationBegin(sessionId, event) {
130988
131199
  const turnId = this.store.getTurnId(sessionId) ?? null;
130989
- this.store.get(sessionId).codexImageGenerationTurnIds.set(event.callId, turnId);
131200
+ const state2 = this.store.get(sessionId);
131201
+ state2.codexImageGenerationTurnIds.set(event.callId, turnId);
131202
+ state2.codexImageGenerationActiveCallIds.add(event.callId);
131203
+ this.enqueueCodexImageGenerationActivityStatusSync(sessionId);
130990
131204
  this.logger.debug(`[${sessionId}] Codex image generation started (callId=${event.callId} turnId=${turnId ?? "none"})`);
130991
131205
  }
131206
+ enqueueCodexImageGenerationActivityStatusSync(sessionId) {
131207
+ const state2 = this.store.get(sessionId);
131208
+ const task = state2.codexImageGenerationActivityStatusChain.catch(() => void 0).then(async () => {
131209
+ const currentState = this.store.get(sessionId);
131210
+ const hasActiveImageGeneration = currentState.codexImageGenerationActiveCallIds.size > 0;
131211
+ const sessionDoc = await this.workspaceDocument.getOrCreateSessionDoc(sessionId);
131212
+ const status = (await sessionDoc.getMetaState())?.status;
131213
+ if (hasActiveImageGeneration) {
131214
+ if (status?.type !== "requestPermission") {
131215
+ await sessionDoc.setStatus(SessionStatusFactory.running("image_generation"));
131216
+ }
131217
+ return;
131218
+ }
131219
+ if (status?.type === "running" && status.activity === "image_generation") {
131220
+ await sessionDoc.setStatus(SessionStatusFactory.running());
131221
+ }
131222
+ });
131223
+ state2.codexImageGenerationActivityStatusChain = task;
131224
+ void task.catch((error2) => {
131225
+ try {
131226
+ this.logger.debug(`[${sessionId}] Failed to sync Codex image generation activity: ${formatErrorMessage(error2)}`);
131227
+ } catch {
131228
+ }
131229
+ });
131230
+ }
130992
131231
  handleCodexImageGenerationEnd(sessionId, event) {
130993
131232
  const state2 = this.store.get(sessionId);
131233
+ const isTerminal2 = isCodexImageGenerationTerminalStatus(event.status);
131234
+ if (isTerminal2) {
131235
+ state2.codexImageGenerationActiveCallIds.delete(event.callId);
131236
+ this.enqueueCodexImageGenerationActivityStatusSync(sessionId);
131237
+ }
130994
131238
  if (state2.codexImageGenerationUploadedCallIds.has(event.callId)) {
130995
131239
  this.logger.debug(`[${sessionId}] Ignoring duplicate Codex image generation upload (callId=${event.callId} status=${event.status})`);
130996
131240
  return;
@@ -131001,7 +131245,6 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
131001
131245
  }
131002
131246
  const cachedTurnId = state2.codexImageGenerationTurnIds.get(event.callId);
131003
131247
  const capturedTurnId = cachedTurnId !== void 0 ? cachedTurnId : this.store.getTurnId(sessionId) ?? null;
131004
- const isTerminal2 = isCodexImageGenerationTerminalStatus(event.status);
131005
131248
  const savedPath = this.resolveCodexGeneratedImagePath(sessionId, event.savedPath);
131006
131249
  if (!savedPath) {
131007
131250
  if (isTerminal2) {
@@ -143674,6 +143917,7 @@ Received ${signal}, shutting down gracefully...` : "\nShutting down gracefully..
143674
143917
  }
143675
143918
  const ELECTRON_BOOTSTRAP_ENV = "LODY_ELECTRON_BOOTSTRAP";
143676
143919
  const ELECTRON_SESSION_TOKEN_ENV = "LODY_ELECTRON_SESSION_TOKEN";
143920
+ const ELECTRON_SESSION_USER_ID_ENV = "LODY_ELECTRON_SESSION_USER_ID";
143677
143921
  const EXIT_CODE_ALREADY_RUNNING = 3;
143678
143922
  function logCliDetectionResults(logger2, availability) {
143679
143923
  logger2.debug("Local agent auth detection results:");
@@ -143721,6 +143965,8 @@ Received ${signal}, shutting down gracefully...` : "\nShutting down gracefully..
143721
143965
  codex: checkCodex()
143722
143966
  };
143723
143967
  logCliDetectionResults(logger2, cliAvailability);
143968
+ const electronSessionUser = electronSessionToken && !process.env[ELECTRON_SESSION_USER_ID_ENV]?.trim() ? await authClient.getSessionUserFromSessionToken(electronSessionToken) : null;
143969
+ const electronSessionUserId = process.env[ELECTRON_SESSION_USER_ID_ENV]?.trim() || electronSessionUser?.id || null;
143724
143970
  const cliSelection = resolveCliTypesSelection({
143725
143971
  requestedCliTypes: options.cliTypes,
143726
143972
  availability: cliAvailability
@@ -143783,13 +144029,19 @@ Received ${signal}, shutting down gracefully...` : "\nShutting down gracefully..
143783
144029
  } else if (existingAuth) {
143784
144030
  logger2.debug("Found existing authentication, checking validity...");
143785
144031
  const validation2 = await authClient.validateToken(existingAuth.token);
143786
- if (validation2.valid) {
144032
+ const existingUserId = validation2.valid ? validation2.userId ?? existingAuth.user.id : null;
144033
+ const electronUserMismatch = !!electronSessionToken && electronSessionUserId !== null && existingUserId !== null && existingUserId !== electronSessionUserId;
144034
+ if (validation2.valid && existingUserId && !electronUserMismatch) {
143787
144035
  token2 = existingAuth.token;
143788
- userId = validation2.userId ?? existingAuth.user.id;
144036
+ userId = existingUserId;
143789
144037
  machineId = existingAuth.machine.machineId;
143790
144038
  machineName = defaultMachineName;
143791
144039
  } else {
143792
- logger2.warn("Existing authentication is invalid.");
144040
+ if (electronUserMismatch) {
144041
+ logger2.warn("Existing CLI credentials belong to a different account than the desktop session \u2014 re-authenticating.");
144042
+ } else {
144043
+ logger2.warn("Existing authentication is invalid.");
144044
+ }
143793
144045
  const bootstrap = await completeBootstrapLogin();
143794
144046
  if (bootstrap) {
143795
144047
  token2 = bootstrap.token;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lody",
3
- "version": "0.50.0",
3
+ "version": "0.50.2",
4
4
  "description": "Lody Agent CLI tool for managing remote command execution",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -21,7 +21,7 @@
21
21
  },
22
22
  "optionalDependencies": {
23
23
  "acp-extension-claude": "0.31.1",
24
- "acp-extension-codex": "0.12.5"
24
+ "acp-extension-codex": "0.14.2"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@agentclientprotocol/sdk": "^0.20.0",
@@ -74,10 +74,10 @@
74
74
  "ws": "^8.18.3",
75
75
  "zod": "^4.1.5",
76
76
  "@lody/cli-supervisor": "0.0.1",
77
+ "@lody/convex": "0.0.1",
77
78
  "@lody/loro-streams-rpc": "0.0.1",
78
79
  "@lody/shared": "0.0.1",
79
- "loro-code": "0.0.1",
80
- "@lody/convex": "0.0.1"
80
+ "loro-code": "0.0.1"
81
81
  },
82
82
  "files": [
83
83
  "dist",