spexcode 0.2.2 → 0.2.3
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/README.md +26 -15
- package/package.json +1 -1
- package/spec-cli/src/cli.ts +3 -2
- package/spec-cli/src/guide.ts +38 -32
- package/spec-cli/src/harness.ts +26 -31
- package/spec-cli/src/help.ts +1 -1
- package/spec-cli/src/layout.ts +1 -3
- package/spec-cli/src/lint.ts +34 -5
- package/spec-cli/src/sessions.ts +11 -7
- package/spec-cli/templates/spexcode.json +7 -0
- package/spec-dashboard/dist/assets/{Dashboard-BwZ2KzxB.js → Dashboard-Dlg78cbC.js} +3 -3
- package/spec-dashboard/dist/assets/EvalsPage-CDxc1-in.js +3 -0
- package/spec-dashboard/dist/assets/{FoldToggle-GwE0-k1d.js → FoldToggle-B5leylLf.js} +1 -1
- package/spec-dashboard/dist/assets/{IssuesPage-B17pnl9I.js → IssuesPage-C2yFXiO-.js} +1 -1
- package/spec-dashboard/dist/assets/{MobileApp-WEZbR8M1.js → MobileApp-RHNECU6x.js} +1 -1
- package/spec-dashboard/dist/assets/{SessionInterface-Sh8kHpnj.js → SessionInterface-YLD6IOmC.js} +1 -1
- package/spec-dashboard/dist/assets/SessionWindow-CmKtpNUX.js +9 -0
- package/spec-dashboard/dist/assets/{Settings-Dgtg-Xb9.js → Settings-ZnOwskMZ.js} +1 -1
- package/spec-dashboard/dist/assets/{index-Dd0_U5rk.js → index-BdRQfrkR.js} +2 -2
- package/spec-dashboard/dist/assets/{index-CCmnCbKS.css → index-DEc5Ru3l.css} +1 -1
- package/spec-dashboard/dist/index.html +2 -2
- package/spec-yatsu/src/cli.ts +39 -11
- package/spec-yatsu/src/evaltab.ts +7 -6
- package/spec-yatsu/src/filing.ts +6 -3
- package/spec-yatsu/src/proof.ts +10 -0
- package/spec-yatsu/src/sidecar.ts +25 -3
- package/spec-yatsu/src/timeline.ts +53 -23
- package/README.zh-CN.md +0 -170
- package/spec-dashboard/dist/assets/EvalsPage-DV75EdP4.js +0 -3
- package/spec-dashboard/dist/assets/SessionWindow-EzFq-hLG.js +0 -9
|
@@ -5,9 +5,14 @@ import { readFileSync, appendFileSync, existsSync } from 'node:fs'
|
|
|
5
5
|
// on disk with status:'note'; render stays tolerant of them, the CLI no longer mints them.)
|
|
6
6
|
export type Verdict = { status: 'pass' | 'fail'; note?: string }
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
//
|
|
10
|
-
//
|
|
8
|
+
// The evidence-kind taxonomy ([[evidence-kind-taxonomy]]) is a MEDIA/RENDER type — how a blob's bytes are
|
|
9
|
+
// shown — kept ORTHOGONAL to the step-map AXIS (which is derived from the kind, not welded to it): `image`
|
|
10
|
+
// (a still), `transcript` (free-form text), `video` (a screenshot with a time axis), `data` (a structured
|
|
11
|
+
// machine export — a JSON/metrics dump — rendered as a validatable data block, not flattened into scrolling
|
|
12
|
+
// transcript text). A `data` reading is honest about being structured: it can be parsed and checked, and it
|
|
13
|
+
// is derived from CONTENT (isJsonBlob), never from which filing flag was used.
|
|
14
|
+
export type EvidenceKind = 'image' | 'transcript' | 'video' | 'data'
|
|
15
|
+
// one piece of a reading's evidence: a content-addressed blob (`hash`) tagged by `kind`.
|
|
11
16
|
export type Evidence = { hash: string; kind: EvidenceKind }
|
|
12
17
|
|
|
13
18
|
// A reading's evidence is a LIST of typed entries — N images and/or a video (with its step-timeline) and/or
|
|
@@ -41,6 +46,23 @@ export function evidenceOf(r: { evidence?: Evidence[]; blob?: string | null; blo
|
|
|
41
46
|
return []
|
|
42
47
|
}
|
|
43
48
|
|
|
49
|
+
// Is this blob STRUCTURED DATA (a machine export — a JSON object/array) rather than free-form transcript
|
|
50
|
+
// text? The `data` evidence kind ([[evidence-kind-taxonomy]]) is derived from CONTENT, never from which
|
|
51
|
+
// filing flag was used: a hyperfine `--export-json`, an API payload, a metrics dump is data, and flattening
|
|
52
|
+
// it into a scrolling transcript loses that it can be structurally validated and rendered as a data block.
|
|
53
|
+
// Sniffed cheaply and self-contained (no deps): a text blob (no NUL) whose trimmed body brackets as an
|
|
54
|
+
// object/array AND parses to one; anything else (plain logs, terminal text) is not data. This is the ONE
|
|
55
|
+
// predicate both the blob MIME sniff (application/json) and the CLI `--result` kind derive from, so the
|
|
56
|
+
// stored kind and the served Content-Type always agree.
|
|
57
|
+
export function isJsonBlob(b: Buffer): boolean {
|
|
58
|
+
if (!b.length || b.includes(0)) return false // empty or binary → not JSON text
|
|
59
|
+
if (b.length > 4_000_000) return false // don't parse an unbounded blob just to sniff a type
|
|
60
|
+
const s = b.toString('utf8').trim()
|
|
61
|
+
const open = s[0], close = s[s.length - 1]
|
|
62
|
+
if (!((open === '{' && close === '}') || (open === '[' && close === ']'))) return false
|
|
63
|
+
try { const v = JSON.parse(s); return v !== null && typeof v === 'object' } catch { return false }
|
|
64
|
+
}
|
|
65
|
+
|
|
44
66
|
// a RETRACTION is the sanctioned inverse of a filing — itself an appended event, never a deleted line
|
|
45
67
|
// (the sidecar stays append-only; git shows who retracted what, when). `retracts` is the target reading's
|
|
46
68
|
// `ts` within `scenario` (its natural key). Deliberately NO `evaluator` field: an old reader's line filter
|
|
@@ -1,33 +1,53 @@
|
|
|
1
|
-
// step-timeline — the map from a
|
|
2
|
-
// this FORMAT (a tiny data contract any userland emitter satisfies — a Playwright reporter, a WebDriver
|
|
3
|
-
// listener, a computer-use hand narrating as it drives
|
|
4
|
-
// emitter's own job. The
|
|
5
|
-
// ndjson column beyond the one hash.
|
|
1
|
+
// step-timeline — the map from a POSITION on a piece of evidence's own axis to a named step. SpexCode owns
|
|
2
|
+
// only this FORMAT (a tiny data contract any userland emitter satisfies — a Playwright reporter, a WebDriver
|
|
3
|
+
// listener, a computer-use hand narrating as it drives, a CLI run stamping line numbers); aligning the
|
|
4
|
+
// emitter's positions to the evidence is the emitter's own job. The map rides as a second content-addressed
|
|
5
|
+
// blob on the reading, never a new ndjson column beyond the one hash.
|
|
6
|
+
//
|
|
7
|
+
// The step is anchored to the evidence's OWN axis, tagged by `axis`: `time` (ms, a video), `frame` (a still
|
|
8
|
+
// SEQUENCE by index), `line` (a transcript by line number), `index` (a bare action ordinal). The set is
|
|
9
|
+
// OPEN by convention — an unknown axis is legal and a reader renders its positions as bare numbers. `stepAt`
|
|
10
|
+
// (last step at or before a position) is axis-agnostic and unchanged.
|
|
6
11
|
|
|
7
|
-
export type TimelineEvent = {
|
|
8
|
-
export type StepTimeline = { v:
|
|
12
|
+
export type TimelineEvent = { at: number; step: string; node?: string }
|
|
13
|
+
export type StepTimeline = { v: 2; axis: string; events: TimelineEvent[] }
|
|
9
14
|
|
|
10
|
-
|
|
15
|
+
// legacy v1 is the TIME axis with `tMs` as the position — read losslessly, normalized to the axis-tagged
|
|
16
|
+
// shape (`axis: 'time'`, `at: tMs`). Kept forever: an emitter that only knew `{ v: 1, events: [{ tMs }] }`
|
|
17
|
+
// still files a valid video step-map.
|
|
18
|
+
export type LegacyTimelineEvent = { tMs: number; step: string; node?: string }
|
|
19
|
+
export type LegacyStepTimeline = { v: 1; events: LegacyTimelineEvent[] }
|
|
11
20
|
|
|
12
|
-
|
|
13
|
-
|
|
21
|
+
const V2_EVENT_KEYS = new Set(['at', 'step', 'node'])
|
|
22
|
+
const V1_EVENT_KEYS = new Set(['tMs', 'step', 'node'])
|
|
23
|
+
|
|
24
|
+
// validate LOUD — every violation named, [] when well-formed. Both schema versions are accepted: v1 (legacy
|
|
25
|
+
// time axis, `tMs`) and v2 (axis-tagged, `at`). The key set is closed per version (like the yatsu.md
|
|
26
|
+
// scenario schema): a malformed timeline is rejected at filing time, never silently reshaped. The `axis`
|
|
27
|
+
// string itself is open — only its ABSENCE is an error, never an unrecognized value.
|
|
14
28
|
export function validateTimeline(raw: unknown): string[] {
|
|
15
|
-
if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) return ['timeline must be a JSON object { v, events }']
|
|
16
|
-
const errs: string[] = []
|
|
29
|
+
if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) return ['timeline must be a JSON object { v, axis, events }']
|
|
17
30
|
const o = raw as Record<string, unknown>
|
|
18
|
-
|
|
19
|
-
|
|
31
|
+
if (o.v !== 1 && o.v !== 2) return ['`v` must be 1 (legacy time axis) or 2 (axis-tagged)']
|
|
32
|
+
const errs: string[] = []
|
|
33
|
+
const v2 = o.v === 2
|
|
34
|
+
const posKey = v2 ? 'at' : 'tMs'
|
|
35
|
+
const rootKeys = v2 ? new Set(['v', 'axis', 'events']) : new Set(['v', 'events'])
|
|
36
|
+
const evKeys = v2 ? V2_EVENT_KEYS : V1_EVENT_KEYS
|
|
37
|
+
for (const k of Object.keys(o)) if (!rootKeys.has(k)) errs.push(`unknown field \`${k}\` (allowed: ${[...rootKeys].join(', ')})`)
|
|
38
|
+
if (v2 && (typeof o.axis !== 'string' || !o.axis.trim())) errs.push('`axis` must be a non-empty string (e.g. time, frame, line, index)')
|
|
20
39
|
if (!Array.isArray(o.events)) { errs.push('`events` must be an array'); return errs }
|
|
21
40
|
let prev = -Infinity
|
|
22
41
|
o.events.forEach((e, i) => {
|
|
23
42
|
if (typeof e !== 'object' || e === null || Array.isArray(e)) { errs.push(`events[${i}] must be an object`); return }
|
|
24
43
|
const ev = e as Record<string, unknown>
|
|
25
|
-
for (const k of Object.keys(ev)) if (!
|
|
26
|
-
|
|
27
|
-
|
|
44
|
+
for (const k of Object.keys(ev)) if (!evKeys.has(k)) errs.push(`events[${i}]: unknown field \`${k}\` (allowed: ${[...evKeys].join(', ')})`)
|
|
45
|
+
const pos = ev[posKey]
|
|
46
|
+
if (typeof pos !== 'number' || !Number.isFinite(pos) || pos < 0) {
|
|
47
|
+
errs.push(`events[${i}].${posKey} must be a finite number ≥ 0`)
|
|
28
48
|
} else {
|
|
29
|
-
if (
|
|
30
|
-
prev =
|
|
49
|
+
if (pos < prev) errs.push(`events[${i}].${posKey} is out of order (the list is ordered by position)`)
|
|
50
|
+
prev = pos
|
|
31
51
|
}
|
|
32
52
|
if (typeof ev.step !== 'string' || !ev.step.trim()) errs.push(`events[${i}].step must be a non-empty string`)
|
|
33
53
|
if (ev.node !== undefined && (typeof ev.node !== 'string' || !ev.node.trim())) errs.push(`events[${i}].node must be a non-empty string when present`)
|
|
@@ -35,12 +55,22 @@ export function validateTimeline(raw: unknown): string[] {
|
|
|
35
55
|
return errs
|
|
36
56
|
}
|
|
37
57
|
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
|
|
58
|
+
// normalize any VALID timeline (v1 or v2) to the axis-tagged shape every reader uses: v1 IS the time axis
|
|
59
|
+
// with `tMs` as the position. Call only on input `validateTimeline` accepted. This is the whole of the
|
|
60
|
+
// lossless back-compat: an old v1 blob and a new `{ v: 2, axis: 'time' }` render identically.
|
|
61
|
+
export function normalizeTimeline(raw: unknown): { axis: string; events: TimelineEvent[] } {
|
|
62
|
+
const o = (raw ?? {}) as Record<string, any>
|
|
63
|
+
const events: any[] = Array.isArray(o.events) ? o.events : []
|
|
64
|
+
if (o.v === 1) return { axis: 'time', events: events.map((e) => ({ at: e.tMs, step: e.step, ...(e.node ? { node: e.node } : {}) })) }
|
|
65
|
+
return { axis: typeof o.axis === 'string' ? o.axis : 'time', events: events.map((e) => ({ at: e.at, step: e.step, ...(e.node ? { node: e.node } : {}) })) }
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// the whole of "which step is this position": the last event at or before `pos`; null before the first event
|
|
69
|
+
// (a plain moment, no step to name — graceful, never an error). Axis-agnostic — `pos` is on the events' axis.
|
|
70
|
+
export function stepAt(events: TimelineEvent[], pos: number): TimelineEvent | null {
|
|
41
71
|
let hit: TimelineEvent | null = null
|
|
42
72
|
for (const e of events) {
|
|
43
|
-
if (e.
|
|
73
|
+
if (e.at <= pos) hit = e
|
|
44
74
|
else break
|
|
45
75
|
}
|
|
46
76
|
return hit
|
package/README.zh-CN.md
DELETED
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
<img src="docs/sdd-tuxedo-pooh.png" alt="tuxedo pooh 梗图" width="420">
|
|
2
|
-
|
|
3
|
-
# SpexCode
|
|
4
|
-
|
|
5
|
-
把 AI agent 纳入回路的 spec 驱动开发。SpexCode 在你的 git 仓库里维护一棵带版本的 spec 树,把每个
|
|
6
|
-
spec 和它管辖的代码链接起来,并运行一个会话管理器,把 coding agent 派进相互隔离的 worktree。你负责
|
|
7
|
-
review 和 merge;工具负责让意图和实现不分家。(下面所有截图都是这个仓库自己跑在自己看板上的样子。)
|
|
8
|
-
|
|
9
|
-
[English](./README.md) | 中文 · 文档:[spexcode.net](https://spexcode.net) · License: MIT
|
|
10
|
-
|
|
11
|
-
快捷入口:[模型](#模型) · [快速开始](#快速开始) · [agent](#和-agent-一起工作) ·
|
|
12
|
-
[yatsu](#测量行为yatsu) · [配置](#配置)
|
|
13
|
-
|
|
14
|
-
## 模型
|
|
15
|
-
|
|
16
|
-
一个 spec 节点就是 `.spec/` 下的一个目录,里面有一个 `spec.md`:frontmatter(title、status、
|
|
17
|
-
声明管辖文件的 `code:` 清单)加一段正文,描述系统这一部分当前应该做什么。节点可以嵌套,所以这棵树
|
|
18
|
-
对应你对项目的理解方式,而不是文件布局。正文分两个部分。很短的 **raw source** 写意图,改它需要
|
|
19
|
-
人的明确认可,agent 起草、人拍板的也算;**expanded spec** 是 agent 对这个意图的详细展开,自由
|
|
20
|
-
迭代,但必须始终和 raw source 一致。
|
|
21
|
-
|
|
22
|
-
<img src="docs/readme-node.png" alt="节点弹窗">
|
|
23
|
-
|
|
24
|
-
两条规则让这套东西成立:
|
|
25
|
-
|
|
26
|
-
1. **git 就是数据库。** 没有第二份存储。节点的版本号是碰过它 `spec.md` 的 commit 数,历史视图就是
|
|
27
|
-
这个文件的 `git log`,每一版通过 `Session:` commit trailer 归属到写它的 agent 会话。也因为这样,
|
|
28
|
-
spec 正文永远只描述当前意图、原地重写:正文里禁止出现 changelog 标题(linter 强制),历史 git
|
|
29
|
-
已经记了。
|
|
30
|
-
2. **spec 和代码一起落地。** 一次改动就是一个 commit,同时更新 `spec.md` 和它所解释的代码。代码
|
|
31
|
-
要是脱开 spec 单独动了,linter 会标出来,
|
|
32
|
-
|
|
33
|
-
```
|
|
34
|
-
drift: spec-cli/src/board.ts is 1 commit(s) ahead of spec 'board-lean' (v8) — may be stale
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
一直标到 spec 跟上为止。
|
|
38
|
-
|
|
39
|
-
## 优化循环
|
|
40
|
-
|
|
41
|
-
spec、commit、yatsu 读数,这几样合起来是一个循环。spec 是损失函数:定义你要什么,这一半由人拍板。commit 是优化器。
|
|
42
|
-
**yatsu** 是测量子系统,负责评估:量出当前行为离 spec 还有多远,分数的历史照样存在 git 里。
|
|
43
|
-
|
|
44
|
-
<img src="docs/readme-loop.zh.png" alt="优化循环示意">
|
|
45
|
-
|
|
46
|
-
它也决定了人平时站在哪:没有人靠盯权重去读神经网络,两次 merge 之间同样不必盯着 agent 的 diff。
|
|
47
|
-
注意力放在 spec 和 eval 读数上;diff 只在 merge 的时候读一次。
|
|
48
|
-
|
|
49
|
-
## 快速开始
|
|
50
|
-
|
|
51
|
-
需要 Node ≥ 22 和 git。这一步是普通工具,还不涉及 AI。
|
|
52
|
-
|
|
53
|
-
```sh
|
|
54
|
-
npm i -g spexcode # 安装 spex 命令
|
|
55
|
-
cd your-repo
|
|
56
|
-
spex init # 生成 .spec/、安装 git hooks、渲染 agent 契约
|
|
57
|
-
spex serve # API 后端,:8787
|
|
58
|
-
spex dashboard # 看板 UI,:5173,代理到后端
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
`spex init` 是增量式的,在任何已有 git 仓库上可用,不会覆盖你的文件:生成根节点
|
|
62
|
-
`.spec/project/spec.md`、一份起始 `spexcode.json`、pre-commit hooks,并向 `CLAUDE.md`/`AGENTS.md`
|
|
63
|
-
写入一个托管块,让任何在这个仓库里工作的 agent 自己发现这套工作流。
|
|
64
|
-
|
|
65
|
-
然后把树长起来:
|
|
66
|
-
|
|
67
|
-
1. 编辑 `.spec/project/spec.md`,描述项目。
|
|
68
|
-
2. 给想管辖的部分加子节点,每个带一个指向现有文件的 `code:` 清单。
|
|
69
|
-
3. 跑 `spex lint`。coverage 警告列出还没有 spec 认领的源文件,那就是你的接入 TODO。
|
|
70
|
-
|
|
71
|
-
这些不需要你全部手写。预期的用法是让 agent 完成大部分 spec 写作;`spex guide spec` 会打印它需要的
|
|
72
|
-
确切文件格式。完整的安装过程见文档站的
|
|
73
|
-
[getting started](https://spexcode.net/getting-started/)。
|
|
74
|
-
|
|
75
|
-
<img src="docs/readme-board.png" alt="看板截图">
|
|
76
|
-
|
|
77
|
-
*SpexCode 自己的仓库跑在自己的看板上;左上角那些会话就是正在造它的 agent。*
|
|
78
|
-
|
|
79
|
-
## 和 agent 一起工作
|
|
80
|
-
|
|
81
|
-
这一步需要 tmux 和本机已登录的 [Claude Code](https://www.anthropic.com/claude-code) 或 Codex。
|
|
82
|
-
|
|
83
|
-
```sh
|
|
84
|
-
spex new "让设置页记住上次打开的标签" --node settings
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
会在 `node/settings` 分支的独立 worktree 里启动一个 worker 会话。worker 动代码之前先读管辖 spec,
|
|
88
|
-
做出改动,把 spec 正文改写到和实现一致,把两者一起 commit(hook 自动盖 `Session:` 戳),然后提出
|
|
89
|
-
merge 并停下。worker 不自己 merge。合并留在管理者手里:你按下 merge 时,实际的 git merge 由这个
|
|
90
|
-
会话自己的 agent 执行,冲突落在最懂这份活的人手里。同样的派工在看板网页上就是一个按钮(board 的
|
|
91
|
-
new-session 输入框);命令行形态是 agent 之间互相委派时用的。
|
|
92
|
-
|
|
93
|
-
你在外面督工,用看板,或者用 agent 也在用的这几条命令:
|
|
94
|
-
|
|
95
|
-
```sh
|
|
96
|
-
spex watch # 实时输出会话状态变化:launched / review / done / needs-input ...
|
|
97
|
-
spex review settings # 领先 trunk 的 commit、merge-base diff、typecheck/lint 门
|
|
98
|
-
spex merge settings # 有门禁的 merge 入 trunk
|
|
99
|
-
spex session close settings
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
相互独立的任务并行跑。每个 worker 隔离在自己的 worktree 里,merge 由 git 序列化,pre-commit 守卫
|
|
103
|
-
拦截对 trunk 的直接提交,所以一切都从可 review 的 node 分支流过。
|
|
104
|
-
|
|
105
|
-
流程靠机制强制,不靠提示词工程:后端建分支、hook 盖归属戳,其余规则由 materialize 出的契约块承载,
|
|
106
|
-
所以派工提示词只需要写任务本身。这套工作方式的长版本:
|
|
107
|
-
[working with agents](https://spexcode.net/working-with-agents/)。
|
|
108
|
-
|
|
109
|
-
## 测量行为:yatsu
|
|
110
|
-
|
|
111
|
-
yatsu 就是[优化循环](#优化循环)里负责测量的那一半。spec 说这部分应该做什么;旁边的 `yatsu.md` 说怎么验。每条
|
|
112
|
-
scenario 就是一段普通描述加一个期望结果。yatsu 自己什么都不跑(没有 DSL,也没有 runner)。agent
|
|
113
|
-
用顺手的方式执行场景:测试文件、真实浏览器,或者干脆手点一遍截个图。把实际结果和期望对比,连证据
|
|
114
|
-
一起把读数记档:
|
|
115
|
-
|
|
116
|
-
```sh
|
|
117
|
-
spex yatsu eval settings --scenario remembers-tab --pass --image proof.png
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
读数存在 spec 旁边一个 git 跟踪的 ndjson 里,所以测量和 spec 版本享有同样的归属和历史。修 bug 要求
|
|
121
|
-
成对:先记一条复现 bug 的 fail 读数,修掉,再在同一条 scenario 上记一条 pass。
|
|
122
|
-
|
|
123
|
-
<img src="docs/readme-eval.png" alt="eval 视图截图">
|
|
124
|
-
|
|
125
|
-
*eval 视图:左侧是各 scenario 的读数;中间是选中读数的期望结果、过期原因和录屏证据。*
|
|
126
|
-
|
|
127
|
-
## 仓库里有什么
|
|
128
|
-
|
|
129
|
-
| 包 | 职责 |
|
|
130
|
-
|---|---|
|
|
131
|
-
| `spec-cli` | `spex` CLI 和 HTTP 后端(Hono,tsx 直跑,无构建步骤)。实时读 `.spec` 和 git;会话状态机和 linter 都在这里。 |
|
|
132
|
-
| `spec-dashboard` | React 看板:节点图、每个节点的 spec/history/issues 面板,以及连到每个活跃 agent 会话的真终端。 |
|
|
133
|
-
| `spec-yatsu` | scenario 定义、读数、证据文件。 |
|
|
134
|
-
| `spec-forge` | 只读追踪器,把 forge 上的 open issue 和 PR 解析到它们服务的 spec 节点(目前支持 GitHub)。issue 在正文里写一行 `Spec: <node-id>` 即完成链接;从 `node/<id>` 分支开的 PR 自动链接。 |
|
|
135
|
-
|
|
136
|
-
## linter
|
|
137
|
-
|
|
138
|
-
`spex lint` 检查 spec↔code 图,它才是真正的门(git hook 只是快速的本地反馈):
|
|
139
|
-
|
|
140
|
-
- **integrity**(error):`code:` 指向不存在的路径
|
|
141
|
-
- **living**(error):spec 正文里出现 changelog 标题
|
|
142
|
-
- **altitude**(warn):正文从契约层滑落成实现细节堆。常见的味道是一串编号步骤,或者满屏函数名;
|
|
143
|
-
spec 正文还能读得下去,靠的就是这条
|
|
144
|
-
- **coverage**(warn):没被认领的源文件
|
|
145
|
-
- **drift**(warn):被管辖的代码在 spec 最后一版之后又改了,实时从 git 推导
|
|
146
|
-
|
|
147
|
-
## 配置
|
|
148
|
-
|
|
149
|
-
`spexcode.json`(提交进仓库,可移植:布局、lint 预算、看板标识、launcher 名字)和
|
|
150
|
-
`spexcode.local.json`(gitignore,单机:launcher 绝对路径,以及给你参与但不拥有的仓库用的
|
|
151
|
-
`private: true` 覆盖)承载全部设置。暂时没有 `spex config set`:两个文件直接手改(或让 agent 改),每个字段的文档在
|
|
152
|
-
`spex guide config`。其他手册:`spex guide`(工作流)、`spex guide spec`、
|
|
153
|
-
`spex guide yatsu`;`spex help` 列出全部命令。
|
|
154
|
-
|
|
155
|
-
## 现状
|
|
156
|
-
|
|
157
|
-
SpexCode 用它自己开发自己:这个仓库的 `.spec/` 树就是工具自身的 spec,对工具的每个改动都走它
|
|
158
|
-
自己实现的那套 worker/manager 循环落地。你装到的看板就是造它用的那块。已知的坑:`spex session new --help`
|
|
159
|
-
不会打印帮助,而是真的创建一个叫 `--help` 的会话(派工请用 `spex new`)。另外 altitude lint 现在
|
|
160
|
-
对这个仓库自己的 spec 报着四十多条警告,我还没腾出手来清。首次公开介绍发在
|
|
161
|
-
[LINUX DO](https://linux.do) 社区,感谢佬友们的第一轮讨论。
|
|
162
|
-
|
|
163
|
-
## 参与开发
|
|
164
|
-
|
|
165
|
-
[`docs/CONTRIBUTING.md`](docs/CONTRIBUTING.md) 带你从 clone 到第一个合入的改动。
|
|
166
|
-
[`docs/AGENT_GUIDE.md`](docs/AGENT_GUIDE.md) 有节点模型和反身配置系统的完整机制。
|
|
167
|
-
|
|
168
|
-
## License
|
|
169
|
-
|
|
170
|
-
[MIT](./LICENSE)。
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import{u as he,r as s,s as qe,j as n,e as We,f as Ye}from"./index-Dd0_U5rk.js";import{V as Ge,W as Ze,X as Je,Y as Ce,Z as Qe,g as ie,n as et,_ as J,$ as tt,a0 as Ae,a1 as nt,a2 as st,u as at,z as ce,a3 as lt,o as ot,a4 as rt}from"./SessionWindow-EzFq-hLG.js";import{F as ue}from"./FoldToggle-GwE0-k1d.js";const de=(l,v)=>{let m=null;for(const N of l)if(N.tMs<=v)m=N;else break;return m},fe=l=>{var v,m;return((v=l.verdict)==null?void 0:v.status)==="pass"?"✓":((m=l.verdict)==null?void 0:m.status)==="fail"?"✗":"·"},Ie=l=>{var v,m;return((v=l.verdict)==null?void 0:v.status)==="pass"?"pass":((m=l.verdict)==null?void 0:m.status)==="fail"?"fail":"legacy"};function it({entry:l,specs:v=[],sessions:m=[],onWrite:N,onOpenSession:R}){var Se,Re,Te,Le,De;const c=he(),f=s.useRef(null),p=s.useRef(null),j=s.useRef(null),w=s.useRef(null),x=s.useRef(0),[u,M]=s.useState([]),[i,g]=s.useState(null),[y,b]=s.useState(""),[E,B]=s.useState(!1),[T,A]=s.useState(null),I=s.useRef(null),P=s.useRef(null),L=s.useRef(null),d=s.useRef(0),[Q,me]=s.useState(0),[ve,pe]=s.useState(-1),[xe,ge]=s.useState(null),[_,ee]=s.useState(!1),[ye,te]=s.useState(!1),[ne,se]=s.useState(null),[q,ae]=s.useState(null),[k,be]=s.useState(null),[D,W]=s.useState(0),h=k&&k[D]||l;s.useEffect(()=>{g(null),b(""),M([]),A(null),W(0),be(null);let e=!0;return fetch(qe(l.node,"evals")).then(t=>t.ok?t.json():null).then(t=>{e&&Array.isArray(t==null?void 0:t.readings)&&be(t.readings.filter(a=>a.scenario===l.scenario))}).catch(()=>{}),()=>{e=!1}},[l.node,l.scenario,l.ts,l.blob]),s.useEffect(()=>{g(null),A(null)},[D]);const O=Ge(h),V=O.find(e=>e.kind==="video"&&e.state==="present"),je=O.filter(e=>e.kind==="image"),Fe=O.filter(e=>e.kind==="transcript"),C=!!V,X=l.thread??null,le=s.useMemo(()=>X?[{by:X.by,at:X.created,body:X.body},...X.replies||[]]:[],[X]),Ke=k&&((Se=k[0])==null?void 0:Se.by)||l.by||null,$=s.useMemo(()=>le.map((e,t)=>{const a=Ze(Je(e.body),u);return a&&a.seekable?{i:t,tMs:a.tMs,step:a.step,label:a.label}:null}).filter(Boolean).sort((e,t)=>e.tMs-t.tMs),[le,u]),we=s.useRef(u);we.current=u;const Me=s.useRef($);Me.current=$;const Y=s.useCallback(()=>{const e=d.current,t=we.current;let a=-1;for(let r=0;r<t.length&&t[r].tMs<=e;r++)a=r;pe(r=>r===a?r:a);let o=null;for(const r of Me.current)if(r.tMs<=e)o=r.i;else break;ge(r=>r===o?r:o)},[]);s.useEffect(()=>{Y()},[$,u,Y]),s.useEffect(()=>{d.current=0,I.current&&(I.current.style.width="0%"),P.current&&(P.current.style.left="0%"),L.current&&(L.current.textContent=""),pe(-1),ge(null),me(0),ee(!1),te(!1),se(null),ae(null)},[l.blob,l.scenario,l.node]),s.useEffect(()=>{if(M([]),!h.timelineBlob||!C)return;let e=!0;return fetch(`/api/yatsu/blob/${h.timelineBlob}`).then(t=>t.ok?t.json():null).then(t=>{e&&Array.isArray(t==null?void 0:t.events)&&M(t.events)}).catch(()=>{}),()=>{e=!1}},[h.timelineBlob,C]),s.useEffect(()=>{const e=f.current;if(!e)return;const t=()=>{const S=Math.round((e.currentTime||0)*1e3),F=Math.round((e.duration||0)*1e3);d.current=S;const K=F?S/F*100:0;I.current&&(I.current.style.width=`${K}%`),P.current&&(P.current.style.left=`${K}%`),L.current&&(L.current.textContent=`${J(S)} / ${J(F)}`),Y()},a=()=>{me(e.duration||0),t()},o=()=>ee(!0),r=()=>ee(!1);return e.addEventListener("timeupdate",t),e.addEventListener("seeked",t),e.addEventListener("loadedmetadata",a),e.addEventListener("durationchange",a),e.addEventListener("play",o),e.addEventListener("pause",r),a(),()=>{e.removeEventListener("timeupdate",t),e.removeEventListener("seeked",t),e.removeEventListener("loadedmetadata",a),e.removeEventListener("durationchange",a),e.removeEventListener("play",o),e.removeEventListener("pause",r)}},[V==null?void 0:V.hash,l.node,l.scenario,D,Y]);const z=Math.round(Q*1e3),G=u[ve]||null,U=s.useCallback(e=>{const t=f.current;t&&(t.currentTime=e/1e3)},[]),Z=s.useCallback(()=>{const e=f.current;e&&(e.paused?e.play():e.pause())},[]),ke=(e,t)=>{ae(e),t!=null&&U(t)},Be=s.useCallback(()=>{var t,a;const e=Math.round((((t=f.current)==null?void 0:t.currentTime)??0)*1e3);return{tMs:e,step:((a=de(u,e))==null?void 0:a.step)??null}},[u]),Ne=s.useCallback(()=>{const e=f.current;if(!e)return;e.pause();const t=Math.round((e.currentTime||0)*1e3),a=de(u,t),o=[Ce(t,a==null?void 0:a.step)];a!=null&&a.node&&a.node!==l.node&&o.push(`re: [[${a.node}]]`),o.push(""),A({seq:++x.current,body:o.join(`
|
|
2
|
-
`)})},[u,l.node]),oe=s.useCallback(e=>{var o;if(!$.length)return;let t=$.findIndex(r=>r.i===q);if(t<0){const r=Math.round((((o=f.current)==null?void 0:o.currentTime)||0)*1e3);t=$.reduce((S,F,K)=>F.tMs<=r?K:S,-1)}t=Math.min($.length-1,Math.max(0,t+e));const a=$[t];ae(a.i),U(a.tMs)},[$,q,U]);s.useEffect(()=>{if(!C)return;const e=t=>{const a=document.activeElement;if(a&&(a.tagName==="INPUT"||a.tagName==="TEXTAREA"||a.tagName==="SELECT"||a.isContentEditable))return;const o=f.current;o&&(t.key===" "?(t.preventDefault(),Z()):t.key==="ArrowRight"?(t.preventDefault(),o.currentTime=Math.min(o.duration||o.currentTime,o.currentTime+(t.shiftKey?1:5))):t.key==="ArrowLeft"?(t.preventDefault(),o.currentTime=Math.max(0,o.currentTime-(t.shiftKey?1:5))):t.key===","?(t.preventDefault(),o.currentTime=Math.max(0,o.currentTime-1/30)):t.key==="."?(t.preventDefault(),o.duration&&(o.currentTime=Math.min(o.duration,o.currentTime+1/30))):t.key==="ArrowDown"?(t.preventDefault(),oe(1)):t.key==="ArrowUp"?(t.preventDefault(),oe(-1)):(t.key==="a"||t.key==="A")&&(t.preventDefault(),Ne()))};return window.addEventListener("keydown",e),()=>window.removeEventListener("keydown",e)},[C,Z,oe,Ne]);const Ee=e=>{var o;const t=(o=j.current)==null?void 0:o.getBoundingClientRect(),a=f.current;!t||!t.width||!a||!a.duration||(a.currentTime=Math.min(1,Math.max(0,(e-t.left)/t.width))*a.duration)},Ve=e=>{e.preventDefault(),te(!0),Ee(e.clientX)},Xe=e=>{var a;const t=(a=j.current)==null?void 0:a.getBoundingClientRect();t!=null&&t.width&&se(Math.min(100,Math.max(0,(e.clientX-t.left)/t.width*100)))};s.useEffect(()=>{if(!ye)return;const e=a=>Ee(a.clientX),t=()=>te(!1);return window.addEventListener("mousemove",e),window.addEventListener("mouseup",t),()=>{window.removeEventListener("mousemove",e),window.removeEventListener("mouseup",t)}},[ye]);const $e=e=>{const t=p.current.getBoundingClientRect();return{x:(e.clientX-t.left)/t.width*100,y:(e.clientY-t.top)/t.height*100}},ze=e=>{if(e.button!==0||E)return;const t=$e(e);g({x0:t.x,y0:t.y,x:t.x,y:t.y})},Oe=e=>{var a,o;if(!i)return;const t=$e(e);!((a=f.current)!=null&&a.paused)&&(Math.abs(t.x-i.x0)>1||Math.abs(t.y-i.y0)>1)&&((o=f.current)==null||o.pause()),g({...i,x:t.x,y:t.y})},Ue=async e=>{const t=f.current;if(!(t!=null&&t.videoWidth))return;const a=Math.round((t.currentTime??0)*1e3),o=de(u,a);B(!0),b(c("annotator.capturing"));try{const r=document.createElement("canvas");r.width=t.videoWidth,r.height=t.videoHeight;const S=r.getContext("2d");S.drawImage(t,0,0,r.width,r.height),S.strokeStyle="#ff9a3c",S.lineWidth=Math.max(2,r.width/300),S.strokeRect(e.x/100*r.width,e.y/100*r.height,e.w/100*r.width,e.h/100*r.height);const F=await new Promise(_e=>r.toBlob(_e,"image/png")),{hash:K}=await We(F);if(!K)throw new Error("no hash");const re=[Ce(a,o==null?void 0:o.step),``];o!=null&&o.node&&o.node!==l.node&&re.push(`re: [[${o.node}]]`),re.push(""),A({seq:++x.current,body:re.join(`
|
|
3
|
-
`)}),b("")}catch{b(c("annotator.failed"))}finally{B(!1)}},He=()=>{if(!i)return;const e={x:Math.min(i.x0,i.x),y:Math.min(i.y0,i.y),w:Math.abs(i.x-i.x0),h:Math.abs(i.y-i.y0)};if(g(null),e.w<1&&e.h<1){Z();return}Ue(e)},H=i&&{x:Math.min(i.x0,i.x),y:Math.min(i.y0,i.y),w:Math.abs(i.x-i.x0),h:Math.abs(i.y-i.y0)};return n.jsxs("div",{className:"an-detail",children:[n.jsxs("header",{className:"an-head",children:[n.jsx("span",{className:"an-title",children:l.scenario}),n.jsx("span",{className:"an-node",children:l.node}),n.jsx("span",{className:`an-verdict-badge ${Ie(h)}`,children:fe(h)}),h.evaluator&&n.jsx("span",{className:"an-meta",children:h.evaluator}),n.jsx("span",{className:"an-meta",children:new Date(h.ts).toLocaleString()}),n.jsx(Qe,{originator:Ke,sessions:m,kind:"eval",onOpenSession:R}),k&&k.length>1&&n.jsxs("div",{className:"an-ab",children:[n.jsx(ie,{icon:"chevron-left",size:13,className:"an-ab-nav",disabled:D>=k.length-1,onClick:()=>W(e=>Math.min(k.length-1,e+1)),label:c("annotator.abOlder")}),n.jsx("div",{className:"an-ab-track",children:k.slice().reverse().map((e,t)=>{const a=k.length-1-t;return n.jsx("button",{type:"button",className:`an-ab-pip ${Ie(e)} ${a===D?"on":""}`,onClick:()=>W(a),"data-tip":`${fe(e)} ${new Date(e.ts).toLocaleString()}`,children:fe(e)},`${e.ts}-${a}`)})}),n.jsx(ie,{icon:"chevron-right",size:13,className:"an-ab-nav",disabled:D<=0,onClick:()=>W(e=>Math.max(0,e-1)),label:c("annotator.abNewer")}),n.jsx("span",{className:"an-ab-pos",children:D===0?c("annotator.abLatest"):c("annotator.abPos",{i:k.length-D,n:k.length})})]})]}),n.jsxs("div",{className:"an-work",children:[n.jsxs("div",{className:"an-stage-col",children:[h.expected&&n.jsxs("div",{className:"an-expected",children:[n.jsx("b",{children:c("nodeView.eval.expected")})," ",h.expected]}),O.length>0&&((Re=h.verdict)==null?void 0:Re.note)&&n.jsxs("div",{className:"an-expected an-prior-note",children:[n.jsx("b",{children:c("nodeView.eval.noteLabel")})," ",h.verdict.note]}),!h.fresh&&(((Te=h.staleAxes)==null?void 0:Te.length)??0)>0&&n.jsxs("div",{className:"an-expected an-stale","data-tip":c("nodeView.eval.staleReadoutTitle"),children:[n.jsx("b",{children:c("nodeView.eval.staleLabel")})," ",h.staleAxes.join(" · "),(((Le=h.codeDrift)==null?void 0:Le.length)??0)>0&&n.jsxs("span",{className:"an-stale-files",children:[" — ",h.codeDrift.map(e=>`${e.file.split("/").pop()} +${e.behind}`).join(", ")]})]}),V&&n.jsxs(n.Fragment,{children:[n.jsxs("div",{className:"an-player",ref:w,children:[n.jsxs("div",{className:`an-stage ${_?"playing":"paused"}`,ref:p,onMouseDown:ze,onMouseMove:Oe,onMouseUp:He,children:[n.jsx("video",{className:"an-video",ref:f,src:`/api/yatsu/blob/${V.hash}`,preload:"metadata",playsInline:!0}),H&&n.jsx("div",{className:"an-rect live",style:{left:`${H.x}%`,top:`${H.y}%`,width:`${H.w}%`,height:`${H.h}%`}}),!_&&!i&&n.jsx("div",{className:"an-bigplay","aria-hidden":!0,children:n.jsx(et,{name:"play",size:22})})]}),n.jsxs("div",{className:"an-bar",children:[n.jsx(ie,{icon:_?"pause":"play",size:14,className:"an-play",label:c(_?"annotator.pause":"annotator.play"),onClick:Z}),n.jsxs("div",{className:"an-seek",ref:j,onMouseDown:Ve,onMouseMove:Xe,onMouseLeave:()=>se(null),children:[n.jsx("div",{className:"an-seek-trk"}),z>0&&u.map((e,t)=>n.jsx("div",{className:"an-band",style:{left:`${e.tMs/z*100}%`},"data-tip":e.step},`band-${t}`)),n.jsx("div",{className:"an-seek-play",ref:I}),z>0&&$.map(e=>n.jsx("button",{type:"button",className:`an-mk ${q===e.i?"on":""} ${xe===e.i?"active":""}`,style:{left:`${e.tMs/z*100}%`},"data-tip":e.label,onMouseDown:t=>t.stopPropagation(),onClick:t=>{t.stopPropagation(),ke(e.i,e.tMs)}},`mk-${e.i}`)),n.jsx("div",{className:"an-knob",ref:P}),ne!=null&&z>0&&n.jsx("div",{className:"an-seek-hov",style:{left:`${ne}%`},children:J(ne/100*z)})]}),n.jsx("span",{className:"an-time",ref:L}),G&&n.jsx("span",{className:"an-curstep","data-tip":G.node?`→ ${G.node}`:void 0,children:G.step}),n.jsx(tt,{target:w})]})]}),u.length>0&&n.jsx("div",{className:"an-ruler",children:u.map((e,t)=>n.jsxs("button",{className:`an-step ${ve===t?"on":""}`,onClick:()=>U(e.tMs),"data-tip":e.node?`→ ${e.node}`:void 0,children:[J(e.tMs)," ",e.step]},t))}),n.jsx("div",{className:"an-hint",children:c("annotator.hint")}),n.jsx("div",{className:"an-keys",children:c("annotator.keys")}),y&&n.jsx("div",{className:"an-flash",children:y})]}),je.length>0&&n.jsx("div",{className:"an-gallery",children:je.map((e,t)=>n.jsx(Ae,{e,alt:l.scenario},`${e.hash}-${t}`))}),Fe.map((e,t)=>n.jsx(Ae,{e,alt:l.scenario},`${e.hash}-${t}`)),O.length===0&&((De=h.verdict)!=null&&De.note?n.jsx("pre",{className:"eval-transcript",children:h.verdict.note}):n.jsx("div",{className:"an-hint",children:c("nodeView.eval.noImage")}))]}),n.jsx(ct,{entry:l,comments:le,specs:v,sessions:m,onWrite:N,codeSha:h.codeSha,seekMs:C?U:null,anchorNow:C?Be:null,draft:T,selIdx:q,activeIdx:xe,onSelect:C?ke:null,events:C?u:null})]})]})}function ct({entry:l,comments:v,codeSha:m,specs:N,sessions:R,onWrite:c,seekMs:f,anchorNow:p,draft:j,selIdx:w,activeIdx:x,onSelect:u,events:M}){var y;const i=he(),g=(b,E)=>Ye({node:l.node,scenario:l.scenario,body:b,codeSha:m,evidence:E});return n.jsxs("aside",{className:"an-rail",children:[n.jsx("div",{className:"an-comments-head",children:i("annotator.comments",{n:v.length})}),n.jsx("div",{className:"an-rail-list",children:n.jsx(nt,{replies:v,onSeek:f,selIdx:w,activeIdx:x,onSelect:u,events:M,threadId:((y=l.thread)==null?void 0:y.id)??null,onRemarkChange:()=>c==null?void 0:c("")})}),n.jsx("div",{className:"an-rail-compose",children:n.jsx(st,{onSend:g,specs:N,sessions:R,focusId:l.node,onDone:c,anchorNow:p,draft:j},`${l.node}·${l.scenario}`)})]})}function Pe({rowKeys:l,sel:v,onSel:m,detail:N,children:R}){const[c,f]=s.useState(!1),p=s.useRef({});p.current={rowKeys:l,sel:v},s.useEffect(()=>{const w=x=>{var b;const u=(b=x.target)==null?void 0:b.tagName;if(u==="INPUT"||u==="TEXTAREA"||x.metaKey||x.ctrlKey||x.altKey||x.key!=="j"&&x.key!=="k")return;x.preventDefault(),x.stopPropagation();const{rowKeys:M,sel:i}=p.current;if(!M.length)return;const g=M.indexOf(i),y=g<0?x.key==="j"?0:M.length-1:Math.max(0,Math.min(M.length-1,g+(x.key==="j"?1:-1)));m(M[y])};return window.addEventListener("keydown",w,!0),()=>window.removeEventListener("keydown",w,!0)},[m]),s.useEffect(()=>{var w;(w=document.querySelector(".fv-list-col .sel"))==null||w.scrollIntoView({block:"nearest"})},[v]);const j=n.jsx(ue,{className:"fv-fold-inline",onToggle:()=>f(!0)});return n.jsxs("div",{className:`fv-master ${c?"folded":""}`,children:[c&&n.jsx(ue,{className:"fv-unfold",folded:!0,onToggle:()=>f(!1)}),n.jsx("div",{className:"fv-list-col",style:c?{display:"none"}:void 0,children:typeof R=="function"?R(j):n.jsxs(n.Fragment,{children:[n.jsx(ue,{className:"fv-fold",onToggle:()=>f(!0)}),R]})}),n.jsx("div",{className:"fv-detail",children:N})]})}function ut({specs:l=[],sessions:v=[],reloadBoard:m,onOpenSession:N}){const R=he(),{page:c,param:f}=at(),[p,j]=s.useState(null),[w,x]=s.useState(""),[u,M]=s.useState([]),i=s.useRef([]),g=s.useMemo(()=>new Map(u.map(d=>[ce(d),d])),[u]);i.current=u.map(ce);const y=p&&g.has(p)?p:i.current[0]??null,b=s.useMemo(()=>{if(!f)return null;const d=f.indexOf("/");return d>0?`eval:${f.slice(0,d)}·${f.slice(d+1)}`:null},[f]),[E,B]=s.useState(null);s.useEffect(()=>{c==="evals"&&b&&(j(b),B(b))},[c,b]),s.useEffect(()=>{E&&g.has(E)&&B(null)},[E,g]);const T=p&&!g.has(p),A=s.useMemo(()=>!T||lt(l).some(d=>ce(d)===p),[T,l,p]);s.useEffect(()=>{T&&!A&&(j(null),B(null))},[T,A]),s.useEffect(()=>{p&&!E&&u.length&&!g.has(p)&&j(null)},[p,E,u,g]),s.useEffect(()=>{if(c!=="evals"||!y||T)return;const d=/^eval:([^·]+)·(.+)$/.exec(y);d&&ot("evals",`${d[1]}/${d[2]}`,{replace:!0})},[c,y,T]);const I=s.useCallback(d=>M(d),[]),P=d=>{d&&(x(d),setTimeout(()=>x(""),6e3))},L=y?g.get(y):null;return n.jsx(Pe,{rowKeys:i.current,sel:y,onSel:j,detail:L?n.jsx(it,{entry:L,specs:l,sessions:v,onOpenSession:N,onWrite:async d=>{P(d),await(m==null?void 0:m())}}):n.jsx("div",{className:"fv-note",children:R("evalsFeed.empty")}),children:d=>n.jsxs(n.Fragment,{children:[w&&n.jsx("div",{className:"fv-notice",children:w}),n.jsx(rt,{nodes:l,sessions:v,sel:y,onSel:Q=>j(Q),onRows:I,mustShow:E,lead:d})]})})}const mt=Object.freeze(Object.defineProperty({__proto__:null,EvalMasterDetail:Pe,default:ut},Symbol.toStringTag,{value:"Module"}));export{it as E,Pe as a,mt as b};
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import{j as s,u as b,r as m,p as Se,s as z}from"./index-Dd0_U5rk.js";function ue(e){let n=2166136261;for(let a=0;a<(e||"").length;a++)n^=e.charCodeAt(a),n=Math.imul(n,16777619);return n>>>0}function he(e){return ue(e)%360}function Ee(e){const n=he(e);return{bg:`hsl(${n} 55% 42%)`,fg:`hsl(${n} 70% 92%)`}}function Ce(e){return`hsl(${he(e)} 65% 50%)`}const J=["◆","▲","●","■","★","✦","⬟","⬢","❖","◈","✸","⟡","✚","❂","◐","◑","⬣","▰","✶","⬤","✹","◇","⊛","✺"],X=["circle","rounded","square","hex"];function Ve(e){const n=ue(e);return{seed:e,glyph:J[(n>>>9)%J.length],shape:X[(n>>>17)%X.length],...Ee(e)}}function Le({seed:e,status:n,title:a,size:t=16}){const l=Ve(e),i={width:t,height:t};return s.jsx("span",{className:`avatar av-st-${n||"none"}`,"data-tip":a,"aria-label":a,style:i,children:s.jsx("span",{className:`av-face av-gen av-${l.shape}`,style:{...i,background:l.bg,color:l.fg,fontSize:t*.62},children:l.glyph})})}function Ae(e){var n,a;return((n=e==null?void 0:e.verdict)==null?void 0:n.status)==="pass"?"check":((a=e==null?void 0:e.verdict)==null?void 0:a.status)==="fail"?"cross":null}const Fe={pass:"✓",fail:"✗",stalePass:"✓",staleFail:"✗",empty:""};function pe(e){const n=Ae(e);return n?e.fresh?n==="cross"?"fail":"pass":n==="cross"?"staleFail":"stalePass":"empty"}function Z(e,n){if(!e)return[];const a=new Map;for(const t of n||[])a.has(t.scenario)||a.set(t.scenario,t);return e.map(t=>{const l=a.get(t.name)||null;return{...t,reading:l,state:l?pe(l):"missing"}})}function Ie(e){return e.length?e.some(n=>n.state==="fail")?"fail":e.some(n=>n.state==="staleFail")?"staleFail":e.some(n=>n.state==="stalePass")?"stalePass":e.some(n=>n.state==="empty"||n.state==="missing")?"empty":"pass":null}function Pe({scenarios:e,evals:n}){const a=b(),t=Z(e,n);if(!t.length)return null;const l=Ie(t),i=t.filter(r=>r.state==="pass").length,c=t.length,o=a("score.count",{satisfied:i,total:c,outstanding:c-i});return s.jsxs("span",{className:`scenario-count ${l}`,"data-tip":o,"aria-label":o,children:["✓",i,"/",c]})}function De({tags:e}){return e!=null&&e.length?s.jsx("span",{className:"tag-chips",children:e.map(n=>s.jsx("span",{className:"tag-chip",children:n},n))}):null}function Y({state:e,title:n}){const a=b();if(!e)return null;const t=n??a(`score.${e}`);return s.jsx("span",{className:`score-badge ${e}`,"data-tip":t,"aria-label":t,children:Fe[e]})}const Bs={merged:{color:"#859900"},active:{color:"#cb4b16"},drift:{color:"#b58900"},pending:{color:"#93a1a1"}},He={added:"+",edited:"~",deleted:"✕",moved:"→"},S={working:"var(--green)",parked:"var(--green)",asking:"var(--yellow)",review:"var(--yellow)",done:"var(--yellow)",error:"var(--red)",idle:"var(--muted)",starting:"var(--muted)",queued:"var(--muted)","close-pending":"var(--muted)",offline:"var(--muted)",unknown:"var(--yellow)"},Te={working:"●",parked:"‖",asking:"?",review:"◑",done:"✓",error:"✕",idle:"·",starting:"◌",queued:"⋯","close-pending":"⊘",offline:"○",unknown:"⁇"},fe=new Set(["asking","review","done","close-pending","error"]),B=e=>(e==null?void 0:e.liveness)==="offline"||(e==null?void 0:e.status)==="offline"?"offline":fe.has(e==null?void 0:e.status)?"need":"run",me=(e,n)=>{const a=n?(e||[]).find(t=>t.id===n):null;return a&&B(a)!=="offline"?a:null},ee=e=>(e==null?void 0:e.sortKey)!=null?e.sortKey:(e==null?void 0:e.created)??0,se=e=>{const n={need:0,run:1,offline:2};return[...e].sort((a,t)=>n[B(a)]-n[B(t)]||ee(t)-ee(a))},Be=e=>(e==null?void 0:e.label)||(e==null?void 0:e.name)||(e==null?void 0:e.node)||(e==null?void 0:e.title)||(e==null?void 0:e.branch)||(e==null?void 0:e.id),xe=e=>(e==null?void 0:e.headline)||(e==null?void 0:e.name)||(e==null?void 0:e.activity)||(e==null?void 0:e.promptPreview)||(e==null?void 0:e.node)||(e==null?void 0:e.title)||(e==null?void 0:e.branch)||(e==null?void 0:e.id);function Re(e){const n=new Set(e.map(l=>l==null?void 0:l.id)),a=new Map,t=[];for(const l of e){const i=l!=null&&l.parent&&l.parent!==l.id&&n.has(l.parent)?l.parent:null;if(i){const c=a.get(i)||[];c.push(l),a.set(i,c)}else t.push(l)}return{roots:t,childrenOf:a}}function ze(e,n){let a=!1,t=!1,l=0;const i=(c,o)=>{for(const r of n.get(c)||[])o.has(r.id)||(o.add(r.id),l++,fe.has(r.status)?a=!0:S[r.status]===S.working&&(t=!0),i(r.id,o))};return i(e,new Set([e])),{color:a?S.asking:t?S.working:S.idle,count:l}}function Ke(e,n){const{roots:a,childrenOf:t}=Re(e),l=[],i=(r,u,p,N)=>{const x=t.get(r.id)||[],k=x.length>0,y=k&&!!n(r.id),f=k?ze(r.id,t):null;if(l.push({type:"row",s:r,depth:u,expandable:k,expanded:y,rollup:(f==null?void 0:f.color)??null,kin:(f==null?void 0:f.count)??0,guides:N}),y){const v=se(x).filter(d=>!p.has(d.id));v.forEach((d,h)=>{p.add(d.id),i(d,u+1,p,[...N,h<v.length-1])})}},c=new Set;let o=null;for(const r of se(a)){if(c.has(r.id))continue;c.add(r.id);const u=B(r);u!==o&&(l.push({type:"zone",zone:u}),o=u),i(r,0,c,[])}return l}function Oe({value:e,onChange:n,options:a}){return s.jsx("select",{className:"fv-filter",value:e,onChange:t=>n(t.target.value),children:a.map(t=>s.jsx("option",{value:t.value,children:t.label},t.value))})}const We={video:"vid",image:"img",transcript:"txt"},ve=e=>{var n;return(n=e.evidence)!=null&&n.length?e.evidence:e.blob!=null?[{hash:e.blob,kind:e.blobKind||"image",state:e.blobState||"present"}]:[]},T=e=>{const n=ve(e);return n.length?["video","image","transcript"].filter(a=>n.some(t=>t.kind===a)):["note"]};function qe(e){var a;const n=[];for(const t of e)if((a=t.evals)!=null&&a.length)for(const l of Z(t.scenarios,t.evals))l.reading&&n.push({...l.reading,expected:l.expected??l.reading.expected,state:l.state,node:t.id,hue:t.hue});return n.sort((t,l)=>t.ts<l.ts?1:-1),n}const F=e=>`eval:${e.node}·${e.scenario}`;function _e({e,selected:n,onClick:a}){return s.jsxs("button",{className:`ef-row ${n?"sel":""}`,onClick:a,children:[s.jsx(Y,{state:e.state}),e.inSession&&s.jsx("span",{className:"ef-insession","data-tip":"measured by this session",children:"✦"}),s.jsx("span",{className:"ef-scenario","data-tip":e.scenario,children:e.scenario}),s.jsx("span",{className:"ef-node",style:{color:`hsl(${e.hue??210} 60% 70%)`},children:e.node}),s.jsx("span",{className:"ef-kind",children:T(e).map(t=>We[t]).filter(Boolean).join("·")}),s.jsx("span",{className:"ef-time",children:Ue(e.ts)})]})}const Ue=e=>{const n=Math.max(0,(Date.now()-new Date(e).getTime())/1e3);return n<3600?`${Math.max(1,Math.floor(n/60))}m`:n<86400?`${Math.floor(n/3600)}h`:`${Math.floor(n/86400)}d`};function Rs({nodes:e=[],sessions:n=[],sel:a,onSel:t,onRows:l,mustShow:i=null,lead:c=null}){const o=b(),[r,u]=m.useState(null),[p,N]=m.useState(!1),x=m.useMemo(()=>qe(e),[e]),k=x.some(w=>T(w).includes("video")),y=x.some(w=>T(w).includes("image")),f=r??(k?"video":y?"image":"all"),v=m.useMemo(()=>x.filter(w=>f==="all"||T(w).includes(f)),[x,f]),d=w=>!!me(n,w.by),h=m.useMemo(()=>v.filter(d).length,[v,n]),g=m.useMemo(()=>p?v.filter(d):v,[v,p,n]);return m.useEffect(()=>{l==null||l(g)},[g,l]),m.useEffect(()=>{i&&(g.some(w=>F(w)===i)||x.some(w=>F(w)===i)&&(u("all"),N(!1)))},[i,g,x]),s.jsxs("section",{className:"fv-group",children:[s.jsxs("header",{className:"fv-group-head",children:[s.jsxs("span",{className:"fv-head-row",children:[c,s.jsx(Oe,{value:f,onChange:u,options:["video","image","all"].map(w=>({value:w,label:o(`evalsFeed.kind.${w}`)}))})]}),h>0&&s.jsx("span",{className:"ef-chipbar",children:s.jsx("button",{type:"button",className:`ef-chip fv-live ${p?"on":""}`,onClick:()=>N(w=>!w),"data-tip":o("masterList.liveChipTitle"),children:o("masterList.liveChip",{n:h})})})]}),g.length===0&&s.jsx("div",{className:"ef-empty",children:o("evalsFeed.empty")}),g.map(w=>s.jsx(_e,{e:w,selected:a===F(w),onClick:()=>t(F(w),w)},F(w)))]})}const Ge={plus:{node:s.jsxs(s.Fragment,{children:[s.jsx("path",{d:"M5 12h14"}),s.jsx("path",{d:"M12 5v14"})]})},x:{node:s.jsxs(s.Fragment,{children:[s.jsx("path",{d:"M18 6 6 18"}),s.jsx("path",{d:"m6 6 12 12"})]})},"chevron-left":{node:s.jsx("path",{d:"m15 18-6-6 6-6"}),sw:2},"chevron-right":{node:s.jsx("path",{d:"m9 18 6-6-6-6"}),sw:2},download:{node:s.jsxs(s.Fragment,{children:[s.jsx("path",{d:"M12 15V3"}),s.jsx("path",{d:"m7 10 5 5 5-5"}),s.jsx("path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"})]})},clock:{node:s.jsxs(s.Fragment,{children:[s.jsx("circle",{cx:"12",cy:"12",r:"10"}),s.jsx("path",{d:"M12 6v6l4 2"})]})},search:{node:s.jsxs(s.Fragment,{children:[s.jsx("circle",{cx:"11",cy:"11",r:"8"}),s.jsx("path",{d:"m21 21-4.3-4.3"})]})},play:{node:s.jsx("path",{d:"M6 3.5 19.5 12 6 20.5Z"})},pause:{node:s.jsxs(s.Fragment,{children:[s.jsx("rect",{x:"6",y:"4",width:"4",height:"16",rx:"1"}),s.jsx("rect",{x:"14",y:"4",width:"4",height:"16",rx:"1"})]})},"panel-left":{sw:2,node:s.jsxs(s.Fragment,{children:[s.jsx("rect",{x:"1",y:"2",width:"22",height:"20",rx:"4"}),s.jsx("rect",{x:"4",y:"5",width:"2",height:"14",rx:"2",fill:"currentColor",stroke:"none"})]})},settings:{node:s.jsxs(s.Fragment,{children:[s.jsx("circle",{cx:"12",cy:"12",r:"3"}),s.jsx("path",{d:"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"})]})},graph:{vb:18,sw:1.4,node:s.jsxs(s.Fragment,{children:[s.jsx("rect",{x:"1.5",y:"6.5",width:"5",height:"4.4",rx:"1"}),s.jsx("rect",{x:"11.5",y:"1.8",width:"5",height:"4.4",rx:"1"}),s.jsx("rect",{x:"11.5",y:"11.8",width:"5",height:"4.4",rx:"1"}),s.jsx("path",{d:"M6.5 8.7 h2.2 M11.5 4 h-1.3 q-1.5 0-1.5 1.5 v7 q0 1.5 1.5 1.5 h1.3"})]})},sessions:{vb:18,sw:1.4,node:s.jsxs(s.Fragment,{children:[s.jsx("rect",{x:"1.5",y:"2.5",width:"15",height:"13",rx:"1.6"}),s.jsx("path",{d:"M4.6 6.5 l2.6 2.3 -2.6 2.3 M9 12.4 h4"})]})},evals:{vb:18,sw:1.4,node:s.jsxs(s.Fragment,{children:[s.jsx("path",{d:"M2.5 15.5 v-11"}),s.jsx("path",{d:"M2.5 15.5 h13"}),s.jsx("rect",{x:"4.6",y:"10",width:"2.6",height:"3.5",rx:"0.5"}),s.jsx("rect",{x:"8.7",y:"7",width:"2.6",height:"6.5",rx:"0.5"}),s.jsx("path",{d:"M13 6 l1.4 1.4 L16.5 3.6"})]})},issues:{vb:18,sw:1.4,node:s.jsxs(s.Fragment,{children:[s.jsx("path",{d:"M2.5 3.5 h13 v8.4 h-7 l-3.6 3 v-3 h-2.4 z"}),s.jsx("path",{d:"M5.4 6.7 h7.2 M5.4 9.2 h4.8"})]})},lock:{vb:16,sw:1.3,node:s.jsxs(s.Fragment,{children:[s.jsx("rect",{x:"3.5",y:"7",width:"9",height:"6.5",rx:"1.2"}),s.jsx("path",{d:"M5.5 7 V5 a2.5 2.5 0 0 1 5 0 V7"})]})},paperclip:{vb:16,sw:1.3,node:s.jsx("path",{d:"M12.5 7.2 L7 12.6 a2.6 2.6 0 0 1-3.7-3.7 L9 3.2 a1.7 1.7 0 0 1 2.4 2.4 L5.8 11.2 a0.8 0.8 0 0 1-1.2-1.2 L9.7 5"})},loader:{vb:16,sw:1.5,node:s.jsxs(s.Fragment,{children:[s.jsx("circle",{cx:"8",cy:"8",r:"5.5",opacity:"0.3"}),s.jsx("path",{d:"M8 2.5 a5.5 5.5 0 0 1 5.5 5.5"})]})},maximize:{vb:16,sw:1.4,node:s.jsxs(s.Fragment,{children:[s.jsx("path",{d:"M2 6V2h4"}),s.jsx("path",{d:"M14 6V2h-4"}),s.jsx("path",{d:"M2 10v4h4"}),s.jsx("path",{d:"M14 10v4h-4"})]})},minimize:{vb:16,sw:1.4,node:s.jsxs(s.Fragment,{children:[s.jsx("path",{d:"M6 2v4H2"}),s.jsx("path",{d:"M10 2v4h4"}),s.jsx("path",{d:"M6 14v-4H2"}),s.jsx("path",{d:"M10 14v-4h4"})]})}};function K({name:e,size:n=16,strokeWidth:a,className:t,style:l}){const i=Ge[e];if(!i)throw new Error(`unknown icon: ${e}`);const c=i.vb||24;return s.jsx("svg",{width:n,height:n,viewBox:`0 0 ${c} ${c}`,fill:"none",stroke:"currentColor",strokeWidth:a??i.sw??1.8,strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",className:t,style:l,children:i.node})}function zs({icon:e,label:n,onClick:a,className:t,size:l,iconClassName:i,...c}){return s.jsx("button",{type:"button",className:t,"data-tip":n,"aria-label":n,onClick:a,...c,children:s.jsx(K,{name:e,size:l,className:i})})}const Ks=()=>s.jsx("svg",{className:"si-agent-glyph",viewBox:"0 0 24 24","aria-hidden":"true",children:s.jsx("path",{d:"M17.3041 3.541h-3.6718l6.696 16.918H24Zm-10.6082 0L0 20.459h3.7442l1.3693-3.5527h7.0052l1.3693 3.5528h3.7442L10.5363 3.5409Zm-.3712 10.2232 2.2914-5.9456 2.2914 5.9456Z"})}),Os=()=>s.jsx("svg",{className:"si-agent-glyph",viewBox:"0 0 24 24","aria-hidden":"true",children:s.jsx("path",{d:"M22.282 9.821a6 6 0 0 0-.516-4.91a6.05 6.05 0 0 0-6.51-2.9A6.065 6.065 0 0 0 4.981 4.18a6 6 0 0 0-3.998 2.9a6.05 6.05 0 0 0 .743 7.097a5.98 5.98 0 0 0 .51 4.911a6.05 6.05 0 0 0 6.515 2.9A6 6 0 0 0 13.26 24a6.06 6.06 0 0 0 5.772-4.206a6 6 0 0 0 3.997-2.9a6.06 6.06 0 0 0-.747-7.073M13.26 22.43a4.48 4.48 0 0 1-2.876-1.04l.141-.081l4.779-2.758a.8.8 0 0 0 .392-.681v-6.737l2.02 1.168a.07.07 0 0 1 .038.052v5.583a4.504 4.504 0 0 1-4.494 4.494M3.6 18.304a4.47 4.47 0 0 1-.535-3.014l.142.085l4.783 2.759a.77.77 0 0 0 .78 0l5.843-3.369v2.332a.08.08 0 0 1-.033.062L9.74 19.95a4.5 4.5 0 0 1-6.14-1.646M2.34 7.896a4.5 4.5 0 0 1 2.366-1.973V11.6a.77.77 0 0 0 .388.677l5.815 3.354l-2.02 1.168a.08.08 0 0 1-.071 0l-4.83-2.786A4.504 4.504 0 0 1 2.34 7.872zm16.597 3.855l-5.833-3.387L15.119 7.2a.08.08 0 0 1 .071 0l4.83 2.791a4.494 4.494 0 0 1-.676 8.105v-5.678a.79.79 0 0 0-.407-.667m2.01-3.023l-.141-.085l-4.774-2.782a.78.78 0 0 0-.785 0L9.409 9.23V6.897a.07.07 0 0 1 .028-.061l4.83-2.787a4.5 4.5 0 0 1 6.68 4.66zm-12.64 4.135l-2.02-1.164a.08.08 0 0 1-.038-.057V6.075a4.5 4.5 0 0 1 7.375-3.453l-.142.08L8.704 5.46a.8.8 0 0 0-.393.681zm1.097-2.365l2.602-1.5l2.607 1.5v2.999l-2.597 1.5l-2.607-1.5Z"})}),P=e=>`/api/yatsu/blob/${e}`;function Ze({src:e,alt:n,onClose:a}){return m.useEffect(()=>{const t=l=>{l.key==="Escape"&&(l.preventDefault(),l.stopPropagation(),a())};return window.addEventListener("keydown",t,!0),()=>window.removeEventListener("keydown",t,!0)},[a]),s.jsx("div",{className:"lightbox",onClick:a,children:s.jsx("img",{src:e,alt:n})})}function Ye({hash:e}){const n=b(),[a,t]=m.useState(null);return m.useEffect(()=>{let l=!0;return fetch(P(e)).then(i=>i.ok?i.text():Promise.reject(new Error("miss"))).then(i=>{l&&t(i)}).catch(()=>{l&&t("")}),()=>{l=!1}},[e]),a===null?s.jsx("pre",{className:"eval-transcript loading",children:n("nodeView.eval.loadingTranscript")}):s.jsx("pre",{className:"eval-transcript",children:a})}function Qe({hash:e,alt:n}){const[a,t]=m.useState(!1);return s.jsxs(s.Fragment,{children:[s.jsx("img",{className:"an-image",src:P(e),alt:n,loading:"lazy",onClick:()=>t(!0)}),a&&s.jsx(Ze,{src:P(e),alt:n,onClose:()=>t(!1)})]})}function Je({exit:e}){return s.jsx(K,{name:e?"minimize":"maximize",size:15})}function Ws({target:e,className:n=""}){const a=b(),[t,l]=m.useState(!1);m.useEffect(()=>{const c=()=>{const o=document.fullscreenElement,r=e==null?void 0:e.current;l(!!o&&!!r&&(o===r||r.contains(o)))};return document.addEventListener("fullscreenchange",c),()=>document.removeEventListener("fullscreenchange",c)},[e]);const i=()=>{var o,r;const c=e==null?void 0:e.current;c&&(document.fullscreenElement?(o=document.exitFullscreen||document.webkitExitFullscreen)==null||o.call(document):(r=c.requestFullscreen||c.webkitRequestFullscreen)==null||r.call(c))};return s.jsx("button",{type:"button",className:`an-fs ${n}`,onClick:i,"data-tip":a(t?"annotator.exitFullscreen":"annotator.fullscreen"),"aria-label":a(t?"annotator.exitFullscreen":"annotator.fullscreen"),children:s.jsx(Je,{exit:t})})}function ge({e,alt:n=""}){const a=b();return e.state==="miss"?s.jsx("div",{className:"eval-noimg",children:a("nodeView.eval.miss")}):e.kind==="transcript"?s.jsx(Ye,{hash:e.hash}):e.kind==="video"?s.jsx("video",{className:"eval-video",src:P(e.hash),controls:!0,preload:"metadata",playsInline:!0}):s.jsx(Qe,{hash:e.hash,alt:n})}const D=new Map;function Xe(e){const[n,a]=m.useState(()=>D.get(e)??null);return m.useEffect(()=>{if(D.has(e)){a(D.get(e));return}let t=!0;return fetch(P(e),{headers:{Range:"bytes=0-0"}}).then(l=>{const i=l.headers.get("content-type")||"",c=l.ok?{kind:i.startsWith("video/")?"video":i.startsWith("image/")?"image":"transcript",state:"present"}:{kind:"image",state:"miss"};D.set(e,c),t&&a(c)}).catch(()=>{t&&a({kind:"image",state:"miss"})}),()=>{t=!1}},[e]),n}function je({hash:e,alt:n=""}){const a=Xe(e);return a?s.jsx(ge,{e:{hash:e,...a},alt:n}):null}const qs=/\[\[(\.?[A-Za-z0-9_-]+)\]\]/g,we=e=>(e||"").replace(/^\.spec\//,"").replace(/\/spec\.md$/,"");function es(e,n,a){const t=n.toLowerCase(),l=[];for(const i of e){const c=i.id.toLowerCase(),o=we(i.path).toLowerCase();let r;if(!t)r=3;else if(c.startsWith(t))r=0;else if(c.includes(t))r=1;else if(o.includes(t))r=2;else continue;i.id===a&&(r=-1),l.push({s:i,score:r})}return l.sort((i,c)=>i.score-c.score||i.s.id.length-c.s.id.length||i.s.id.localeCompare(c.s.id)),l.slice(0,8).map(i=>i.s)}function ss(e,n){const a=n.toLowerCase(),t=o=>xe(o)||(o.id||"").slice(0,8),l=[];for(const o of e||[]){if(o.liveness!=="online")continue;const r=(o.id||"").toLowerCase(),u=t(o).toLowerCase();let p;if(!a)p=3;else if(r===a||u===a)p=0;else if(r.startsWith(a)||u.startsWith(a))p=1;else if(r.includes(a)||u.includes(a))p=2;else continue;l.push({s:o,score:p})}l.sort((o,r)=>o.score-r.score||(r.s.created||0)-(o.s.created||0));const i=l.map(o=>({id:o.s.id,label:t(o.s),sub:o.s.node||o.s.status})),c=l.filter(o=>o.score===0).length;return i.splice(c,0,{id:"new",label:"new",sub:"spawn a fresh worker"}),i.slice(0,8)}function ne(e,n){if(!n)return e;const a=e.toLowerCase().indexOf(n.toLowerCase());return a<0?e:s.jsxs(s.Fragment,{children:[e.slice(0,a),s.jsx("b",{className:"mention-hit",children:e.slice(a,a+n.length)}),e.slice(a+n.length)]})}function ns(e,n,a,t){let l=n-1;for(;l>=0&&/[A-Za-z0-9_.\-]/.test(e[l]);)l--;if(l>=1&&e[l-1]==="["&&e[l]==="["){const i=e.slice(l+1,n),c=es(a,i,t);return c.length?{kind:"mention",items:c,index:0,start:l-1,end:n,query:i}:null}return null}function ts(e,n,a){let t=n-1;for(;t>=0&&/[A-Za-z0-9_.\-]/.test(e[t]);)t--;if(t>=0&&e[t]==="@"&&(t===0||/\s/.test(e[t-1]))){const l=e.slice(t+1,n),i=ss(a,l);return i.length?{kind:"actor",items:i,index:0,start:t,end:n,query:l}:null}return null}function as({menu:e,up:n,fixedStyle:a,onPick:t,onHover:l}){const i=b(),c=e.kind==="actor",o=e.query?c?`@${e.query}`:`[[${e.query}]]`:i(c?"session.menuSessions":"session.menuSpecNodes");return s.jsxs("ul",{className:`${n?"mention-menu up":"mention-menu"}${a?" fixed":""}`,style:a||void 0,role:"listbox",children:[s.jsxs("li",{className:"mention-head",children:["// ",o," — ",i("session.menuHint")]}),e.items.map((r,u)=>s.jsxs("li",{role:"option","aria-selected":u===e.index,className:`${u===e.index?"mention-item on":"mention-item"}${c&&r.id==="new"?" new":""}`,onMouseDown:p=>{p.preventDefault(),t(r)},onMouseEnter:()=>l(u),children:[!c&&s.jsx("span",{className:"mention-dot",style:{background:S[r.status]||S.offline}}),s.jsx("span",{className:"mention-id",children:c?s.jsxs(s.Fragment,{children:["@",ne(r.label,e.query)]}):ne(r.id,e.query)}),s.jsx("span",{className:"mention-path",children:c?r.sub:we(r.path)})]},r.id))]})}function ls({inputRef:e,value:n,setValue:a,specs:t=[],sessions:l=[],focusId:i=null,up:c=!1,fixedAbove:o=null}){const[r,u]=m.useState(null),[p,N]=m.useState(null),x=h=>{var j;if(!h){u(null),N(null);return}const g=h.selectionStart,w=ns(h.value,g,t,i)||ts(h.value,g,l);if(u(w),!w||!o){N(null);return}const M=h.getBoundingClientRect(),E=(j=h.closest(o))==null?void 0:j.getBoundingClientRect(),L=(E==null?void 0:E.top)??M.top;N({left:`${M.left}px`,width:`${M.width}px`,right:"auto",bottom:`${Math.max(8,window.innerHeight-L+8)}px`})},k=h=>u(g=>g&&{...g,index:(g.index+h+g.items.length)%g.items.length}),y=h=>{if(!h||!r)return;const g=r.kind==="actor"?`@${h.id} `:`[[${h.id}]] `,w=n.slice(0,r.start);a(w+g+n.slice(r.end)),u(null),N(null);const M=w.length+g.length;requestAnimationFrame(()=>{const E=e.current;E&&(E.focus(),E.setSelectionRange(M,M))})};return{menu:r,sync:x,onKeyDown:h=>r?h.key==="ArrowDown"?(h.preventDefault(),h.stopPropagation(),k(1),!0):h.key==="ArrowUp"?(h.preventDefault(),h.stopPropagation(),k(-1),!0):h.key==="Enter"||h.key==="Tab"?(h.preventDefault(),h.stopPropagation(),y(r.items[r.index]),!0):h.key==="Escape"?(h.preventDefault(),h.stopPropagation(),u(null),N(null),!0):!1:!1,close:()=>{u(null),N(null)},menuEl:r?s.jsx(as,{menu:r,up:c,fixedStyle:p,onPick:y,onHover:h=>u(g=>g&&{...g,index:h})}):null}}function is(e,n,a=0){e&&(e.style.height="auto",e.style.overflowY=e.scrollHeight>n?"auto":"hidden",e.style.height=`${Math.min(Math.max(e.scrollHeight,a),n)}px`)}const rs=/^▶\s*(\d+):([0-5]?\d)(?:\s*·\s*([^\n]*))?/,cs=/\/api\/yatsu\/blob\/([0-9a-f]{64})/g,te=/!\[[^\]]*\]\(\/api\/yatsu\/blob\/([0-9a-f]{64})\)/g,os=e=>{const n=Math.floor(e/1e3);return`${Math.floor(n/60)}:${String(n%60).padStart(2,"0")}`};function ye(e){var i;const n=e||"",a=n.split(`
|
|
2
|
-
`,1)[0],t=rs.exec(a);return t?{tMs:(parseInt(t[1],10)*60+parseInt(t[2],10))*1e3,step:((i=t[3])==null?void 0:i.trim())||null,label:a.trim(),rest:n.slice(a.length).replace(/^\n/,"")}:null}const Ne=(e,n)=>`▶${os(e)}${n?` · ${n}`:""}`,ae=e=>[...(e||"").matchAll(cs)].map(n=>n[1]);function ds(e,n){if(!e)return null;if(e.step&&(n!=null&&n.length)){const a=n.find(t=>t.step===e.step);return a?{tMs:a.tMs,step:e.step,label:Ne(a.tMs,e.step),seekable:!0,degraded:!1}:{tMs:e.tMs,step:e.step,label:e.label,seekable:!1,degraded:!0}}return{tMs:e.tMs,step:e.step,label:e.label,seekable:!0,degraded:!1}}function _s({originator:e,sessions:n=[],kind:a="issue",onOpenSession:t=null}){const l=b();if(!e)return null;const i=me(n,e),c=!!i,o=c?S[i.status]||S.working:S.offline,r=l(a==="eval"?"thread.originatorEval":"thread.originatorIssue",{by:e}),u=s.jsxs(s.Fragment,{children:[s.jsx("span",{className:"fv-originator-dot",style:{background:o},"aria-hidden":"true"}),s.jsx("span",{className:"fv-originator-who",children:e})]});return c&&t?s.jsx("button",{type:"button",className:"fv-originator alive openable","data-tip":r,"aria-label":r,onClick:()=>t(e),children:u}):s.jsx("span",{className:`fv-originator ${c?"alive":"offline"}`,"data-tip":r,children:u})}function us({replies:e,onSeek:n,selIdx:a=null,activeIdx:t=null,onSelect:l=null,events:i=null,threadId:c=null,onRemarkChange:o=null}){const r=b(),[u,p]=m.useState(null),[N,x]=m.useState(null),k=async(y,f)=>{if(!u){p(f),x(null);try{const v=await Se(y,f);v!=null&&v.ok?await(o==null?void 0:o()):x({ref:f,msg:(v==null?void 0:v.error)||`${y} failed`})}finally{p(null)}}};return e.map((y,f)=>{const v=ye(y.body),d=ds(v,i),h=v?v.rest:y.body,g=[...h.matchAll(te)].map(C=>C[1]),w=(g.length?h.replace(te,""):h).trim(),M=y.rid!==void 0,E=M?y.resolved?" remark resolved":" remark open":"",L=`fv-reply${a===f?" sel":""}${t===f?" active":""}${E}`,j=d&&d.seekable&&n?()=>l?l(f,d.tMs):n(d.tMs):null,$=M&&c?`${c}#${y.rid}`:null;return s.jsxs("div",{className:L,children:[s.jsxs("div",{className:"fv-reply-meta",children:[s.jsx("span",{className:"fv-reply-by",children:y.by}),y.at&&s.jsx("span",{className:"fv-reply-at",children:y.at}),d&&(j?s.jsx("button",{type:"button",className:"fv-anchor",onClick:j,"data-tip":"seek the clip to this moment",children:d.label}):s.jsxs("span",{className:`fv-anchor static${d.degraded?" degraded":""}`,"data-tip":d.degraded?r("thread.anchorDegraded"):void 0,children:[d.label,d.degraded?" ⚠":""]})),M&&(y.resolved?s.jsxs("span",{className:"fv-remark-state resolved","data-tip":y.resolvedBy?r("thread.resolvedBy",{by:y.resolvedBy}):r("thread.resolved"),children:["✓ ",r("thread.resolved")]}):s.jsxs("span",{className:"fv-remark-state open","data-tip":r("thread.openRemark"),children:["● ",r("thread.openRemark")]})),$&&o&&!y.resolved&&(y.by==="human"?s.jsx("button",{type:"button",className:"fv-remark-act retract",disabled:!!u,"data-tip":r("thread.retractTitle"),onClick:()=>k("retract",$),children:r("thread.retract")}):s.jsx("button",{type:"button",className:"fv-remark-act resolve",disabled:!!u,"data-tip":r("thread.resolveTitle"),onClick:()=>k("resolve",$),children:r("thread.resolve")}))]}),N&&N.ref===$&&s.jsx("div",{className:"fv-error",children:N.msg}),w&&s.jsx("div",{className:"fvd-body",children:s.jsx(R,{body:w})}),g.length>0&&s.jsx("div",{className:"fv-reply-media",children:g.map((C,A)=>s.jsx(je,{hash:C,alt:"evidence"},`${C}-${A}`))})]},f)})}function Us({onSend:e,specs:n=[],sessions:a=[],focusId:t=null,onDone:l,anchorNow:i=null,draft:c=null,actionsEnd:o=null}){const r=b(),[u,p]=m.useState(""),[N,x]=m.useState(!1),[k,y]=m.useState(""),[f,v]=m.useState(!1),d=m.useRef(null),h=ls({inputRef:d,value:u,setValue:p,specs:n,sessions:a,focusId:t,up:!0}),g=ae(u),w=f||!!u||g.length>0||!!k,M=w||!!o;m.useEffect(()=>{const j=d.current;if(!j)return;const $=getComputedStyle(j);is(j,parseFloat($.maxHeight)||1/0,parseFloat($.minHeight)||0)},[u,M]),m.useEffect(()=>{var j;if(!c){p("");return}p(c.body||""),(j=d.current)==null||j.focus()},[c==null?void 0:c.seq]);const E=()=>{var C;const j=i==null?void 0:i();if(!j)return;const $=Ne(j.tMs,j.step);p(A=>{const Q=ye(A);return Q?`${$}
|
|
3
|
-
${Q.rest}`:A?`${$}
|
|
4
|
-
${A}`:`${$}
|
|
5
|
-
`}),(C=d.current)==null||C.focus()},L=async()=>{const j=u.trim();if(!(!j||N)){x(!0);try{const $=await e(j,ae(j));$!=null&&$.ok?(p(""),y(""),await(l==null?void 0:l($.outcomes||""))):y(($==null?void 0:$.error)||"reply failed")}finally{x(!1)}}};return s.jsxs("div",{className:`fv-compose${w?" engaged":""}`,children:[g.length>0&&s.jsx("div",{className:"fv-frames",children:g.map(j=>s.jsx(je,{hash:j,alt:"frame"},j))}),s.jsxs("div",{className:"fv-tawrap",children:[s.jsx("textarea",{ref:d,className:"fv-textarea",rows:1,value:u,placeholder:r("session.issuesReplyPlaceholder"),disabled:N,onChange:j=>{p(j.target.value),h.sync(j.target)},onSelect:j=>h.sync(j.target),onFocus:()=>v(!0),onBlur:()=>{v(!1),h.close()},onKeyDown:j=>{h.onKeyDown(j)||j.key==="Enter"&&(j.metaKey||j.ctrlKey)&&(j.preventDefault(),L())}}),h.menuEl]}),M&&s.jsxs("div",{className:"fv-actions",children:[i&&s.jsxs("button",{type:"button",className:"fv-anchor-btn","data-tip":r("thread.anchorTitle"),onMouseDown:j=>j.preventDefault(),onClick:E,children:[s.jsx(K,{name:"clock",size:11})," ",r("thread.anchorNow")]}),s.jsx("span",{className:"fv-hint",children:k||r("session.issuesMentionHint")}),s.jsx("button",{type:"button",className:"fv-send",disabled:N||!u.trim(),onMouseDown:j=>j.preventDefault(),onClick:L,children:r(N?"session.issuesSending":"session.issuesSend")}),o]})]})}const hs=["graph","sessions","evals","issues","settings"];function G(e){const n=(e||"").replace(/^#\/?/,"").split("/").filter(Boolean),a=hs.includes(n[0])?n[0]:"graph",t=a==="sessions"?n[1]||null:a==="evals"?n.length>1?n.slice(1).map(decodeURIComponent).join("/"):null:a==="issues"&&n.length>1?n.slice(1).map(decodeURIComponent).join("/"):null;return{page:a,param:t}}const V=(e,n)=>`#/${e}${n?`/${String(n).split("/").map(encodeURIComponent).join("/")}`:""}`;function O(e,n=null,{replace:a=!1}={}){const t=V(e,n);window.location.hash!==t&&(a?window.history.replaceState(null,"",t):window.location.hash=t)}function Gs(){const[e,n]=m.useState(()=>G(window.location.hash));return m.useEffect(()=>{const a=()=>n(G(window.location.hash));return window.addEventListener("hashchange",a),()=>window.removeEventListener("hashchange",a)},[]),e}const Zs=e=>({kind:"graph-node",nodeId:e}),Ys=e=>({kind:"session",sessionId:e}),ps=e=>({kind:"issue",issueId:e}),Qs=(e,n)=>({kind:"eval",nodeId:e,scenario:n});function be(e){return!e||e.kind==="graph-node"?V("graph"):e.kind==="session"?V("sessions",e.sessionId):e.kind==="issue"?V("issues",e.issueId):e.kind==="eval"?V("evals",`${e.nodeId}/${e.scenario}`):V("graph")}function Js(e,{onFocusNode:n,onOpenSession:a}={}){if(e)if(e.kind==="graph-node")n==null||n(e.nodeId),O("graph");else if(e.kind==="session")a?a(e.sessionId):O("sessions",e.sessionId);else{const{page:t,param:l}=G(be(e));O(t,l)}}function le({issue:e,onNavigateAddress:n}){const a=(e==null?void 0:e.store)||"local",t=(e==null?void 0:e.status)||"open",l=ps(e.id);return s.jsxs("a",{className:"issue-card",href:be(l),"data-tip":e.concern||e.id,onClick:n?i=>{i.defaultPrevented||i.button!==0||i.metaKey||i.ctrlKey||i.shiftKey||i.altKey||(i.preventDefault(),n(l))}:void 0,children:[s.jsxs("span",{className:"issue-card-top",children:[s.jsx("span",{className:"issue-num",children:e.id}),s.jsx("span",{className:`fv-store fv-store-${a==="local"?"local":"forge"}`,children:a}),s.jsx("span",{className:`issue-state st-${t}`,children:t})]}),s.jsx("span",{className:"issue-card-title",children:e.concern})]})}const ie=[{key:"spec",label:"spec"},{key:"history",label:"history"},{key:"issues",label:"issues"},{key:"eval",label:"eval"}];function fs(e){var n;return(n=e==null?void 0:e.overlays)!=null&&n.length?[{key:"edit",label:"edit"},...ie]:ie}const ms={added:"+",edited:"~",deleted:"✕",moved:"→"};function I(e){const n=[],a=/`([^`]+)`|\*\*([^*]+)\*\*|\[\[([^\]]+)\]\]/g;let t=0,l,i=0;for(;l=a.exec(e);)l.index>t&&n.push(e.slice(t,l.index)),l[1]!=null?n.push(s.jsx("code",{children:l[1]},i++)):l[2]!=null?n.push(s.jsx("strong",{children:l[2]},i++)):n.push(s.jsx("span",{className:"doc-link",children:l[3]},i++)),t=a.lastIndex;return t<e.length&&n.push(e.slice(t)),n}function re(e){const n=e.trim();return n.includes("|")&&/^\|?(\s*:?-+:?\s*\|)+(\s*:?-+:?\s*)?$/.test(n)}function W(e){let n=e.trim();return n.startsWith("|")&&(n=n.slice(1)),n.endsWith("|")&&(n=n.slice(0,-1)),n.split("|").map(a=>a.trim())}function xs(e){const n=e.startsWith(":"),a=e.endsWith(":");return n&&a?"center":a?"right":null}function R({body:e}){if(!e)return null;const n=e.replace(/^#\s+[^\n]*\n+/,"").split(`
|
|
6
|
-
`),a=[];let t=0,l=0;for(;t<n.length;){const i=n[t].trim();if(/^```/.test(i)){const c=[];for(t++;t<n.length&&!/^```/.test(n[t].trim());)c.push(n[t++]);t++,a.push(s.jsx("pre",{className:"doc-pre",children:s.jsx("code",{children:c.join(`
|
|
7
|
-
`)})},l++))}else if(/^#{1,6}\s+/.test(n[t]))a.push(s.jsx("h4",{className:"doc-h",children:I(n[t].replace(/^#+\s+/,""))},l++)),t++;else if(i.includes("|")&&t+1<n.length&&re(n[t+1])){const c=W(n[t]),o=W(n[t+1]).map(xs);t+=2;const r=[];for(;t<n.length&&n[t].includes("|")&&n[t].trim()!==""&&!/^```/.test(n[t].trim());)r.push(W(n[t])),t++;a.push(s.jsxs("table",{className:"doc-table",children:[s.jsx("thead",{children:s.jsx("tr",{children:c.map((u,p)=>s.jsx("th",{style:o[p]?{textAlign:o[p]}:void 0,children:I(u)},p))})}),s.jsx("tbody",{children:r.map((u,p)=>s.jsx("tr",{children:c.map((N,x)=>s.jsx("td",{style:o[x]?{textAlign:o[x]}:void 0,children:I(u[x]??"")},x))},p))})]},l++))}else if(/^-\s+/.test(i)){const c=[];for(;t<n.length&&/^-\s+/.test(n[t].trim());)c.push(n[t++].trim().replace(/^-\s+/,""));a.push(s.jsx("ul",{children:c.map((o,r)=>s.jsx("li",{children:I(o)},r))},l++))}else if(i==="")t++;else{const c=[];for(;t<n.length;){const o=n[t];if(o.trim()===""||/^```/.test(o.trim())||/^#{1,6}\s+/.test(o)||/^-\s+/.test(o.trim())||o.includes("|")&&t+1<n.length&&re(n[t+1]))break;c.push(o),t++}a.push(s.jsx("p",{children:I(c.join(" "))},l++))}}return s.jsx("div",{className:"doc-body",children:a})}function ce({kind:e,title:n,owner:a,ownerLabel:t,note:l,children:i}){return s.jsxs("section",{className:`spec-part part-${e}`,children:[s.jsxs("header",{className:"part-head",children:[s.jsx("span",{className:"part-title",children:n}),s.jsx("span",{className:`part-owner owner-${a}`,children:t}),l&&s.jsx("span",{className:"part-note",children:l})]}),s.jsx("div",{className:"part-body",children:i})]})}function vs({parts:e}){const n=b();return s.jsxs("div",{className:"spec-parts",children:[s.jsx(ce,{kind:"raw",title:n("nodeView.rawTitle"),owner:"human",ownerLabel:n("nodeView.rawOwner"),note:n("nodeView.rawNote"),children:s.jsx(R,{body:e.rawSource})}),s.jsx(ce,{kind:"expanded",title:n("nodeView.expandedTitle"),owner:"agent",ownerLabel:n("nodeView.expandedOwner"),note:n("nodeView.expandedNote"),children:s.jsx(R,{body:e.expandedSpec})})]})}const q=new Map;function gs(e,n){const a=`${e}@${n??""}`,[t,l]=m.useState(()=>q.get(a)??null);return m.useEffect(()=>{const i=q.get(a);if(i){l(i);return}l(null);let c=!0;return fetch(z(e,"content")).then(o=>o.ok?o.json():Promise.reject(o.status)).then(o=>{q.set(a,o),c&&l(o)}).catch(()=>{c&&l({body:"",parts:null})}),()=>{c=!1}},[e,n,a]),t}function js({node:e}){var l;const n=b(),a=gs(e.id,e.version),t=(e.driftFiles||[]).map(i=>`${i.file}: ${n("specNode.driftAhead",{n:i.behind})}`).join(`
|
|
8
|
-
`);return s.jsxs("div",{className:"pane-doc",children:[s.jsxs("h1",{children:["# ",e.title]}),s.jsx("blockquote",{children:e.desc}),s.jsxs("div",{className:"doc-stat",children:[s.jsxs("span",{className:`stat-status st-${e.status}`,"data-tip":n("nodeView.statusLabel"),children:[s.jsx("i",{className:"stat-dot"}),n(`status.${e.status}`)]}),s.jsxs("span",{className:"stat-chip","data-tip":n("nodeView.versionLabel"),children:["v",e.version||0]}),s.jsx(Pe,{scenarios:e.scenarios,evals:e.evals}),e.drift>0&&s.jsxs("span",{className:"stat-chip stat-drift","data-tip":t,children:["⚠",e.drift]}),s.jsxs("span",{className:"stat-sess","data-tip":n("nodeView.lastEditedBy"),children:["✎ ",s.jsx("b",{children:e.session||n("common.none")})]})]}),((l=e.code)==null?void 0:l.length)>0?s.jsxs("div",{className:"doc-gov",children:[s.jsxs("span",{className:"doc-gov-h",children:[n("nodeView.governs")," ",s.jsx("b",{children:e.code.length})]}),s.jsx("div",{className:"doc-gov-files",children:e.code.map(i=>s.jsx("code",{className:"gov-f",children:i},i))})]}):s.jsx("div",{className:"doc-gov prose",children:s.jsx("span",{className:"doc-gov-h",children:n("nodeView.proseNode")})}),(()=>{if(a===null&&e.body==null)return s.jsx("div",{className:"pane-loading",children:s.jsx("span",{className:"spinner","aria-label":n("common.loading")})});const i=e.body??(a==null?void 0:a.body)??"",c=e.parts??(a==null?void 0:a.parts)??null;return c?s.jsx(vs,{parts:c}):s.jsx(R,{body:i})})()]})}function ws(e,n=!0){const[a,t]=m.useState(null);return m.useEffect(()=>{if(!n)return;let l=!0;return fetch(z(e,"history")).then(i=>i.json()).then(i=>{l&&t(i)}).catch(()=>l&&t([])),()=>{l=!1}},[e,n]),a}const _=new Map;function ys(e,n,a){const t=`${e}/${n}`,[l,i]=m.useState(()=>_.get(t)??null);return m.useEffect(()=>{const c=_.get(t);if(c){i(c);return}let o=!0;return fetch(z(e,"diff",n)).then(r=>r.json()).then(r=>{_.set(t,r),o&&i(r)}).catch(()=>o&&i({patch:""})),()=>{o=!1}},[e,n,a,t]),l}function Ns(e){const n=[];let a=!1;for(const t of e.split(`
|
|
9
|
-
`)){if(t.startsWith("@@")){a=!0,n.push({t:"hunk",s:t});continue}!a||t.startsWith("\\")||(t.startsWith("+")?n.push({t:"add",s:t.slice(1)}):t.startsWith("-")?n.push({t:"del",s:t.slice(1)}):n.push({t:"ctx",s:t.slice(1)}))}for(;n.length&&n[n.length-1].t==="ctx"&&n[n.length-1].s==="";)n.pop();return n}function ke({diff:e}){const n=b();if(e==null)return s.jsx("figcaption",{className:"ev-note",children:n("nodeView.loadingChange")});const a=e.patch?Ns(e.patch):[];return a.length?s.jsxs(s.Fragment,{children:[s.jsx("figcaption",{className:"ev-difflabel",children:n("nodeView.diffLabel")}),s.jsx("pre",{className:"ev-diff",children:a.map((t,l)=>s.jsx("div",{className:`dl dl-${t.t}`,children:t.s||" "},l))})]}):s.jsx("figcaption",{className:"ev-note",children:n("nodeView.noChange")})}function $e({items:e,itemKey:n,classes:a,rowClass:t,renderHeader:l,renderEvidence:i,leading:c,trailing:o,resetKey:r}){const u=b(),p=m.useRef(null),[N,x]=m.useState(()=>new Set([0]));m.useEffect(()=>{r!==void 0&&x(new Set([0]))},[r]);const k=m.useCallback(f=>x(v=>{const d=new Set(v);return d.has(f)?d.delete(f):d.add(f),d}),[]),y=m.useCallback(()=>x(f=>{const v=p.current;if(!v)return f;let d=-1;for(;f.has(d+1);)d++;if(d<0||d>=e.length-1)return f;const h=v.querySelector(`[data-i="${d}"]`);return!h||h.getBoundingClientRect().bottom-v.getBoundingClientRect().top>v.clientHeight+40?f:new Set(f).add(d+1)}),[e]);return m.useEffect(()=>{const f=p.current;if(!f)return;let v=f.scrollTop;const d=()=>{const g=f.scrollTop,w=g>v;v=g,w&&y()},h=g=>{g.key!=="j"&&g.key!=="ArrowDown"||f.scrollHeight-f.clientHeight-f.scrollTop>1||y()};return f.addEventListener("scroll",d,{passive:!0}),window.addEventListener("keydown",h,!0),()=>{f.removeEventListener("scroll",d),window.removeEventListener("keydown",h,!0)}},[y]),s.jsxs("div",{className:a.pane,ref:p,children:[e.length>1&&N.size<e.length&&s.jsx("div",{className:"chrono-tools",children:s.jsx("button",{className:"chrono-all",onClick:()=>x(new Set(e.keys())),children:u("nodeView.expandAll",{n:e.length})})}),c,e.map((f,v)=>{const d=N.has(v),h=t?t(f,v):"";return s.jsxs("div",{"data-i":v,className:`${a.row}${h?` ${h}`:""}${d?" open":""}`,children:[s.jsx("button",{className:a.head,onClick:()=>k(v),"aria-expanded":d,children:l(f,v,d)}),d&&s.jsx("figure",{className:a.evidence,children:i(f,v)})]},n(f,v))}),o]})}function bs({node:e,r:n,latest:a}){const t=ys(e.id,n.hash,!0);return s.jsx(ke,{diff:t})}function ks({node:e,rows:n}){const a=b();return n?n.length?s.jsx($e,{items:n,itemKey:t=>t.hash,classes:{pane:"pane-hist",row:"ver-row",head:"rec-toggle",evidence:"rec-evidence"},rowClass:(t,l)=>l===0?"latest":"",renderHeader:(t,l,i)=>s.jsxs(s.Fragment,{children:[s.jsxs("div",{className:"rec-head",children:[s.jsx("span",{className:"rec-caret",children:i?"▾":"▸"}),s.jsxs("span",{className:"rec-v",children:["v",n.length-l]}),s.jsx("code",{className:"rec-hash",children:t.hash.slice(0,7)}),s.jsx("span",{className:"rec-date",children:(t.date||"").slice(0,10)}),s.jsxs("span",{className:"rec-diff",children:[s.jsxs("b",{className:"rec-add",children:["+",t.additions??0]}),s.jsxs("b",{className:"rec-del",children:["−",t.deletions??0]})]})]}),s.jsx("div",{className:"rec-msg",children:t.reason}),s.jsxs("div",{className:"rec-sub",children:[a("nodeView.filesChanged",{n:t.files??0})," · ",t.session||a("common.idle")]})]}),renderEvidence:(t,l)=>s.jsx(bs,{node:e,r:t,latest:l===0})}):s.jsx("div",{className:"pane-hist empty",children:a("common.noVersions")}):s.jsx("div",{className:"pane-hist empty",children:a("nodeView.loadingHistory")})}function Me({q:e,setQ:n,placeholder:a}){return s.jsx("div",{className:"pane-filter",children:s.jsx("input",{className:"pane-filter-input",value:e,placeholder:a,onChange:t=>n(t.target.value)})})}function $s({node:e}){const n=b(),[a,t]=m.useState(""),l=e.issues||[];if(!l.length)return s.jsx("div",{className:"pane-issues empty",children:n("nodeView.noIssues")});const i=a.trim().toLowerCase(),c=i?l.filter(u=>`${u.id} ${u.concern||""}`.toLowerCase().includes(i)):l,o=c.filter(u=>u.status==="open"),r=c.filter(u=>u.status!=="open");return s.jsxs("div",{className:"pane-issues",children:[l.length>5&&s.jsx(Me,{q:a,setQ:t,placeholder:n("nodeView.filterIssues")}),i&&!c.length&&s.jsx("div",{className:"pane-filter-none",children:n("nodeView.filterNone")}),o.length>0&&s.jsxs(s.Fragment,{children:[s.jsx("div",{className:"issue-group-head",children:n("nodeView.openIssues",{n:o.length})}),o.map(u=>s.jsx(le,{issue:u},u.id))]}),r.length>0&&s.jsxs(s.Fragment,{children:[s.jsx("div",{className:"issue-group-head closed",children:n("nodeView.closedIssues",{n:r.length})}),r.map(u=>s.jsx(le,{issue:u},u.id))]})]})}const U=new Map;function Ms(e,n,a){const t=`${e} ${n}`,[l,i]=m.useState(()=>U.get(t)??null);return m.useEffect(()=>{if(!e||!n)return;const c=U.get(t);c&&i(c);let o=!0;return fetch(`/api/edit?source=${encodeURIComponent(e)}&path=${encodeURIComponent(n)}`).then(r=>r.json()).then(r=>{U.set(t,r),o&&i(r)}).catch(()=>o&&i(r=>r??{patch:""})),()=>{o=!1}},[e,n,a,t]),l}function Ss({node:e,ov:n}){const a=b(),t=Ms(n.source,e.path,!0);return s.jsxs("figure",{className:"edit-rev",children:[s.jsxs("figcaption",{className:"edit-by",children:[s.jsx("span",{className:`ov-mark ov-${n.op}`,children:ms[n.op]||"•"}),s.jsx("span",{className:"edit-by-label",children:n.label}),s.jsx("span",{className:"edit-state",children:n.committed?a("nodeView.editCommitted"):a("nodeView.editDirty")})]}),s.jsx(ke,{diff:t})]})}function Es({node:e}){const n=b(),a=e.overlays||[];return a.length?s.jsx("div",{className:"pane-edit",children:a.map((t,l)=>s.jsx(Ss,{node:e,ov:t},l))}):s.jsx("div",{className:"pane-edit empty",children:n("nodeView.noEdit")})}function Cs({verdict:e}){const n=b();return e?e.status==="pass"?s.jsx("span",{className:"eval-verdict pass",children:n("nodeView.eval.pass")}):e.status==="fail"?s.jsx("span",{className:"eval-verdict fail",children:n("nodeView.eval.fail")}):s.jsx("span",{className:"eval-verdict note","data-tip":e.note,children:n("nodeView.eval.note")}):s.jsx("span",{className:"eval-verdict legacy",children:n("nodeView.eval.legacy")})}function Vs({r:e}){var t;const n=b(),a=ve(e);return s.jsxs(s.Fragment,{children:[e.expected&&s.jsxs("div",{className:"eval-expected",children:[s.jsx("span",{className:"eval-expected-label",children:n("nodeView.eval.expected")})," ",e.expected]}),((t=e.verdict)==null?void 0:t.note)&&s.jsxs("div",{className:"eval-note",children:[s.jsx("span",{className:"eval-expected-label",children:n("nodeView.eval.noteLabel")})," ",e.verdict.note]}),a.length>0?s.jsx("div",{className:"eval-gallery",style:{display:"flex",flexDirection:"column",gap:8},children:a.map((l,i)=>s.jsx(ge,{e:l,alt:n("nodeView.eval.shotAlt",{scenario:e.scenario})},`${l.hash}-${i}`))}):s.jsx("figcaption",{className:"eval-noimg",children:e.blobState==="miss"?n("nodeView.eval.miss"):n("nodeView.eval.noImage")})]})}function oe({s:e}){var a;const n=b();return s.jsxs("div",{className:"eval-row eval-declared-row",children:[s.jsxs("span",{className:"eval-top",children:[s.jsx(Y,{state:"empty",title:n("score.missing")}),s.jsx("span",{className:"eval-scenario",children:e.name}),s.jsx(De,{tags:e.tags}),((a=e.code)==null?void 0:a.length)>0&&s.jsx("code",{className:"eval-tracks",children:e.code.join(", ")})]}),e.expected&&s.jsxs("div",{className:"eval-expected",children:[s.jsx("span",{className:"eval-expected-label",children:n("nodeView.eval.expected")})," ",e.expected]})]})}function de({track:e}){const n=b();return s.jsxs("div",{className:"eval-row eval-dangling-row",children:[s.jsxs("span",{className:"eval-top",children:[s.jsx("span",{className:"eval-dangling-badge","data-tip":n("nodeView.eval.danglingTitle"),children:"⚠"}),s.jsx("span",{className:"eval-scenario eval-dangling-name",children:e.scenario}),s.jsx("span",{className:"eval-dangling-tag",children:n("nodeView.eval.danglingGone")})]}),s.jsx("div",{className:"eval-dangling-remarks",children:s.jsx(us,{replies:e.remarks})})]})}const H=new Map;function Ls({node:e}){var y,f,v;const n=b(),[a,t]=m.useState(""),l=`${e.id}@${((f=(y=e.evals)==null?void 0:y[0])==null?void 0:f.ts)||""}:${((v=e.evals)==null?void 0:v.length)||0}`,[i,c]=m.useState(()=>H.get(l)??null);if(m.useEffect(()=>{if(H.has(l)){c(H.get(l));return}let d=!0;return c(null),fetch(z(e.id,"evals")).then(h=>h.ok?h.json():Promise.reject(h.status)).then(h=>{const g={scenarios:h.scenarios||e.scenarios||[],readings:h.readings||[],dangling:h.dangling||[]};H.set(l,g),d&&c(g)}).catch(()=>{d&&c({scenarios:e.scenarios||[],readings:e.evals||[],dangling:[]})}),()=>{d=!1}},[l,e.id]),!e.evals)return s.jsx("div",{className:"pane-eval empty",children:n("nodeView.eval.noScenarios")});if(i===null)return s.jsx("div",{className:"pane-eval pane-loading",children:s.jsx("span",{className:"spinner","aria-label":n("common.loading")})});const o=i.readings,r=a.trim().toLowerCase(),u=d=>!r||(d||"").toLowerCase().includes(r),p=r?o.filter(d=>u(d.scenario)):o,N=(i.dangling||[]).filter(d=>u(d.scenario)),x=Z(i.scenarios,o).filter(d=>!d.reading&&u(d.name)),k=i.readings.length+i.scenarios.length>5?s.jsx(Me,{q:a,setQ:t,placeholder:n("nodeView.filterScenarios")},"filter"):null;return o.length?s.jsx($e,{items:p,resetKey:r,leading:[k,r&&!p.length&&!x.length&&!N.length?s.jsx("div",{className:"pane-filter-none",children:n("nodeView.filterNone")},"none"):null,...x.map(d=>s.jsx(oe,{s:d},d.name))],trailing:N.map(d=>s.jsx(de,{track:d},d.threadId)),itemKey:(d,h)=>`${d.scenario}-${d.ts}-${h}`,classes:{pane:"pane-eval",row:"eval-row",head:"eval-head",evidence:"eval-shot"},renderHeader:(d,h,g)=>s.jsxs(s.Fragment,{children:[s.jsxs("span",{className:"eval-top",children:[s.jsx("span",{className:"eval-caret",children:g?"▾":"▸"}),s.jsx("span",{className:"eval-scenario",children:d.scenario}),s.jsx(Cs,{verdict:d.verdict}),s.jsx(Y,{state:pe(d),title:d.fresh?void 0:n("nodeView.eval.staleAxes",{axes:d.staleAxes.join(", ")})})]}),s.jsxs("span",{className:"eval-meta",children:[s.jsx("span",{className:"eval-evaluator",children:d.evaluator}),s.jsx("code",{className:"eval-sha",children:d.codeSha.slice(0,7)}),s.jsx("span",{className:"eval-ts",children:d.ts.replace("T"," ").slice(0,16)})]})]}),renderEvidence:d=>s.jsx(Vs,{r:d})}):s.jsxs("div",{className:"pane-eval pane-eval-declared",children:[k,s.jsx("div",{className:"eval-todo-note",children:r&&!x.length?n("nodeView.filterNone"):n("nodeView.eval.noReadings")}),x.map(d=>s.jsx(oe,{s:d},d.name)),N.map(d=>s.jsx(de,{track:d},d.threadId))]})}const As={spec:"nodeView.paneSpec",history:"nodeView.paneHistory",issues:"nodeView.paneIssues",eval:"nodeView.paneEval",edit:"nodeView.paneEdit"};function Xs({node:e,pane:n,setPane:a,onClose:t}){const l=b(),i=e.issues||[],c=i.filter(x=>x.status==="open").length,o=i.length-c,r=(e.overlays||[]).length,u=fs(e),p=u.some(x=>x.key===n)?n:u[0].key,N=ws(e.id,p==="history");return s.jsx("div",{className:"ov-backdrop","data-focus-overlay":!0,onMouseDown:t,children:s.jsxs("div",{className:"ov-panel",onMouseDown:x=>x.stopPropagation(),children:[s.jsxs("div",{className:"ov-head",children:[s.jsx("span",{className:"ov-title",children:e.title}),s.jsx("div",{className:"ov-tabs",children:u.map((x,k)=>s.jsxs("button",{className:x.key===p?"ov-tab on":"ov-tab",onClick:()=>a(x.key),children:[s.jsx("kbd",{children:k+1})," ",l(As[x.key]),x.key==="issues"&&(c>0||o>0)&&s.jsxs("span",{className:"ov-tab-counts",children:[c>0&&s.jsx("span",{className:"ovc st-open","data-tip":l("nodeView.openIssues",{n:c}),children:c}),o>0&&s.jsx("span",{className:"ovc st-closed","data-tip":l("nodeView.closedIssues",{n:o}),children:o})]}),x.key==="edit"&&r>0&&s.jsx("span",{className:"ov-tab-counts",children:s.jsx("span",{className:"ovc st-edit","data-tip":l("nodeView.pendingEdits",{n:r}),children:r})})]},x.key))}),s.jsx("span",{className:"ov-hint",children:l("nodeView.hint")})]}),s.jsxs("div",{className:"ov-body",children:[p==="spec"&&s.jsx("div",{className:"pane-solo",children:s.jsx(js,{node:e})}),p==="history"&&s.jsx(ks,{node:e,rows:N}),p==="issues"&&s.jsx($s,{node:e}),p==="eval"&&s.jsx(Ls,{node:e}),p==="edit"&&s.jsx(Es,{node:e})]})]})})}const Fs=({size:e=12})=>s.jsx(K,{name:"lock",size:e});function Is(e){if(!e.length)return null;const n={};return e.forEach(a=>{n[a.op]=(n[a.op]||0)+1}),Object.entries(n).map(([a,t])=>`${He[a]}${t}`).join(" ")}function Ps({guides:e=[],expandable:n,expanded:a,rollup:t,kin:l=0,onToggle:i}){return s.jsxs("span",{className:"sess-lead",children:[e.map((c,o)=>{const r=o===e.length-1?c?"tee":"elbow":c?"rail":"gap";return s.jsx("span",{className:`sess-rail ${r}`,"aria-hidden":"true"},o)}),n&&s.jsx("span",{className:`sess-fold pod${a?" open":""}`,role:"button",tabIndex:-1,style:a?{color:t,borderColor:t}:{background:t,borderColor:t},"data-tip":`${l} nested session${l===1?"":"s"} — click to ${a?"collapse":"expand"}`,onClick:c=>{c.stopPropagation(),i==null||i()},onMouseDown:c=>c.stopPropagation(),children:l})]})}function Ds(){const[e,n]=m.useState(()=>new Set);return{expanded:e,toggle:t=>n(l=>{const i=new Set(l);return i.has(t)?i.delete(t):i.add(t),i})}}function Hs({s:e,locked:n,showAvatar:a=!0,compact:t=!1,lead:l=null}){const i=b(),c=Is(e.ops),o=xe(e),r=i(`status.${e.status}`);return s.jsxs(s.Fragment,{children:[l,a&&s.jsx(Le,{seed:e.id,status:e.status,title:`${Be(e)} · ${r} — ${e.id.slice(0,8)}`}),s.jsxs("span",{className:"sess-meta",children:[t?s.jsx("span",{className:"sess-glyph",style:{color:S[e.status]},"data-tip":r,"aria-label":r,children:Te[e.status]}):s.jsx("span",{className:"sess-status",style:{color:S[e.status]},children:r}),c&&s.jsx("span",{className:"sess-ops",children:c})]}),s.jsx("span",{className:"sess-id","data-tip":o,children:o}),n&&s.jsx("span",{className:"sess-lock","data-tip":i("sessionWindow.lockedTitle"),children:s.jsx(Fs,{})})]})}function en({sessions:e,activeId:n,onPick:a,onOpenSession:t}){const l=b(),{expanded:i,toggle:c}=Ds(),o=m.useMemo(()=>Ke(e,r=>i.has(r)),[e,i]);return s.jsx("div",{className:"sesswin",children:e.length===0?s.jsxs("div",{className:"sesswin-empty",children:[l("sessionWindow.emptyBefore"),s.jsx("kbd",{children:"⏎"}),l("sessionWindow.emptyAfter")]}):o.map(r=>{if(r.type==="zone")return s.jsx("div",{className:`sesswin-zone sesswin-zone-${r.zone}`,children:l(`sessionZone.${r.zone}`)},`zone-${r.zone}`);const u=r.s,p=u.source===n,N=r.expandable||r.depth?s.jsx(Ps,{guides:r.guides,expandable:r.expandable,expanded:r.expanded,rollup:r.rollup,kin:r.kin,onToggle:()=>c(u.id)}):null;return s.jsx("button",{className:p?"sess-row locked":"sess-row",style:{"--ov":Ce(u.id)},onClick:()=>a(u),onDoubleClick:()=>t(u.id),"data-tip":l("sessionWindow.rowTitle"),children:s.jsx(Hs,{s:u,locked:p,compact:!0,lead:N})},u.id)})})}export{Ws as $,Le as A,_e as B,Ds as C,Ke as D,Es as E,is as F,He as G,ks as H,le as I,ne as J,qs as K,Fs as L,as as M,Xs as N,Os as O,we as P,ns as Q,Ps as R,Bs as S,De as T,ts as U,ve as V,ds as W,ye as X,Ne as Y,_s as Z,os as _,Pe as a,ge as a0,us as a1,Us as a2,qe as a3,Rs as a4,me as a5,Oe as a6,R as a7,ls as a8,Te as b,S as c,Z as d,Ie as e,Qs as f,zs as g,Zs as h,ps as i,Be as j,Ys as k,Ce as l,Y as m,K as n,O as o,Js as p,fs as q,en as r,xe as s,ws as t,Gs as u,js as v,$s as w,Hs as x,Ks as y,F as z};
|