@rowan-agent/agent 0.9.16 → 0.9.17

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;
@@ -2785,4 +2788,4 @@ declare function parseFrontmatter<T = Record<string, unknown>>(raw: string): Fro
2785
2788
 
2786
2789
  declare function createCoreTools(input?: CoreToolContext): Tool[];
2787
2790
 
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 };
2791
+ 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) {
@@ -95,6 +95,13 @@ function buildPhaseDirectiveMessage(phase, output) {
95
95
  const parts = [];
96
96
  parts.push(`<phase_content name="${escapeXml(phase.name)}">`);
97
97
  parts.push(phase.content);
98
+ if (output.payload !== void 0) {
99
+ const payload = jsonToXml(output.payload, 2);
100
+ parts.push(" <phase_input>");
101
+ if (payload) parts.push(payload);
102
+ else if (output.payload === null) parts.push(" null");
103
+ parts.push(" </phase_input>");
104
+ }
98
105
  if (output.results && output.results.length > 0 || output.instruction) {
99
106
  parts.push(` <prev_phase_outputs>`);
100
107
  if (output.instruction) {
@@ -161,15 +168,147 @@ function normalizeRelativePath(path) {
161
168
  }
162
169
 
163
170
  // src/harness/tools/route-tool.ts
171
+ import Type2 from "typebox";
172
+
173
+ // src/runtime/json.ts
174
+ function isPlainObject(value) {
175
+ const prototype = Object.getPrototypeOf(value);
176
+ return prototype === Object.prototype || prototype === null;
177
+ }
178
+ function hasOnlyDataProperties(value) {
179
+ return Reflect.ownKeys(value).every((key) => {
180
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
181
+ return Boolean(descriptor && "value" in descriptor);
182
+ });
183
+ }
184
+ function visit(value, seen) {
185
+ if (value === null || typeof value === "boolean" || typeof value === "string") return true;
186
+ if (typeof value === "number") return Number.isFinite(value);
187
+ if (typeof value !== "object" || seen.has(value)) return false;
188
+ seen.add(value);
189
+ try {
190
+ if (Array.isArray(value)) {
191
+ if (Object.getPrototypeOf(value) !== Array.prototype || !hasOnlyDataProperties(value)) return false;
192
+ if (Reflect.ownKeys(value).some((key) => key !== "length" && typeof key !== "string")) return false;
193
+ for (let index = 0; index < value.length; index += 1) {
194
+ if (!Object.prototype.hasOwnProperty.call(value, index) || !visit(value[index], seen)) return false;
195
+ }
196
+ return true;
197
+ }
198
+ if (!isPlainObject(value) || !hasOnlyDataProperties(value)) return false;
199
+ if (Reflect.ownKeys(value).some((key) => typeof key !== "string")) return false;
200
+ if (Object.prototype.hasOwnProperty.call(value, "toJSON")) return false;
201
+ return Object.keys(value).every((key) => visit(value[key], seen));
202
+ } finally {
203
+ seen.delete(value);
204
+ }
205
+ }
206
+ function isJsonValue(value) {
207
+ return visit(value, /* @__PURE__ */ new Set());
208
+ }
209
+ function assertJsonValue(value, argument = "value") {
210
+ if (!isJsonValue(value)) throw new TypeError(`${argument} must be JSON-safe`);
211
+ }
212
+ function canonicalJson(value) {
213
+ assertJsonValue(value);
214
+ const encode = (candidate) => {
215
+ if (candidate === null || typeof candidate !== "object") return JSON.stringify(candidate);
216
+ if (Array.isArray(candidate)) return `[${candidate.map(encode).join(",")}]`;
217
+ const object = candidate;
218
+ return `{${Object.keys(object).sort().map((key) => `${JSON.stringify(key)}:${encode(object[key])}`).join(",")}}`;
219
+ };
220
+ return encode(value);
221
+ }
222
+ function utf8ByteLength(value) {
223
+ return new TextEncoder().encode(value).byteLength;
224
+ }
225
+ function assertUtf8ByteLimit(value, limit, argument = "value") {
226
+ if (utf8ByteLength(value) > limit) throw new RangeError(`${argument} exceeds ${limit} UTF-8 bytes`);
227
+ }
228
+
229
+ // src/harness/phases/input.ts
164
230
  import Type from "typebox";
231
+ import Schema from "typebox/schema";
232
+ function parsePhaseInput(value) {
233
+ if (value === void 0) return void 0;
234
+ if (!isJsonValue(value) || value === null || Array.isArray(value)) {
235
+ throw new TypeError("input must be a JSON-safe object");
236
+ }
237
+ return value;
238
+ }
239
+ function phaseInputSchema(input) {
240
+ return schemaForObject(input);
241
+ }
242
+ function preparePhasePayload(input, payload) {
243
+ if (payload !== void 0 && !isJsonValue(payload)) {
244
+ throw new TypeError("Phase payload must be JSON-safe");
245
+ }
246
+ if (input === void 0 || payload === void 0) {
247
+ if (input === void 0) return payload;
248
+ return mergeDefaultObject(input, {});
249
+ }
250
+ const validator = Schema.Compile(phaseInputSchema(input));
251
+ if (!validator.Check(payload)) {
252
+ throw new TypeError("Phase payload does not match the Phase input definition");
253
+ }
254
+ return mergeDefaultObject(input, payload);
255
+ }
256
+ function schemaForValue(value) {
257
+ if (value === null) return Type.Unknown();
258
+ if (typeof value === "string") return Type.String();
259
+ if (typeof value === "boolean") return Type.Boolean();
260
+ if (typeof value === "number") return Type.Number();
261
+ if (Array.isArray(value)) {
262
+ if (value.length === 0) return Type.Array(Type.Unknown());
263
+ const schemas = value.map(schemaForValue);
264
+ const unique = new Map(schemas.map((schema) => [JSON.stringify(schema), schema]));
265
+ const item = unique.size === 1 ? [...unique.values()][0] : Type.Union([...unique.values()]);
266
+ return Type.Array(item);
267
+ }
268
+ if (!isJsonObject(value)) throw new TypeError("Phase input values must be JSON-safe");
269
+ return schemaForObject(value);
270
+ }
271
+ function schemaForObject(value) {
272
+ const keys = Object.keys(value);
273
+ if (keys.length === 0) return Type.Record(Type.String(), Type.Unknown());
274
+ const properties = Object.fromEntries(keys.map((key) => [key, schemaForValue(value[key])]));
275
+ return Type.Partial(Type.Object(properties), { additionalProperties: false });
276
+ }
277
+ function mergeDefaultObject(defaults, payload) {
278
+ const result = {};
279
+ for (const [key, value] of Object.entries(payload)) {
280
+ const defaultValue = defaults[key];
281
+ result[key] = defaultValue !== void 0 && isJsonObject(defaultValue) && isJsonObject(value) ? mergeDefaultObject(defaultValue, value) : cloneJsonValue(value);
282
+ }
283
+ for (const [key, value] of Object.entries(defaults)) {
284
+ if (!Object.prototype.hasOwnProperty.call(payload, key)) {
285
+ result[key] = cloneJsonValue(value);
286
+ }
287
+ }
288
+ return result;
289
+ }
290
+ function cloneJsonValue(value) {
291
+ if (Array.isArray(value)) return value.map(cloneJsonValue);
292
+ if (isJsonObject(value)) {
293
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, cloneJsonValue(item)]));
294
+ }
295
+ return value;
296
+ }
297
+ function isJsonObject(value) {
298
+ return value !== null && typeof value === "object" && !Array.isArray(value);
299
+ }
300
+
301
+ // src/harness/tools/route-tool.ts
165
302
  var PhaseRouteTool = "route";
166
303
  function buildPhaseEntry(p) {
167
304
  const entry = { name: p.name, description: p.description };
168
305
  if (p.tools && p.tools.length > 0) {
169
306
  entry.available_tools = p.tools.join(", ");
170
307
  }
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");
308
+ if (p.input !== void 0) {
309
+ entry.payload_schema = JSON.stringify(phaseInputSchema(p.input));
310
+ } else {
311
+ entry.payload_schema = "any JSON-safe value";
173
312
  }
174
313
  return entry;
175
314
  }
@@ -203,14 +342,20 @@ function buildRouteDescription(availablePhases) {
203
342
  }
204
343
  function createRouteTool(availablePhases) {
205
344
  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
- });
345
+ const decisionTargets = [
346
+ ...routablePhases.map((phase) => Type2.Object({
347
+ phase: Type2.Literal(phase.name),
348
+ reason: Type2.Optional(Type2.String({ description: "Brief reason for this decision" })),
349
+ payload: Type2.Optional(
350
+ phase.input === void 0 ? Type2.Unknown({ description: "Any JSON-safe value for the target phase" }) : phaseInputSchema(phase.input)
351
+ )
352
+ })),
353
+ Type2.Object({
354
+ phase: Type2.Literal("stop"),
355
+ reason: Type2.Optional(Type2.String({ description: "Brief reason for this decision" })),
356
+ payload: Type2.Optional(Type2.Unknown({ description: "Optional stop payload" }))
357
+ })
358
+ ];
214
359
  return {
215
360
  name: PhaseRouteTool,
216
361
  description: buildRouteDescription(availablePhases),
@@ -219,9 +364,9 @@ function createRouteTool(availablePhases) {
219
364
  "Do not call route together with ordinary tools.",
220
365
  "Use route(stop) only when the current user request or task is complete; the Stop Phase will provide the final user-facing conclusion."
221
366
  ],
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" }))
367
+ parameters: Type2.Object({
368
+ decision: Type2.Array(Type2.Union(decisionTargets), { description: "Phase executions to start", minItems: 1 }),
369
+ instruction: Type2.Optional(Type2.String({ description: "Overall instruction, passed as context" }))
225
370
  }),
226
371
  // No-op: this tool is intercepted by phases, never executed via tool execution
227
372
  execute: async (_, context) => ({
@@ -274,37 +419,37 @@ var DEFAULT_MAX_READ_BYTES = 50 * 1024;
274
419
  var DEFAULT_MAX_READ_LINES = 2e3;
275
420
  var DEFAULT_BASH_TIMEOUT_MS = 3e4;
276
421
  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." }))
422
+ var ReadArgsSchema = Type3.Object({
423
+ path: Type3.String({ description: "Path to the file to read." }),
424
+ offset: Type3.Optional(Type3.Number({ description: "1-based line to start from." })),
425
+ limit: Type3.Optional(Type3.Number({ description: "Maximum number of lines." }))
281
426
  });
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." })
427
+ var ReadArgsValidator = Schema2.Compile(ReadArgsSchema);
428
+ var WriteArgsSchema = Type3.Object({
429
+ path: Type3.String({ description: "Path to the file to write." }),
430
+ content: Type3.String({ description: "Complete file contents." })
286
431
  });
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." })
432
+ var WriteArgsValidator = Schema2.Compile(WriteArgsSchema);
433
+ var EditArgsSchema = Type3.Object({
434
+ path: Type3.String({ description: "Path to the file to edit." }),
435
+ edits: Type3.Array(Type3.Object({
436
+ oldText: Type3.String({ description: "Exact unique text to replace." }),
437
+ newText: Type3.String({ description: "Replacement text." })
293
438
  }), { description: "One or more non-overlapping replacements." })
294
439
  });
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." }))
440
+ var EditArgsValidator = Schema2.Compile(EditArgsSchema);
441
+ var BashArgsSchema = Type3.Object({
442
+ command: Type3.String({ description: "Bash command to execute." }),
443
+ timeout: Type3.Optional(Type3.Number({ description: "Timeout in seconds." }))
299
444
  });
300
- var BashArgsValidator = Schema.Compile(BashArgsSchema);
445
+ var BashArgsValidator = Schema2.Compile(BashArgsSchema);
301
446
  var validatorCache = /* @__PURE__ */ new WeakMap();
302
447
  function validatorFor(schema) {
303
448
  const cached = validatorCache.get(schema);
304
449
  if (cached) {
305
450
  return cached;
306
451
  }
307
- const validator = Schema.Compile(schema);
452
+ const validator = Schema2.Compile(schema);
308
453
  validatorCache.set(schema, validator);
309
454
  return validator;
310
455
  }
@@ -1134,62 +1279,6 @@ function createExtensionAPI(hooks, options, runtime, eventBus) {
1134
1279
  };
1135
1280
  }
1136
1281
 
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
1282
  // src/harness/phases/interactions.ts
1194
1283
  var PhaseInteractionCancelledError = class extends Error {
1195
1284
  code = "phase_interaction_cancelled";
@@ -1621,13 +1710,21 @@ async function loadPhase(targetPath) {
1621
1710
  warnResourceDiagnostics("phase", resolved, diagnostics);
1622
1711
  const baseDir = dirname4(resolved);
1623
1712
  const skills = await loadPhaseSkills(baseDir);
1713
+ let input;
1714
+ try {
1715
+ input = parsePhaseInput(metadata.input);
1716
+ } catch (error) {
1717
+ const message = error instanceof Error ? error.message : String(error);
1718
+ warnResourceDiagnostics("phase", resolved, [message]);
1719
+ throw new ResourceMetadataError("invalid_metadata", message);
1720
+ }
1624
1721
  const phase = {
1625
1722
  name,
1626
1723
  description: description.description,
1627
1724
  tools: definition.tools ? [...definition.tools] : void 0,
1628
1725
  skills,
1629
1726
  target: metadata.target,
1630
- input: metadata.input,
1727
+ ...input === void 0 ? {} : { input },
1631
1728
  isolated: metadata.isolated,
1632
1729
  filePath: resolved,
1633
1730
  baseDir,
@@ -2191,9 +2288,21 @@ function resolveRouteDecision(route, registry) {
2191
2288
  if (requestedCount !== 1) return void 0;
2192
2289
  return { ...route, requestedCount };
2193
2290
  }
2194
- const decision = route.decision.filter(({ phase }) => registry.phases.has(phase));
2291
+ const decision = route.decision.flatMap((candidate) => {
2292
+ const target = registry.phases.get(candidate.phase);
2293
+ if (!target) return [];
2294
+ try {
2295
+ const payload = preparePhasePayload(target.input, normalizePayload(candidate.payload));
2296
+ return [{
2297
+ ...candidate,
2298
+ ...payload === void 0 ? {} : { payload }
2299
+ }];
2300
+ } catch {
2301
+ return [];
2302
+ }
2303
+ });
2195
2304
  if (decision.length === 0) return void 0;
2196
- return { ...route, decision, requestedCount };
2305
+ return { ...route, decision, requestedCount: decision.length };
2197
2306
  }
2198
2307
  function resolveModelRouteDecision(toolCalls, registry) {
2199
2308
  return resolveRouteDecision(toolCalls ? extractRouteCall(toolCalls) : void 0, registry);
@@ -2428,7 +2537,8 @@ async function runPhaseLoop(config, state, registry) {
2428
2537
  if (resumingSuspendedRun) {
2429
2538
  state.status = "running";
2430
2539
  }
2431
- let previousPayload = resumingSuspendedRun ? state.continuation?.previousPayload : void 0;
2540
+ const initialPayload = !resumingSuspendedRun ? config.execution.runMetadata?.phasePayload : void 0;
2541
+ let previousPayload = resumingSuspendedRun ? state.continuation?.previousPayload : initialPayload;
2432
2542
  let previousPhaseMsgId = resumingSuspendedRun ? state.continuation?.previousPhaseMessageId : void 0;
2433
2543
  let previousPhaseInputMsgId;
2434
2544
  let previousResults = resumingSuspendedRun ? state.continuation?.previousResults?.map((result) => ({ ...result })) ?? [] : [];
@@ -2519,6 +2629,9 @@ async function runPhaseLoop(config, state, registry) {
2519
2629
  if (!phase) {
2520
2630
  throw new Error(`Phase "${currentPhaseId}" not found`);
2521
2631
  }
2632
+ if (previousPayload !== void 0 || phase.input !== void 0) {
2633
+ previousPayload = preparePhasePayload(phase.input, previousPayload);
2634
+ }
2522
2635
  state.currentPhase = currentPhaseId;
2523
2636
  const allTools = buildToolsWithRouting(config, availablePhases);
2524
2637
  const messageManager = createMessageManager(config.context, config.onMessage, config.onMessageDelta);
@@ -2584,7 +2697,7 @@ async function runPhaseLoop(config, state, registry) {
2584
2697
  const lastMessageBeforePhase = config.context.messages.at(-1);
2585
2698
  previousPhaseMsgId = injectPhaseContent(
2586
2699
  phase,
2587
- { results: previousResults, instruction: pendingInstruction },
2700
+ { results: previousResults, instruction: pendingInstruction, payload: previousPayload },
2588
2701
  config.context.messages,
2589
2702
  phaseContext.messages
2590
2703
  );
@@ -2630,6 +2743,7 @@ async function runPhaseLoop(config, state, registry) {
2630
2743
  output = extAfter.output;
2631
2744
  if (hookHasRoute) {
2632
2745
  routeDecision = resolveHookRouteDecision(output, registry);
2746
+ if (routeDecision) applyFirstDecision(routeDecision, output);
2633
2747
  } else if (phase.run || phase.factory) {
2634
2748
  output = { ...output, route: "stop" };
2635
2749
  routeDecision = void 0;
@@ -2736,7 +2850,7 @@ async function runPhaseLoop(config, state, registry) {
2736
2850
  }
2737
2851
  currentPhaseId = STOP_PHASE_ID;
2738
2852
  previousPayload = output.payload;
2739
- previousResults = output.payload !== void 0 ? [{ name: phase.name, output: output.payload }] : [];
2853
+ previousResults = [];
2740
2854
  pendingInstruction = phase.target ? void 0 : routeDecision?.instruction;
2741
2855
  continue;
2742
2856
  }
@@ -2753,8 +2867,10 @@ async function runPhaseLoop(config, state, registry) {
2753
2867
  to: targetPhaseId,
2754
2868
  ts: createTimestamp()
2755
2869
  });
2756
- previousPayload = output.payload;
2757
- previousResults = output.payload !== void 0 ? [{ name: phase.name, output: output.payload }] : [];
2870
+ const targetPhase = registry.phases.get(targetPhaseId);
2871
+ const targetPayload = !phase.target && routeDecision ? routeDecision.decision[0]?.payload : output.payload;
2872
+ previousPayload = preparePhasePayload(targetPhase?.input, targetPayload);
2873
+ previousResults = [];
2758
2874
  pendingInstruction = phase.target ? void 0 : routeDecision?.instruction;
2759
2875
  currentPhaseId = targetPhaseId;
2760
2876
  }
@@ -3023,6 +3139,7 @@ function createPhaseExecution(config, state, phase, messageManager, toolExecutio
3023
3139
  }
3024
3140
  async function executeParallelPhase(config, state, registry, phase, payload, instruction, context, availablePhases, instanceId, groupId, index, count, sourcePhaseId) {
3025
3141
  const messages = [...context];
3142
+ const effectivePayload = preparePhasePayload(phase.input, payload);
3026
3143
  const allTools = buildToolsWithRouting(config, availablePhases);
3027
3144
  const phaseTools = selectPhaseTools(allTools, phase.tools, false);
3028
3145
  const phaseSkills = mergeSkills(config.context.skills, phase.skills);
@@ -3045,7 +3162,7 @@ async function executeParallelPhase(config, state, registry, phase, payload, ins
3045
3162
  current: phase.name,
3046
3163
  available: Array.from(registry.phases.keys()),
3047
3164
  iterations: 0,
3048
- payload
3165
+ payload: effectivePayload
3049
3166
  }
3050
3167
  };
3051
3168
  const messageManager = createMessageManager({ messages }, config.onMessage, config.onMessageDelta);
@@ -3053,7 +3170,8 @@ async function executeParallelPhase(config, state, registry, phase, payload, ins
3053
3170
  phase,
3054
3171
  {
3055
3172
  instruction,
3056
- results: payload !== void 0 ? [{ name: sourcePhaseId, output: payload }] : []
3173
+ payload: effectivePayload,
3174
+ results: []
3057
3175
  },
3058
3176
  messages
3059
3177
  );
@@ -5891,6 +6009,7 @@ var AgentRuntime = class _AgentRuntime {
5891
6009
  kind: "phase",
5892
6010
  name: phase.name,
5893
6011
  description: phase.description,
6012
+ ...phase.input === void 0 ? {} : { input: phase.input },
5894
6013
  ...phase.core ? { core: true } : {},
5895
6014
  disableAutoInvocation: phase.disableAutoInvocation ?? false,
5896
6015
  disableImplicitInvocation: phase.disableImplicitInvocation ?? false,
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.17",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",