@agentknight/sdk 0.9.47 → 0.9.48

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.
@@ -1,24 +1,25 @@
1
1
  import {
2
2
  hasClaudeProviderConfiguration
3
- } from "./chunk-EIFNERXM.js";
3
+ } from "./chunk-T6226JFS.js";
4
4
  import {
5
5
  latestPreviewItems
6
- } from "./chunk-QBKKQQCB.js";
6
+ } from "./chunk-6VEMXJW4.js";
7
7
  import {
8
8
  UnsupportedAgentOperationError,
9
9
  validateSessionName
10
- } from "./chunk-7A2SQAZ7.js";
10
+ } from "./chunk-R5LSWHRH.js";
11
11
  import {
12
12
  ConversationItemSchema,
13
13
  InteractionRequestSchema,
14
14
  InteractionResponseSchema,
15
15
  SESSION_TIMELINE_PAGE_SIZE,
16
16
  SessionSummarySchema
17
- } from "./chunk-DUMC5F2D.js";
17
+ } from "./chunk-BDKVGENQ.js";
18
18
 
19
19
  // src/claude-adapter.ts
20
20
  import { randomUUID as randomUUID2 } from "node:crypto";
21
21
  import { mkdir, readFile, readdir, rename, rm, writeFile } from "node:fs/promises";
22
+ import { homedir } from "node:os";
22
23
  import { isAbsolute, join, resolve } from "node:path";
23
24
  import { z as z4 } from "zod";
24
25
 
