@moxt-ai/mobius 0.0.356 → 0.0.357

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 (36) hide show
  1. package/LICENSE +202 -0
  2. package/dist/cli.js +179 -106
  3. package/dist/cli.js.map +3 -3
  4. package/dist/engine/acp/adapter/codex/adapter.d.ts.map +1 -1
  5. package/dist/engine/acp/adapter/codex/adapter.js +6 -4
  6. package/dist/engine/acp/adapter/codex/adapter.js.map +1 -1
  7. package/dist/engine/acp/adapter/session-events.d.ts.map +1 -1
  8. package/dist/engine/acp/adapter/session-events.js +57 -56
  9. package/dist/engine/acp/adapter/session-events.js.map +1 -1
  10. package/dist/engine/acp/content/content-externalizer.d.ts +8 -2
  11. package/dist/engine/acp/content/content-externalizer.d.ts.map +1 -1
  12. package/dist/engine/acp/content/content-externalizer.js +23 -6
  13. package/dist/engine/acp/content/content-externalizer.js.map +1 -1
  14. package/dist/engine/acp/harness/codex/history.d.ts.map +1 -1
  15. package/dist/engine/acp/harness/codex/history.js +25 -23
  16. package/dist/engine/acp/harness/codex/history.js.map +1 -1
  17. package/dist/engine/agent/runtime-agents.d.ts +1 -1
  18. package/dist/engine/agent/runtime-agents.d.ts.map +1 -1
  19. package/dist/engine/agent/runtime-agents.js +2 -2
  20. package/dist/engine/agent/runtime-agents.js.map +1 -1
  21. package/dist/engine/runtime/run.d.ts.map +1 -1
  22. package/dist/engine/runtime/run.js +1 -1
  23. package/dist/engine/runtime/run.js.map +1 -1
  24. package/dist/engine/session/content/external-content-store.d.ts +4 -0
  25. package/dist/engine/session/content/external-content-store.d.ts.map +1 -1
  26. package/dist/engine/session/content/external-content-store.js +31 -1
  27. package/dist/engine/session/content/external-content-store.js.map +1 -1
  28. package/dist/engine/session/prompt/prompt-lifecycle.d.ts.map +1 -1
  29. package/dist/engine/session/prompt/prompt-lifecycle.js +6 -1
  30. package/dist/engine/session/prompt/prompt-lifecycle.js.map +1 -1
  31. package/dist/engine/session/turn/execution-receipts.d.ts.map +1 -1
  32. package/dist/engine/session/turn/execution-receipts.js +14 -4
  33. package/dist/engine/session/turn/execution-receipts.js.map +1 -1
  34. package/dist/engine.js +176 -106
  35. package/dist/engine.js.map +3 -3
  36. package/package.json +4 -4
