@harness-kernel/core 0.1.1 → 0.2.1

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 (46) hide show
  1. package/dist/agent/context.d.ts +1 -1
  2. package/dist/agent/context.js +1 -1
  3. package/dist/agent/event.js +2 -2
  4. package/dist/agent/hook.d.ts +2 -2
  5. package/dist/agent/hook.js +1 -1
  6. package/dist/agent/mode.d.ts +1 -1
  7. package/dist/agent/mode.js +1 -1
  8. package/dist/agent/role.js +1 -1
  9. package/dist/agent/session.d.ts +1 -1
  10. package/dist/agent/session.js +1 -1
  11. package/dist/agent/tool.d.ts +1 -1
  12. package/dist/agent/tool.js +1 -1
  13. package/dist/agent.d.ts +4 -4
  14. package/dist/{approval-DfvjpbFs.d.ts → approval-D_G2w-fW.d.ts} +10 -14
  15. package/dist/{chunk-4A2P4QU5.js → chunk-B4Q6CPYO.js} +2 -1
  16. package/dist/chunk-B4Q6CPYO.js.map +1 -0
  17. package/dist/{chunk-4SYLFKIX.js → chunk-JIJHGB6H.js} +2 -2
  18. package/dist/chunk-ONYDIU4X.js +284 -0
  19. package/dist/chunk-ONYDIU4X.js.map +1 -0
  20. package/dist/{chunk-4WWSQAWA.js → chunk-QEVKKJ7N.js} +199 -75
  21. package/dist/chunk-QEVKKJ7N.js.map +1 -0
  22. package/dist/{chunk-AD3BCYWU.js → chunk-ZU6ADDET.js} +1 -1
  23. package/dist/chunk-ZU6ADDET.js.map +1 -0
  24. package/dist/{context-75mlon5x.d.ts → context-BfpLqV11.d.ts} +5 -3
  25. package/dist/{hook-DMb9fw9Z.d.ts → hook-CfBbhUQf.d.ts} +1 -1
  26. package/dist/index.d.ts +13 -11
  27. package/dist/index.js +9 -5
  28. package/dist/{model-provider-BrZ2RRmS.d.ts → model-provider-Ch7tzk1x.d.ts} +6 -8
  29. package/dist/runner/approval.d.ts +5 -5
  30. package/dist/runner/event.js +2 -2
  31. package/dist/runner/model-provider.d.ts +4 -4
  32. package/dist/runner/sandbox.d.ts +1 -1
  33. package/dist/runner/sandbox.js +1 -1
  34. package/dist/runner/storage.d.ts +2 -2
  35. package/dist/runner/storage.js +5 -1
  36. package/dist/{runner-Dxo7ALtp.d.ts → runner-B41JEovO.d.ts} +16 -9
  37. package/dist/runner.d.ts +6 -6
  38. package/dist/runner.js +5 -5
  39. package/dist/{storage-BmOEwW-p.d.ts → storage-DCZE_hES.d.ts} +84 -2
  40. package/package.json +2 -2
  41. package/dist/chunk-4A2P4QU5.js.map +0 -1
  42. package/dist/chunk-4WWSQAWA.js.map +0 -1
  43. package/dist/chunk-AD3BCYWU.js.map +0 -1
  44. package/dist/chunk-AZVA22HW.js +0 -135
  45. package/dist/chunk-AZVA22HW.js.map +0 -1
  46. /package/dist/{chunk-4SYLFKIX.js.map → chunk-JIJHGB6H.js.map} +0 -0
