@aiden-ade/sandbox-agent 0.1.82 → 0.1.83

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.cjs +509 -205
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -1477,30 +1477,30 @@ var require_permessage_deflate = __commonJS({
1477
1477
  */
1478
1478
  acceptAsServer(offers) {
1479
1479
  const opts = this._options;
1480
- const accepted = offers.find((params) => {
1480
+ const accepted2 = offers.find((params) => {
1481
1481
  if (opts.serverNoContextTakeover === false && params.server_no_context_takeover || params.server_max_window_bits && (opts.serverMaxWindowBits === false || typeof opts.serverMaxWindowBits === "number" && opts.serverMaxWindowBits > params.server_max_window_bits) || typeof opts.clientMaxWindowBits === "number" && !params.client_max_window_bits) {
1482
1482
  return false;
1483
1483
  }
1484
1484
  return true;
1485
1485
  });
1486
- if (!accepted) {
1486
+ if (!accepted2) {
1487
1487
  throw new Error("None of the extension offers can be accepted");
1488
1488
  }
1489
1489
  if (opts.serverNoContextTakeover) {
1490
- accepted.server_no_context_takeover = true;
1490
+ accepted2.server_no_context_takeover = true;
1491
1491
  }
1492
1492
  if (opts.clientNoContextTakeover) {
1493
- accepted.client_no_context_takeover = true;
1493
+ accepted2.client_no_context_takeover = true;
1494
1494
  }
1495
1495
  if (typeof opts.serverMaxWindowBits === "number") {
1496
- accepted.server_max_window_bits = opts.serverMaxWindowBits;
1496
+ accepted2.server_max_window_bits = opts.serverMaxWindowBits;
1497
1497
  }
1498
1498
  if (typeof opts.clientMaxWindowBits === "number") {
1499
- accepted.client_max_window_bits = opts.clientMaxWindowBits;
1500
- } else if (accepted.client_max_window_bits === true || opts.clientMaxWindowBits === false) {
1501
- delete accepted.client_max_window_bits;
1499
+ accepted2.client_max_window_bits = opts.clientMaxWindowBits;
1500
+ } else if (accepted2.client_max_window_bits === true || opts.clientMaxWindowBits === false) {
1501
+ delete accepted2.client_max_window_bits;
1502
1502
  }
1503
- return accepted;
1503
+ return accepted2;
1504
1504
  }
1505
1505
  /**
1506
1506
  * Accept the extension negotiation response.
@@ -15240,7 +15240,7 @@ function describeError(error61) {
15240
15240
  }
15241
15241
 
15242
15242
  // src/version.ts
15243
- var AGENT_VERSION = "0.1.82";
15243
+ var AGENT_VERSION = "0.1.83";
15244
15244
 
15245
15245
  // src/daemon-worktree.ts
15246
15246
  var import_node_child_process3 = require("child_process");
@@ -40308,7 +40308,7 @@ function resolveAntigravityParentTranscriptPath(conversationId, context) {
40308
40308
  "transcript.jsonl"
40309
40309
  );
40310
40310
  }
40311
- function syncAntigravityParentThinkingFromTranscript(context, state) {
40311
+ function syncAntigravityParentTranscript(context, state) {
40312
40312
  const parent = state.antigravityParentTranscript;
40313
40313
  if (!parent?.transcriptPath) return 0;
40314
40314
  const lines = readAntigravityTranscriptLines(parent.transcriptPath);
@@ -40329,6 +40329,14 @@ function syncAntigravityParentThinkingFromTranscript(context, state) {
40329
40329
  }
40330
40330
  processedLineCount = lineIndex + 1;
40331
40331
  if (stringField2(entry, "source") !== "MODEL") continue;
40332
+ const content = stringField2(entry, "content");
40333
+ if (content?.includes("[diff_block_start]") && isAntigravityToolResultEntry(entry) && typeof entry.step_index === "number") {
40334
+ emittedEventCount += 1;
40335
+ void context.presenter.onToolResult?.(
40336
+ liveStepId({ ...entry, conversation_id: parent.conversationId }),
40337
+ stripAntigravityToolResultEnvelope(content)
40338
+ );
40339
+ }
40332
40340
  const thinking = stringField2(entry, "thinking") ?? stringField2(entry, "thinking_delta");
40333
40341
  if (!thinking) continue;
40334
40342
  emittedEventCount += 1;
@@ -40338,7 +40346,7 @@ function syncAntigravityParentThinkingFromTranscript(context, state) {
40338
40346
  return emittedEventCount;
40339
40347
  }
40340
40348
  function syncAntigravitySubagentEventsFromTranscripts(context, state) {
40341
- let emittedEventCount = syncAntigravityParentThinkingFromTranscript(context, state);
40349
+ let emittedEventCount = syncAntigravityParentTranscript(context, state);
40342
40350
  for (const [launcherId, task] of state.antigravitySubagentTranscripts ?? []) {
40343
40351
  if (!task.transcriptPath) continue;
40344
40352
  const replay = syncAntigravitySubagentTranscript(
@@ -42958,10 +42966,7 @@ var parseCopilotStructuredLine = createStructuredLineParser(
42958
42966
  );
42959
42967
  function buildCopilotCliArgs(context, prompt, defaultArgs = []) {
42960
42968
  const args = [
42961
- "--autopilot",
42962
42969
  "--yolo",
42963
- "--max-autopilot-continues",
42964
- "20",
42965
42970
  "-s",
42966
42971
  "--stream",
42967
42972
  "on",
@@ -44771,36 +44776,6 @@ function createKimiCliBackend(command = "kimi-cli", defaultArgs = []) {
44771
44776
  }
44772
44777
  };
44773
44778
  }
44774
- var AUTONOMOUS_OPENCODE_CONFIG_CONTENT = JSON.stringify({
44775
- $schema: "https://opencode.ai/config.json",
44776
- permission: "allow"
44777
- });
44778
- function buildAutonomousOpenCodeConfigContent(existing) {
44779
- if (!existing?.trim()) return AUTONOMOUS_OPENCODE_CONFIG_CONTENT;
44780
- try {
44781
- const parsed = JSON.parse(existing);
44782
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
44783
- return JSON.stringify({
44784
- ...parsed,
44785
- permission: "allow"
44786
- });
44787
- }
44788
- } catch {
44789
- }
44790
- return AUTONOMOUS_OPENCODE_CONFIG_CONTENT;
44791
- }
44792
- function withAutonomousOpenCodePermissions(context) {
44793
- if (shouldUseReadOnlyRuntimePermissions(context.config)) return context;
44794
- return {
44795
- ...context,
44796
- env: {
44797
- ...context.env,
44798
- OPENCODE_CONFIG_CONTENT: buildAutonomousOpenCodeConfigContent(
44799
- context.env.OPENCODE_CONFIG_CONTENT
44800
- )
44801
- }
44802
- };
44803
- }
44804
44779
  function getRecord(value2) {
44805
44780
  return typeof value2 === "object" && value2 !== null ? value2 : null;
44806
44781
  }
@@ -45094,7 +45069,6 @@ function createOpencodeCliBackend(command = "opencode", defaultArgs = []) {
45094
45069
  context.cwd
45095
45070
  );
45096
45071
  try {
45097
- const runContext = withAutonomousOpenCodePermissions(context);
45098
45072
  return await createGenericCliBackend({
45099
45073
  kind: "opencode_cli",
45100
45074
  supportTier: "structured",
@@ -45103,7 +45077,7 @@ function createOpencodeCliBackend(command = "opencode", defaultArgs = []) {
45103
45077
  buildArgs: (ctx, prompt) => {
45104
45078
  const args = ["run", "--format", "json", "--thinking"];
45105
45079
  if (!shouldUseReadOnlyRuntimePermissions(ctx.config)) {
45106
- args.push("--dangerously-skip-permissions");
45080
+ args.push("--auto");
45107
45081
  }
45108
45082
  const resumeId = ctx.config.runtimeSessionId?.trim() || ctx.config.providerSessionId?.trim();
45109
45083
  if (resumeId) args.push("--session", resumeId);
@@ -45124,7 +45098,7 @@ function createOpencodeCliBackend(command = "opencode", defaultArgs = []) {
45124
45098
  return ctx.config.mode === "plan" ? buildPlanModePrefix(base) : base;
45125
45099
  },
45126
45100
  parseStructuredLine: parseOpencodeStructuredLine
45127
- }).run(runContext);
45101
+ }).run(context);
45128
45102
  } finally {
45129
45103
  cleanup();
45130
45104
  cleanupFiles();
@@ -45132,6 +45106,36 @@ function createOpencodeCliBackend(command = "opencode", defaultArgs = []) {
45132
45106
  }
45133
45107
  };
45134
45108
  }
45109
+ var AUTONOMOUS_OPENCODE_CONFIG_CONTENT = JSON.stringify({
45110
+ $schema: "https://opencode.ai/config.json",
45111
+ permission: "allow"
45112
+ });
45113
+ function buildAutonomousOpenCodeConfigContent(existing) {
45114
+ if (!existing?.trim()) return AUTONOMOUS_OPENCODE_CONFIG_CONTENT;
45115
+ try {
45116
+ const parsed = JSON.parse(existing);
45117
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
45118
+ return JSON.stringify({
45119
+ ...parsed,
45120
+ permission: "allow"
45121
+ });
45122
+ }
45123
+ } catch {
45124
+ }
45125
+ return AUTONOMOUS_OPENCODE_CONFIG_CONTENT;
45126
+ }
45127
+ function withAutonomousOpenCodePermissions(context) {
45128
+ if (shouldUseReadOnlyRuntimePermissions(context.config)) return context;
45129
+ return {
45130
+ ...context,
45131
+ env: {
45132
+ ...context.env,
45133
+ OPENCODE_CONFIG_CONTENT: buildAutonomousOpenCodeConfigContent(
45134
+ context.env.OPENCODE_CONFIG_CONTENT
45135
+ )
45136
+ }
45137
+ };
45138
+ }
45135
45139
  function parseSseEventBlock(block) {
45136
45140
  const dataLines = block.split("\n").map((line) => line.endsWith("\r") ? line.slice(0, -1) : line).filter((line) => line.startsWith("data:")).map((line) => line.slice("data:".length).replace(/^ /, ""));
45137
45141
  if (dataLines.length === 0) return null;
@@ -46077,7 +46081,7 @@ function defaultSelectedModelForBackend(backendKind) {
46077
46081
  case "droid_cli":
46078
46082
  return DROID_DEFAULT_MODEL;
46079
46083
  case "copilot_cli":
46080
- return "claude-sonnet-4.5";
46084
+ return void 0;
46081
46085
  case "kimi_cli":
46082
46086
  return void 0;
46083
46087
  case "grok_cli":
@@ -46879,6 +46883,7 @@ ${output2.stderr}`;
46879
46883
  var import_node_crypto2 = require("crypto");
46880
46884
  var import_node_fs7 = require("fs");
46881
46885
  var import_node_path6 = require("path");
46886
+ var import_node_util = require("util");
46882
46887
  var OUTBOX_VERSION = 1;
46883
46888
  var MAX_INTERMEDIATE_EVENTS_PER_RUN = 200;
46884
46889
  var MAX_ENCRYPTED_OUTBOX_BYTES = 64 * 1024 * 1024;
@@ -47040,6 +47045,7 @@ var EncryptedEventOutbox = class _EncryptedEventOutbox {
47040
47045
  inFlightEventId = null;
47041
47046
  ackTimer = null;
47042
47047
  retryTimer = null;
47048
+ acknowledgementWaiters = /* @__PURE__ */ new Map();
47043
47049
  /** Latch so a persistent disk failure logs once per streak, not per event. */
47044
47050
  persistFailureLogged = false;
47045
47051
  /**
@@ -47058,7 +47064,7 @@ var EncryptedEventOutbox = class _EncryptedEventOutbox {
47058
47064
  maxIntermediateEventsPerRun;
47059
47065
  enqueue(conversationId, runId, event) {
47060
47066
  const last = this.entries[this.entries.length - 1];
47061
- if (last && last.eventId !== this.inFlightEventId && !this.transmittedEventIds.has(last.eventId) && last.kind !== "condensation_marker" && last.conversationId === conversationId && last.runId === runId && canCoalesceEvents(last.event, event)) {
47067
+ if (last && !last.explicitSource && last.eventId !== this.inFlightEventId && !this.transmittedEventIds.has(last.eventId) && last.kind !== "condensation_marker" && last.conversationId === conversationId && last.runId === runId && canCoalesceEvents(last.event, event)) {
47062
47068
  last.event = coalesceEvents(last.event, event);
47063
47069
  last.createdAtMs = Date.now();
47064
47070
  this.enforceCaps(runId);
@@ -47077,12 +47083,48 @@ var EncryptedEventOutbox = class _EncryptedEventOutbox {
47077
47083
  this.persist();
47078
47084
  return entry.eventId;
47079
47085
  }
47086
+ /**
47087
+ * Durably enqueue an event whose id is derived from an external source cursor.
47088
+ * Identical retries are idempotent, while reusing an id for different content
47089
+ * fails closed so the backend can never observe divergent payloads under one
47090
+ * receipt key.
47091
+ */
47092
+ enqueueSource(conversationId, runId, eventId, event) {
47093
+ if (!eventId.trim()) throw new Error("event outbox source event id must not be empty");
47094
+ const existing = this.entries.find((entry) => entry.eventId === eventId);
47095
+ if (existing) {
47096
+ if (existing.explicitSource === true && existing.conversationId === conversationId && existing.runId === runId && (0, import_node_util.isDeepStrictEqual)(existing.event, event)) {
47097
+ return eventId;
47098
+ }
47099
+ throw new Error(`event outbox source event id ${eventId} has divergent payload`);
47100
+ }
47101
+ this.entries.push({
47102
+ eventId,
47103
+ conversationId,
47104
+ runId,
47105
+ event,
47106
+ createdAtMs: Date.now(),
47107
+ explicitSource: true
47108
+ });
47109
+ this.enforceCaps(runId);
47110
+ this.persist();
47111
+ return eventId;
47112
+ }
47080
47113
  hasTerminalEvent(runId) {
47081
47114
  return this.entries.some((entry) => entry.runId === runId && isTerminal(entry.event));
47082
47115
  }
47083
47116
  pendingCount() {
47084
47117
  return this.entries.length;
47085
47118
  }
47119
+ /** Resolve only after the backend has durably acknowledged this queued event. */
47120
+ waitForAcknowledgement(eventId) {
47121
+ if (!this.entries.some((entry) => entry.eventId === eventId)) return Promise.resolve();
47122
+ return new Promise((resolve15) => {
47123
+ const waiters = this.acknowledgementWaiters.get(eventId) ?? [];
47124
+ waiters.push(resolve15);
47125
+ this.acknowledgementWaiters.set(eventId, waiters);
47126
+ });
47127
+ }
47086
47128
  disconnect() {
47087
47129
  this.clearAckTimer();
47088
47130
  this.clearRetryTimer();
@@ -47111,6 +47153,8 @@ var EncryptedEventOutbox = class _EncryptedEventOutbox {
47111
47153
  this.condensedRunIds.add(entry.runId);
47112
47154
  }
47113
47155
  this.persist();
47156
+ for (const resolve15 of this.acknowledgementWaiters.get(entry.eventId) ?? []) resolve15();
47157
+ this.acknowledgementWaiters.delete(entry.eventId);
47114
47158
  queueMicrotask(() => this.flush(socket));
47115
47159
  return;
47116
47160
  }
@@ -47130,7 +47174,7 @@ var EncryptedEventOutbox = class _EncryptedEventOutbox {
47130
47174
  );
47131
47175
  }
47132
47176
  isDroppable(entry) {
47133
- return entry.eventId !== this.inFlightEventId && entry.kind !== "condensation_marker" && !isTerminal(entry.event);
47177
+ return entry.eventId !== this.inFlightEventId && !entry.explicitSource && entry.kind !== "condensation_marker" && !isTerminal(entry.event);
47134
47178
  }
47135
47179
  /**
47136
47180
  * Pick the next droppable entry to evict. Heartbeats are always evicted
@@ -47201,7 +47245,7 @@ var EncryptedEventOutbox = class _EncryptedEventOutbox {
47201
47245
  continue;
47202
47246
  }
47203
47247
  const terminal = this.entries.find(
47204
- (entry) => entry.eventId !== this.inFlightEventId && isTerminal(entry.event) && !entry.terminalCondensed
47248
+ (entry) => entry.eventId !== this.inFlightEventId && !entry.explicitSource && isTerminal(entry.event) && !entry.terminalCondensed
47205
47249
  );
47206
47250
  if (!terminal) break;
47207
47251
  terminal.event = condenseTerminalEvent(terminal.event);
@@ -61692,10 +61736,10 @@ function isPlainObject2(value2) {
61692
61736
  return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
61693
61737
  }
61694
61738
  function partitionInputResponses(inputResponses) {
61695
- const accepted = {};
61739
+ const accepted2 = {};
61696
61740
  const droppedKeys = [];
61697
61741
  if (!isPlainObject2(inputResponses)) return {
61698
- accepted,
61742
+ accepted: accepted2,
61699
61743
  droppedKeys
61700
61744
  };
61701
61745
  for (const [key, entry] of Object.entries(inputResponses)) {
@@ -61703,10 +61747,10 @@ function partitionInputResponses(inputResponses) {
61703
61747
  droppedKeys.push(key);
61704
61748
  continue;
61705
61749
  }
61706
- accepted[key] = entry;
61750
+ accepted2[key] = entry;
61707
61751
  }
61708
61752
  return {
61709
- accepted,
61753
+ accepted: accepted2,
61710
61754
  droppedKeys
61711
61755
  };
61712
61756
  }
@@ -73862,15 +73906,18 @@ async function drainNativeHookInbox(input2) {
73862
73906
  return input2.inbox.drain(async (envelope) => {
73863
73907
  const isStart = envelope.eventType === "session_start" || envelope.eventType === "subagent_start";
73864
73908
  const isTerminal2 = envelope.eventType === "stop" || envelope.eventType === "session_end" || envelope.eventType === "subagent_end";
73909
+ let binding = envelope.eventType === "user_prompt" ? void 0 : input2.transcriptStreamer?.bindingFor?.(envelope);
73865
73910
  const streamTranscript = async () => {
73866
73911
  if (isStart || !input2.transcriptStreamer || !envelope.transcriptPath) {
73867
73912
  return "unavailable";
73868
73913
  }
73869
73914
  try {
73870
- const streamed = await input2.transcriptStreamer.stream(
73871
- envelope,
73872
- (event) => input2.sink.deliver(event)
73873
- );
73915
+ const streamed = await input2.transcriptStreamer.stream(envelope, async (event) => {
73916
+ if (binding && input2.sink.deliverCanonical) {
73917
+ return input2.sink.deliverCanonical(binding, event);
73918
+ }
73919
+ return accepted(await input2.sink.deliver(event));
73920
+ });
73874
73921
  return streamed.complete ? streamed.assistantOutputDelivered ? "replayed" : "unavailable" : "deferred";
73875
73922
  } catch (error61) {
73876
73923
  if (error61 instanceof PermanentNativeHookDeliveryError) throw error61;
@@ -73892,14 +73939,27 @@ async function drainNativeHookInbox(input2) {
73892
73939
  if (isTerminal2) {
73893
73940
  const transcriptReplay = await streamTranscript();
73894
73941
  if (transcriptReplay === "deferred") return false;
73895
- return input2.sink.deliver(
73896
- transcriptReplay === "replayed" ? { ...envelope, assistantOutputReplayed: true } : envelope
73942
+ return accepted(
73943
+ await input2.sink.deliver(
73944
+ transcriptReplay === "replayed" ? { ...envelope, assistantOutputReplayed: true } : envelope
73945
+ )
73897
73946
  );
73898
73947
  }
73899
- if (!await input2.sink.deliver(envelope)) return false;
73948
+ const lifecycleDelivery = normalizeDelivery(await input2.sink.deliver(envelope));
73949
+ if (!lifecycleDelivery.accepted) return false;
73950
+ if (envelope.eventType === "user_prompt") {
73951
+ binding = lifecycleDelivery.binding;
73952
+ input2.transcriptStreamer?.setBinding?.(envelope, binding);
73953
+ }
73900
73954
  return await streamTranscript() !== "deferred";
73901
73955
  }, input2.limit);
73902
73956
  }
73957
+ function normalizeDelivery(result) {
73958
+ return typeof result === "boolean" ? { accepted: result } : result;
73959
+ }
73960
+ function accepted(result) {
73961
+ return normalizeDelivery(result).accepted;
73962
+ }
73903
73963
  function isMissingTranscriptError(error61) {
73904
73964
  return error61.code === "ENOENT";
73905
73965
  }
@@ -74112,6 +74172,24 @@ var NativeTranscriptStreamer = class {
74112
74172
  throw new Error("native transcript maxReadBytes must be a positive integer");
74113
74173
  }
74114
74174
  }
74175
+ bindingFor(hook) {
74176
+ if (!hook.transcriptPath) return void 0;
74177
+ return this.cursorStore.get(this.cursorKey(hook))?.binding;
74178
+ }
74179
+ setBinding(hook, binding) {
74180
+ if (!hook.transcriptPath) return;
74181
+ const key = this.cursorKey(hook);
74182
+ const previous = this.cursorStore.get(key);
74183
+ this.cursorStore.commit(key, {
74184
+ offset: previous?.offset ?? 0,
74185
+ generation: previous?.generation ?? 0,
74186
+ device: previous?.device ?? 0,
74187
+ inode: previous?.inode ?? 0,
74188
+ assistantOutputSeen: previous?.assistantOutputSeen,
74189
+ assistantOutputTurnId: previous?.assistantOutputTurnId,
74190
+ ...binding ? { binding } : {}
74191
+ });
74192
+ }
74115
74193
  async stream(hook, deliver) {
74116
74194
  if (!hook.transcriptPath) {
74117
74195
  return {
@@ -74121,11 +74199,7 @@ var NativeTranscriptStreamer = class {
74121
74199
  assistantOutputDelivered: false
74122
74200
  };
74123
74201
  }
74124
- const key = nativeTranscriptCursorKey({
74125
- provider: hook.provider,
74126
- externalSessionId: hook.externalSessionId,
74127
- transcriptPath: hook.transcriptPath
74128
- });
74202
+ const key = this.cursorKey(hook);
74129
74203
  let previous = this.cursorStore.get(key);
74130
74204
  if (hook.eventType === "user_prompt" && previous && previous.assistantOutputTurnId !== hook.eventId) {
74131
74205
  previous = {
@@ -74136,15 +74210,17 @@ var NativeTranscriptStreamer = class {
74136
74210
  this.cursorStore.commit(key, previous);
74137
74211
  }
74138
74212
  const file2 = (0, import_node_fs18.statSync)(hook.transcriptPath);
74139
- const replaced = Boolean(previous) && (previous.device !== file2.dev || previous.inode !== file2.ino);
74213
+ const hasFileIdentity = Boolean(previous && (previous.device !== 0 || previous.inode !== 0));
74214
+ const replaced = hasFileIdentity && (previous.device !== file2.dev || previous.inode !== file2.ino);
74140
74215
  const truncated = Boolean(previous) && file2.size < previous.offset;
74141
- const start = !previous || replaced || truncated ? {
74216
+ const start = !previous || !hasFileIdentity || replaced || truncated ? {
74142
74217
  offset: 0,
74143
- generation: previous ? previous.generation + 1 : 0,
74218
+ generation: previous ? previous.generation + (hasFileIdentity ? 1 : 0) : 0,
74144
74219
  device: file2.dev,
74145
74220
  inode: file2.ino,
74146
74221
  assistantOutputSeen: previous?.assistantOutputSeen ?? false,
74147
- assistantOutputTurnId: hook.eventType === "user_prompt" ? hook.eventId : previous?.assistantOutputTurnId
74222
+ assistantOutputTurnId: hook.eventType === "user_prompt" ? hook.eventId : previous?.assistantOutputTurnId,
74223
+ binding: previous?.binding
74148
74224
  } : previous;
74149
74225
  let assistantOutputSeen = start.assistantOutputSeen ?? false;
74150
74226
  if (file2.size <= start.offset) {
@@ -74197,9 +74273,10 @@ var NativeTranscriptStreamer = class {
74197
74273
  const events = parsed ? providerRecordToAgentEvents(hook.provider, parsed) : [];
74198
74274
  for (let index = 0; index < events.length; index += 1) {
74199
74275
  const deliveredEvent = events[index];
74276
+ if (deliveredEvent.type === "start" && start.binding) continue;
74200
74277
  const sourceCursor = `${start.generation}:${recordEndOffset}:${index}`;
74201
74278
  const eventId = stableSourceEventId(hook, sourceCursor);
74202
- const accepted = await deliver({
74279
+ const accepted2 = await deliver({
74203
74280
  schemaVersion: 1,
74204
74281
  eventId,
74205
74282
  provider: hook.provider,
@@ -74208,7 +74285,7 @@ var NativeTranscriptStreamer = class {
74208
74285
  observedAt: timestampOf(parsed) ?? hook.observedAt,
74209
74286
  event: deliveredEvent
74210
74287
  });
74211
- if (!accepted) {
74288
+ if (!accepted2) {
74212
74289
  return {
74213
74290
  complete: false,
74214
74291
  recordsRead,
@@ -74238,6 +74315,14 @@ var NativeTranscriptStreamer = class {
74238
74315
  cursor: `${start.generation}:${committedOffset}`
74239
74316
  };
74240
74317
  }
74318
+ cursorKey(hook) {
74319
+ if (!hook.transcriptPath) throw new Error("native transcript hook has no transcript path");
74320
+ return nativeTranscriptCursorKey({
74321
+ provider: hook.provider,
74322
+ externalSessionId: hook.externalSessionId,
74323
+ transcriptPath: hook.transcriptPath
74324
+ });
74325
+ }
74241
74326
  };
74242
74327
  function stableSourceEventId(hook, sourceCursor) {
74243
74328
  const bytes = (0, import_node_crypto10.createHash)("sha256").update(`${hook.provider}\0${hook.externalSessionId}\0${hook.transcriptPath}\0${sourceCursor}`).digest().subarray(0, 16);
@@ -74257,8 +74342,19 @@ function parseJsonObject3(value2) {
74257
74342
  function providerRecordToAgentEvents(provider, record2) {
74258
74343
  if (provider === "codex") return codexRecordToAgentEvents(record2);
74259
74344
  if (provider === "copilot") return copilotRecordToAgentEvents(record2);
74345
+ if (provider === "cursor") return cursorRecordToAgentEvents(record2);
74260
74346
  return claudeCompatibleRecordToAgentEvents(record2);
74261
74347
  }
74348
+ function cursorRecordToAgentEvents(record2) {
74349
+ const message = isRecord2(record2.message) ? record2.message : record2;
74350
+ const role = stringValue(message.role, record2.role);
74351
+ if (role !== "user") return claudeCompatibleRecordToAgentEvents(record2);
74352
+ const rawPrompt = extractText2(message.content).trim();
74353
+ if (!rawPrompt) return [];
74354
+ const wrappedPrompt = rawPrompt.match(/<user_query>\s*([\s\S]*?)\s*<\/user_query>/i)?.[1];
74355
+ const prompt = (wrappedPrompt ?? rawPrompt).trim();
74356
+ return prompt ? [{ type: "start", task: prompt, ts: Date.now() }] : [];
74357
+ }
74262
74358
  function copilotRecordToAgentEvents(record2) {
74263
74359
  if (typeof record2.type !== "string" || !isRecord2(record2.data)) return [];
74264
74360
  const timestamp = timestampOf(record2) ? Date.parse(timestampOf(record2)) : Date.now();
@@ -74684,9 +74780,9 @@ var import_yaml = __toESM(require_dist2(), 1);
74684
74780
  // ../shared/dist/skills/domain.js
74685
74781
  var SKILLS_PROTOCOL_VERSION = 1;
74686
74782
  var SKILL_PACKAGE_LIMITS = {
74687
- maxArchiveBytes: 10 * 1024 * 1024,
74688
- maxExpandedBytes: 25 * 1024 * 1024,
74689
- maxFileBytes: 25 * 1024 * 1024,
74783
+ maxArchiveBytes: 20 * 1024 * 1024,
74784
+ maxExpandedBytes: 20 * 1024 * 1024,
74785
+ maxFileBytes: 20 * 1024 * 1024,
74690
74786
  maxSkillMarkdownBytes: 2 * 1024 * 1024,
74691
74787
  maxFiles: 1e3,
74692
74788
  maxPathBytes: 240,
@@ -74711,14 +74807,16 @@ var skillInvocationSchema = defineWireSchema()(external_exports.discriminatedUni
74711
74807
  kind: external_exports.literal("skill_version"),
74712
74808
  skillId: skillIdSchema,
74713
74809
  versionId: skillVersionIdSchema,
74714
- displayName: external_exports.string().trim().min(1).max(128)
74810
+ displayName: external_exports.string().trim().min(1).max(128),
74811
+ textOffset: external_exports.number().int().nonnegative().optional()
74715
74812
  }),
74716
74813
  external_exports.object({
74717
74814
  kind: external_exports.literal("local_skill"),
74718
74815
  runtimeId: runtimeIdSchema,
74719
74816
  localKey: external_exports.string().min(1).max(256),
74720
74817
  contentHash: sha256Schema,
74721
- displayName: external_exports.string().trim().min(1).max(128)
74818
+ displayName: external_exports.string().trim().min(1).max(128),
74819
+ textOffset: external_exports.number().int().nonnegative().optional()
74722
74820
  })
74723
74821
  ]));
74724
74822
  var runtimeUploadGrantSchema = external_exports.object({
@@ -74886,12 +74984,7 @@ var discoveredSkillCandidateSchema = external_exports.object({
74886
74984
  repositoryId: external_exports.string().min(1).max(128)
74887
74985
  })
74888
74986
  ]),
74889
- diagnostics: external_exports.array(skillPackageDiagnosticSchema).max(100),
74890
- publication: external_exports.object({
74891
- skillId: skillIdSchema,
74892
- versionId: skillVersionIdSchema,
74893
- contentHash: sha256Schema
74894
- }).optional()
74987
+ diagnostics: external_exports.array(skillPackageDiagnosticSchema).max(100)
74895
74988
  });
74896
74989
  var skillScanOutcomeSchema = defineWireSchema()(external_exports.discriminatedUnion("status", [
74897
74990
  external_exports.object({
@@ -74925,6 +75018,19 @@ var desiredSkillManifestEntrySchema = external_exports.object({
74925
75018
  downloadUrl: external_exports.url(),
74926
75019
  downloadUrlExpiresAt: external_exports.iso.datetime()
74927
75020
  });
75021
+ var skillInstallConflictSchema = external_exports.object({
75022
+ kind: external_exports.enum(["managed_destination", "compatibility_destination"]),
75023
+ runtimeDirectory: external_exports.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/),
75024
+ observedHash: sha256Schema,
75025
+ observedName: external_exports.string().min(1).max(64).regex(SKILL_NAME_PATTERN, "Invalid")
75026
+ });
75027
+ var skillConflictResolutionSchema = external_exports.discriminatedUnion("kind", [
75028
+ external_exports.object({
75029
+ kind: external_exports.literal("rename_local"),
75030
+ name: external_exports.string().min(1).max(64).regex(SKILL_NAME_PATTERN, "Invalid")
75031
+ }),
75032
+ external_exports.object({ kind: external_exports.literal("overwrite_local") })
75033
+ ]);
74928
75034
  var desiredSkillManifestSchema = defineWireSchema()(external_exports.object({
74929
75035
  protocolVersion: external_exports.literal(SKILLS_PROTOCOL_VERSION),
74930
75036
  revision: sha256Schema,
@@ -74958,7 +75064,8 @@ var reconcileSkillResultSchema = external_exports.object({
74958
75064
  observedHash: sha256Schema.optional(),
74959
75065
  state: external_exports.enum(["applied", "conflict", "failed"]),
74960
75066
  errorCode: external_exports.string().max(128).optional(),
74961
- errorMessage: external_exports.string().max(2048).optional()
75067
+ errorMessage: external_exports.string().max(2048).optional(),
75068
+ conflict: skillInstallConflictSchema.optional()
74962
75069
  });
74963
75070
  var reconcileSkillsResultSchema = defineWireSchema()(external_exports.object({
74964
75071
  attemptId: external_exports.string().min(1).max(128),
@@ -74985,14 +75092,23 @@ var runtimeSkillCommandSchema = defineWireSchema()(external_exports.discriminate
74985
75092
  uploadGrant: runtimeUploadGrantSchema
74986
75093
  }),
74987
75094
  external_exports.object({
74988
- kind: external_exports.literal("bind_source"),
75095
+ kind: external_exports.literal("resolve_conflict"),
74989
75096
  commandId: skillCommandIdSchema,
74990
- localKey: external_exports.string().min(1).max(256),
74991
- expectedContentHash: sha256Schema,
75097
+ manifest: desiredSkillManifestSchema,
74992
75098
  skillId: skillIdSchema,
74993
- versionId: skillVersionIdSchema
75099
+ versionId: skillVersionIdSchema,
75100
+ expectedConflict: skillInstallConflictSchema,
75101
+ resolution: skillConflictResolutionSchema
74994
75102
  })
74995
75103
  ]));
75104
+ var skillConflictResolutionResultSchema = defineWireSchema()(external_exports.object({
75105
+ commandId: skillCommandIdSchema,
75106
+ skillId: skillIdSchema,
75107
+ versionId: skillVersionIdSchema,
75108
+ state: external_exports.enum(["applied", "stale", "failed"]),
75109
+ errorCode: external_exports.string().max(128).optional(),
75110
+ errorMessage: external_exports.string().max(2048).optional()
75111
+ }));
74996
75112
  var publishSkillResultSchema = defineWireSchema()(external_exports.object({
74997
75113
  commandId: skillCommandIdSchema,
74998
75114
  previewId: skillPreviewIdSchema,
@@ -75012,6 +75128,7 @@ var runtimeSkillSyncRequestSchema = defineWireSchema()(external_exports.object({
75012
75128
  inventoryAttempt: skillScanOutcomeSchema.optional(),
75013
75129
  appliedResult: reconcileSkillsResultSchema.optional(),
75014
75130
  publishedPreviews: external_exports.array(publishSkillResultSchema).max(100).optional(),
75131
+ resolvedConflicts: external_exports.array(skillConflictResolutionResultSchema).max(100).optional(),
75015
75132
  completedCommandIds: external_exports.array(skillCommandIdSchema).max(100).optional()
75016
75133
  }));
75017
75134
  var runtimeSkillSyncResponseSchema = defineWireSchema()(external_exports.object({
@@ -75535,7 +75652,6 @@ var DEFAULT_SKILL_CONFIG = `version = 1
75535
75652
 
75536
75653
  [skills]
75537
75654
  enabled = true
75538
- publish_changes = "prompt"
75539
75655
  discover_repository_skills = true
75540
75656
  ignore = ["**/.git/**", "**/node_modules/**"]
75541
75657
 
@@ -75887,7 +76003,6 @@ function parseSkillSourceConfig(content, homeDirectory2) {
75887
76003
  }
75888
76004
  const skills = parsed.skills;
75889
76005
  const enabled = optionalBoolean(skills.enabled, true, "skills.enabled");
75890
- const publishChanges = optionalPublishChanges(skills.publish_changes);
75891
76006
  const discoverRepositorySkills = optionalBoolean(
75892
76007
  skills.discover_repository_skills,
75893
76008
  true,
@@ -75929,7 +76044,7 @@ function parseSkillSourceConfig(content, homeDirectory2) {
75929
76044
  if (duplicateId) {
75930
76045
  throw new Error(`skills.sources contains duplicate source id ${duplicateId.declarationId}`);
75931
76046
  }
75932
- return { enabled, publishChanges, discoverRepositorySkills, ignoredPaths, sources };
76047
+ return { enabled, discoverRepositorySkills, ignoredPaths, sources };
75933
76048
  }
75934
76049
  function stableSourceDeclarationId(sourcePath) {
75935
76050
  const digest = (0, import_node_crypto12.createHash)("sha256").update("alan-skill-source-v1\0").update(sourcePath.normalize("NFC")).digest("hex").slice(0, 24);
@@ -75955,11 +76070,6 @@ function optionalBoolean(value2, fallback, field) {
75955
76070
  if (typeof value2 !== "boolean") throw new Error(`${field} must be true or false`);
75956
76071
  return value2;
75957
76072
  }
75958
- function optionalPublishChanges(value2) {
75959
- if (value2 === void 0) return "prompt";
75960
- if (value2 === "prompt" || value2 === "automatic" || value2 === "never") return value2;
75961
- throw new Error('skills.publish_changes must be "prompt", "automatic", or "never"');
75962
- }
75963
76073
  function optionalStringArray(value2, field) {
75964
76074
  if (value2 === void 0) return [];
75965
76075
  if (!Array.isArray(value2) || value2.some((item) => typeof item !== "string")) {
@@ -76068,6 +76178,7 @@ var RuntimeSkillCoordinator = class {
76068
76178
  pendingInventory;
76069
76179
  pendingApplied;
76070
76180
  pendingPublished = /* @__PURE__ */ new Map();
76181
+ pendingResolvedConflicts = /* @__PURE__ */ new Map();
76071
76182
  completedCommandIds = /* @__PURE__ */ new Set();
76072
76183
  tail = Promise.resolve();
76073
76184
  lastScanInput;
@@ -76133,16 +76244,22 @@ var RuntimeSkillCoordinator = class {
76133
76244
  producedWork = true;
76134
76245
  break;
76135
76246
  }
76136
- case "bind_source":
76137
- await this.manager.bindSource({
76138
- localKey: command.localKey,
76139
- expectedContentHash: command.expectedContentHash,
76247
+ case "resolve_conflict": {
76248
+ const resolved = await this.manager.resolveConflict({
76249
+ commandId: command.commandId,
76250
+ manifest: command.manifest,
76140
76251
  skillId: command.skillId,
76141
- versionId: command.versionId
76252
+ versionId: command.versionId,
76253
+ expectedConflict: command.expectedConflict,
76254
+ resolution: command.resolution
76142
76255
  });
76143
- this.completedCommandIds.add(command.commandId);
76256
+ this.pendingResolvedConflicts.set(resolved.commandId, resolved);
76257
+ if (resolved.state === "applied") {
76258
+ this.pendingApplied = await this.manager.reconcile({ manifest: command.manifest });
76259
+ }
76144
76260
  producedWork = true;
76145
76261
  break;
76262
+ }
76146
76263
  }
76147
76264
  }
76148
76265
  if (!producedWork && !this.hasPendingReports()) return;
@@ -76157,6 +76274,7 @@ var RuntimeSkillCoordinator = class {
76157
76274
  ...this.pendingInventory ? { inventoryAttempt: this.pendingInventory } : {},
76158
76275
  ...this.pendingApplied ? { appliedResult: this.pendingApplied } : {},
76159
76276
  ...this.pendingPublished.size > 0 ? { publishedPreviews: [...this.pendingPublished.values()] } : {},
76277
+ ...this.pendingResolvedConflicts.size > 0 ? { resolvedConflicts: [...this.pendingResolvedConflicts.values()] } : {},
76160
76278
  ...this.completedCommandIds.size > 0 ? { completedCommandIds: [...this.completedCommandIds] } : {}
76161
76279
  };
76162
76280
  }
@@ -76166,23 +76284,26 @@ var RuntimeSkillCoordinator = class {
76166
76284
  return this.manager.scan(prepared);
76167
76285
  }
76168
76286
  accept(attemptIds) {
76169
- const accepted = new Set(attemptIds);
76170
- if (this.pendingInventory && accepted.has(this.pendingInventory.attemptId)) {
76287
+ const accepted2 = new Set(attemptIds);
76288
+ if (this.pendingInventory && accepted2.has(this.pendingInventory.attemptId)) {
76171
76289
  this.pendingInventory = void 0;
76172
76290
  }
76173
- if (this.pendingApplied && accepted.has(this.pendingApplied.attemptId)) {
76291
+ if (this.pendingApplied && accepted2.has(this.pendingApplied.attemptId)) {
76174
76292
  this.pendingApplied = void 0;
76175
76293
  }
76176
76294
  for (const commandId of this.pendingPublished.keys()) {
76177
- if (accepted.has(commandId)) this.pendingPublished.delete(commandId);
76295
+ if (accepted2.has(commandId)) this.pendingPublished.delete(commandId);
76296
+ }
76297
+ for (const commandId of this.pendingResolvedConflicts.keys()) {
76298
+ if (accepted2.has(commandId)) this.pendingResolvedConflicts.delete(commandId);
76178
76299
  }
76179
76300
  for (const commandId of this.completedCommandIds) {
76180
- if (accepted.has(commandId)) this.completedCommandIds.delete(commandId);
76301
+ if (accepted2.has(commandId)) this.completedCommandIds.delete(commandId);
76181
76302
  }
76182
76303
  }
76183
76304
  hasPendingReports() {
76184
76305
  return Boolean(
76185
- this.pendingInventory || this.pendingApplied || this.pendingPublished.size > 0 || this.completedCommandIds.size > 0
76306
+ this.pendingInventory || this.pendingApplied || this.pendingPublished.size > 0 || this.pendingResolvedConflicts.size > 0 || this.completedCommandIds.size > 0
76186
76307
  );
76187
76308
  }
76188
76309
  async runExclusively(work) {
@@ -76249,36 +76370,6 @@ var ManagedSkillStore = class _ManagedSkillStore {
76249
76370
  resolveSource(localKey) {
76250
76371
  return this.state.sources[localKey] ?? null;
76251
76372
  }
76252
- publicationFor(localKey, contentHash) {
76253
- const source = this.state.sources[localKey];
76254
- return source?.contentHash === contentHash && source.publication?.contentHash === contentHash ? source.publication : null;
76255
- }
76256
- sourceForPublication(projection) {
76257
- return Object.values(this.state.sources).find(
76258
- (source) => source.contentHash === projection.contentHash && source.publication?.skillId === projection.skillId && source.publication.versionId === projection.versionId && source.publication.contentHash === projection.contentHash
76259
- ) ?? null;
76260
- }
76261
- async bindSource(input2) {
76262
- const source = this.state.sources[input2.localKey];
76263
- if (!source || source.contentHash !== input2.expectedContentHash) {
76264
- throw new Error("Skill source changed before its publication binding was applied");
76265
- }
76266
- this.state = {
76267
- ...this.state,
76268
- sources: {
76269
- ...this.state.sources,
76270
- [input2.localKey]: {
76271
- ...source,
76272
- publication: {
76273
- skillId: input2.skillId,
76274
- versionId: input2.versionId,
76275
- contentHash: input2.expectedContentHash
76276
- }
76277
- }
76278
- }
76279
- };
76280
- await this.persist();
76281
- }
76282
76373
  appliedRevision() {
76283
76374
  return this.state.appliedRevision ?? null;
76284
76375
  }
@@ -76295,19 +76386,10 @@ var ManagedSkillStore = class _ManagedSkillStore {
76295
76386
  }
76296
76387
  async recordCompleteScan(input2) {
76297
76388
  const sources = Object.fromEntries(
76298
- input2.candidates.map((candidate) => {
76299
- const publication = this.publicationFor(candidate.localKey, candidate.contentHash) ?? Object.values(this.state.sources).find(
76300
- (source) => source.absolutePath === candidate.absolutePath && source.contentHash === candidate.contentHash && source.publication?.contentHash === candidate.contentHash
76301
- )?.publication;
76302
- return [
76303
- candidate.localKey,
76304
- {
76305
- absolutePath: candidate.absolutePath,
76306
- contentHash: candidate.contentHash,
76307
- ...publication ? { publication } : {}
76308
- }
76309
- ];
76310
- })
76389
+ input2.candidates.map((candidate) => [
76390
+ candidate.localKey,
76391
+ { absolutePath: candidate.absolutePath, contentHash: candidate.contentHash }
76392
+ ])
76311
76393
  );
76312
76394
  this.state = {
76313
76395
  ...this.state,
@@ -76355,11 +76437,9 @@ function parseState(value2) {
76355
76437
  if (!localKey.startsWith("local:") || !isRecord5(source) || typeof source.absolutePath !== "string" || !isSha256(source.contentHash)) {
76356
76438
  throw new Error("invalid Skill source state");
76357
76439
  }
76358
- const publication = parsePublication(source.publication, source.contentHash);
76359
76440
  sources[localKey] = {
76360
76441
  absolutePath: source.absolutePath,
76361
- contentHash: source.contentHash,
76362
- ...publication ? { publication } : {}
76442
+ contentHash: source.contentHash
76363
76443
  };
76364
76444
  }
76365
76445
  const projections = {};
@@ -76383,17 +76463,6 @@ function parseState(value2) {
76383
76463
  projections
76384
76464
  };
76385
76465
  }
76386
- function parsePublication(value2, sourceHash) {
76387
- if (value2 === void 0) return void 0;
76388
- if (!isRecord5(value2) || typeof value2.skillId !== "string" || typeof value2.versionId !== "string" || !isSha256(value2.contentHash) || value2.contentHash !== sourceHash) {
76389
- throw new Error("invalid Skill source publication state");
76390
- }
76391
- return {
76392
- skillId: value2.skillId,
76393
- versionId: value2.versionId,
76394
- contentHash: value2.contentHash
76395
- };
76396
- }
76397
76466
  function optionalSha256(value2) {
76398
76467
  if (value2 === void 0) return void 0;
76399
76468
  if (!isSha256(value2)) throw new Error("invalid Skill state hash");
@@ -76417,8 +76486,8 @@ var import_node_path21 = require("path");
76417
76486
  var import_node_stream = require("stream");
76418
76487
  var import_node_zlib = require("zlib");
76419
76488
  var import_tar_stream = __toESM(require_tar_stream(), 1);
76420
- async function createSkillBundle(packageRoot) {
76421
- const before = await inspectSkillPackage(packageRoot);
76489
+ async function createSkillBundle(packageRoot, options = {}) {
76490
+ const before = await inspectSkillPackage(packageRoot, options);
76422
76491
  const archive = (0, import_tar_stream.pack)();
76423
76492
  const tarPromise = collectStream(archive);
76424
76493
  for (const file2 of before.files) {
@@ -76440,7 +76509,7 @@ async function createSkillBundle(packageRoot) {
76440
76509
  if (bytes.byteLength > SKILL_PACKAGE_LIMITS.maxArchiveBytes) {
76441
76510
  throw new Error(`Skill archive exceeds ${SKILL_PACKAGE_LIMITS.maxArchiveBytes} bytes`);
76442
76511
  }
76443
- const after = await inspectSkillPackage(packageRoot);
76512
+ const after = await inspectSkillPackage(packageRoot, options);
76444
76513
  if (after.contentHash !== before.contentHash) {
76445
76514
  throw new Error("Skill source changed while its archive was being created");
76446
76515
  }
@@ -76637,17 +76706,7 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
76637
76706
  );
76638
76707
  }
76639
76708
  async scan(input2) {
76640
- const scanned = await this.localSource.scan(input2);
76641
- const outcome = scanned.status === "complete" ? {
76642
- ...scanned,
76643
- candidates: scanned.candidates.map((candidate) => {
76644
- const publication = this.store.publicationFor(
76645
- candidate.localKey,
76646
- candidate.contentHash
76647
- );
76648
- return publication ? { ...candidate, publication } : candidate;
76649
- })
76650
- } : scanned;
76709
+ const outcome = await this.localSource.scan(input2);
76651
76710
  if (outcome.status === "complete") {
76652
76711
  await this.store.recordCompleteScan({
76653
76712
  revision: outcome.inventoryRevision,
@@ -76664,9 +76723,6 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
76664
76723
  }
76665
76724
  return outcome;
76666
76725
  }
76667
- async bindSource(input2) {
76668
- await this.store.bindSource(input2);
76669
- }
76670
76726
  async publish(input2) {
76671
76727
  const source = this.store.resolveSource(input2.localKey);
76672
76728
  if (!source) throw new Error("Skill source is not present in the last complete scan");
@@ -76754,6 +76810,11 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
76754
76810
  () => this.withMachineLock(() => this.reconcileUnlocked(input2.manifest))
76755
76811
  );
76756
76812
  }
76813
+ async resolveConflict(input2) {
76814
+ return this.runExclusively(
76815
+ () => this.withMachineLock(() => this.resolveConflictUnlocked(input2))
76816
+ );
76817
+ }
76757
76818
  async inspect() {
76758
76819
  const status = this.store.inspect();
76759
76820
  return {
@@ -76815,11 +76876,7 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
76815
76876
  await this.adoptLegacyProjection(entry);
76816
76877
  await this.requireReplaceable(entry);
76817
76878
  const observedHash = await this.reuseCurrentProjection(entry);
76818
- const publishedSource = this.store.sourceForPublication(entry);
76819
- const appliedHash = observedHash ?? await this.project(
76820
- entry,
76821
- publishedSource && await packageMatches(publishedSource.absolutePath, entry.contentHash) ? publishedSource.absolutePath : await this.ensureCached(entry)
76822
- );
76879
+ const appliedHash = observedHash ?? await this.project(entry, await this.ensureCached(entry));
76823
76880
  nextProjections[entry.runtimeDirectory] = {
76824
76881
  skillId: entry.skillId,
76825
76882
  versionId: entry.versionId,
@@ -76833,13 +76890,16 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
76833
76890
  state: "applied"
76834
76891
  });
76835
76892
  } catch (error61) {
76893
+ const code = errorCode(error61);
76894
+ const conflict = isUnmanagedConflict(error61) ? await this.observeConflict(entry, code).catch(() => null) : null;
76836
76895
  results.push({
76837
76896
  skillId: entry.skillId,
76838
76897
  versionId: entry.versionId,
76839
76898
  contentHash: entry.contentHash,
76840
76899
  state: isUnmanagedConflict(error61) ? "conflict" : "failed",
76841
- errorCode: errorCode(error61),
76842
- errorMessage: error61 instanceof Error ? error61.message : String(error61)
76900
+ errorCode: code,
76901
+ errorMessage: error61 instanceof Error ? error61.message : String(error61),
76902
+ ...conflict ? { conflict } : {}
76843
76903
  });
76844
76904
  }
76845
76905
  }
@@ -76856,6 +76916,147 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
76856
76916
  skills: results
76857
76917
  };
76858
76918
  }
76919
+ async resolveConflictUnlocked(input2) {
76920
+ const manifest = desiredSkillManifestSchema.parse(input2.manifest);
76921
+ const entry = manifest.skills.find(
76922
+ (candidate) => candidate.skillId === input2.skillId && candidate.versionId === input2.versionId
76923
+ );
76924
+ if (!entry || entry.runtimeDirectory !== input2.expectedConflict.runtimeDirectory) {
76925
+ return resolutionFailure(
76926
+ input2,
76927
+ "stale",
76928
+ "SKILL_CONFLICT_STALE",
76929
+ "The Skill conflict changed. Refresh and try again."
76930
+ );
76931
+ }
76932
+ const current = await this.observeConflict(
76933
+ entry,
76934
+ input2.expectedConflict.kind === "managed_destination" ? "UNMANAGED_PATH_CONFLICT" : "COMPATIBILITY_PATH_CONFLICT"
76935
+ ).catch(() => null);
76936
+ if (!current || !sameConflict(current, input2.expectedConflict)) {
76937
+ return resolutionFailure(
76938
+ input2,
76939
+ "stale",
76940
+ "SKILL_CONFLICT_STALE",
76941
+ "The local Skill changed. Refresh and try again."
76942
+ );
76943
+ }
76944
+ try {
76945
+ if (input2.resolution.kind === "rename_local") {
76946
+ await this.renameLocalConflict(entry, current, input2.resolution.name);
76947
+ const reconciled = await this.reconcileUnlocked(manifest);
76948
+ return resolutionFromReconcile(input2, reconciled);
76949
+ }
76950
+ return await this.overwriteLocalConflict(input2, entry, current, manifest);
76951
+ } catch (error61) {
76952
+ return resolutionFailure(
76953
+ input2,
76954
+ errorCode(error61) === "SKILL_CONFLICT_STALE" ? "stale" : "failed",
76955
+ errorCode(error61),
76956
+ error61 instanceof Error ? error61.message : String(error61)
76957
+ );
76958
+ }
76959
+ }
76960
+ async observeConflict(entry, code) {
76961
+ const kind = code === "COMPATIBILITY_PATH_CONFLICT" ? "compatibility_destination" : "managed_destination";
76962
+ const directory = this.conflictDirectory(entry.runtimeDirectory, kind);
76963
+ const inspected = await inspectSkillPackage(directory, { expectedName: null });
76964
+ return {
76965
+ kind,
76966
+ runtimeDirectory: entry.runtimeDirectory,
76967
+ observedHash: inspected.contentHash,
76968
+ observedName: inspected.metadata.name
76969
+ };
76970
+ }
76971
+ conflictDirectory(runtimeDirectory, kind) {
76972
+ requireSafeRuntimeDirectory(runtimeDirectory);
76973
+ return (0, import_node_path22.join)(
76974
+ kind === "managed_destination" ? this.managedSkillsDirectory : this.compatibilitySkillsDirectory,
76975
+ runtimeDirectory
76976
+ );
76977
+ }
76978
+ async renameLocalConflict(entry, conflict, name) {
76979
+ requirePortableSkillName(name);
76980
+ const source = this.conflictDirectory(entry.runtimeDirectory, conflict.kind);
76981
+ const destination = (0, import_node_path22.join)((0, import_node_path22.dirname)(source), name);
76982
+ if (await pathExists2(destination)) {
76983
+ throw Object.assign(new Error(`A local Skill named ${name} already exists`), {
76984
+ code: "SKILL_LOCAL_NAME_CONFLICT"
76985
+ });
76986
+ }
76987
+ const suffix = `${process.pid}-${(0, import_node_crypto15.randomBytes)(8).toString("hex")}`;
76988
+ const backup = `${source}.rename-backup-${suffix}`;
76989
+ const pending = `${source}.rename-pending-${suffix}`;
76990
+ let sourceMoved = false;
76991
+ let destinationInstalled = false;
76992
+ try {
76993
+ await (0, import_promises5.rename)(source, backup);
76994
+ sourceMoved = true;
76995
+ await (0, import_promises5.cp)(backup, pending, { recursive: true, errorOnExist: true, force: false });
76996
+ const skillMarkdown = (0, import_node_path22.join)(pending, "SKILL.md");
76997
+ await (0, import_promises5.writeFile)(
76998
+ skillMarkdown,
76999
+ rewriteSkillMarkdownName(await (0, import_promises5.readFile)(skillMarkdown, "utf8"), name),
77000
+ {
77001
+ encoding: "utf8",
77002
+ mode: 420
77003
+ }
77004
+ );
77005
+ await inspectSkillPackage(pending, { expectedName: name });
77006
+ await (0, import_promises5.rename)(pending, destination);
77007
+ destinationInstalled = true;
77008
+ await (0, import_promises5.rm)(backup, { recursive: true, force: true });
77009
+ } catch (error61) {
77010
+ if (destinationInstalled) await (0, import_promises5.rm)(destination, { recursive: true, force: true });
77011
+ if (sourceMoved && await pathExists2(backup) && !await pathExists2(source)) {
77012
+ await (0, import_promises5.rename)(backup, source);
77013
+ }
77014
+ throw error61;
77015
+ } finally {
77016
+ await (0, import_promises5.rm)(pending, { recursive: true, force: true });
77017
+ if (!sourceMoved || destinationInstalled) {
77018
+ await (0, import_promises5.rm)(backup, { recursive: true, force: true });
77019
+ }
77020
+ }
77021
+ }
77022
+ async overwriteLocalConflict(input2, entry, conflict, manifest) {
77023
+ const source = this.conflictDirectory(entry.runtimeDirectory, conflict.kind);
77024
+ const backup = `${source}.overwrite-backup-${process.pid}-${(0, import_node_crypto15.randomBytes)(8).toString("hex")}`;
77025
+ await (0, import_promises5.rename)(source, backup);
77026
+ try {
77027
+ const reconciled = await this.reconcileUnlocked(manifest);
77028
+ const result = resolutionFromReconcile(input2, reconciled);
77029
+ if (result.state !== "applied") {
77030
+ await this.removeResolvedProjection(entry, conflict.kind);
77031
+ await (0, import_promises5.rename)(backup, source);
77032
+ return result;
77033
+ }
77034
+ await (0, import_promises5.rm)(backup, { recursive: true, force: true });
77035
+ return result;
77036
+ } catch (error61) {
77037
+ await this.removeResolvedProjection(entry, conflict.kind).catch(() => void 0);
77038
+ if (!await pathExists2(source) && await pathExists2(backup)) await (0, import_promises5.rename)(backup, source);
77039
+ throw error61;
77040
+ }
77041
+ }
77042
+ async removeResolvedProjection(entry, conflictKind) {
77043
+ const destination = this.conflictDirectory(entry.runtimeDirectory, conflictKind);
77044
+ if (!await pathExists2(destination)) return;
77045
+ if (conflictKind === "managed_destination") {
77046
+ const marker = await readMarker(destination);
77047
+ if (marker?.skillId === entry.skillId && marker.versionId === entry.versionId && marker.contentHash === entry.contentHash) {
77048
+ await (0, import_promises5.rm)(destination, { recursive: true, force: true });
77049
+ }
77050
+ return;
77051
+ }
77052
+ const canonical = (0, import_node_path22.join)(this.managedSkillsDirectory, entry.runtimeDirectory);
77053
+ const projection = {
77054
+ skillId: entry.skillId,
77055
+ versionId: entry.versionId,
77056
+ contentHash: entry.contentHash
77057
+ };
77058
+ await removeCompatibilityProjection(destination, canonical, projection);
77059
+ }
76859
77060
  async reuseCurrentProjection(entry) {
76860
77061
  const destination = (0, import_node_path22.join)(this.managedSkillsDirectory, entry.runtimeDirectory);
76861
77062
  const marker = await readMarker(destination);
@@ -77233,6 +77434,66 @@ function requireSafeRuntimeDirectory(value2) {
77233
77434
  throw new Error(`Unsafe managed Skill directory: ${value2}`);
77234
77435
  }
77235
77436
  }
77437
+ function requirePortableSkillName(value2) {
77438
+ if (!/^(?!-)(?!.*--)[a-z0-9]+(?:-[a-z0-9]+)*$/.test(value2) || value2.length > 64) {
77439
+ throw Object.assign(
77440
+ new Error("Local Skill names use lowercase letters, numbers, and single hyphens"),
77441
+ { code: "INVALID_SKILL_NAME" }
77442
+ );
77443
+ }
77444
+ }
77445
+ function rewriteSkillMarkdownName(markdown, name) {
77446
+ const lineEnding = markdown.includes("\r\n") ? "\r\n" : "\n";
77447
+ const lines = markdown.split(/\r?\n/);
77448
+ if (lines[0]?.trim() !== "---") {
77449
+ throw Object.assign(new Error("The local Skill has invalid SKILL.md frontmatter"), {
77450
+ code: "INVALID_FRONTMATTER"
77451
+ });
77452
+ }
77453
+ const end = lines.findIndex((line, index) => index > 0 && line.trim() === "---");
77454
+ const nameIndex = lines.findIndex(
77455
+ (line, index) => index > 0 && index < end && /^name\s*:/.test(line)
77456
+ );
77457
+ if (end < 0 || nameIndex < 0) {
77458
+ throw Object.assign(new Error("The local Skill has invalid SKILL.md frontmatter"), {
77459
+ code: "INVALID_FRONTMATTER"
77460
+ });
77461
+ }
77462
+ lines[nameIndex] = `name: ${name}`;
77463
+ return lines.join(lineEnding);
77464
+ }
77465
+ function sameConflict(left, right) {
77466
+ return left.kind === right.kind && left.runtimeDirectory === right.runtimeDirectory && left.observedHash === right.observedHash && left.observedName === right.observedName;
77467
+ }
77468
+ function resolutionFromReconcile(input2, result) {
77469
+ const resolved = result.skills.find(
77470
+ (candidate) => candidate.skillId === input2.skillId && candidate.versionId === input2.versionId
77471
+ );
77472
+ if (resolved?.state === "applied") {
77473
+ return {
77474
+ commandId: input2.commandId,
77475
+ skillId: input2.skillId,
77476
+ versionId: input2.versionId,
77477
+ state: "applied"
77478
+ };
77479
+ }
77480
+ return resolutionFailure(
77481
+ input2,
77482
+ "failed",
77483
+ resolved?.errorCode ?? "SKILL_RECONCILE_FAILED",
77484
+ resolved?.errorMessage ?? "The Skill could not be installed after resolving the conflict"
77485
+ );
77486
+ }
77487
+ function resolutionFailure(input2, state, errorCodeValue, errorMessage) {
77488
+ return {
77489
+ commandId: input2.commandId,
77490
+ skillId: input2.skillId,
77491
+ versionId: input2.versionId,
77492
+ state,
77493
+ errorCode: errorCodeValue,
77494
+ errorMessage
77495
+ };
77496
+ }
77236
77497
  function localRasterMediaType(path2) {
77237
77498
  const lower = path2.toLowerCase();
77238
77499
  if (lower.endsWith(".gif")) return "image/gif";
@@ -77556,12 +77817,13 @@ async function startDaemon(args) {
77556
77817
  transcriptStreamer: nativeTranscriptStreamer,
77557
77818
  sink: {
77558
77819
  deliver: (envelope) => new Promise((resolveDelivery, rejectDelivery) => {
77820
+ const payload = "eventType" in envelope ? { ...envelope, canonicalSourceDelivery: true } : envelope;
77559
77821
  socket.timeout(5e3).emit(
77560
77822
  "native.session.event",
77561
- envelope,
77823
+ payload,
77562
77824
  (error61, acknowledgement) => {
77563
- const accepted = !error61 && acknowledgement?.ok === true;
77564
- if (!accepted) {
77825
+ const accepted2 = !error61 && acknowledgement?.ok === true;
77826
+ if (!accepted2) {
77565
77827
  pushLog(
77566
77828
  `native-hook-delivery-deferred event=${envelope.eventId} provider=${envelope.provider} error=${error61?.message ?? "none"} ack=${acknowledgement?.ok ?? "missing"} retryable=${acknowledgement?.retryable ?? "missing"}`
77567
77829
  );
@@ -77574,10 +77836,24 @@ async function startDaemon(args) {
77574
77836
  );
77575
77837
  return;
77576
77838
  }
77577
- resolveDelivery(accepted);
77839
+ resolveDelivery({
77840
+ accepted: accepted2,
77841
+ ...!error61 && acknowledgement?.binding ? { binding: acknowledgement.binding } : {}
77842
+ });
77578
77843
  }
77579
77844
  );
77580
- })
77845
+ }),
77846
+ deliverCanonical: async (binding, source) => {
77847
+ eventOutbox.enqueueSource(
77848
+ binding.conversationId,
77849
+ binding.runId,
77850
+ source.eventId,
77851
+ source.event
77852
+ );
77853
+ eventOutbox.flush(socket);
77854
+ await eventOutbox.waitForAcknowledgement(source.eventId);
77855
+ return true;
77856
+ }
77581
77857
  },
77582
77858
  onTranscriptError: ({ envelope, error: error61 }) => pushLog(
77583
77859
  `native-transcript-unavailable event=${envelope.eventId} provider=${envelope.provider} error=${error61.message}`
@@ -80085,31 +80361,40 @@ function configFileContainsMarker(path2, marker) {
80085
80361
  return false;
80086
80362
  }
80087
80363
  }
80088
- function inspectNativeHooksConfigured(providerKind, homeDir = (0, import_node_os14.homedir)()) {
80364
+ function inspectNativeHooksConfigured(providerKind, homeDir = (0, import_node_os14.homedir)(), expectedConfigPath) {
80365
+ const matchesActiveProfile = (path2, marker) => {
80366
+ if (!configFileContainsMarker(path2, marker)) return false;
80367
+ if (!expectedConfigPath) return true;
80368
+ try {
80369
+ return extractNativeHookConfigPath((0, import_node_fs22.readFileSync)(path2, "utf8")) === expectedConfigPath;
80370
+ } catch {
80371
+ return false;
80372
+ }
80373
+ };
80089
80374
  switch (providerKind) {
80090
80375
  case "claude_cli":
80091
- return configFileContainsMarker((0, import_node_path25.join)(homeDir, ".claude", "settings.json"), ALAN_HOOK_MARKER);
80376
+ return matchesActiveProfile((0, import_node_path25.join)(homeDir, ".claude", "settings.json"), ALAN_HOOK_MARKER);
80092
80377
  case "codex_app_server":
80093
- return configFileContainsMarker((0, import_node_path25.join)(homeDir, ".codex", "hooks.json"), ALAN_HOOK_MARKER);
80378
+ return matchesActiveProfile((0, import_node_path25.join)(homeDir, ".codex", "hooks.json"), ALAN_HOOK_MARKER);
80094
80379
  case "copilot_cli":
80095
- return configFileContainsMarker(
80380
+ return matchesActiveProfile(
80096
80381
  (0, import_node_path25.join)(homeDir, ".copilot", "hooks", "alan-native-session.json"),
80097
80382
  ALAN_HOOK_MARKER
80098
80383
  );
80099
80384
  case "cursor_agent_cli":
80100
- return configFileContainsMarker((0, import_node_path25.join)(homeDir, ".cursor", "hooks.json"), ALAN_HOOK_MARKER);
80385
+ return matchesActiveProfile((0, import_node_path25.join)(homeDir, ".cursor", "hooks.json"), ALAN_HOOK_MARKER);
80101
80386
  case "kimi_cli":
80102
- return configFileContainsMarker(
80387
+ return matchesActiveProfile(
80103
80388
  (0, import_node_path25.join)(homeDir, ".kimi-code", "config.toml"),
80104
80389
  ALAN_KIMI_HOOKS_START
80105
80390
  );
80106
80391
  case "grok_cli":
80107
- return configFileContainsMarker(
80392
+ return matchesActiveProfile(
80108
80393
  (0, import_node_path25.join)(homeDir, ".grok", "hooks", "alan-native-session.json"),
80109
80394
  ALAN_HOOK_MARKER
80110
80395
  );
80111
80396
  case "opencode_cli":
80112
- return configFileContainsMarker(
80397
+ return matchesActiveProfile(
80113
80398
  (0, import_node_path25.join)(homeDir, ".config", "opencode", "plugins", "alan-native-session.js"),
80114
80399
  ALAN_OPENCODE_PLUGIN_MARKER
80115
80400
  );
@@ -80117,6 +80402,24 @@ function inspectNativeHooksConfigured(providerKind, homeDir = (0, import_node_os
80117
80402
  return false;
80118
80403
  }
80119
80404
  }
80405
+ function extractNativeHookConfigPath(text) {
80406
+ const jsonMatch = text.match(/"--config-path"\s*,\s*"((?:\\"|[^"])*)"/);
80407
+ if (jsonMatch?.[1]) {
80408
+ try {
80409
+ return JSON.parse(`"${jsonMatch[1]}"`);
80410
+ } catch {
80411
+ }
80412
+ }
80413
+ const match = text.match(
80414
+ /(?:['"])?--config-path(?:['"])?(?:=|\s+)('(?:\\'|[^'])*'|"(?:\\"|[^"])*"|[^\s#]+)/
80415
+ );
80416
+ if (!match?.[1]) return null;
80417
+ const raw = match[1];
80418
+ if (raw.startsWith("'") || raw.startsWith('"')) {
80419
+ return raw.slice(1, -1).replaceAll(String.raw`\'`, "'").replaceAll(String.raw`\"`, '"');
80420
+ }
80421
+ return raw;
80422
+ }
80120
80423
 
80121
80424
  // src/integration-cli.ts
80122
80425
  function alanMcpServers(apiUrl) {
@@ -80733,13 +81036,14 @@ function runNativeHookCommand(args) {
80733
81036
  function collectNativeIntegrationStatus(args = []) {
80734
81037
  bindConfigPath(args);
80735
81038
  const config2 = readConfig();
81039
+ const configPath = getConfigPath();
80736
81040
  const env = getDaemonCliEnvironment();
80737
81041
  const homeDir = (0, import_node_os15.homedir)();
80738
81042
  return NATIVE_SESSION_PROVIDER_CAPABILITIES.map((capability) => ({
80739
81043
  provider: capability.providerKind,
80740
81044
  command: capability.command,
80741
81045
  installed: resolveCliExecutable(capability.command, env) !== null,
80742
- hooksConfigured: inspectNativeHooksConfigured(capability.providerKind, homeDir),
81046
+ hooksConfigured: inspectNativeHooksConfigured(capability.providerKind, homeDir, configPath),
80743
81047
  hookCoverage: capability.capabilities.lifecycleHooks ? "lifecycle" : capability.capabilities.state === "degraded" ? "notification" : "none",
80744
81048
  transcriptStreaming: capability.capabilities.transcriptDelta,
80745
81049
  inboxReady: Boolean(config2.eventSpoolKey)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiden-ade/sandbox-agent",
3
- "version": "0.1.82",
3
+ "version": "0.1.83",
4
4
  "type": "module",
5
5
  "description": "Alan agent runtime — cloud sandbox and local daemon (alan-agent CLI)",
6
6
  "bin": {