@myagentroam/agent 0.12.1 → 0.12.3

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.
@@ -32,5 +32,4 @@ export interface DomainCompactOperations {
32
32
  output: ExtensionCompactResult | undefined;
33
33
  };
34
34
  }
35
- export interface DomainCompactFacts {
36
- }
35
+ export type DomainCompactFacts = Record<never, never>;
@@ -60,8 +60,7 @@ export type ExtensionFileCompletion = {
60
60
  };
61
61
  }[keyof ExtensionFilesApi];
62
62
  export declare function bindCoreFiles(backend: ExtensionFilesBackend, run: <Value>(operation: (call: ExtensionOperationContext) => Promise<Value>) => Promise<Value>, hooks: Pick<ExtensionHooks<DomainFileOperations, DomainFileFacts>, 'invoke' | 'emit'>): ExtensionFilesApi;
63
- export interface DomainFileOperations extends ExtensionFileOperations {
64
- }
63
+ export type DomainFileOperations = ExtensionFileOperations;
65
64
  export interface DomainFileFacts {
66
65
  'files.completed': ExtensionFileCompletion;
67
66
  }
@@ -61,5 +61,4 @@ export interface DomainModelRequestOperations {
61
61
  output: ExtensionModelRequest;
62
62
  };
63
63
  }
64
- export interface DomainModelRequestFacts {
65
- }
64
+ export type DomainModelRequestFacts = Record<never, never>;
@@ -30,8 +30,7 @@ export interface DomainPromptCompositionOperations {
30
30
  output: string | null;
31
31
  };
32
32
  }
33
- export interface DomainPromptCompositionFacts {
34
- }
33
+ export type DomainPromptCompositionFacts = Record<never, never>;
35
34
  export interface ExtensionPromptSection {
36
35
  readonly id: string;
37
36
  readonly text: string;
@@ -26,5 +26,4 @@ export interface DomainPromptInputOperations {
26
26
  output: ExtensionPromptResult;
27
27
  };
28
28
  }
29
- export interface DomainPromptInputFacts {
30
- }
29
+ export type DomainPromptInputFacts = Record<never, never>;
@@ -3,11 +3,8 @@ export interface ExtensionResourceRegistration {
3
3
  readonly ready: Promise<void>;
4
4
  dispose(): Promise<void>;
5
5
  }
6
- export interface ExtensionResourceScope {
7
- effect(acquire: () => ExtensionDisposer | Promise<ExtensionDisposer>): ExtensionResourceRegistration;
8
- }
9
6
  /** An instance's managed resources, not an OS sandbox or a process-wide resource owner. */
