@rowan-agent/agent 0.9.2 → 0.9.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.
package/dist/index.d.ts CHANGED
@@ -789,6 +789,7 @@ type UserMessage = MessageBase & Readonly<{
789
789
  type AssistantMessage = MessageBase & Readonly<{
790
790
  role: "assistant";
791
791
  content: AssistantContent;
792
+ interrupted?: boolean;
792
793
  }>;
793
794
  type ToolMessage = MessageBase & Readonly<{
794
795
  role: "tool";
@@ -1342,6 +1343,11 @@ type AgentRecord = Readonly<{
1342
1343
  activatedAt?: string;
1343
1344
  updatedAt: string;
1344
1345
  }>;
1346
+ type AgentDeletionRequest = Readonly<{
1347
+ agentId: AgentId;
1348
+ expectedRunIds: readonly RunId[];
1349
+ confirmation: "conversation-delete-v1";
1350
+ }>;
1345
1351
  type ExecutionToken = Readonly<{
1346
1352
  runId: RunId;
1347
1353
  ownerEpoch: number;
@@ -1522,6 +1528,7 @@ interface OwnedStore {
1522
1528
  configIdentity?: string;
1523
1529
  idempotencyKey: string;
1524
1530
  }): Promise<AgentRecord>;
1531
+ deleteAgent(input: AgentDeletionRequest): Promise<void>;
1525
1532
  createRun(input: {
1526
1533
  agentId: AgentId;
1527
1534
  input: UserInput;
@@ -1600,6 +1607,7 @@ interface OwnedStore {
1600
1607
  runId: RunId;
1601
1608
  expectedRevision?: number;
1602
1609
  reason?: string;
1610
+ output?: AssistantMessage;
1603
1611
  }): Promise<RunRecord>;
1604
1612
  snapshotRun(runId: RunId): Promise<RunSnapshot>;
1605
1613
  listAgents(): Promise<readonly AgentRecord[]>;
@@ -1670,6 +1678,7 @@ interface AgentRuntime$1 {
1670
1678
  updateAgentConfig(agentId: AgentId, config: AgentConfigRequest, options: {
1671
1679
  idempotencyKey: string;
1672
1680
  }): Promise<void>;
1681
+ deleteAgent(input: AgentDeletionRequest): Promise<void>;
1673
1682
  start(agentId: AgentId, input: UserInput, options: {
1674
1683
  idempotencyKey: string;
1675
1684
  metadata?: Metadata;
@@ -1706,6 +1715,9 @@ declare class AgentRuntime implements AgentRuntime$1 {
1706
1715
  private readonly storeIncarnation;
1707
1716
  private readonly activeAgents;
1708
1717
  private readonly executions;
1718
+ private readonly executionDone;
1719
+ private readonly cancellationRequested;
1720
+ private readonly cancellationReasons;
1709
1721
  private readonly consumers;
1710
1722
  private readonly transientEvents;
1711
1723
  private readonly resources;
@@ -1732,6 +1744,7 @@ declare class AgentRuntime implements AgentRuntime$1 {
1732
1744
  updateAgentConfig(agentId: AgentId, config: AgentConfigRequest, options: {
1733
1745
  idempotencyKey: string;
1734
1746
  }): Promise<void>;
1747
+ deleteAgent(input: AgentDeletionRequest): Promise<void>;
1735
1748
  start(agentId: AgentId, input: UserInput, options: {
1736
1749
  idempotencyKey: string;
1737
1750
  metadata?: Metadata;
@@ -1840,6 +1853,7 @@ declare class InMemoryStore implements DurableStore {
1840
1853
  configIdentity?: string;
1841
1854
  idempotencyKey: string;
1842
1855
  }): AgentRecord;
1856
+ deleteAgent(lease: OwnerLease, input: AgentDeletionRequest): void;
1843
1857
  createRun(lease: OwnerLease, input: {
1844
1858
  agentId: AgentId;
1845
1859
  input: UserInput;
@@ -1925,6 +1939,7 @@ declare class InMemoryStore implements DurableStore {
1925
1939
  runId: RunId;
1926
1940
  expectedRevision?: number;
1927
1941
  reason?: string;
1942
+ output?: AssistantMessage;
1928
1943
  }): RunRecord;
1929
1944
  snapshotRun(lease: OwnerLease, runId: RunId): RunSnapshot;
1930
1945
  listAgents(lease: OwnerLease): readonly AgentRecord[];
@@ -1987,6 +2002,7 @@ declare class SqliteStore implements DurableStore {
1987
2002
  configIdentity?: string;
1988
2003
  idempotencyKey: string;
1989
2004
  }): Promise<AgentRecord>;
2005
+ deleteAgent(lease: OwnerLease, input: AgentDeletionRequest): Promise<void>;
1990
2006
  createRun(lease: OwnerLease, input: {
1991
2007
  agentId: AgentId;
1992
2008
  input: UserInput;
@@ -2072,6 +2088,7 @@ declare class SqliteStore implements DurableStore {
2072
2088
  runId: RunId;
2073
2089
  expectedRevision?: number;
2074
2090
  reason?: string;
2091
+ output?: AssistantMessage;
2075
2092
  }): Promise<RunRecord>;
2076
2093
  snapshotRun(lease: OwnerLease, runId: RunId): Promise<RunSnapshot>;
2077
2094
  listAgents(lease: OwnerLease): Promise<readonly AgentRecord[]>;
package/dist/index.js CHANGED
@@ -2879,6 +2879,14 @@ function isImage(value) {
2879
2879
  function isUserContent(value) {
2880
2880
  return typeof value === "string" || Array.isArray(value) && value.every((part) => isText(part) || isImage(part));
2881
2881
  }
2882
+ function isAssistantContent(value) {
2883
+ return typeof value === "string" || Array.isArray(value) && value.every((part) => {
2884
+ if (isText(part)) return true;
2885
+ if (!isRecord3(part)) return false;
2886
+ if (part.type === "thinking") return typeof part.thinking === "string" && (part.signature === void 0 || typeof part.signature === "string");
2887
+ return part.type === "tool_use" && typeof part.toolCallId === "string" && typeof part.name === "string" && isJsonValue(part.input);
2888
+ });
2889
+ }
2882
2890
  function normalizeUserInput(input) {
2883
2891
  const normalized = typeof input === "string" ? { content: input } : input;
2884
2892
  if (!isRecord3(normalized) || !hasOnlyKeys(normalized, ["content", "metadata"]) || !isUserContent(normalized.content)) throw new TypeError("input must contain only valid UserContent");
@@ -2886,6 +2894,9 @@ function normalizeUserInput(input) {
2886
2894
  assertJsonValue(normalized, "input");
2887
2895
  return normalized;
2888
2896
  }
2897
+ function isAssistantMessage(value) {
2898
+ return isRecord3(value) && hasOnlyKeys(value, ["id", "agentId", "runId", "role", "content", "metadata", "sequenceWithinRun", "createdAt", "interrupted"]) && typeof value.id === "string" && typeof value.agentId === "string" && typeof value.runId === "string" && value.role === "assistant" && Number.isInteger(value.sequenceWithinRun) && value.sequenceWithinRun >= 0 && typeof value.createdAt === "string" && isAssistantContent(value.content) && (value.metadata === void 0 || isMetadata(value.metadata)) && (value.interrupted === void 0 || typeof value.interrupted === "boolean");
2899
+ }
2889
2900
  function isToolResult(value) {
2890
2901
  if (!isRecord3(value) || !isJsonValue(value.content) || typeof value.ok !== "boolean") return false;
2891
2902
  const expected = value.ok ? ["content", "ok"] : ["content", "error", "ok"];
@@ -3402,18 +3413,23 @@ function projectModelContext(input) {
3402
3413
  ...input.context.phases ? { phases: input.context.phases } : {}
3403
3414
  };
3404
3415
  }
3405
- function projectAssistantMessage(message, agentId, runId, sequenceWithinRun) {
3416
+ function projectAssistantMessage(message, agentId, runId, sequenceWithinRun, options = {}) {
3406
3417
  return {
3407
3418
  id: message.id,
3408
3419
  agentId,
3409
3420
  runId,
3410
3421
  role: "assistant",
3411
- content: durableAssistantContent(message.content),
3422
+ content: options.interrupted ? durableInterruptedAssistantContent(message.content) : durableAssistantContent(message.content),
3412
3423
  sequenceWithinRun,
3413
3424
  createdAt: message.createdAt,
3414
- ...message.metadata ? { metadata: message.metadata } : {}
3425
+ ...message.metadata ? { metadata: message.metadata } : {},
3426
+ ...options.interrupted ? { interrupted: true } : {}
3415
3427
  };
3416
3428
  }
3429
+ function durableInterruptedAssistantContent(content) {
3430
+ if (typeof content === "string") return content;
3431
+ return content.filter((part) => part.type === "text").map((part) => ({ type: "text", text: part.text }));
3432
+ }
3417
3433
  function projectMessage(message) {
3418
3434
  switch (message.role) {
3419
3435
  case "user":
@@ -5103,6 +5119,9 @@ var AgentRuntime = class _AgentRuntime {
5103
5119
  storeIncarnation;
5104
5120
  activeAgents = /* @__PURE__ */ new Set();
5105
5121
  executions = /* @__PURE__ */ new Map();
5122
+ executionDone = /* @__PURE__ */ new Map();
5123
+ cancellationRequested = /* @__PURE__ */ new Set();
5124
+ cancellationReasons = /* @__PURE__ */ new Map();
5106
5125
  consumers = /* @__PURE__ */ new Map();
5107
5126
  transientEvents = new TransientRunEventHub();
5108
5127
  resources;
@@ -5167,6 +5186,16 @@ var AgentRuntime = class _AgentRuntime {
5167
5186
  this.assertOpen();
5168
5187
  await this.commands.updateAgentConfig({ agentId, config, idempotencyKey: options.idempotencyKey });
5169
5188
  }
5189
+ async deleteAgent(input) {
5190
+ this.assertOpen();
5191
+ const runs = await this.owned.listRuns({ agentId: input.agentId });
5192
+ for (const run of runs) {
5193
+ if (["queued", "running", "input_required"].includes(run.state)) {
5194
+ await this.cancel(run.id, "Conversation deleted.");
5195
+ }
5196
+ }
5197
+ await this.owned.deleteAgent(input);
5198
+ }
5170
5199
  async start(agentId, input, options) {
5171
5200
  this.assertOpen();
5172
5201
  const agent = await this.requireAgent(agentId);
@@ -5231,6 +5260,14 @@ var AgentRuntime = class _AgentRuntime {
5231
5260
  }
5232
5261
  async cancel(runId, reason) {
5233
5262
  this.assertOpen();
5263
+ const done = this.executionDone.get(runId);
5264
+ if (done) {
5265
+ this.cancellationRequested.add(runId);
5266
+ this.cancellationReasons.set(runId, reason ?? "Agent run stopped.");
5267
+ this.executions.get(runId)?.controller.abort();
5268
+ await done;
5269
+ return boundaryFromSnapshot(await this.owned.snapshotRun(runId));
5270
+ }
5234
5271
  const snapshot = await this.owned.snapshotRun(runId);
5235
5272
  await this.owned.cancelRun({ runId, expectedRevision: snapshot.revision, ...reason === void 0 ? {} : { reason } });
5236
5273
  const active = this.executions.get(runId);
@@ -5252,8 +5289,12 @@ var AgentRuntime = class _AgentRuntime {
5252
5289
  if (execution) this.transientEvents.clear(next.id, execution.executionId);
5253
5290
  this.activeAgents.delete(next.agentId);
5254
5291
  this.executions.delete(next.id);
5292
+ this.cancellationRequested.delete(next.id);
5293
+ this.cancellationReasons.delete(next.id);
5294
+ this.executionDone.delete(next.id);
5255
5295
  void this.pump();
5256
5296
  });
5297
+ this.executionDone.set(next.id, task);
5257
5298
  void task;
5258
5299
  }
5259
5300
  } catch (error) {
@@ -5311,6 +5352,7 @@ var AgentRuntime = class _AgentRuntime {
5311
5352
  executionRevision = claim.run.revision;
5312
5353
  const controller = new AbortController();
5313
5354
  this.executions.set(run.id, { controller, executionId });
5355
+ if (this.cancellationRequested.has(run.id)) controller.abort();
5314
5356
  const config = resolvedConfig;
5315
5357
  const assembly = assembleRegisteredExtensions(config, this.resources.extensionRunner);
5316
5358
  let toolQueue = Promise.resolve();
@@ -5399,6 +5441,18 @@ var AgentRuntime = class _AgentRuntime {
5399
5441
  }
5400
5442
  }
5401
5443
  });
5444
+ if (this.cancellationRequested.has(run.id) || controller.signal.aborted) {
5445
+ const output = latestAssistant(run, result.messages, claim.history.length, true);
5446
+ const reason = this.cancellationReasons.get(run.id) ?? "Agent run stopped.";
5447
+ await this.owned.cancelRun({
5448
+ runId: run.id,
5449
+ expectedRevision: executionRevision,
5450
+ reason,
5451
+ ...output && hasVisibleAssistantText(output) ? { output } : {}
5452
+ });
5453
+ this.transientEvents.clear(run.id, claim.execution.executionId);
5454
+ return;
5455
+ }
5402
5456
  if (result.type === "input_required") {
5403
5457
  const prompt = promptMessage(run, result.request.prompt, result.messages.length);
5404
5458
  await this.owned.commitInputRequired({ runId: run.id, execution: claim.execution, expectedRevision: executionRevision, requestId: createId("input"), phase: result.request.phase, prompt, checkpoint: result.checkpoint });
@@ -5417,6 +5471,14 @@ var AgentRuntime = class _AgentRuntime {
5417
5471
  } catch (error) {
5418
5472
  if (!claim && error instanceof RuntimeError && ["run_state_conflict", "runtime_ownership_lost", "run_not_found"].includes(error.code)) return;
5419
5473
  if (claim) {
5474
+ if (this.cancellationRequested.has(run.id)) {
5475
+ await this.owned.cancelRun({
5476
+ runId: run.id,
5477
+ expectedRevision: executionRevision,
5478
+ reason: this.cancellationReasons.get(run.id) ?? "Agent run stopped."
5479
+ }).catch(() => void 0);
5480
+ return;
5481
+ }
5420
5482
  await this.owned.commitOutcome({
5421
5483
  runId: run.id,
5422
5484
  execution: claim.execution,
@@ -5663,10 +5725,13 @@ var DurableRun = class {
5663
5725
  function promptMessage(run, prompt, sequence) {
5664
5726
  return { id: createId("msg"), agentId: run.agentId, runId: run.id, role: "assistant", content: prompt, sequenceWithinRun: sequence, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
5665
5727
  }
5666
- function latestAssistant(run, messages, sequence) {
5728
+ function latestAssistant(run, messages, sequence, interrupted = false) {
5667
5729
  const message = [...messages].reverse().find((candidate) => candidate.role === "assistant");
5668
5730
  if (!message) return void 0;
5669
- return projectAssistantMessage(message, run.agentId, run.id, sequence);
5731
+ return projectAssistantMessage(message, run.agentId, run.id, sequence, { interrupted });
5732
+ }
5733
+ function hasVisibleAssistantText(message) {
5734
+ return typeof message.content === "string" ? message.content.length > 0 : message.content.some((part) => part.type === "text" && part.text.length > 0);
5670
5735
  }
5671
5736
  function durableOutcome(outcome) {
5672
5737
  return {
@@ -5890,6 +5955,43 @@ var InMemoryStore = class _InMemoryStore {
5890
5955
  this.writeReceipt(scope, payload, agent);
5891
5956
  return clone(agent);
5892
5957
  }
5958
+ deleteAgent(lease, input) {
5959
+ this.assertOwner(lease);
5960
+ if (input.confirmation !== "conversation-delete-v1") {
5961
+ throw new TypeError("Agent deletion requires the conversation deletion confirmation token");
5962
+ }
5963
+ const agent = this.requireAgent(input.agentId);
5964
+ const runs = [...this.runs.values()].filter((run) => run.agentId === agent.id);
5965
+ const expected = new Set(input.expectedRunIds.map(String));
5966
+ const actual = new Set(runs.map((run) => String(run.id)));
5967
+ if (expected.size !== actual.size || [...actual].some((runId) => !expected.has(runId))) {
5968
+ throw new RuntimeError("run_state_conflict", {
5969
+ runId: runs[0]?.id ?? input.expectedRunIds[0] ?? "unknown",
5970
+ expected: [],
5971
+ actual: "cancelled"
5972
+ });
5973
+ }
5974
+ const runIds = new Set(runs.map((run) => run.id));
5975
+ this.agents.delete(agent.id);
5976
+ this.nextAgentSequence.delete(agent.id);
5977
+ this.nextReadySequence.delete(agent.id);
5978
+ for (const run of runs) this.runs.delete(run.id);
5979
+ for (const [messageId, message] of this.messages) {
5980
+ if (runIds.has(message.runId)) this.messages.delete(messageId);
5981
+ }
5982
+ for (const [toolCallId, toolCall] of this.toolCalls) {
5983
+ if (runIds.has(toolCall.runId)) this.toolCalls.delete(toolCallId);
5984
+ }
5985
+ for (let index = this.events.length - 1; index >= 0; index -= 1) {
5986
+ if (this.events[index].agentId === agent.id) this.events.splice(index, 1);
5987
+ }
5988
+ for (const [key] of this.idempotency) {
5989
+ if (key.includes(String(agent.id)) || [...runIds].some((runId) => key.includes(String(runId)))) this.idempotency.delete(key);
5990
+ }
5991
+ for (const [key] of this.operationReceipts) {
5992
+ if (key.includes(String(agent.id)) || [...runIds].some((runId) => key.includes(String(runId)))) this.operationReceipts.delete(key);
5993
+ }
5994
+ }
5893
5995
  createRun(lease, input) {
5894
5996
  this.assertOwner(lease);
5895
5997
  this.requireAgent(input.agentId);
@@ -6258,11 +6360,16 @@ var InMemoryStore = class _InMemoryStore {
6258
6360
  cancelRun(lease, input) {
6259
6361
  this.assertOwner(lease);
6260
6362
  const operationKey = `cancel:${input.runId}:${input.expectedRevision ?? "current"}`;
6261
- const operationPayload = canonicalJson(input.reason ?? null);
6363
+ const operationPayload = canonicalJson([input.reason ?? null, input.output ?? null]);
6262
6364
  const replay = this.replayOperation(operationKey, operationPayload);
6263
6365
  if (replay) return clone(replay);
6264
6366
  const run = this.requireRun(input.runId);
6265
6367
  if (input.expectedRevision !== void 0) this.assertRevision(run, input.expectedRevision);
6368
+ if (input.output !== void 0) {
6369
+ if (!isAssistantMessage(input.output) || input.output.agentId !== run.agentId || input.output.runId !== run.id || input.output.interrupted !== true) {
6370
+ throw new TypeError("cancelled output must be an interrupted AssistantMessage from this Run");
6371
+ }
6372
+ }
6266
6373
  if (["completed", "failed", "cancelled"].includes(run.state)) {
6267
6374
  const result2 = clone(run);
6268
6375
  if (input.expectedRevision !== void 0) this.writeOperationReceipt(operationKey, operationPayload, result2);
@@ -6272,6 +6379,11 @@ var InMemoryStore = class _InMemoryStore {
6272
6379
  const activeToolCalls = run.execution ? [...this.toolCalls.values()].filter((toolCall) => toolCall.runId === run.id && toolCall.executionId === run.execution?.executionId && (toolCall.state === "pending" || toolCall.state === "running")) : [];
6273
6380
  const indeterminateToolCallIds = activeToolCalls.filter((toolCall) => toolCall.state === "running").map((toolCall) => toolCall.id);
6274
6381
  for (const toolCall of activeToolCalls) this.interruptToolCall(run, toolCall, input.reason ?? "The Run was cancelled.");
6382
+ if (input.output && (typeof input.output.content === "string" ? input.output.content.length > 0 : input.output.content.length > 0)) {
6383
+ const output = { ...clone(input.output), sequenceWithinRun: this.nextMessageSequence(run.id) };
6384
+ this.messages.set(output.id, output);
6385
+ this.appendMessage(run, output);
6386
+ }
6275
6387
  if (indeterminateToolCallIds.length > 0) {
6276
6388
  const failure = {
6277
6389
  code: "tool_indeterminate",
@@ -6535,6 +6647,9 @@ var MemoryOwnedStore = class {
6535
6647
  async updateAgentConfigToken(input) {
6536
6648
  return this.store.updateAgentConfigToken(this.lease, input);
6537
6649
  }
6650
+ async deleteAgent(input) {
6651
+ this.store.deleteAgent(this.lease, input);
6652
+ }
6538
6653
  async createRun(input) {
6539
6654
  return this.store.createRun(this.lease, input);
6540
6655
  }
@@ -6791,6 +6906,9 @@ var SqliteStore = class {
6791
6906
  async updateAgentConfigToken(lease, input) {
6792
6907
  return this.invoke(lease, (store, current) => store.updateAgentConfigToken(current, input));
6793
6908
  }
6909
+ async deleteAgent(lease, input) {
6910
+ return this.invoke(lease, (store, current) => store.deleteAgent(current, input));
6911
+ }
6794
6912
  async createRun(lease, input) {
6795
6913
  return this.invoke(lease, (store, current) => store.createRun(current, input));
6796
6914
  }
@@ -6852,9 +6970,9 @@ var SqliteStore = class {
6852
6970
  if (after === waterline) return [];
6853
6971
  }
6854
6972
  const rows = this.database.query(
6855
- "SELECT payload_json FROM run_events ORDER BY sequence LIMIT -1 OFFSET ?"
6856
- ).all(after);
6857
- return rows.map((row) => JSON.parse(row.payload_json));
6973
+ "SELECT payload_json FROM run_events ORDER BY sequence"
6974
+ ).all();
6975
+ return rows.map((row) => JSON.parse(row.payload_json)).filter((event) => parseEventCursor(lease, event.cursor) > after);
6858
6976
  });
6859
6977
  }
6860
6978
  async openConsumer(lease, consumerId) {
@@ -7078,6 +7196,9 @@ var SqliteOwnedStore = class {
7078
7196
  updateAgentConfigToken(input) {
7079
7197
  return this.store.updateAgentConfigToken(this.lease, input);
7080
7198
  }
7199
+ deleteAgent(input) {
7200
+ return this.store.deleteAgent(this.lease, input);
7201
+ }
7081
7202
  createRun(input) {
7082
7203
  return this.store.createRun(this.lease, input);
7083
7204
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rowan-agent/agent",
3
- "version": "0.9.2",
3
+ "version": "0.9.3",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",