@@ -17,10 +17,11 @@ import {
17
17
  import {
18
18
  HarnessSandboxSession,
19
19
  NoopSandbox
20
- } from "./chunk-AD3BCYWU.js";
20
+ } from "./chunk-ZU6ADDET.js";
21
21
  import {
22
- NoopRunStorage
23
- } from "./chunk-AZVA22HW.js";
22
+ HarnessSessionStorage,
23
+ MemorySessionStorage
24
+ } from "./chunk-ONYDIU4X.js";
24
25
  import {
25
26
  ContextReadyEvent,
26
27
  ErrorEvent,
@@ -43,7 +44,7 @@ import {
43
44
  TurnEndEvent,
44
45
  TurnStartEvent,
45
46
  runtimeEventClasses
46
- } from "./chunk-4SYLFKIX.js";
47
+ } from "./chunk-JIJHGB6H.js";
47
48
  import {
48
49
  normalizeSchema
49
50
  } from "./chunk-OBKS4AJR.js";
@@ -58,7 +59,7 @@ import {
58
59
  systemRole,
59
60
  toolRole,
60
61
  userRole
61
- } from "./chunk-4A2P4QU5.js";
62
+ } from "./chunk-B4Q6CPYO.js";
62
63
 
63
64
  // src/logging/tool-errors.ts
64
65
  function createToolErrorPayload(input) {
@@ -576,7 +577,7 @@ var SandboxManager = class {
576
577
  agentKey: this.input.agentKey,
577
578
  workDir: this.input.workDir,
578
579
  outputDir: this.input.getOutputDir(),
579
- services: this.input.services
580
+ resources: this.input.resources
580
581
  });
581
582
  this.session = this.wrap(opened);
582
583
  this.input.logOpened({ sandboxId: opened.id, workDir: opened.workDir });
