maka-agent 0.2.0-dev.26.20260909 → 0.2.0-dev.28.20260911

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 (125) hide show
  1. package/LICENSE +106 -0
  2. package/dist/cli-core.js +28 -0
  3. package/dist/runtime-host-cli.js +43 -3
  4. package/dist/runtime-host-handoff-surface.js +10 -8
  5. package/dist/runtime-host-lifecycle-transaction.js +28 -3
  6. package/dist/runtime-host-local-source-retirement.js +1 -1
  7. package/dist/runtime-host-run-command.js +15 -7
  8. package/dist/runtime-host-setup-command.js +29 -6
  9. package/dist/runtime-host-update-command.js +49 -5
  10. package/dist/session-export-command.js +72 -0
  11. package/dist/session-import-command.js +69 -0
  12. package/native/runtime-host-peer/prebuilds/darwin-arm64/maka_runtime_host_peer.node +0 -0
  13. package/native/runtime-host-peer/prebuilds/linux-arm64/maka_runtime_host_peer.node +0 -0
  14. package/native/runtime-host-peer/prebuilds/linux-x64/maka_runtime_host_peer.node +0 -0
  15. package/native/runtime-host-peer/prebuilds/win32-x64/maka_runtime_host_peer.node +0 -0
  16. package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
  17. package/node_modules/@earendil-works/pi-tui/dist/components/editor.js +9 -4
  18. package/node_modules/@maka/core/dist/diagnostic-log.js +18 -7
  19. package/node_modules/@maka/core/dist/text-sanitize.js +8 -2
  20. package/node_modules/@maka/core/dist/usage-stats/types.js +2 -0
  21. package/node_modules/@maka/core/dist/workhub-routing.js +126 -0
  22. package/node_modules/@maka/core/package.json +2 -1
  23. package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +15 -11
  24. package/node_modules/@maka/runtime/dist/archive-read-tool.js +3 -1
  25. package/node_modules/@maka/runtime/dist/bots/bot-test.js +2 -1
  26. package/node_modules/@maka/runtime/dist/bots/feishu-bridge.js +24 -20
  27. package/node_modules/@maka/runtime/dist/bots/slack-bridge.js +5 -3
  28. package/node_modules/@maka/runtime/dist/bots/wechat-bridge.js +87 -29
  29. package/node_modules/@maka/runtime/dist/bots/wecom-bridge.js +40 -36
  30. package/node_modules/@maka/runtime/dist/builtin-tools.js +21 -2
  31. package/node_modules/@maka/runtime/dist/computer-use-tools.js +4 -1
  32. package/node_modules/@maka/runtime/dist/deep-research-tools.js +11 -5
  33. package/node_modules/@maka/runtime/dist/edit-replace.js +2 -2
  34. package/node_modules/@maka/runtime/dist/filesystem-worker/operations.js +5 -6
  35. package/node_modules/@maka/runtime/dist/memory-extraction-evidence.js +7 -1
  36. package/node_modules/@maka/runtime/dist/memory-extraction.js +12 -7
  37. package/node_modules/@maka/runtime/dist/openai-responses-websocket.js +2 -5
  38. package/node_modules/@maka/runtime/dist/plugin-agent-service.js +131 -0
  39. package/node_modules/@maka/runtime/dist/plugin-approval-service.js +33 -0
  40. package/node_modules/@maka/runtime/dist/plugin-attachment-service.js +53 -0
  41. package/node_modules/@maka/runtime/dist/plugin-command-service.js +100 -0
  42. package/node_modules/@maka/runtime/dist/plugin-composition-loader.js +11 -2
  43. package/node_modules/@maka/runtime/dist/plugin-data-services.js +332 -0
  44. package/node_modules/@maka/runtime/dist/plugin-fs-service.js +62 -0
  45. package/node_modules/@maka/runtime/dist/plugin-goal-service.js +61 -0
  46. package/node_modules/@maka/runtime/dist/plugin-invocation-signal.js +24 -0
  47. package/node_modules/@maka/runtime/dist/plugin-llm-service.js +85 -0
  48. package/node_modules/@maka/runtime/dist/plugin-lsp-service.js +94 -0
  49. package/node_modules/@maka/runtime/dist/plugin-scope-registry.js +91 -0
  50. package/node_modules/@maka/runtime/dist/plugin-session-query-service.js +71 -0
  51. package/node_modules/@maka/runtime/dist/plugin-shell-env-service.js +87 -0
  52. package/node_modules/@maka/runtime/dist/plugin-shell-service.js +74 -0
  53. package/node_modules/@maka/runtime/dist/plugin-skill-service.js +93 -0
  54. package/node_modules/@maka/runtime/dist/plugin-system-prompt-service.js +346 -0
  55. package/node_modules/@maka/runtime/dist/plugin-tool-service.js +18 -52
  56. package/node_modules/@maka/runtime/dist/plugin-user-question-service.js +43 -0
  57. package/node_modules/@maka/runtime/dist/plugin-web-service.js +69 -0
  58. package/node_modules/@maka/runtime/dist/request-customization-fetch.js +14 -7
  59. package/node_modules/@maka/runtime/dist/session-export.js +178 -0
  60. package/node_modules/@maka/runtime/dist/session-import.js +100 -0
  61. package/node_modules/@maka/runtime/dist/session-manager.js +6 -1
  62. package/node_modules/@maka/runtime/dist/shell-run-manager.js +9 -6
  63. package/node_modules/@maka/runtime/dist/shell-run-tool-result.js +13 -6
  64. package/node_modules/@maka/runtime/dist/stream-graph-coordinator.js +43 -10
  65. package/node_modules/@maka/runtime/dist/stream-graph-handoff.js +6 -1
  66. package/node_modules/@maka/runtime/dist/subscription-model-fetch.js +1 -4
  67. package/node_modules/@maka/runtime/dist/system-prompt/workspace-instructions.js +11 -6
  68. package/node_modules/@maka/runtime/dist/tavily-search.js +8 -2
  69. package/node_modules/@maka/runtime/dist/text-line-window.js +49 -0
  70. package/node_modules/@maka/runtime/dist/tool-output.js +35 -12
  71. package/node_modules/@maka/runtime/dist/unified-diff.js +40 -10
  72. package/node_modules/@maka/runtime/dist/web-fetch-tool.js +4 -2
  73. package/node_modules/@maka/runtime/dist/workers/filesystem-worker.js +82 -33
  74. package/node_modules/@maka/runtime/dist/workspace-executor.js +2 -6
  75. package/node_modules/@maka/runtime/package.json +20 -1
  76. package/node_modules/@maka/runtime-host/dist/client/host-handoff-copy.js +73 -24
  77. package/node_modules/@maka/runtime-host/dist/client/host-handoff.js +46 -12
  78. package/node_modules/@maka/runtime-host/dist/operator/setup-frame.js +31 -21
  79. package/node_modules/@maka/runtime-host/dist/peer-mesh/node.js +7 -4
  80. package/node_modules/@maka/runtime-host/dist/protocol/artifact.js +2 -2
  81. package/node_modules/@maka/runtime-host/dist/protocol/index.js +10 -1
  82. package/node_modules/@maka/runtime-host/dist/protocol/plugin-platform.js +30 -5
  83. package/node_modules/@maka/runtime-host/dist/protocol/session-transcript.js +3 -2
  84. package/node_modules/@maka/runtime-host/dist/protocol/skill-catalog.js +2 -1
  85. package/node_modules/@maka/runtime-host/dist/protocol/workhub-coordination.js +25 -23
  86. package/node_modules/@maka/runtime-host/dist/server/artifact-coordinator.js +2 -0
  87. package/node_modules/@maka/runtime-host/dist/server/client-capability-coordinator.js +62 -4
  88. package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +481 -11
  89. package/node_modules/@maka/runtime-host/dist/server/execution-model-authority.js +74 -0
  90. package/node_modules/@maka/runtime-host/dist/server/goal-coordinator.js +6 -1
  91. package/node_modules/@maka/runtime-host/dist/server/hosted-execution-recovery.js +2 -2
  92. package/node_modules/@maka/runtime-host/dist/server/hosted-execution-tool-profile.js +30 -0
  93. package/node_modules/@maka/runtime-host/dist/server/interactive-run-composer.js +114 -20
  94. package/node_modules/@maka/runtime-host/dist/server/message-coordinator.js +49 -12
  95. package/node_modules/@maka/runtime-host/dist/server/plugin-data-runtime.js +234 -0
  96. package/node_modules/@maka/runtime-host/dist/server/plugin-platform-coordinator.js +6 -0
  97. package/node_modules/@maka/runtime-host/dist/server/plugin-platform.js +12 -0
  98. package/node_modules/@maka/runtime-host/dist/server/root-admission-owner.js +17 -8
  99. package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +189 -92
  100. package/node_modules/@maka/runtime-host/dist/server/runtime-resource-projection.js +3 -9
  101. package/node_modules/@maka/runtime-host/dist/server/session-continuity-coordinator.js +20 -21
  102. package/node_modules/@maka/runtime-host/dist/server/session-transcript-pager.js +24 -2
  103. package/node_modules/@maka/runtime-host/dist/server/skill-catalog-coordinator.js +18 -2
  104. package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-action-gate.js +44 -35
  105. package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +92 -10
  106. package/node_modules/@maka/runtime-host/dist/transport/framed-byte-stream-transport.js +4 -1
  107. package/node_modules/@maka/runtime-host/dist/transport/local-ipc-framing.js +4 -2
  108. package/node_modules/@maka/runtime-host/dist/transport/peer-native.js +9 -8
  109. package/node_modules/@maka/runtime-host/dist/transport/resumable-peer-stream.js +14 -9
  110. package/node_modules/@maka/storage/dist/agent-run-store.js +54 -10
  111. package/node_modules/@maka/storage/dist/context-offload-snapshot.js +32 -10
  112. package/node_modules/@maka/storage/dist/context-value-mutation-gate.js +59 -0
  113. package/node_modules/@maka/storage/dist/execution-stores.js +1 -1
  114. package/node_modules/@maka/storage/dist/session-bundle-policy.js +909 -34
  115. package/node_modules/@maka/storage/dist/session-message-projection.js +8 -4
  116. package/node_modules/@maka/storage/dist/session-store.js +2 -2
  117. package/node_modules/@maka/storage/dist/sqlite-context-offload-store.js +18 -4
  118. package/node_modules/@maka/storage/dist/sqlite-session-metadata-store.js +12 -5
  119. package/node_modules/@maka/storage/dist/stable-storage.js +2 -1
  120. package/node_modules/@maka/storage/package.json +2 -0
  121. package/node_modules/@modelcontextprotocol/client/dist/src-D_zzAWoS.mjs +22 -5
  122. package/node_modules/@modelcontextprotocol/client/dist/src-NAgB4Mp8.cjs +22 -5
  123. package/node_modules/zod/v4/core/memoizer.cjs +12 -16
  124. package/node_modules/zod/v4/core/memoizer.js +12 -16
  125. package/package.json +1 -1
