pantheon-opencode 1.6.0-beta.6 → 1.6.0-beta.7
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/AGENTS.md +27 -820
- package/CHANGELOG.md +116 -0
- package/README.md +132 -39
- package/README.pt-BR.md +47 -23
- package/package.json +6 -1
- package/plugin.json +1 -1
- package/pyproject.toml +3 -1
- package/scripts/build-agents-md.mjs +162 -28
- package/scripts/doctor.mjs +104 -22
- package/scripts/install/agents-md.mjs +2 -2
- package/scripts/install/config-migration.mjs +154 -7
- package/scripts/install/opencode.mjs +202 -40
- package/scripts/install/shared.mjs +115 -7
- package/scripts/install/strings.mjs +8 -0
- package/scripts/probe-pantheon-cost.mjs +16 -3
- package/scripts/sync-artifacts.mjs +2 -2
- package/scripts/test-opencode-v2-sandbox.sh +820 -86
- package/skills-lock.json +6 -5
- package/src/agents/aphrodite.md +17 -28
- package/src/agents/demeter.md +8 -18
- package/src/agents/hephaestus.md +10 -11
- package/src/agents/hermes.md +15 -30
- package/src/agents/prometheus.md +16 -10
- package/src/agents/talos.md +4 -1
- package/src/agents/themis.md +4 -3
- package/src/agents/zeus.md +16 -280
- package/src/instructions/agent-return-format.instructions.md +4 -50
- package/src/instructions/backend-standards.instructions.md +2 -0
- package/src/instructions/frontend-standards.instructions.md +3 -1
- package/src/instructions/memory-protocol.instructions.md +12 -121
- package/src/instructions/zeus-anti-stall.instructions.md +8 -111
- package/src/instructions/zeus-communication-rules.instructions.md +0 -1
- package/src/instructions/zeus-memory-operations.instructions.md +26 -0
- package/src/instructions/zeus-timeout-retry.instructions.md +9 -130
- package/src/pantheon/secret-scanner.ts +489 -0
- package/src/pantheon/v2-delegation-enforce.ts +86 -0
- package/src/pantheon/v2-execute-after.ts +186 -0
- package/src/pantheon/v2-unsupported.mjs +0 -23
- package/src/plugin-v2.ts +254 -102
- package/src/plugins/pantheon-hooks.ts +82 -35
- package/src/plugins/tui/README.md +14 -0
- package/src/plugins/tui/dist/tui.d.ts +11 -8
- package/src/plugins/tui/dist/tui.d.ts.map +1 -1
- package/src/plugins/tui/dist/tui.js +102 -62
- package/src/plugins/tui/package-lock.json +30 -33
- package/src/plugins/tui/package.json +5 -1
- package/src/plugins/tui/src/index.tsx +163 -107
- package/src/plugins/tui/tsdown.config.ts +17 -0
- package/src/routing.yml +12 -9
- package/src/skills/README.md +9 -4
- package/src/skills/agent-coordination/SKILL.md +13 -83
- package/src/skills/artifact-management/SKILL.md +12 -5
- package/src/skills/auto-continue/SKILL.md +37 -27
- package/src/skills/code-review-checklist/SKILL.md +5 -5
- package/src/skills/council-synthesis/SKILL.md +29 -0
- package/src/skills/tdd-with-agents/SKILL.md +18 -10
- package/scripts/hooks/scan-secrets.sh +0 -97
- package/src/instructions/zeus-council-synthesis.instructions.md +0 -299
|
@@ -53,16 +53,7 @@ import { homedir } from 'node:os'
|
|
|
53
53
|
import { dirname, join } from 'node:path'
|
|
54
54
|
import { fileURLToPath } from 'node:url'
|
|
55
55
|
import type { TuiPlugin, TuiPluginApi, TuiPluginModule } from '@opencode-ai/plugin/tui'
|
|
56
|
-
import {
|
|
57
|
-
createEffect,
|
|
58
|
-
createMemo,
|
|
59
|
-
createResource,
|
|
60
|
-
createSignal,
|
|
61
|
-
For,
|
|
62
|
-
onCleanup,
|
|
63
|
-
onMount,
|
|
64
|
-
Show,
|
|
65
|
-
} from 'solid-js'
|
|
56
|
+
import { createMemo, createResource, createSignal, For, onCleanup, onMount, Show } from 'solid-js'
|
|
66
57
|
|
|
67
58
|
// ─── Double-load guard (mirrors src/pantheon/plugin-once.ts) ───────────────
|
|
68
59
|
// Opencode merges global + project plugin paths, so this module can load twice
|
|
@@ -749,12 +740,27 @@ async function loadConfig(configPath: string | undefined): Promise<UsageBarConfi
|
|
|
749
740
|
}
|
|
750
741
|
}
|
|
751
742
|
|
|
752
|
-
/** Vendored usage-bar plugin init: reads config, seeds the kv cache,
|
|
753
|
-
* the
|
|
743
|
+
/** Vendored usage-bar plugin init: reads config, seeds the kv cache, registers
|
|
744
|
+
* the `app_bottom` slot, then starts the poll loop. Skips everything (and
|
|
754
745
|
* registers nothing) when no provider is enabled — mirrors the standalone
|
|
755
746
|
* plugin's early return. */
|
|
756
747
|
async function setupUsageBar(api: TuiPluginApi) {
|
|
748
|
+
let disposed = false
|
|
749
|
+
let clockInterval: ReturnType<typeof setInterval> | undefined
|
|
750
|
+
const pollTimeouts = new Set<ReturnType<typeof setTimeout>>()
|
|
751
|
+
|
|
752
|
+
// This optional feature owns timers independently from the sidebar. A slow
|
|
753
|
+
// config read may finish after shutdown, so register cleanup before awaiting
|
|
754
|
+
// anything and check the disposed flag at every async boundary.
|
|
755
|
+
api.lifecycle.onDispose(() => {
|
|
756
|
+
disposed = true
|
|
757
|
+
if (clockInterval !== undefined) clearInterval(clockInterval)
|
|
758
|
+
for (const timeout of pollTimeouts) clearTimeout(timeout)
|
|
759
|
+
pollTimeouts.clear()
|
|
760
|
+
})
|
|
761
|
+
|
|
757
762
|
const config = await loadConfig(api.state.path?.config)
|
|
763
|
+
if (disposed) return
|
|
758
764
|
if (api.state.path?.state) opencodeAuthFile = join(api.state.path.state, 'auth.json')
|
|
759
765
|
|
|
760
766
|
const enabled = providers.filter((p) => config.providers[p.id].enabled)
|
|
@@ -771,28 +777,6 @@ async function setupUsageBar(api: TuiPluginApi) {
|
|
|
771
777
|
const [byStatus, setByStatus] = createSignal<Record<string, StatusIndicator>>({})
|
|
772
778
|
const [now, setNow] = createSignal(Date.now())
|
|
773
779
|
|
|
774
|
-
setInterval(() => setNow(Date.now()), 1_000)
|
|
775
|
-
|
|
776
|
-
for (const p of enabled) {
|
|
777
|
-
const cfg = config.providers[p.id]
|
|
778
|
-
const poll = async () => {
|
|
779
|
-
// Fetch usage and status concurrently; status pages are CDN-backed and
|
|
780
|
-
// never throttle, so we poll them on the same cadence as usage.
|
|
781
|
-
const statusP = config.showStatus && p.statusUrl ? fetchStatus(p.statusUrl) : null
|
|
782
|
-
const [all, status] = await Promise.all([p.fetchUsage(cfg), statusP])
|
|
783
|
-
if (all) {
|
|
784
|
-
const windows = all.filter((w) => cfg.show[w.category] && w.resetsAt > Date.now())
|
|
785
|
-
setByProvider((prev) => ({ ...prev, [p.id]: windows }))
|
|
786
|
-
api.kv.set(`usage-bar.${p.id}.windows`, windows)
|
|
787
|
-
}
|
|
788
|
-
// `null` means the status fetch failed — keep the last known indicator.
|
|
789
|
-
if (status !== null) setByStatus((prev) => ({ ...prev, [p.id]: status }))
|
|
790
|
-
// Back off when the usage fetch failed (e.g. 429 — these endpoints throttle).
|
|
791
|
-
setTimeout(poll, all ? POLL_MS : POLL_MS * 3)
|
|
792
|
-
}
|
|
793
|
-
void poll()
|
|
794
|
-
}
|
|
795
|
-
|
|
796
780
|
api.slots.register({
|
|
797
781
|
order: 60,
|
|
798
782
|
slots: {
|
|
@@ -880,6 +864,46 @@ async function setupUsageBar(api: TuiPluginApi) {
|
|
|
880
864
|
},
|
|
881
865
|
},
|
|
882
866
|
})
|
|
867
|
+
|
|
868
|
+
// Start background work only once the optional UI slot was accepted. If
|
|
869
|
+
// registration fails, the outer fail-open handler can suppress it without
|
|
870
|
+
// leaving a timer or provider request running in the background.
|
|
871
|
+
clockInterval = setInterval(() => setNow(Date.now()), 1_000)
|
|
872
|
+
|
|
873
|
+
const schedulePoll = (run: () => void, delay: number) => {
|
|
874
|
+
if (disposed) return
|
|
875
|
+
const timeout = setTimeout(() => {
|
|
876
|
+
pollTimeouts.delete(timeout)
|
|
877
|
+
if (!disposed) run()
|
|
878
|
+
}, delay)
|
|
879
|
+
pollTimeouts.add(timeout)
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
for (const p of enabled) {
|
|
883
|
+
const cfg = config.providers[p.id]
|
|
884
|
+
const poll = async () => {
|
|
885
|
+
if (disposed) return
|
|
886
|
+
// Fetch usage and status concurrently; status pages are CDN-backed and
|
|
887
|
+
// never throttle, so we poll them on the same cadence as usage.
|
|
888
|
+
const statusP = config.showStatus && p.statusUrl ? fetchStatus(p.statusUrl) : null
|
|
889
|
+
const [all, status] = await Promise.all([p.fetchUsage(cfg), statusP])
|
|
890
|
+
if (disposed) return
|
|
891
|
+
if (all) {
|
|
892
|
+
const windows = all.filter((w) => cfg.show[w.category] && w.resetsAt > Date.now())
|
|
893
|
+
setByProvider((prev) => ({ ...prev, [p.id]: windows }))
|
|
894
|
+
api.kv.set(`usage-bar.${p.id}.windows`, windows)
|
|
895
|
+
}
|
|
896
|
+
// `null` means the status fetch failed — keep the last known indicator.
|
|
897
|
+
if (status !== null) setByStatus((prev) => ({ ...prev, [p.id]: status }))
|
|
898
|
+
// Back off when the usage fetch failed (e.g. 429 — these endpoints throttle).
|
|
899
|
+
schedulePoll(runPoll, all ? POLL_MS : POLL_MS * 3)
|
|
900
|
+
}
|
|
901
|
+
const runPoll = () => {
|
|
902
|
+
if (disposed) return
|
|
903
|
+
void poll().catch(() => schedulePoll(runPoll, POLL_MS * 3))
|
|
904
|
+
}
|
|
905
|
+
runPoll()
|
|
906
|
+
}
|
|
883
907
|
}
|
|
884
908
|
|
|
885
909
|
/* ─── Delegations (real-time panel) ────────────────────────
|
|
@@ -919,9 +943,9 @@ async function setupUsageBar(api: TuiPluginApi) {
|
|
|
919
943
|
* (filterDelegationsToSession) drops every non-focused report — the panel
|
|
920
944
|
* lists only the focused session.
|
|
921
945
|
*
|
|
922
|
-
* 3. REFRESH — session.created/updated/deleted/status events and
|
|
923
|
-
*
|
|
924
|
-
*
|
|
946
|
+
* 3. REFRESH — session.created/updated/deleted/status events and live-store
|
|
947
|
+
* subscription notifications re-fetch children; a 1s safety poll
|
|
948
|
+
* (oh-my-opencode-slim pattern) re-fetches
|
|
925
949
|
* children + re-reads the md so the panel updates EVEN without events.
|
|
926
950
|
*
|
|
927
951
|
* 4. DIAGNOSTICS — every re-fetch logs
|
|
@@ -947,8 +971,8 @@ async function setupUsageBar(api: TuiPluginApi) {
|
|
|
947
971
|
* panel renders EMPTY — zero rows, zero errors.
|
|
948
972
|
*
|
|
949
973
|
* The live tool-part lifecycle helpers below remain (tested, exported) but
|
|
950
|
-
* are no longer the source of truth: they
|
|
951
|
-
* re-
|
|
974
|
+
* are no longer the source of truth: they update the shared live store, whose
|
|
975
|
+
* subscribers re-trigger the children fetch. Everything is fail-open: a missing
|
|
952
976
|
* children API or directory yields [], and any unreadable input is skipped —
|
|
953
977
|
* never crash. */
|
|
954
978
|
|
|
@@ -2631,18 +2655,88 @@ function DelegationRow(props: {
|
|
|
2631
2655
|
|
|
2632
2656
|
/* ─── Main Sidebar View ──────────────────────────────────── */
|
|
2633
2657
|
|
|
2634
|
-
/** Plugin-level live delegation store shared
|
|
2635
|
-
*
|
|
2636
|
-
*
|
|
2637
|
-
* durable child list and also reads the map as an optimistic live source. */
|
|
2658
|
+
/** Plugin-level live delegation store shared by event subscriptions and View.
|
|
2659
|
+
* Mutations notify subscribers so the View can refresh its durable child
|
|
2660
|
+
* list while also reading the map as an optimistic live source. */
|
|
2638
2661
|
export type LiveDelegationStore = {
|
|
2639
2662
|
map: Map<string, LiveDelegationEntry>
|
|
2640
|
-
/**
|
|
2641
|
-
|
|
2642
|
-
/**
|
|
2663
|
+
/** Subscribe to invalidations consumed by the sidebar's refresh path. */
|
|
2664
|
+
subscribe: (listener: () => void) => () => void
|
|
2665
|
+
/** Notify sidebar subscribers after a live mutation. */
|
|
2643
2666
|
bump: () => void
|
|
2644
2667
|
}
|
|
2645
2668
|
|
|
2669
|
+
/** Create the observable live store shared by task events and the sidebar. */
|
|
2670
|
+
export function createLiveDelegationStore(): LiveDelegationStore {
|
|
2671
|
+
const listeners = new Set<() => void>()
|
|
2672
|
+
return {
|
|
2673
|
+
map: new Map<string, LiveDelegationEntry>(),
|
|
2674
|
+
subscribe(listener) {
|
|
2675
|
+
listeners.add(listener)
|
|
2676
|
+
return () => listeners.delete(listener)
|
|
2677
|
+
},
|
|
2678
|
+
bump() {
|
|
2679
|
+
for (const listener of listeners) listener()
|
|
2680
|
+
},
|
|
2681
|
+
}
|
|
2682
|
+
}
|
|
2683
|
+
|
|
2684
|
+
/** Register the live task-part events that invalidate the sidebar store. */
|
|
2685
|
+
export function registerLiveDelegationEvents(
|
|
2686
|
+
api: TuiPluginApi,
|
|
2687
|
+
liveStore: LiveDelegationStore,
|
|
2688
|
+
): () => void {
|
|
2689
|
+
const unsubLive: (() => void)[] = []
|
|
2690
|
+
|
|
2691
|
+
try {
|
|
2692
|
+
unsubLive.push(
|
|
2693
|
+
api.event.on('message.part.updated', (event: unknown) => {
|
|
2694
|
+
// SDK v2 shape: properties.part (v1 drift: properties.info.part).
|
|
2695
|
+
const props = ((event as { properties?: unknown } | null)?.properties ?? {}) as {
|
|
2696
|
+
part?: unknown
|
|
2697
|
+
info?: { part?: unknown }
|
|
2698
|
+
}
|
|
2699
|
+
const part = (props.part ?? props.info?.part) as DelegationToolPart | undefined
|
|
2700
|
+
if (part === undefined) return
|
|
2701
|
+
// beta.6: track the live tool call per child session (panel's
|
|
2702
|
+
// activity line). Fire-and-forget — display-only state.
|
|
2703
|
+
const sample = extractToolActivity(part as Parameters<typeof extractToolActivity>[0])
|
|
2704
|
+
if (sample !== null)
|
|
2705
|
+
trackToolActivity(latestToolActivity, sample.sessionID, sample.activity)
|
|
2706
|
+
if (reduceDelegationToolPart(liveStore.map, part)) liveStore.bump()
|
|
2707
|
+
}),
|
|
2708
|
+
)
|
|
2709
|
+
} catch {
|
|
2710
|
+
/* events API unavailable — children API + 1s poll cover the panel */
|
|
2711
|
+
}
|
|
2712
|
+
|
|
2713
|
+
try {
|
|
2714
|
+
unsubLive.push(
|
|
2715
|
+
api.event.on('message.part.removed', (event: unknown) => {
|
|
2716
|
+
// SDK v2 shape: properties.partID (v1 drift: properties.part.id).
|
|
2717
|
+
const props = ((event as { properties?: unknown } | null)?.properties ?? {}) as {
|
|
2718
|
+
partID?: string
|
|
2719
|
+
part?: { id?: string }
|
|
2720
|
+
}
|
|
2721
|
+
const partID = props.partID ?? props.part?.id
|
|
2722
|
+
if (partID !== undefined && removeDelegationEntry(liveStore.map, partID)) liveStore.bump()
|
|
2723
|
+
}),
|
|
2724
|
+
)
|
|
2725
|
+
} catch {
|
|
2726
|
+
/* events API unavailable — children API + 1s poll cover the panel */
|
|
2727
|
+
}
|
|
2728
|
+
|
|
2729
|
+
return () => {
|
|
2730
|
+
for (const unsub of unsubLive) {
|
|
2731
|
+
try {
|
|
2732
|
+
unsub()
|
|
2733
|
+
} catch {
|
|
2734
|
+
/* ignore */
|
|
2735
|
+
}
|
|
2736
|
+
}
|
|
2737
|
+
}
|
|
2738
|
+
}
|
|
2739
|
+
|
|
2646
2740
|
function View(props: {
|
|
2647
2741
|
api: TuiPluginApi
|
|
2648
2742
|
sessionID: string | undefined
|
|
@@ -2758,7 +2852,7 @@ function View(props: {
|
|
|
2758
2852
|
// means the event bus isn't delivering, and the 1s poll is what keeps
|
|
2759
2853
|
// the panel honest.
|
|
2760
2854
|
let eventRefreshCount = 0
|
|
2761
|
-
// In-flight guard: overlap between the poll, events and
|
|
2855
|
+
// In-flight guard: overlap between the poll, events and store subscribers
|
|
2762
2856
|
// collapses into one fetch — no duplicate work, no duplicate entries.
|
|
2763
2857
|
let delegationsInflight: Promise<void> | null = null
|
|
2764
2858
|
const refreshDelegations = () => {
|
|
@@ -2910,14 +3004,17 @@ function View(props: {
|
|
|
2910
3004
|
/* events API not available — the 1s poll still covers it */
|
|
2911
3005
|
}
|
|
2912
3006
|
|
|
2913
|
-
//
|
|
2914
|
-
//
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
3007
|
+
// Task-part events invalidate the same store the panel reads, refreshing
|
|
3008
|
+
// children when those events arrive and making optimistic live rows visible.
|
|
3009
|
+
cleanup.push(
|
|
3010
|
+
props.liveStore.subscribe(() => {
|
|
3011
|
+
eventRefreshCount += 1
|
|
3012
|
+
void refreshDelegations()
|
|
3013
|
+
}),
|
|
3014
|
+
)
|
|
3015
|
+
// Fetch the initial state; future updates use the store subscription above.
|
|
3016
|
+
eventRefreshCount += 1
|
|
3017
|
+
void refreshDelegations()
|
|
2921
3018
|
|
|
2922
3019
|
// Safety poll (OMO-slim pattern): 1s re-fetch children + md + tick the
|
|
2923
3020
|
// elapsed counter. Guarantees the panel updates even with zero events.
|
|
@@ -2935,8 +3032,8 @@ function View(props: {
|
|
|
2935
3032
|
// compacting, so after a compaction/attach the live map would start
|
|
2936
3033
|
// empty even though the session still holds the delegation tool parts.
|
|
2937
3034
|
// Re-seed the live map from the session's existing parts (in message
|
|
2938
|
-
// order) right after the subscriptions are registered — the
|
|
2939
|
-
//
|
|
3035
|
+
// order) right after the subscriptions are registered — the store
|
|
3036
|
+
// notification re-triggers the children fetch. Fail-open: if the SDK access is
|
|
2940
3037
|
// unavailable we skip — the children API + the 1s poll keep the panel
|
|
2941
3038
|
// working.
|
|
2942
3039
|
try {
|
|
@@ -3070,63 +3167,22 @@ const tui: TuiPlugin = (api, _options, _meta) => {
|
|
|
3070
3167
|
.catch(() => setVersion(null))
|
|
3071
3168
|
|
|
3072
3169
|
// Vendored feature (MIT): AI subscription usage gauges.
|
|
3073
|
-
|
|
3170
|
+
// Usage indicators are optional: a config, storage, or slot failure must
|
|
3171
|
+
// never turn an otherwise healthy TUI startup into an unhandled rejection.
|
|
3172
|
+
void setupUsageBar(api).catch(() => undefined)
|
|
3074
3173
|
|
|
3075
|
-
// ── Live delegation store (tool-part tracker
|
|
3076
|
-
// TRIGGER) ──
|
|
3174
|
+
// ── Live delegation store (tool-part tracker + sidebar invalidation) ──
|
|
3077
3175
|
// The Delegations panel's PRIMARY source is `api.client.session.children`
|
|
3078
3176
|
// (delegations-sidebar pattern) — `message.part.updated` may not fire for
|
|
3079
3177
|
// the native task tool on runtime 1.18.13 (the "(0)" symptom). The
|
|
3080
|
-
// tool-part lifecycle below still tracks `task` parts and
|
|
3081
|
-
//
|
|
3178
|
+
// tool-part lifecycle below still tracks `task` parts and invalidates the
|
|
3179
|
+
// store subscription consumed by the View: when part events DO arrive, the panel
|
|
3082
3180
|
// re-fetches children. Fail-open: if the events API is unavailable nothing
|
|
3083
3181
|
// subscribes and the 1s safety poll keeps the panel working (never crash).
|
|
3084
|
-
const
|
|
3085
|
-
const
|
|
3086
|
-
map: new Map<string, LiveDelegationEntry>(),
|
|
3087
|
-
version: liveVersion,
|
|
3088
|
-
bump: () => setLiveVersion((v) => v + 1),
|
|
3089
|
-
}
|
|
3090
|
-
const unsubLive: (() => void)[] = []
|
|
3091
|
-
|
|
3092
|
-
try {
|
|
3093
|
-
unsubLive.push(
|
|
3094
|
-
api.event.on('message.part.updated', (event: any) => {
|
|
3095
|
-
// SDK v2 shape: properties.part (v1 drift: properties.info.part).
|
|
3096
|
-
const props = (event?.properties ?? {}) as { part?: unknown; info?: { part?: unknown } }
|
|
3097
|
-
const part = (props.part ?? props.info?.part) as DelegationToolPart | undefined
|
|
3098
|
-
if (part === undefined) return
|
|
3099
|
-
// beta.6: track the live tool call per child session (panel's
|
|
3100
|
-
// activity line). Fire-and-forget — display-only state.
|
|
3101
|
-
const sample = extractToolActivity(part as any)
|
|
3102
|
-
if (sample !== null)
|
|
3103
|
-
trackToolActivity(latestToolActivity, sample.sessionID, sample.activity)
|
|
3104
|
-
if (reduceDelegationToolPart(liveStore.map, part)) liveStore.bump()
|
|
3105
|
-
}),
|
|
3106
|
-
)
|
|
3107
|
-
} catch {
|
|
3108
|
-
/* events API unavailable — children API + 1s poll cover the panel */
|
|
3109
|
-
}
|
|
3110
|
-
try {
|
|
3111
|
-
unsubLive.push(
|
|
3112
|
-
api.event.on('message.part.removed', (event: any) => {
|
|
3113
|
-
// SDK v2 shape: properties.partID (v1 drift: properties.part.id).
|
|
3114
|
-
const props = (event?.properties ?? {}) as { partID?: string; part?: { id?: string } }
|
|
3115
|
-
const partID = props.partID ?? props.part?.id
|
|
3116
|
-
if (partID !== undefined && removeDelegationEntry(liveStore.map, partID)) liveStore.bump()
|
|
3117
|
-
}),
|
|
3118
|
-
)
|
|
3119
|
-
} catch {
|
|
3120
|
-
/* events API unavailable — children API + 1s poll cover the panel */
|
|
3121
|
-
}
|
|
3182
|
+
const liveStore = createLiveDelegationStore()
|
|
3183
|
+
const unsubscribeLive = registerLiveDelegationEvents(api, liveStore)
|
|
3122
3184
|
api.lifecycle.onDispose(() => {
|
|
3123
|
-
|
|
3124
|
-
try {
|
|
3125
|
-
unsub()
|
|
3126
|
-
} catch {
|
|
3127
|
-
/* ignore */
|
|
3128
|
-
}
|
|
3129
|
-
}
|
|
3185
|
+
unsubscribeLive()
|
|
3130
3186
|
liveStore.map.clear()
|
|
3131
3187
|
})
|
|
3132
3188
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs'
|
|
2
|
+
import { appendFile, readdir, readFile } from 'node:fs/promises'
|
|
2
3
|
import { join } from 'node:path'
|
|
3
4
|
import { fileURLToPath } from 'node:url'
|
|
4
5
|
import solid from 'rolldown-plugin-solid'
|
|
@@ -9,6 +10,21 @@ const pkg = JSON.parse(readFileSync(join(__dirname, 'package.json'), 'utf8')) as
|
|
|
9
10
|
version: string
|
|
10
11
|
}
|
|
11
12
|
|
|
13
|
+
async function ensureDistArtifactsEndWithNewline(): Promise<void> {
|
|
14
|
+
const dist = join(__dirname, 'dist')
|
|
15
|
+
const entries = await readdir(dist, { withFileTypes: true })
|
|
16
|
+
|
|
17
|
+
await Promise.all(
|
|
18
|
+
entries
|
|
19
|
+
.filter((entry) => entry.isFile())
|
|
20
|
+
.map(async ({ name }) => {
|
|
21
|
+
const path = join(dist, name)
|
|
22
|
+
const contents = await readFile(path, 'utf8')
|
|
23
|
+
if (!contents.endsWith('\n')) await appendFile(path, '\n')
|
|
24
|
+
}),
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
12
28
|
export default defineConfig({
|
|
13
29
|
entry: { tui: 'src/index.tsx', server: 'src/server.ts' },
|
|
14
30
|
format: ['esm'],
|
|
@@ -33,4 +49,5 @@ export default defineConfig({
|
|
|
33
49
|
deps: {
|
|
34
50
|
neverBundle: [/^@opencode-ai\//, /^@opentui\//, /^solid-js/],
|
|
35
51
|
},
|
|
52
|
+
onSuccess: ensureDistArtifactsEndWithNewline,
|
|
36
53
|
})
|
package/src/routing.yml
CHANGED
|
@@ -133,7 +133,7 @@ agents:
|
|
|
133
133
|
- artifact-management
|
|
134
134
|
- context-compression
|
|
135
135
|
- auto-continue
|
|
136
|
-
-
|
|
136
|
+
- council-synthesis
|
|
137
137
|
athena:
|
|
138
138
|
role: Strategic planner
|
|
139
139
|
reasoning_effort: high
|
|
@@ -188,8 +188,6 @@ agents:
|
|
|
188
188
|
- pantheon_code_mode
|
|
189
189
|
skills:
|
|
190
190
|
- tdd-with-agents
|
|
191
|
-
- file-prompts
|
|
192
|
-
- streaming-patterns
|
|
193
191
|
- git-workflow-and-versioning
|
|
194
192
|
- incremental-implementation
|
|
195
193
|
aphrodite:
|
|
@@ -211,7 +209,7 @@ agents:
|
|
|
211
209
|
skills:
|
|
212
210
|
- tdd-with-agents
|
|
213
211
|
- visual-review-pipeline
|
|
214
|
-
-
|
|
212
|
+
- incremental-implementation
|
|
215
213
|
demeter:
|
|
216
214
|
role: Database specialist
|
|
217
215
|
reasoning_effort: medium
|
|
@@ -459,6 +457,8 @@ handoffs:
|
|
|
459
457
|
(priority-aware, budget-allocated), archive IMPL/REVIEW to 02-progress-log.md,
|
|
460
458
|
update _xref/index.md cross-references.'
|
|
461
459
|
|
|
460
|
+
# Canonical explicit fallback map for Zeus timeout/retry policy. Evaluate
|
|
461
|
+
# left-to-right; an absent agent entry means escalate (never guess a fallback).
|
|
462
462
|
fallback_chains:
|
|
463
463
|
apollo:
|
|
464
464
|
- athena
|
|
@@ -518,7 +518,7 @@ background_delegation:
|
|
|
518
518
|
prune_ttl_ms: 86400000 # 24h
|
|
519
519
|
read_only_agents: [apollo, gaia]
|
|
520
520
|
session_max: 3
|
|
521
|
-
retry_count: 1
|
|
521
|
+
retry_count: 1 # one retry after an initial transient failure; no retry loops
|
|
522
522
|
# ── O5: permission.task — glob rules for which subagents an agent may
|
|
523
523
|
# invoke (last matching rule wins; deny removes the agent from the delegate
|
|
524
524
|
# tool description entirely — prevents circular delegation at the
|
|
@@ -551,7 +551,10 @@ hashline:
|
|
|
551
551
|
full_auto:
|
|
552
552
|
enabled: false # opt-in: goal continuations are OFF by default
|
|
553
553
|
max_continuations: 25 # hard cap on goal continuations per session
|
|
554
|
-
#
|
|
555
|
-
#
|
|
556
|
-
#
|
|
557
|
-
#
|
|
554
|
+
# Full-auto only continues a task/scope explicitly authorized by the user.
|
|
555
|
+
# It never authorizes auth/security gates, risky data/schema or destructive
|
|
556
|
+
# operations, global config, broad permissions, production deploy, push, or merge.
|
|
557
|
+
# Low-risk execution is proportional: no forced plan/discovery/council/artifact
|
|
558
|
+
# for a bounded fix; use focused verification. Documentation mirror — runtime
|
|
559
|
+
# behavior is implemented in the plugin (GOAL_LOOP_DEFAULTS).
|
|
560
|
+
# Wave 3 (PR #46): one active goal per session (pending → in_progress → done).
|
package/src/skills/README.md
CHANGED
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
Skills are reference modules loaded on demand by agents. Each skill provides domain-specific knowledge — architecture patterns, security checklists, optimization strategies, and implementation guides. Agents load skills when their task matches the skill's description, keeping context focused and token-efficient.
|
|
6
6
|
|
|
7
|
-
There are **
|
|
7
|
+
There are **21 skills** divided into **5 domains**.
|
|
8
8
|
|
|
9
9
|
## Skills by Domain
|
|
10
10
|
|
|
11
11
|
### Domain 1: Orchestration & Workflow
|
|
12
12
|
|
|
13
13
|
1. **agent-coordination** — `agent-coordination/SKILL.md`
|
|
14
|
-
-
|
|
14
|
+
- Lightweight coordination for substantial tasks with multiple independent specialists.
|
|
15
15
|
- Used by: Zeus
|
|
16
16
|
|
|
17
17
|
2. **artifact-management** — `artifact-management/SKILL.md`
|
|
@@ -34,6 +34,10 @@ There are **20 skills** divided into **5 domains**.
|
|
|
34
34
|
- Pin session objectives to prevent scope creep across long multi-agent sessions.
|
|
35
35
|
- Used by: Zeus, Mnemosyne
|
|
36
36
|
|
|
37
|
+
- **council-synthesis** — `council-synthesis/SKILL.md`
|
|
38
|
+
- Full structured council workflow, loaded only for `/pantheon` or a material multi-perspective decision.
|
|
39
|
+
- Used by: Zeus
|
|
40
|
+
|
|
37
41
|
### Domain 2: Development Tools
|
|
38
42
|
|
|
39
43
|
7. **clonedeps** — `clonedeps/SKILL.md`
|
|
@@ -46,7 +50,7 @@ There are **20 skills** divided into **5 domains**.
|
|
|
46
50
|
|
|
47
51
|
9. **incremental-implementation** — `incremental-implementation/SKILL.md`
|
|
48
52
|
- Implement in thin vertical slices — one commit per task, testable, rollback-safe.
|
|
49
|
-
- Used by:
|
|
53
|
+
- Used by: Hermes, Aphrodite, Demeter, Prometheus, Talos
|
|
50
54
|
|
|
51
55
|
10. **loop-engineering** — `loop-engineering/SKILL.md`
|
|
52
56
|
- Iterative refinement pattern for complex problems — solve, review, improve, repeat in controlled cycles.
|
|
@@ -129,7 +133,7 @@ The `SKILL.md` file contains YAML frontmatter with `name`, `description`, and pl
|
|
|
129
133
|
| 6 | session-goal | Orchestration & Workflow | Zeus, Mnemosyne |
|
|
130
134
|
| 7 | clonedeps | Development Tools | Hermes, Hephaestus |
|
|
131
135
|
| 8 | git-workflow-and-versioning | Development Tools | Hermes, Prometheus, Iris |
|
|
132
|
-
| 9 | incremental-implementation | Development Tools |
|
|
136
|
+
| 9 | incremental-implementation | Development Tools | Hermes, Aphrodite, Demeter, Prometheus, Talos |
|
|
133
137
|
| 10 | loop-engineering | Development Tools | All agents |
|
|
134
138
|
| 11 | reflect | Development Tools | All agents |
|
|
135
139
|
| 12 | simplify | Development Tools | All agents |
|
|
@@ -141,6 +145,7 @@ The `SKILL.md` file contains YAML frontmatter with `name`, `description`, and pl
|
|
|
141
145
|
| 18 | spec-driven-development | Planning | Athena |
|
|
142
146
|
| 19 | verification-planning | Planning | Athena, Zeus |
|
|
143
147
|
| 20 | visual-review-pipeline | Frontend Development | Aphrodite |
|
|
148
|
+
| 21 | council-synthesis | Orchestration & Workflow | Zeus |
|
|
144
149
|
|
|
145
150
|
---
|
|
146
151
|
|
|
@@ -1,95 +1,25 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: agent-coordination
|
|
3
|
-
description: "
|
|
3
|
+
description: "Coordinate two or more specialists for substantial multi-phase work. Skip for questions, bounded fixes, and one-agent tasks."
|
|
4
4
|
context: fork
|
|
5
5
|
globs: []
|
|
6
6
|
alwaysApply: false
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
# Agent
|
|
9
|
+
# Agent coordination
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Load only when the work genuinely needs multiple specialists or dependent phases. For the default routing, retry, and safety rules, follow Zeus and `pantheon://routing`.
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
## Agent Hierarchy
|
|
16
|
-
|
|
17
|
-
See `AGENTS.md` for the full agent table.
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## Category Routing
|
|
22
|
-
|
|
23
|
-
Route tasks to optimized agents based on category instead of specifying agents manually:
|
|
24
|
-
|
|
25
|
-
| Category | Agent | Use For |
|
|
26
|
-
|----------|-------|---------|
|
|
27
|
-
| **Deep** | Athena, Hephaestus | Complex planning, AI pipelines |
|
|
28
|
-
| **Quick** | Talos, Apollo | Fast fixes, codebase search |
|
|
29
|
-
| **Ultrabrain** | Zeus | Multi-agent orchestration, model routing |
|
|
30
|
-
|
|
31
|
-
---
|
|
32
|
-
|
|
33
|
-
## Sprint Workflow
|
|
34
|
-
|
|
35
|
-
```
|
|
36
|
-
1. User describes feature
|
|
37
|
-
2. @athena creates PLAN with phases and agents
|
|
38
|
-
3. User approves PLAN (GATE 1)
|
|
39
|
-
4. Zeus dispatches agents to phases (parallel when possible)
|
|
40
|
-
5. Each agent: TDD cycle → writes IMPL artifact
|
|
41
|
-
6. @themis reviews all phases (GATE 2)
|
|
42
|
-
7. User reviews Themis findings
|
|
43
|
-
8. If approved → user commits (GATE 3)
|
|
44
|
-
9. @mnemosyne updates memory bank
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
---
|
|
48
|
-
|
|
49
|
-
## Parallel Execution Declaration
|
|
13
|
+
## Lean coordination
|
|
50
14
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
- @aphrodite → frontend → .tmp/IMPL-phase2-aphrodite.md
|
|
56
|
-
- @demeter → migrations → .tmp/IMPL-phase2-demeter.md
|
|
57
|
-
Themis reviews all three after completion.
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
---
|
|
61
|
-
|
|
62
|
-
## Artifact Protocol
|
|
63
|
-
|
|
64
|
-
All phase outputs go to `.pantheon/memory-bank/.tmp/`. See `skill: artifact-management` for the complete protocol (who generates what, templates, lifecycle).
|
|
65
|
-
|
|
66
|
-
---
|
|
67
|
-
|
|
68
|
-
## Safety Gates
|
|
69
|
-
|
|
70
|
-
| Gate | When | Why |
|
|
71
|
-
|------|------|-----|
|
|
72
|
-
| **GATE 1** | After PLAN | User confirms scope |
|
|
73
|
-
| **GATE 2** | After Themis review | User sees changes |
|
|
74
|
-
| **GATE 3** | Before git commit | User controls history |
|
|
75
|
-
|
|
76
|
-
---
|
|
77
|
-
|
|
78
|
-
## Model Selection
|
|
79
|
-
|
|
80
|
-
Model routing is handled by Prometheus and the platform configuration. Zeus delegates model selection to the tier system (`fast` / `default` / `coding` / `premium`).
|
|
81
|
-
|
|
82
|
-
- **Deep tasks** → High-quality models (Claude, GPT-4)
|
|
83
|
-
- **Quick tasks** → Fast/cheap models (Haiku, 4o-mini)
|
|
84
|
-
- **Visual tasks** → Multimodal models (GPT-4V, Claude Vision)
|
|
85
|
-
- **Cost optimization** → Route to cheapest model that meets quality bar
|
|
86
|
-
|
|
87
|
-
---
|
|
15
|
+
1. State the desired outcome and split only work with distinct ownership or dependencies. Ask @athena to plan only when scope, architecture, or acceptance criteria are materially unclear.
|
|
16
|
+
2. Assign each bounded task to the most specific configured specialist. Include relevant context, expected result, and focused verification; do not make @apollo rediscover context the implementer can inspect directly.
|
|
17
|
+
3. Dispatch independent tasks together only when native background delegation is enabled. Otherwise use normal `task()` calls. Do not create waves for a single task or poll without a returned task ID.
|
|
18
|
+
4. Collect summaries once, resolve dependencies, and report evidence, remaining risk, and blockers. Add @themis, artifacts, or user approval only where the task's risk or requested workflow requires them.
|
|
88
19
|
|
|
89
|
-
##
|
|
20
|
+
## Boundaries
|
|
90
21
|
|
|
91
|
-
-
|
|
92
|
-
-
|
|
93
|
-
-
|
|
94
|
-
-
|
|
95
|
-
- ❌ Ignoring Themis CRITICAL findings
|
|
22
|
+
- Zeus coordinates; implementation agents inspect, edit, and verify their assigned scope.
|
|
23
|
+
- Preserve auth/security, data/schema, destructive-action, production, and permission gates.
|
|
24
|
+
- Do not commit, push, merge, deploy, or broaden scope without explicit authorization.
|
|
25
|
+
- Model selection comes from the active configured preset; do not invent a model tier or price.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: artifact-management
|
|
3
|
-
description: "Structured
|
|
3
|
+
description: "Structured artifacts for planned, multi-phase, or materially risky implementation work."
|
|
4
4
|
context: fork
|
|
5
5
|
globs: []
|
|
6
6
|
alwaysApply: false
|
|
@@ -8,7 +8,7 @@ alwaysApply: false
|
|
|
8
8
|
|
|
9
9
|
# Artifact Management
|
|
10
10
|
|
|
11
|
-
Structured artifact trail
|
|
11
|
+
Structured artifact trail for planned, multi-phase, or materially risky implementation work. Artifacts are not a prerequisite for simple fixes or read-only investigation.
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
|
@@ -55,6 +55,13 @@ All ephemeral artifacts live in `.pantheon/memory-bank/.tmp/` — gitignored and
|
|
|
55
55
|
|
|
56
56
|
> **Zeus does NOT generate artifacts.** He orchestrates agents that generate them.
|
|
57
57
|
|
|
58
|
+
## Proportional Use
|
|
59
|
+
|
|
60
|
+
- Create PLAN/IMPL/REVIEW artifacts for planned multi-phase work or when risk, auditability, or an explicit request needs a durable phase trail.
|
|
61
|
+
- A bounded, reversible fix should be investigated only as far as needed, implemented, verified with focused checks, and returned as a concise summary. Do not create PLAN, IMPL, or REVIEW artifacts for it.
|
|
62
|
+
- Read-only lookup or trivial discovery needs no DISC artifact; use one only when findings are broad enough to be reused or drive a material decision.
|
|
63
|
+
- The absence of an artifact never waives Themis review or human approval for auth/security, data/schema/migrations, production-impacting work, or sensitive actions.
|
|
64
|
+
|
|
58
65
|
---
|
|
59
66
|
|
|
60
67
|
## Templates
|
|
@@ -103,9 +110,9 @@ All ephemeral artifacts live in `.pantheon/memory-bank/.tmp/` — gitignored and
|
|
|
103
110
|
|
|
104
111
|
## Human Pause Points
|
|
105
112
|
|
|
106
|
-
1. **After PLAN** →
|
|
107
|
-
2. **After REVIEW** →
|
|
108
|
-
3. **Before
|
|
113
|
+
1. **After a required PLAN** → pause only when a material scope/risk decision needs approval.
|
|
114
|
+
2. **After required REVIEW** → preserve auth/security/data/schema and other sensitive approval gates.
|
|
115
|
+
3. **Before commit/push/merge/deploy or destructive/global action** → require explicit human approval.
|
|
109
116
|
|
|
110
117
|
---
|
|
111
118
|
|