@alecsibilia/luca 14.0.0-alpha.2 → 14.0.0-alpha.21
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/CHANGELOG.md +76 -0
- package/README.md +7 -2
- package/board/client/board-look.ts +31 -1
- package/board/client/board-panel-parts.tsx +150 -0
- package/board/client/board-panel-stages.ts +99 -0
- package/board/client/board-panel-styles.ts +223 -0
- package/board/client/board-panel.tsx +77 -1275
- package/board/client/engine-life.ts +48 -0
- package/board/client/final-review-stack.tsx +144 -0
- package/board/client/heartbeat.tsx +61 -0
- package/board/client/helper-lines.tsx +69 -0
- package/board/client/live-feed.tsx +86 -0
- package/board/client/live-line.ts +73 -0
- package/board/client/model-line.tsx +178 -0
- package/board/client/needs-you-section.tsx +133 -0
- package/board/client/one-line.tsx +193 -0
- package/board/client/repo-wait-banner.tsx +69 -0
- package/board/client/run-footer.tsx +68 -0
- package/board/client/run-header.tsx +189 -0
- package/board/client/run-picker.tsx +98 -0
- package/board/client/status-bar.tsx +131 -0
- package/board/client/step-dots.tsx +127 -0
- package/board/client/stuck-actions.tsx +269 -0
- package/board/client/ticket-card-view.tsx +238 -0
- package/board/client/ticket-detail.tsx +87 -0
- package/board/client/ticket-stack.tsx +119 -0
- package/board/client/ticket-wait.ts +188 -0
- package/board/client/timeline-rows.tsx +41 -14
- package/board/client/usage-line.tsx +51 -0
- package/board/client/wait-line.tsx +39 -0
- package/board/index.client.tsx +1 -1
- package/board/index.server.ts +7 -1
- package/board/package.json +1 -1
- package/board/server/board-panel-state-helpers.ts +67 -0
- package/board/server/board-server-check.ts +294 -0
- package/board/server/board-server-engine.ts +129 -0
- package/board/server/board-server-events.ts +134 -0
- package/board/server/board-server-read.ts +190 -0
- package/board/server/board-server-resume.ts +164 -0
- package/board/server/board-server-start.ts +174 -0
- package/board/server/board-server-store.ts +281 -0
- package/board/server/board-server-types.ts +114 -0
- package/board/server/board-server-util.ts +4 -0
- package/board/server/board-server.ts +52 -943
- package/board/server/board-vocabulary-engine.ts +144 -0
- package/board/server/board-vocabulary-review.ts +115 -0
- package/board/server/board-vocabulary-run.ts +145 -0
- package/board/server/board-vocabulary-shapes.ts +153 -0
- package/board/server/board-vocabulary.ts +20 -375
- package/board/server/current-step-panel-helpers.ts +108 -0
- package/board/server/describe-record.ts +403 -0
- package/board/server/engine-launch.ts +20 -3
- package/board/server/engine-restart-helpers.ts +105 -0
- package/board/server/engine-watch.ts +45 -5
- package/board/server/journal-rebuild-helpers.ts +169 -0
- package/board/server/live-file.ts +130 -0
- package/board/server/luca-version.ts +1 -1
- package/board/server/make-rows.ts +14 -629
- package/board/server/record-texts.ts +350 -0
- package/board/server/reduce-board-agent-start.ts +224 -0
- package/board/server/reduce-board-agents.ts +160 -0
- package/board/server/reduce-board-counters.ts +138 -0
- package/board/server/reduce-board-create.ts +148 -0
- package/board/server/reduce-board-final-review.ts +174 -0
- package/board/server/reduce-board-needs-you.ts +283 -0
- package/board/server/reduce-board-reasons.ts +59 -0
- package/board/server/reduce-board-run.ts +255 -0
- package/board/server/reduce-board-settle.ts +88 -0
- package/board/server/reduce-board-steps.ts +226 -0
- package/board/server/reduce-board-text.ts +139 -0
- package/board/server/reduce-board-ticket-checks.ts +168 -0
- package/board/server/reduce-board-ticket-join.ts +144 -0
- package/board/server/reduce-board-ticket-start.ts +132 -0
- package/board/server/reduce-board-update.ts +117 -0
- package/board/server/reduce-board-usage.ts +247 -0
- package/board/server/reduce-board.ts +100 -1917
- package/board/server/reply-post.ts +407 -0
- package/board/server/run-registry.ts +8 -4
- package/board/server/ticket-stages-panel-helpers.ts +296 -0
- package/board/shared/board-rows.ts +9 -0
- package/board/shared/board-rpc.ts +42 -0
- package/board/shared/board-state-counts.ts +77 -0
- package/board/shared/board-state-live.ts +99 -0
- package/board/shared/board-state-review.ts +59 -0
- package/board/shared/board-state-run.ts +236 -0
- package/board/shared/board-state-ticket.ts +257 -0
- package/board/shared/board-state-usage.ts +107 -0
- package/board/shared/board-state.ts +97 -567
- package/board/shared/reply-actions.ts +207 -0
- package/engine/agents/agent-launcher.ts +27 -4
- package/engine/agents/claude-launcher-helpers.ts +233 -0
- package/engine/agents/claude-launcher.ts +28 -669
- package/engine/agents/claude-messages.ts +212 -0
- package/engine/agents/claude-options.ts +43 -8
- package/engine/agents/common-instructions.ts +138 -0
- package/engine/agents/input-channel.ts +36 -0
- package/engine/agents/launch-paths.ts +29 -0
- package/engine/agents/launch-session.ts +227 -0
- package/engine/agents/learner-instructions.ts +31 -0
- package/engine/agents/open-session.ts +210 -0
- package/engine/agents/prompt-sections.ts +95 -0
- package/engine/agents/rejoin-prompts.ts +135 -0
- package/engine/agents/reviewer-instructions.ts +99 -0
- package/engine/agents/role-instructions.ts +31 -244
- package/engine/agents/role-prompts.ts +39 -109
- package/engine/agents/role-results.ts +106 -7
- package/engine/agents/scripted-launcher.ts +21 -6
- package/engine/agents/session-pool.ts +124 -0
- package/engine/agents/turn-progress.ts +283 -0
- package/engine/agents/writer-instructions.ts +151 -0
- package/engine/cli/computer-adapters-real.ts +15 -3
- package/engine/cli/computer-adapters.ts +2 -0
- package/engine/cli/computer-checks.ts +87 -4
- package/engine/cli/config-reload-text.ts +39 -0
- package/engine/cli/decision-model-setup.ts +70 -0
- package/engine/cli/doctor-command.ts +1 -0
- package/engine/cli/doctor.ts +21 -3
- package/engine/cli/going-runs.ts +192 -36
- package/engine/cli/init-command.ts +1 -0
- package/engine/cli/init.ts +23 -1
- package/engine/cli/live-runs.ts +73 -0
- package/engine/cli/luca-run.ts +42 -7
- package/engine/cli/luca-skills.ts +183 -0
- package/engine/cli/luca.ts +8 -1
- package/engine/cli/models-command.ts +119 -0
- package/engine/cli/read-run.ts +81 -0
- package/engine/cli/run-args.ts +33 -6
- package/engine/cli/run-demo.ts +143 -0
- package/engine/cli/run-end.ts +184 -0
- package/engine/cli/run-modes-helpers.ts +81 -0
- package/engine/cli/run-modes.ts +62 -439
- package/engine/cli/setup.ts +5 -48
- package/engine/cli/unfinished-runs.ts +122 -0
- package/engine/cli/upgrade-command.ts +212 -30
- package/engine/cli/upgrade-finish.ts +149 -0
- package/engine/cli/upgrade.ts +112 -71
- package/engine/config/config-fields.ts +127 -0
- package/engine/config/config-reload.ts +250 -0
- package/engine/config/engine-config-schema.ts +235 -0
- package/engine/config/engine-config.ts +72 -142
- package/engine/config/test-commands.ts +102 -0
- package/engine/core/agent-guards-helpers.ts +84 -0
- package/engine/core/agent-turn.ts +395 -0
- package/engine/core/already-done.ts +44 -1
- package/engine/core/build-action.ts +214 -0
- package/engine/core/build-context.ts +135 -0
- package/engine/core/build-launch.ts +259 -0
- package/engine/core/commit-in.ts +282 -0
- package/engine/core/decide-build-early.ts +108 -0
- package/engine/core/decide-build-ending.ts +145 -0
- package/engine/core/decide-build-helpers.ts +87 -0
- package/engine/core/decide-build-tickets.ts +207 -0
- package/engine/core/decide-build-view.ts +103 -0
- package/engine/core/decide-build.ts +101 -1368
- package/engine/core/decide-final-fix.ts +203 -0
- package/engine/core/decide-final-review.ts +55 -310
- package/engine/core/decide-red-green.ts +398 -0
- package/engine/core/decide-rejoin.ts +259 -0
- package/engine/core/decide-repo-wait.ts +129 -0
- package/engine/core/decide-run-budget.ts +86 -12
- package/engine/core/decide-stuck-helpers.ts +127 -0
- package/engine/core/decide-stuck.ts +60 -8
- package/engine/core/decide-ticket-review.ts +95 -0
- package/engine/core/decide-ticket.ts +267 -0
- package/engine/core/decide-waits.ts +47 -0
- package/engine/core/decide.ts +74 -23
- package/engine/core/engine-chores.ts +163 -0
- package/engine/core/environment-rerun.ts +83 -0
- package/engine/core/evidence-text.ts +84 -0
- package/engine/core/execute-action.ts +303 -0
- package/engine/core/execute-already-done-check.ts +133 -0
- package/engine/core/execute-build.ts +68 -944
- package/engine/core/execute-changeset.ts +5 -7
- package/engine/core/execute-final-review.ts +125 -13
- package/engine/core/execute-gates.ts +182 -0
- package/engine/core/execute-intake.ts +130 -0
- package/engine/core/execute-join.ts +276 -0
- package/engine/core/execute-memory.ts +12 -0
- package/engine/core/execute-pull-request.ts +84 -0
- package/engine/core/execute-red-check.ts +219 -0
- package/engine/core/execute-repo-wait.ts +129 -0
- package/engine/core/execute-stuck.ts +25 -10
- package/engine/core/execute-waits.ts +158 -0
- package/engine/core/execute-with-jev.ts +118 -0
- package/engine/core/execute.ts +124 -831
- package/engine/core/final-failure-cap.ts +38 -0
- package/engine/core/final-review-action.ts +125 -0
- package/engine/core/final-review-text.ts +63 -17
- package/engine/core/final-stuck-text.ts +86 -0
- package/engine/core/fix-loop-text.ts +49 -2
- package/engine/core/left-out.ts +120 -0
- package/engine/core/live-file.ts +372 -0
- package/engine/core/loop-caps.ts +25 -2
- package/engine/core/not-in-run.ts +7 -3
- package/engine/core/pull-request-text.ts +255 -6
- package/engine/core/repo-wait-text.ts +81 -0
- package/engine/core/review-text.ts +55 -1
- package/engine/core/run-crash-recovery-helpers.ts +16 -0
- package/engine/core/run-session-close-helpers.ts +96 -0
- package/engine/core/run-start.ts +148 -0
- package/engine/core/run-stuck-text.ts +86 -0
- package/engine/core/schedule-rules.ts +193 -0
- package/engine/core/stuck-detail-text.ts +89 -0
- package/engine/core/stuck-format.ts +22 -0
- package/engine/core/stuck-text.ts +29 -332
- package/engine/core/ticket-stuck-text.ts +192 -0
- package/engine/core/worktree-changes.ts +106 -0
- package/engine/gates/already-done-check.ts +120 -0
- package/engine/gates/feature-map.ts +96 -0
- package/engine/gates/gate-runner.ts +108 -108
- package/engine/gates/gate-schemas.ts +61 -2
- package/engine/gates/leftover-scan.ts +154 -31
- package/engine/gates/new-tests.ts +45 -0
- package/engine/gates/own-files-check.ts +98 -0
- package/engine/gates/prepare-check.ts +60 -0
- package/engine/gates/prepare-slots.ts +99 -0
- package/engine/gates/red-check.ts +184 -16
- package/engine/gates/shell-check.ts +64 -0
- package/engine/gates/slot-waits.ts +55 -0
- package/engine/gates/slow-test-runner.ts +59 -0
- package/engine/gates/test-checks.ts +161 -0
- package/engine/gates/test-runner.ts +164 -5
- package/engine/gates/test-slots.ts +48 -0
- package/engine/git/git-adapter.ts +55 -396
- package/engine/git/git-commits.ts +132 -0
- package/engine/git/git-join.ts +139 -0
- package/engine/git/git-read.ts +123 -0
- package/engine/git/git-run.ts +32 -0
- package/engine/git/git-worktrees.ts +143 -0
- package/engine/git/outside-links.ts +55 -0
- package/engine/guards/after-turn-check.ts +31 -5
- package/engine/guards/guard-hook.ts +60 -12
- package/engine/guards/guard-roles.ts +68 -0
- package/engine/guards/helper-rules.ts +92 -0
- package/engine/guards/permission-rules.ts +161 -0
- package/engine/guards/role-rules-helpers.ts +48 -0
- package/engine/guards/role-rules.ts +34 -606
- package/engine/guards/shell-rules.ts +344 -0
- package/engine/guards/worktree-git.ts +208 -0
- package/engine/guards/worktree-snapshot.ts +412 -0
- package/engine/guards/worktree-state.ts +28 -825
- package/engine/guards/worktree-undo.ts +260 -0
- package/engine/guards/write-rules.ts +149 -0
- package/engine/index.ts +30 -9
- package/engine/intake/criteria-checks.ts +114 -0
- package/engine/intake/intake-checks.ts +164 -30
- package/engine/intake/intake-schemas.ts +37 -2
- package/engine/jev/clef-client.ts +259 -0
- package/engine/jev/decision-model-credentials.ts +212 -0
- package/engine/jev/jev-jobs.ts +144 -60
- package/engine/jev/jev-schemas.ts +86 -5
- package/engine/jev/jev-shadow-helpers.ts +218 -0
- package/engine/jev/jev-shadow.ts +109 -64
- package/engine/jev/model-pick.ts +275 -0
- package/engine/journal/agent-entries.ts +167 -0
- package/engine/journal/agent-message-entries.ts +72 -0
- package/engine/journal/agent-turn-replay.ts +203 -0
- package/engine/journal/already-done-entries.ts +55 -0
- package/engine/journal/commit-entries.ts +56 -0
- package/engine/journal/cross-ticket-replay.ts +222 -0
- package/engine/journal/decision-model-entries.ts +86 -0
- package/engine/journal/entry-fields.ts +10 -0
- package/engine/journal/final-review-entries.ts +128 -0
- package/engine/journal/final-review-replay.ts +324 -0
- package/engine/journal/final-review-round.ts +320 -0
- package/engine/journal/final-review-state.ts +180 -0
- package/engine/journal/gate-entries.ts +81 -0
- package/engine/journal/intake-entries.ts +87 -0
- package/engine/journal/join-entries.ts +126 -0
- package/engine/journal/journal-entry-schema.ts +218 -0
- package/engine/journal/journal-kinds.ts +97 -0
- package/engine/journal/journal-record-schema.ts +218 -0
- package/engine/journal/journal-record.ts +43 -1400
- package/engine/journal/journal.ts +6 -1
- package/engine/journal/limit-entries.ts +90 -0
- package/engine/journal/memory-entries.ts +124 -0
- package/engine/journal/memory-replay.ts +157 -0
- package/engine/journal/plan-replay.ts +139 -0
- package/engine/journal/pull-request-entries.ts +41 -0
- package/engine/journal/replay.ts +157 -2144
- package/engine/journal/reply-replay.ts +144 -0
- package/engine/journal/repo-wait-entries.ts +53 -0
- package/engine/journal/run-entries.ts +172 -0
- package/engine/journal/run-state.ts +264 -0
- package/engine/journal/shared-failure-replay.ts +133 -0
- package/engine/journal/stuck-entries.ts +193 -0
- package/engine/journal/ticket-progress.ts +392 -0
- package/engine/journal/ticket-replay.ts +411 -0
- package/engine/journal/ticket-result-replay.ts +364 -0
- package/engine/journal/ticket-retry-replay.ts +142 -0
- package/engine/journal/worktree-entries.ts +122 -0
- package/engine/models/model-score.ts +512 -0
- package/engine/shell/run-command.ts +187 -18
- package/engine/testing/agent-turn-fixtures.ts +287 -0
- package/engine/testing/already-done-fixtures.ts +97 -0
- package/engine/testing/build-fixtures.ts +68 -973
- package/engine/testing/checkouts-fixtures.ts +48 -0
- package/engine/testing/final-review-fixtures.ts +62 -1
- package/engine/testing/final-review-steps.ts +143 -0
- package/engine/testing/limit-fixtures.ts +85 -0
- package/engine/testing/live-file-fixtures.ts +57 -0
- package/engine/testing/practice-repo.ts +10 -4
- package/engine/testing/practice-run.ts +1 -1
- package/engine/testing/reply-fixtures.ts +88 -0
- package/engine/testing/run-budget-fixtures.ts +165 -0
- package/engine/testing/run-fixtures.ts +219 -0
- package/engine/testing/sdk-play.ts +177 -0
- package/engine/testing/setup-repo.ts +194 -0
- package/engine/testing/step-record-fixtures.ts +55 -0
- package/engine/testing/ticket-step-fixtures.ts +272 -0
- package/engine/testing/wait-scenarios.ts +186 -0
- package/engine/tracker/gh-retry.ts +217 -0
- package/engine/tracker/github-tracker.ts +310 -90
- package/engine/tracker/in-memory-tracker.ts +8 -4
- package/engine/tracker/tracker.ts +131 -6
- package/package.json +2 -1
- package/skills/luca-retro/SKILL.md +74 -0
- package/skills/luca-retro/scripts/retro-summary.ts +1356 -0
- package/skills/luca-unstick/SKILL.md +153 -0
- package/skills/luca-unstick/scripts/stuck-summary.ts +1063 -0
- package/engine/jev/jev-client.ts +0 -173
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A run with no sign of life (#635): its engine is gone (the plugin's
|
|
3
|
+
* process check found none, for a run it didn't start), or its live file
|
|
4
|
+
* went stale (written more than a minute ago). Its busy cards say so, with
|
|
5
|
+
* a flat red heartbeat, and their timers stop at the last sign of life.
|
|
6
|
+
*/
|
|
7
|
+
import {
|
|
8
|
+
elapsedText,
|
|
9
|
+
liveIsFresh,
|
|
10
|
+
needsEngine,
|
|
11
|
+
type BoardState,
|
|
12
|
+
} from '../shared/board-state'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Why the run shows no sign of life, in words, and the time of its last
|
|
16
|
+
* sign (the live file's write, else the latest record): `null` while the
|
|
17
|
+
* engine is alive, or when the run needs none.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* noLife({ state, now }) // { text: 'no sign of life for 1m 10s', at: 1790000000000 }
|
|
21
|
+
*/
|
|
22
|
+
export const noLife = ({
|
|
23
|
+
state,
|
|
24
|
+
now,
|
|
25
|
+
}: {
|
|
26
|
+
state: BoardState
|
|
27
|
+
now: number
|
|
28
|
+
}): { text: string; at: number } | null => {
|
|
29
|
+
if (!needsEngine({ state })) return null
|
|
30
|
+
const written = state.live_written_at
|
|
31
|
+
if (state.engine_gone) {
|
|
32
|
+
const at = Date.parse(
|
|
33
|
+
written ??
|
|
34
|
+
state.latest_at ??
|
|
35
|
+
state.run.last_time ??
|
|
36
|
+
state.run.started_at
|
|
37
|
+
)
|
|
38
|
+
return {
|
|
39
|
+
text: `engine not running · last sign of life ${elapsedText({ ms: now - at })} ago`,
|
|
40
|
+
at,
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (written === null || liveIsFresh({ written_at: written, now })) {
|
|
44
|
+
return null
|
|
45
|
+
}
|
|
46
|
+
const at = Date.parse(written)
|
|
47
|
+
return { text: `no sign of life for ${elapsedText({ ms: now - at })}`, at }
|
|
48
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The final review as a stack of its lenses (Waiting, Reviewing, Fixing,
|
|
3
|
+
* Clean), with the round and fix round and a line saying where it stands.
|
|
4
|
+
*/
|
|
5
|
+
import { Icon } from '@getpaseo/plugin/client/react-native'
|
|
6
|
+
import { Text, View } from 'react-native'
|
|
7
|
+
|
|
8
|
+
import { DIMMED, lensColor } from './board-look'
|
|
9
|
+
import { Counter, StageHeader } from './board-panel-parts'
|
|
10
|
+
import { LENS_STAGES } from './board-panel-stages'
|
|
11
|
+
import type { Look } from './board-panel-styles'
|
|
12
|
+
|
|
13
|
+
import { LOOP_CAP, type BoardState, type LensCard } from '../shared/board-state'
|
|
14
|
+
|
|
15
|
+
const findingsLine = ({ lens }: { lens: LensCard }): string | null => {
|
|
16
|
+
const parts = [
|
|
17
|
+
lens.findings.blocker > 0 ? `${lens.findings.blocker} blocker` : null,
|
|
18
|
+
lens.findings.should_fix > 0
|
|
19
|
+
? `${lens.findings.should_fix} should-fix`
|
|
20
|
+
: null,
|
|
21
|
+
lens.findings.nit > 0 ? `${lens.findings.nit} nit` : null,
|
|
22
|
+
].filter((part) => part !== null)
|
|
23
|
+
return parts.length > 0 ? parts.join(', ') : null
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const LensCardView = ({ lens, theme, styles }: { lens: LensCard } & Look) => {
|
|
27
|
+
const color = lensColor({ state: lens.state, theme })
|
|
28
|
+
const findings = findingsLine({ lens })
|
|
29
|
+
return (
|
|
30
|
+
<View style={styles.card}>
|
|
31
|
+
<View style={styles.cardTop}>
|
|
32
|
+
<Text style={styles.cardTitle}>{lens.name}</Text>
|
|
33
|
+
<View style={styles.spacer} />
|
|
34
|
+
<Text style={[styles.pill, { color, borderColor: color }]}>
|
|
35
|
+
{lens.state}
|
|
36
|
+
</Text>
|
|
37
|
+
</View>
|
|
38
|
+
{findings ? (
|
|
39
|
+
<Text
|
|
40
|
+
style={[
|
|
41
|
+
styles.muted,
|
|
42
|
+
{
|
|
43
|
+
color:
|
|
44
|
+
lens.findings.blocker > 0
|
|
45
|
+
? theme.colors.statusDanger
|
|
46
|
+
: theme.colors.statusWarning,
|
|
47
|
+
},
|
|
48
|
+
]}
|
|
49
|
+
>
|
|
50
|
+
{findings}
|
|
51
|
+
</Text>
|
|
52
|
+
) : null}
|
|
53
|
+
</View>
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const reviewLine = ({ state }: { state: BoardState }): string => {
|
|
58
|
+
const review = state.final_review
|
|
59
|
+
const open = state.tickets.filter(
|
|
60
|
+
(ticket) => ticket.stage !== 'done' && ticket.stage !== 'skipped'
|
|
61
|
+
).length
|
|
62
|
+
switch (review.state) {
|
|
63
|
+
case 'waiting':
|
|
64
|
+
return review.active
|
|
65
|
+
? 'Every ticket is done or skipped. It starts next.'
|
|
66
|
+
: `Starts when every ticket is done or skipped (${open} still open).`
|
|
67
|
+
case 'reviewing':
|
|
68
|
+
return `Round ${review.round}: a fresh reviewer per lens reads the whole run branch.`
|
|
69
|
+
case 'fixing':
|
|
70
|
+
return `Fix round ${review.fix_round}/${LOOP_CAP}: the implementer fixes what the lenses found.`
|
|
71
|
+
case 'stuck':
|
|
72
|
+
return 'Stuck: it needs your reply (see Needs you).'
|
|
73
|
+
case 'passed':
|
|
74
|
+
return 'Passed.'
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const FinalReviewStack = ({
|
|
79
|
+
state,
|
|
80
|
+
theme,
|
|
81
|
+
styles,
|
|
82
|
+
}: { state: BoardState } & Look) => {
|
|
83
|
+
const review = state.final_review
|
|
84
|
+
return (
|
|
85
|
+
<View
|
|
86
|
+
style={[styles.block, review.active ? null : { opacity: DIMMED }]}
|
|
87
|
+
>
|
|
88
|
+
<View style={styles.row}>
|
|
89
|
+
<Icon
|
|
90
|
+
name="GitPullRequest"
|
|
91
|
+
size={15}
|
|
92
|
+
color={theme.colors.foreground}
|
|
93
|
+
/>
|
|
94
|
+
<Text style={styles.reviewTitle}>Final review · 5 lenses</Text>
|
|
95
|
+
<View style={styles.spacer} />
|
|
96
|
+
<Counter
|
|
97
|
+
label="round"
|
|
98
|
+
value={review.round}
|
|
99
|
+
theme={theme}
|
|
100
|
+
styles={styles}
|
|
101
|
+
/>
|
|
102
|
+
<Counter
|
|
103
|
+
label="fix"
|
|
104
|
+
value={review.fix_round}
|
|
105
|
+
theme={theme}
|
|
106
|
+
styles={styles}
|
|
107
|
+
/>
|
|
108
|
+
</View>
|
|
109
|
+
<Text style={styles.muted}>{reviewLine({ state })}</Text>
|
|
110
|
+
{LENS_STAGES.map((entry) => {
|
|
111
|
+
const lenses = review.lenses.filter(
|
|
112
|
+
(lens) => lens.state === entry.stage
|
|
113
|
+
)
|
|
114
|
+
return (
|
|
115
|
+
<View key={entry.stage} style={styles.block}>
|
|
116
|
+
<StageHeader
|
|
117
|
+
title={entry.title}
|
|
118
|
+
hint=""
|
|
119
|
+
icon={entry.icon}
|
|
120
|
+
count={lenses.length}
|
|
121
|
+
color={lensColor({ state: entry.stage, theme })}
|
|
122
|
+
folded={null}
|
|
123
|
+
onPress={null}
|
|
124
|
+
theme={theme}
|
|
125
|
+
styles={styles}
|
|
126
|
+
/>
|
|
127
|
+
{lenses.length > 0 ? (
|
|
128
|
+
<View style={styles.stageBody}>
|
|
129
|
+
{lenses.map((lens) => (
|
|
130
|
+
<LensCardView
|
|
131
|
+
key={lens.name}
|
|
132
|
+
lens={lens}
|
|
133
|
+
theme={theme}
|
|
134
|
+
styles={styles}
|
|
135
|
+
/>
|
|
136
|
+
))}
|
|
137
|
+
</View>
|
|
138
|
+
) : null}
|
|
139
|
+
</View>
|
|
140
|
+
)
|
|
141
|
+
})}
|
|
142
|
+
</View>
|
|
143
|
+
)
|
|
144
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A busy card's heartbeat (#631): 20 small bars at the end of its
|
|
3
|
+
* step-dots line, one per second, oldest on the left, each as tall as its
|
|
4
|
+
* agent's activity that second in the live file. Green while working; a
|
|
5
|
+
* flat line in the muted colour when nothing happened in those seconds; a
|
|
6
|
+
* flat red line when the run shows no sign of life (#635).
|
|
7
|
+
* Plain `View`s, redrawn with the card on the board's 1-second tick.
|
|
8
|
+
*/
|
|
9
|
+
import { View } from 'react-native'
|
|
10
|
+
|
|
11
|
+
import type { Look } from './board-panel-styles'
|
|
12
|
+
|
|
13
|
+
import type { LiveAgent } from '../shared/board-state'
|
|
14
|
+
|
|
15
|
+
/** How many seconds the heartbeat shows. */
|
|
16
|
+
const BARS = 20
|
|
17
|
+
|
|
18
|
+
/** A quiet second's bar height, in points. */
|
|
19
|
+
const LOW = 2
|
|
20
|
+
|
|
21
|
+
/** A count this big or bigger fills a bar. */
|
|
22
|
+
const LOUD = 12
|
|
23
|
+
|
|
24
|
+
/** A bar's height for `count`: one point more per tick, up to `LOUD`. */
|
|
25
|
+
const heightOf = (count: number): number => LOW + Math.min(count, LOUD)
|
|
26
|
+
|
|
27
|
+
export const Heartbeat = ({
|
|
28
|
+
live,
|
|
29
|
+
gone = false,
|
|
30
|
+
theme,
|
|
31
|
+
styles,
|
|
32
|
+
}: {
|
|
33
|
+
/** `null` for a card with no live entry: a flat line. */
|
|
34
|
+
live: LiveAgent | null
|
|
35
|
+
/** The run shows no sign of life (#635): a flat red line. */
|
|
36
|
+
gone?: boolean
|
|
37
|
+
} & Look) => {
|
|
38
|
+
const counts = gone || live === null ? [] : live.activity.slice(-BARS)
|
|
39
|
+
const bars = [
|
|
40
|
+
...Array.from({ length: BARS - counts.length }, () => 0),
|
|
41
|
+
...counts,
|
|
42
|
+
]
|
|
43
|
+
const color = gone
|
|
44
|
+
? theme.colors.statusDanger
|
|
45
|
+
: bars.some((count) => count > 0)
|
|
46
|
+
? theme.colors.statusSuccess
|
|
47
|
+
: theme.colors.foregroundMuted
|
|
48
|
+
return (
|
|
49
|
+
<View accessibilityLabel="Heartbeat" style={styles.heartbeat}>
|
|
50
|
+
{bars.map((count, index) => (
|
|
51
|
+
<View
|
|
52
|
+
key={index}
|
|
53
|
+
style={[
|
|
54
|
+
styles.heartbeatBar,
|
|
55
|
+
{ height: heightOf(count), backgroundColor: color },
|
|
56
|
+
]}
|
|
57
|
+
/>
|
|
58
|
+
))}
|
|
59
|
+
</View>
|
|
60
|
+
)
|
|
61
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A card's helper lines (#636): under its step-dots line, one line per
|
|
3
|
+
* helper its agent has running, from the run's live file: "helper", the
|
|
4
|
+
* helper's model as a coloured tag, its latest action and how long ago,
|
|
5
|
+
* with a left edge in the model's colour. A helper that's done has no line.
|
|
6
|
+
*/
|
|
7
|
+
import { Text, View } from 'react-native'
|
|
8
|
+
|
|
9
|
+
import { modelColor } from './board-look'
|
|
10
|
+
import type { Look } from './board-panel-styles'
|
|
11
|
+
import { callWords } from './live-line'
|
|
12
|
+
import { CutLine } from './one-line'
|
|
13
|
+
|
|
14
|
+
import { elapsedText, type LiveAgent } from '../shared/board-state'
|
|
15
|
+
|
|
16
|
+
export const HelperLines = ({
|
|
17
|
+
live,
|
|
18
|
+
now,
|
|
19
|
+
theme,
|
|
20
|
+
styles,
|
|
21
|
+
}: { live: LiveAgent | null; now: number } & Look) => (
|
|
22
|
+
<>
|
|
23
|
+
{(live?.helpers ?? [])
|
|
24
|
+
.filter((helper) => !helper.done)
|
|
25
|
+
.map((helper, index) => {
|
|
26
|
+
const model = helper.model ?? 'haiku'
|
|
27
|
+
const color = modelColor({ model, theme })
|
|
28
|
+
const { action } = helper
|
|
29
|
+
return (
|
|
30
|
+
<View
|
|
31
|
+
key={index}
|
|
32
|
+
style={[
|
|
33
|
+
styles.line,
|
|
34
|
+
styles.helperLine,
|
|
35
|
+
{ borderLeftColor: color },
|
|
36
|
+
]}
|
|
37
|
+
>
|
|
38
|
+
<Text
|
|
39
|
+
style={[styles.muted, styles.fixed]}
|
|
40
|
+
numberOfLines={1}
|
|
41
|
+
>
|
|
42
|
+
helper
|
|
43
|
+
</Text>
|
|
44
|
+
<Text
|
|
45
|
+
style={[styles.tag, { color, borderColor: color }]}
|
|
46
|
+
numberOfLines={1}
|
|
47
|
+
>
|
|
48
|
+
{model}
|
|
49
|
+
</Text>
|
|
50
|
+
{action !== null ? (
|
|
51
|
+
<>
|
|
52
|
+
<CutLine
|
|
53
|
+
text={callWords(action)}
|
|
54
|
+
style={styles.text}
|
|
55
|
+
styles={styles}
|
|
56
|
+
/>
|
|
57
|
+
<Text
|
|
58
|
+
style={[styles.mono, styles.fixed]}
|
|
59
|
+
numberOfLines={1}
|
|
60
|
+
>
|
|
61
|
+
{`${elapsedText({ ms: now - Date.parse(action.at) })} ago`}
|
|
62
|
+
</Text>
|
|
63
|
+
</>
|
|
64
|
+
) : null}
|
|
65
|
+
</View>
|
|
66
|
+
)
|
|
67
|
+
})}
|
|
68
|
+
</>
|
|
69
|
+
)
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The live feed (#634): a folded section at the bottom of the side panel,
|
|
3
|
+
* the run's last actions from its live file, across tickets. Open, it
|
|
4
|
+
* shows one line per action, newest last: the time, the ticket, the role
|
|
5
|
+
* in its model's colour, and the action. The panel keeps whether it is
|
|
6
|
+
* open, so a new read leaves it as it was.
|
|
7
|
+
*/
|
|
8
|
+
import { Text, View } from 'react-native'
|
|
9
|
+
|
|
10
|
+
import { clockText, modelColor } from './board-look'
|
|
11
|
+
import { Section, StageHeader } from './board-panel-parts'
|
|
12
|
+
import type { Look } from './board-panel-styles'
|
|
13
|
+
import { callWords } from './live-line'
|
|
14
|
+
import { BubbleScope, CutLine, useBubbles } from './one-line'
|
|
15
|
+
|
|
16
|
+
import type { BoardState, LiveFeedEntry } from '../shared/board-state'
|
|
17
|
+
|
|
18
|
+
const FeedLine = ({
|
|
19
|
+
entry,
|
|
20
|
+
theme,
|
|
21
|
+
styles,
|
|
22
|
+
}: { entry: LiveFeedEntry } & Look) => (
|
|
23
|
+
<View style={styles.line}>
|
|
24
|
+
<Text style={[styles.mono, styles.fixed]} numberOfLines={1}>
|
|
25
|
+
{clockText({ iso: entry.at })}
|
|
26
|
+
</Text>
|
|
27
|
+
{entry.ticket === null ? null : (
|
|
28
|
+
<Text style={[styles.muted, styles.fixed]} numberOfLines={1}>
|
|
29
|
+
#{entry.ticket}
|
|
30
|
+
</Text>
|
|
31
|
+
)}
|
|
32
|
+
<Text
|
|
33
|
+
style={[
|
|
34
|
+
styles.muted,
|
|
35
|
+
styles.fixed,
|
|
36
|
+
{ color: modelColor({ model: entry.model ?? '', theme }) },
|
|
37
|
+
]}
|
|
38
|
+
numberOfLines={1}
|
|
39
|
+
>
|
|
40
|
+
{entry.role}
|
|
41
|
+
</Text>
|
|
42
|
+
<CutLine text={callWords(entry)} style={styles.muted} styles={styles} />
|
|
43
|
+
</View>
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
export const LiveFeed = ({
|
|
47
|
+
state,
|
|
48
|
+
folded,
|
|
49
|
+
onToggle,
|
|
50
|
+
theme,
|
|
51
|
+
styles,
|
|
52
|
+
}: {
|
|
53
|
+
state: BoardState
|
|
54
|
+
folded: boolean
|
|
55
|
+
onToggle: () => void
|
|
56
|
+
} & Look) => {
|
|
57
|
+
const bubbles = useBubbles()
|
|
58
|
+
const look = { theme, styles }
|
|
59
|
+
return (
|
|
60
|
+
<Section styles={styles}>
|
|
61
|
+
<StageHeader
|
|
62
|
+
title="Live feed"
|
|
63
|
+
hint="the latest actions, newest last"
|
|
64
|
+
icon="Activity"
|
|
65
|
+
count={state.feed.length}
|
|
66
|
+
color={theme.colors.foregroundMuted}
|
|
67
|
+
folded={folded}
|
|
68
|
+
onPress={onToggle}
|
|
69
|
+
{...look}
|
|
70
|
+
/>
|
|
71
|
+
{folded || state.feed.length === 0 ? null : (
|
|
72
|
+
<View style={styles.stageBody}>
|
|
73
|
+
<BubbleScope bubbles={bubbles} styles={styles}>
|
|
74
|
+
{state.feed.map((entry, index) => (
|
|
75
|
+
<FeedLine
|
|
76
|
+
key={`${entry.at}-${index}`}
|
|
77
|
+
entry={entry}
|
|
78
|
+
{...look}
|
|
79
|
+
/>
|
|
80
|
+
))}
|
|
81
|
+
</BubbleScope>
|
|
82
|
+
</View>
|
|
83
|
+
)}
|
|
84
|
+
</Section>
|
|
85
|
+
)
|
|
86
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A card's "doing now" words (#628), from its agent's entry in the run's
|
|
3
|
+
* live file: who is working and on what, and how long ago it last acted.
|
|
4
|
+
*/
|
|
5
|
+
import { elapsedText, type LiveAgent } from '../shared/board-state'
|
|
6
|
+
|
|
7
|
+
/** A tool's icon and verb on the card; a tool not here gets "•" and its name. */
|
|
8
|
+
const TOOL_WORDS: Record<string, { icon: string; verb: string }> = {
|
|
9
|
+
Edit: { icon: '✎', verb: 'editing' },
|
|
10
|
+
MultiEdit: { icon: '✎', verb: 'editing' },
|
|
11
|
+
NotebookEdit: { icon: '✎', verb: 'editing' },
|
|
12
|
+
Write: { icon: '✎', verb: 'writing' },
|
|
13
|
+
Read: { icon: '≡', verb: 'reading' },
|
|
14
|
+
Grep: { icon: '⌕', verb: 'searching' },
|
|
15
|
+
Glob: { icon: '⌕', verb: 'searching' },
|
|
16
|
+
Bash: { icon: '▸', verb: '' },
|
|
17
|
+
Agent: { icon: '⇢', verb: 'helper' },
|
|
18
|
+
Task: { icon: '⇢', verb: 'helper' },
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** A tool call in words, with its icon first: "✎ editing gate-runner.ts". */
|
|
22
|
+
export const callWords = ({
|
|
23
|
+
tool,
|
|
24
|
+
target,
|
|
25
|
+
}: {
|
|
26
|
+
tool: string
|
|
27
|
+
target: string
|
|
28
|
+
}) => {
|
|
29
|
+
const { icon, verb } = TOOL_WORDS[tool] ?? { icon: '•', verb: tool }
|
|
30
|
+
return [icon, verb, target].filter((part) => part !== '').join(' ')
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* "<role> <icon> <action>", or, while a tool call is open, "<role> <icon>
|
|
35
|
+
* <action> · running <time>". `null` with no live entry or no action yet,
|
|
36
|
+
* so the card shows its step text.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* liveText({ live, now }) // 'implementer ✎ editing gate-runner.ts'
|
|
40
|
+
* liveText({ live: running, now }) // 'implementer ▸ bun test · running 3m 12s'
|
|
41
|
+
*/
|
|
42
|
+
export const liveText = ({
|
|
43
|
+
live,
|
|
44
|
+
now,
|
|
45
|
+
}: {
|
|
46
|
+
live: LiveAgent | null
|
|
47
|
+
now: number
|
|
48
|
+
}): string | null => {
|
|
49
|
+
if (live === null) return null
|
|
50
|
+
const { open_call, action } = live
|
|
51
|
+
if (open_call !== null) {
|
|
52
|
+
const running = elapsedText({
|
|
53
|
+
ms: now - Date.parse(open_call.started_at),
|
|
54
|
+
})
|
|
55
|
+
return `${live.role} ${callWords(open_call)} · running ${running}`
|
|
56
|
+
}
|
|
57
|
+
return action === null ? null : `${live.role} ${callWords(action)}`
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The latest action's age after a card's tokens, " · 2s ago", or nothing
|
|
62
|
+
* with no live action.
|
|
63
|
+
*/
|
|
64
|
+
export const liveAgeText = ({
|
|
65
|
+
live,
|
|
66
|
+
now,
|
|
67
|
+
}: {
|
|
68
|
+
live: LiveAgent | null
|
|
69
|
+
now: number
|
|
70
|
+
}): string =>
|
|
71
|
+
live?.action
|
|
72
|
+
? ` · ${elapsedText({ ms: now - Date.parse(live.action.at) })} ago`
|
|
73
|
+
: ''
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A card's model line (#630): the agent's model as a coloured tag, then
|
|
3
|
+
* Clef's line saying why it got that model, from its `model_reason` and
|
|
4
|
+
* its `agent_model` answer. A pick from Clef's answer shows a bar filled to
|
|
5
|
+
* its confidence: green when sharp (at or over the config's
|
|
6
|
+
* `models.confidence_bar`), orange when split. A tap on the line shows a
|
|
7
|
+
* full sentence. An older journal, with no `model_reason`, shows the tag
|
|
8
|
+
* only.
|
|
9
|
+
*/
|
|
10
|
+
import { Text, View } from 'react-native'
|
|
11
|
+
|
|
12
|
+
import { CONFIDENCE_COLORS, modelColor } from './board-look'
|
|
13
|
+
import type { Look } from './board-panel-styles'
|
|
14
|
+
import { CutLine } from './one-line'
|
|
15
|
+
|
|
16
|
+
import type { AgentModel } from '../shared/board-state'
|
|
17
|
+
|
|
18
|
+
/** Clef's line: its short text, its full sentence, and its bar, if any. */
|
|
19
|
+
type ClefLine = {
|
|
20
|
+
short: string
|
|
21
|
+
full: string
|
|
22
|
+
/** The confidence its bar is filled to; `null` for no bar. */
|
|
23
|
+
confidence: number | null
|
|
24
|
+
sharp: boolean
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** "ticket-reviewer" → "ticket-reviewers". */
|
|
28
|
+
const plural = ({ role }: { role: string }): string => `${role}s`
|
|
29
|
+
|
|
30
|
+
const capital = ({ text }: { text: string }): string =>
|
|
31
|
+
text.charAt(0).toUpperCase() + text.slice(1)
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Clef's line for a model and its reason, judged against the bar; `null`
|
|
35
|
+
* for an older journal's start, with no reason, or a reason the board
|
|
36
|
+
* doesn't know.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* clefLine({ model: { role: 'implementer', name: 'opus', reason: 'unsure', answer: { role: 'implementer', pick: 'sonnet', confidence: 0.56 } }, bar: 0.8 })
|
|
40
|
+
* // { short: '0.56 split → stayed on opus', full: 'Clef picked sonnet but was only 0.56 sure (split: under 0.8), so Luca stayed on opus.', confidence: 0.56, sharp: false }
|
|
41
|
+
*/
|
|
42
|
+
export const clefLine = ({
|
|
43
|
+
model,
|
|
44
|
+
bar,
|
|
45
|
+
}: {
|
|
46
|
+
model: AgentModel
|
|
47
|
+
bar: number
|
|
48
|
+
}): ClefLine | null => {
|
|
49
|
+
const { role, name, reason, answer } = model
|
|
50
|
+
const confidence = answer?.confidence ?? null
|
|
51
|
+
const pick = answer?.pick ?? 'no model'
|
|
52
|
+
const sharp = confidence !== null && confidence >= bar
|
|
53
|
+
const word = sharp ? 'sharp' : 'split'
|
|
54
|
+
const sure = confidence?.toFixed(2) ?? ''
|
|
55
|
+
const judged = `${word}: ${sharp ? 'at or over' : 'under'} ${bar}`
|
|
56
|
+
const plain = { confidence: null, sharp: false }
|
|
57
|
+
switch (reason) {
|
|
58
|
+
case 'decision_model':
|
|
59
|
+
return {
|
|
60
|
+
short: `${confidence === null ? 'clef' : `${sure} ${word}`} → ${name}`,
|
|
61
|
+
full:
|
|
62
|
+
confidence === null
|
|
63
|
+
? `Clef picked ${name}, so Luca ran the ${role} on it.`
|
|
64
|
+
: `Clef picked ${name} and was ${sure} sure (${judged}), so Luca ran the ${role} on it.`,
|
|
65
|
+
confidence,
|
|
66
|
+
sharp,
|
|
67
|
+
}
|
|
68
|
+
case 'unsure':
|
|
69
|
+
return {
|
|
70
|
+
short: `${confidence === null ? 'unsure' : `${sure} ${word}`} → stayed on ${name}`,
|
|
71
|
+
full:
|
|
72
|
+
confidence === null
|
|
73
|
+
? `Clef picked ${pick} but didn't say how sure it was, so Luca stayed on ${name}.`
|
|
74
|
+
: sharp
|
|
75
|
+
? `Clef picked ${pick}, which the ${role} can't run on, so Luca stayed on ${name}.`
|
|
76
|
+
: `Clef picked ${pick} but was only ${sure} sure (${judged}), so Luca stayed on ${name}.`,
|
|
77
|
+
confidence,
|
|
78
|
+
sharp,
|
|
79
|
+
}
|
|
80
|
+
case 'floor':
|
|
81
|
+
return {
|
|
82
|
+
short: `floor ${name} → ${name}`,
|
|
83
|
+
full: `The ${role}'s model floor is ${name}, so it runs on ${name} whatever Clef picks.`,
|
|
84
|
+
...plain,
|
|
85
|
+
}
|
|
86
|
+
case 'after_failure':
|
|
87
|
+
return {
|
|
88
|
+
short: `a try failed → ${name}`,
|
|
89
|
+
full: `The ${role} already failed a try on this ticket on a cheaper model, so Luca runs it on ${name}.`,
|
|
90
|
+
...plain,
|
|
91
|
+
}
|
|
92
|
+
case 'no_answer':
|
|
93
|
+
return {
|
|
94
|
+
short: `clef didn't answer → ${name}`,
|
|
95
|
+
full: `Clef didn't answer (it was off, timed out, or failed), so Luca ran the ${role} on ${name}.`,
|
|
96
|
+
...plain,
|
|
97
|
+
}
|
|
98
|
+
case 'locked':
|
|
99
|
+
return {
|
|
100
|
+
short: `${plural({ role })} always run on ${name}`,
|
|
101
|
+
full: `${capital({ text: plural({ role }) })} always run on ${name}: Clef doesn't pick their model.`,
|
|
102
|
+
...plain,
|
|
103
|
+
}
|
|
104
|
+
default:
|
|
105
|
+
return null
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Clef's confidence as a small bar, filled to it. */
|
|
110
|
+
const ConfidenceBar = ({
|
|
111
|
+
confidence,
|
|
112
|
+
sharp,
|
|
113
|
+
styles,
|
|
114
|
+
}: {
|
|
115
|
+
confidence: number
|
|
116
|
+
sharp: boolean
|
|
117
|
+
} & Pick<Look, 'styles'>) => {
|
|
118
|
+
const now = Math.round(confidence * 100)
|
|
119
|
+
return (
|
|
120
|
+
<View
|
|
121
|
+
accessibilityRole="progressbar"
|
|
122
|
+
accessibilityLabel={`Clef's confidence, ${sharp ? 'sharp' : 'split'}`}
|
|
123
|
+
accessibilityValue={{ min: 0, max: 100, now }}
|
|
124
|
+
style={styles.confidence}
|
|
125
|
+
>
|
|
126
|
+
<View
|
|
127
|
+
style={[
|
|
128
|
+
styles.confidenceFill,
|
|
129
|
+
{
|
|
130
|
+
width: `${now}%`,
|
|
131
|
+
backgroundColor: sharp
|
|
132
|
+
? CONFIDENCE_COLORS.sharp
|
|
133
|
+
: CONFIDENCE_COLORS.split,
|
|
134
|
+
},
|
|
135
|
+
]}
|
|
136
|
+
/>
|
|
137
|
+
</View>
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** The model tag and Clef's line, on one line. */
|
|
142
|
+
export const ModelLine = ({
|
|
143
|
+
model,
|
|
144
|
+
bar,
|
|
145
|
+
theme,
|
|
146
|
+
styles,
|
|
147
|
+
}: {
|
|
148
|
+
model: AgentModel
|
|
149
|
+
bar: number
|
|
150
|
+
} & Look) => {
|
|
151
|
+
const color = modelColor({ model: model.name, theme })
|
|
152
|
+
const clef = clefLine({ model, bar })
|
|
153
|
+
return (
|
|
154
|
+
<View style={styles.line}>
|
|
155
|
+
<Text
|
|
156
|
+
style={[styles.tag, { color, borderColor: color }]}
|
|
157
|
+
numberOfLines={1}
|
|
158
|
+
>
|
|
159
|
+
{model.name}
|
|
160
|
+
</Text>
|
|
161
|
+
{clef !== null && clef.confidence !== null ? (
|
|
162
|
+
<ConfidenceBar
|
|
163
|
+
confidence={clef.confidence}
|
|
164
|
+
sharp={clef.sharp}
|
|
165
|
+
styles={styles}
|
|
166
|
+
/>
|
|
167
|
+
) : null}
|
|
168
|
+
{clef ? (
|
|
169
|
+
<CutLine
|
|
170
|
+
text={clef.short}
|
|
171
|
+
full={clef.full}
|
|
172
|
+
style={styles.muted}
|
|
173
|
+
styles={styles}
|
|
174
|
+
/>
|
|
175
|
+
) : null}
|
|
176
|
+
</View>
|
|
177
|
+
)
|
|
178
|
+
}
|