package/dist/engine.js CHANGED
@@ -20720,6 +20720,7 @@ var lifecycleMetadataSchema = external_exports.discriminatedUnion("kind", [
20720
20720
  external_exports.object({
20721
20721
  ...lifecycleBase,
20722
20722
  kind: external_exports.literal("prompt.completed"),
20723
+ execution: external_exports.literal("not-started").optional(),
20723
20724
  stopReason: shortString
20724
20725
  }).readonly(),
20725
20726
  external_exports.object({
@@ -70232,40 +70233,42 @@ async function replayCodexHistory(client2, threadId, emit, signal) {
70232
70233
  continue;
70233
70234
  }
70234
70235
  if (item.type === "userMessage") {
70235
- const text2 = item.content.flatMap((content) => {
70236
+ const contents = item.content.flatMap((content) => {
70236
70237
  if (typeof content !== "object" || content === null) {
70237
70238
  return [];
70238
70239
  }
70239
70240
  const type = Reflect.get(content, "type");
70240
- const text3 = Reflect.get(content, "text");
70241
+ const text2 = Reflect.get(content, "text");
70241
70242
  const url2 = Reflect.get(content, "url");
70242
70243
  if (type === "image" && typeof url2 === "string") {
70243
70244
  return [`[@image](${url2})`];
70244
70245
  }
70245
- return type === "text" && typeof text3 === "string" ? [text3] : [];
70246
- }).join("");
70247
- const history = await embedLocalHistoryImages(
70248
- { status: "available", truncated: false, messages: [{ role: "user", text: text2 }] },
70249
- deadline
70250
- );
70251
- if (history.status !== "available") {
70252
- continue;
70253
- }
70254
- const message = history.messages[0];
70255
- for (const image of message?.images ?? []) {
70256
- await emit({
70257
- content: { type: "image", data: image.data, mimeType: image.mimeType },
70258
- messageId: item.id,
70259
- sessionUpdate: "user_message_chunk"
70260
- });
70261
- }
70262
- const restoredText = message?.text ?? "";
70263
- if (restoredText.length > 0) {
70264
- await emit({
70265
- content: { text: restoredText, type: "text" },
70266
- messageId: item.id,
70267
- sessionUpdate: "user_message_chunk"
70268
- });
70246
+ return type === "text" && typeof text2 === "string" ? [text2] : [];
70247
+ });
70248
+ for (const text2 of contents) {
70249
+ const history = await embedLocalHistoryImages(
70250
+ { status: "available", truncated: false, messages: [{ role: "user", text: text2 }] },
70251
+ deadline
70252
+ );
70253
+ if (history.status !== "available") {
70254
+ continue;
70255
+ }
70256
+ const message = history.messages[0];
70257
+ for (const image of message?.images ?? []) {
70258
+ await emit({
70259
+ content: { type: "image", data: image.data, mimeType: image.mimeType },
70260
+ messageId: item.id,
70261
+ sessionUpdate: "user_message_chunk"
70262
+ });
70263
+ }
70264
+ const restoredText = message?.text ?? "";
70265
+ if (restoredText.length > 0) {
70266
+ await emit({
70267
+ content: { text: restoredText, type: "text" },
70268
+ messageId: item.id,
70269
+ sessionUpdate: "user_message_chunk"
70270
+ });
70271
+ }
70269
70272
  }
70270
70273
  } else if (item.type === "agentMessage") {
70271
70274
  await emit(agentMessageUpdate(item.id, item.text));
@@ -73327,14 +73330,21 @@ function toToolKind(toolName) {
73327
73330
  }
73328
73331
 
73329
73332
  // src/engine/acp/harness/pi/upstream/dist/acp/translate/pi-messages.js
73330
- function normalizePiMessageText(content) {
73333
+ var userContentSchema = external_exports.discriminatedUnion("type", [
73334
+ external_exports.object({ type: external_exports.literal("text"), text: external_exports.string() }),
73335
+ external_exports.object({ type: external_exports.literal("image"), data: external_exports.string(), mimeType: external_exports.string() })
73336
+ ]);
73337
+ function piUserMessageContent(content) {
73331
73338
  if (typeof content === "string") {
73332
- return content;
73339
+ return content.length === 0 ? [] : [{ type: "text", text: content }];
73333
73340
  }
73334
73341
  if (!Array.isArray(content)) {
73335
- return "";
73342
+ return [];
73336
73343
  }
73337
- return content.map((c) => c?.type === "text" && typeof c.text === "string" ? c.text : "").filter(Boolean).join("");
73344
+ return content.flatMap((value) => {
73345
+ const parsed = userContentSchema.safeParse(value);
73346
+ return parsed.success ? [parsed.data] : [];
73347
+ });
73338
73348
  }
73339
73349
  function normalizePiAssistantText(content) {
73340
73350
  if (!Array.isArray(content)) {
@@ -74427,13 +74437,13 @@ ${JSON.stringify(stats, null, 2)}`;
74427
74437
  const m = entry.message;
74428
74438
  const role = String(m?.role ?? "");
74429
74439
  if (role === "user") {
74430
- const text2 = normalizePiMessageText(m?.content);
74431
- if (text2) {
74440
+ this.conn.startUserMessage();
74441
+ for (const content of piUserMessageContent(m.content)) {
74432
74442
  await this.conn.sessionUpdate({
74433
74443
  sessionId: session.sessionId,
74434
74444
  update: {
74435
74445
  sessionUpdate: "user_message_chunk",
74436
- content: { type: "text", text: text2 }
74446
+ content
74437
74447
  }
74438
74448
  });
74439
74449
  }
@@ -76262,63 +76272,64 @@ var AdapterSessionConnection = class {
76262
76272
  const externalized = await externalizer.update(options.sessionId(), notification.update, signal);
76263
76273
  signal.throwIfAborted();
76264
76274
  const update = externalized.agent;
76265
- const eventUpdate = externalized.event;
76266
- const backgroundTask = update._meta?.["mobius.backgroundTask"];
76267
- const payload = decodeAcpSessionUpdate(
76268
- JSON.stringify(
76269
- normalizeEvent(
76270
- (update.sessionUpdate === "tool_call" || update.sessionUpdate === "tool_call_update") && backgroundTask !== void 0 && typeof eventUpdate === "object" && eventUpdate !== null ? { ...eventUpdate, backgroundTask } : eventUpdate
76275
+ for (const eventUpdate of externalized.events) {
76276
+ const backgroundTask = update._meta?.["mobius.backgroundTask"];
76277
+ const payload = decodeAcpSessionUpdate(
76278
+ JSON.stringify(
76279
+ normalizeEvent(
76280
+ (update.sessionUpdate === "tool_call" || update.sessionUpdate === "tool_call_update") && backgroundTask !== void 0 && typeof eventUpdate === "object" && eventUpdate !== null ? { ...eventUpdate, backgroundTask } : eventUpdate
76281
+ )
76271
76282
  )
76272
- )
76273
- );
76274
- let event;
76275
- switch (update.sessionUpdate) {
76276
- case "available_commands_update":
76277
- event = {
76278
- kind: "commands",
76279
- payload,
76280
- commands: update.availableCommands.slice(0, 500).map((command) => ({
76281
- description: truncate(command.description, 500),
76282
- inputHint: truncate(command.input?.hint ?? "", 255),
76283
- name: truncate(command.name, 128)
76284
- })).filter((command) => command.name.length > 0)
76285
- };
76286
- break;
76287
- case "config_option_update":
76288
- event = {
76289
- kind: "configuration",
76290
- payload,
76291
- configOptions: protocol.normalizeUpdate(options.configOptions(), update.configOptions)
76292
- };
76293
- break;
76294
- case "current_mode_update":
76295
- event = { kind: "mode", payload, modeId: update.currentModeId };
76296
- break;
76297
- case "session_info_update": {
76298
- const changes = [];
76299
- if (Object.hasOwn(update, "title")) {
76300
- changes.push({ kind: "title", value: truncate(update.title ?? "", 500) });
76301
- }
76302
- if (Object.hasOwn(update, "updatedAt")) {
76303
- changes.push({ kind: "updated-at", value: truncate(update.updatedAt ?? "", 128) });
76283
+ );
76284
+ let event;
76285
+ switch (update.sessionUpdate) {
76286
+ case "available_commands_update":
76287
+ event = {
76288
+ kind: "commands",
76289
+ payload,
76290
+ commands: update.availableCommands.slice(0, 500).map((command) => ({
76291
+ description: truncate(command.description, 500),
76292
+ inputHint: truncate(command.input?.hint ?? "", 255),
76293
+ name: truncate(command.name, 128)
76294
+ })).filter((command) => command.name.length > 0)
76295
+ };
76296
+ break;
76297
+ case "config_option_update":
76298
+ event = {
76299
+ kind: "configuration",
76300
+ payload,
76301
+ configOptions: protocol.normalizeUpdate(options.configOptions(), update.configOptions)
76302
+ };
76303
+ break;
76304
+ case "current_mode_update":
76305
+ event = { kind: "mode", payload, modeId: update.currentModeId };
76306
+ break;
76307
+ case "session_info_update": {
76308
+ const changes = [];
76309
+ if (Object.hasOwn(update, "title")) {
76310
+ changes.push({ kind: "title", value: truncate(update.title ?? "", 500) });
76311
+ }
76312
+ if (Object.hasOwn(update, "updatedAt")) {
76313
+ changes.push({ kind: "updated-at", value: truncate(update.updatedAt ?? "", 128) });
76314
+ }
76315
+ event = { kind: "info", payload, changes };
76316
+ break;
76304
76317
  }
76305
- event = { kind: "info", payload, changes };
76306
- break;
76318
+ case "user_message_chunk":
76319
+ case "agent_message_chunk":
76320
+ case "agent_thought_chunk":
76321
+ case "tool_call":
76322
+ case "tool_call_update":
76323
+ case "plan":
76324
+ case "plan_update":
76325
+ case "plan_removed":
76326
+ event = { kind: "content", payload };
76327
+ break;
76328
+ default:
76329
+ event = { kind: "other", payload };
76307
76330
  }
76308
- case "user_message_chunk":
76309
- case "agent_message_chunk":
76310
- case "agent_thought_chunk":
76311
- case "tool_call":
76312
- case "tool_call_update":
76313
- case "plan":
76314
- case "plan_update":
76315
- case "plan_removed":
76316
- event = { kind: "content", payload };
76317
- break;
76318
- default:
76319
- event = { kind: "other", payload };
76331
+ await options.onEvent(event);
76320
76332
  }
76321
- await options.onEvent(event);
76322
76333
  });
76323
76334
  this.events = this.#updates;
76324
76335
  const client2 = {
@@ -83084,10 +83095,12 @@ var CodexAdapter = class {
83084
83095
  request.nativeSessionId,
83085
83096
  async (update) => {
83086
83097
  const externalized = await this.#contentExternalizer.update(sessionId, update, signal);
83087
- events.push({
83088
- type: "acp",
83089
- payload: decodeAcpSessionUpdate(JSON.stringify(fitCodexToolEvent(externalized.event)))
83090
- });
83098
+ for (const event of externalized.events) {
83099
+ events.push({
83100
+ type: "acp",
83101
+ payload: decodeAcpSessionUpdate(JSON.stringify(fitCodexToolEvent(event)))
83102
+ });
83103
+ }
83091
83104
  },
83092
83105
  signal
83093
83106
  );
@@ -83208,8 +83221,18 @@ function isRecord7(value) {
83208
83221
  }
83209
83222
  var AcpContentExternalizer = class {
83210
83223
  #store;
83211
- constructor(store) {
83224
+ #linkedResources;
83225
+ constructor(store, linkedResources = () => []) {
83212
83226
  this.#store = store;
83227
+ this.#linkedResources = linkedResources;
83228
+ }
83229
+ #resourceLinks(text2) {
83230
+ return this.#linkedResources(text2).map((resource) => ({
83231
+ type: "resource_link",
83232
+ uri: resource.uri,
83233
+ name: resource.name,
83234
+ mimeType: resource.mediaType
83235
+ }));
83213
83236
  }
83214
83237
  async #content(sessionId, content, signal) {
83215
83238
  switch (content.type) {
@@ -83361,7 +83384,7 @@ var AcpContentExternalizer = class {
83361
83384
  const hasContent = update.content !== void 0 && update.content !== null || agentContent.length > 0;
83362
83385
  return {
83363
83386
  agent: { ...update, ...meta3, ...output2, ...hasContent ? { content: agentContent } : {} },
83364
- event: { ...update, ...meta3, ...output2, ...hasContent ? { content: eventContent } : {} }
83387
+ events: [{ ...update, ...meta3, ...output2, ...hasContent ? { content: eventContent } : {} }]
83365
83388
  };
83366
83389
  }
83367
83390
  update = async (sessionId, update, signal) => {
@@ -83373,7 +83396,12 @@ var AcpContentExternalizer = class {
83373
83396
  const content = await this.#content(sessionId, update.content, signal);
83374
83397
  return {
83375
83398
  agent: { ...update, content: content.agent },
83376
- event: { ...update, content: content.event }
83399
+ events: [
83400
+ { ...update, content: content.event },
83401
+ // Keep native text intact. Only add cards for exact file URLs whose
83402
+ // original upload metadata is still known; arbitrary context stays text.
83403
+ ...update.sessionUpdate === "user_message_chunk" && update.content.type === "text" ? this.#resourceLinks(update.content.text).map((content2) => ({ ...update, content: content2 })) : []
83404
+ ]
83377
83405
  };
83378
83406
  }
83379
83407
  case "tool_call":
@@ -83381,14 +83409,14 @@ var AcpContentExternalizer = class {
83381
83409
  return await this.#toolUpdate(sessionId, update, signal);
83382
83410
  case "compaction_update": {
83383
83411
  if (update.summary === void 0 || update.summary === null) {
83384
- return { agent: update, event: update };
83412
+ return { agent: update, events: [update] };
83385
83413
  }
83386
83414
  const content = await Promise.all(
83387
83415
  update.summary.map(async (item) => await this.#content(sessionId, item, signal))
83388
83416
  );
83389
83417
  return {
83390
83418
  agent: { ...update, summary: content.map((item) => item.agent) },
83391
- event: { ...update, summary: content.map((item) => item.event) }
83419
+ events: [{ ...update, summary: content.map((item) => item.event) }]
83392
83420
  };
83393
83421
  }
83394
83422
  case "plan":
@@ -83399,16 +83427,16 @@ var AcpContentExternalizer = class {
83399
83427
  case "config_option_update":
83400
83428
  case "session_info_update":
83401
83429
  case "usage_update":
83402
- return { agent: update, event: update };
83430
+ return { agent: update, events: [update] };
83403
83431
  }
83404
83432
  };
83405
83433
  };
83406
83434
 
83407
83435
  // src/engine/agent/runtime-agents.ts
83408
- function createRuntimeAdapterRegistry(adapters, processes, externalize) {
83436
+ function createRuntimeAdapterRegistry(adapters, processes, externalize, linkedResources) {
83409
83437
  return new AdapterRegistry(adapters, {
83410
83438
  processes,
83411
- contentExternalizer: new AcpContentExternalizer(externalize)
83439
+ contentExternalizer: new AcpContentExternalizer(externalize, linkedResources)
83412
83440
  });
83413
83441
  }
83414
83442
  var RuntimeAgentDependencies = class {
@@ -87674,7 +87702,12 @@ var PromptLifecycle = class {
87674
87702
  }
87675
87703
  const recorded = await this.#recordLifecycle(sessionId, {
87676
87704
  type: "lifecycle-metadata",
87677
- payload: { kind: "prompt.completed", commandId, stopReason }
87705
+ payload: {
87706
+ kind: "prompt.completed",
87707
+ commandId,
87708
+ stopReason,
87709
+ ...request.phase === "queued" ? { execution: "not-started" } : {}
87710
+ }
87678
87711
  });
87679
87712
  if (!recorded) {
87680
87713
  this.#sink.publishOperationFailure(
@@ -87841,7 +87874,7 @@ var ExecutionReceipts = class {
87841
87874
  /** Started identities remain authoritative after their detailed receipt is evicted. */
87842
87875
  hasStarted = (commandId, sessionId) => {
87843
87876
  const receipt = this.#executions.get(commandId);
87844
- return receipt === void 0 ? this.#table.identity(commandId)?.sessionId === sessionId : receipt.sessionId === sessionId && receipt.started;
87877
+ return receipt?.sessionId === sessionId && receipt.started || this.#table.identity(commandId)?.sessionId === sessionId;
87845
87878
  };
87846
87879
  /** Reserves an unseen command for cancellation before a delayed prompt can claim its identity. */
87847
87880
  recordCancellationBeforeAdmission = (commandId, sessionId) => {
@@ -87849,9 +87882,16 @@ var ExecutionReceipts = class {
87849
87882
  if (existing !== void 0) {
87850
87883
  return existing.sessionId === sessionId ? this.outcome(commandId) : void 0;
87851
87884
  }
87885
+ const identity2 = this.#table.identity(commandId);
87886
+ if (identity2 !== void 0) {
87887
+ return identity2.sessionId === sessionId ? {
87888
+ type: "lifecycle-metadata",
87889
+ payload: { kind: "prompt.completed", commandId, stopReason: "cancelled" }
87890
+ } : void 0;
87891
+ }
87852
87892
  const outcome = {
87853
87893
  type: "lifecycle-metadata",
87854
- payload: { kind: "prompt.completed", commandId, stopReason: "cancelled" }
87894
+ payload: { kind: "prompt.completed", commandId, stopReason: "cancelled", execution: "not-started" }
87855
87895
  };
87856
87896
  const commandHash = createHash7("sha256").update(
87857
87897
  encodeSessionCancelBeforeStartCommand({
@@ -88644,6 +88684,7 @@ function buildSessionLayer(options) {
88644
88684
  // src/engine/session/content/external-content-store.ts
88645
88685
  import { createHash as createHash8 } from "node:crypto";
88646
88686
  import { extname as extname2, join as join29 } from "node:path";
88687
+ import { pathToFileURL as pathToFileURL3 } from "node:url";
88647
88688
 
88648
88689
  // src/engine/storage/files/scratch-directory.ts
88649
88690
  import { randomUUID as randomUUID17 } from "node:crypto";
@@ -88789,8 +88830,12 @@ var LocalExternalContentStore = class {
88789
88830
  }
88790
88831
  await this.#expire();
88791
88832
  active.throwIfAborted();
88792
- const path7 = await this.#scratch.write(kind, contentFileName(key, metadata.name), bytes, active);
88833
+ const writtenPath = await this.#scratch.write(kind, contentFileName(key, metadata.name), bytes, active);
88834
+ const path7 = await this.#scratch.resolveFile(writtenPath);
88793
88835
  active.throwIfAborted();
88836
+ if (path7 === null) {
88837
+ throw new OperationError("content_unavailable", "The cached content file is unavailable");
88838
+ }
88794
88839
  const retained = this.#entries.get(request);
88795
88840
  this.#entries.set(request, {
88796
88841
  byteLength: bytes.byteLength,
@@ -88831,6 +88876,30 @@ var LocalExternalContentStore = class {
88831
88876
  const bytes = await this.#scratch.read(resolved.path, active);
88832
88877
  return bytes === null ? null : { ...resolved, bytes };
88833
88878
  });
88879
+ /** Metadata for exact, still-cached file URLs. This does not grant content access. */
88880
+ linkedResources = (text2) => {
88881
+ if (!text2.includes("file:///")) {
88882
+ return [];
88883
+ }
88884
+ const resources = [];
88885
+ for (const [request, entry] of this.#entries) {
88886
+ if (!request.startsWith("mobius-content:resource:") || entry.expiresAt <= Date.now()) {
88887
+ continue;
88888
+ }
88889
+ const uri = pathToFileURL3(entry.path).href;
88890
+ for (let index2 = text2.indexOf(uri); index2 >= 0; index2 = text2.indexOf(uri, index2 + uri.length)) {
88891
+ const following = text2[index2 + uri.length];
88892
+ if (following === void 0 || /[\s<>"')\]]/u.test(following)) {
88893
+ resources.push({
88894
+ index: index2,
88895
+ resource: { uri, mediaType: entry.mediaType, name: entry.name, path: entry.path }
88896
+ });
88897
+ break;
88898
+ }
88899
+ }
88900
+ }
88901
+ return resources.sort((left, right) => left.index - right.index).map(({ resource }) => resource);
88902
+ };
88834
88903
  [Symbol.asyncDispose] = async () => {
88835
88904
  clearInterval(this.#expirationTimer);
88836
88905
  this.#lifetime.abort();
@@ -91068,7 +91137,7 @@ var sessionPromptCommand = {
91068
91137
  // package.json
91069
91138
  var package_default = {
91070
91139
  name: "@moxt-ai/mobius",
91071
- version: "0.0.356",
91140
+ version: "0.0.357",
91072
91141
  description: "Mobius CLI for pairing and running local AI agent services.",
91073
91142
  type: "module",
91074
91143
  bin: {
@@ -91143,8 +91212,8 @@ var package_default = {
91143
91212
  ws: "^8.21.3"
91144
91213
  },
91145
91214
  keywords: [],
91146
- author: "",
91147
- license: "ISC",
91215
+ author: "Moxt",
91216
+ license: "Apache-2.0",
91148
91217
  bugs: {
91149
91218
  url: "https://github.com/moxt-ai/mobius/issues"
91150
91219
  },
@@ -92333,7 +92402,8 @@ async function runRuntime(config2, parentSignal, timer = systemTimer, options =
92333
92402
  const adapters = createRuntimeAdapterRegistry(
92334
92403
  config2.adapters,
92335
92404
  processes,
92336
- (sessionId, content, contentSignal) => externalContent.put({ ...content, sessionId, signal: contentSignal })
92405
+ (sessionId, content, contentSignal) => externalContent.put({ ...content, sessionId, signal: contentSignal }),
92406
+ externalContent.linkedResources
92337
92407
  );
92338
92408
  const resolveAgent = async (agentId, resolveSignal, sessionId) => {
92339
92409
  const catalogAgent = await resolveCatalogAgent(config2, agentId, resolveSignal);