@rowan-agent/agent 0.9.12 → 0.9.14

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
@@ -1380,7 +1380,7 @@ type AgentConfiguration = Readonly<{
1380
1380
  }>;
1381
1381
  resourceView: ResourceView;
1382
1382
  contexts?: readonly ContextCandidate[];
1383
- /** Trusted Host-owned Contexts appended after Definition selection. */
1383
+ /** Trusted Host-owned Contexts rendered as durable user messages. */
1384
1384
  additionalContexts?: readonly ContextCandidate[];
1385
1385
  cwd?: string;
1386
1386
  maxAttempts?: number;
@@ -1398,6 +1398,7 @@ type ConfigurationSnapshot = Readonly<{
1398
1398
  refs: Readonly<Record<ResourceKind, readonly ResourceRef[]>>;
1399
1399
  }>;
1400
1400
  contexts: readonly ContextCandidate[];
1401
+ additionalContexts: readonly ContextCandidate[];
1401
1402
  resourceView: ResourceView;
1402
1403
  cwd?: string;
1403
1404
  maxAttempts?: number;
@@ -1506,6 +1507,8 @@ type AgentConfig = Readonly<{
1506
1507
  identity: string;
1507
1508
  definition: AgentDefinition;
1508
1509
  resources: AgentResources;
1510
+ /** Host-owned Contexts are rendered as durable user messages for a Run. */
1511
+ additionalContexts?: readonly ContextCandidate[];
1509
1512
  cwd?: string;
1510
1513
  maxAttempts?: number;
1511
1514
  beforeToolCall?: BeforeToolCall;
@@ -1770,6 +1773,7 @@ interface OwnedStore {
1770
1773
  executionId?: ExecutionId;
1771
1774
  messageId?: MessageId;
1772
1775
  configToken?: ConfigToken;
1776
+ inputContext?: UserInput;
1773
1777
  }): Promise<RunClaim>;