@@ -633,7 +634,7 @@ var RunStorageCoordinator = class {
633
634
  constructor(input) {
634
635
  this.input = input;
635
636
  this.runIdValue = input.runId;
636
- this.storePromise = this.createStore(this.runIdValue);
637
+ this.createRunOnOpen = !input.openExistingRun;
637
638
  }
638
639
  input;
639
640
  runIdValue;
@@ -641,6 +642,7 @@ var RunStorageCoordinator = class {
641
642
  storePromise;
642
643
  initialized = false;
643
644
  runtimeLoaded = false;
645
+ createRunOnOpen;
644
646
  get runDir() {
645
647
  return this.store?.runDir;
646
648
  }
@@ -664,6 +666,7 @@ var RunStorageCoordinator = class {
664
666
  await this.write("save_transcript", (store) => store.saveTranscript(messages));
665
667
  }
666
668
  async saveMetrics(metrics) {
669
+ if (this.input.storage instanceof HarnessSessionStorage) return;
667
670
  await this.write("save_metrics", (store) => store.saveMetrics(metrics));
668
671
  }
669
672
  async saveSnapshot(snapshot) {
@@ -685,16 +688,27 @@ var RunStorageCoordinator = class {
685
688
  await this.close();
686
689
  this.runIdValue = runId;
687
690
  this.store = void 0;
688
- this.storePromise = this.createStore(runId);
691
+ this.storePromise = void 0;
689
692
  this.initialized = false;
690
693
  this.runtimeLoaded = false;
694
+ this.createRunOnOpen = true;
691
695
  }
692
696
  async close() {
693
- const store = this.store ?? await this.storePromise;
694
- await store.close?.();
697
+ const store = this.store ?? (this.storePromise ? await this.storePromise : void 0);
698
+ await store?.close?.();
695
699
  }
696
700
  async createStore(runId) {
697
701
  try {
702
+ if (this.input.storage instanceof HarnessSessionStorage && this.createRunOnOpen) {
703
+ await this.input.storage.createRun({
704
+ runId,
705
+ sessionId: this.input.sessionId,
706
+ agentKey: this.input.agentKey,
707
+ outputDir: this.input.outputDir,
708
+ mode: this.input.mode()
709
+ });
710
+ }
711
+ this.createRunOnOpen = false;
698
712
  const store = await this.input.storage.openRun({
699
713
  runId,
700
714
  sessionId: this.input.sessionId,
@@ -710,6 +724,7 @@ var RunStorageCoordinator = class {
710
724
  }
711
725
  async getStore() {
712
726
  if (this.store) return this.store;
727
+ this.storePromise ??= this.createStore(this.runIdValue);
713
728
  this.store = await this.storePromise;
714
729
  return this.store;
715
730
  }
@@ -1228,7 +1243,15 @@ var ToolExecutor = class {
1228
1243
  return result;
1229
1244
  }
1230
1245
  const approved = await this.approveTool(
1231
- { id, name: input.tool.name, args: parsedArgs.data, modeId: this.input.getCurrentMode(), risk: input.tool.risk, permissions: input.tool.permissions },
1246
+ {
1247
+ id,
1248
+ name: input.tool.name,
1249
+ args: parsedArgs.data,
1250
+ modeId: this.input.getCurrentMode(),
1251
+ risk: input.tool.risk,
1252
+ permissions: input.tool.permissions,
1253
+ approvalTimeoutMs: input.tool.approvalTimeoutMs
1254
+ },
1232
1255
  input.tool,
1233
1256
  parsedArgs.data,
1234
1257
  callerEventOptions
@@ -1728,13 +1751,15 @@ var AgentSessionRunner = class {
1728
1751
  this.options = options;
1729
1752
  this.agent = normalizeAgent(options.agent);
1730
1753
  this.sessionIdValue = options.sessionId ?? randomId();
1754
+ this.runIdValue = options.initialRunId ?? randomId();
1755
+ this.restoredFromRun = Boolean(options.initialRunId);
1731
1756
  this.workDir = options.workDir ?? ".";
1732
1757
  this.outputDir = options.outputDir ?? ".harness-kernel/runs";
1733
- const storage = options.storage ?? new NoopRunStorage();
1758
+ const storage = options.storage ?? new MemorySessionStorage();
1734
1759
  const sandbox = options.sandbox ?? new NoopSandbox();
1735
1760
  this.providerRegistry = new HarnessModelProviderRegistry(options.providers);
1736
- this.currentMode = options.initialMode ? this.resolveModeType(options.initialMode) : this.agent.initialMode;
1737
- this.services = options.services ?? {};
1761
+ this.currentMode = this.agent.initialMode;
1762
+ this.resources = options.resources ?? {};
1738
1763
  this.roles = validateRoles(options.roles) ?? this.agent.roles ?? defaultRoles();
1739
1764
  const initialProvider = this.resolveModelProvider(options.defaultModel).provider;
1740
1765
  this.roleResolver = new RoleResolver(this.roles, {
@@ -1748,6 +1773,8 @@ var AgentSessionRunner = class {
1748
1773
  sessionId: this.sessionIdValue,
1749
1774
  agentKey: this.agent.key,
1750
1775
  outputDir: this.outputDir,
1776
+ mode: () => this.currentMode,
1777
+ openExistingRun: Boolean(options.initialRunId),
1751
1778
  logOpened: (fields) => this.log(RunStorageOpenedLog, fields),
1752
1779
  logFailed: (fields) => this.log(StorageWriteFailedLog, fields)
1753
1780
  });
@@ -1836,7 +1863,7 @@ var AgentSessionRunner = class {
1836
1863
  sessionId: this.sessionIdValue,
1837
1864
  agentKey: this.agent.key,
1838
1865
  workDir: this.workDir,
1839
- services: this.services,
1866
+ resources: this.resources,
1840
1867
  getRunId: () => this.runId,
1841
1868
  getOutputDir: () => this.storeRunDir(),
1842
1869
  logOpened: (fields) => this.log(SandboxOpenedLog, fields),
@@ -1848,7 +1875,7 @@ var AgentSessionRunner = class {
1848
1875
  this.toolExecutor = new ToolExecutor({
1849
1876
  getMetrics: () => this.metrics,
1850
1877
  getCurrentMode: () => this.currentMode,
1851
- getToolApprovalMode: () => this.options.toolApproval ?? this.getModeDefinition(this.currentMode).toolApproval,
1878
+ getToolApprovalMode: () => this.getModeDefinition(this.currentMode).toolApproval,
1852
1879
  approveTool: this.options.approveTool,
1853
1880
  ensureSandboxOpen: () => this.ensureSandboxOpen(),
1854
1881
  buildActionSession: (tool, source, correlationId, causationId) => this.buildActionSession(tool, source, correlationId, causationId),
@@ -1899,7 +1926,7 @@ var AgentSessionRunner = class {
1899
1926
  snapshotManager;
1900
1927
  toolExecutor;
1901
1928
  modelPipeline;
1902
- services;
1929
+ resources;
1903
1930
  roles;
1904
1931
  roleResolver;
1905
1932
  providerRegistry;
@@ -1917,6 +1944,7 @@ var AgentSessionRunner = class {
1917
1944
  providerStack = [];
1918
1945
  hookDepth = 0;
1919
1946
  turnHandoffRequested = false;
1947
+ restoredFromRun = false;
1920
1948
  logSource(source) {
1921
1949
  if (!source) return { kind: "runtime" };
1922
1950
  return {
@@ -2012,11 +2040,12 @@ var AgentSessionRunner = class {
2012
2040
  });
2013
2041
  }
2014
2042
  async beginNewRun() {
2015
- if (this.started) {
2043
+ if (this.started || this.restoredFromRun) {
2016
2044
  await this.closeSandbox();
2017
2045
  this.runIdValue = randomId();
2018
2046
  await this.storageCoordinator.beginRun(this.runIdValue);
2019
2047
  this.started = false;
2048
+ this.restoredFromRun = false;
2020
2049
  }
2021
2050
  this.startedAtPerf = 0;
2022
2051
  this.metrics = emptyMetrics();
@@ -2039,7 +2068,7 @@ var AgentSessionRunner = class {
2039
2068
  role: options.userRole,
2040
2069
  external: true
2041
2070
  });
2042
- const maxRunnerTurns = this.options.maxTurns ?? 5;
2071
+ const maxRunnerTurns = this.getModeDefinition(this.currentMode).maxTurns ?? 5;
2043
2072
  while (this.pendingInputs.length > 0 && this.metrics.turnCount < maxRunnerTurns) {
2044
2073
  if (options.signal?.aborted) throw new Error("Run aborted.");
2045
2074
  const input = this.pendingInputs.shift();
@@ -2060,7 +2089,8 @@ var AgentSessionRunner = class {
2060
2089
  this.log(RunCompletedLog, {
2061
2090
  durationMs: this.metrics.durationMs,
2062
2091
  messageCount: this.metrics.messageCount,
2063
- eventCount: this.metrics.eventCount
2092
+ eventCount: this.metrics.eventCount,
2093
+ metrics: cloneJSON4(this.metrics)
2064
2094
  });
2065
2095
  return {
2066
2096
  runId: this.runId,
@@ -2082,6 +2112,10 @@ var AgentSessionRunner = class {
2082
2112
  async prompt(message, options = {}) {
2083
2113
  return this.run(message, options);
2084
2114
  }
2115
+ async hydrate() {
2116
+ if (!this.restoredFromRun) return;
2117
+ await this.ensureStoreInitialized();
2118
+ }
2085
2119
  async close() {
2086
2120
  await this.closeSandbox();
2087
2121
  await this.closeStore();
@@ -2370,7 +2404,7 @@ var AgentSessionRunner = class {
2370
2404
  agentKey: this.agent.key,
2371
2405
  workDir: this.workDir,
2372
2406
  outputDir: this.storeRunDir(),
2373
- services: this.services,
2407
+ resources: this.resources,
2374
2408
  state: {
2375
2409
  get: () => cloneJSON4(this.state)
2376
2410
  },
@@ -2936,17 +2970,18 @@ var ApprovalController = class {
2936
2970
  }
2937
2971
  request(request) {
2938
2972
  return new Promise((resolve) => {
2973
+ const timeoutMs = request.approvalTimeoutMs ?? this.input.defaultTimeoutMs;
2939
2974
  const handle = createToolApprovalHandle({
2940
2975
  sessionId: this.input.sessionId,
2941
2976
  runId: this.input.getRunId(),
2942
2977
  request,
2943
- timeoutMs: this.input.timeoutMs,
2978
+ timeoutMs,
2944
2979
  approve: async (id) => this.resolve(id, true),
2945
2980
  deny: async (id, reason) => this.resolve(id, false, reason)
2946
2981
  });
2947
- const timeout = setTimeout(() => {
2982
+ const timeout = timeoutMs > 0 ? setTimeout(() => {
2948
2983
  this.resolve(handle.id, false);
2949
- }, this.input.timeoutMs);
2984
+ }, timeoutMs) : void 0;
2950
2985
  this.pendingApprovals.set(handle.id, {
2951
2986
  handle,
2952
2987
  timeout,
@@ -2974,11 +3009,8 @@ var ApprovalController = class {
2974
3009
  async function resolveAgent(input) {
2975
3010
  return input.definition;
2976
3011
  }
2977
- function resolveWorkDir(workDir) {
2978
- return workDir ?? ".";
2979
- }
2980
3012
  function resolveStorage(storage) {
2981
- return storage ?? new NoopRunStorage();
3013
+ return storage ?? new MemorySessionStorage();
2982
3014
  }
2983
3015
 
2984
3016
  // src/session/event-hub.ts
@@ -3353,14 +3385,6 @@ function nowIso6() {
3353
3385
  function normalizeInput(input) {
3354
3386
  return typeof input === "string" ? { content: input } : input;
3355
3387
  }
3356
- function resolveInitialMode(agent, mode) {
3357
- if (mode === void 0) return void 0;
3358
- if (typeof mode !== "string") return mode;
3359
- if (!Array.isArray(agent.modes)) return void 0;
3360
- const resolved = agent.modes.find((candidate) => getConstructType(candidate) === mode);
3361
- if (!resolved) throw new Error(`Unknown initial mode '${mode}'.`);
3362
- return resolved;
3363
- }
3364
3388
  function toErrorShape(error) {
3365
3389
  if (error instanceof Error) {
3366
3390
  return {
@@ -3376,6 +3400,8 @@ var HarnessSessionImpl = class {
3376
3400
  constructor(config, input) {
3377
3401
  this.config = config;
3378
3402
  this.id = input.sessionId ?? randomId();
3403
+ this.createdAt = input.restoredSession?.createdAt ?? this.createdAt;
3404
+ this.lastActiveAt = input.restoredSession?.lastActiveAt ?? this.createdAt;
3379
3405
  this.events = new SessionEventHub({
3380
3406
  sessionId: this.id,
3381
3407
  hydrateApprovalId: (toolCallId) => this.approvals.hydrateApprovalId(toolCallId)
@@ -3384,7 +3410,7 @@ var HarnessSessionImpl = class {
3384
3410
  this.approvals = new ApprovalController({
3385
3411
  sessionId: this.id,
3386
3412
  getRunId: () => this.runner.runId,
3387
- timeoutMs: this.config.toolApprovalTimeoutMs ?? defaultApprovalTimeoutMs,
3413
+ defaultTimeoutMs: defaultApprovalTimeoutMs,
3388
3414
  notifyRequested: (approval) => this.notify({ type: "tool.approval.requested", approval })
3389
3415
  });
3390
3416
  const storage = resolveStorage(config.storage);
@@ -3394,12 +3420,9 @@ var HarnessSessionImpl = class {
3394
3420
  providers: config.providers,
3395
3421
  defaultModel: config.defaultModel,
3396
3422
  sandbox: config.sandbox,
3397
- workDir: input.workDir,
3398
3423
  storage,
3399
- initialMode: resolveInitialMode(input.agent, config.initialMode),
3400
- toolApproval: config.toolApproval,
3401
- maxTurns: config.maxTurns,
3402
- services: config.services,
3424
+ initialRunId: input.restoredSession?.latestRunId,
3425
+ resources: config.resources,
3403
3426
  approveTool: (request) => this.requestToolApproval(request),
3404
3427
  logger: this.logger
3405
3428
  });
@@ -3608,6 +3631,9 @@ var HarnessSessionImpl = class {
3608
3631
  this.events.clear();
3609
3632
  await this.logger.close();
3610
3633
  }
3634
+ async hydrate() {
3635
+ await this.runner.hydrate();
3636
+ }
3611
3637
  notify(event) {
3612
3638
  this.events.notify(event);
3613
3639
  }
@@ -3652,13 +3678,26 @@ var HarnessSessionImpl = class {
3652
3678
  }
3653
3679
  };
3654
3680
  async function createHarnessSession(config, options = {}) {
3655
- const workDir = resolveWorkDir(config.workDir);
3656
3681
  const agent = await resolveAgent(config.agent);
3657
- return new HarnessSessionImpl(config, {
3682
+ const storage = resolveStorage(config.storage);
3683
+ const sessionConfig = { ...config, storage };
3684
+ const session = new HarnessSessionImpl(sessionConfig, {
3658
3685
  sessionId: options.sessionId,
3659
3686
  agent,
3660
- workDir
3687
+ restoredSession: options.restoredSession
3661
3688
  });
3689
+ if (!options.restoredSession) {
3690
+ const status = session.getStatus();
3691
+ await storage.createSession({
3692
+ sessionId: status.sessionId,
3693
+ agentKey: status.agentKey,
3694
+ createdAt: status.createdAt,
3695
+ lastActiveAt: status.lastActiveAt,
3696
+ mode: status.mode
3697
+ });
3698
+ }
3699
+ await session.hydrate();
3700
+ return session;
3662
3701
  }
3663
3702
 
3664
3703
  // src/session/store.ts
@@ -3668,53 +3707,150 @@ function mergeConfig(base, overrides) {
3668
3707
  ...overrides,
3669
3708
  agent: overrides?.agent ?? base.agent,
3670
3709
  providers: overrides?.providers ?? base.providers,
3671
- services: overrides?.services ?? base.services,
3672
- logging: overrides?.logging ?? base.logging
3710
+ resources: overrides?.resources ?? base.resources,
3711
+ logging: overrides?.logging ?? base.logging,
3712
+ storage: overrides?.storage ?? base.storage
3713
+ };
3714
+ }
3715
+ function statusToSummary(status, latestRunId) {
3716
+ return {
3717
+ sessionId: status.sessionId,
3718
+ agentKey: status.agentKey,
3719
+ createdAt: status.createdAt,
3720
+ lastActiveAt: status.lastActiveAt,
3721
+ mode: status.mode,
3722
+ latestRunId
3723
+ };
3724
+ }
3725
+ function sortSummaries(items) {
3726
+ return [...items].sort((a, b) => {
3727
+ const byLastActive = b.lastActiveAt.localeCompare(a.lastActiveAt);
3728
+ return byLastActive || a.sessionId.localeCompare(b.sessionId);
3729
+ });
3730
+ }
3731
+ function encodeCursor(summary) {
3732
+ return Buffer.from(JSON.stringify([summary.lastActiveAt, summary.sessionId]), "utf8").toString("base64url");
3733
+ }
3734
+ function decodeCursor(cursor) {
3735
+ if (!cursor) return void 0;
3736
+ try {
3737
+ const parsed = JSON.parse(Buffer.from(cursor, "base64url").toString("utf8"));
3738
+ if (!Array.isArray(parsed) || typeof parsed[0] !== "string" || typeof parsed[1] !== "string") return void 0;
3739
+ return [parsed[0], parsed[1]];
3740
+ } catch {
3741
+ return void 0;
3742
+ }
3743
+ }
3744
+ function paginateActive(items, query) {
3745
+ const limit = Math.max(1, Math.min(query?.limit ?? 50, 100));
3746
+ const cursor = decodeCursor(query?.cursor);
3747
+ const filtered = sortSummaries(items).filter((summary) => !query?.agentKey || summary.agentKey === query.agentKey).filter((summary) => {
3748
+ if (!cursor) return true;
3749
+ const [lastActiveAt, sessionId] = cursor;
3750
+ return summary.lastActiveAt < lastActiveAt || summary.lastActiveAt === lastActiveAt && summary.sessionId > sessionId;
3751
+ });
3752
+ const page = filtered.slice(0, limit);
3753
+ return {
3754
+ items: page,
3755
+ nextCursor: filtered.length > limit && page.length > 0 ? encodeCursor(page[page.length - 1]) : void 0
3673
3756
  };
3674
3757
  }
3675
3758
  var HarnessSessionStoreImpl = class {
3676
3759
  constructor(config) {
3677
3760
  this.config = config;
3761
+ this.storage = config.storage ?? new MemorySessionStorage();
3678
3762
  }
3679
3763
  config;
3680
3764
  sessions = /* @__PURE__ */ new Map();
3681
3765
  unsubscriptions = /* @__PURE__ */ new Map();
3766
+ latestRunIds = /* @__PURE__ */ new Map();
3682
3767
  listeners = /* @__PURE__ */ new Set();
3768
+ storage;
3683
3769
  closed = false;
3770
+ async init() {
3771
+ await this.storage.init?.();
3772
+ }
3684
3773
  async getOrCreate(sessionId, overrides) {
3685
- this.cleanupExpired();
3686
3774
  if (this.closed) throw new Error("Harness session store is closed.");
3687
3775
  const id = sessionId?.trim() || randomId();
3688
3776
  const existing = this.sessions.get(id);
3689
3777
  if (existing) return existing;
3690
- const session = await createHarnessSession(mergeConfig(this.config, overrides), { sessionId: id });
3778
+ const config = mergeConfig({ ...this.config, storage: this.storage }, overrides);
3779
+ const stored = await this.storage.getSession(id);
3780
+ const session = await createHarnessSession(config, { sessionId: id, restoredSession: stored });
3781
+ const status = session.getStatus();
3782
+ if (stored?.latestRunId) this.latestRunIds.set(id, stored.latestRunId);
3783
+ else this.latestRunIds.delete(id);
3784
+ if (!stored) {
3785
+ await this.storage.createSession({
3786
+ sessionId: id,
3787
+ agentKey: status.agentKey,
3788
+ createdAt: status.createdAt,
3789
+ lastActiveAt: status.lastActiveAt,
3790
+ mode: status.mode
3791
+ });
3792
+ }
3691
3793
  this.sessions.set(id, session);
3692
- this.unsubscriptions.set(id, session.on((event) => this.notify({ type: "session.event", sessionId: id, event })));
3693
- this.notify({ type: "session.created", sessionId: id, status: session.getStatus() });
3794
+ this.unsubscriptions.set(id, session.on((event) => {
3795
+ if (event.type === "run.started") this.latestRunIds.set(id, event.runId);
3796
+ if (event.type === "run.completed") this.latestRunIds.set(id, event.result.runId);
3797
+ if (event.type === "session.status") {
3798
+ void this.storage.touchSession({
3799
+ sessionId: id,
3800
+ lastActiveAt: event.status.lastActiveAt,
3801
+ mode: event.status.mode
3802
+ });
3803
+ }
3804
+ this.notify({ type: "session.event", sessionId: id, event });
3805
+ }));
3806
+ this.notify({ type: "session.created", sessionId: id, status });
3694
3807
  return session;
3695
3808
  }
3696
3809
  get(sessionId) {
3697
- this.cleanupExpired();
3698
3810
  return this.sessions.get(sessionId);
3699
3811
  }
3700
- list() {
3701
- this.cleanupExpired();
3702
- return [...this.sessions.values()].map((session) => session.getStatus());
3812
+ async list(query) {
3813
+ if (query?.active) {
3814
+ return paginateActive([...this.sessions.values()].map((session) => statusToSummary(session.getStatus(), this.latestRunIds.get(session.id))), query);
3815
+ }
3816
+ const result = await this.storage.listSessions(query);
3817
+ const active = new Map([...this.sessions.values()].map((session) => [session.id, statusToSummary(session.getStatus(), this.latestRunIds.get(session.id))]));
3818
+ return {
3819
+ items: result.items.map((item) => active.get(item.sessionId) ?? item),
3820
+ nextCursor: result.nextCursor
3821
+ };
3703
3822
  }
3704
- async delete(sessionId) {
3823
+ async close(sessionId) {
3824
+ if (sessionId === void 0) {
3825
+ if (this.closed) return;
3826
+ this.closed = true;
3827
+ await this.closeAll();
3828
+ this.listeners.clear();
3829
+ return;
3830
+ }
3705
3831
  const session = this.sessions.get(sessionId);
3706
3832
  if (!session) return false;
3707
3833
  this.sessions.delete(sessionId);
3834
+ this.latestRunIds.delete(sessionId);
3708
3835
  this.unsubscriptions.get(sessionId)?.();
3709
3836
  this.unsubscriptions.delete(sessionId);
3710
3837
  await session.close();
3711
- this.notify({ type: "session.deleted", sessionId });
3712
3838
  return true;
3713
3839
  }
3714
- async clear() {
3715
- for (const sessionId of [...this.sessions.keys()]) await this.delete(sessionId);
3840
+ async delete(sessionId) {
3841
+ await this.close(sessionId);
3842
+ this.latestRunIds.delete(sessionId);
3843
+ const deleted = await this.storage.deleteSession(sessionId);
3844
+ if (deleted) this.notify({ type: "session.deleted", sessionId });
3845
+ return deleted;
3846
+ }
3847
+ async clearActive() {
3848
+ await this.closeAll();
3716
3849
  this.notify({ type: "session.cleared" });
3717
3850
  }
3851
+ async closeAll() {
3852
+ for (const sessionId of [...this.sessions.keys()]) await this.close(sessionId);
3853
+ }
3718
3854
  async send(sessionId, input, options) {
3719
3855
  const session = await this.getOrCreate(sessionId);
3720
3856
  return session.send(input, options);
@@ -3744,26 +3880,14 @@ var HarnessSessionStoreImpl = class {
3744
3880
  this.listeners.add(listener);
3745
3881
  return () => this.listeners.delete(listener);
3746
3882
  }
3747
- async close() {
3748
- if (this.closed) return;
3749
- this.closed = true;
3750
- await this.clear();
3751
- this.listeners.clear();
3752
- }
3753
3883
  notify(event) {
3754
3884
  for (const listener of this.listeners) void listener(event);
3755
3885
  }
3756
- cleanupExpired() {
3757
- const ttl = this.config.sessionTtlMs;
3758
- if (!ttl || ttl <= 0) return;
3759
- const cutoff = Date.now() - ttl;
3760
- for (const session of [...this.sessions.values()]) {
3761
- if (Date.parse(session.getStatus().lastActiveAt) < cutoff) void this.delete(session.id);
3762
- }
3763
- }
3764
3886
  };
3765
3887
  async function createHarnessSessionStore(config) {
3766
- return new HarnessSessionStoreImpl(config);
3888
+ const store = new HarnessSessionStoreImpl(config);
3889
+ await store.init();
3890
+ return store;
3767
3891
  }
3768
3892
 
3769
3893
  export {
@@ -3775,4 +3899,4 @@ export {
3775
3899
  HarnessSessionStoreImpl,
3776
3900
  createHarnessSessionStore
3777
3901
  };
3778
- //# sourceMappingURL=chunk-4WWSQAWA.js.map
3902
+ //# sourceMappingURL=chunk-QEVKKJ7N.js.map