Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
8396709
feat: add runtime workflow inputs (--set KEY=VALUE / inputs API field)
gemmawood Apr 21, 2026
ab2404d
fix: preserve runtime inputs across pause/resume cycles
gemmawood Apr 21, 2026
508a70e
Fix resolved_inputs validation and preCreatedRun metadata persistence
gemmawood Apr 21, 2026
215aae2
Fix regex key escaping and defer model/provider resolution until afte…
gemmawood Apr 21, 2026
a5169fd
Fix four CodeRabbit review issues in executor resolveInputs / applyIn…
gemmawood Apr 21, 2026
621a064
Tighten assertSafeInputKeys to enforce explicit identifier grammar
gemmawood Apr 21, 2026
4115ea5
chore: update Homebrew formula for v0.3.9
github-actions[bot] Apr 22, 2026
359b6d3
chore(release-skill): use --help (not version) for Step 1.5 smoke pro…
Wirasm Apr 22, 2026
6f86402
chore(test-release-skill): preserve archon-stable across test cycles
Wirasm Apr 22, 2026
0e9f1c8
fix(providers/pi): install PI_PACKAGE_DIR shim so Pi workflows run in…
Wirasm Apr 22, 2026
b99cee4
feat(providers): autodetect canonical binary install paths for Claude…
Wirasm Apr 22, 2026
f9f8775
fix(providers/test): use os.homedir() instead of $HOME in claude bina…
coleam00 Apr 23, 2026
5957c6e
fix(server): contain Discord login failure so it doesn't kill the ser…
coleam00 Apr 23, 2026
46874ca
docs(script-nodes): dedicated guide + teach the archon skill (#1362)
Wirasm Apr 24, 2026
2c15439
docs/skill: general hardening — fix inaccuracies, fill workflow/CLI/e…
Wirasm Apr 24, 2026
ad13d83
chore(workflows): switch default Opus pin to opus[1m] alias (#1395)
Wirasm Apr 24, 2026
e56d904
Merge upstream dev into workflow runtime inputs
gemmawood Apr 24, 2026
d0d0c96
test(workflows): add unit tests for resolveInputs, applyInputsToStrin…
gemmawood Apr 27, 2026
026fd66
chore(schemas): remove verbose JSDoc from workflowInputSchema and inp…
gemmawood Apr 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions packages/cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ async function main(): Promise<number> {
'download-only': { type: 'boolean' },
scope: { type: 'string' },
force: { type: 'boolean' },
set: { type: 'string', multiple: true },
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Document --set in CLI usage.

The flag is parsed, but printUsage() does not list it or show an example, so the new runtime-input feature is hard to discover from archon help.

📝 Proposed help text update
   --json                     Output machine-readable JSON (for workflow list)
+  --set KEY=VALUE            Set a workflow runtime input; repeatable for multiple inputs
   --workflow <name>          Workflow to run for 'continue' (default: archon-assist)
   archon workflow run plan --cwd /path/to/repo "Add dark mode"
+  archon workflow run plan --set TICKET=123 --set PRIORITY=high "Add dark mode"
   archon workflow run implement --branch feature-auth "Implement auth"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/cli/src/cli.ts` at line 207, The CLI flag --set is defined (set: {
type: 'string', multiple: true }) but not documented in the help text; update
the function printUsage() to include a concise entry for --set (showing it
accepts multiple key=value pairs or file references), add a short description of
its purpose (runtime input override), and include an example invocation
demonstrating multiple uses (e.g. --set key1=val1 --set key2=val2). Ensure the
help output formatting matches other flags in printUsage() so --set appears in
the options list and examples.

},
allowPositionals: true,
strict: false, // Allow unknown flags to pass through
Expand Down Expand Up @@ -352,13 +353,34 @@ async function main(): Promise<number> {
);
return 1;
}
// Parse --set KEY=VALUE flags into a runtime inputs map
const setFlags = values.set as string[] | undefined;
let inputs: Record<string, string> | undefined;
if (setFlags && setFlags.length > 0) {
inputs = {};
for (const flag of setFlags) {
const eqIndex = flag.indexOf('=');
if (eqIndex === -1) {
console.error(`Error: --set flag must be in KEY=VALUE format, got: ${flag}`);
return 1;
}
const key = flag.slice(0, eqIndex).trim();
const value = flag.slice(eqIndex + 1);
if (!key) {
console.error(`Error: --set flag has empty key: ${flag}`);
return 1;
}
inputs[key] = value;
}
}
const options = {
branchName,
fromBranch,
noWorktree,
resume: resumeFlag,
quiet: values.quiet as boolean | undefined,
verbose: values.verbose as boolean | undefined,
inputs,
};
await workflowRunCommand(effectiveCwd, workflowName, userMessage, options);
break;
Expand Down
12 changes: 11 additions & 1 deletion packages/cli/src/commands/workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ export interface WorkflowRunOptions {
verbose?: boolean;
/** Platform conversation ID (e.g. `cli-{ts}-{rand}`), NOT a DB UUID. */
conversationId?: string;
/**
* Runtime input values for workflows that declare an `inputs:` block.
* Populated from `--set KEY=VALUE` CLI flags.
*/
inputs?: Record<string, string>;
}

/**
Expand Down Expand Up @@ -655,7 +660,12 @@ export async function workflowRunCommand(
workflow,
userMessage,
conversation.id,
codebase?.id
codebase?.id,
undefined,
undefined,
undefined,
undefined,
options.inputs
);
} finally {
unsubscribe?.();
Expand Down
55 changes: 44 additions & 11 deletions packages/core/src/orchestrator/orchestrator-agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,8 @@ async function dispatchOrchestratorWorkflow(
codebase: Codebase,
workflow: WorkflowDefinition,
userMessage: string,
isolationHints?: HandleMessageContext['isolationHints']
isolationHints?: HandleMessageContext['isolationHints'],
inputs?: Record<string, string>
): Promise<void> {
// Auto-attach project to conversation
await db.updateConversation(conversation.id, {
Expand Down Expand Up @@ -293,7 +294,12 @@ async function dispatchOrchestratorWorkflow(
workflow,
userMessage,
conversation.id,
codebase.id
codebase.id,
undefined,
undefined,
undefined,
undefined,
inputs
);
} else if (workflow.interactive) {
// Interactive workflows run in foreground so output stays in the user's conversation
Expand All @@ -305,7 +311,12 @@ async function dispatchOrchestratorWorkflow(
workflow,
userMessage,
conversation.id,
codebase.id
codebase.id,
undefined,
undefined,
undefined,
undefined,
inputs
);
} else {
await dispatchBackgroundWorkflow(
Expand All @@ -318,6 +329,7 @@ async function dispatchOrchestratorWorkflow(
codebaseId: codebase.id,
availableWorkflows: [workflow],
isolationHints,
inputs,
},
workflow
);
Expand All @@ -331,7 +343,12 @@ async function dispatchOrchestratorWorkflow(
workflow,
userMessage,
conversation.id,
codebase.id
codebase.id,
undefined,
undefined,
undefined,
undefined,
inputs
);
}
}
Expand Down Expand Up @@ -530,8 +547,14 @@ export async function handleMessage(
message: string,
context?: HandleMessageContext
): Promise<void> {
const { issueContext, threadContext, parentConversationId, isolationHints, attachedFiles } =
context ?? {};
const {
issueContext,
threadContext,
parentConversationId,
isolationHints,
attachedFiles,
inputs,
} = context ?? {};
try {
getLog().debug({ conversationId }, 'orchestrator_message_received');

Expand Down Expand Up @@ -646,14 +669,20 @@ export async function handleMessage(
return;
}
await platform.sendMessage(conversationId, `▶️ Resuming **${workflow.name}**...`);
const pausedResumeInputs = pausedRun.metadata?.resolved_inputs as
| Record<string, string>
| undefined;
await dispatchOrchestratorWorkflow(
platform,
conversationId,
conversation,
codebase,
workflow,
pausedRun.user_message,
isolationHints
isolationHints,
pausedResumeInputs && Object.keys(pausedResumeInputs).length > 0
? pausedResumeInputs
: undefined
);
getLog().info(
{ conversationId, workflowRunId: pausedRun.id, workflowName: pausedRun.workflow_name },
Expand Down Expand Up @@ -723,7 +752,8 @@ export async function handleMessage(
conversation,
result.workflow.definition,
result.workflow.args ?? message,
isolationHints
isolationHints,
inputs
);
}
return;
Expand Down Expand Up @@ -1392,7 +1422,8 @@ async function handleWorkflowRunCommand(
conversation: Conversation,
workflow: WorkflowDefinition,
userMessage: string,
isolationHints?: HandleMessageContext['isolationHints']
isolationHints?: HandleMessageContext['isolationHints'],
inputs?: Record<string, string>
): Promise<void> {
// Check if conversation has a project
if (conversation.codebase_id) {
Expand All @@ -1412,7 +1443,8 @@ async function handleWorkflowRunCommand(
codebase,
workflow,
userMessage,
isolationHints
isolationHints,
inputs
);
return;
}
Expand Down Expand Up @@ -1489,7 +1521,8 @@ async function handleWorkflowRunCommand(
codebase,
resolvedWorkflow,
userMessage,
isolationHints
isolationHints,
inputs
);
return;
}
Expand Down
5 changes: 4 additions & 1 deletion packages/core/src/orchestrator/orchestrator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,8 @@ export interface WorkflowRoutingContext {
* Hints for isolation environment (PR review context, etc.)
*/
readonly isolationHints?: IsolationHints;
/** Runtime input values for workflows declared with an `inputs:` block. */
readonly inputs?: Record<string, string>;
}

/**
Expand Down Expand Up @@ -374,7 +376,8 @@ export async function dispatchBackgroundWorkflow(
ctx.issueContext,
isolationContext,
ctx.conversationDbId,
preCreatedRun
preCreatedRun,
ctx.inputs
);
// Surface workflow output to parent conversation as a result card
if ('paused' in result) {
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ export interface HandleMessageContext {
readonly parentConversationId?: string;
readonly isolationHints?: IsolationHints;
readonly attachedFiles?: AttachedFile[];
/** Runtime input values for workflows declared with an `inputs:` block. */
readonly inputs?: Record<string, string>;
}

export interface Codebase {
Expand Down
8 changes: 6 additions & 2 deletions packages/server/src/routes/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1709,7 +1709,7 @@ export function registerApiRoutes(
return apiError(c, 400, 'Invalid workflow name');
}
try {
const { conversationId, message } = getValidatedBody(c, runWorkflowBodySchema);
const { conversationId, message, inputs } = getValidatedBody(c, runWorkflowBodySchema);
// Persist user message and register DB ID (same as message endpoint)
let conv: Awaited<ReturnType<typeof conversationDb.findConversationByPlatformId>> = null;
try {
Expand Down Expand Up @@ -1737,7 +1737,11 @@ export function registerApiRoutes(
}

const fullMessage = `/workflow run ${workflowName} ${message}`;
const result = await dispatchToOrchestrator(conversationId, fullMessage);
const result = await dispatchToOrchestrator(
conversationId,
fullMessage,
inputs ? { inputs } : undefined
);
return c.json(result);
} catch (error) {
getLog().error({ err: error }, 'run_workflow_failed');
Expand Down
6 changes: 6 additions & 0 deletions packages/server/src/routes/schemas/workflow.schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,12 @@ export const runWorkflowBodySchema = z
.object({
conversationId: z.string(),
message: z.string(),
/**
* Runtime input values for workflows that declare an `inputs:` block.
* Keys must match input names declared in the workflow YAML; values are strings.
* Unrecognised keys are passed through and ignored by workflows that don't declare them.
*/
inputs: z.record(z.string()).optional(),
})
.openapi('RunWorkflowBody');

Expand Down
Loading