Skip to content

Commit efcd0be

Browse files
committed
merge: resolve conflicts with dev after Copilot (#1505) landed
Both PRs added a community provider, so they collide on: - providers/registry.ts (import + register block) - providers/registry.test.ts (per-provider describe blocks) - docs-web ai-assistants.md (frontmatter description) - bun.lock + bundled-defaults.generated.ts (regenerated post-merge) Resolution: kept both providers — opencode + copilot — alongside pi. Type-check + @archon/providers tests pass clean. Conflicts surfaced when #1505 squash-merged into dev as 6ccfb4b.
2 parents 981e24f + 6ccfb4b commit efcd0be

89 files changed

Lines changed: 7603 additions & 449 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.archon/maintainer-standup/direction.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,24 @@ This file is **committed and shared by all maintainers**. Edit deliberately —
2626
- **Not opinionated about the dev environment.** No mandatory editor integrations, framework lock-in, or Docker requirement beyond what users opt into.
2727
- **Not a workflow marketplace.** Bundled workflows are reference patterns; Archon is not aiming to be a hub for third-party workflow distribution.
2828

29+
## Community providers
30+
31+
Archon ships built-in providers for Claude (`@anthropic-ai/claude-agent-sdk`) and Codex (`@openai/codex-sdk`). Pi (`@mariozechner/pi-coding-agent`) is the reference community provider and sets the pattern others should follow.
32+
33+
**Acceptance criteria** for new community providers:
34+
35+
- **Coding-agent SDK only.** The provider must wrap an existing coding-agent SDK — one that handles file edits, tool use, multi-turn sessions, and planning. Raw LLM API integrations (`chat.completions`-style) are out of scope. Pi already covers ~20 LLM backends via one harness, so single-model wrappers duplicate work that is already done.
36+
- **Match the Pi pattern.** Structure mirrors `packages/providers/src/community/pi/` — provider class implementing `IAgentProvider`, options translator, event bridge, capability matrix, registered with `builtIn: false`. Tests at parity with the Pi suite (config, options-translator, event-bridge, provider, session-resolver as the baseline).
37+
- **Docs page.** Add the provider to `packages/docs-web/src/content/docs/getting-started/ai-assistants.md` with setup, capability matrix, and supported config keys.
38+
39+
**Maintenance policy:**
40+
41+
- We accept any provider that meets the criteria above. There is no cap.
42+
- The contributor and the community maintain the provider. Archon maintainers do not own upstream-SDK breaks for community providers.
43+
- A community provider that goes non-functional — CI broken, upstream SDK gone, no maintainer response — is marked deprecated and removed in the next minor release unless someone from the community submits a fix.
44+
45+
When citing this policy in a PR comment: `direction.md §community-providers`.
46+
2947
## Open questions (no stance yet)
3048

3149
These are direction calls we haven't made. PRs that touch these areas should surface the question for explicit decision rather than be silently accepted or rejected. The workflow may add to this list as new questions appear.

.archon/workflows/defaults/archon-refactor-safely.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ nodes:
117117
- Rationale for each grouping (cohesion, shared dependencies)
118118
depends_on: [scan-scope]
119119
context: fresh
120-
denied_tools: [Write, Edit, Bash]
120+
denied_tools: [Edit, Bash]
121121

122122
# ═══════════════════════════════════════════════════════════════
123123
# PHASE 3: PLAN REFACTOR — Ordered task list with rollback strategy
@@ -199,7 +199,7 @@ nodes:
199199
- Format: `bun run format:check`
200200
depends_on: [analyze-impact]
201201
context: fresh
202-
denied_tools: [Write, Edit, Bash]
202+
denied_tools: [Edit, Bash]
203203

204204
# ═══════════════════════════════════════════════════════════════
205205
# PHASE 4: EXECUTE REFACTOR — Implements the plan with guardrails

.archon/workflows/maintainer/marketplace-pr-review-and-merge.yaml

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,10 +276,19 @@ nodes:
276276
# NODE 8: DECIDE — deterministic decision logic (inline Bun script)
277277
# ═══════════════════════════════════════════════════════════════
278278

279+
- id: persist-ai-review
280+
# bash bridge: in a bash node $ai-review.output is shell-quoted, so a failed
281+
# or non-JSON AI verdict (e.g. provider/API error) is written verbatim to a
282+
# file instead of being injected raw into the decide script — where it would
283+
# otherwise be invalid JS and crash the run at parse time.
284+
depends_on: [ai-review]
285+
bash: |
286+
printf '%s' $ai-review.output > "$ARTIFACTS_DIR/ai-review.json"
287+
279288
- id: decide
280289
runtime: bun
281290
timeout: 10000
282-
depends_on: [ai-review, fetch-pr-metadata]
291+
depends_on: [persist-ai-review, fetch-pr-metadata]
283292
script: |
284293
import { readFileSync, writeFileSync } from 'node:fs';
285294
import { resolve } from 'node:path';
@@ -292,9 +301,26 @@ nodes:
292301
const scopeResult = readFileSync(resolve(artifactsDir, '.scope-result'), 'utf8').trim();
293302
294303
const scanResult = $security-scan.output;
295-
const aiReview = $ai-review.output;
296304
const schemaResult = $validate-schema.output;
297305
306+
// ai-review verdict is read from a file (written by the persist-ai-review
307+
// bash bridge) so a failed or non-JSON AI output can't crash this script.
308+
let aiReview: { recommendation?: string; reasoning?: string };
309+
try {
310+
const aiReviewRaw = readFileSync(resolve(artifactsDir, 'ai-review.json'), 'utf8').trim();
311+
aiReview = JSON.parse(aiReviewRaw) as { recommendation?: string; reasoning?: string };
312+
if (typeof aiReview.recommendation !== 'string') {
313+
throw new Error('missing "recommendation" field');
314+
}
315+
} catch (err) {
316+
console.error(
317+
`decide: ai-review did not produce a valid structured verdict (${(err as Error).message}). ` +
318+
`The AI review step likely failed or was unavailable (e.g. provider/API quota). ` +
319+
`Failing safe — not auto-merging. Re-run the marketplace auto-review once the provider is available.`,
320+
);
321+
process.exit(1);
322+
}
323+
298324
const author = prMeta.author.login;
299325
const isDraft = prMeta.isDraft;
300326
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# E2E manual abort test — GitHub Copilot community provider
2+
# Verifies: Ctrl-C propagates through the bridge to session.abort() and
3+
# sendAndWait unwinds cleanly without dangling listeners.
4+
# Manual: start, wait for streaming to begin, press Ctrl-C. Not for CI.
5+
name: e2e-copilot-abort
6+
description: 'Manual test: start, then Ctrl-C. Verifies abort wiring.'
7+
provider: copilot
8+
model: gpt-5-mini
9+
10+
nodes:
11+
- id: long
12+
prompt: 'Count slowly from 1 to 200, one number per line, with a brief phrase after each number explaining its mathematical significance. Do not skip any numbers.'
13+
effort: low
14+
idle_timeout: 120000
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
# E2E smoke test — Copilot provider, every CI-compatible node type
2+
# Covers: prompt, command, loop (AI node types) + bash, script bun/uv
3+
# (deterministic node types) + depends_on / when / trigger_rule / $nodeId.output
4+
# (DAG features) + Copilot-specific options: effort, allowed_tools,
5+
# output_format (best-effort JSON via prompt augment + 2-tier parser).
6+
# Skipped: `approval:` — pauses for human input, incompatible with CI.
7+
# Auth: `gh auth login` OR `COPILOT_GITHUB_TOKEN`.
8+
# To use `GH_TOKEN` / `GITHUB_TOKEN`, also set `assistantConfig.useLoggedInUser: false`.
9+
# Requires an active GitHub Copilot subscription.
10+
name: e2e-copilot-all-nodes-smoke
11+
description: 'Copilot provider smoke across every CI-compatible node type plus Copilot-specific options.'
12+
provider: copilot
13+
model: gpt-5-mini
14+
15+
nodes:
16+
# ─── AI node types ──────────────────────────────────────────────────────
17+
18+
# 1. prompt: inline prompt + effort + allowed_tools (no tool calls).
19+
# Verifies reasoningEffort and availableTools=[] reach the SDK.
20+
- id: prompt-node
21+
prompt: "Reply with exactly the single word 'ok' and nothing else."
22+
allowed_tools: []
23+
effort: low
24+
idle_timeout: 30000
25+
26+
# 2. command: named command file (.archon/commands/e2e-echo-command.md).
27+
# The command echoes back $ARGUMENTS (the workflow invocation message).
28+
- id: command-node
29+
command: e2e-echo-command
30+
allowed_tools: []
31+
idle_timeout: 30000
32+
33+
# 3. loop: iterative AI prompt until completion signal.
34+
# Bounded by max_iterations: 2 so a misbehaving model can't hang CI.
35+
- id: loop-node
36+
loop:
37+
prompt: "Reply with exactly 'DONE' and nothing else."
38+
until: 'DONE'
39+
max_iterations: 2
40+
allowed_tools: []
41+
effort: low
42+
idle_timeout: 60000
43+
44+
# 4. output_format: Copilot's best-effort structured output path
45+
# (prompt augmented with schema + 2-tier JSON parser on result text).
46+
# Unique to Copilot/Pi vs. Claude/Codex native JSON mode — only an
47+
# E2E test catches "real model drifted around the schema".
48+
- id: structured-node
49+
prompt: |
50+
Return a JSON object with two fields, no fences and no prose:
51+
- "status": always "ok" (string)
52+
- "value": always 42 (number)
53+
allowed_tools: []
54+
effort: low
55+
idle_timeout: 30000
56+
output_format:
57+
type: object
58+
properties:
59+
status:
60+
type: string
61+
value:
62+
type: number
63+
required: [status, value]
64+
65+
# ─── Deterministic node types (no AI) ───────────────────────────────────
66+
67+
# 5. bash: shell script with JSON output (enables $nodeId.output.status
68+
# dot-access downstream).
69+
- id: bash-json-node
70+
bash: 'echo ''{"status":"ok"}'''
71+
72+
# 6. script: bun (TypeScript/JavaScript runtime)
73+
- id: script-bun-node
74+
script: echo-args
75+
runtime: bun
76+
timeout: 30000
77+
78+
# 7. script: uv (Python runtime)
79+
- id: script-python-node
80+
script: echo-py
81+
runtime: uv
82+
timeout: 30000
83+
84+
# ─── DAG features ───────────────────────────────────────────────────────
85+
86+
# 8. depends_on + $nodeId.output substitution
87+
- id: downstream
88+
bash: "echo 'downstream got: $prompt-node.output'"
89+
depends_on: [prompt-node]
90+
91+
# 9. when: conditional (JSON dot-access on bash JSON output)
92+
- id: gated
93+
bash: "echo 'gated-ok'"
94+
depends_on: [bash-json-node]
95+
when: "$bash-json-node.output.status == 'ok'"
96+
97+
# 10. when: conditional on AI structured output (proves output_format
98+
# parsed and dot-access works on the resulting object).
99+
- id: structured-check
100+
bash: "echo \"structured.status=$structured-node.output.status\""
101+
depends_on: [structured-node]
102+
when: "$structured-node.output.status == 'ok'"
103+
104+
# 11. trigger_rule: merge multiple deps (all_success semantics)
105+
- id: merge
106+
bash: "echo 'merge-ok'"
107+
depends_on:
108+
[downstream, gated, structured-check, script-bun-node, script-python-node]
109+
trigger_rule: all_success
110+
111+
# ─── Final assertion ────────────────────────────────────────────────────
112+
113+
# 12. Verify every upstream node produced non-empty output, including
114+
# dot-access on the structured-output node (proves output_format
115+
# parsed and downstream consumers can index into it).
116+
# Note: value-equality on string fields is avoided on purpose —
117+
# shellQuote() wraps strings in literal single quotes, so a literal
118+
# `[ "$x" != "ok" ]` would always fail. Non-emptiness is the right
119+
# bar for a smoke; the `when:` gate on structured-check already
120+
# proved the value matched 'ok' to reach this node.
121+
- id: assert
122+
bash: |
123+
fail=0
124+
check() {
125+
local name="$1"
126+
local value="$2"
127+
if [ -z "$value" ]; then
128+
echo "FAIL: $name produced empty output"
129+
fail=1
130+
fi
131+
}
132+
check prompt-node "$prompt-node.output"
133+
check command-node "$command-node.output"
134+
check loop-node "$loop-node.output"
135+
check bash-json-node "$bash-json-node.output"
136+
check script-bun-node "$script-bun-node.output"
137+
check script-python-node "$script-python-node.output"
138+
check downstream "$downstream.output"
139+
check gated "$gated.output"
140+
check merge "$merge.output"
141+
check structured.status "$structured-node.output.status"
142+
check structured.value "$structured-node.output.value"
143+
144+
if [ "$fail" -eq 1 ]; then exit 1; fi
145+
echo "PASS: all node types + structured output verified"
146+
depends_on: [merge, loop-node, command-node]
147+
trigger_rule: all_success

.env.example

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ CLAUDE_USE_GLOBAL_AUTH=true
2626
# Then:
2727
# CLAUDE_BIN_PATH=$HOME/.local/bin/claude (native installer)
2828
# CLAUDE_BIN_PATH=$(npm root -g)/@anthropic-ai/claude-code/cli.js (npm alternative)
29+
# CLAUDE_BIN_PATH=$(npm root -g)/@anthropic-ai/claude-code-win32-x64 (Windows npm platform dir — auto-expanded to claude.exe)
2930
# CLAUDE_BIN_PATH=
3031

3132
# Codex Authentication (get from ~/.codex/auth.json after running 'codex login')
@@ -38,6 +39,18 @@ CODEX_REFRESH_TOKEN=
3839
CODEX_ACCOUNT_ID=
3940
# CODEX_BIN_PATH= # Optional: path to Codex native binary (binary builds only)
4041

42+
# GitHub Copilot (community provider — @github/copilot-sdk)
43+
# Requires an active GitHub Copilot subscription. By default, Archon uses
44+
# the credentials you configured via the Copilot CLI (`copilot login`).
45+
# Generic GH_TOKEN / GITHUB_TOKEN (declared below) are intentionally NOT
46+
# picked up — classic PATs lack Copilot entitlement and would fail. To
47+
# opt back into env-token auth, set `useLoggedInUser: false` in
48+
# `.archon/config.yaml`. Setting COPILOT_GITHUB_TOKEN is treated as
49+
# explicit Copilot intent and always wins.
50+
#
51+
# COPILOT_GITHUB_TOKEN= # Copilot-scoped PAT (always wins when set)
52+
# COPILOT_BIN_PATH= # Optional: path to Copilot CLI binary (binary builds only)
53+
4154
# Pi (community provider — @mariozechner/pi-coding-agent)
4255
# One adapter, ~20 LLM backends. Archon's Pi adapter picks up credentials
4356
# you've already configured via the Pi CLI (`pi /login` writes to
@@ -63,7 +76,7 @@ CODEX_ACCOUNT_ID=
6376
# before the container starts (Pi reads it on each file path lookup).
6477
# PI_CODING_AGENT_DIR=/.archon/pi
6578

66-
# Default AI Assistant (must match a registered provider, e.g. claude, codex, pi)
79+
# Default AI Assistant (must match a registered provider, e.g. claude, codex, copilot, pi)
6780
# Used for new conversations when no codebase specified — errors on unknown values
6881
DEFAULT_AI_ASSISTANT=claude
6982

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
- **`assistants.opencode` provider**: community provider that runs OpenCode as an embedded runtime, with per-node agent materialization, multi-agent sessions, structured output, token usage, and multi-agent MCP tool execution (#1703).
1313
- MCP server support for Codex workflow nodes via the shared `loadMcpConfig` module — pass `mcp: <path>` on a Codex node and the config is translated to Codex's `mcp_servers` overrides at runtime. MCP client errors are surfaced to the workflow author as `system` chunks when MCP is explicitly configured for the node (#1459).
1414

15+
### Fixed
16+
17+
- **`workflow approve/resume/reject` no longer fail with "Workflow not found" when the run's working path is a worktree or workspace clone.** Resume, approve, and reject now use `codebase.default_cwd` for workflow YAML discovery, falling back to `working_path` when no codebase record is found. Fixes #1663 (#1743).
18+
1519
## [0.3.12] - 2026-05-14
1620

1721
Orchestrator prompt-cache fix, SDK termination edge cases, marketplace expansion, and broad workflow fixes.

CLAUDE.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,10 @@ assistants:
488488
- user # User-level ~/.claude/ (included in default; omit both to restrict to project-only)
489489
claudeBinaryPath: /absolute/path/to/claude # Optional: Claude Code executable.
490490
# Native binary (curl installer at
491-
# ~/.local/bin/claude) or npm cli.js.
491+
# ~/.local/bin/claude), npm cli.js, or
492+
# the npm platform-package directory
493+
# (e.g. @anthropic-ai/claude-code-win32-x64)
494+
# which is auto-expanded to claude/claude.exe.
492495
# Required in compiled binaries if
493496
# CLAUDE_BIN_PATH env var is not set.
494497
codex:
@@ -585,13 +588,23 @@ curl http://localhost:3637/api/conversations/<conversationId>/messages
585588

586589
## Development Guidelines
587590

591+
### UI and Visual Design
592+
593+
All UI changes — production web (`packages/web/`), experiments (`packages/web/src/experiments/`), the docs site, marketing surfaces, and any future visual surface — must align with the Archon brand foundation.
594+
595+
- **Canonical brand guide:** https://archon.diy/brand/ (source: `packages/docs-web/src/content/docs/brand/index.md` + `packages/docs-web/public/brand/foundation.html`).
596+
- **Use brand tokens, not ad-hoc values.** Colors, gradients, surfaces, and typography must come from the established design tokens (`packages/web/src/index.css`) or the brand guide. Don't hard-code hex values that aren't in the system.
597+
- **Introducing a new visual token** (color, font, radius, spacing) means updating both the token source and the brand guide. Don't fork the palette per package.
598+
- **When in doubt, consult the brand guide first** before inventing new visual treatments. Open a discussion if the guide doesn't cover your case.
599+
588600
### When Creating New Features
589601

590602
**Quick reference:**
591603
- **Platform Adapters**: Implement `IPlatformAdapter`, handle auth, polling/webhooks
592604
- **AI Providers**: Implement `IAgentProvider`, session management, streaming
593605
- **Slash Commands**: Add to command-handler.ts, update database, no AI
594606
- **Database Operations**: Use `IDatabase` interface (supports PostgreSQL and SQLite via adapters)
607+
- **Plan insertion points**: Use stable text anchors (e.g., "after the `it('throws on ...')` test block"), never raw line numbers — line numbers drift on every preceding edit.
595608

596609
### SDK Type Patterns
597610

0 commit comments

Comments
 (0)