@mindstudio-ai/remy 0.1.337 → 0.1.339

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.
package/dist/index.js CHANGED
@@ -4949,127 +4949,6 @@ ${partial}` : "[INTERRUPTED] Tool execution was stopped.";
4949
4949
  }
4950
4950
  });
4951
4951
 
4952
- // src/statusWatcher.ts
4953
- function sanitizeStatusText(text) {
4954
- if (!text) {
4955
- return "";
4956
- }
4957
- for (const marker of INTERNAL_PAYLOAD_MARKERS) {
4958
- if (text.includes(marker)) {
4959
- return "";
4960
- }
4961
- }
4962
- const truncIdx = text.indexOf("(tool result truncated at");
4963
- return truncIdx === -1 ? text : text.slice(0, truncIdx);
4964
- }
4965
- function startStatusWatcher(config) {
4966
- const { apiConfig, getContext, onStatus, interval = 5e3, signal } = config;
4967
- let inflight2 = false;
4968
- let stopped = false;
4969
- let pauseCount = 0;
4970
- const url = `${apiConfig.baseUrl}/_internal/v2/agent/remy/generate-status`;
4971
- let consecutiveFailures = 0;
4972
- let backoffMs = 0;
4973
- let nextAllowedAt = 0;
4974
- const MAX_CONSECUTIVE_FAILURES = 10;
4975
- const MAX_BACKOFF_MS = 6e4;
4976
- function recordFailure() {
4977
- consecutiveFailures++;
4978
- backoffMs = Math.min(
4979
- backoffMs === 0 ? interval : backoffMs * 2,
4980
- MAX_BACKOFF_MS
4981
- );
4982
- nextAllowedAt = Date.now() + backoffMs;
4983
- if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES && !stopped) {
4984
- stopped = true;
4985
- clearInterval(timer);
4986
- }
4987
- }
4988
- async function tick() {
4989
- if (stopped || signal?.aborted || inflight2 || pauseCount > 0) {
4990
- return;
4991
- }
4992
- if (Date.now() < nextAllowedAt) {
4993
- return;
4994
- }
4995
- inflight2 = true;
4996
- try {
4997
- const context = getContext();
4998
- if (!context) {
4999
- return;
5000
- }
5001
- const res = await fetch(url, {
5002
- method: "POST",
5003
- headers: {
5004
- "Content-Type": "application/json",
5005
- Authorization: `Bearer ${apiConfig.apiKey}`,
5006
- // Also a liveness signal for this box, and the most frequent one there is — this ticks
5007
- // every few seconds for as long as the agent is working, which is exactly the window in
5008
- // which the user may have backgrounded the tab and stopped its own keepalive.
5009
- ...sandboxSessionHeader()
5010
- },
5011
- body: JSON.stringify({ appId: apiConfig.appId, context }),
5012
- signal
5013
- });
5014
- if (stopped) {
5015
- return;
5016
- }
5017
- if (!res.ok) {
5018
- recordFailure();
5019
- return;
5020
- }
5021
- const data = await res.json();
5022
- if (!data.label) {
5023
- recordFailure();
5024
- return;
5025
- }
5026
- consecutiveFailures = 0;
5027
- backoffMs = 0;
5028
- nextAllowedAt = 0;
5029
- if (pauseCount > 0 || stopped || signal?.aborted) {
5030
- return;
5031
- }
5032
- onStatus(data.label);
5033
- } catch {
5034
- if (!stopped && !signal?.aborted) {
5035
- recordFailure();
5036
- }
5037
- } finally {
5038
- inflight2 = false;
5039
- }
5040
- }
5041
- const timer = setInterval(tick, interval);
5042
- tick().catch(() => {
5043
- });
5044
- return {
5045
- stop() {
5046
- stopped = true;
5047
- clearInterval(timer);
5048
- },
5049
- pause() {
5050
- pauseCount++;
5051
- },
5052
- resume() {
5053
- pauseCount = Math.max(0, pauseCount - 1);
5054
- }
5055
- };
5056
- }
5057
- var INTERNAL_PAYLOAD_MARKERS;
5058
- var init_statusWatcher = __esm({
5059
- "src/statusWatcher.ts"() {
5060
- "use strict";
5061
- init_api();
5062
- INTERNAL_PAYLOAD_MARKERS = [
5063
- "[USER CANCELLED]",
5064
- "[INTERRUPTED]",
5065
- "[INTERRUPTED - PARTIAL OUTPUT RETRIEVED]",
5066
- "<background_results>",
5067
- "<workspace_status>",
5068
- "<tool_result"
5069
- ];
5070
- }
5071
- });
5072
-
5073
4952
  // src/subagents/runner.ts
5074
4953
  async function runSubAgent(config) {
5075
4954
  const {
@@ -5137,376 +5016,339 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
5137
5016
  }
5138
5017
  return { text: cancelledToolResult(signal), messages: thisInvocation() };
5139
5018
  }
5140
- let lastToolResult = "";
5141
- let watchedBlocks = [];
5142
- let watchedToolNames = "";
5143
- const statusWatcher = startStatusWatcher({
5144
- apiConfig,
5145
- getContext: () => {
5146
- const parts = [];
5147
- if (task) {
5148
- parts.push(`Task: ${sanitizeStatusText(task).slice(-200)}`);
5149
- }
5150
- const text = getPartialText(watchedBlocks);
5151
- if (text) {
5152
- parts.push(`Assistant text: ${text.slice(-500)}`);
5153
- }
5154
- if (watchedToolNames) {
5155
- parts.push(`Tool: ${watchedToolNames}`);
5156
- }
5157
- const toolResult = sanitizeStatusText(lastToolResult);
5158
- if (toolResult) {
5159
- parts.push(`Tool result: ${toolResult.slice(-200)}`);
5160
- }
5161
- return parts.join("\n");
5162
- },
5163
- onStatus: (label) => emit({ type: "status", message: label }),
5164
- signal
5165
- });
5166
- try {
5167
- while (true) {
5168
- turns++;
5169
- if (signal?.aborted) {
5170
- return abortResult([]);
5171
- }
5172
- const iterStart = Date.now();
5173
- const contentBlocks = [];
5174
- let thinkingStartedAt = 0;
5175
- let lastThinkingRelatedStartedAt;
5176
- let stopReason = "end_turn";
5177
- let lastUsage;
5178
- let lastProviderMetadata;
5179
- let lastModelId;
5180
- watchedBlocks = contentBlocks;
5181
- watchedToolNames = "";
5182
- try {
5183
- for await (const event of streamChatWithRetry(
5184
- {
5185
- ...apiConfig,
5186
- model,
5187
- requestId,
5188
- subAgentId,
5189
- system: fullSystem,
5190
- messages: cleanMessagesForApi(messages),
5191
- tools: tools2,
5192
- cachePolicy,
5193
- signal
5194
- },
5195
- {
5196
- onRetry: (attempt) => emit({
5197
- type: "status",
5198
- message: `Lost connection, retrying (attempt ${attempt + 2} of 3)`
5199
- })
5200
- }
5201
- )) {
5202
- if (signal?.aborted) {
5203
- break;
5204
- }
5205
- switch (event.type) {
5206
- case "text": {
5207
- const lastBlock = contentBlocks.at(-1);
5208
- if (lastBlock?.type === "text") {
5209
- lastBlock.text += event.text;
5210
- } else {
5211
- contentBlocks.push({
5212
- type: "text",
5213
- text: event.text,
5214
- startedAt: event.ts
5215
- });
5216
- }
5217
- emit({ type: "text", text: event.text });
5218
- break;
5219
- }
5220
- case "thinking":
5221
- if (!thinkingStartedAt) {
5222
- thinkingStartedAt = event.ts;
5223
- }
5224
- emit({ type: "thinking", text: event.text });
5225
- break;
5226
- case "thinking_complete":
5227
- contentBlocks.push({
5228
- type: "thinking",
5229
- thinking: event.thinking,
5230
- signature: event.signature,
5231
- startedAt: thinkingStartedAt,
5232
- completedAt: event.ts
5233
- });
5234
- lastThinkingRelatedStartedAt = thinkingStartedAt;
5235
- thinkingStartedAt = 0;
5236
- break;
5237
- case "redacted_thinking_complete": {
5238
- const startedAt = lastThinkingRelatedStartedAt !== void 0 ? lastThinkingRelatedStartedAt + 1 : event.ts;
5019
+ while (true) {
5020
+ turns++;
5021
+ if (signal?.aborted) {
5022
+ return abortResult([]);
5023
+ }
5024
+ const iterStart = Date.now();
5025
+ const contentBlocks = [];
5026
+ let thinkingStartedAt = 0;
5027
+ let lastThinkingRelatedStartedAt;
5028
+ let stopReason = "end_turn";
5029
+ let lastUsage;
5030
+ let lastProviderMetadata;
5031
+ let lastModelId;
5032
+ try {
5033
+ for await (const event of streamChatWithRetry(
5034
+ {
5035
+ ...apiConfig,
5036
+ model,
5037
+ requestId,
5038
+ subAgentId,
5039
+ system: fullSystem,
5040
+ messages: cleanMessagesForApi(messages),
5041
+ tools: tools2,
5042
+ cachePolicy,
5043
+ signal
5044
+ },
5045
+ {
5046
+ onRetry: (attempt) => emit({
5047
+ type: "status",
5048
+ message: `Lost connection, retrying (attempt ${attempt + 2} of 3)`
5049
+ })
5050
+ }
5051
+ )) {
5052
+ if (signal?.aborted) {
5053
+ break;
5054
+ }
5055
+ switch (event.type) {
5056
+ case "text": {
5057
+ const lastBlock = contentBlocks.at(-1);
5058
+ if (lastBlock?.type === "text") {
5059
+ lastBlock.text += event.text;
5060
+ } else {
5239
5061
  contentBlocks.push({
5240
- type: "redacted_thinking",
5241
- data: event.data,
5242
- startedAt,
5243
- completedAt: event.ts
5062
+ type: "text",
5063
+ text: event.text,
5064
+ startedAt: event.ts
5244
5065
  });
5245
- lastThinkingRelatedStartedAt = startedAt;
5246
- break;
5247
5066
  }
5248
- case "tool_use":
5249
- contentBlocks.push({
5250
- type: "tool",
5251
- id: event.id,
5252
- name: event.name,
5253
- input: event.input,
5254
- startedAt: Date.now()
5255
- });
5256
- emit({
5257
- type: "tool_start",
5258
- id: event.id,
5259
- name: event.name,
5260
- input: event.input
5261
- });
5262
- break;
5263
- case "done":
5264
- stopReason = event.stopReason;
5265
- lastUsage = {
5266
- inputTokens: event.usage.inputTokens,
5267
- outputTokens: event.usage.outputTokens,
5268
- cacheCreationTokens: event.usage.cacheCreationTokens,
5269
- cacheReadTokens: event.usage.cacheReadTokens,
5270
- llmCalls: 1
5271
- };
5272
- lastProviderMetadata = event.providerMetadata;
5273
- lastModelId = event.modelId;
5274
- recordUsage({
5275
- ts: Date.now(),
5276
- requestId,
5277
- agentName: subAgentId || "sub-agent",
5278
- parentToolId,
5279
- modelId: event.modelId,
5280
- inputTokens: event.usage.inputTokens,
5281
- outputTokens: event.usage.outputTokens,
5282
- cacheCreationTokens: event.usage.cacheCreationTokens,
5283
- cacheReadTokens: event.usage.cacheReadTokens,
5284
- cost: nanoToDollars(event.cost),
5285
- billingEvents: event.billingEvents,
5286
- durationMs: Date.now() - iterStart,
5287
- toolNames: contentBlocks.filter(
5288
- (b) => b.type === "tool"
5289
- ).map((b) => b.name)
5290
- });
5291
- break;
5292
- case "error":
5293
- statusWatcher.stop();
5294
- return {
5295
- text: `Error: ${event.error}`,
5296
- messages: thisInvocation()
5297
- };
5067
+ emit({ type: "text", text: event.text });
5068
+ break;
5298
5069
  }
5299
- }
5300
- } catch (err) {
5301
- if (!signal?.aborted) {
5302
- throw err;
5303
- }
5304
- }
5305
- if (signal?.aborted) {
5306
- statusWatcher.stop();
5307
- return abortResult(contentBlocks);
5308
- }
5309
- messages.push({
5310
- role: "assistant",
5311
- content: contentBlocks,
5312
- ...lastUsage ? { usage: lastUsage } : {},
5313
- ...lastProviderMetadata ? { providerMetadata: lastProviderMetadata } : {},
5314
- model: lastModelId ?? model
5315
- });
5316
- const toolCalls = contentBlocks.filter(
5317
- (b) => b.type === "tool"
5318
- );
5319
- if (stopReason !== "tool_use" || toolCalls.length === 0) {
5320
- let text = getPartialText(contentBlocks);
5321
- if (validateResult) {
5322
- let objection = null;
5323
- try {
5324
- objection = await validateResult(text, thisInvocation());
5325
- } catch (err) {
5326
- log8.warn("Result validator failed, accepting response", {
5327
- requestId,
5328
- parentToolId,
5329
- agentName,
5330
- error: err.message
5070
+ case "thinking":
5071
+ if (!thinkingStartedAt) {
5072
+ thinkingStartedAt = event.ts;
5073
+ }
5074
+ emit({ type: "thinking", text: event.text });
5075
+ break;
5076
+ case "thinking_complete":
5077
+ contentBlocks.push({
5078
+ type: "thinking",
5079
+ thinking: event.thinking,
5080
+ signature: event.signature,
5081
+ startedAt: thinkingStartedAt,
5082
+ completedAt: event.ts
5331
5083
  });
5084
+ lastThinkingRelatedStartedAt = thinkingStartedAt;
5085
+ thinkingStartedAt = 0;
5086
+ break;
5087
+ case "redacted_thinking_complete": {
5088
+ const startedAt = lastThinkingRelatedStartedAt !== void 0 ? lastThinkingRelatedStartedAt + 1 : event.ts;
5089
+ contentBlocks.push({
5090
+ type: "redacted_thinking",
5091
+ data: event.data,
5092
+ startedAt,
5093
+ completedAt: event.ts
5094
+ });
5095
+ lastThinkingRelatedStartedAt = startedAt;
5096
+ break;
5332
5097
  }
5333
- if (objection && !validationRetried && !signal?.aborted) {
5334
- validationRetried = true;
5335
- log8.info("Result rejected by validator, retrying once", {
5336
- requestId,
5337
- parentToolId,
5338
- agentName,
5339
- objection: objection.slice(0, 200)
5098
+ case "tool_use":
5099
+ contentBlocks.push({
5100
+ type: "tool",
5101
+ id: event.id,
5102
+ name: event.name,
5103
+ input: event.input,
5104
+ startedAt: Date.now()
5340
5105
  });
5341
5106
  emit({
5342
- type: "status",
5343
- message: "Response failed validation, revising"
5107
+ type: "tool_start",
5108
+ id: event.id,
5109
+ name: event.name,
5110
+ input: event.input
5344
5111
  });
5345
- messages.push({ role: "user", content: objection });
5346
- continue;
5347
- }
5348
- if (objection) {
5349
- text = `${text}
5350
-
5351
- [Response validator: ${objection}]`;
5352
- }
5353
- }
5354
- statusWatcher.stop();
5355
- const hasArtifacts = Object.keys(artifacts).length > 0;
5356
- return {
5357
- text,
5358
- messages: thisInvocation(),
5359
- ...hasArtifacts ? { artifacts } : {}
5360
- };
5361
- }
5362
- log8.info("Tools executing", {
5363
- requestId,
5364
- parentToolId,
5365
- count: toolCalls.length,
5366
- tools: toolCalls.map((tc) => tc.name)
5367
- });
5368
- watchedToolNames = toolCalls.map((tc) => tc.name).join(", ");
5369
- const results = await Promise.all(
5370
- toolCalls.map(async (tc) => {
5371
- if (signal?.aborted) {
5372
- return {
5373
- id: tc.id,
5374
- result: cancelledToolResult(signal),
5375
- isError: true
5112
+ break;
5113
+ case "done":
5114
+ stopReason = event.stopReason;
5115
+ lastUsage = {
5116
+ inputTokens: event.usage.inputTokens,
5117
+ outputTokens: event.usage.outputTokens,
5118
+ cacheCreationTokens: event.usage.cacheCreationTokens,
5119
+ cacheReadTokens: event.usage.cacheReadTokens,
5120
+ llmCalls: 1
5376
5121
  };
5377
- }
5378
- let settle;
5379
- const resultPromise = new Promise((res) => {
5380
- settle = (result, isError, recording) => res({
5381
- id: tc.id,
5382
- result,
5383
- isError,
5384
- ...recording ? { recording } : {}
5122
+ lastProviderMetadata = event.providerMetadata;
5123
+ lastModelId = event.modelId;
5124
+ recordUsage({
5125
+ ts: Date.now(),
5126
+ requestId,
5127
+ agentName: subAgentId || "sub-agent",
5128
+ parentToolId,
5129
+ modelId: event.modelId,
5130
+ inputTokens: event.usage.inputTokens,
5131
+ outputTokens: event.usage.outputTokens,
5132
+ cacheCreationTokens: event.usage.cacheCreationTokens,
5133
+ cacheReadTokens: event.usage.cacheReadTokens,
5134
+ cost: nanoToDollars(event.cost),
5135
+ billingEvents: event.billingEvents,
5136
+ durationMs: Date.now() - iterStart,
5137
+ toolNames: contentBlocks.filter(
5138
+ (b) => b.type === "tool"
5139
+ ).map((b) => b.name)
5385
5140
  });
5386
- });
5387
- let toolAbort = new AbortController();
5388
- const cascadeAbort = () => toolAbort.abort();
5389
- signal?.addEventListener("abort", cascadeAbort, { once: true });
5390
- let settled = false;
5391
- const safeSettle = (result, isError, recording) => {
5392
- if (settled) {
5393
- return;
5394
- }
5395
- settled = true;
5396
- signal?.removeEventListener("abort", cascadeAbort);
5397
- settle(result, isError, recording);
5398
- };
5399
- const run2 = async (input) => {
5400
- try {
5401
- let result;
5402
- let recording;
5403
- if (externalTools.has(tc.name) && resolveExternalTool) {
5404
- result = await resolveExternalTool(tc.id, tc.name, input);
5405
- if (tc.name === "browserCommand") {
5406
- const lifted = liftRecording(result);
5407
- result = lifted.result;
5408
- recording = lifted.recording;
5409
- }
5410
- } else {
5411
- const onLog = (line) => emit({
5412
- type: "tool_input_delta",
5413
- id: tc.id,
5414
- name: tc.name,
5415
- result: line
5416
- });
5417
- result = await executeTool2(
5418
- tc.name,
5419
- input,
5420
- tc.id,
5421
- onLog,
5422
- subAgentMessages
5423
- );
5424
- }
5425
- safeSettle(
5426
- capToolResult(result),
5427
- result.startsWith("Error"),
5428
- recording
5429
- );
5430
- } catch (err) {
5431
- safeSettle(`Error: ${err.message}`, true);
5432
- }
5433
- };
5434
- const entry = {
5435
- id: tc.id,
5436
- name: tc.name,
5437
- input: tc.input,
5141
+ break;
5142
+ case "error":
5143
+ return {
5144
+ text: `Error: ${event.error}`,
5145
+ messages: thisInvocation()
5146
+ };
5147
+ }
5148
+ }
5149
+ } catch (err) {
5150
+ if (!signal?.aborted) {
5151
+ throw err;
5152
+ }
5153
+ }
5154
+ if (signal?.aborted) {
5155
+ return abortResult(contentBlocks);
5156
+ }
5157
+ messages.push({
5158
+ role: "assistant",
5159
+ content: contentBlocks,
5160
+ ...lastUsage ? { usage: lastUsage } : {},
5161
+ ...lastProviderMetadata ? { providerMetadata: lastProviderMetadata } : {},
5162
+ model: lastModelId ?? model
5163
+ });
5164
+ const toolCalls = contentBlocks.filter(
5165
+ (b) => b.type === "tool"
5166
+ );
5167
+ if (stopReason !== "tool_use" || toolCalls.length === 0) {
5168
+ let text = getPartialText(contentBlocks);
5169
+ if (validateResult) {
5170
+ let objection = null;
5171
+ try {
5172
+ objection = await validateResult(text, thisInvocation());
5173
+ } catch (err) {
5174
+ log8.warn("Result validator failed, accepting response", {
5175
+ requestId,
5438
5176
  parentToolId,
5439
- abortController: toolAbort,
5440
- startedAt: Date.now(),
5441
- settle: safeSettle,
5442
- rerun: (newInput) => {
5443
- settled = false;
5444
- toolAbort = new AbortController();
5445
- signal?.addEventListener("abort", () => toolAbort.abort(), {
5446
- once: true
5447
- });
5448
- entry.abortController = toolAbort;
5449
- entry.input = newInput;
5450
- run2(newInput);
5451
- }
5452
- };
5453
- toolRegistry?.register(entry);
5454
- const toolStart = Date.now();
5455
- run2(tc.input);
5456
- const r = await resultPromise;
5457
- toolRegistry?.unregister(tc.id);
5458
- log8.info("Tool completed", {
5177
+ agentName,
5178
+ error: err.message
5179
+ });
5180
+ }
5181
+ if (objection && !validationRetried && !signal?.aborted) {
5182
+ validationRetried = true;
5183
+ log8.info("Result rejected by validator, retrying once", {
5459
5184
  requestId,
5460
5185
  parentToolId,
5461
- toolCallId: tc.id,
5462
- name: tc.name,
5463
- durationMs: Date.now() - toolStart,
5464
- isError: r.isError
5186
+ agentName,
5187
+ objection: objection.slice(0, 200)
5465
5188
  });
5466
5189
  emit({
5467
- type: "tool_done",
5190
+ type: "status",
5191
+ message: "Response failed validation, revising"
5192
+ });
5193
+ messages.push({ role: "user", content: objection });
5194
+ continue;
5195
+ }
5196
+ if (objection) {
5197
+ text = `${text}
5198
+
5199
+ [Response validator: ${objection}]`;
5200
+ }
5201
+ }
5202
+ const hasArtifacts = Object.keys(artifacts).length > 0;
5203
+ return {
5204
+ text,
5205
+ messages: thisInvocation(),
5206
+ ...hasArtifacts ? { artifacts } : {}
5207
+ };
5208
+ }
5209
+ log8.info("Tools executing", {
5210
+ requestId,
5211
+ parentToolId,
5212
+ count: toolCalls.length,
5213
+ tools: toolCalls.map((tc) => tc.name)
5214
+ });
5215
+ const results = await Promise.all(
5216
+ toolCalls.map(async (tc) => {
5217
+ if (signal?.aborted) {
5218
+ return {
5219
+ id: tc.id,
5220
+ result: cancelledToolResult(signal),
5221
+ isError: true
5222
+ };
5223
+ }
5224
+ let settle;
5225
+ const resultPromise = new Promise((res) => {
5226
+ settle = (result, isError, recording) => res({
5468
5227
  id: tc.id,
5469
- name: tc.name,
5470
- result: r.result,
5471
- isError: r.isError,
5472
- ...r.recording ? { recording: r.recording } : {}
5228
+ result,
5229
+ isError,
5230
+ ...recording ? { recording } : {}
5473
5231
  });
5474
- return r;
5475
- })
5476
- );
5477
- lastToolResult = results.at(-1)?.result ?? "";
5478
- for (const r of results) {
5479
- const block = contentBlocks.find(
5480
- (b) => b.type === "tool" && b.id === r.id
5481
- );
5482
- if (block?.type === "tool") {
5483
- block.result = r.result;
5484
- block.isError = r.isError;
5485
- block.completedAt = Date.now();
5486
- if (r.recording) {
5487
- block.recording = r.recording;
5488
- }
5489
- const innerMsgs = subAgentMessages.get(r.id);
5490
- if (innerMsgs) {
5491
- attachSubAgentTranscript(block, innerMsgs);
5232
+ });
5233
+ let toolAbort = new AbortController();
5234
+ const cascadeAbort = () => toolAbort.abort();
5235
+ signal?.addEventListener("abort", cascadeAbort, { once: true });
5236
+ let settled = false;
5237
+ const safeSettle = (result, isError, recording) => {
5238
+ if (settled) {
5239
+ return;
5492
5240
  }
5493
- if (captureArtifacts?.includes(block.name) && !r.isError) {
5494
- try {
5495
- artifacts[block.name] = JSON.parse(r.result);
5496
- } catch {
5241
+ settled = true;
5242
+ signal?.removeEventListener("abort", cascadeAbort);
5243
+ settle(result, isError, recording);
5244
+ };
5245
+ const run2 = async (input) => {
5246
+ try {
5247
+ let result;
5248
+ let recording;
5249
+ if (externalTools.has(tc.name) && resolveExternalTool) {
5250
+ result = await resolveExternalTool(tc.id, tc.name, input);
5251
+ if (tc.name === "browserCommand") {
5252
+ const lifted = liftRecording(result);
5253
+ result = lifted.result;
5254
+ recording = lifted.recording;
5255
+ }
5256
+ } else {
5257
+ const onLog = (line) => emit({
5258
+ type: "tool_input_delta",
5259
+ id: tc.id,
5260
+ name: tc.name,
5261
+ result: line
5262
+ });
5263
+ result = await executeTool2(
5264
+ tc.name,
5265
+ input,
5266
+ tc.id,
5267
+ onLog,
5268
+ subAgentMessages
5269
+ );
5497
5270
  }
5271
+ safeSettle(
5272
+ capToolResult(result),
5273
+ result.startsWith("Error"),
5274
+ recording
5275
+ );
5276
+ } catch (err) {
5277
+ safeSettle(`Error: ${err.message}`, true);
5498
5278
  }
5499
- }
5500
- messages.push({
5501
- role: "user",
5502
- content: r.result,
5503
- toolCallId: r.id,
5504
- isToolError: r.isError
5279
+ };
5280
+ const entry = {
5281
+ id: tc.id,
5282
+ name: tc.name,
5283
+ input: tc.input,
5284
+ parentToolId,
5285
+ abortController: toolAbort,
5286
+ startedAt: Date.now(),
5287
+ settle: safeSettle,
5288
+ rerun: (newInput) => {
5289
+ settled = false;
5290
+ toolAbort = new AbortController();
5291
+ signal?.addEventListener("abort", () => toolAbort.abort(), {
5292
+ once: true
5293
+ });
5294
+ entry.abortController = toolAbort;
5295
+ entry.input = newInput;
5296
+ run2(newInput);
5297
+ }
5298
+ };
5299
+ toolRegistry?.register(entry);
5300
+ const toolStart = Date.now();
5301
+ run2(tc.input);
5302
+ const r = await resultPromise;
5303
+ toolRegistry?.unregister(tc.id);
5304
+ log8.info("Tool completed", {
5305
+ requestId,
5306
+ parentToolId,
5307
+ toolCallId: tc.id,
5308
+ name: tc.name,
5309
+ durationMs: Date.now() - toolStart,
5310
+ isError: r.isError
5311
+ });
5312
+ emit({
5313
+ type: "tool_done",
5314
+ id: tc.id,
5315
+ name: tc.name,
5316
+ result: r.result,
5317
+ isError: r.isError,
5318
+ ...r.recording ? { recording: r.recording } : {}
5505
5319
  });
5320
+ return r;
5321
+ })
5322
+ );
5323
+ for (const r of results) {
5324
+ const block = contentBlocks.find(
5325
+ (b) => b.type === "tool" && b.id === r.id
5326
+ );
5327
+ if (block?.type === "tool") {
5328
+ block.result = r.result;
5329
+ block.isError = r.isError;
5330
+ block.completedAt = Date.now();
5331
+ if (r.recording) {
5332
+ block.recording = r.recording;
5333
+ }
5334
+ const innerMsgs = subAgentMessages.get(r.id);
5335
+ if (innerMsgs) {
5336
+ attachSubAgentTranscript(block, innerMsgs);
5337
+ }
5338
+ if (captureArtifacts?.includes(block.name) && !r.isError) {
5339
+ try {
5340
+ artifacts[block.name] = JSON.parse(r.result);
5341
+ } catch {
5342
+ }
5343
+ }
5506
5344
  }
5345
+ messages.push({
5346
+ role: "user",
5347
+ content: r.result,
5348
+ toolCallId: r.id,
5349
+ isToolError: r.isError
5350
+ });
5507
5351
  }
5508
- } finally {
5509
- statusWatcher.stop();
5510
5352
  }
5511
5353
  };
5512
5354
  const wrapRun = async () => {
@@ -5578,7 +5420,6 @@ var init_runner = __esm({
5578
5420
  init_toolRegistry();
5579
5421
  init_historyLimits();
5580
5422
  init_recording();
5581
- init_statusWatcher();
5582
5423
  init_cleanMessages();
5583
5424
  log8 = createLogger("sub-agent");
5584
5425
  }
@@ -9393,6 +9234,153 @@ var init_parsePartialJson = __esm({
9393
9234
  }
9394
9235
  });
9395
9236
 
9237
+ // src/statusWatcher.ts
9238
+ import { createHash } from "crypto";
9239
+ function sanitizeStatusText(text) {
9240
+ if (!text) {
9241
+ return "";
9242
+ }
9243
+ for (const marker of INTERNAL_PAYLOAD_MARKERS) {
9244
+ if (text.includes(marker)) {
9245
+ return "";
9246
+ }
9247
+ }
9248
+ const truncIdx = text.indexOf("(tool result truncated at");
9249
+ return truncIdx === -1 ? text : text.slice(0, truncIdx);
9250
+ }
9251
+ function startStatusWatcher(config) {
9252
+ const { apiConfig, getContext, onStatus, interval = 5e3, signal } = config;
9253
+ let inflight2 = false;
9254
+ let stopped = false;
9255
+ let pauseCount = 0;
9256
+ let labeledContextHash = null;
9257
+ const url = `${apiConfig.baseUrl}/_internal/v2/agent/remy/generate-status`;
9258
+ let consecutiveFailures = 0;
9259
+ let backoffMs = 0;
9260
+ let nextAllowedAt = 0;
9261
+ const MAX_CONSECUTIVE_FAILURES = 10;
9262
+ const MAX_BACKOFF_MS = 6e4;
9263
+ function recordFailure() {
9264
+ consecutiveFailures++;
9265
+ backoffMs = Math.min(
9266
+ backoffMs === 0 ? interval : backoffMs * 2,
9267
+ MAX_BACKOFF_MS
9268
+ );
9269
+ nextAllowedAt = Date.now() + backoffMs;
9270
+ if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES && !stopped) {
9271
+ stopped = true;
9272
+ clearInterval(timer);
9273
+ }
9274
+ }
9275
+ async function tick() {
9276
+ if (stopped || signal?.aborted || inflight2 || pauseCount > 0) {
9277
+ return;
9278
+ }
9279
+ if (Date.now() < nextAllowedAt) {
9280
+ return;
9281
+ }
9282
+ inflight2 = true;
9283
+ try {
9284
+ const context = getContext();
9285
+ if (!context) {
9286
+ return;
9287
+ }
9288
+ const contextHash = createHash("sha1").update(context).digest("hex");
9289
+ const unchanged = contextHash === labeledContextHash;
9290
+ const startedAt = Date.now();
9291
+ const res = await fetch(url, {
9292
+ method: "POST",
9293
+ headers: {
9294
+ "Content-Type": "application/json",
9295
+ Authorization: `Bearer ${apiConfig.apiKey}`,
9296
+ // Also a liveness signal for this box, and the most frequent one there is — this ticks
9297
+ // every few seconds for as long as the agent is working, which is exactly the window in
9298
+ // which the user may have backgrounded the tab and stopped its own keepalive.
9299
+ ...sandboxSessionHeader()
9300
+ },
9301
+ body: JSON.stringify({
9302
+ appId: apiConfig.appId,
9303
+ ...unchanged ? {} : { context }
9304
+ }),
9305
+ signal
9306
+ });
9307
+ if (stopped) {
9308
+ return;
9309
+ }
9310
+ if (unchanged) {
9311
+ await res.body?.cancel().catch(() => {
9312
+ });
9313
+ return;
9314
+ }
9315
+ if (!res.ok) {
9316
+ recordFailure();
9317
+ return;
9318
+ }
9319
+ const data = await res.json();
9320
+ if (!data.label) {
9321
+ recordFailure();
9322
+ return;
9323
+ }
9324
+ recordUsage({
9325
+ ts: Date.now(),
9326
+ agentName: "statusLabel",
9327
+ modelId: data.modelId,
9328
+ inputTokens: data.usage?.inputTokens ?? 0,
9329
+ outputTokens: data.usage?.outputTokens ?? 0,
9330
+ cost: nanoToDollars(data.cost),
9331
+ billingEvents: data.billingEvents,
9332
+ durationMs: Date.now() - startedAt,
9333
+ toolNames: []
9334
+ });
9335
+ consecutiveFailures = 0;
9336
+ backoffMs = 0;
9337
+ nextAllowedAt = 0;
9338
+ if (pauseCount > 0 || stopped || signal?.aborted) {
9339
+ return;
9340
+ }
9341
+ onStatus(data.label);
9342
+ labeledContextHash = contextHash;
9343
+ } catch {
9344
+ if (!stopped && !signal?.aborted) {
9345
+ recordFailure();
9346
+ }
9347
+ } finally {
9348
+ inflight2 = false;
9349
+ }
9350
+ }
9351
+ const timer = setInterval(tick, interval);
9352
+ tick().catch(() => {
9353
+ });
9354
+ return {
9355
+ stop() {
9356
+ stopped = true;
9357
+ clearInterval(timer);
9358
+ },
9359
+ pause() {
9360
+ pauseCount++;
9361
+ },
9362
+ resume() {
9363
+ pauseCount = Math.max(0, pauseCount - 1);
9364
+ }
9365
+ };
9366
+ }
9367
+ var INTERNAL_PAYLOAD_MARKERS;
9368
+ var init_statusWatcher = __esm({
9369
+ "src/statusWatcher.ts"() {
9370
+ "use strict";
9371
+ init_api();
9372
+ init_usageLedger();
9373
+ INTERNAL_PAYLOAD_MARKERS = [
9374
+ "[USER CANCELLED]",
9375
+ "[INTERRUPTED]",
9376
+ "[INTERRUPTED - PARTIAL OUTPUT RETRIEVED]",
9377
+ "<background_results>",
9378
+ "<workspace_status>",
9379
+ "<tool_result"
9380
+ ];
9381
+ }
9382
+ });
9383
+
9396
9384
  // src/automatedActions/resolve.ts
9397
9385
  function resolveAction(text) {
9398
9386
  const parsed = parseSentinel(text);
@@ -9681,7 +9669,7 @@ var init_suggestions = __esm({
9681
9669
  // src/brandExtraction/index.ts
9682
9670
  import fs20 from "fs";
9683
9671
  import path11 from "path";
9684
- import { createHash } from "crypto";
9672
+ import { createHash as createHash2 } from "crypto";
9685
9673
  async function runExtraction(apiConfig, model) {
9686
9674
  const inputHash = computeInputHash();
9687
9675
  const cached3 = readCache();
@@ -9722,7 +9710,7 @@ function computeInputHash() {
9722
9710
  return sha256(fingerprint);
9723
9711
  }
9724
9712
  function sha256(input) {
9725
- return createHash("sha256").update(input).digest("hex");
9713
+ return createHash2("sha256").update(input).digest("hex");
9726
9714
  }
9727
9715
  function readSafe(filePath) {
9728
9716
  try {