@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,403 @@
|
|
|
1
|
+
import type { BoardRecord } from './board-vocabulary'
|
|
2
|
+
import {
|
|
3
|
+
COMMITTED,
|
|
4
|
+
event,
|
|
5
|
+
finalFinishedText,
|
|
6
|
+
finalStartedText,
|
|
7
|
+
finishedText,
|
|
8
|
+
ignoredText,
|
|
9
|
+
learnerFinishedText,
|
|
10
|
+
lowerFirst,
|
|
11
|
+
messageText,
|
|
12
|
+
nothingToDoText,
|
|
13
|
+
on,
|
|
14
|
+
recalledText,
|
|
15
|
+
savedText,
|
|
16
|
+
SHIPPED_TEXT,
|
|
17
|
+
startedText,
|
|
18
|
+
} from './record-texts'
|
|
19
|
+
import {
|
|
20
|
+
failureText,
|
|
21
|
+
finalReasonText,
|
|
22
|
+
LEARNER,
|
|
23
|
+
reasonText,
|
|
24
|
+
RETRY_REFUSED_TEXT,
|
|
25
|
+
rebasedText,
|
|
26
|
+
runReasonText,
|
|
27
|
+
roleWords,
|
|
28
|
+
shortSha,
|
|
29
|
+
testsSentBackText,
|
|
30
|
+
} from './reduce-board'
|
|
31
|
+
|
|
32
|
+
import type { Tone } from '../shared/board-rows'
|
|
33
|
+
import {
|
|
34
|
+
LOOP_CAP,
|
|
35
|
+
stoppedText,
|
|
36
|
+
windowWords,
|
|
37
|
+
type BoardState,
|
|
38
|
+
type FindingCounts,
|
|
39
|
+
} from '../shared/board-state'
|
|
40
|
+
|
|
41
|
+
const findingsText = ({ findings }: { findings: FindingCounts }): string => {
|
|
42
|
+
const parts = [
|
|
43
|
+
findings.blocker > 0 ? `${findings.blocker} blocker` : null,
|
|
44
|
+
findings.should_fix > 0 ? `${findings.should_fix} should-fix` : null,
|
|
45
|
+
findings.nit > 0 ? `${findings.nit} nit` : null,
|
|
46
|
+
].filter((part) => part !== null)
|
|
47
|
+
return parts.length > 0 ? parts.join(', ') : 'no findings'
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* What a record means in one line, or `null` for records that are noise in
|
|
52
|
+
* a chat (snapshots, session summaries, a clean leftover scan, the decision
|
|
53
|
+
* model's asks in shadow mode, ...).
|
|
54
|
+
*/
|
|
55
|
+
export const describeRecord = ({
|
|
56
|
+
before,
|
|
57
|
+
after,
|
|
58
|
+
record,
|
|
59
|
+
}: {
|
|
60
|
+
before: BoardState
|
|
61
|
+
after: BoardState
|
|
62
|
+
record: BoardRecord
|
|
63
|
+
}): { text: string; tone: Tone } | null => {
|
|
64
|
+
const at = on({ ticket: record.ticket })
|
|
65
|
+
switch (record.kind) {
|
|
66
|
+
case 'run_started':
|
|
67
|
+
return event({
|
|
68
|
+
text: `The run started on spec #${record.content.spec_number}.`,
|
|
69
|
+
tone: 'info',
|
|
70
|
+
})
|
|
71
|
+
// Only a resume on a different Luca version is worth a row.
|
|
72
|
+
case 'engine_resumed':
|
|
73
|
+
return after.run.version_note !== null &&
|
|
74
|
+
after.run.version_note !== before.run.version_note
|
|
75
|
+
? event({ text: after.run.version_note, tone: 'warning' })
|
|
76
|
+
: null
|
|
77
|
+
case 'config_reloaded':
|
|
78
|
+
return event({
|
|
79
|
+
text: `Config reloaded: ${record.content.changes.map(({ field }) => field).join(', ')} changed.`,
|
|
80
|
+
tone: 'info',
|
|
81
|
+
})
|
|
82
|
+
case 'decision_model_off':
|
|
83
|
+
return event({
|
|
84
|
+
text: `Decision model off: ${record.content.detail}`,
|
|
85
|
+
tone: 'info',
|
|
86
|
+
})
|
|
87
|
+
case 'config_reload_refused':
|
|
88
|
+
return event({
|
|
89
|
+
text: `Config not reloaded, so the run keeps its own: ${record.content.reason.split('\n')[0] ?? ''}`,
|
|
90
|
+
tone: 'warning',
|
|
91
|
+
})
|
|
92
|
+
case 'intake_refused':
|
|
93
|
+
return event({
|
|
94
|
+
text: `Intake refused the run. ${after.run.refusal.join('; ')}`,
|
|
95
|
+
tone: 'danger',
|
|
96
|
+
})
|
|
97
|
+
case 'nothing_to_do':
|
|
98
|
+
return event({
|
|
99
|
+
text: nothingToDoText({ content: record.content }),
|
|
100
|
+
tone: 'info',
|
|
101
|
+
})
|
|
102
|
+
case 'spec_snapshot': {
|
|
103
|
+
const { spec, ticket_order, left_out } = record.content
|
|
104
|
+
const people =
|
|
105
|
+
left_out.length === 0
|
|
106
|
+
? ''
|
|
107
|
+
: ` ${left_out.length} left for a person: ${left_out.map(({ number }) => `#${number}`).join(', ')}.`
|
|
108
|
+
return event({
|
|
109
|
+
text: `Intake passed: spec #${spec.number}, ${ticket_order.length} tickets.${people}`,
|
|
110
|
+
tone: 'success',
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
case 'run_branch_created':
|
|
114
|
+
return event({
|
|
115
|
+
text: `The run branch ${record.content.branch} is ready.`,
|
|
116
|
+
tone: 'info',
|
|
117
|
+
})
|
|
118
|
+
case 'ticket_worktree_created':
|
|
119
|
+
return event({ text: `${at}started.`, tone: 'info' })
|
|
120
|
+
case 'usage_line_wait_started': {
|
|
121
|
+
const { line, percent, resets_at } = record.content
|
|
122
|
+
const window = windowWords({ window: record.content.window })
|
|
123
|
+
return event({
|
|
124
|
+
text: `Paused at the ${window} usage line: the account's ${window} window is at ${percent}% (line ${line}%). The run carries on when it resets at ${resets_at}, or once the line is raised.`,
|
|
125
|
+
tone: 'warning',
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
case 'usage_line_wait_ended':
|
|
129
|
+
return event({
|
|
130
|
+
text: 'The run carries on from the usage line.',
|
|
131
|
+
tone: 'info',
|
|
132
|
+
})
|
|
133
|
+
case 'dependencies_installed': {
|
|
134
|
+
const { check } = record.content
|
|
135
|
+
if (check === null || check.ok) return null
|
|
136
|
+
const where =
|
|
137
|
+
record.ticket === null
|
|
138
|
+
? "The run branch's install"
|
|
139
|
+
: `${at}the install`
|
|
140
|
+
return event({
|
|
141
|
+
text: `${where} failed: \`${check.command}\`.`,
|
|
142
|
+
tone: 'danger',
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
case 'agent_started':
|
|
146
|
+
if (record.content.role === LEARNER) {
|
|
147
|
+
return event({
|
|
148
|
+
text: "The learner reads the run's journal for lessons to keep.",
|
|
149
|
+
tone: 'info',
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
return record.ticket === null
|
|
153
|
+
? finalStartedText({ record })
|
|
154
|
+
: startedText({ before, after, record })
|
|
155
|
+
case 'agent_finished':
|
|
156
|
+
if (record.content.role === LEARNER) {
|
|
157
|
+
return learnerFinishedText({ record })
|
|
158
|
+
}
|
|
159
|
+
return record.ticket === null
|
|
160
|
+
? finalFinishedText({ record })
|
|
161
|
+
: finishedText({ before, record })
|
|
162
|
+
case 'agent_failed': {
|
|
163
|
+
const { role, error, failure } = record.content
|
|
164
|
+
const who =
|
|
165
|
+
role === LEARNER
|
|
166
|
+
? 'The learner'
|
|
167
|
+
: record.ticket === null
|
|
168
|
+
? `Final review: the ${roleWords({ role })}`
|
|
169
|
+
: `${at}the ${role}`
|
|
170
|
+
return event({
|
|
171
|
+
text: `${who} ${failureText({ failure })}: ${error.split('\n')[0] ?? ''}`,
|
|
172
|
+
tone: failure === 'engine' ? 'warning' : 'danger',
|
|
173
|
+
})
|
|
174
|
+
}
|
|
175
|
+
case 'run_stopped': {
|
|
176
|
+
const text = stoppedText(record.content)
|
|
177
|
+
return event({
|
|
178
|
+
text: `${at}${text.charAt(0).toLowerCase()}${text.slice(1)}`,
|
|
179
|
+
tone: 'danger',
|
|
180
|
+
})
|
|
181
|
+
}
|
|
182
|
+
case 'worktree_reset':
|
|
183
|
+
return event({
|
|
184
|
+
text: `${at}starting over from the tests after a bad test.`,
|
|
185
|
+
tone: 'warning',
|
|
186
|
+
})
|
|
187
|
+
case 'red_check': {
|
|
188
|
+
const failing = after.tickets.find(
|
|
189
|
+
(ticket) => ticket.number === record.ticket
|
|
190
|
+
)?.tests?.failing
|
|
191
|
+
return record.content.ok
|
|
192
|
+
? event({
|
|
193
|
+
text: `${at}red check passed${failing ? ` (${failing} new tests fail)` : ''}.`,
|
|
194
|
+
tone: 'success',
|
|
195
|
+
})
|
|
196
|
+
: event({
|
|
197
|
+
text: `${at}red check failed: ${record.content.problems[0] ?? 'no detail'}`,
|
|
198
|
+
tone: 'danger',
|
|
199
|
+
})
|
|
200
|
+
}
|
|
201
|
+
case 'leftover_scan':
|
|
202
|
+
return record.content.hits.length === 0
|
|
203
|
+
? null
|
|
204
|
+
: event({
|
|
205
|
+
text: `${record.ticket === null ? 'Final review: ' : at}the leftover scan found ${record.content.hits.map((hit) => hit.path).join(', ')}.`,
|
|
206
|
+
tone: 'warning',
|
|
207
|
+
})
|
|
208
|
+
case 'commit_made':
|
|
209
|
+
return record.ticket === null
|
|
210
|
+
? event({
|
|
211
|
+
text: 'Final review: the fixes committed on the run branch.',
|
|
212
|
+
tone: 'info',
|
|
213
|
+
})
|
|
214
|
+
: event({
|
|
215
|
+
text: `${at}${COMMITTED[record.content.stage] ?? 'tests'} committed.`,
|
|
216
|
+
tone: 'info',
|
|
217
|
+
})
|
|
218
|
+
case 'gates_run': {
|
|
219
|
+
const failed = record.content.checks
|
|
220
|
+
.filter((check) => !check.ok)
|
|
221
|
+
.map((check) => check.name)
|
|
222
|
+
// The final review's slow tests, after round 1's lenses (#563).
|
|
223
|
+
if (record.content.target === 'final_slow') {
|
|
224
|
+
return record.content.ok
|
|
225
|
+
? event({
|
|
226
|
+
text: 'Final review: slow tests passed.',
|
|
227
|
+
tone: 'success',
|
|
228
|
+
})
|
|
229
|
+
: event({
|
|
230
|
+
text: `Final review: slow tests failed: ${failed.join(', ') || 'no detail'}.`,
|
|
231
|
+
tone: 'danger',
|
|
232
|
+
})
|
|
233
|
+
}
|
|
234
|
+
const where =
|
|
235
|
+
record.ticket === null
|
|
236
|
+
? ' on the fixes'
|
|
237
|
+
: record.content.target === 'run_branch'
|
|
238
|
+
? ' after joining'
|
|
239
|
+
: ''
|
|
240
|
+
const who = record.ticket === null ? 'Final review: ' : at
|
|
241
|
+
return record.content.ok
|
|
242
|
+
? event({
|
|
243
|
+
text: `${who}checks passed${where}.`,
|
|
244
|
+
tone: 'success',
|
|
245
|
+
})
|
|
246
|
+
: event({
|
|
247
|
+
text: `${who}checks failed${where}: ${failed.join(', ') || 'no detail'}.`,
|
|
248
|
+
tone: 'danger',
|
|
249
|
+
})
|
|
250
|
+
}
|
|
251
|
+
case 'ticket_joined':
|
|
252
|
+
return record.content.ok
|
|
253
|
+
? event({
|
|
254
|
+
text: `${at}joined the run branch.`,
|
|
255
|
+
tone: 'success',
|
|
256
|
+
})
|
|
257
|
+
: event({
|
|
258
|
+
text: `${at}couldn't join the run branch: ${record.content.error ?? 'no detail'}`,
|
|
259
|
+
tone: 'danger',
|
|
260
|
+
})
|
|
261
|
+
case 'ticket_rebased':
|
|
262
|
+
return event({
|
|
263
|
+
text: `${at}${lowerFirst({ text: rebasedText({ content: record.content }) })}.`,
|
|
264
|
+
tone: 'warning',
|
|
265
|
+
})
|
|
266
|
+
case 'tests_sent_back':
|
|
267
|
+
return event({
|
|
268
|
+
text: `${at}${lowerFirst({ text: testsSentBackText({ content: record.content }) })}.`,
|
|
269
|
+
tone: 'warning',
|
|
270
|
+
})
|
|
271
|
+
case 'ticket_stuck':
|
|
272
|
+
return event({
|
|
273
|
+
text: `${at}stuck. ${reasonText({ reason: record.content.reason })}`,
|
|
274
|
+
tone: 'danger',
|
|
275
|
+
})
|
|
276
|
+
case 'run_stuck':
|
|
277
|
+
return event({
|
|
278
|
+
text: `The run is stuck. ${runReasonText({ reason: record.content.reason })}`,
|
|
279
|
+
tone: 'danger',
|
|
280
|
+
})
|
|
281
|
+
case 'pull_request_opened':
|
|
282
|
+
return event({
|
|
283
|
+
text: `Pull request #${record.content.number} opened: ${record.content.url}`,
|
|
284
|
+
tone: 'success',
|
|
285
|
+
})
|
|
286
|
+
case 'reply_received': {
|
|
287
|
+
const ticket = record.content.ticket ?? record.ticket
|
|
288
|
+
return event({
|
|
289
|
+
text: `You replied \`${record.content.word}${ticket === null ? '' : ` #${ticket}`}\`.`,
|
|
290
|
+
tone: 'info',
|
|
291
|
+
})
|
|
292
|
+
}
|
|
293
|
+
case 'ticket_already_done':
|
|
294
|
+
return event({
|
|
295
|
+
text: `${at}already done on the base branch, by ${record.content.shas.map((sha) => shortSha({ sha })).join(', ')}. Nothing to build.`,
|
|
296
|
+
tone: 'success',
|
|
297
|
+
})
|
|
298
|
+
case 'ticket_closed':
|
|
299
|
+
return event({
|
|
300
|
+
text: `${at}closed, since its work was already done.`,
|
|
301
|
+
tone: 'info',
|
|
302
|
+
})
|
|
303
|
+
case 'ticket_skipped': {
|
|
304
|
+
const { because } = record.content
|
|
305
|
+
return event({
|
|
306
|
+
text:
|
|
307
|
+
because === null
|
|
308
|
+
? `${at}skipped. It stays open for a later run.`
|
|
309
|
+
: `${at}skipped, since it waits on #${because}, which was skipped. It stays open for a later run.`,
|
|
310
|
+
tone: 'info',
|
|
311
|
+
})
|
|
312
|
+
}
|
|
313
|
+
case 'ticket_retried':
|
|
314
|
+
switch (record.content.mode) {
|
|
315
|
+
case 'resume':
|
|
316
|
+
return null
|
|
317
|
+
case 'restart':
|
|
318
|
+
return event({
|
|
319
|
+
text: `${at}starts over from the edited ticket.`,
|
|
320
|
+
tone: 'info',
|
|
321
|
+
})
|
|
322
|
+
case 'refused':
|
|
323
|
+
return event({
|
|
324
|
+
text: `${at}the retry was refused: ${lowerFirst({ text: RETRY_REFUSED_TEXT })} ${record.content.problems.join(' ')}`.trimEnd(),
|
|
325
|
+
tone: 'warning',
|
|
326
|
+
})
|
|
327
|
+
}
|
|
328
|
+
break
|
|
329
|
+
case 'reply_ignored':
|
|
330
|
+
return event({
|
|
331
|
+
text: `Your reply was sent back: ${ignoredText({ reason: record.content.reason, before })}. The answer is on the spec issue.`,
|
|
332
|
+
tone: 'warning',
|
|
333
|
+
})
|
|
334
|
+
case 'final_review_started':
|
|
335
|
+
return event({
|
|
336
|
+
text: `The final review started (round ${after.final_review.round}).`,
|
|
337
|
+
tone: 'info',
|
|
338
|
+
})
|
|
339
|
+
case 'lens_finished':
|
|
340
|
+
return event({
|
|
341
|
+
text: `Final review, ${record.content.lens} lens: ${findingsText({ findings: record.content.findings })}.`,
|
|
342
|
+
tone:
|
|
343
|
+
record.content.findings.blocker +
|
|
344
|
+
record.content.findings.should_fix >
|
|
345
|
+
0
|
|
346
|
+
? 'warning'
|
|
347
|
+
: 'success',
|
|
348
|
+
})
|
|
349
|
+
case 'final_review_fixing':
|
|
350
|
+
return event({
|
|
351
|
+
text: `Final review: fix round ${record.content.round}/${LOOP_CAP}.`,
|
|
352
|
+
tone: 'warning',
|
|
353
|
+
})
|
|
354
|
+
case 'final_review_stuck':
|
|
355
|
+
return event({
|
|
356
|
+
text: `The final review is stuck. ${finalReasonText({ reason: record.content.reason })}`,
|
|
357
|
+
tone: 'danger',
|
|
358
|
+
})
|
|
359
|
+
case 'final_review_passed':
|
|
360
|
+
return event({ text: 'The final review passed.', tone: 'success' })
|
|
361
|
+
case 'agent_message':
|
|
362
|
+
return messageText({ record })
|
|
363
|
+
case 'final_review_shipped':
|
|
364
|
+
return event({ text: SHIPPED_TEXT, tone: 'info' })
|
|
365
|
+
case 'memory_recalled':
|
|
366
|
+
return recalledText({ record })
|
|
367
|
+
case 'memories_saved':
|
|
368
|
+
return savedText({ record })
|
|
369
|
+
case 'learning_skipped':
|
|
370
|
+
return event({
|
|
371
|
+
text: `The learner was skipped: ${record.content.reason.split('\n')[0] ?? ''} The run ends without new memories.`,
|
|
372
|
+
tone: 'warning',
|
|
373
|
+
})
|
|
374
|
+
case 'memories_reported':
|
|
375
|
+
return event({
|
|
376
|
+
text: `The ${record.content.count} new ${record.content.count === 1 ? 'memory went' : 'memories went'} on the spec issue, since there is no PR.`,
|
|
377
|
+
tone: 'info',
|
|
378
|
+
})
|
|
379
|
+
// The repo wait shows as a banner on the panel (#632).
|
|
380
|
+
case 'repo_wait_started':
|
|
381
|
+
case 'repo_wait_ended':
|
|
382
|
+
case 'intake_read':
|
|
383
|
+
case 'ticket_snapshot':
|
|
384
|
+
case 'baseline_tests':
|
|
385
|
+
case 'baseline_reused':
|
|
386
|
+
case 'run_branch_pushed':
|
|
387
|
+
case 'worktrees_removed':
|
|
388
|
+
case 'agent_session':
|
|
389
|
+
case 'jev_asked':
|
|
390
|
+
case 'jev_answered':
|
|
391
|
+
case 'jev_failed':
|
|
392
|
+
case 'agent_message_delivered':
|
|
393
|
+
case 'shared_git_changed':
|
|
394
|
+
case 'limit_wait_started':
|
|
395
|
+
case 'limit_wait_ended':
|
|
396
|
+
case 'usage_recorded':
|
|
397
|
+
case 'lens_started':
|
|
398
|
+
case 'step_started':
|
|
399
|
+
case 'step_ended':
|
|
400
|
+
case 'ticket_waiting':
|
|
401
|
+
return null
|
|
402
|
+
}
|
|
403
|
+
}
|
|
@@ -10,25 +10,42 @@ import type { EngineSettings } from '../shared/engine-settings'
|
|
|
10
10
|
* `import.meta.url` is undefined and the cwd is `/`.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
/** What a run builds: one spec, or the engine's safe practice run. */
|
|
13
|
+
/** What a new run builds: one spec, or the engine's safe practice run. */
|
|
14
14
|
export type RunTarget = { kind: 'spec'; spec: number } | { kind: 'demo' }
|
|
15
15
|
|
|
16
|
+
/** What `/luca-run` asks for: a new run, or to resume a run the board started. */
|
|
17
|
+
export type RunRequest = RunTarget | { kind: 'resume'; run_id: string }
|
|
18
|
+
|
|
16
19
|
/**
|
|
17
|
-
* Reads what followed `/luca-run`: `123`, `#123`,
|
|
20
|
+
* Reads what followed `/luca-run`: `123`, `#123`, `demo`, or
|
|
21
|
+
* `resume <run id>`.
|
|
18
22
|
*
|
|
19
23
|
* @example
|
|
20
24
|
* parseRunArgs({ args: '#12' }) // { ok: true, target: { kind: 'spec', spec: 12 } }
|
|
25
|
+
* parseRunArgs({ args: 'resume luca-20260928-124805-69hp' }) // { ok: true, target: { kind: 'resume', run_id: 'luca-20260928-124805-69hp' } }
|
|
21
26
|
* parseRunArgs({ args: 'soon' }).ok // false
|
|
22
27
|
*/
|
|
23
28
|
export const parseRunArgs = ({
|
|
24
29
|
args,
|
|
25
30
|
}: {
|
|
26
31
|
args: string
|
|
27
|
-
}): { ok: true; target:
|
|
32
|
+
}): { ok: true; target: RunRequest } | { ok: false; message: string } => {
|
|
28
33
|
const text = args.trim()
|
|
29
34
|
if (text.toLowerCase() === 'demo') {
|
|
30
35
|
return { ok: true, target: { kind: 'demo' } }
|
|
31
36
|
}
|
|
37
|
+
const words = text.split(/\s+/)
|
|
38
|
+
if (words[0]?.toLowerCase() === 'resume') {
|
|
39
|
+
const run_id = words[1]
|
|
40
|
+
return run_id !== undefined &&
|
|
41
|
+
words.length === 2 &&
|
|
42
|
+
!run_id.startsWith('-')
|
|
43
|
+
? { ok: true, target: { kind: 'resume', run_id } }
|
|
44
|
+
: {
|
|
45
|
+
ok: false,
|
|
46
|
+
message: `Which run? Give its id, as the board shows it. Usage: ${RUN_USAGE}`,
|
|
47
|
+
}
|
|
48
|
+
}
|
|
32
49
|
const match = /^#?(\d+)$/.exec(text)
|
|
33
50
|
const spec = match?.[1] ? Number(match[1]) : 0
|
|
34
51
|
if (spec > 0 && Number.isSafeInteger(spec)) {
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { mkdtemp } from 'node:fs/promises'
|
|
2
|
+
import { tmpdir } from 'node:os'
|
|
3
|
+
import { join } from 'node:path'
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
createHarness,
|
|
7
|
+
type Harness,
|
|
8
|
+
type UnfinishedRun,
|
|
9
|
+
} from './testing/board-harness'
|
|
10
|
+
import { runStarted } from './testing/journal-fixtures'
|
|
11
|
+
|
|
12
|
+
import { ROW_KIND } from '../shared/board-rows'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Helpers shared by the engine restart tests (`engine-restart.test.ts` and
|
|
16
|
+
* the `engine-restart-*.test.ts` files beside it).
|
|
17
|
+
*
|
|
18
|
+
* The boards and folders a test makes are in `harnesses` and `dirs`; each
|
|
19
|
+
* test file cleans them up after every test.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
export const harnesses: Harness[] = []
|
|
23
|
+
export const dirs: string[] = []
|
|
24
|
+
|
|
25
|
+
/** A plugin on a registry kept in `registry_dir`, so it can be restarted. */
|
|
26
|
+
export const plugin = async ({
|
|
27
|
+
registry_dir,
|
|
28
|
+
...options
|
|
29
|
+
}: {
|
|
30
|
+
registry_dir: string
|
|
31
|
+
} & Omit<Parameters<typeof createHarness>[0] & object, 'registry_dir'>) => {
|
|
32
|
+
const harness = await createHarness({ registry_dir, ...options })
|
|
33
|
+
harnesses.push(harness)
|
|
34
|
+
return harness
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const registryDir = async () => {
|
|
38
|
+
const dir = await mkdtemp(join(tmpdir(), 'luca-board-restart-'))
|
|
39
|
+
dirs.push(dir)
|
|
40
|
+
return dir
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Runs started by a first plugin, whose engines sent `run_started`. */
|
|
44
|
+
export const startRuns = async ({
|
|
45
|
+
registry_dir,
|
|
46
|
+
runs,
|
|
47
|
+
}: {
|
|
48
|
+
registry_dir: string
|
|
49
|
+
runs: { args: string; cwd?: string }[]
|
|
50
|
+
}) => {
|
|
51
|
+
const first = await plugin({ registry_dir })
|
|
52
|
+
const started = []
|
|
53
|
+
for (const { args, cwd } of runs) {
|
|
54
|
+
const run = await first.start({ args, cwd })
|
|
55
|
+
await first.send({
|
|
56
|
+
run_id: run.run_id,
|
|
57
|
+
token: run.token,
|
|
58
|
+
entries: [runStarted({ spec: 10 })],
|
|
59
|
+
})
|
|
60
|
+
started.push(run)
|
|
61
|
+
}
|
|
62
|
+
return started
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const listed = ({
|
|
66
|
+
run_id,
|
|
67
|
+
restart = true,
|
|
68
|
+
reason = 'resumable',
|
|
69
|
+
message = null,
|
|
70
|
+
}: Partial<UnfinishedRun> & { run_id: string }): UnfinishedRun => ({
|
|
71
|
+
run_id,
|
|
72
|
+
restart,
|
|
73
|
+
reason,
|
|
74
|
+
message,
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
export const registryEntry = async ({
|
|
78
|
+
registry_dir,
|
|
79
|
+
run_id,
|
|
80
|
+
}: {
|
|
81
|
+
registry_dir: string
|
|
82
|
+
run_id: string
|
|
83
|
+
}) => {
|
|
84
|
+
const file = JSON.parse(
|
|
85
|
+
await Bun.file(join(registry_dir, 'runs.json')).text()
|
|
86
|
+
) as { runs: { run_id: string; restarts: number; ended: unknown }[] }
|
|
87
|
+
return file.runs.find((entry) => entry.run_id === run_id)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export const runOf = async ({
|
|
91
|
+
harness,
|
|
92
|
+
run_id,
|
|
93
|
+
}: {
|
|
94
|
+
harness: Harness
|
|
95
|
+
run_id: string
|
|
96
|
+
}) => {
|
|
97
|
+
const { selected } = await harness.read({ run_id })
|
|
98
|
+
if (!selected) throw new Error(`no run ${run_id}`)
|
|
99
|
+
return selected.run
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export const eventTexts = ({ harness }: { harness: Harness }) =>
|
|
103
|
+
harness.rows.flatMap(({ row }) =>
|
|
104
|
+
row.kind === ROW_KIND.event ? [row.data.text] : []
|
|
105
|
+
)
|
|
@@ -131,6 +131,18 @@ export type StopWhy =
|
|
|
131
131
|
| { kind: 'check_failed'; error: string }
|
|
132
132
|
| { kind: 'spawn_failed'; error: string }
|
|
133
133
|
|
|
134
|
+
/**
|
|
135
|
+
* How the owner resumes run `run_id` with the board attached: the
|
|
136
|
+
* `/luca-run resume` slash command (see `resumeRow`). A bare
|
|
137
|
+
* `luca-run --resume` would run without the board's token, so the board
|
|
138
|
+
* would show it read-only.
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* resumeHint({ run_id: 'r1' }) // 'type /luca-run resume r1 in a Paseo chat'
|
|
142
|
+
*/
|
|
143
|
+
export const resumeHint = ({ run_id }: { run_id: string }): string =>
|
|
144
|
+
`type /luca-run resume ${run_id} in a Paseo chat`
|
|
145
|
+
|
|
134
146
|
/**
|
|
135
147
|
* What the board says when a run's engine is gone and the plugin won't
|
|
136
148
|
* restart it. The panel and header show it as "The engine stopped: <text>".
|
|
@@ -147,22 +159,22 @@ export const engineStoppedText = ({
|
|
|
147
159
|
run_id: string
|
|
148
160
|
log_path: string
|
|
149
161
|
}): string => {
|
|
150
|
-
const resume =
|
|
162
|
+
const resume = resumeHint({ run_id })
|
|
151
163
|
switch (why.kind) {
|
|
152
164
|
case 'demo':
|
|
153
165
|
return `its process is gone, and a demo can't be picked up again. Start a new one with /luca-run demo. Its log is ${log_path}.`
|
|
154
166
|
case 'not_listed':
|
|
155
167
|
return `its process is gone, and its journal has nothing to pick up again (it never wrote one, or the run already ended). Read its log: ${log_path}.`
|
|
156
168
|
case 'launcher_stopped':
|
|
157
|
-
return `The run stopped: ${why.reason}. It isn't restarted automatically, because it would stop the same way. Fix that, then
|
|
169
|
+
return `The run stopped: ${why.reason}. It isn't restarted automatically, because it would stop the same way. Fix that, then ${resume}. Its log is ${log_path}.`
|
|
158
170
|
case 'billing_stopped':
|
|
159
171
|
return `The run stopped for billing: ${why.reason}. It will not go on; start a new run once per-token billing is off.`
|
|
160
172
|
case 'restarts_used_up':
|
|
161
|
-
return `it stopped again after ${MAX_AUTO_RESTARTS} automatic restarts, so Paseo stopped restarting it. Read its log: ${log_path}. To try once more,
|
|
173
|
+
return `it stopped again after ${MAX_AUTO_RESTARTS} automatic restarts, so Paseo stopped restarting it. Read its log: ${log_path}. To try once more, ${resume}.`
|
|
162
174
|
case 'check_failed':
|
|
163
|
-
return `its process is gone, and Paseo couldn't check whether the run can go on (${why.error}). To go on from its journal,
|
|
175
|
+
return `its process is gone, and Paseo couldn't check whether the run can go on (${why.error}). To go on from its journal, ${resume}. Its log is ${log_path}.`
|
|
164
176
|
case 'spawn_failed':
|
|
165
|
-
return `its process is gone, and Paseo couldn't restart the engine: ${why.error}. To go on from its journal,
|
|
177
|
+
return `its process is gone, and Paseo couldn't restart the engine: ${why.error}. To go on from its journal, ${resume}. Its log is ${log_path}.`
|
|
166
178
|
}
|
|
167
179
|
}
|
|
168
180
|
|
|
@@ -195,3 +207,31 @@ export const restartRow = ({
|
|
|
195
207
|
tone: 'warning',
|
|
196
208
|
},
|
|
197
209
|
})
|
|
210
|
+
|
|
211
|
+
/** The words of the chat row for a run resumed with `/luca-run resume`. */
|
|
212
|
+
export const RESUMED_TEXT =
|
|
213
|
+
'The run was resumed from its journal with /luca-run resume.'
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* The chat row for a run resumed with `/luca-run resume`; its id is unique
|
|
217
|
+
* per run and time, so each resume gets its own row.
|
|
218
|
+
*
|
|
219
|
+
* @example
|
|
220
|
+
* resumeRow({ run_id, time: new Date().toISOString() })
|
|
221
|
+
*/
|
|
222
|
+
export const resumeRow = ({
|
|
223
|
+
run_id,
|
|
224
|
+
time,
|
|
225
|
+
}: {
|
|
226
|
+
run_id: string
|
|
227
|
+
time: string
|
|
228
|
+
}): BoardRow => ({
|
|
229
|
+
id: `${run_id}-resumed-${time}`,
|
|
230
|
+
kind: ROW_KIND.event,
|
|
231
|
+
data: {
|
|
232
|
+
time,
|
|
233
|
+
ticket: null,
|
|
234
|
+
text: RESUMED_TEXT,
|
|
235
|
+
tone: 'info',
|
|
236
|
+
},
|
|
237
|
+
})
|