@lensagent/core 0.1.0 → 0.1.2

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 (99) hide show
  1. package/README.md +63 -4
  2. package/config/lens.example.jsonc +36 -13
  3. package/dist/adapters/executor-router.d.ts +20 -0
  4. package/dist/adapters/executor-router.js +86 -0
  5. package/dist/adapters/executor.d.ts +9 -3
  6. package/dist/adapters/executor.js +196 -61
  7. package/dist/adapters/lsp.d.ts +2 -1
  8. package/dist/adapters/lsp.js +98 -41
  9. package/dist/adapters/mcp.js +52 -17
  10. package/dist/adapters/revert.d.ts +5 -4
  11. package/dist/adapters/revert.js +6 -64
  12. package/dist/adapters/web.d.ts +9 -0
  13. package/dist/adapters/web.js +244 -0
  14. package/dist/config/files.d.ts +14 -0
  15. package/dist/config/files.js +57 -0
  16. package/dist/config/runtime.d.ts +3 -4
  17. package/dist/config/runtime.js +116 -97
  18. package/dist/config/schema.d.ts +56 -10
  19. package/dist/config/schema.js +48 -8
  20. package/dist/contracts/capability.d.ts +27 -1
  21. package/dist/contracts/domain.d.ts +1 -0
  22. package/dist/contracts/model.d.ts +33 -6
  23. package/dist/contracts/review.d.ts +17 -0
  24. package/dist/contracts/review.js +1 -0
  25. package/dist/core/access-policy.d.ts +40 -0
  26. package/dist/core/access-policy.js +68 -0
  27. package/dist/core/access.d.ts +30 -2
  28. package/dist/core/access.js +159 -26
  29. package/dist/core/context-boundaries.d.ts +25 -0
  30. package/dist/core/context-boundaries.js +106 -0
  31. package/dist/core/context.d.ts +3 -0
  32. package/dist/core/context.js +11 -0
  33. package/dist/core/host.js +261 -173
  34. package/dist/entry/pair.d.ts +1 -0
  35. package/dist/entry/pair.js +14 -0
  36. package/dist/entry/run.js +1 -1
  37. package/dist/execution/lsp-process.d.ts +3 -2
  38. package/dist/execution/lsp-process.js +46 -28
  39. package/dist/http/server.d.ts +5 -0
  40. package/dist/http/server.js +262 -80
  41. package/dist/runtime/agent.d.ts +9 -4
  42. package/dist/runtime/agent.js +361 -286
  43. package/dist/runtime/artifacts.d.ts +13 -1
  44. package/dist/runtime/artifacts.js +137 -10
  45. package/dist/runtime/cache.d.ts +59 -16
  46. package/dist/runtime/cache.js +124 -43
  47. package/dist/runtime/calls.d.ts +1 -1
  48. package/dist/runtime/calls.js +164 -48
  49. package/dist/runtime/capabilities.d.ts +2 -2
  50. package/dist/runtime/capabilities.js +4 -5
  51. package/dist/runtime/cleanup.d.ts +5 -2
  52. package/dist/runtime/cleanup.js +48 -17
  53. package/dist/runtime/compaction.d.ts +9 -0
  54. package/dist/runtime/compaction.js +52 -0
  55. package/dist/runtime/context.d.ts +8 -4
  56. package/dist/runtime/context.js +115 -128
  57. package/dist/runtime/events.d.ts +7 -0
  58. package/dist/runtime/events.js +83 -0
  59. package/dist/runtime/images.d.ts +14 -0
  60. package/dist/runtime/images.js +82 -0
  61. package/dist/runtime/kernel-capabilities.d.ts +7 -1
  62. package/dist/runtime/kernel-capabilities.js +146 -68
  63. package/dist/runtime/model-audit.d.ts +9 -0
  64. package/dist/runtime/model-audit.js +39 -0
  65. package/dist/runtime/model.d.ts +109 -4
  66. package/dist/runtime/model.js +368 -143
  67. package/dist/runtime/observability.d.ts +1 -0
  68. package/dist/runtime/observability.js +60 -15
  69. package/dist/runtime/policy.d.ts +1 -1
  70. package/dist/runtime/policy.js +2 -2
  71. package/dist/runtime/profile.d.ts +2 -1
  72. package/dist/runtime/profile.js +12 -15
  73. package/dist/runtime/projects.d.ts +3 -1
  74. package/dist/runtime/projects.js +38 -17
  75. package/dist/runtime/ptc-worker.d.ts +1 -0
  76. package/dist/runtime/ptc-worker.js +97 -0
  77. package/dist/runtime/ptc.d.ts +16 -0
  78. package/dist/runtime/ptc.js +128 -0
  79. package/dist/runtime/results.d.ts +5 -4
  80. package/dist/runtime/results.js +29 -51
  81. package/dist/runtime/review.d.ts +4 -1
  82. package/dist/runtime/review.js +101 -22
  83. package/dist/runtime/scheduler.js +1 -0
  84. package/dist/runtime/skills.js +34 -5
  85. package/dist/runtime/task-audit.d.ts +21 -0
  86. package/dist/runtime/task-audit.js +122 -0
  87. package/dist/runtime/time.d.ts +9 -0
  88. package/dist/runtime/time.js +18 -0
  89. package/dist/runtime/tokenizer.d.ts +2 -0
  90. package/dist/runtime/tokenizer.js +26 -1
  91. package/dist/store/database.js +26 -1
  92. package/dist/store/event-log.d.ts +1 -0
  93. package/dist/store/event-log.js +22 -5
  94. package/dist/store/schema.d.ts +1 -1
  95. package/dist/store/schema.js +50 -1
  96. package/dist/store/store.d.ts +65 -19
  97. package/dist/store/store.js +986 -188
  98. package/package.json +18 -8
  99. package/proto/executor.proto +69 -55
