@rowan-agent/agent 0.9.16 → 0.9.18

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
@@ -980,6 +980,8 @@ interface PhaseContext {
980
980
  /** Text to append after the system prompt */
981
981
  appendSystemPrompt?: string;
982
982
  }
983
+ /** JSON-safe structural defaults declared by a Phase's `input` mapping. */
984
+ type PhaseInput = Readonly<Record<string, JsonValue>>;
983
985
  /** JSON-safe option metadata exposed by a Phase to a host Settings surface. */
984
986
  interface PhaseSettingsOption {
985
987
  value: string;
@@ -1073,8 +1075,8 @@ interface Phase {
1073
1075
  skills?: Skill[];
1074
1076
  /** Forced next phase */
1075
1077
  target?: string;
1076
- /** Expected input fields (key → description) */
1077
- input?: Record<string, string>;
1078
+ /** Structural JSON-safe input defaults */
1079
+ input?: PhaseInput;
1078
1080
  /** If true, phase gets a fresh context when executed in parallel */
1079
1081
  isolated?: boolean;
1080
1082
  /** Path to PHASE.md file */
@@ -1598,6 +1600,7 @@ type InvocationCatalogEntry = Readonly<{
1598
1600
  kind: "phase" | "skill";
1599
1601
  name: string;
1600
1602
  description: string;
1603
+ input?: Phase["input"];
1601
1604
  core?: boolean;
1602
1605
  disableAutoInvocation: boolean;
1603
1606
  disableImplicitInvocation: boolean;
@@ -1873,7 +1876,6 @@ interface OwnedStore {
1873
1876
  executionId?: ExecutionId;
1874
1877
  messageId?: MessageId;
1875
1878
  configToken?: ConfigToken;
1876
- inputContext?: UserInput;
1877
1879
  }): Promise<RunClaim>;
1878
1880
  failQueuedRun(input: {
1879
1881
  runId: RunId;
@@ -2288,7 +2290,6 @@ declare class InMemoryStore implements DurableStore {
2288
2290
  executionId?: ExecutionId;
2289
2291
  messageId?: MessageId;
2290
2292
  configToken?: ConfigToken;
2291
- inputContext?: UserInput;
2292
2293
  }): RunClaim;
2293
2294
  failQueuedRun(lease: OwnerLease, input: {
2294
2295
  runId: RunId;
@@ -2472,7 +2473,6 @@ declare class SqliteStore implements DurableStore {
2472
2473
  executionId?: ExecutionId;
2473
2474
  messageId?: MessageId;
2474
2475
  configToken?: ConfigToken;
2475
- inputContext?: UserInput;
2476
2476
  }): Promise<RunClaim>;
2477
2477
  failQueuedRun(lease: OwnerLease, input: {
2478
2478
  runId: RunId;
@@ -2785,4 +2785,4 @@ declare function parseFrontmatter<T = Record<string, unknown>>(raw: string): Fro
2785
2785
 
2786
2786
  declare function createCoreTools(input?: CoreToolContext): Tool[];
2787
2787
 
2788
- export { type AfterToolCall, type AgentConfig, type AgentConfigRequest, type AgentConfiguration, type AgentDefinition, type AgentId, type AgentListCursor, type AgentRecord, type AgentResources, type AgentRun, AgentRuntime, type AgentRuntimeOptions, type AgentSummary, type AnyRuntimeError, type AssistantContent, type AssistantMessage, type BeforeToolCall, COMPACT_PHASE_ID, type ConfigProvider, type ConfigPutResult, type ConfigResolution, type ConfigToken, type ConfigurationSnapshot, type ContextCandidate, type ContextCompactionRecord, type ContextStatus, type CoreToolContext, DEFAULT_PHASE_ID, type DefinitionLayer, type DurableConsumer, type DurableRunEvent, type DurableStore, type DurableToolResult, type EventCursor, type EventId, type ExecutionCheckpoint, type ExecutionId, type ExecutionToken, type ExtensionAPI, type ExtensionActivationError, type ExtensionActivationResult, type ExtensionContribution, type ExtensionDisposer, type ExtensionFactory, type ExtensionFactoryResult, ExtensionLifetimeError, type ExtensionLifetimeErrorCode, type ExtensionLoadInput, type FrontmatterResult, type HistorySeed, type HookEvent, type HookEventType, type HookHandler, InMemoryConfigProvider, InMemoryStore, type InputRequest, type InputRequestId, type InputRequiredCommit, type InvocationCatalogEntry, type InvocationSource, type JsonObject, type JsonPrimitive, type JsonValue, type LoadExtensionsResult, type LoadInput, type LoadResult, type LoadedExtension, type Message, type MessageBase, type MessageCommitted, type MessageContent, type MessageDelta, type MessageId, type MessageRevised, type MessageRevisionResult, type Metadata, type OpaqueId, type Outcome, type OwnerLease, type OwnerToken, type Page, type Phase, type PhaseContext, type PhaseContribution, type PhaseExecution, type PhaseExecutionIdentity, type PhaseInteraction, PhaseInteractionBoundary, PhaseInteractionCancelledError, type PhaseInteractionDriver, type PhaseInteractionKind, type PhaseInteractionState, type PhaseInteractionStatus, type PhaseInvocation, type PhaseMessageManager, type PhaseOutput, type PhaseRegistry, type PhaseRegistrySelection, type PhaseSettingsBadge, type PhaseSettingsContext, type PhaseSettingsControl, type PhaseSettingsDefinition, type PhaseSettingsItem, type PhaseSettingsOption, type PhaseSettingsProvider, type PhaseSettingsSection, type PhaseState, type PhaseStatus, type PhaseStatusState, type ResolvedResourceView, type ResourceDiagnostic, type ResourceKind, type ResourceRef, ResourceRegistry, ResourceRegistryError, type ResourceRegistryErrorCode, type ResourceSourceId, type ResourceView, type RetentionResult, type RunBoundary, type RunClaim, type RunEvent, type RunFailure, type RunId, type RunListCursor, type RunRecord, type RunSnapshot, type RunState, type RunStateChanged, type RunSummary, RuntimeBootstrapRegistry, RuntimeError, type RuntimeErrorCode, type RuntimeErrorDetails, RuntimeExtensionLifetime, STOP_PHASE_ID, type Skill, SqliteStore, type TextContent, type ThinkingContent, type ThinkingDelta, type Tool, type ToolCallId, type ToolCallSnapshot, type ToolCallState, type ToolContribution, type ToolDefinition, type ToolExecutionResult$1 as ToolExecutionResult, type ToolInvocationContext, type ToolMessage, type ToolMessageContent, type ToolProgress, type ToolResultContent, type ToolStateChanged, type ToolUseContent, type UserContent, type UserInput, type UserMessage, brandConfigToken, createCompactPhase, createCorePhases, createCoreTools, createDefaultPhase, createStopPhase, isRuntimeError, loadExtensionsFromPath as loadExtensions, loadPhase, loadPhaseSettings, loadPhases, loadSkill, loadSkills, materializeConfigurationSnapshot, parseAgentDefinition, parseFrontmatter, resolveConfigurationSnapshot };
2788
+ export { type AfterToolCall, type AgentConfig, type AgentConfigRequest, type AgentConfiguration, type AgentDefinition, type AgentId, type AgentListCursor, type AgentRecord, type AgentResources, type AgentRun, AgentRuntime, type AgentRuntimeOptions, type AgentSummary, type AnyRuntimeError, type AssistantContent, type AssistantMessage, type BeforeToolCall, COMPACT_PHASE_ID, type ConfigProvider, type ConfigPutResult, type ConfigResolution, type ConfigToken, type ConfigurationSnapshot, type ContextCandidate, type ContextCompactionRecord, type ContextStatus, type CoreToolContext, DEFAULT_PHASE_ID, type DefinitionLayer, type DurableConsumer, type DurableRunEvent, type DurableStore, type DurableToolResult, type EventCursor, type EventId, type ExecutionCheckpoint, type ExecutionId, type ExecutionToken, type ExtensionAPI, type ExtensionActivationError, type ExtensionActivationResult, type ExtensionContribution, type ExtensionDisposer, type ExtensionFactory, type ExtensionFactoryResult, ExtensionLifetimeError, type ExtensionLifetimeErrorCode, type ExtensionLoadInput, type FrontmatterResult, type HistorySeed, type HookEvent, type HookEventType, type HookHandler, InMemoryConfigProvider, InMemoryStore, type InputRequest, type InputRequestId, type InputRequiredCommit, type InvocationCatalogEntry, type InvocationSource, type JsonObject, type JsonPrimitive, type JsonValue, type LoadExtensionsResult, type LoadInput, type LoadResult, type LoadedExtension, type Message, type MessageBase, type MessageCommitted, type MessageContent, type MessageDelta, type MessageId, type MessageRevised, type MessageRevisionResult, type Metadata, type OpaqueId, type Outcome, type OwnerLease, type OwnerToken, type Page, type Phase, type PhaseContext, type PhaseContribution, type PhaseExecution, type PhaseExecutionIdentity, type PhaseInput, type PhaseInteraction, PhaseInteractionBoundary, PhaseInteractionCancelledError, type PhaseInteractionDriver, type PhaseInteractionKind, type PhaseInteractionState, type PhaseInteractionStatus, type PhaseInvocation, type PhaseMessageManager, type PhaseOutput, type PhaseRegistry, type PhaseRegistrySelection, type PhaseSettingsBadge, type PhaseSettingsContext, type PhaseSettingsControl, type PhaseSettingsDefinition, type PhaseSettingsItem, type PhaseSettingsOption, type PhaseSettingsProvider, type PhaseSettingsSection, type PhaseState, type PhaseStatus, type PhaseStatusState, type ResolvedResourceView, type ResourceDiagnostic, type ResourceKind, type ResourceRef, ResourceRegistry, ResourceRegistryError, type ResourceRegistryErrorCode, type ResourceSourceId, type ResourceView, type RetentionResult, type RunBoundary, type RunClaim, type RunEvent, type RunFailure, type RunId, type RunListCursor, type RunRecord, type RunSnapshot, type RunState, type RunStateChanged, type RunSummary, RuntimeBootstrapRegistry, RuntimeError, type RuntimeErrorCode, type RuntimeErrorDetails, RuntimeExtensionLifetime, STOP_PHASE_ID, type Skill, SqliteStore, type TextContent, type ThinkingContent, type ThinkingDelta, type Tool, type ToolCallId, type ToolCallSnapshot, type ToolCallState, type ToolContribution, type ToolDefinition, type ToolExecutionResult$1 as ToolExecutionResult, type ToolInvocationContext, type ToolMessage, type ToolMessageContent, type ToolProgress, type ToolResultContent, type ToolStateChanged, type ToolUseContent, type UserContent, type UserInput, type UserMessage, brandConfigToken, createCompactPhase, createCorePhases, createCoreTools, createDefaultPhase, createStopPhase, isRuntimeError, loadExtensionsFromPath as loadExtensions, loadPhase, loadPhaseSettings, loadPhases, loadSkill, loadSkills, materializeConfigurationSnapshot, parseAgentDefinition, parseFrontmatter, resolveConfigurationSnapshot };
package/dist/index.js CHANGED
@@ -2,8 +2,8 @@
2
2
  import { chmod, mkdir, readFile as readFile2, stat, writeFile } from "fs/promises";
3
3
  import { basename as basename2, dirname as dirname2, relative as relative2, resolve as resolve2 } from "path";
4
4
  import { tmpdir } from "os";
5
- import Type2 from "typebox";
6
- import Schema from "typebox/schema";
5
+ import Type3 from "typebox";
6
+ import Schema2 from "typebox/schema";
7
7
 
8
8
  // src/harness/context/resource-formatter.ts
9
9
  function escapeXml(value) {
@@ -46,9 +46,6 @@ 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
- }
52
49
  function formatResourceOutput(resource) {
53
50
  const parts = [`<${resource.type} name="${escapeXml(resource.name)}" location="${escapeXml(resource.location)}">`];
54
51
  if (resource.baseDir) {
@@ -95,6 +92,13 @@ function buildPhaseDirectiveMessage(phase, output) {
95
92
  const parts = [];
96
93
  parts.push(`<phase_content name="${escapeXml(phase.name)}">`);
97
94
  parts.push(phase.content);
95
+ if (output.payload !== void 0) {
96
+ const payload = jsonToXml(output.payload, 2);
97
+ parts.push(" <phase_input>");
98
+ if (payload) parts.push(payload);
99
+ else if (output.payload === null) parts.push(" null");
100
+ parts.push(" </phase_input>");
101
+ }
98
102
  if (output.results && output.results.length > 0 || output.instruction) {
99
103
  parts.push(` <prev_phase_outputs>`);
100
104
  if (output.instruction) {
@@ -161,15 +165,147 @@ function normalizeRelativePath(path) {
161
165
  }
162
166
 
163
167
  // src/harness/tools/route-tool.ts
168
+ import Type2 from "typebox";
169
+
170
+ // src/runtime/json.ts
171
+ function isPlainObject(value) {
172
+ const prototype = Object.getPrototypeOf(value);
173
+ return prototype === Object.prototype || prototype === null;
174
+ }
175
+ function hasOnlyDataProperties(value) {
176
+ return Reflect.ownKeys(value).every((key) => {
177
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
178
+ return Boolean(descriptor && "value" in descriptor);
179
+ });
180
+ }
181
+ function visit(value, seen) {
182
+ if (value === null || typeof value === "boolean" || typeof value === "string") return true;
183
+ if (typeof value === "number") return Number.isFinite(value);
184
+ if (typeof value !== "object" || seen.has(value)) return false;
185
+ seen.add(value);
186
+ try {
187
+ if (Array.isArray(value)) {
188
+ if (Object.getPrototypeOf(value) !== Array.prototype || !hasOnlyDataProperties(value)) return false;
189
+ if (Reflect.ownKeys(value).some((key) => key !== "length" && typeof key !== "string")) return false;
190
+ for (let index = 0; index < value.length; index += 1) {
191
+ if (!Object.prototype.hasOwnProperty.call(value, index) || !visit(value[index], seen)) return false;
192
+ }
193
+ return true;
194
+ }
195
+ if (!isPlainObject(value) || !hasOnlyDataProperties(value)) return false;
196
+ if (Reflect.ownKeys(value).some((key) => typeof key !== "string")) return false;
197
+ if (Object.prototype.hasOwnProperty.call(value, "toJSON")) return false;
198
+ return Object.keys(value).every((key) => visit(value[key], seen));
199
+ } finally {
200
+ seen.delete(value);
201
+ }
202
+ }
203
+ function isJsonValue(value) {
204
+ return visit(value, /* @__PURE__ */ new Set());
205
+ }
206
+ function assertJsonValue(value, argument = "value") {
207
+ if (!isJsonValue(value)) throw new TypeError(`${argument} must be JSON-safe`);
208
+ }
209
+ function canonicalJson(value) {
210
+ assertJsonValue(value);
211
+ const encode = (candidate) => {
212
+ if (candidate === null || typeof candidate !== "object") return JSON.stringify(candidate);
213
+ if (Array.isArray(candidate)) return `[${candidate.map(encode).join(",")}]`;
214
+ const object = candidate;
215
+ return `{${Object.keys(object).sort().map((key) => `${JSON.stringify(key)}:${encode(object[key])}`).join(",")}}`;
216
+ };
217
+ return encode(value);
218
+ }
219
+ function utf8ByteLength(value) {
220
+ return new TextEncoder().encode(value).byteLength;
221
+ }
222
+ function assertUtf8ByteLimit(value, limit, argument = "value") {
223
+ if (utf8ByteLength(value) > limit) throw new RangeError(`${argument} exceeds ${limit} UTF-8 bytes`);
224
+ }
225
+
226
+ // src/harness/phases/input.ts
164
227
  import Type from "typebox";
228
+ import Schema from "typebox/schema";
229
+ function parsePhaseInput(value) {
230
+ if (value === void 0) return void 0;
231
+ if (!isJsonValue(value) || value === null || Array.isArray(value)) {
232
+ throw new TypeError("input must be a JSON-safe object");
233
+ }
234
+ return value;
235
+ }
236
+ function phaseInputSchema(input) {
237
+ return schemaForObject(input);
238
+ }
239
+ function preparePhasePayload(input, payload) {
240
+ if (payload !== void 0 && !isJsonValue(payload)) {
241
+ throw new TypeError("Phase payload must be JSON-safe");
242
+ }
243
+ if (input === void 0 || payload === void 0) {
244
+ if (input === void 0) return payload;
245
+ return mergeDefaultObject(input, {});
246
+ }
247
+ const validator = Schema.Compile(phaseInputSchema(input));
248
+ if (!validator.Check(payload)) {
249
+ throw new TypeError("Phase payload does not match the Phase input definition");
250
+ }
251
+ return mergeDefaultObject(input, payload);
252
+ }
253
+ function schemaForValue(value) {
254
+ if (value === null) return Type.Unknown();
255
+ if (typeof value === "string") return Type.String();
256
+ if (typeof value === "boolean") return Type.Boolean();
257
+ if (typeof value === "number") return Type.Number();
258
+ if (Array.isArray(value)) {
259
+ if (value.length === 0) return Type.Array(Type.Unknown());
260
+ const schemas = value.map(schemaForValue);
261
+ const unique = new Map(schemas.map((schema) => [JSON.stringify(schema), schema]));
262
+ const item = unique.size === 1 ? [...unique.values()][0] : Type.Union([...unique.values()]);
263
+ return Type.Array(item);
264
+ }
265
+ if (!isJsonObject(value)) throw new TypeError("Phase input values must be JSON-safe");
266
+ return schemaForObject(value);
267
+ }
268
+ function schemaForObject(value) {
269
+ const keys = Object.keys(value);
270
+ if (keys.length === 0) return Type.Record(Type.String(), Type.Unknown());
271
+ const properties = Object.fromEntries(keys.map((key) => [key, schemaForValue(value[key])]));
272
+ return Type.Partial(Type.Object(properties), { additionalProperties: false });
273
+ }
274
+ function mergeDefaultObject(defaults, payload) {
275
+ const result = {};
276
+ for (const [key, value] of Object.entries(payload)) {
277
+ const defaultValue = defaults[key];
278
+ result[key] = defaultValue !== void 0 && isJsonObject(defaultValue) && isJsonObject(value) ? mergeDefaultObject(defaultValue, value) : cloneJsonValue(value);
279
+ }
280
+ for (const [key, value] of Object.entries(defaults)) {
281
+ if (!Object.prototype.hasOwnProperty.call(payload, key)) {
282
+ result[key] = cloneJsonValue(value);
283
+ }
284
+ }
285
+ return result;
286
+ }
287
+ function cloneJsonValue(value) {
288
+ if (Array.isArray(value)) return value.map(cloneJsonValue);
289
+ if (isJsonObject(value)) {
290
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, cloneJsonValue(item)]));
291
+ }
292
+ return value;
293
+ }
294
+ function isJsonObject(value) {
295
+ return value !== null && typeof value === "object" && !Array.isArray(value);
296
+ }
297
+
298
+ // src/harness/tools/route-tool.ts
165
299
  var PhaseRouteTool = "route";
166
300
  function buildPhaseEntry(p) {
167
301
  const entry = { name: p.name, description: p.description };
168
302
  if (p.tools && p.tools.length > 0) {
169
303
  entry.available_tools = p.tools.join(", ");
170
304
  }
171
- if (p.input && Object.keys(p.input).length > 0) {
172
- entry.required_input = Object.entries(p.input).map(([key, desc]) => `- ${key}: ${desc}`).join("\n");
305
+ if (p.input !== void 0) {
306
+ entry.payload_schema = JSON.stringify(phaseInputSchema(p.input));
307
+ } else {
308
+ entry.payload_schema = "any JSON-safe value";
173
309
  }
174
310
  return entry;
175
311
  }
@@ -203,14 +339,20 @@ function buildRouteDescription(availablePhases) {
203
339
  }
204
340
  function createRouteTool(availablePhases) {
205
341
  const routablePhases = availablePhases.filter(({ name }) => name !== "stop");
206
- const DecisionTarget = Type.Object({
207
- phase: Type.Union([
208
- ...routablePhases.map((p) => Type.Literal(p.name)),
209
- Type.Literal("stop")
210
- ]),
211
- reason: Type.Optional(Type.String({ description: "Brief reason for this decision" })),
212
- payload: Type.Optional(Type.Unknown({ description: "Structured input for the target phase" }))
213
- });
342
+ const decisionTargets = [
343
+ ...routablePhases.map((phase) => Type2.Object({
344
+ phase: Type2.Literal(phase.name),
345
+ reason: Type2.Optional(Type2.String({ description: "Brief reason for this decision" })),
346
+ payload: Type2.Optional(
347
+ phase.input === void 0 ? Type2.Unknown({ description: "Any JSON-safe value for the target phase" }) : phaseInputSchema(phase.input)
348
+ )
349
+ })),
350
+ Type2.Object({
351
+ phase: Type2.Literal("stop"),
352
+ reason: Type2.Optional(Type2.String({ description: "Brief reason for this decision" })),
353
+ payload: Type2.Optional(Type2.Unknown({ description: "Optional stop payload" }))
354
+ })
355
+ ];
214
356
  return {
215
357
  name: PhaseRouteTool,
216
358
  description: buildRouteDescription(availablePhases),
@@ -219,9 +361,9 @@ function createRouteTool(availablePhases) {
219
361
  "Do not call route together with ordinary tools.",
220
362
  "Use route(stop) only when the current user request or task is complete; the Stop Phase will provide the final user-facing conclusion."
221
363
  ],
222
- parameters: Type.Object({
223
- decision: Type.Array(DecisionTarget, { description: "Phase executions to start", minItems: 1 }),
224
- instruction: Type.Optional(Type.String({ description: "Overall instruction, passed as context" }))
364
+ parameters: Type2.Object({
365
+ decision: Type2.Array(Type2.Union(decisionTargets), { description: "Phase executions to start", minItems: 1 }),
366
+ instruction: Type2.Optional(Type2.String({ description: "Overall instruction, passed as context" }))
225
367
  }),
226
368
  // No-op: this tool is intercepted by phases, never executed via tool execution
227
369
  execute: async (_, context) => ({
@@ -274,37 +416,37 @@ var DEFAULT_MAX_READ_BYTES = 50 * 1024;
274
416
  var DEFAULT_MAX_READ_LINES = 2e3;
275
417
  var DEFAULT_BASH_TIMEOUT_MS = 3e4;
276
418
  var DEFAULT_MAX_BASH_OUTPUT_BYTES = 64e3;
277
- var ReadArgsSchema = Type2.Object({
278
- path: Type2.String({ description: "Path to the file to read." }),
279
- offset: Type2.Optional(Type2.Number({ description: "1-based line to start from." })),
280
- limit: Type2.Optional(Type2.Number({ description: "Maximum number of lines." }))
419
+ var ReadArgsSchema = Type3.Object({
420
+ path: Type3.String({ description: "Path to the file to read." }),
421
+ offset: Type3.Optional(Type3.Number({ description: "1-based line to start from." })),
422
+ limit: Type3.Optional(Type3.Number({ description: "Maximum number of lines." }))
281
423
  });
282
- var ReadArgsValidator = Schema.Compile(ReadArgsSchema);
283
- var WriteArgsSchema = Type2.Object({
284
- path: Type2.String({ description: "Path to the file to write." }),
285
- content: Type2.String({ description: "Complete file contents." })
424
+ var ReadArgsValidator = Schema2.Compile(ReadArgsSchema);
425
+ var WriteArgsSchema = Type3.Object({
426
+ path: Type3.String({ description: "Path to the file to write." }),
427
+ content: Type3.String({ description: "Complete file contents." })
286
428
  });
287
- var WriteArgsValidator = Schema.Compile(WriteArgsSchema);
288
- var EditArgsSchema = Type2.Object({
289
- path: Type2.String({ description: "Path to the file to edit." }),
290
- edits: Type2.Array(Type2.Object({
291
- oldText: Type2.String({ description: "Exact unique text to replace." }),
292
- newText: Type2.String({ description: "Replacement text." })
429
+ var WriteArgsValidator = Schema2.Compile(WriteArgsSchema);
430
+ var EditArgsSchema = Type3.Object({
431
+ path: Type3.String({ description: "Path to the file to edit." }),
432
+ edits: Type3.Array(Type3.Object({
433
+ oldText: Type3.String({ description: "Exact unique text to replace." }),
434
+ newText: Type3.String({ description: "Replacement text." })
293
435
  }), { description: "One or more non-overlapping replacements." })
294
436
  });
295
- var EditArgsValidator = Schema.Compile(EditArgsSchema);
296
- var BashArgsSchema = Type2.Object({
297
- command: Type2.String({ description: "Bash command to execute." }),
298
- timeout: Type2.Optional(Type2.Number({ description: "Timeout in seconds." }))
437
+ var EditArgsValidator = Schema2.Compile(EditArgsSchema);
438
+ var BashArgsSchema = Type3.Object({
439
+ command: Type3.String({ description: "Bash command to execute." }),
440
+ timeout: Type3.Optional(Type3.Number({ description: "Timeout in seconds." }))
299
441
  });
300
- var BashArgsValidator = Schema.Compile(BashArgsSchema);
442
+ var BashArgsValidator = Schema2.Compile(BashArgsSchema);
301
443
  var validatorCache = /* @__PURE__ */ new WeakMap();
302
444
  function validatorFor(schema) {
303
445
  const cached = validatorCache.get(schema);
304
446
  if (cached) {
305
447
  return cached;
306
448
  }
307
- const validator = Schema.Compile(schema);
449
+ const validator = Schema2.Compile(schema);
308
450
  validatorCache.set(schema, validator);
309
451
  return validator;
310
452
  }
@@ -1134,62 +1276,6 @@ function createExtensionAPI(hooks, options, runtime, eventBus) {
1134
1276
  };
1135
1277
  }
1136
1278
 
1137
- // src/runtime/json.ts
1138
- function isPlainObject(value) {
1139
- const prototype = Object.getPrototypeOf(value);
1140
- return prototype === Object.prototype || prototype === null;
1141
- }
1142
- function hasOnlyDataProperties(value) {
1143
- return Reflect.ownKeys(value).every((key) => {
1144
- const descriptor = Object.getOwnPropertyDescriptor(value, key);
1145
- return Boolean(descriptor && "value" in descriptor);
1146
- });
1147
- }
1148
- function visit(value, seen) {
1149
- if (value === null || typeof value === "boolean" || typeof value === "string") return true;
1150
- if (typeof value === "number") return Number.isFinite(value);
1151
- if (typeof value !== "object" || seen.has(value)) return false;
1152
- seen.add(value);
1153
- try {
1154
- if (Array.isArray(value)) {
1155
- if (Object.getPrototypeOf(value) !== Array.prototype || !hasOnlyDataProperties(value)) return false;
1156
- if (Reflect.ownKeys(value).some((key) => key !== "length" && typeof key !== "string")) return false;
1157
- for (let index = 0; index < value.length; index += 1) {
1158
- if (!Object.prototype.hasOwnProperty.call(value, index) || !visit(value[index], seen)) return false;
1159
- }
1160
- return true;
1161
- }
1162
- if (!isPlainObject(value) || !hasOnlyDataProperties(value)) return false;
1163
- if (Reflect.ownKeys(value).some((key) => typeof key !== "string")) return false;
1164
- if (Object.prototype.hasOwnProperty.call(value, "toJSON")) return false;
1165
- return Object.keys(value).every((key) => visit(value[key], seen));
1166
- } finally {
1167
- seen.delete(value);
1168
- }
1169
- }
1170
- function isJsonValue(value) {
1171
- return visit(value, /* @__PURE__ */ new Set());
1172
- }
1173
- function assertJsonValue(value, argument = "value") {
1174
- if (!isJsonValue(value)) throw new TypeError(`${argument} must be JSON-safe`);
1175
- }
1176
- function canonicalJson(value) {
1177
- assertJsonValue(value);
1178
- const encode = (candidate) => {
1179
- if (candidate === null || typeof candidate !== "object") return JSON.stringify(candidate);
1180
- if (Array.isArray(candidate)) return `[${candidate.map(encode).join(",")}]`;
1181
- const object = candidate;
1182
- return `{${Object.keys(object).sort().map((key) => `${JSON.stringify(key)}:${encode(object[key])}`).join(",")}}`;
1183
- };
1184
- return encode(value);
1185
- }
1186
- function utf8ByteLength(value) {
1187
- return new TextEncoder().encode(value).byteLength;
1188
- }
1189
- function assertUtf8ByteLimit(value, limit, argument = "value") {
1190
- if (utf8ByteLength(value) > limit) throw new RangeError(`${argument} exceeds ${limit} UTF-8 bytes`);
1191
- }
1192
-
1193
1279
  // src/harness/phases/interactions.ts
1194
1280
  var PhaseInteractionCancelledError = class extends Error {
1195
1281
  code = "phase_interaction_cancelled";
@@ -1621,13 +1707,21 @@ async function loadPhase(targetPath) {
1621
1707
  warnResourceDiagnostics("phase", resolved, diagnostics);
1622
1708
  const baseDir = dirname4(resolved);
1623
1709
  const skills = await loadPhaseSkills(baseDir);
1710
+ let input;
1711
+ try {
1712
+ input = parsePhaseInput(metadata.input);
1713
+ } catch (error) {
1714
+ const message = error instanceof Error ? error.message : String(error);
1715
+ warnResourceDiagnostics("phase", resolved, [message]);
1716
+ throw new ResourceMetadataError("invalid_metadata", message);
1717
+ }
1624
1718
  const phase = {
1625
1719
  name,
1626
1720
  description: description.description,
1627
1721
  tools: definition.tools ? [...definition.tools] : void 0,
1628
1722
  skills,
1629
1723
  target: metadata.target,
1630
- input: metadata.input,
1724
+ ...input === void 0 ? {} : { input },
1631
1725
  isolated: metadata.isolated,
1632
1726
  filePath: resolved,
1633
1727
  baseDir,
@@ -2191,9 +2285,21 @@ function resolveRouteDecision(route, registry) {
2191
2285
  if (requestedCount !== 1) return void 0;
2192
2286
  return { ...route, requestedCount };
2193
2287
  }
2194
- const decision = route.decision.filter(({ phase }) => registry.phases.has(phase));
2288
+ const decision = route.decision.flatMap((candidate) => {
2289
+ const target = registry.phases.get(candidate.phase);
2290
+ if (!target) return [];
2291
+ try {
2292
+ const payload = preparePhasePayload(target.input, normalizePayload(candidate.payload));
2293
+ return [{
2294
+ ...candidate,
2295
+ ...payload === void 0 ? {} : { payload }
2296
+ }];
2297
+ } catch {
2298
+ return [];
2299
+ }
2300
+ });
2195
2301
  if (decision.length === 0) return void 0;
2196
- return { ...route, decision, requestedCount };
2302
+ return { ...route, decision, requestedCount: decision.length };
2197
2303
  }
2198
2304
  function resolveModelRouteDecision(toolCalls, registry) {
2199
2305
  return resolveRouteDecision(toolCalls ? extractRouteCall(toolCalls) : void 0, registry);
@@ -2428,7 +2534,8 @@ async function runPhaseLoop(config, state, registry) {
2428
2534
  if (resumingSuspendedRun) {
2429
2535
  state.status = "running";
2430
2536
  }
2431
- let previousPayload = resumingSuspendedRun ? state.continuation?.previousPayload : void 0;
2537
+ const initialPayload = !resumingSuspendedRun ? config.execution.runMetadata?.phasePayload : void 0;
2538
+ let previousPayload = resumingSuspendedRun ? state.continuation?.previousPayload : initialPayload;
2432
2539
  let previousPhaseMsgId = resumingSuspendedRun ? state.continuation?.previousPhaseMessageId : void 0;
2433
2540
  let previousPhaseInputMsgId;
2434
2541
  let previousResults = resumingSuspendedRun ? state.continuation?.previousResults?.map((result) => ({ ...result })) ?? [] : [];
@@ -2519,6 +2626,9 @@ async function runPhaseLoop(config, state, registry) {
2519
2626
  if (!phase) {
2520
2627
  throw new Error(`Phase "${currentPhaseId}" not found`);
2521
2628
  }
2629
+ if (previousPayload !== void 0 || phase.input !== void 0) {
2630
+ previousPayload = preparePhasePayload(phase.input, previousPayload);
2631
+ }
2522
2632
  state.currentPhase = currentPhaseId;
2523
2633
  const allTools = buildToolsWithRouting(config, availablePhases);
2524
2634
  const messageManager = createMessageManager(config.context, config.onMessage, config.onMessageDelta);
@@ -2584,7 +2694,7 @@ async function runPhaseLoop(config, state, registry) {
2584
2694
  const lastMessageBeforePhase = config.context.messages.at(-1);
2585
2695
  previousPhaseMsgId = injectPhaseContent(
2586
2696
  phase,
2587
- { results: previousResults, instruction: pendingInstruction },
2697
+ { results: previousResults, instruction: pendingInstruction, payload: previousPayload },
2588
2698
  config.context.messages,
2589
2699
  phaseContext.messages
2590
2700
  );
@@ -2630,6 +2740,7 @@ async function runPhaseLoop(config, state, registry) {
2630
2740
  output = extAfter.output;
2631
2741
  if (hookHasRoute) {
2632
2742
  routeDecision = resolveHookRouteDecision(output, registry);
2743
+ if (routeDecision) applyFirstDecision(routeDecision, output);
2633
2744
  } else if (phase.run || phase.factory) {
2634
2745
  output = { ...output, route: "stop" };
2635
2746
  routeDecision = void 0;
@@ -2736,7 +2847,7 @@ async function runPhaseLoop(config, state, registry) {
2736
2847
  }
2737
2848
  currentPhaseId = STOP_PHASE_ID;
2738
2849
  previousPayload = output.payload;
2739
- previousResults = output.payload !== void 0 ? [{ name: phase.name, output: output.payload }] : [];
2850
+ previousResults = [];
2740
2851
  pendingInstruction = phase.target ? void 0 : routeDecision?.instruction;
2741
2852
  continue;
2742
2853
  }
@@ -2753,8 +2864,10 @@ async function runPhaseLoop(config, state, registry) {
2753
2864
  to: targetPhaseId,
2754
2865
  ts: createTimestamp()
2755
2866
  });
2756
- previousPayload = output.payload;
2757
- previousResults = output.payload !== void 0 ? [{ name: phase.name, output: output.payload }] : [];
2867
+ const targetPhase = registry.phases.get(targetPhaseId);
2868
+ const targetPayload = !phase.target && routeDecision ? routeDecision.decision[0]?.payload : output.payload;
2869
+ previousPayload = preparePhasePayload(targetPhase?.input, targetPayload);
2870
+ previousResults = [];
2758
2871
  pendingInstruction = phase.target ? void 0 : routeDecision?.instruction;
2759
2872
  currentPhaseId = targetPhaseId;
2760
2873
  }
@@ -3023,6 +3136,7 @@ function createPhaseExecution(config, state, phase, messageManager, toolExecutio
3023
3136
  }
3024
3137
  async function executeParallelPhase(config, state, registry, phase, payload, instruction, context, availablePhases, instanceId, groupId, index, count, sourcePhaseId) {
3025
3138
  const messages = [...context];
3139
+ const effectivePayload = preparePhasePayload(phase.input, payload);
3026
3140
  const allTools = buildToolsWithRouting(config, availablePhases);
3027
3141
  const phaseTools = selectPhaseTools(allTools, phase.tools, false);
3028
3142
  const phaseSkills = mergeSkills(config.context.skills, phase.skills);
@@ -3045,7 +3159,7 @@ async function executeParallelPhase(config, state, registry, phase, payload, ins
3045
3159
  current: phase.name,
3046
3160
  available: Array.from(registry.phases.keys()),
3047
3161
  iterations: 0,
3048
- payload
3162
+ payload: effectivePayload
3049
3163
  }
3050
3164
  };
3051
3165
  const messageManager = createMessageManager({ messages }, config.onMessage, config.onMessageDelta);
@@ -3053,7 +3167,8 @@ async function executeParallelPhase(config, state, registry, phase, payload, ins
3053
3167
  phase,
3054
3168
  {
3055
3169
  instruction,
3056
- results: payload !== void 0 ? [{ name: sourcePhaseId, output: payload }] : []
3170
+ payload: effectivePayload,
3171
+ results: []
3057
3172
  },
3058
3173
  messages
3059
3174
  );
@@ -3308,7 +3423,6 @@ function isContinuation(value) {
3308
3423
  }
3309
3424
 
3310
3425
  // src/runtime/contracts.ts
3311
- var HOST_CONTEXT_MESSAGE_KIND = "host_context";
3312
3426
  var THINKING_LEVELS = [
3313
3427
  "off",
3314
3428
  "minimal",
@@ -3331,7 +3445,7 @@ function thinkingLevelFromMessages(messages) {
3331
3445
  if (message?.role !== "user") continue;
3332
3446
  const metadata = message.metadata;
3333
3447
  const kind = isRecord3(metadata) && typeof metadata.kind === "string" ? metadata.kind : void 0;
3334
- if (kind === "phase_prompt" || kind === "phase_input" || kind === HOST_CONTEXT_MESSAGE_KIND) continue;
3448
+ if (kind === "phase_prompt" || kind === "phase_input") continue;
3335
3449
  return thinkingLevelFromMetadata(metadata);
3336
3450
  }
3337
3451
  return void 0;
@@ -3385,9 +3499,6 @@ function normalizeUserInput(input) {
3385
3499
  assertJsonValue(normalized, "input");
3386
3500
  return normalized;
3387
3501
  }
3388
- function canonicalUserInput(input) {
3389
- return canonicalJson(normalizeUserInput(input));
3390
- }
3391
3502
  function isAssistantMessage(value) {
3392
3503
  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");
3393
3504
  }
@@ -4068,11 +4179,14 @@ function resolveDefinitionContext(config, assembled = {}) {
4068
4179
  selectNamedResources(config.resources.skills, config.definition.skills, "Skill"),
4069
4180
  config.definition.bundledSkills
4070
4181
  );
4071
- const contexts = selectNamedResources(
4072
- config.resources.contexts ?? [],
4073
- config.definition.contexts,
4074
- "Context"
4075
- );
4182
+ const contexts = [
4183
+ ...selectNamedResources(
4184
+ config.resources.contexts ?? [],
4185
+ config.definition.contexts,
4186
+ "Context"
4187
+ ),
4188
+ ...config.additionalContexts ?? []
4189
+ ];
4076
4190
  const candidateRegistry = assembled.phases ?? config.resources.phases;
4077
4191
  const phaseCandidates = [...candidateRegistry?.phases.values() ?? []];
4078
4192
  const coreNames = /* @__PURE__ */ new Set([DEFAULT_PHASE_ID, STOP_PHASE_ID, COMPACT_PHASE_ID]);
@@ -5891,6 +6005,7 @@ var AgentRuntime = class _AgentRuntime {
5891
6005
  kind: "phase",
5892
6006
  name: phase.name,
5893
6007
  description: phase.description,
6008
+ ...phase.input === void 0 ? {} : { input: phase.input },
5894
6009
  ...phase.core ? { core: true } : {},
5895
6010
  disableAutoInvocation: phase.disableAutoInvocation ?? false,
5896
6011
  disableImplicitInvocation: phase.disableImplicitInvocation ?? false,
@@ -6082,8 +6197,7 @@ var AgentRuntime = class _AgentRuntime {
6082
6197
  runId: run.id,
6083
6198
  expectedRevision: run.revision,
6084
6199
  executionId,
6085
- configToken: token,
6086
- ...controlKind ? {} : { inputContext: additionalContextInput(config) }
6200
+ configToken: token
6087
6201
  });
6088
6202
  executionRevision = claim.run.revision;
6089
6203
  const controller = new AbortController();
@@ -6553,12 +6667,6 @@ var AgentRuntime = class _AgentRuntime {
6553
6667
  this.heartbeat.unref?.();
6554
6668
  }
6555
6669
  };
6556
- function additionalContextInput(config) {
6557
- const contexts = config.additionalContexts ?? [];
6558
- if (contexts.length === 0) return void 0;
6559
- const content = buildAdditionalContextMessage(contexts);
6560
- return content.length === 0 ? void 0 : { content, metadata: { kind: HOST_CONTEXT_MESSAGE_KIND } };
6561
- }
6562
6670
  var DurableRun = class {
6563
6671
  constructor(runtime, id) {
6564
6672
  this.runtime = runtime;
@@ -7107,13 +7215,11 @@ var InMemoryStore = class _InMemoryStore {
7107
7215
  this.assertOwner(lease);
7108
7216
  const executionId = input.executionId ?? createId("exec");
7109
7217
  const operationKey = `claim:${executionId}`;
7110
- const inputContext = input.inputContext === void 0 ? void 0 : normalizeUserInput(input.inputContext);
7111
7218
  const operationPayload = canonicalJson([
7112
7219
  input.runId,
7113
7220
  input.expectedRevision,
7114
7221
  input.messageId ?? null,
7115
- input.configToken ?? null,
7116
- inputContext ?? null
7222
+ input.configToken ?? null
7117
7223
  ]);
7118
7224
  const replay = this.replayOperation(operationKey, operationPayload);
7119
7225
  if (replay) return clone(replay);
@@ -7131,21 +7237,6 @@ var InMemoryStore = class _InMemoryStore {
7131
7237
  }
7132
7238
  if (!run.pinnedConfigToken && input.configToken) run.pinnedConfigToken = input.configToken;
7133
7239
  const committedMessages = [];
7134
- const existingMessages = this.messagesForRun(run.id);
7135
- 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))) {
7136
- const contextMessage = {
7137
- id: createId("msg"),
7138
- agentId: run.agentId,
7139
- runId: run.id,
7140
- role: "user",
7141
- content: userInputContent(inputContext),
7142
- ...userInputMetadata(inputContext) ? { metadata: clone(userInputMetadata(inputContext)) } : {},
7143
- sequenceWithinRun: this.nextMessageSequence(run.id),
7144
- createdAt: createTimestamp()
7145
- };
7146
- this.messages.set(contextMessage.id, contextMessage);
7147
- committedMessages.push(contextMessage);
7148
- }
7149
7240
  if (!run.checkpoint && !run.initialMessageId && (!isControlRun(run) || hasUserInput(run.input))) {
7150
7241
  const userInput = normalizeUserInput(run.input);
7151
7242
  const message = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rowan-agent/agent",
3
- "version": "0.9.16",
3
+ "version": "0.9.18",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",