10
- export declare class ExtensionResourceScope implements ExtensionResourceScope {
7
+ export declare class ExtensionResourceScope {
11
8
  #private;
12
9
  readonly instanceId: `${string}-${string}-${string}-${string}-${string}`;
13
10
  static get initializing(): boolean;
@@ -15,6 +12,7 @@ export declare class ExtensionResourceScope implements ExtensionResourceScope {
15
12
  get state(): "STAGED" | "ACTIVE" | "RETIRING" | "DISPOSED";
16
13
  get signal(): AbortSignal;
17
14
  activate(): void;
15
+ effect(acquire: () => ExtensionDisposer | Promise<ExtensionDisposer>): ExtensionResourceRegistration;
18
16
  run<T>(operation: (signal: AbortSignal) => Promise<T>, parent?: AbortSignal): Promise<T>;
19
17
  after(durationMs: number, callback: (signal: AbortSignal) => void | Promise<void>, onError?: (error: unknown) => void): ExtensionResourceRegistration;
20
18
  every(durationMs: number, callback: (signal: AbortSignal) => void | Promise<void>, onError?: (error: unknown) => void): ExtensionResourceRegistration;
@@ -1,5 +1,5 @@
1
1
  import { type LocalProcessExecutor } from './local-process-executor.js';
2
- import type { ExecOutputSnapshot, ExecResult } from '../tools/contracts.js';
2
+ import type { ExecOutputSnapshot, ExecResult, ExecutionSource } from '../tools/contracts.js';
3
3
  import { type ToolWaitInterrupt } from '../tools/wait.js';
4
4
  import { WorkspacePathResolver } from '../tools/shared/path-resolver.js';
5
5
  import { type ExtensionProcessInput, type ExtensionProcessHandle } from '../extensions/processes.js';
@@ -13,7 +13,7 @@ export declare class ExecTool {
13
13
  private readonly shell?;
14
14
  private readonly processExecutor;
15
15
  constructor(paths: WorkspacePathResolver, maxOutputBytes?: number, maxProcesses?: number, environment?: NodeJS.ProcessEnv, shell?: string | undefined, processExecutor?: LocalProcessExecutor);
16
- execute(arguments_: unknown, signal?: AbortSignal, owner?: NonNullable<import('./local-process-executor.js').LocalProcessSpawnRequest['owner']>, waitInterrupt?: ToolWaitInterrupt, onOutput?: (output: ExecOutputSnapshot) => Promise<void>): Promise<ExecResult>;
16
+ execute(arguments_: unknown, signal?: AbortSignal, owner?: NonNullable<import('./local-process-executor.js').LocalProcessSpawnRequest['owner']>, waitInterrupt?: ToolWaitInterrupt, onOutput?: (output: ExecOutputSnapshot) => Promise<void>, onExecutionSource?: (source: ExecutionSource) => Promise<void> | void): Promise<ExecResult>;
17
17
  spawn(input: ExtensionProcessInput, call: ExtensionOperationContext): Promise<ExtensionProcessHandle>;
18
18
  listSessionResources(sessionId: string): {
19
19
  processId: string;
@@ -6,6 +6,14 @@ import { TOOL_EXECUTION_LIMITS } from '../tools/execution-limits.js';
6
6
  import { waitForToolCondition, waitMetadata } from '../tools/wait.js';
7
7
  import { validateCoreProcessInput } from '../extensions/processes.js';
8
8
  import { parseExecInput } from '../tools/local/exec.js';
9
+ function processSource(process) {
10
+ return {
11
+ kind: 'SHELL',
12
+ text: process.command.slice(0, 10_000),
13
+ cwd: process.cwd.slice(0, 10_000),
14
+ truncated: process.command.length > 10_000 || process.cwd.length > 10_000
15
+ };
16
+ }
9
17
  export class ExecTool {
10
18
  paths;
11
19
  maxOutputBytes;
@@ -25,7 +33,7 @@ export class ExecTool {
25
33
  this.shell = shell;
26
34
  this.processExecutor = processExecutor;
27
35
  }
28
- async execute(arguments_, signal, owner, waitInterrupt, onOutput) {
36
+ async execute(arguments_, signal, owner, waitInterrupt, onOutput, onExecutionSource) {
29
37
  if (signal?.aborted)
30
38
  throw executionAbortReason(signal);
31
39
  const input = parseExecInput(arguments_);
@@ -36,6 +44,10 @@ export class ExecTool {
36
44
  if (completed) {
37
45
  if (owner !== undefined && completed.ownerSessionId !== owner.sessionId)
38
46
  throw new AgentError('MAR_AGENT_PROCESS_NOT_FOUND', 'Managed process was not found.');
47
+ if (onExecutionSource)
48
+ await onExecutionSource(processSource(completed));
49
+ if (signal?.aborted)
50
+ throw executionAbortReason(signal);
39
51
  if (input.action === 'write')
40
52
  throw new AgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Completed process cannot accept input.');
41
53
  if (input.action === 'cancel')
@@ -52,6 +64,10 @@ export class ExecTool {
52
64
  process.native ||
53
65
  (owner !== undefined && process.ownerSessionId !== owner.sessionId))
54
66
  throw new AgentError('MAR_AGENT_PROCESS_NOT_FOUND', 'Managed process was not found.');
67
+ if (onExecutionSource)
68
+ await onExecutionSource(processSource(process));
69
+ if (signal?.aborted)
70
+ throw executionAbortReason(signal);
55
71
  let outcome;
56
72
  if (input.action === 'poll' && input.yieldMs)
57
73
  outcome = await waitForProcessCompletion(input.processId, process, Math.min(Math.max(input.yieldMs, 0), TOOL_EXECUTION_LIMITS.execMaxPollYieldMs), signal, waitInterrupt, onOutput);
@@ -427,12 +443,7 @@ export class ExecTool {
427
443
  stdoutDroppedBytes: stdout.droppedBytes,
428
444
  stderrDroppedBytes: stderr.droppedBytes,
429
445
  displayOutput: displayOutput(process),
430
- executionSource: {
431
- kind: 'SHELL',
432
- text: process.command.slice(0, 10_000),
433
- cwd: process.cwd.slice(0, 10_000),
434
- truncated: process.command.length > 10_000 || process.cwd.length > 10_000
435
- }
446
+ executionSource: processSource(process)
436
447
  }
437
448
  });
438
449
  while (this.#completed.size > TOOL_EXECUTION_LIMITS.execMaxCompletedResults)
@@ -496,12 +507,7 @@ export class ExecTool {
496
507
  stdoutDroppedBytes: stdout.droppedBytes,
497
508
  stderrDroppedBytes: stderr.droppedBytes,
498
509
  displayOutput: displayOutput(process),
499
- executionSource: {
500
- kind: 'SHELL',
501
- text: process.command.slice(0, 10_000),
502
- cwd: process.cwd.slice(0, 10_000),
503
- truncated: process.command.length > 10_000 || process.cwd.length > 10_000
504
- }
510
+ executionSource: processSource(process)
505
511
  };
506
512
  }
507
513
  async dispose() {
@@ -98,7 +98,7 @@ export class LocalToolRegistry {
98
98
  ? {}
99
99
  : { ownerInstanceId: context.operation.ownerInstanceId })
100
100
  })
101
- }, context.waitInterrupt, context.onOutput);
101
+ }, context.waitInterrupt, context.onOutput, context.onExecutionSource);
102
102
  return formatExecResult(result);
103
103
  }
104
104
  ])