@@ -2,13 +2,14 @@ export declare class LspProcess {
2
2
  #private;
3
3
  readonly maxFrameBytes: number;
4
4
  readonly maxBufferBytes: number;
5
- constructor(command: string, args: readonly string[], cwd: string, maxFrameBytes: number, maxBufferBytes: number);
5
+ readonly requestTimeoutMs: number;
6
+ constructor(command: string, args: readonly string[], cwd: string, maxFrameBytes: number, maxBufferBytes: number, requestTimeoutMs?: number);
6
7
  get pid(): number | undefined;
7
8
  get initialized(): boolean;
8
9
  get closed(): boolean;
9
10
  markInitialized(): void;
10
11
  request(method: string, params: unknown, signal?: AbortSignal, allowClosing?: boolean): Promise<unknown>;
11
- notify(method: string, params: unknown, allowClosing?: boolean): void;
12
+ notify(method: string, params: unknown, allowClosing?: boolean, signal?: AbortSignal): Promise<void>;
12
13
  drainNotifications(): readonly unknown[];
13
14
  close(): Promise<void>;
14
15
  }
@@ -1,7 +1,9 @@
1
+ import { once } from 'node:events';
1
2
  import { spawn } from 'node:child_process';
2
3
  export class LspProcess {
3
4
  maxFrameBytes;
4
5
  maxBufferBytes;
6
+ requestTimeoutMs;
5
7
  #child;
6
8
  #pending = new Map();
7
9
  #notifications = [];
@@ -11,10 +13,15 @@ export class LspProcess {
11
13
  #initialized = false;
12
14
  #closed = false;
13
15
  #closing;
14
- constructor(command, args, cwd, maxFrameBytes, maxBufferBytes) {
16
+ #writeTail = Promise.resolve();
17
+ #queuedBytes = 0;
18
+ #lifetime = new AbortController();
19
+ constructor(command, args, cwd, maxFrameBytes, maxBufferBytes, requestTimeoutMs = 30000) {
15
20
  this.maxFrameBytes = maxFrameBytes;
16
21
  this.maxBufferBytes = maxBufferBytes;
22
+ this.requestTimeoutMs = requestTimeoutMs;
17
23
  this.#child = spawn(command, args, { cwd, env: processEnvironment(), shell: false, windowsHide: true });
24
+ this.#child.stderr.resume();
18
25
  this.#child.stdout.on('data', (chunk) => this.#receive(chunk));
19
26
  this.#child.stdin.on('error', () => this.#fail(lspError('LSP_SERVER_UNAVAILABLE', 'The LSP input stream failed.')));
20
27
  this.#child.once('error', () => this.#fail(lspError('LSP_SERVER_UNAVAILABLE', 'The LSP server could not start.')));
@@ -37,39 +44,39 @@ export class LspProcess {
37
44
  return Promise.reject(lspError('LSP_SERVER_UNAVAILABLE', 'The LSP session is closed.'));
38
45
  if (signal?.aborted === true)
39
46
  return Promise.reject(abortError(signal.reason));
47
+ if (this.#pending.size >= 128)
48
+ return Promise.reject(lspError('LSP_PROTOCOL_ERROR', 'Too many pending LSP requests.'));
49
+ const deadline = new AbortController();
50
+ const timeout = setTimeout(() => deadline.abort(lspError('LSP_SERVER_UNAVAILABLE', 'The LSP request timed out.')), this.requestTimeoutMs);
51
+ signal = signal === undefined ? deadline.signal : AbortSignal.any([signal, deadline.signal]);
40
52
  const id = this.#nextId++;
41
53
  return new Promise((resolve, reject) => {
42
54
  const onAbort = () => {
43
55
  if (!this.#pending.delete(id))
44
56
  return;
45
- try {
46
- this.notify('$/cancelRequest', { id }, allowClosing);
47
- }
48
- catch { /* The local cancellation remains authoritative. */ }
49
- reject(abortError(signal?.reason));
57
+ void this.notify('$/cancelRequest', { id }, allowClosing).catch(() => undefined);
58
+ finish(() => reject(abortError(signal.reason)));
50
59
  };
51
60
  const finish = (operation) => {
52
- signal?.removeEventListener('abort', onAbort);
61
+ clearTimeout(timeout);
62
+ signal.removeEventListener('abort', onAbort);
53
63
  operation();
54
64
  };
55
65
  this.#pending.set(id, {
56
66
  resolve: (value) => finish(() => resolve(value)),
57
67
  reject: (error) => finish(() => reject(error))
58
68
  });
59
- signal?.addEventListener('abort', onAbort, { once: true });
60
- try {
61
- this.#send({ jsonrpc: '2.0', id, method, params });
62
- }
63
- catch (error) {
69
+ signal.addEventListener('abort', onAbort, { once: true });
70
+ void this.#send({ jsonrpc: '2.0', id, method, params }, signal).catch((error) => {
64
71
  this.#pending.delete(id);
65
72
  finish(() => reject(asError(error)));
66
- }
73
+ });
67
74
  });
68
75
  }
69
- notify(method, params, allowClosing = false) {
76
+ async notify(method, params, allowClosing = false, signal) {
70
77
  if (this.#closed || (this.#closing !== undefined && !allowClosing))
71
78
  throw lspError('LSP_SERVER_UNAVAILABLE', 'The LSP session is closed.');
72
- this.#send({ jsonrpc: '2.0', method, params });
79
+ await this.#send({ jsonrpc: '2.0', method, params }, signal);
73
80
  }
74
81
  drainNotifications() {
75
82
  const result = this.#notifications.splice(0);
@@ -86,27 +93,34 @@ export class LspProcess {
86
93
  const timeout = setTimeout(() => controller.abort(), 1000);
87
94
  try {
88
95
  await this.request('shutdown', null, controller.signal, true);
96
+ await this.notify('exit', null, true, controller.signal);
89
97
  }
90
98
  catch { /* Process termination follows. */ }
91
99
  finally {
92
100
  clearTimeout(timeout);
93
101
  }
94
- try {
95
- this.notify('exit', null, true);
96
- }
97
- catch { /* The process may already be gone. */ }
98
102
  }
99
103
  await terminate(this.#child);
100
104
  this.#fail(lspError('LSP_SERVER_UNAVAILABLE', 'The LSP session was closed.'));
101
105
  }
102
- #send(value) {
103
- if (!this.#child.stdin.writable)
104
- throw lspError('LSP_SERVER_UNAVAILABLE', 'The LSP server is not writable.');
106
+ #send(value, signal) {
105
107
  const content = Buffer.from(JSON.stringify(value));
106
108
  if (content.byteLength > this.maxFrameBytes)
107
- throw lspError('LSP_PROTOCOL_ERROR', 'The LSP frame exceeds the configured limit.');
108
- this.#child.stdin.write(`Content-Length: ${content.byteLength}\r\n\r\n`);
109
- this.#child.stdin.write(content);
109
+ return Promise.reject(lspError('LSP_PROTOCOL_ERROR', 'The LSP frame exceeds the configured limit.'));
110
+ const frame = Buffer.concat([Buffer.from(`Content-Length: ${content.byteLength}\r\n\r\n`), content]);
111
+ if (this.#queuedBytes + frame.byteLength > this.maxBufferBytes)
112
+ return Promise.reject(lspError('LSP_PROTOCOL_ERROR', 'The LSP write queue is full.'));
113
+ this.#queuedBytes += frame.byteLength;
114
+ const combined = signal === undefined ? this.#lifetime.signal : AbortSignal.any([signal, this.#lifetime.signal]);
115
+ const operation = this.#writeTail.then(async () => {
116
+ combined.throwIfAborted();
117
+ if (!this.#child.stdin.writable)
118
+ throw lspError('LSP_SERVER_UNAVAILABLE', 'The LSP server is not writable.');
119
+ if (!this.#child.stdin.write(frame))
120
+ await once(this.#child.stdin, 'drain', { signal: combined });
121
+ }).finally(() => { this.#queuedBytes -= frame.byteLength; });
122
+ this.#writeTail = operation.catch(() => undefined);
123
+ return operation;
110
124
  }
111
125
  #receive(chunk) {
112
126
  if (this.#closed)
@@ -150,7 +164,7 @@ export class LspProcess {
150
164
  #dispatch(message) {
151
165
  if (typeof message.method === 'string') {
152
166
  if (typeof message.id === 'number' || typeof message.id === 'string') {
153
- this.#send({ jsonrpc: '2.0', id: message.id, error: { code: -32601, message: 'Server-initiated requests are not supported.' } });
167
+ void this.#send({ jsonrpc: '2.0', id: message.id, error: { code: -32601, message: 'Server-initiated requests are not supported.' } }).catch((error) => this.#protocolFailure(asError(error).message));
154
168
  }
155
169
  else {
156
170
  const bytes = Buffer.byteLength(JSON.stringify(message));
@@ -184,6 +198,7 @@ export class LspProcess {
184
198
  if (this.#closed && this.#pending.size === 0)
185
199
  return;
186
200
  this.#closed = true;
201
+ this.#lifetime.abort(error);
187
202
  for (const pending of this.#pending.values())
188
203
  pending.reject(error);
189
204
  this.#pending.clear();
@@ -210,11 +225,14 @@ function processEnvironment(explicit = {}) {
210
225
  return Object.fromEntries(selected.values());
211
226
  }
212
227
  async function terminate(child) {
213
- if (child.exitCode !== null || child.signalCode !== null)
228
+ if (child.pid === undefined || child.exitCode !== null || child.signalCode !== null)
214
229
  return;
215
230
  const closed = new Promise((resolve) => child.once('close', () => resolve()));
216
231
  child.kill('SIGTERM');
217
- const graceful = await Promise.race([closed.then(() => true), new Promise((resolve) => setTimeout(() => resolve(false), 1000))]);
232
+ let timer;
233
+ const graceful = await Promise.race([closed.then(() => true), new Promise((resolve) => { timer = setTimeout(() => resolve(false), 1000); })]);
234
+ if (timer !== undefined)
235
+ clearTimeout(timer);
218
236
  if (!graceful) {
219
237
  child.kill('SIGKILL');
220
238
  await closed;
@@ -8,7 +8,10 @@ import type { ProjectRuntime } from '../runtime/projects.js';
8
8
  import type { ArtifactStore } from '../runtime/artifacts.js';
9
9
  import type { Observability } from '../runtime/observability.js';
10
10
  import type { ExecutorClient } from '../adapters/executor.js';
11
+ import type { ExecutorRouter } from '../adapters/executor-router.js';
12
+ import type { TaskAuditReader } from '../runtime/task-audit.js';
11
13
  interface Dependencies {
14
+ readonly taskAudit?: TaskAuditReader;
12
15
  readonly https?: {
13
16
  readonly key: Buffer;
14
17
  readonly cert: Buffer;
@@ -21,6 +24,8 @@ interface Dependencies {
21
24
  readonly artifacts: ArtifactStore;
22
25
  readonly observability: Observability;
23
26
  readonly executor: ExecutorClient;
27
+ readonly executorRouter?: ExecutorRouter;
28
+ readonly localExecutor?: ExecutorClient;
24
29
  readonly defaultProjectId: string;
25
30
  readonly runtime: (projectId: string) => ProjectRuntime;
26
31
  readonly refreshProject: (projectId: string) => void;