@@ -172,10 +172,11 @@ export class WechatBridge extends BaseBotAdapter {
172
172
  const baseUrl = normalizeWechatBridgeUrl(this.settings.webhookUrl);
173
173
  if (!baseUrl)
174
174
  return;
175
+ const cursor = { sinceEpochSeconds };
175
176
  while (this.running) {
176
177
  this.abortController = new AbortController();
177
178
  try {
178
- const response = await proxiedFetch(`${baseUrl}/messages/stream?since=${sinceEpochSeconds}`, {
179
+ const response = await proxiedFetch(`${baseUrl}/messages/stream?since=${cursor.sinceEpochSeconds}`, {
179
180
  method: 'GET',
180
181
  headers: wechatBridgeHeaders(this.settings),
181
182
  signal: this.abortController.signal,
@@ -183,19 +184,8 @@ export class WechatBridge extends BaseBotAdapter {
183
184
  });
184
185
  if (!response.ok || !response.body)
185
186
  throw new Error(`WeChat stream HTTP ${response.status}`);
186
- for await (const raw of readSseJsonObjects(response.body)) {
187
- const messages = Array.isArray(raw) ? raw : [raw];
188
- for (const message of messages) {
189
- const event = mapWechatBridgeMessage(message);
190
- if (!event)
191
- continue;
192
- sinceEpochSeconds = Math.max(sinceEpochSeconds, Math.floor(event.receivedAt / 1000));
193
- this.readiness = 'operational';
194
- this.reason = undefined;
195
- this.emitIncomingMessage(event);
196
- this.emitStatusChange();
197
- }
198
- }
187
+ const messages = readSseJsonObjects(response.body);
188
+ while (await this.processNextLiveMessages(messages, cursor)) { }
199
189
  if (this.running)
200
190
  await sleep(1_000);
201
191
  }
@@ -212,6 +202,31 @@ export class WechatBridge extends BaseBotAdapter {
212
202
  }
213
203
  }
214
204
  }
205
+ async processNextLiveMessages(stream, cursor) {
206
+ // Keep the iterator result and mapped events out of the next idle read's frame.
207
+ const next = await stream.next();
208
+ if (next.done)
209
+ return false;
210
+ try {
211
+ const messages = Array.isArray(next.value) ? next.value : [next.value];
212
+ for (const message of messages) {
213
+ const event = mapWechatBridgeMessage(message);
214
+ if (!event)
215
+ continue;
216
+ cursor.sinceEpochSeconds = Math.max(cursor.sinceEpochSeconds, Math.floor(event.receivedAt / 1000));
217
+ this.readiness = 'operational';
218
+ this.reason = undefined;
219
+ this.emitIncomingMessage(event);
220
+ this.emitStatusChange();
221
+ }
222
+ return true;
223
+ }
224
+ catch (error) {
225
+ // Match for-await: close after processing failures, preserving the original error.
226
+ await stream.return(undefined).catch(() => { });
227
+ throw error;
228
+ }
229
+ }
215
230
  async streamIlinkMessages(cursor) {
216
231
  const baseUrl = normalizeWechatIlinkBaseUrl(this.settings.webhookUrl);
217
232
  const token = this.settings.token.trim();
@@ -402,24 +417,67 @@ export function mapWechatIlinkMessage(raw) {
402
417
  }
403
418
  export async function* readSseJsonObjects(body) {
404
419
  const decoder = new TextDecoder();
420
+ const iterator = body[Symbol.asyncIterator]();
405
421
  let buffer = '';
406
- for await (const chunk of body) {
407
- buffer += decoder.decode(chunk, { stream: true });
408
- let boundary = findSseBoundary(buffer);
409
- while (boundary) {
410
- const event = buffer.slice(0, boundary.index);
411
- buffer = buffer.slice(boundary.index + boundary.length);
412
- const data = event
413
- .split(/\r?\n/)
414
- .filter((line) => line.startsWith('data:'))
415
- .map((line) => line.slice(5).trimStart())
416
- .join('\n')
417
- .trim();
418
- if (data)
419
- yield JSON.parse(data);
420
- boundary = findSseBoundary(buffer);
422
+ let closeIterator = false;
423
+ try {
424
+ while (true) {
425
+ // A rejected next() does not close its iterator under for-await semantics.
426
+ closeIterator = false;
427
+ let text = await readSseTextChunk(iterator, decoder);
428
+ if (text === undefined)
429
+ return;
430
+ closeIterator = true;
431
+ buffer += text;
432
+ text = undefined;
433
+ let boundary = findSseBoundary(buffer);
434
+ while (boundary) {
435
+ let data = sseEventData(buffer.slice(0, boundary.index));
436
+ buffer = buffer.slice(boundary.index + boundary.length);
437
+ if (data)
438
+ yield JSON.parse(data);
439
+ data = '';
440
+ boundary = findSseBoundary(buffer);
441
+ }
442
+ }
443
+ }
444
+ catch (error) {
445
+ if (closeIterator) {
446
+ closeIterator = false;
447
+ try {
448
+ await iterator.return?.();
449
+ }
450
+ catch { }
421
451
  }
452
+ throw error;
453
+ }
454
+ finally {
455
+ if (closeIterator)
456
+ await iterator.return?.();
457
+ }
458
+ }
459
+ async function readSseTextChunk(iterator, decoder) {
460
+ const next = await iterator.next();
461
+ if (next.done)
462
+ return;
463
+ try {
464
+ return decoder.decode(next.value, { stream: true });
422
465
  }
466
+ catch (error) {
467
+ try {
468
+ await iterator.return?.();
469
+ }
470
+ catch { }
471
+ throw error;
472
+ }
473
+ }
474
+ function sseEventData(event) {
475
+ return event
476
+ .split(/\r?\n/)
477
+ .filter((line) => line.startsWith('data:'))
478
+ .map((line) => line.slice(5).trimStart())
479
+ .join('\n')
480
+ .trim();
423
481
  }
424
482
  export async function testWechatBridge(channel) {
425
483
  const baseUrl = normalizeWechatBridgeUrl(channel.webhookUrl);
@@ -16,7 +16,7 @@
16
16
  * specific language governing permissions and limitations
17
17
  * under the License.
18
18
  */
19
- import { WSClient } from '@wecom/aibot-node-sdk';
19
+ import { createRequire } from 'node:module';
20
20
  import { BaseBotAdapter, botReadinessFromSettings } from './base-adapter.js';
21
21
  const AUTH_TIMEOUT_MS = 15_000;
22
22
  /** Detach bridge callbacks before disconnect can synchronously emit. */
@@ -78,36 +78,39 @@ export class WeComBotBridge extends BaseBotAdapter {
78
78
  return;
79
79
  }
80
80
  this.explicitlyStopped = false;
81
- const client = new WSClient({
82
- botId,
83
- secret,
84
- maxReconnectAttempts: -1,
85
- logger: quietLogger,
86
- });
87
- this.client = client;
88
- this.wire(client, botId);
89
- await new Promise((resolve, reject) => {
90
- let settled = false;
91
- const onAuthenticated = () => finish(resolve);
92
- const onError = (error) => finish(() => reject(error));
93
- const cleanup = () => {
94
- clearTimeout(timer);
95
- client.off('authenticated', onAuthenticated);
96
- client.off('error', onError);
97
- };
98
- const finish = (fn) => {
99
- if (settled)
100
- return;
101
- settled = true;
102
- cleanup();
103
- fn();
104
- };
105
- const timer = setTimeout(() => finish(() => reject(new Error('WeCom authentication timed out'))), AUTH_TIMEOUT_MS);
106
- client.once('authenticated', onAuthenticated);
107
- client.once('error', onError);
108
- client.connect();
109
- })
110
- .then(() => {
81
+ let startedClient;
82
+ try {
83
+ const { WSClient } = createRequire(import.meta.url)('@wecom/aibot-node-sdk');
84
+ const client = new WSClient({
85
+ botId,
86
+ secret,
87
+ maxReconnectAttempts: -1,
88
+ logger: quietLogger,
89
+ });
90
+ startedClient = client;
91
+ this.client = client;
92
+ this.wire(client, botId);
93
+ await new Promise((resolve, reject) => {
94
+ let settled = false;
95
+ const onAuthenticated = () => finish(resolve);
96
+ const onError = (error) => finish(() => reject(error));
97
+ const cleanup = () => {
98
+ clearTimeout(timer);
99
+ client.off('authenticated', onAuthenticated);
100
+ client.off('error', onError);
101
+ };
102
+ const finish = (fn) => {
103
+ if (settled)
104
+ return;
105
+ settled = true;
106
+ cleanup();
107
+ fn();
108
+ };
109
+ const timer = setTimeout(() => finish(() => reject(new Error('WeCom authentication timed out'))), AUTH_TIMEOUT_MS);
110
+ client.once('authenticated', onAuthenticated);
111
+ client.once('error', onError);
112
+ client.connect();
113
+ });
111
114
  if (this.explicitlyStopped || this.client !== client)
112
115
  return;
113
116
  this.running = true;
@@ -116,17 +119,18 @@ export class WeComBotBridge extends BaseBotAdapter {
116
119
  this.reason = undefined;
117
120
  this.readiness = 'credentials_valid';
118
121
  this.emitStatusChange();
119
- })
120
- .catch((error) => {
121
- if (this.explicitlyStopped || this.client !== client)
122
+ }
123
+ catch (error) {
124
+ if (this.explicitlyStopped || (startedClient && this.client !== startedClient))
122
125
  return;
123
126
  this.running = false;
124
127
  this.recordFailure(error);
125
128
  this.readiness = 'configured';
126
129
  this.emitStatusChange();
127
130
  this.client = null;
128
- closeWeComClient(client);
129
- });
131
+ if (startedClient)
132
+ closeWeComClient(startedClient);
133
+ }
130
134
  }
131
135
  async stop() {
132
136
  this.explicitlyStopped = true;
@@ -194,9 +194,28 @@ export function buildBuiltinTools(options = {}) {
194
194
  shell,
195
195
  ...(options.sandboxManager
196
196
  ? {
197
- transformCommand: ({ command, pty, requiredBoundary, ctx }) => sandboxCommand(options.sandboxManager, options.permissionProfile, sandboxPlatform, command, pty, ctx, requiredBoundary, 'background_command'),
197
+ transformCommand: ({ command, pty, requiredBoundary, ctx }) => {
198
+ const transformed = sandboxCommand(options.sandboxManager, options.permissionProfile, sandboxPlatform, command, pty, ctx, requiredBoundary, 'background_command');
199
+ if (!options.shellEnvironment)
200
+ return transformed;
201
+ return {
202
+ ...(transformed ?? { cwd: ctx.cwd }),
203
+ env: {
204
+ ...process.env,
205
+ ...transformed?.env,
206
+ ...options.shellEnvironment,
207
+ },
208
+ };
209
+ },
198
210
  }
199
- : {}),
211
+ : options.shellEnvironment
212
+ ? {
213
+ transformCommand: ({ ctx }) => ({
214
+ cwd: ctx.cwd,
215
+ env: { ...process.env, ...options.shellEnvironment },
216
+ }),
217
+ }
218
+ : {}),
200
219
  }),
201
220
  ]
202
221
  : [
@@ -945,6 +945,7 @@ export function buildComputerUseTools(deps) {
945
945
  release();
946
946
  if (invocationQueues.get(sessionId) === current) {
947
947
  invocationQueues.delete(sessionId);
948
+ presentationGenerations.delete(sessionId);
948
949
  }
949
950
  }
950
951
  }
@@ -2385,7 +2386,9 @@ export function buildComputerUseTools(deps) {
2385
2386
  }
2386
2387
  const tools = [tool];
2387
2388
  tools.clearSession = (sessionId) => {
2388
- presentationGenerations.set(sessionId, (presentationGenerations.get(sessionId) ?? 0) + 1);
2389
+ if (invocationQueues.has(sessionId)) {
2390
+ presentationGenerations.set(sessionId, (presentationGenerations.get(sessionId) ?? 0) + 1);
2391
+ }
2389
2392
  for (const wake of presentationQueueWaiters.get(sessionId) ?? [])
2390
2393
  wake();
2391
2394
  for (const wake of presentationWaiters.get(sessionId) ?? [])
@@ -142,14 +142,20 @@ function buildReadArtifactTool(deps) {
142
142
  }
143
143
  const offset = input.offset_chars ?? 0;
144
144
  const maxChars = input.max_chars ?? DEEP_RESEARCH_ARTIFACT_READ_DEFAULT_CHARS;
145
- const characters = Array.from(read.text);
146
- const end = Math.min(characters.length, offset + maxChars);
147
- const chunk = safeResearchArtifactContent(characters.slice(offset, end).join(''));
145
+ const selected = [];
146
+ let total = 0;
147
+ for (const character of read.text) {
148
+ if (total >= offset && total < offset + maxChars)
149
+ selected.push(character);
150
+ total += 1;
151
+ }
152
+ const end = Math.min(total, offset + maxChars);
153
+ const chunk = safeResearchArtifactContent(selected.join(''));
148
154
  return [
149
- `<deep-research-artifact id="${ref.artifactId}" role="${ref.role}" offset="${offset}" end="${end}" total="${characters.length}">`,
155
+ `<deep-research-artifact id="${ref.artifactId}" role="${ref.role}" offset="${offset}" end="${end}" total="${total}">`,
150
156
  `Name: ${normalizeInlineText(ref.name)}`,
151
157
  ...(ref.locator ? [`Locator: ${normalizeInlineText(ref.locator)}`] : []),
152
- `Truncated: ${end < characters.length}`,
158
+ `Truncated: ${end < total}`,
153
159
  '',
154
160
  chunk,
155
161
  '</deep-research-artifact>',
@@ -139,10 +139,10 @@ export function computeEditedSource(source, oldString, newString, where) {
139
139
  function finish(content, span, replacement, matchedVia) {
140
140
  const index = content.indexOf(span);
141
141
  const before = content.slice(0, index);
142
- const startLine = before.split('\n').length;
142
+ const startLine = countOccurrences(before, '\n') + 1;
143
143
  // A trailing newline in the span is the last line's terminator, not an
144
144
  // extra line, so it must not bump endLine.
145
- const spanLineCount = span.split('\n').length - (span.endsWith('\n') ? 1 : 0);
145
+ const spanLineCount = countOccurrences(span, '\n') + 1 - (span.endsWith('\n') ? 1 : 0);
146
146
  const endLine = startLine + Math.max(spanLineCount, 1) - 1;
147
147
  // slice-join (not String.replace) so `$&`/`$1` in newString are literal.
148
148
  const next = before + replacement + content.slice(index + span.length);
@@ -25,6 +25,7 @@ import { sandboxPathApi } from './sandbox-paths.js';
25
25
  import { sandboxBoundaryExpansionAllowsPath } from '@maka/core/sandbox-boundary';
26
26
  import { ApplyPatchRejectedError, applyUpdateToContent, createPatchedFile, } from '../apply-patch-file.js';
27
27
  import { computeEditedSource } from '../edit-replace.js';
28
+ import { readTextLineWindow } from '../text-line-window.js';
28
29
  import { createEditUnifiedDiff, createUnifiedDiff } from '../unified-diff.js';
29
30
  import { compareAndDeleteEntry, hostVisibilityAfterWrite, openStableTarget, readModifyWriteThroughHandle, StableWriteFailure, writeThroughHandle, } from '../file-stable-write.js';
30
31
  import { isSupportedImagePath, readWorkspaceImage } from '../image-file.js';
@@ -75,12 +76,10 @@ export async function executeFilesystemOperation(operation, operationBoundary, d
75
76
  }
76
77
  }
77
78
  const content = await fs.readFile(path, 'utf8');
78
- if (operation.offset === undefined && operation.limit === undefined)
79
- return { kind: 'read', content };
80
- const lines = content.split('\n');
81
- const start = operation.offset ?? 0;
82
- const end = operation.limit ? start + operation.limit : lines.length;
83
- return { kind: 'read', content: lines.slice(start, end).join('\n') };
79
+ return {
80
+ kind: 'read',
81
+ content: readTextLineWindow(content, operation.offset, operation.limit),
82
+ };
84
83
  }
85
84
  case 'write': {
86
85
  const path = await resolveWritableAllowed(operation.cwd, operation.path, 'Write', operationBoundary);
@@ -273,7 +273,13 @@ function historyRole(event) {
273
273
  return event.role === 'model' ? 'assistant' : 'user';
274
274
  }
275
275
  function sliceCodePoints(value, maximum) {
276
- return Array.from(value).slice(0, maximum).join('');
276
+ const prefix = [];
277
+ for (const point of value) {
278
+ if (prefix.length >= maximum)
279
+ break;
280
+ prefix.push(point);
281
+ }
282
+ return prefix.join('');
277
283
  }
278
284
  function uniqueSorted(values) {
279
285
  return [...new Set(values)].sort((left, right) => left - right);
@@ -369,7 +369,7 @@ export class MemoryExtractionEngine {
369
369
  return undefined;
370
370
  maximumSplitIndex = requestedTurnStart;
371
371
  }
372
- const split = memoryRangeSplitCandidates(pendingEntries, maximumSplitIndex).find(({ first, second }) => {
372
+ const split = findMemoryRangeSplit(pendingEntries, maximumSplitIndex, ({ first, second }) => {
373
373
  const firstThroughOrdinal = first.at(-1).ordinal;
374
374
  const firstTrigger = 'extract';
375
375
  const firstPrepared = this.prepareRange({
@@ -924,9 +924,9 @@ function memorySegmentOperationId(operationId, afterOrdinal, throughOrdinal, por
924
924
  .update(JSON.stringify({ operationId, afterOrdinal, throughOrdinal }))
925
925
  .digest('hex')}`;
926
926
  }
927
- function memoryRangeSplitCandidates(entries, maximumSplitIndex = entries.length - 1) {
927
+ function findMemoryRangeSplit(entries, maximumSplitIndex = entries.length - 1, accepts) {
928
928
  if (entries.length < 2)
929
- return [];
929
+ return undefined;
930
930
  const weights = entries.map(memoryRangeEventWeight);
931
931
  const total = weights.reduce((sum, weight) => sum + weight, 0);
932
932
  let prefix = 0;
@@ -942,11 +942,16 @@ function memoryRangeSplitCandidates(entries, maximumSplitIndex = entries.length
942
942
  };
943
943
  })
944
944
  .filter(({ index }) => index <= maximumSplitIndex);
945
- return candidates
946
- .sort((left, right) => Number(right.turnBoundary) - Number(left.turnBoundary) ||
945
+ candidates.sort((left, right) => Number(right.turnBoundary) - Number(left.turnBoundary) ||
947
946
  left.distance - right.distance ||
948
- left.index - right.index)
949
- .map(({ index }) => ({ first: entries.slice(0, index), second: entries.slice(index) }));
947
+ left.index - right.index);
948
+ // Keep only the candidate being checked; each pair copies the entire range.
949
+ for (const { index } of candidates) {
950
+ const split = { first: entries.slice(0, index), second: entries.slice(index) };
951
+ if (accepts(split))
952
+ return split;
953
+ }
954
+ return undefined;
950
955
  }
951
956
  function preparedMemoryRangeFits(prepared, trigger) {
952
957
  if (!prepared)
@@ -221,11 +221,8 @@ function prepareWireRequest(body, baseline) {
221
221
  const { previous_response_id: _previousResponseId, ...currentProperties } = body;
222
222
  const fullBody = {
223
223
  ...currentProperties,
224
- input: [
225
- ...structuredClone(baseline.fullBody.input),
226
- ...structuredClone(baseline.responseOutput),
227
- ...structuredClone(body.input),
228
- ],
224
+ // These JSON values are privately owned; only the combined input array needs to be new.
225
+ input: [...baseline.fullBody.input, ...baseline.responseOutput, ...body.input],
229
226
  };
230
227
  const sameProperties = isDeepStrictEqual(requestProperties(baseline.fullBody), requestProperties(fullBody));
231
228
  return {
@@ -0,0 +1,131 @@
1
+ /*
2
+ * Licensed to the Apache Software Foundation (ASF) under one
3
+ * or more contributor license agreements. See the NOTICE file
4
+ * distributed with this work for additional information
5
+ * regarding copyright ownership. The ASF licenses this file
6
+ * to you under the Apache License, Version 2.0 (the
7
+ * "License"); you may not use this file except in compliance
8
+ * with the License. You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing,
13
+ * software distributed under the License is distributed on an
14
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ * KIND, either express or implied. See the License for the
16
+ * specific language governing permissions and limitations
17
+ * under the License.
18
+ */
19
+ import { AsyncLocalStorage } from 'node:async_hooks';
20
+ import { Service } from './plugin-kernel.js';
21
+ import { pluginInvocationSignal } from './plugin-invocation-signal.js';
22
+ /** Agent registry and invocation carrier exposed to trusted Host plugins. */
23
+ export class PluginAgentService extends Service {
24
+ invocations = new AsyncLocalStorage();
25
+ agentRuntime;
26
+ constructor(ctx) {
27
+ super(ctx, 'agents');
28
+ ctx.accessor('agent', {
29
+ get: () => this.current(),
30
+ });
31
+ }
32
+ bindRuntime(runtime) {
33
+ if (this.ctx.maka)
34
+ throw new Error('Only the Host may bind the Agent Runtime');
35
+ if (this.agentRuntime)
36
+ throw new Error('Plugin Agent Runtime is already bound');
37
+ this.agentRuntime = runtime;
38
+ return this.ctx.effect(() => () => {
39
+ if (this.agentRuntime === runtime)
40
+ this.agentRuntime = undefined;
41
+ }, 'agents.bindRuntime()');
42
+ }
43
+ currentInvocation() {
44
+ return this.invocations.getStore();
45
+ }
46
+ requireInvocation() {
47
+ const invocation = this.currentInvocation();
48
+ if (!invocation)
49
+ throw new Error('This capability requires an active Agent invocation');
50
+ return invocation;
51
+ }
52
+ withInvocation(toolContext, operation) {
53
+ const invocation = Object.freeze({
54
+ sessionId: toolContext.sessionId,
55
+ ...(toolContext.runId ? { runId: toolContext.runId } : {}),
56
+ turnId: toolContext.turnId,
57
+ cwd: toolContext.cwd,
58
+ ...(toolContext.permissionMode ? { permissionMode: toolContext.permissionMode } : {}),
59
+ ...(toolContext.executionBoundary
60
+ ? { executionBoundary: toolContext.executionBoundary }
61
+ : {}),
62
+ toolCallId: toolContext.toolCallId,
63
+ abortSignal: toolContext.abortSignal,
64
+ toolContext,
65
+ });
66
+ return this.invocations.run(invocation, operation);
67
+ }
68
+ current() {
69
+ const invocation = this.currentInvocation();
70
+ if (!invocation)
71
+ return undefined;
72
+ return this.handle({
73
+ id: invocation.sessionId,
74
+ sessionId: invocation.sessionId,
75
+ root: true,
76
+ status: 'running',
77
+ });
78
+ }
79
+ async create(options = {}) {
80
+ const invocation = this.requireInvocation();
81
+ return this.handle(await this.runtime().create({ ...options, signal: pluginInvocationSignal(invocation.abortSignal, options.signal) }, invocation), invocation);
82
+ }
83
+ async resume(options) {
84
+ const invocation = this.requireInvocation();
85
+ return this.handle(await this.runtime().resume({ ...options, signal: pluginInvocationSignal(invocation.abortSignal, options.signal) }, invocation), invocation);
86
+ }
87
+ async get(id) {
88
+ const invocation = this.requireInvocation();
89
+ const descriptor = await this.runtime().get(assertId(id), invocation);
90
+ return descriptor ? this.handle(descriptor, invocation) : undefined;
91
+ }
92
+ async list() {
93
+ const invocation = this.requireInvocation();
94
+ return Object.freeze((await this.runtime().list(invocation)).map((descriptor) => this.handle(descriptor, invocation)));
95
+ }
96
+ async roots() {
97
+ const invocation = this.requireInvocation();
98
+ return Object.freeze((await this.runtime().roots(invocation)).map((descriptor) => this.handle(descriptor, invocation)));
99
+ }
100
+ runtime() {
101
+ if (!this.agentRuntime)
102
+ throw new Error('Plugin Agent Runtime is unavailable');
103
+ return this.agentRuntime;
104
+ }
105
+ handle(descriptor, invocation = this.requireInvocation()) {
106
+ const service = this;
107
+ const id = assertId(descriptor.id);
108
+ return Object.freeze({
109
+ ...descriptor,
110
+ id,
111
+ followup: (message) => service.runtime().followup(id, message, invocation),
112
+ steer: (message) => service.runtime().steer(id, message, invocation),
113
+ inject: (message) => service.runtime().inject(id, message, invocation),
114
+ cancel: () => service.runtime().cancel(id, invocation),
115
+ whenIdle: (signal) => service
116
+ .runtime()
117
+ .whenIdle(id, pluginInvocationSignal(invocation.abortSignal, signal), invocation),
118
+ snapshot: () => service.runtime().snapshot(id, invocation),
119
+ inbox: () => service.runtime().inbox(id, invocation),
120
+ result: () => service.runtime().result(id, invocation),
121
+ artifacts: () => service.runtime().artifacts(id, invocation),
122
+ transcript: () => service.runtime().transcript(id, invocation),
123
+ dispose: () => service.runtime().dispose(id, invocation),
124
+ });
125
+ }
126
+ }
127
+ function assertId(id) {
128
+ if (!id || /[\0\r\n]/u.test(id))
129
+ throw new TypeError('Agent id is invalid');
130
+ return id;
131
+ }
@@ -0,0 +1,33 @@
1
+ /*
2
+ * Licensed to the Apache Software Foundation (ASF) under one
3
+ * or more contributor license agreements. See the NOTICE file
4
+ * distributed with this work for additional information
5
+ * regarding copyright ownership. The ASF licenses this file
6
+ * to you under the Apache License, Version 2.0 (the
7
+ * "License"); you may not use this file except in compliance
8
+ * with the License. You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing,
13
+ * software distributed under the License is distributed on an
14
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ * KIND, either express or implied. See the License for the
16
+ * specific language governing permissions and limitations
17
+ * under the License.
18
+ */
19
+ import { Service } from './plugin-kernel.js';
20
+ /** Permission request surface backed by Maka's durable Sandbox Boundary authority. */
21
+ export class PluginApprovalService extends Service {
22
+ agents;
23
+ constructor(ctx, agents) {
24
+ super(ctx, 'approval');
25
+ this.agents = agents;
26
+ }
27
+ request(request) {
28
+ const ask = this.agents.requireInvocation().toolContext?.requestSandboxBoundary;
29
+ if (!ask)
30
+ throw new Error('Approval is unavailable on this Agent surface');
31
+ return ask(request.expansion, request.justification);
32
+ }
33
+ }