@@ -166,6 +166,3 @@ async function matchingCheckpointWasPersisted(store, sessionId, draft, records,
166
166
  });
167
167
  return matches ? { records: updatedRecords, projection } : false;
168
168
  }
169
- function isRecord(value) {
170
- return value !== null && typeof value === 'object' && !Array.isArray(value);
171
- }
@@ -53,12 +53,16 @@ export async function* observeModelProgress(start, parent) {
53
53
  }
54
54
  finally {
55
55
  controller.abort();
56
- try {
57
- await iterator.return?.();
56
+ if (failed) {
57
+ try {
58
+ await iterator.return?.();
59
+ }
60
+ catch {
61
+ // Keep the generation failure rather than replacing it with a cleanup failure.
62
+ }
58
63
  }
59
- catch (error) {
60
- if (!failed)
61
- throw error;
64
+ else {
65
+ await iterator.return?.();
62
66
  }
63
67
  }
64
68
  }
@@ -107,7 +107,7 @@ export async function launchBubblewrap(spec) {
107
107
  if (childExited || childError || child.exitCode !== null || child.signalCode !== null)
108
108
  throw unavailable();
109
109
  }
110
- catch (error) {
110
+ catch {
111
111
  failed = true;
112
112
  failure = spec.signal?.aborted ? spec.signal.reason : unavailable();
113
113
  }
@@ -80,7 +80,7 @@ export class SessionExecution {
80
80
  this.snapshot = snapshot;
81
81
  this.compactRequests = new CompactRequests(executionId, () => this.compact.assertNotReentrant());
82
82
  this.#bindings = { compactRequests: this.compactRequests };
83
- const owner = this;
83
+ const bindings = () => this.#bindings;
84
84
  this.context = Object.freeze({
85
85
  executionId,
86
86
  modelId: snapshot.modelId,
@@ -88,19 +88,19 @@ export class SessionExecution {
88
88
  models: snapshot.models,
89
89
  instruction: snapshot.instruction,
90
90
  get compactRequests() {
91
- return owner.#bindings.compactRequests;
91
+ return bindings().compactRequests;
92
92
  },
93
93
  get steer() {
94
- return owner.#bindings.steer;
94
+ return bindings().steer;
95
95
  },
96
96
  get requestMessages() {
97
- return owner.#bindings.requestMessages;
97
+ return bindings().requestMessages;
98
98
  },
99
99
  get appendedMessages() {
100
- return owner.#bindings.appendedMessages;
100
+ return bindings().appendedMessages;
101
101
  },
102
102
  get toolSources() {
103
- return owner.#bindings.toolSources;
103
+ return bindings().toolSources;
104
104
  }
105
105
  });
106
106
  this.modelResources = createExecutionModelResources(activeAdapters);
@@ -134,13 +134,16 @@ export class SessionExecution {
134
134
  throw error;
135
135
  }
136
136
  finally {
137
- try {
138
- await turn.close();
137
+ if (failed) {
138
+ try {
139
+ await turn.close();
140
+ }
141
+ catch {
142
+ // A cleanup failure cannot replace the generation failure.
143
+ }
139
144
  }
140
- catch (error) {
141
- // A cleanup failure cannot replace the generation failure.
142
- if (!failed)
143
- throw error;
145
+ else {
146
+ await turn.close();
144
147
  }
145
148
  }
146
149
  }
@@ -403,10 +406,10 @@ export class SessionExecution {
403
406
  return this.resultPromise;
404
407
  }
405
408
  handle() {
406
- const owner = this;
409
+ const persistedEvents = () => this.persistedTerminal;
407
410
  return {
408
411
  get persistedEvents() {
409
- return owner.persistedTerminal;
412
+ return persistedEvents();
410
413
  },
411
414
  executionId: this.executionId,
412
415
  turnId: this.turnId,
@@ -415,7 +418,7 @@ export class SessionExecution {
415
418
  this.events.discardIfUnobserved();
416
419
  return this.resultPromise;
417
420
  },
418
- cancel: () => owner.cancel(new AgentError('MAR_AGENT_CANCELLED', 'Execution was cancelled.'))
421
+ cancel: () => this.cancel(new AgentError('MAR_AGENT_CANCELLED', 'Execution was cancelled.'))
419
422
  };
420
423
  }
421
424
  cancel(reason) {
@@ -72,12 +72,16 @@ export class SessionMaintenanceCoordinator {
72
72
  throw error;
73
73
  }
74
74
  finally {
75
- try {
76
- await maintenance.release();
75
+ if (failed) {
76
+ try {
77
+ await maintenance.release();
78
+ }
79
+ catch {
80
+ // Preserve the failure from clearing the Session.
81
+ }
77
82
  }
78
- catch (error) {
79
- if (!failed)
80
- throw error;
83
+ else {
84
+ await maintenance.release();
81
85
  }
82
86
  }
83
87
  });
@@ -150,11 +154,17 @@ export class SessionMaintenanceCoordinator {
150
154
  }
151
155
  finally {
152
156
  try {
153
- await maintenance.release();
154
- }
155
- catch (error) {
156
- if (!failed)
157
- throw error;
157
+ if (failed) {
158
+ try {
159
+ await maintenance.release();
160
+ }
161
+ catch {
162
+ // Preserve the failure from deleting the Sessions.
163
+ }
164
+ }
165
+ else {
166
+ await maintenance.release();
167
+ }
158
168
  }
159
169
  finally {
160
170
  for (const sessionId of ids) {
@@ -1,4 +1,3 @@
1
1
  import type { SessionServices } from './session-composition.js';
2
- import type { AgentSession } from './types.js';
3
2
  /** SDK Session orchestration owns warm resources; Runtime owns only the model/tool loop. */
4
- export declare function createSessionOpener(services: SessionServices): (id: string, workspace?: string) => Promise<AgentSession>;
3
+ export declare function createSessionOpener(services: SessionServices): (id: string, workspace?: string) => Promise<import("./types.js").AgentSession>;
@@ -225,12 +225,16 @@ export function createSessionCatalog(store, borrowed) {
225
225
  throw error;
226
226
  }
227
227
  finally {
228
- try {
229
- await maintenance.release();
228
+ if (failed) {
229
+ try {
230
+ await maintenance.release();
231
+ }
232
+ catch {
233
+ // Preserve the failure from deleting the Sessions.
234
+ }
230
235
  }
231
- catch (error) {
232
- if (!failed)
233
- throw error;
236
+ else {
237
+ await maintenance.release();
234
238
  }
235
239
  }
236
240
  },
@@ -1165,6 +1165,3 @@ function isNonnegativeInteger(value) {
1165
1165
  function isNonnegativeFinite(value) {
1166
1166
  return typeof value === 'number' && Number.isFinite(value) && value >= 0;
1167
1167
  }
1168
- function isValidBoundary(value) {
1169
- return Number.isSafeInteger(value) && value > 0;
1170
- }
@@ -42,7 +42,7 @@ export function buildContextGcProtection(records, modelId, contextWindowTokens)
42
42
  if (attempt?.matching) {
43
43
  if (payload.state === 'SUCCEEDED')
44
44
  successful.push(attempt.start);
45
- for (const _identity of attempt.native)
45
+ for (let i = 0; i < attempt.native.size; i++)
46
46
  thinking.push(attempt.start);
47
47
  }
48
48
  attempts.delete(key);
@@ -60,7 +60,7 @@ export function buildContextGcProtection(records, modelId, contextWindowTokens)
60
60
  }
61
61
  for (const attempt of attempts.values())
62
62
  if (attempt.matching)
63
- for (const _identity of attempt.native)
63
+ for (let i = 0; i < attempt.native.size; i++)
64
64
  thinking.push(attempt.start);
65
65
  successful.sort((a, b) => a - b);
66
66
  thinking.sort((a, b) => a - b);
@@ -353,9 +353,6 @@ function stableJson(value) {
353
353
  function isRecord(value) {
354
354
  return value !== null && typeof value === 'object' && !Array.isArray(value);
355
355
  }
356
- function isNonemptyString(value) {
357
- return typeof value === 'string' && value.length > 0;
358
- }
359
356
  function isNonnegativeInteger(value) {
360
357
  return Number.isSafeInteger(value) && Number(value) >= 0;
361
358
  }
@@ -19,7 +19,7 @@ export function generatedImageFileNames(records) {
19
19
  artifact.kind !== 'image' ||
20
20
  typeof artifact.fileName !== 'string' ||
21
21
  basename(artifact.fileName) !== artifact.fileName ||
22
- !/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}\.png$/u.test(artifact.fileName))
22
+ !/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}\.(?:png|gif)$/u.test(artifact.fileName))
23
23
  continue;
24
24
  names.add(artifact.fileName);
25
25
  }
@@ -2146,6 +2146,7 @@ export class JsonlSessionStore {
2146
2146
  return;
2147
2147
  const releases = [];
2148
2148
  let failure;
2149
+ let failed = false;
2149
2150
  try {
2150
2151
  for (const member of [...transaction.members].sort((a, b) => a.id.localeCompare(b.id))) {
2151
2152
  if (await sessionDirectoryExists(join(root, member.id)))
@@ -2171,22 +2172,22 @@ export class JsonlSessionStore {
2171
2172
  await this.#refreshIndex();
2172
2173
  }
2173
2174
  catch (error) {
2175
+ failed = true;
2174
2176
  failure = error;
2175
- throw error;
2176
2177
  }
2177
- finally {
2178
- let releaseFailure;
2179
- for (const release of releases.reverse()) {
2180
- try {
2181
- await release();
2182
- }
2183
- catch (error) {
2184
- releaseFailure ??= error;
2185
- }
2178
+ let releaseFailure;
2179
+ for (const release of releases.reverse()) {
2180
+ try {
2181
+ await release();
2182
+ }
2183
+ catch (error) {
2184
+ releaseFailure ??= error;
2186
2185
  }
2187
- if (failure === undefined && releaseFailure !== undefined)
2188
- throw releaseFailure;
2189
2186
  }
2187
+ if (failure === undefined && releaseFailure !== undefined)
2188
+ throw releaseFailure;
2189
+ if (failed)
2190
+ throw failure;
2190
2191
  }
2191
2192
  async delete(sessionId, maintenance) {
2192
2193
  this.#assertOpen();
@@ -48,7 +48,6 @@ export async function* readRecordsFromHead(path, encoding = 1) {
48
48
  pending.push(data.subarray(start));
49
49
  }
50
50
  if (pending.length > 0) {
51
- let record;
52
51
  const tail = Buffer.concat(pending).toString('utf8');
53
52
  let value;
54
53
  try {
@@ -57,7 +56,7 @@ export async function* readRecordsFromHead(path, encoding = 1) {
57
56
  catch {
58
57
  return; // Readers ignore only an interrupted final line.
59
58
  }
60
- record = decodeJsonlValue(value, encoding);
59
+ const record = decodeJsonlValue(value, encoding);
61
60
  yield record;
62
61
  }
63
62
  }
@@ -22,6 +22,7 @@ export interface SessionInputImageMetadata {
22
22
  readonly urlExpiresAt?: number;
23
23
  }
24
24
  export interface RestoredSessionInputImage extends ModelImage {
25
+ readonly mimeType: ModelImage['mimeType'];
25
26
  }
26
27
  export interface SessionInputImageData extends SessionInputImageMetadata {
27
28
  readonly dataBase64: string;
@@ -298,6 +298,9 @@ export class CodeCells {
298
298
  const cell = this.#cells.get(input.cellId);
299
299
  if (!cell || cell.sessionId !== context.sessionId)
300
300
  throw new AgentError('MAR_AGENT_CODE_CELL_NOT_FOUND', 'Code Exec cell is unavailable in this Session.');
301
+ if (context.onExecutionSource)
302
+ await context.onExecutionSource({ ...cell.source });
303
+ context.signal.throwIfAborted();
301
304
  if (input.action === 'cancel' && cell.status === 'running') {
302
305
  cell.controller.abort(new AgentError('MAR_AGENT_CODE_CANCELLED', 'Code Exec cell was terminated.'));
303
306
  await new Promise((resolve, reject) => {
@@ -33,6 +33,8 @@ export interface ToolExecutionContext {
33
33
  waitInterrupt?: import('./wait.js').ToolWaitInterrupt | undefined;
34
34
  /** Live-only process output; does not consume the eventual tool result. */
35
35
  onOutput?: (output: ExecOutputSnapshot) => Promise<void>;
36
+ /** Publish owner-confirmed source metadata before waiting, without consuming output. */
37
+ onExecutionSource?: (source: ExecutionSource) => Promise<void> | void;
36
38
  sessionId: string;
37
39
  executionId: string;
38
40
  /** Current model identity for neutral Host-owned tool policies. */
@@ -88,7 +90,7 @@ export interface McpToolCallContext extends Pick<ToolExecutionContext, 'sessionI
88
90
  export interface AgentToolImageArtifact {
89
91
  kind: 'image';
90
92
  name: string;
91
- mimeType: 'image/png';
93
+ mimeType: 'image/png' | 'image/gif';
92
94
  byteSize: number;
93
95
  sha256: string;
94
96
  fileName: string;
@@ -119,8 +119,10 @@ function compare(a, b) {
119
119
  : k - 1;
120
120
  const previousX = previous.get(previousK) ?? 0;
121
121
  const previousY = previousX - previousK;
122
- while (x > previousX && y > previousY)
123
- (reversed.push({ kind: ' ', text: left[--x] }), y--);
122
+ while (x > previousX && y > previousY) {
123
+ reversed.push({ kind: ' ', text: left[--x] });
124
+ y--;
125
+ }
124
126
  if (d === 0)
125
127
  break;
126
128
  if (x === previousX)
@@ -56,7 +56,10 @@ function displayWidth(text) {
56
56
  function displayCharacter(character) {
57
57
  if (character === '\t')
58
58
  return ' ';
59
- if (/[\u0000-\u001f\u007f-\u009f\u2028\u2029\p{Bidi_Control}]/u.test(character)) {
59
+ const code = character.codePointAt(0);
60
+ if (code <= 0x1f ||
61
+ (code >= 0x7f && code <= 0x9f) ||
62
+ /[\u2028\u2029\p{Bidi_Control}]/u.test(character)) {
60
63
  const escaped = JSON.stringify(character).slice(1, -1);
61
64
  return escaped === character ? `\\u{${character.codePointAt(0).toString(16)}}` : escaped;
62
65
  }
@@ -171,7 +171,9 @@ function commonPrefixLength(actual, expected) {
171
171
  }
172
172
  function characterCount(value) {
173
173
  let count = 0;
174
- for (const _character of value)
175
- count++;
174
+ for (let index = 0; index < value.length; index++, count++) {
175
+ if (value.codePointAt(index) > 0xffff)
176
+ index++;
177
+ }
176
178
  return count;
177
179
  }
@@ -439,11 +439,12 @@ export function structuredToolArtifacts(value) {
439
439
  value.length !== 1 ||
440
440
  value.some((artifact) => !artifact ||
441
441
  artifact.kind !== 'image' ||
442
- artifact.mimeType !== 'image/png' ||
442
+ (artifact.mimeType !== 'image/png' && artifact.mimeType !== 'image/gif') ||
443
443
  typeof artifact.name !== 'string' ||
444
444
  artifact.name.length === 0 ||
445
445
  typeof artifact.fileName !== 'string' ||
446
- !/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}\.png$/u.test(artifact.fileName) ||
446
+ !/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}\.(?:png|gif)$/u.test(artifact.fileName) ||
447
+ !artifact.fileName.endsWith(artifact.mimeType === 'image/gif' ? '.gif' : '.png') ||
447
448
  !isNonnegativeInteger(artifact.byteSize) ||
448
449
  typeof artifact.sha256 !== 'string' ||
449
450
  !/^[a-f0-9]{64}$/u.test(artifact.sha256)))
@@ -53,6 +53,13 @@ export async function executeCapturedTool(kind, call, context) {
53
53
  ...((name === 'exec' || name === 'exec_wait' || name === 'code_exec_wait') &&
54
54
  callId !== undefined
55
55
  ? {
56
+ onExecutionSource: (source) => context.emit({
57
+ type: 'tool.updated',
58
+ toolName: name,
59
+ callId,
60
+ ...(context.parentCallId === undefined ? {} : { parentCallId: context.parentCallId }),
61
+ data: { executionSource: source }
62
+ }),
56
63
  onOutput: (output) => context.emit({
57
64
  type: 'tool.output',
58
65
  toolName: name,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myagentroam/agent",
3
- "version": "0.12.1",
3
+ "version": "0.12.3",
4
4
  "description": "Embeddable MAR coding agent SDK and CLI.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",