@@ -178,11 +179,13 @@ var ClaudeInteractions = class {
178
179
  import { createHash } from "node:crypto";
179
180
  import { z as z2 } from "zod";
180
181
  var identity = (value) => createHash("sha256").update(value).digest("hex");
182
+ var sourceId = (value) => value.length > 256 ? identity(value) : value;
181
183
  var ObjectSchema = z2.record(z2.string(), z2.unknown());
182
184
  var EnvelopeSchema = z2.object({
183
185
  type: z2.string(),
184
186
  uuid: z2.string().optional(),
185
- parent_tool_use_id: z2.string().nullable().optional()
187
+ parent_tool_use_id: z2.string().nullable().optional(),
188
+ timestamp: z2.iso.datetime().optional()
186
189
  }).passthrough();
187
190
  var BlockSchema = z2.object({
188
191
  type: z2.string(),
@@ -203,6 +206,7 @@ var ClaudeTimeline = class {
203
206
  publish;
204
207
  #streams = /* @__PURE__ */ new Map();
205
208
  #seen = /* @__PURE__ */ new Set();
209
+ #timestamp;
206
210
  upsert(value) {
207
211
  const index = this.items.findIndex((item2) => item2.id === value.id);
208
212
  const previous = this.items[index];
@@ -218,7 +222,13 @@ var ClaudeTimeline = class {
218
222
  this.publish(structuredClone(item));
219
223
  }
220
224
  base(id, turnId) {
221
- return { id, turnId, revision: 1, timestamp: (/* @__PURE__ */ new Date()).toISOString(), sourceEventIds: [id] };
225
+ return {
226
+ id: sourceId(id),
227
+ turnId: sourceId(turnId),
228
+ revision: 1,
229
+ timestamp: this.#timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
230
+ sourceEventIds: [sourceId(id)]
231
+ };
222
232
  }
223
233
  notice(turnId, content, severity = "error") {
224
234
  const id = identity(`${turnId}:notice:${content}`);
@@ -246,6 +256,14 @@ var ClaudeTimeline = class {
246
256
  }
247
257
  project(raw, turnId) {
248
258
  const message = EnvelopeSchema.parse(raw);
259
+ this.#timestamp = message.timestamp;
260
+ try {
261
+ this.#project(message, turnId);
262
+ } finally {
263
+ this.#timestamp = void 0;
264
+ }
265
+ }
266
+ #project(message, turnId) {
249
267
  if (message.uuid && this.#seen.has(message.uuid)) return;
250
268
  if (message.uuid) this.#seen.add(message.uuid);
251
269
  const source = message.uuid ?? turnId;
@@ -262,9 +280,21 @@ var ClaudeTimeline = class {
262
280
  const detached = z2.object({ detachedToolCall: z2.literal(true) }).safeParse(message.tool_use_result);
263
281
  if (detached.success) return;
264
282
  const body = z2.object({ content: z2.unknown() }).parse(message.message);
283
+ const blocks = Array.isArray(body.content) ? body.content.map((block) => BlockSchema.parse(block)) : [];
284
+ const text = typeof body.content === "string" ? body.content : blocks.filter((block) => block.type === "text").map((block) => block.text ?? "").join("\n");
285
+ const uuid = message.uuid;
286
+ if (text && uuid && !this.items.some((item) => item.id === sourceId(uuid))) {
287
+ this.upsert({
288
+ ...this.base(uuid, turnId),
289
+ sourceMessageIds: [sourceId(uuid)],
290
+ kind: "human-message",
291
+ actor: "human",
292
+ content: text,
293
+ attachments: []
294
+ });
295
+ }
265
296
  if (!Array.isArray(body.content)) return;
266
- for (const rawBlock of body.content) {
267
- const block = BlockSchema.parse(rawBlock);
297
+ for (const block of blocks) {
268
298
  if (block.type === "tool_result" && block.tool_use_id) {
269
299
  this.#tool(block.tool_use_id, void 0, block.is_error ? "failed" : "completed", turnId);
270
300
  }
@@ -321,7 +351,7 @@ var ClaudeTimeline = class {
321
351
  if (!final && old?.kind === "assistant-message" && old.final) return;
322
352
  this.upsert({
323
353
  ...this.base(id, old?.turnId ?? turnId),
324
- sourceMessageIds: [messageId],
354
+ sourceMessageIds: [sourceId(messageId)],
325
355
  kind: "assistant-message",
326
356
  actor: "assistant",
327
357
  content,
@@ -339,7 +369,7 @@ var ClaudeTimeline = class {
339
369
  kind: "activity-group",
340
370
  actor: "assistant",
341
371
  status,
342
- sourceMessageIds: [toolId],
372
+ sourceMessageIds: [sourceId(toolId)],
343
373
  steps: [{ id, kind: "tool", name: (name ?? oldStep?.name ?? "Tool").slice(0, 256), status }]
344
374
  });
345
375
  }
@@ -458,9 +488,12 @@ var StoredSchema = z4.object({
458
488
  summary: SessionSummarySchema,
459
489
  nativeId: UuidSchema.optional(),
460
490
  modelId: z4.string().min(1).max(256),
491
+ nativeDirectory: z4.string().min(1).optional(),
461
492
  items: z4.array(ConversationItemSchema),
462
493
  receipts: z4.array(ReceiptSchema),
463
494
  unsafe: z4.boolean()
495
+ }).refine((stored) => stored.nativeDirectory === void 0 || stored.nativeId !== void 0 && stored.summary.sessionId === `claude:native:${stored.nativeId}`, {
496
+ message: "Invalid Claude native session identity"
464
497
  });
465
498
  var MessageSchema = z4.object({
466
499
  type: z4.string(),
@@ -486,6 +519,8 @@ var ClaudeCodingAgentAdapter = class {
486
519
  #options;
487
520
  #directory;
488
521
  #sessions = /* @__PURE__ */ new Map();
522
+ #discovered = /* @__PURE__ */ new Map();
523
+ #retiredSessions = /* @__PURE__ */ new Set();
489
524
  #interactions;
490
525
  #callbacks;
491
526
  #started = false;
@@ -502,6 +537,9 @@ var ClaudeCodingAgentAdapter = class {
502
537
  const exitTimeout = options.processExitTimeoutMs ?? 5e3;
503
538
  if (!Number.isSafeInteger(exitTimeout) || exitTimeout < 1) throw new Error("Invalid Claude process exit timeout");
504
539
  this.#options = options;
540
+ if (options.nativeSessionOwner !== void 0) {
541
+ SessionSummarySchema.pick({ userId: true, machineId: true }).parse(options.nativeSessionOwner);
542
+ }
505
543
  this.#directory = join(resolve(options.dataDirectory), "claude");
506
544
  this.#interactions = new ClaudeInteractions(() => this.#callbacks, timeout);
507
545
  }
@@ -571,12 +609,12 @@ var ClaudeCodingAgentAdapter = class {
571
609
  }
572
610
  #known(id) {
573
611
  this.#ready();
574
- const session = this.#sessions.get(id);
612
+ const session = this.#sessions.get(id) ?? this.#discovered.get(id);
575
613
  if (!session) throw new Error(`Unknown host-owned Claude session: ${id}`);
576
614
  return session;
577
615
  }
578
616
  isSessionVisible(id) {
579
- return this.#started && !this.#stopping && this.#sessions.has(id);
617
+ return this.#started && !this.#stopping && (this.#sessions.has(id) || this.#discovered.has(id));
580
618
  }
581
619
  sessionPublicationSource(id) {
582
620
  this.#known(id);
@@ -587,7 +625,77 @@ var ClaudeCodingAgentAdapter = class {
587
625
  }
588
626
  async listSessions(userId, machineId) {
589
627
  this.#ready();
590
- return [...this.#sessions.values()].filter((s) => s.stored.summary.userId === userId && s.stored.summary.machineId === machineId).map((s) => structuredClone(s.stored.summary));
628
+ const owner = this.#options.nativeSessionOwner;
629
+ if (owner?.userId === userId && owner.machineId === machineId) await this.#scanNative(owner);
630
+ return [...this.#sessions.values(), ...this.#discovered.values()].filter((s) => s.stored.summary.userId === userId && s.stored.summary.machineId === machineId).map((s) => structuredClone(s.stored.summary));
631
+ }
632
+ async #nativeSessions() {
633
+ return this.#options.nativeSessions ?? import("@anthropic-ai/claude-agent-sdk");
634
+ }
635
+ async #scanNative(owner) {
636
+ const sdk = await this.#nativeSessions();
637
+ const sessions = z4.array(z4.object({
638
+ sessionId: UuidSchema,
639
+ summary: z4.string(),
640
+ customTitle: z4.string().optional(),
641
+ lastModified: z4.number(),
642
+ createdAt: z4.number().optional(),
643
+ cwd: z4.string().optional()
644
+ })).parse(await sdk.listSessions({ includeProgrammatic: true }));
645
+ this.#ready();
646
+ const next = /* @__PURE__ */ new Map();
647
+ const present = new Set(sessions.map((native) => `claude:native:${native.sessionId}`));
648
+ for (const native of sessions) {
649
+ const owned = [...this.#sessions.values()].find((session) => session.stored.nativeId === native.sessionId);
650
+ if (owned && !owned.stored.nativeDirectory) continue;
651
+ const id = `claude:native:${native.sessionId}`;
652
+ this.#retiredSessions.delete(id);
653
+ const previous = owned ?? this.#discovered.get(id);
654
+ if (previous && (previous.stored.summary.userId !== owner.userId || previous.stored.summary.machineId !== owner.machineId)) {
655
+ throw new Error("Claude native session belongs to another PC owner");
656
+ }
657
+ const summary = SessionSummarySchema.parse({
658
+ ...owner,
659
+ sessionId: id,
660
+ summary: (native.customTitle || native.summary).slice(0, 4096),
661
+ ...native.cwd === void 0 ? {} : { workingDirectory: native.cwd },
662
+ createdAt: previous?.stored.summary.createdAt ?? new Date(native.createdAt ?? native.lastModified).toISOString(),
663
+ modifiedAt: new Date(native.lastModified).toISOString(),
664
+ isRemote: false,
665
+ renameSupported: true
666
+ });
667
+ if (previous) {
668
+ previous.stored.summary = summary;
669
+ if (!owned) next.set(id, previous);
670
+ } else {
671
+ const env = this.#options.env ?? process.env;
672
+ next.set(id, this.#newSession({
673
+ version: 1,
674
+ summary,
675
+ nativeId: native.sessionId,
676
+ nativeDirectory: resolve(env.CLAUDE_CONFIG_DIR || join(homedir(), ".claude")),
677
+ modelId: CLAUDE_DEFAULT_MODEL_ID,
678
+ items: [],
679
+ receipts: [],
680
+ unsafe: false
681
+ }));
682
+ }
683
+ }
684
+ for (const id of this.#discovered.keys()) {
685
+ if (!next.has(id) && !this.#sessions.has(id)) this.#retiredSessions.add(id);
686
+ }
687
+ this.#discovered.clear();
688
+ for (const [id, session] of next) this.#discovered.set(id, session);
689
+ for (const [id, session] of this.#sessions) {
690
+ if (session.stored.nativeDirectory && !present.has(id) && !session.work && !session.operations && !session.queue.length) {
691
+ await session.saving;
692
+ this.#ready();
693
+ if (session.work || session.operations || session.cancellations || session.aborting || session.stored.unsafe || session.error) continue;
694
+ this.#sessions.delete(id);
695
+ this.#retiredSessions.add(id);
696
+ await rm(join(this.#directory, `${identity(id)}.json`));
697
+ }
698
+ }
591
699
  }
592
700
  async listModels() {
593
701
  this.#ready();
@@ -631,7 +739,9 @@ var ClaudeCodingAgentAdapter = class {
631
739
  async setPeerChatEnabled(enabled) {
632
740
  if (enabled) this.#unsupported("peer chat");
633
741
  }
634
- async watchSession(id, _watched) {
742
+ async watchSession(id, watched) {
743
+ this.#ready();
744
+ if (!watched && this.#retiredSessions.has(id)) return;
635
745
  this.#known(id);
636
746
  }
637
747
  #validate(options) {
@@ -670,6 +780,13 @@ var ClaudeCodingAgentAdapter = class {
670
780
  const session = this.#known(id);
671
781
  const title = validateSessionName(name);
672
782
  assertAccess?.();
783
+ if (session.stored.nativeDirectory && session.stored.nativeId) {
784
+ const sdk = await this.#nativeSessions();
785
+ assertAccess?.();
786
+ await sdk.renameSession(session.stored.nativeId, title, this.#nativeReadOptions(session));
787
+ assertAccess?.();
788
+ this.#known(id);
789
+ }
673
790
  session.stored.summary.summary = title;
674
791
  await this.#save(session);
675
792
  this.#callbacks?.onSessionInventoryChanged(id);
@@ -679,6 +796,18 @@ var ClaudeCodingAgentAdapter = class {
679
796
  const session = this.#known(id);
680
797
  const size = options.pageSize ?? SESSION_TIMELINE_PAGE_SIZE;
681
798
  if (!Number.isSafeInteger(size) || size < 1 || size > SESSION_TIMELINE_PAGE_SIZE) throw new Error("Invalid Claude page size");
799
+ if (session.stored.nativeDirectory && !session.work) {
800
+ session.operations++;
801
+ const read = session.lock.then(async () => {
802
+ if (!session.work && !session.queue.length) await this.#readNativeHistory(session);
803
+ });
804
+ session.lock = read.then(() => void 0, () => void 0);
805
+ try {
806
+ await read;
807
+ } finally {
808
+ session.operations--;
809
+ }
810
+ }
682
811
  const items = session.stored.items;
683
812
  const boundary = options.cursor === void 0 ? items.length : items.findIndex((item) => `${identity(id)}:${item.id}` === options.cursor);
684
813
  const end = boundary < 0 ? items.length : boundary;
@@ -691,6 +820,50 @@ var ClaudeCodingAgentAdapter = class {
691
820
  cursorStatus: boundary < 0 ? "expired" : "ok"
692
821
  };
693
822
  }
823
+ #nativeReadOptions(session) {
824
+ const dir = session.stored.summary.workingDirectory;
825
+ return dir === void 0 ? {} : { dir };
826
+ }
827
+ async #readNativeHistory(session) {
828
+ const sdk = await this.#nativeSessions();
829
+ const nativeId = UuidSchema.parse(session.stored.nativeId);
830
+ const raw = await sdk.getSessionMessages(nativeId, this.#nativeReadOptions(session));
831
+ const messages = z4.array(z4.object({
832
+ type: z4.enum(["user", "assistant", "system"]),
833
+ uuid: z4.string(),
834
+ session_id: UuidSchema,
835
+ message: z4.unknown(),
836
+ timestamp: z4.iso.datetime().optional(),
837
+ parent_tool_use_id: z4.string().nullable(),
838
+ parent_agent_id: z4.string().nullable()
839
+ }).passthrough()).parse(raw);
840
+ this.#known(session.stored.summary.sessionId);
841
+ const items = [];
842
+ const timeline = new ClaudeTimeline(session.stored.summary.sessionId, items, () => void 0);
843
+ let turnId = session.stored.summary.sessionId;
844
+ for (const message of messages) {
845
+ if (message.session_id !== nativeId) throw new Error("Claude returned history for another native session");
846
+ if (message.parent_agent_id || message.parent_tool_use_id) continue;
847
+ if (message.type === "user") {
848
+ const body = z4.object({ content: z4.unknown() }).parse(message.message);
849
+ if (typeof body.content === "string" || Array.isArray(body.content) && body.content.some((block) => z4.object({ type: z4.literal("text") }).safeParse(block).success)) turnId = message.uuid;
850
+ }
851
+ timeline.project({ ...message, timestamp: message.timestamp ?? session.stored.summary.createdAt }, turnId);
852
+ }
853
+ const projected = items.map((item) => {
854
+ const previous = session.stored.items.find((entry) => entry.id === item.id);
855
+ if (!previous) return item;
856
+ const candidate = { ...item, timestamp: previous.timestamp, revision: previous.revision };
857
+ if (previous.kind === "human-message" && candidate.kind === "human-message" && previous.operationId) {
858
+ candidate.operationId = previous.operationId;
859
+ }
860
+ if (JSON.stringify(candidate) !== JSON.stringify(previous)) candidate.revision++;
861
+ return candidate;
862
+ });
863
+ const ids = new Set(projected.map((item) => item.id));
864
+ const notices = session.stored.items.filter((item) => item.kind === "notice" && item.actor === "system" && item.severity !== "info" && !ids.has(item.id));
865
+ session.stored.items.splice(0, session.stored.items.length, ...projected, ...notices);
866
+ }
694
867
  async sendPrompt(id, prompt, mode, attachments = [], _persistInteractions = true, files = [], operationId, avatar) {
695
868
  const session = this.#known(id);
696
869
  if (attachments.length || files.length) this.#unsupported("attachments");
@@ -705,6 +878,11 @@ var ClaudeCodingAgentAdapter = class {
705
878
  if (session.error instanceof ClaudeProcessTerminationError) throw session.error;
706
879
  if (session.error) throw new Error("Claude session persistence failed", { cause: session.error });
707
880
  if (session.stored.unsafe) throw new Error("Claude native session identity is uncertain; create a new session");
881
+ if (session.stored.nativeDirectory) {
882
+ this.#environment(session);
883
+ const directory = session.stored.summary.workingDirectory;
884
+ if (!directory || !isAbsolute(directory)) throw new Error("Claude native session working directory is unavailable; open it in the CLI first");
885
+ }
708
886
  const duplicate = operationId === void 0 ? void 0 : session.stored.receipts.find((entry) => entry.operationId === operationId);
709
887
  if (duplicate) {
710
888
  if (duplicate.prompt !== prompt || duplicate.mode !== mode) throw new Error("Claude operation ID already has different content");
@@ -717,6 +895,12 @@ var ClaudeCodingAgentAdapter = class {
717
895
  if (session.stored.unsafe) throw new Error("Claude native session identity is uncertain; create a new session");
718
896
  }
719
897
  const acceptedEpoch = session.epoch;
898
+ if (this.#discovered.has(id)) {
899
+ await this.#readNativeHistory(session);
900
+ if (session.epoch !== epoch || this.#stopping) throw new Error("Claude prompt cancelled before acceptance");
901
+ this.#sessions.set(id, session);
902
+ this.#discovered.delete(id);
903
+ }
720
904
  const queued = session.busy || session.queue.length > 0;
721
905
  const receipt = ReceiptSchema.parse({
722
906
  id: randomUUID2(),
@@ -766,8 +950,11 @@ var ClaudeCodingAgentAdapter = class {
766
950
  this.#callbacks?.onSyncError(session.stored.summary.sessionId, error);
767
951
  });
768
952
  }
769
- #environment() {
770
- return prepareClaudeEnvironment(this.#options.env ?? process.env, join(this.#directory, "native"));
953
+ #environment(session) {
954
+ return prepareClaudeEnvironment(
955
+ this.#options.env ?? process.env,
956
+ session.stored.nativeDirectory ?? join(this.#directory, "native")
957
+ );
771
958
  }
772
959
  async #consume(session, epoch) {
773
960
  const id = session.stored.summary.sessionId;
@@ -778,7 +965,7 @@ var ClaudeCodingAgentAdapter = class {
778
965
  session.controller = controller;
779
966
  const seen = /* @__PURE__ */ new Set();
780
967
  try {
781
- const env = this.#environment();
968
+ const env = this.#environment(session);
782
969
  const sdk = this.#options.sdk ?? await import("@anthropic-ai/claude-agent-sdk");
783
970
  if (epoch !== session.epoch || this.#stopping) return;
784
971
  const query = sdk.query({
@@ -943,7 +1130,14 @@ var ClaudeCodingAgentAdapter = class {
943
1130
  }
944
1131
  }
945
1132
  async abortSession(id) {
1133
+ this.#ready();
1134
+ if (this.#retiredSessions.has(id)) return;
946
1135
  const session = this.#known(id);
1136
+ if (this.#discovered.has(id)) {
1137
+ session.epoch++;
1138
+ await session.lock;
1139
+ return;
1140
+ }
947
1141
  if (session.aborting) return session.aborting;
948
1142
  const cancellation = this.#cancel(session, true);
949
1143
  const work = (async () => {
@@ -993,6 +1187,7 @@ var ClaudeCodingAgentAdapter = class {
993
1187
  this.#interactions.replay();
994
1188
  }
995
1189
  #save(session) {
1190
+ if (!this.#sessions.has(session.stored.summary.sessionId)) return Promise.resolve();
996
1191
  const data = JSON.stringify(StoredSchema.parse(session.stored));
997
1192
  const file = join(this.#directory, `${identity(session.stored.summary.sessionId)}.json`);
998
1193
  session.saves++;
@@ -1032,6 +1227,7 @@ var ClaudeCodingAgentAdapter = class {
1032
1227
  await session.saving;
1033
1228
  }));
1034
1229
  this.#started = false;
1230
+ this.#retiredSessions.clear();
1035
1231
  }
1036
1232
  };
1037
1233
  export {