1774
1778
  failQueuedRun(input: {
1775
1779
  runId: RunId;
@@ -2184,6 +2188,7 @@ declare class InMemoryStore implements DurableStore {
2184
2188
  executionId?: ExecutionId;
2185
2189
  messageId?: MessageId;
2186
2190
  configToken?: ConfigToken;
2191
+ inputContext?: UserInput;
2187
2192
  }): RunClaim;
2188
2193
  failQueuedRun(lease: OwnerLease, input: {
2189
2194
  runId: RunId;
@@ -2367,6 +2372,7 @@ declare class SqliteStore implements DurableStore {
2367
2372
  executionId?: ExecutionId;
2368
2373
  messageId?: MessageId;
2369
2374
  configToken?: ConfigToken;
2375
+ inputContext?: UserInput;
2370
2376
  }): Promise<RunClaim>;
2371
2377
  failQueuedRun(lease: OwnerLease, input: {
2372
2378
  runId: RunId;
package/dist/index.js CHANGED
@@ -46,6 +46,9 @@ function buildContextDescription(contexts) {
46
46
  lines.push("</agent_context>");
47
47
  return lines.join("\n");
48
48
  }
49
+ function buildAdditionalContextMessage(contexts) {
50
+ return buildContextDescription(contexts);
51
+ }
49
52
  function formatResourceOutput(resource) {
50
53
  const parts = [`<${resource.type} name="${escapeXml(resource.name)}" location="${escapeXml(resource.location)}">`];
51
54
  if (resource.baseDir) {
@@ -2123,7 +2126,7 @@ function findLatestUserInputMessage(messages) {
2123
2126
  for (let index = messages.length - 1; index >= 0; index -= 1) {
2124
2127
  const message = messages[index];
2125
2128
  const kind = message.metadata?.kind;
2126
- if (message.role === "user" && kind !== "phase_prompt" && kind !== "phase_input") {
2129
+ if (message.role === "user" && kind !== "phase_prompt" && kind !== "phase_input" && kind !== "host_context") {
2127
2130
  return message;
2128
2131
  }
2129
2132
  }
@@ -3276,6 +3279,7 @@ function isContinuation(value) {
3276
3279
  }
3277
3280
 
3278
3281
  // src/runtime/contracts.ts
3282
+ var HOST_CONTEXT_MESSAGE_KIND = "host_context";
3279
3283
  var THINKING_LEVELS = [
3280
3284
  "off",
3281
3285
  "minimal",
@@ -3298,7 +3302,7 @@ function thinkingLevelFromMessages(messages) {
3298
3302
  if (message?.role !== "user") continue;
3299
3303
  const metadata = message.metadata;
3300
3304
  const kind = isRecord3(metadata) && typeof metadata.kind === "string" ? metadata.kind : void 0;
3301
- if (kind === "phase_prompt" || kind === "phase_input") continue;
3305
+ if (kind === "phase_prompt" || kind === "phase_input" || kind === HOST_CONTEXT_MESSAGE_KIND) continue;
3302
3306
  return thinkingLevelFromMetadata(metadata);
3303
3307
  }
3304
3308
  return void 0;
@@ -3352,6 +3356,9 @@ function normalizeUserInput(input) {
3352
3356
  assertJsonValue(normalized, "input");
3353
3357
  return normalized;
3354
3358
  }
3359
+ function canonicalUserInput(input) {
3360
+ return canonicalJson(normalizeUserInput(input));
3361
+ }
3355
3362
  function isAssistantMessage(value) {
3356
3363
  return isRecord3(value) && hasOnlyKeys(value, ["id", "agentId", "runId", "messageRevision", "role", "content", "metadata", "sequenceWithinRun", "createdAt", "interrupted"]) && typeof value.id === "string" && typeof value.agentId === "string" && typeof value.runId === "string" && value.role === "assistant" && (value.messageRevision === void 0 || Number.isInteger(value.messageRevision) && value.messageRevision >= 0) && 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");
3357
3364
  }
@@ -3378,6 +3385,12 @@ function assertAgentConfig(config) {
3378
3385
  names.add(context.name);
3379
3386
  assertJsonValue(context.value, `Context candidate "${context.name}" value`);
3380
3387
  }
3388
+ for (const context of config.additionalContexts ?? []) {
3389
+ if (typeof context.name !== "string" || context.name.trim() === "") {
3390
+ throw new TypeError("Additional Context candidate name must be non-empty");
3391
+ }
3392
+ assertJsonValue(context.value, `Additional Context candidate "${context.name}" value`);
3393
+ }
3381
3394
  }
3382
3395
  function assertAgentConfigRequest(config) {
3383
3396
  if (!isAgentConfiguration(config)) {
@@ -3545,7 +3558,17 @@ function snapshotConfig(config) {
3545
3558
  })))
3546
3559
  } : {}
3547
3560
  });
3548
- return Object.freeze({ ...config, definition, resources });
3561
+ return Object.freeze({
3562
+ ...config,
3563
+ definition,
3564
+ resources,
3565
+ ...config.additionalContexts ? {
3566
+ additionalContexts: Object.freeze(config.additionalContexts.map((context) => Object.freeze({
3567
+ name: context.name,
3568
+ value: snapshotJsonValue(context.value)
3569
+ })))
3570
+ } : {}
3571
+ });
3549
3572
  }
3550
3573
  function snapshotPhaseRegistry(registry) {
3551
3574
  const phases = /* @__PURE__ */ new Map();
@@ -5512,11 +5535,8 @@ function resolveConfigurationSnapshot(registry, input) {
5512
5535
  }
5513
5536
  const layer = input.definition.layer;
5514
5537
  const selectedContexts = selectNamedResources(input.contexts ?? [], base.contexts, "Context");
5515
- const contexts = appendAdditionalContexts(selectedContexts, input.additionalContexts ?? []);
5516
- const definition = addAdditionalContextNames(
5517
- applyDefinitionLayer(base, layer),
5518
- contexts.slice(selectedContexts.length).map(({ name }) => name)
5519
- );
5538
+ const additionalContexts = deduplicateContexts(input.additionalContexts ?? [], selectedContexts);
5539
+ const definition = applyDefinitionLayer(base, layer);
5520
5540
  const tools = selectNamedResources(resolved.tools, definition.tools, "Tool");
5521
5541
  const skills = mergeSkills(
5522
5542
  selectNamedResources(resolved.skills, definition.skills, "Skill"),
@@ -5538,7 +5558,8 @@ function resolveConfigurationSnapshot(registry, input) {
5538
5558
  phase: selectedRefs(resolved.refs.phase, [...phases?.phases.keys() ?? []])
5539
5559
  }
5540
5560
  },
5541
- contexts,
5561
+ contexts: selectedContexts,
5562
+ additionalContexts,
5542
5563
  resourceView: input.resourceView,
5543
5564
  ...input.cwd === void 0 ? {} : { cwd: input.cwd },
5544
5565
  ...input.maxAttempts === void 0 ? {} : { maxAttempts: input.maxAttempts },
@@ -5563,6 +5584,7 @@ function materializeConfigurationSnapshot(snapshot) {
5563
5584
  ],
5564
5585
  resourceRevisions: snapshot.resources.revisions
5565
5586
  },
5587
+ ...snapshot.additionalContexts.length > 0 ? { additionalContexts: snapshot.additionalContexts } : {},
5566
5588
  ...snapshot.cwd === void 0 ? {} : { cwd: snapshot.cwd },
5567
5589
  ...snapshot.maxAttempts === void 0 ? {} : { maxAttempts: snapshot.maxAttempts },
5568
5590
  ..."stream" in snapshot && snapshot.stream ? { model: snapshot.model, stream: snapshot.stream } : { model: snapshot.model }
@@ -5580,9 +5602,9 @@ function applyDefinitionLayer(base, layer) {
5580
5602
  ...layer.phases === void 0 ? {} : { phases: intersectPhaseSelection(base.phases, layer.phases) }
5581
5603
  };
5582
5604
  }
5583
- function appendAdditionalContexts(selected, additional) {
5584
- const contexts = [...selected];
5585
- const seen = new Set(contexts.map(({ name }) => name));
5605
+ function deduplicateContexts(additional, existing = []) {
5606
+ const contexts = [];
5607
+ const seen = new Set(existing.map(({ name }) => name));
5586
5608
  for (const context of additional) {
5587
5609
  if (seen.has(context.name)) continue;
5588
5610
  seen.add(context.name);
@@ -5590,12 +5612,6 @@ function appendAdditionalContexts(selected, additional) {
5590
5612
  }
5591
5613
  return contexts;
5592
5614
  }
5593
- function addAdditionalContextNames(definition, names) {
5594
- if (definition.contexts === void 0 || names.length === 0) return definition;
5595
- const existing = new Set(definition.contexts);
5596
- const additions = names.filter((name) => !existing.has(name));
5597
- return additions.length === 0 ? definition : { ...definition, contexts: [...definition.contexts, ...additions] };
5598
- }
5599
5615
  function intersectNames(parent, layer, kind) {
5600
5616
  if (parent === void 0) return [...layer];
5601
5617
  const parentNames = new Set(parent);
@@ -6033,7 +6049,13 @@ var AgentRuntime = class _AgentRuntime {
6033
6049
  }
6034
6050
  }
6035
6051
  const executionId = createId("exec");
6036
- claim = await this.owned.claimRun({ runId: run.id, expectedRevision: run.revision, executionId, configToken: token });
6052
+ claim = await this.owned.claimRun({
6053
+ runId: run.id,
6054
+ expectedRevision: run.revision,
6055
+ executionId,
6056
+ configToken: token,
6057
+ ...controlKind ? {} : { inputContext: additionalContextInput(config) }
6058
+ });
6037
6059
  executionRevision = claim.run.revision;
6038
6060
  const controller = new AbortController();
6039
6061
  this.executions.set(run.id, { controller, executionId });
@@ -6502,6 +6524,12 @@ var AgentRuntime = class _AgentRuntime {
6502
6524
  this.heartbeat.unref?.();
6503
6525
  }
6504
6526
  };
6527
+ function additionalContextInput(config) {
6528
+ const contexts = config.additionalContexts ?? [];
6529
+ if (contexts.length === 0) return void 0;
6530
+ const content = buildAdditionalContextMessage(contexts);
6531
+ return content.length === 0 ? void 0 : { content, metadata: { kind: HOST_CONTEXT_MESSAGE_KIND } };
6532
+ }
6505
6533
  var DurableRun = class {
6506
6534
  constructor(runtime, id) {
6507
6535
  this.runtime = runtime;
@@ -7050,7 +7078,14 @@ var InMemoryStore = class _InMemoryStore {
7050
7078
  this.assertOwner(lease);
7051
7079
  const executionId = input.executionId ?? createId("exec");
7052
7080
  const operationKey = `claim:${executionId}`;
7053
- const operationPayload = canonicalJson([input.runId, input.expectedRevision, input.messageId ?? null, input.configToken ?? null]);
7081
+ const inputContext = input.inputContext === void 0 ? void 0 : normalizeUserInput(input.inputContext);
7082
+ const operationPayload = canonicalJson([
7083
+ input.runId,
7084
+ input.expectedRevision,
7085
+ input.messageId ?? null,
7086
+ input.configToken ?? null,
7087
+ inputContext ?? null
7088
+ ]);
7054
7089
  const replay = this.replayOperation(operationKey, operationPayload);
7055
7090
  if (replay) return clone(replay);
7056
7091
  const run = this.requireRun(input.runId);
@@ -7066,10 +7101,25 @@ var InMemoryStore = class _InMemoryStore {
7066
7101
  throw new RuntimeError("run_state_conflict", { runId: run.id, expected: ["queued"], actual: run.state });
7067
7102
  }
7068
7103
  if (!run.pinnedConfigToken && input.configToken) run.pinnedConfigToken = input.configToken;
7069
- let message;
7104
+ const committedMessages = [];
7105
+ const existingMessages = this.messagesForRun(run.id);
7106
+ if (inputContext && hasUserInput(inputContext) && !isControlRun(run) && !existingMessages.some((message) => message.role === "user" && message.metadata?.kind === HOST_CONTEXT_MESSAGE_KIND && canonicalUserInput({ content: message.content, metadata: message.metadata }) === canonicalUserInput(inputContext))) {
7107
+ const contextMessage = {
7108
+ id: createId("msg"),
7109
+ agentId: run.agentId,
7110
+ runId: run.id,
7111
+ role: "user",
7112
+ content: userInputContent(inputContext),
7113
+ ...userInputMetadata(inputContext) ? { metadata: clone(userInputMetadata(inputContext)) } : {},
7114
+ sequenceWithinRun: this.nextMessageSequence(run.id),
7115
+ createdAt: createTimestamp()
7116
+ };
7117
+ this.messages.set(contextMessage.id, contextMessage);
7118
+ committedMessages.push(contextMessage);
7119
+ }
7070
7120
  if (!run.checkpoint && !run.initialMessageId && (!isControlRun(run) || hasUserInput(run.input))) {
7071
7121
  const userInput = normalizeUserInput(run.input);
7072
- message = {
7122
+ const message = {
7073
7123
  id: input.messageId ?? createId("msg"),
7074
7124
  agentId: run.agentId,
7075
7125
  runId: run.id,
@@ -7080,6 +7130,7 @@ var InMemoryStore = class _InMemoryStore {
7080
7130
  createdAt: createTimestamp()
7081
7131
  };
7082
7132
  this.messages.set(message.id, message);
7133
+ committedMessages.push(message);
7083
7134
  }
7084
7135
  const execution = {
7085
7136
  runId: run.id,
@@ -7090,7 +7141,7 @@ var InMemoryStore = class _InMemoryStore {
7090
7141
  run.execution = execution;
7091
7142
  run.revision += 1;
7092
7143
  run.updatedAt = createTimestamp();
7093
- if (message) this.appendMessage(run, message);
7144
+ for (const message of committedMessages) this.appendMessage(run, message);
7094
7145
  this.appendTransition(run, "queued", "running");
7095
7146
  const result = { run: clone(run), execution: clone(execution), history: this.activeHistory(run.agentId, run.agentSequence) };
7096
7147
  this.writeOperationReceipt(operationKey, operationPayload, result);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rowan-agent/agent",
3
- "version": "0.9.12",
3
+ "version": "0.9.14",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -23,7 +23,7 @@
23
23
  "build": "tsup && bun scripts/check-public-interface.ts"
24
24
  },
25
25
  "dependencies": {
26
- "@rowan-agent/models": "0.6.6",
26
+ "@rowan-agent/models": "0.6.7",
27
27
  "jiti": "2.7.0",
28
28
  "typebox": "^1.0.0",
29
29
  "yaml": "^2.7.0"