lody 0.86.2 → 0.87.0

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.
@@ -80,7 +80,7 @@ let __tla = Promise.all([
80
80
  }
81
81
  })()
82
82
  ]).then(async () => {
83
- const reviewViewerVersion = "0.86.2";
83
+ const reviewViewerVersion = "0.87.0";
84
84
  const reviewViewerSha256 = "dc9c7fc1dde49bb39c5fe86392d69219b469c0cab4fd492d8b562045e7f676e4";
85
85
  const reviewViewerFileName = "standalone.html";
86
86
  const DEFAULT_CDN_BASES = [
package/dist/index.js CHANGED
@@ -4325,7 +4325,7 @@ Upgrade Node, then re-run: npx lody@latest`;
4325
4325
  }
4326
4326
  const name$2 = "lody";
4327
4327
  const name$1 = "@lody/cli-cloud";
4328
- const version$9 = "0.86.2";
4328
+ const version$9 = "0.87.0";
4329
4329
  const type$2 = "module";
4330
4330
  const scripts = {
4331
4331
  "dev": "node dev.mjs",
@@ -5202,6 +5202,14 @@ Requirements:
5202
5202
  const SESSION_FILE_MAX_SIZE_BYTES = 100 * 1024 * 1024;
5203
5203
  const SESSION_FILE_MAX_COUNT = 8;
5204
5204
  const SESSION_FILE_PREVIEW_SNIFF_BYTES = 8 * 1024;
5205
+ function isSessionFileSourcePath(value) {
5206
+ if (value.length === 0 || value.includes("\0")) return false;
5207
+ const normalized = value.replace(/\\/g, "/");
5208
+ if (normalized === "." || normalized.startsWith("/") || /^[A-Za-z]:/.test(normalized)) {
5209
+ return false;
5210
+ }
5211
+ return !normalized.split("/").some((segment) => segment === "..");
5212
+ }
5205
5213
  const MESSAGE_TEXT_SPAN_KINDS = [
5206
5214
  "file",
5207
5215
  "dir",
@@ -11857,6 +11865,7 @@ Requirements:
11857
11865
  sizeBytes: number$4().int().nonnegative().max(SESSION_FILE_MAX_SIZE_BYTES),
11858
11866
  sha256: string$1(),
11859
11867
  textPreview: boolean(),
11868
+ sourcePath: string$1().refine(isSessionFileSourcePath).optional(),
11860
11869
  transport: _enum$1([
11861
11870
  "r2",
11862
11871
  "local"
@@ -20694,6 +20703,7 @@ ${tailedOutput}` : null;
20694
20703
  };
20695
20704
  const toFilePayload = (block) => {
20696
20705
  const machineId = typeof block.machineId === "string" ? block.machineId : void 0;
20706
+ const sourcePath = typeof block.sourcePath === "string" && isSessionFileSourcePath(block.sourcePath) ? block.sourcePath : void 0;
20697
20707
  const storageSessionId = typeof block.storageSessionId === "string" ? block.storageSessionId : void 0;
20698
20708
  return {
20699
20709
  type: "file",
@@ -20703,6 +20713,9 @@ ${tailedOutput}` : null;
20703
20713
  sizeBytes: block.sizeBytes,
20704
20714
  sha256: block.sha256,
20705
20715
  textPreview: block.textPreview,
20716
+ ...sourcePath === void 0 ? {} : {
20717
+ sourcePath
20718
+ },
20706
20719
  transport: block.transport,
20707
20720
  ...machineId === void 0 ? {} : {
20708
20721
  machineId
@@ -166427,6 +166440,9 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
166427
166440
  if (elapsedMs < day) return `${Math.max(1, Math.floor(elapsedMs / hour))}h`;
166428
166441
  return `${Math.max(1, Math.floor(elapsedMs / day))}d`;
166429
166442
  }
166443
+ function normalizeSessionFileSourcePath(sourcePath, separator = path__default$1.sep) {
166444
+ return separator === "/" ? sourcePath : sourcePath.split(separator).join("/");
166445
+ }
166430
166446
  const SESSION_FILE_MAX_PART_RETRIES = 3;
166431
166447
  const SESSION_FILE_MIME_TYPE_BY_EXTENSION = {
166432
166448
  txt: "text/plain",
@@ -171449,13 +171465,19 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
171449
171465
  }
171450
171466
  const uploadedFiles = [];
171451
171467
  const failures2 = [];
171468
+ const canonicalWorkspaceRoot = await fs__default.promises.realpath(workspaceRoot);
171452
171469
  for (const file2 of validatedFiles) {
171453
171470
  try {
171454
- uploadedFiles.push(await this.uploadValidatedSessionFile({
171471
+ const uploaded = await this.uploadValidatedSessionFile({
171455
171472
  workspaceId: this.workspaceId,
171456
171473
  sessionId,
171457
171474
  file: file2
171458
- }));
171475
+ });
171476
+ const sourcePath = normalizeSessionFileSourcePath(path__default$1.relative(canonicalWorkspaceRoot, file2.absolutePath));
171477
+ uploadedFiles.push({
171478
+ ...uploaded,
171479
+ sourcePath
171480
+ });
171459
171481
  } catch (error2) {
171460
171482
  failures2.push(`${file2.fileName}: ${formatErrorMessage(error2)}`);
171461
171483
  }
@@ -220289,7 +220311,7 @@ ${page}${helpTipBottom}${choiceDescription}${ansiEscapes.cursorHide}`;
220289
220311
  data = createReviewBundleSnapshot(bundle);
220290
220312
  }
220291
220313
  const { injectReviewSnapshot } = await import("./chunks/index-VoI6Ds2-.js");
220292
- const { resolveReviewViewerTemplate } = await import("./chunks/review-viewer-Bcj5lkU7.js").then(async (m) => {
220314
+ const { resolveReviewViewerTemplate } = await import("./chunks/review-viewer-BetqYmAc.js").then(async (m) => {
220293
220315
  await m.__tla;
220294
220316
  return m;
220295
220317
  });
@@ -238735,19 +238757,14 @@ ${result.stdout ?? ""}`;
238735
238757
  "agentConfigId",
238736
238758
  ...Object.keys(SessionRunConfigInputShape)
238737
238759
  ];
238738
- const addAgentRoleOverrideIssues = (value, ctx, pathPrefix = []) => {
238739
- if (value.agentRoleId === void 0) return;
238760
+ const omitAgentRoleManualOverrides = (input2) => {
238761
+ const normalized = {
238762
+ ...input2
238763
+ };
238740
238764
  for (const field of AGENT_ROLE_MANUAL_OVERRIDE_FIELDS) {
238741
- if (value[field] === void 0) continue;
238742
- ctx.addIssue({
238743
- code: "custom",
238744
- path: [
238745
- ...pathPrefix,
238746
- field
238747
- ],
238748
- message: `${field} cannot be combined with agentRoleId.`
238749
- });
238765
+ delete normalized[field];
238750
238766
  }
238767
+ return normalized;
238751
238768
  };
238752
238769
  const SessionCreateRuntimeInputSchema = xor([
238753
238770
  object$1({
@@ -238755,7 +238772,7 @@ ${result.stdout ?? ""}`;
238755
238772
  resume: literal$1(true)
238756
238773
  }).strict(),
238757
238774
  ...sessionCreateCommandSchemas
238758
- ]).superRefine((value, ctx) => addAgentRoleOverrideIssues(value, ctx));
238775
+ ]);
238759
238776
  const SessionCreateToolInputSchema = object$1({
238760
238777
  ...SessionCreateCommandInputShape,
238761
238778
  prompt: SessionCreateCommandInputShape.prompt.optional(),
@@ -238882,18 +238899,7 @@ ${result.stdout ?? ""}`;
238882
238899
  }).strict(),
238883
238900
  items: array$2(SessionCreateBatchItemWithInheritedWorkContextSchema).max(1e3)
238884
238901
  }).strict()
238885
- ]).superRefine((value, ctx) => {
238886
- value.items.forEach((item, index) => {
238887
- const expanded = {
238888
- ...value.defaults ?? {},
238889
- ...item
238890
- };
238891
- addAgentRoleOverrideIssues(expanded, ctx, [
238892
- "items",
238893
- index
238894
- ]);
238895
- });
238896
- });
238902
+ ]);
238897
238903
  const SessionCreateBatchPublishedItemSchema = object$1({
238898
238904
  ...SessionCreateBatchItemShape,
238899
238905
  useCurrentSessionAsParent: boolean().optional().describe("Create a child of the current Session; cannot be combined with workContext."),
@@ -239241,7 +239247,7 @@ ${prompt2}` : prompt2;
239241
239247
  useCurrentSessionAsParent = true;
239242
239248
  }
239243
239249
  const resolvedInput = {
239244
- ...input2,
239250
+ ...omitAgentRoleManualOverrides(input2),
239245
239251
  machineId: role.machineId,
239246
239252
  agentConfigId: role.agentConfigId,
239247
239253
  ...useCurrentSessionAsParent !== void 0 ? {
@@ -240268,6 +240274,15 @@ ${prompt2}` : prompt2;
240268
240274
  throw new Error("Create result did not preserve preallocated target ids.");
240269
240275
  }
240270
240276
  await withOperationStore((store) => store.markItemInputDurable(ctx.sessionId, args2.operationId, 0, materializationClaimToken));
240277
+ captureSessionCommandEvent("session_create_succeeded", {
240278
+ created_via: "mcp",
240279
+ mcp_create_mode: "single",
240280
+ session_id: result.sessionId,
240281
+ has_agent_role: Boolean(resolved.role),
240282
+ is_child_session: Boolean(result.parentSessionId)
240283
+ }, {
240284
+ distinctId: auth.machineId
240285
+ });
240271
240286
  }
240272
240287
  return snapshotOperation(ctx.sessionId, args2.operationId);
240273
240288
  });
@@ -240594,8 +240609,17 @@ ${prompt2}` : prompt2;
240594
240609
  options.chainDepth = invoking.chainDepth + 1;
240595
240610
  options.bypassSessionQuota = shouldBypassSessionQuota("session_create_many");
240596
240611
  options.workspaceMetaPrewriteSatisfied = true;
240597
- await createSessionResult(auth, workspace, manager, resolved.prompt, options, targetDispatchConfigs[index] ?? resolved.dispatchConfig);
240612
+ const result = await createSessionResult(auth, workspace, manager, resolved.prompt, options, targetDispatchConfigs[index] ?? resolved.dispatchConfig);
240598
240613
  await withOperationStore((store) => store.markItemInputDurable(ctx.sessionId, args2.operationId, index, materializationClaimToken));
240614
+ captureSessionCommandEvent("session_create_succeeded", {
240615
+ created_via: "mcp",
240616
+ mcp_create_mode: "batch",
240617
+ session_id: result.sessionId,
240618
+ has_agent_role: Boolean(resolved.role),
240619
+ is_child_session: Boolean(result.parentSessionId)
240620
+ }, {
240621
+ distinctId: auth.machineId
240622
+ });
240599
240623
  return markOperationItemInputDurable(storedItem);
240600
240624
  } catch {
240601
240625
  return storedItem;
@@ -240965,6 +240989,7 @@ ${prompt2}` : prompt2;
240965
240989
  };
240966
240990
  };
240967
240991
  function buildLodyMcpServer(config2 = {}) {
240992
+ initCliAnalytics();
240968
240993
  const server = new McpServer({
240969
240994
  name: "lody",
240970
240995
  version: "0.1.0"
@@ -240997,6 +241022,14 @@ ${prompt2}` : prompt2;
240997
241022
  const workspace = await resolveWorkspaceOrThrow$1(auth, getMcpWorkspaceId(ctx));
240998
241023
  return await withWorkspaceManager$1(auth, workspace, "mcp", async (manager) => {
240999
241024
  const result = await configureWorkspaceMcpServer(manager, workspace.id, auth.userId, args2);
241025
+ captureCli("workspace/mcp_created", {
241026
+ workspace_id: workspace.id,
241027
+ source: "mcp",
241028
+ transport: result.server.transport,
241029
+ enabled_by_default: result.server.enabledByDefault === true,
241030
+ has_description: Boolean(result.server.description),
241031
+ synced: result.synced
241032
+ });
241000
241033
  return jsonTextResult({
241001
241034
  ok: true,
241002
241035
  ...result,
@@ -241154,7 +241187,7 @@ ${lines2.join("\n")}` : ""}${suffix}`);
241154
241187
  });
241155
241188
  server.registerTool(SESSION_CREATE_TOOL_NAME, {
241156
241189
  title: "Create a Lody session",
241157
- description: "Start durable asynchronous work that creates a Lody session. Supply operationId; the result arrives automatically as a continuation, so do not poll operation_get. To recover an already accepted create without resending its prompt, send only operationId with resume=true. To use an Agent Role, pass its agentRoleId and do not pass machineId, agentConfigId, modelId, reasoningEffort, fastMode, or planMode; the current workspace catalog row supplies them. useCurrentSessionAsParent=true and workContext are mutually exclusive schema branches. Machine/config ids and runConfig values for non-Role creates come from lody_session_create_options. The wait field is temporary legacy compatibility only.",
241190
+ description: "Start durable asynchronous work that creates a Lody session. Supply operationId; the result arrives automatically as a continuation, so do not poll operation_get. To use an Agent Role, pass agentRoleId; the current workspace catalog row supplies the exact Machine, Agent config, model, reasoning, and permission mode. If manual machine or run-config fields are also present, the Role takes precedence and those fields are ignored. To recover an already accepted create without resending its prompt, send only operationId with resume=true. useCurrentSessionAsParent=true and workContext are mutually exclusive schema branches. Machine/config ids and runConfig values for non-Role creates come from lody_session_create_options. The wait field is temporary legacy compatibility only.",
241158
241191
  inputSchema: SessionCreateToolInputSchema
241159
241192
  }, async (input2) => {
241160
241193
  try {
@@ -241183,6 +241216,15 @@ ${lines2.join("\n")}` : ""}${suffix}`);
241183
241216
  bindAgentRoleCreateOptions(options, resolved.role);
241184
241217
  options.workspaceMetaPrewriteSatisfied = true;
241185
241218
  const result = await createSessionResult(auth, workspace, manager, resolved.prompt, options, resolved.dispatchConfig, buildStructuredOutputOptions(args2));
241219
+ captureSessionCommandEvent("session_create_succeeded", {
241220
+ created_via: "mcp",
241221
+ mcp_create_mode: "legacy_single",
241222
+ session_id: result.sessionId,
241223
+ has_agent_role: Boolean(resolved.role),
241224
+ is_child_session: Boolean(result.parentSessionId)
241225
+ }, {
241226
+ distinctId: auth.machineId
241227
+ });
241186
241228
  const response2 = {
241187
241229
  ok: true,
241188
241230
  sessionId: result.sessionId,
@@ -241261,7 +241303,7 @@ ${lines2.join("\n")}` : ""}${suffix}`);
241261
241303
  });
241262
241304
  server.registerTool(SESSION_CREATE_MANY_TOOL_NAME, {
241263
241305
  title: "Create multiple Lody sessions",
241264
- description: "Start one durable batch Operation for 1-20 Session creates. defaults and items shallow-merge; nested objects replace wholesale. Each item may use an agentRoleId from the workspace catalog; Role items cannot also override machine, agent config, or run config. Non-Role items accept modelId, reasoningEffort, fastMode, and planMode. Ordered item failures are isolated. Completion arrives automatically as one continuation, so do not poll operation_get in a loop.",
241306
+ description: "Start one durable batch Operation for 1-20 Session creates. defaults and items shallow-merge; nested objects replace wholesale. Each item may use an agentRoleId from the workspace catalog. When a Role item also includes manual machine, agent config, or run-config fields, the Role takes precedence and those fields are ignored. Non-Role items accept modelId, reasoningEffort, fastMode, and planMode. Ordered item failures are isolated. Completion arrives automatically as one continuation, so do not poll operation_get in a loop.",
241265
241307
  inputSchema: SessionCreateManyToolInputSchema
241266
241308
  }, async (input2) => {
241267
241309
  try {
@@ -243627,6 +243669,13 @@ Other machines will not see it until this machine syncs.`);
243627
243669
  createdBy: userId
243628
243670
  };
243629
243671
  const result = await upsertWorkspaceMcpCatalogEntry(repo, workspaceId, entry, writeOptions);
243672
+ captureCli("workspace/mcp_created", {
243673
+ workspace_id: workspaceId,
243674
+ source: "cli",
243675
+ transport: entry.transport,
243676
+ enabled_by_default: entry.enabledByDefault === true,
243677
+ has_description: Boolean(entry.description)
243678
+ });
243630
243679
  reportWrite(options, {
243631
243680
  workspaceId,
243632
243681
  server: entry
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lody",
3
- "version": "0.86.2",
3
+ "version": "0.87.0",
4
4
  "description": "Lody Agent CLI tool for managing remote command execution",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",