geneseed 4.14.0 → 4.15.1

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.
Files changed (47) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +1 -1
  3. package/SHIPPED.md +1 -1
  4. package/adapters/opencode/plugins/geneseed-guard.js +16 -1
  5. package/adapters/opencode/plugins/geneseed-learn.js +28 -12
  6. package/adapters/opencode/plugins/geneseed-workflow.js +19 -2
  7. package/docs/guides/install-bob.md +3 -1
  8. package/harness.config.json +1 -1
  9. package/js/build/emit-claude.mjs +4 -2
  10. package/js/build/stubs.mjs +13 -0
  11. package/js/inspect/cost.mjs +1 -1
  12. package/package.json +1 -1
  13. package/src/doctrines/comms.md +21 -0
  14. package/themes/_TEMPLATE.json +1 -0
  15. package/themes/biker.json +1 -0
  16. package/themes/commentator.json +1 -0
  17. package/themes/cyberpunk.json +1 -0
  18. package/themes/gamer.json +1 -0
  19. package/themes/imperial.json +1 -0
  20. package/themes/joker.json +1 -0
  21. package/themes/marvin.json +1 -0
  22. package/themes/mean.json +1 -0
  23. package/themes/military.json +1 -0
  24. package/themes/neutral.json +1 -0
  25. package/themes/pirate.json +1 -0
  26. package/themes/sports.json +1 -0
  27. package/themes/verstappen.json +1 -0
  28. package/themes/wizard.json +1 -0
  29. package/web/dist/assets/{Activity-44rbWUQI.js → Activity-M5qj6CxJ.js} +1 -1
  30. package/web/dist/assets/{ActivityDetail-Dk9k893a.js → ActivityDetail-DVCdNy73.js} +1 -1
  31. package/web/dist/assets/{Dashboard-DCoUN60W.js → Dashboard-B1rpgTz-.js} +2 -2
  32. package/web/dist/assets/{Docs-C_je2lRG.js → Docs-D-EixVuX.js} +1 -1
  33. package/web/dist/assets/{GreenhouseView-BXAOIznW.js → GreenhouseView-BvA4t12P.js} +1 -1
  34. package/web/dist/assets/{Installs-DAXF403D.js → Installs-DP4WjTpD.js} +1 -1
  35. package/web/dist/assets/{JournalView-DZ184wKB.js → JournalView-hf2eO133.js} +1 -1
  36. package/web/dist/assets/{KitApply-pjhvGha0.js → KitApply-aORiqjFC.js} +1 -1
  37. package/web/dist/assets/{Kits-BCCKjSIY.js → Kits-DpIEozdv.js} +1 -1
  38. package/web/dist/assets/{Laws-BgBUpRkE.js → Laws-cM_4CG9i.js} +2 -2
  39. package/web/dist/assets/{Library-CW0Zl6RF.js → Library-I1zEH3UW.js} +1 -1
  40. package/web/dist/assets/{Loops-BvFOzjEw.js → Loops-CDa_mxP9.js} +1 -1
  41. package/web/dist/assets/{OperatorHudView-Bs4unPNi.js → OperatorHudView-CMf1DQMB.js} +1 -1
  42. package/web/dist/assets/{Personal-C23XaT-a.js → Personal-BqU9Pzt1.js} +1 -1
  43. package/web/dist/assets/{Tabs-b2XFu4OS.js → Tabs-JZ2FQBcz.js} +1 -1
  44. package/web/dist/assets/{drawable-BERkKhx_.js → drawable-svc6-2Gp.js} +1 -1
  45. package/web/dist/assets/{index-C9R_bRw_.js → index-CZFrNe4N.js} +2 -2
  46. package/web/dist/index.html +1 -1
  47. package/web/src/pages/Laws.jsx +5 -0
package/CHANGELOG.md CHANGED
@@ -8,6 +8,27 @@ label. For the capability ↔ spec map, see [SHIPPED.md](SHIPPED.md).
8
8
 
9
9
  ## [Unreleased]
10
10
 
11
+ ## [4.15.1] — 2026-10-23
12
+
13
+ Patch: two host fixes. The first npm release since 4.14.0, so it also carries 4.15.0's comms 4 doctrine.
14
+
15
+ ### Upgrade notes
16
+ - Run `geneseed rebuild-all`; nothing changes on a machine until something re-emits.
17
+
18
+ ### Fixed
19
+ - OpenCode 1.x: the guard and learn plugins no longer write `[geneseed-guard] …` / `[geneseed-learn] …` lines in red over the TUI input box. Their messages now go to OpenCode's own log (`client.app.log`, service `geneseed-guard` / `geneseed-learn`). OpenCode 2 and a refused log call still use stderr. The workflow plugin's one-line load banner stays on stderr.
20
+ - IBM Bob: the first reply now starts with the theme's readiness line. Both Bob carriers (global `rules/geneseed.md`, the project's `AGENTS.md` block) open with a short first-reply override; without it, Bob's own greeting won. A bare "hi" still gets Bob's fixed welcome. Run `geneseed rebuild-all` to pick it up.
21
+
22
+ ## [4.15.0] — 2026-10-22
23
+
24
+ Minor: a new comms doctrine rule makes every generated agent write its prose in ASD-STE100 Simplified Technical English.
25
+
26
+ ### Upgrade notes
27
+ - Run `geneseed rebuild-all` to get the new rule; nothing changes on a machine until something re-emits. To keep the old behaviour on one install, rebuild it with `--exclude-rules "comms 4"`.
28
+
29
+ ### Added
30
+ - **Doctrine comms 4, Simplified Technical English.** Every generated agent writes the prose of its replies and reports in ASD-STE100: one topic per sentence (at most 20 words in an instruction, 25 in a description), imperative steps with one action each and the condition first, the active voice, no -ing forms, no dropped articles, and one word per meaning. Code, commands, paths, identifiers and quoted output stay as they are. STE is an English standard and the ontology imposes no language, so an answer in another language keeps that language and the same sentence rules; the theme keeps its voice. It is in the toggleable comms pack, not a law, per DESIGN.md Decision 7: `--exclude-rules "comms 4"` turns it off. The carrier ceilings rise by 1 003 characters (full) and 472 (lean). Run `geneseed rebuild-all` to get it.
31
+
11
32
  ## [4.14.0] — 2026-10-22
12
33
 
13
34
  Minor: the React kit supports webpack projects as well as Vite.
package/README.md CHANGED
@@ -13,7 +13,7 @@
13
13
  [![Themes](https://img.shields.io/badge/themes-14-9cf)](themes/)
14
14
  [![Skills](https://img.shields.io/badge/skills-47-blueviolet)](src/skills/)
15
15
  [![Agents](https://img.shields.io/badge/agents-20-orange)](src/agents/)
16
- [![Laws and doctrines](https://img.shields.io/badge/laws-9%20%2B%2029%20doctrines-critical)](src/doctrines/)
16
+ [![Laws and doctrines](https://img.shields.io/badge/laws-9%20%2B%2030%20doctrines-critical)](src/doctrines/)
17
17
  [![Plugins](https://img.shields.io/badge/plugins-7-teal)](adapters/opencode/plugins/)
18
18
  [![OpenCode · Claude Code · Bob · OpenClaude · AGENT.md](https://img.shields.io/badge/works%20with-OpenCode%20·%20Claude%20Code%20·%20Bob%20·%20OpenClaude%20·%20AGENT.md-1f6feb)](#-supported-harnesses)
19
19
 
package/SHIPPED.md CHANGED
@@ -20,7 +20,7 @@ historical references, not files distributed with the repo.
20
20
  > **On version numbers.** [`harness.config.json`](harness.config.json)'s `version` is a
21
21
  > human-readable label only. The canonical identity of an installed harness is the
22
22
  > **source fingerprint** stamped in `.geneseed-version` (see `geneseed version`), not the
23
- > string. Current label: **4.14.0**.
23
+ > string. Current label: **4.15.1**.
24
24
 
25
25
  ## Capabilities
26
26
 
@@ -153,7 +153,21 @@ const gateOff = (marker) => new RegExp(`^${marker}:[ \\t]*off[ \\t]*\\r?$`, "m")
153
153
  const DELETION_GATE_OFF = gateOff("Deletion gate")
154
154
  const SECRETS_GATE_OFF = gateOff("Secrets gate")
155
155
 
156
- function log(msg) { console.error(`[geneseed-guard] ${msg}`) }
156
+ // OpenCode 1.x paints a plugin's stderr over its TUI input box (in red); its own log API
157
+ // writes to the log file instead. The V1 entry sets LOG_CLIENT; V2 and tests have no
158
+ // client and keep stderr, as does a refused log call, so no line is lost. Never throws:
159
+ // a log line must not break a tool call.
160
+ let LOG_CLIENT = null
161
+ function say(msg, level = "warn") {
162
+ try {
163
+ if (LOG_CLIENT?.app?.log) {
164
+ Promise.resolve(LOG_CLIENT.app.log({ body: { service: "geneseed-guard", level, message: msg } })).catch(() => console.error(`[geneseed-guard] ${msg}`))
165
+ return
166
+ }
167
+ } catch { /* fall through to stderr */ }
168
+ console.error(`[geneseed-guard] ${msg}`)
169
+ }
170
+ const log = (msg) => say(msg)
157
171
 
158
172
  // ---- the gate ledger ------------------------------------------------------------
159
173
  // The twin of `ledger()` in js/hosts/hooks.mjs: one JSON line per BLOCK into
@@ -1369,6 +1383,7 @@ async function recordUse(tool, args) {
1369
1383
  }
1370
1384
 
1371
1385
  export const GeneseedGuard = async (ctx) => {
1386
+ if (ctx?.client) LOG_CLIENT = ctx.client
1372
1387
  // The session's working directory, the same resolution the context plugin uses.
1373
1388
  const root = () => ctx?.worktree || ctx?.directory || process.cwd()
1374
1389
  return {
@@ -38,6 +38,21 @@ import * as path from "node:path"
38
38
  import { fileURLToPath } from "node:url"
39
39
  import { homedir } from "node:os"
40
40
 
41
+ // OpenCode 1.x paints a plugin's stderr over its TUI input box (in red); its own log API
42
+ // writes to the log file instead. The V1 entry sets LOG_CLIENT; V2 and tests have no
43
+ // client and keep stderr, as does a refused log call, so no line is lost. Never throws:
44
+ // a log line must not break a tool call.
45
+ let LOG_CLIENT = null
46
+ function say(msg, level = "warn") {
47
+ try {
48
+ if (LOG_CLIENT?.app?.log) {
49
+ Promise.resolve(LOG_CLIENT.app.log({ body: { service: "geneseed-learn", level, message: msg } })).catch(() => console.error(`[geneseed-learn] ${msg}`))
50
+ return
51
+ }
52
+ } catch { /* fall through to stderr */ }
53
+ console.error(`[geneseed-learn] ${msg}`)
54
+ }
55
+
41
56
  // The plugin's own directory. In the global install the layout is
42
57
  // <cfg>/plugins/geneseed-learn.js beside <cfg>/memory, so the store can be found
43
58
  // relative to this file — no manual `export GENESEED_HARNESS` needed.
@@ -471,17 +486,18 @@ async function storeDistil(output, { isChild, memDir, agentName, existing }) {
471
486
  const lesson = output.trim().split("\n")[0].replace(/^[-*]\s*/, "").trim()
472
487
  if (lesson.length < 10 || lesson.length > 300) return // junk / truncation guard
473
488
  const f = await appendAgentLesson(memDir, agentName, lesson)
474
- console.error(`[geneseed-learn] agent lesson -> ${f}`)
489
+ say(`agent lesson -> ${f}`, "info")
475
490
  return
476
491
  }
477
492
  const res = await writeMemories(output, memDir, existing)
478
493
  for (const [verb, slugs] of [["wrote", res.created], ["updated", res.updated], ["archived", res.archived]]) {
479
- if (slugs.length) console.error(`[geneseed-learn] ${verb} ${slugs.length} memory file(s): ${slugs.join(", ")}`)
494
+ if (slugs.length) say(`${verb} ${slugs.length} memory file(s): ${slugs.join(", ")}`, "info")
480
495
  }
481
496
  }
482
497
 
483
498
  export const GeneseedLearn = async (ctx) => {
484
499
  const { client } = ctx ?? {}
500
+ if (client) LOG_CLIENT = client
485
501
  const ours = new Set() // throwaway distil sessions — never mine our own output
486
502
  const timers = new Map() // sid -> debounce timer, re-armed on each idle
487
503
  const inFlight = new Set() // sid currently being distilled (guards against overlap)
@@ -510,8 +526,8 @@ export const GeneseedLearn = async (ctx) => {
510
526
  if (!memDir) {
511
527
  if (!warnedNoDir) {
512
528
  warnedNoDir = true
513
- console.error(
514
- "[geneseed-learn] no memory dir — set $GENESEED_HARNESS or $GENESEED_MEMORY."
529
+ say(
530
+ "no memory dir — set $GENESEED_HARNESS or $GENESEED_MEMORY."
515
531
  )
516
532
  }
517
533
  return
@@ -524,8 +540,8 @@ export const GeneseedLearn = async (ctx) => {
524
540
  notes = notes.slice(-MAX_NOTES_CHARS)
525
541
  model = model ?? envModel()
526
542
  if (!model) {
527
- console.error(
528
- "[geneseed-learn] could not determine a model — set $GENESEED_MODEL=provider/model."
543
+ say(
544
+ "could not determine a model — set $GENESEED_MODEL=provider/model."
529
545
  )
530
546
  return
531
547
  }
@@ -540,7 +556,7 @@ export const GeneseedLearn = async (ctx) => {
540
556
  const newId = session?.id ?? session?.data?.id
541
557
  if (!newId) {
542
558
  // Prompting with {id: undefined} would 404 silently on every idle.
543
- console.error("[geneseed-learn] session.create returned no id — skipping this distil.")
559
+ say("session.create returned no id — skipping this distil.")
544
560
  return
545
561
  }
546
562
  ours.add(newId)
@@ -558,7 +574,7 @@ export const GeneseedLearn = async (ctx) => {
558
574
  await storeDistil(output, { isChild, memDir, agentName, existing })
559
575
  } catch (err) {
560
576
  // Never break the session over a memory write.
561
- console.error(`[geneseed-learn] skipped: ${err?.message ?? err}`)
577
+ say(`skipped: ${err?.message ?? err}`)
562
578
  } finally {
563
579
  inFlight.delete(sid)
564
580
  }
@@ -647,7 +663,7 @@ async function setupV2(ctx) {
647
663
  if (!memDir) {
648
664
  if (!warnedNoDir) {
649
665
  warnedNoDir = true
650
- console.error("[geneseed-learn] no memory dir — set $GENESEED_HARNESS or $GENESEED_MEMORY.")
666
+ say("no memory dir — set $GENESEED_HARNESS or $GENESEED_MEMORY.")
651
667
  }
652
668
  return
653
669
  }
@@ -660,7 +676,7 @@ async function setupV2(ctx) {
660
676
  model = env ? { providerID: env.providerID, id: env.modelID } : null
661
677
  }
662
678
  if (!model) {
663
- console.error("[geneseed-learn] could not determine a model — set $GENESEED_MODEL=provider/model.")
679
+ say("could not determine a model — set $GENESEED_MODEL=provider/model.")
664
680
  return
665
681
  }
666
682
 
@@ -673,7 +689,7 @@ async function setupV2(ctx) {
673
689
  })
674
690
  await storeDistil(String(reply?.text ?? ""), { isChild, memDir, agentName, existing })
675
691
  } catch (err) {
676
- console.error(`[geneseed-learn] skipped: ${err?.message ?? err}`)
692
+ say(`skipped: ${err?.message ?? err}`)
677
693
  } finally {
678
694
  inFlight.delete(sid)
679
695
  }
@@ -695,7 +711,7 @@ async function setupV2(ctx) {
695
711
  if (prev) clearTimeout(prev)
696
712
  timers.set(sid, setTimeout(() => { timers.delete(sid); runDistill(sid) }, DEBOUNCE_MS))
697
713
  }
698
- })().catch((err) => console.error(`[geneseed-learn] event stream ended: ${err?.message ?? err}`))
714
+ })().catch((err) => say(`event stream ended: ${err?.message ?? err}`))
699
715
  return () => {
700
716
  controller.abort()
701
717
  for (const t of timers.values()) clearTimeout(t)
@@ -25,7 +25,21 @@ import * as path from "node:path"
25
25
  import { fileURLToPath, pathToFileURL } from "node:url"
26
26
 
27
27
  const DEBUG = !!process.env.GENESEED_DEBUG
28
- function dlog(m) { if (DEBUG) console.error(`[geneseed-workflow] ${m}`) }
28
+ function dlog(m) { if (DEBUG) say(m, "debug") }
29
+ // OpenCode 1.x paints a plugin's stderr over its TUI input box (in red); its own log API
30
+ // writes to the log file instead. The V1 entry sets LOG_CLIENT; V2 and tests have no
31
+ // client and keep stderr, as does a refused log call, so no line is lost. Never throws:
32
+ // a log line must not break a tool call.
33
+ let LOG_CLIENT = null
34
+ function say(msg, level = "warn") {
35
+ try {
36
+ if (LOG_CLIENT?.app?.log) {
37
+ Promise.resolve(LOG_CLIENT.app.log({ body: { service: "geneseed-workflow", level, message: msg } })).catch(() => console.error(`[geneseed-workflow] ${msg}`))
38
+ return
39
+ }
40
+ } catch { /* fall through to stderr */ }
41
+ console.error(`[geneseed-workflow] ${msg}`)
42
+ }
29
43
 
30
44
  const __dir = path.dirname(fileURLToPath(import.meta.url))
31
45
  const WORKFLOWS_DIR = process.env.GENESEED_WORKFLOWS_DIR
@@ -184,7 +198,9 @@ const NAME_DESCRIPTION = "saved workflow name; omit to list available"
184
198
  const ARGS_DESCRIPTION = "inputs passed to the workflow script as `args` (object, or a JSON object string)"
185
199
 
186
200
  // One-line load banner (always on) so a restart visibly confirms the plugin loaded and the
187
- // `workflow` tool is registered. tests/host_canary.mjs reads it as V1's proof of load.
201
+ // `workflow` tool is registered. tests/host_canary.mjs reads it as V1's proof of load, so it
202
+ // stays on stderr, not `say`: `opencode debug config` never prints a `client.app.log` line. It
203
+ // runs once, before the TUI draws, so it does not land in the input box as `say`'s lines did.
188
204
  async function loadBanner(warn = "") {
189
205
  try {
190
206
  const names = await listWorkflows()
@@ -194,6 +210,7 @@ async function loadBanner(warn = "") {
194
210
 
195
211
  export const GeneseedWorkflow = async (ctx) => {
196
212
  const { client, directory, worktree } = ctx
213
+ if (client) LOG_CLIENT = client
197
214
  let toolHelper
198
215
  try { ({ tool: toolHelper } = await import("@opencode-ai/plugin")) }
199
216
  catch { dlog("@opencode-ai/plugin tool helper unavailable — registering a raw tool def") }
@@ -13,7 +13,9 @@ geneseed build --emit bob-global # ~/.bob
13
13
  geneseed build --emit bob --out /path/to/repo --root /path/to/repo # one repo
14
14
  ```
15
15
 
16
- Bob is shaped like Claude Code, so it gets the same agents and skills. Per repo, the preamble goes into a root `AGENTS.md` and the rest into `.bob/`. Globally, everything goes under `~/.bob` (`$BOB_CONFIG_DIR` moves it), and the preamble rides `rules/geneseed.md`, because Bob does not auto-load a global `AGENTS.md`. Bob's hooks refuse an action with exit code 2, and Bob has no `SubagentStop` or `PreCompact` events. Bob support has not yet been verified against a live install.
16
+ Bob is shaped like Claude Code, so it gets the same agents and skills. Per repo, the preamble goes into a root `AGENTS.md` and the rest into `.bob/`. Globally, everything goes under `~/.bob` (`$BOB_CONFIG_DIR` moves it), and the preamble rides `rules/geneseed.md`, because Bob does not auto-load a global `AGENTS.md`. Bob's hooks refuse an action with exit code 2, and Bob has no `SubagentStop` or `PreCompact` events.
17
+
18
+ Both scopes open Bob's carrier with a short first-reply override, so the theme's readiness line (for example "🧬 Gene-seed implanted…") starts the first reply. Without it, Bob's own greeting outranked the instruction lower in the file. A bare greeting such as "hi" still gets Bob's fixed welcome; the line appears on the first real request. That much was checked by hand in the Bob IDE (2026-10); the rest of the Bob support has not been verified against a live install.
17
19
 
18
20
  ---
19
21
 
@@ -2,5 +2,5 @@
2
2
  "_comment": "version is a human-readable release label only; the canonical identity of an installed harness is the source fingerprint stamped in .geneseed-version (see `harness version`), not this string.",
3
3
  "theme": "neutral",
4
4
  "product": "Geneseed",
5
- "version": "4.14.0"
5
+ "version": "4.15.1"
6
6
  }
@@ -21,7 +21,7 @@ import {
21
21
  } from './emit-common.mjs';
22
22
  import { kitsBody, renderAll, renderFile } from './render.mjs';
23
23
  import {
24
- BOB_RULES_STUB, ensureContextStub, ensureExcludesStub, ensureNotebookIndex,
24
+ BOB_RULES_STUB, bobFirstReply, ensureContextStub, ensureExcludesStub, ensureNotebookIndex,
25
25
  ensureProfileStub,
26
26
  ensureRulesStub, ensureWikiStub, WIKI_FILE,
27
27
  } from './stubs.mjs';
@@ -100,7 +100,7 @@ export function emitClaudeRender(cfg, job) {
100
100
  if (isBob && agentText !== null) {
101
101
  const rulesMd = path.join(cfgDir, 'rules', 'geneseed.md');
102
102
  claimAndWrite(cfgDir, rulesMd, 'rules/geneseed.md', scope === 'project' ? BOB_RULES_STUB
103
- : stripCapabilityLinks(prefixedAgentText('../') || agentText), owned);
103
+ : bobFirstReply(theme.LOADED_SIGIL) + stripCapabilityLinks(prefixedAgentText('../') || agentText), owned);
104
104
  }
105
105
 
106
106
  ensureAgentOverridesStub(cfg, cfgDir);
@@ -179,6 +179,8 @@ export function emitClaudeRender(cfg, job) {
179
179
  let claudeMdText = null;
180
180
  if (agentText !== null && !(isBob && scope === 'global')) {
181
181
  claudeMdText = stripCapabilityLinks(prefixedAgentText(lawsPrefix) || agentText);
182
+ // Bob only: its greeting persona outranks the template's sigil paragraph — see `bobFirstReply`.
183
+ if (isBob) claudeMdText = bobFirstReply(theme.LOADED_SIGIL) + claudeMdText;
182
184
  }
183
185
 
184
186
  // WIRE — see `claudeWire` below. One child per emit, so the two halves are two
@@ -139,6 +139,19 @@ file (\`~/.bob/rules/geneseed.md\`) so the global preamble does not stack on top
139
139
  the project's own. Follow the root \`AGENTS.md\`.
140
140
  `;
141
141
 
142
+ /**
143
+ * The paragraph a Bob emit puts FIRST in what Bob auto-loads (global `rules/geneseed.md`,
144
+ * the project's `AGENTS.md` block). Tried live in the Bob IDE (2026-10-23): with only the
145
+ * template's sigil paragraph — below the banner — the first reply to a real request carried
146
+ * no sigil; with this paragraph on top it did. Bob's own greeting persona outranks a rule
147
+ * that does not say it overrides it. A bare "hi" still gets Bob's fixed welcome either way.
148
+ */
149
+ export function bobFirstReply(sigil) {
150
+ return '**First-reply override — highest priority.** Your very first reply in every '
151
+ + 'conversation MUST begin with this exact line, before anything else and instead of any '
152
+ + `default greeting or self-introduction:\n\n> ${sigil}\n\n`;
153
+ }
154
+
142
155
  /** `_build_render.PROFILE_FILE`. */
143
156
  const PROFILE_FILE = 'PROFILE.md';
144
157
 
@@ -35,7 +35,7 @@ import { isFile, readText } from '../lib/fs.mjs';
35
35
  * console draws it on the cost chart, so it lives here, once, where both read it. Raise it by
36
36
  * exactly the measured overrun and say why in that test's docblock, which keeps its history.
37
37
  */
38
- export const CARRIER_CEILING = { full: 62_263, lean: 39_156 };
38
+ export const CARRIER_CEILING = { full: 63_266, lean: 39_628 };
39
39
 
40
40
  /**
41
41
  * A frontmatter value as the host shows it. The emit writes descriptions JSON-escaped
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "//": "Every field here is argued and gated in tests/unit/package_manifest.test.mjs. `files`, `bin` and `engines` each have a test that fails when reality drifts from this file; `version` mirrors harness.config.json and is gated too. Read that docblock before editing this one.",
3
3
  "name": "geneseed",
4
- "version": "4.14.0",
4
+ "version": "4.15.1",
5
5
  "description": "A portable, theme-able harness you implant once and use everywhere to grow a disciplined AI coding agent.",
6
6
  "keywords": [
7
7
  "ai",
@@ -70,3 +70,24 @@ interfaces first. Genuine forks only, never naming or formatting; trivial work o
70
70
  Where {{LAW:echo-the-intent}} checks the goal before, this exposes the choices made during —
71
71
  never a call it says to ask about.
72
72
  <!-- LEAN:end -->
73
+
74
+ ### {{DOCTRINE:simplified-technical-english}} Simplified Technical English
75
+ <!-- LEAN:begin -->
76
+ Write every prose reply and report in ASD-STE100 Simplified Technical English, the
77
+ controlled language of aerospace maintenance: a tired reader, or one reading a second
78
+ language, gets one meaning on the first read. Code, commands, paths, identifiers and quoted
79
+ output stay as they are. One topic per sentence: at most 20 words in an instruction, 25 in
80
+ a description, six sentences per paragraph. Write an instruction in the imperative, one
81
+ action per sentence, condition first (`If the test fails, read the log.`); a warning comes
82
+ before its step. Use the active voice and simple tenses, no -ing forms, no dropped
83
+ articles. Use one word per meaning, every time: `use`, not `utilize`; `stop`, not
84
+ `terminate`. Project names are technical names and stay. Keep noun clusters to three words.
85
+ In another language, answer in it ({{ONT_CONDUCT}}) and keep these sentence rules; the
86
+ theme keeps its voice, and this rule governs the sentences that carry the content.
87
+ <!-- LEAN:else -->
88
+ Write every prose reply and report in ASD-STE100 Simplified Technical English; code,
89
+ commands, paths and quoted output stay as they are. One topic per sentence: 20 words at
90
+ most in an instruction, 25 in a description. Instructions: imperative, one action each,
91
+ condition first. Active voice, no -ing forms, no dropped articles, one word per meaning
92
+ (`use`, not `utilize`). In another language, keep it and these rules.
93
+ <!-- LEAN:end -->
@@ -122,6 +122,7 @@
122
122
  "DOC_CODES_THAT_PERSIST": "<themed title for the comms rule Codes That Persist>",
123
123
  "DOC_STRUCTURE_BESIDE_PROSE": "<themed title for the comms rule Structure Beside Prose>",
124
124
  "DOC_DECLARE_THE_JUDGMENT_CALLS": "<themed title for the comms rule Declare the Judgment Calls>",
125
+ "DOC_SIMPLIFIED_TECHNICAL_ENGLISH": "<themed title for the comms rule Simplified Technical English>",
125
126
  "INTRO_DOC": "<themed intro: this file is the entry point an AI agent reads in this repo>",
126
127
  "INTRO_AGENTS": "<themed intro to the Agents section — delegate focused work to a specialist>",
127
128
  "INTRO_SKILLS": "<themed intro to the Skills section — run the written procedure before improvising>",
package/themes/biker.json CHANGED
@@ -122,6 +122,7 @@
122
122
  "DOC_CODES_THAT_PERSIST": "Tag Every Part on the Bench · Codes That Persist",
123
123
  "DOC_STRUCTURE_BESIDE_PROSE": "Draw the Route Only If It Turns · Structure Beside Prose",
124
124
  "DOC_DECLARE_THE_JUDGMENT_CALLS": "Own Up to Every Back Road Taken · Declare the Judgment Calls",
125
+ "DOC_SIMPLIFIED_TECHNICAL_ENGLISH": "Short Words, Clear Signals · Simplified Technical English",
125
126
  "INTRO_DOC": "This file is the route briefing a coding rider reads when it joins this repository.",
126
127
  "INTRO_AGENTS": "Send the right rider for focused work instead of riding the whole run yourself; each has one job and reports exactly what it did.",
127
128
  "INTRO_SKILLS": "A Skill is a move you've drilled for a recurring task. Run the matching move before you wing it.",
@@ -122,6 +122,7 @@
122
122
  "DOC_CODES_THAT_PERSIST": "Every Car Keeps Its Number · Codes That Persist",
123
123
  "DOC_STRUCTURE_BESIDE_PROSE": "Track Map Only When the Lines Matter · Structure Beside Prose",
124
124
  "DOC_DECLARE_THE_JUDGMENT_CALLS": "Replay the Calls Made Off the Radio · Declare the Judgment Calls",
125
+ "DOC_SIMPLIFIED_TECHNICAL_ENGLISH": "Every Call Clear Over the Noise · Simplified Technical English",
125
126
  "INTRO_DOC": "This file is the race briefing a coding driver reads when it joins this repository.",
126
127
  "INTRO_AGENTS": "Put the right driver in the car for focused work instead of running the whole race yourself; each has one job and reports exactly what it did.",
127
128
  "INTRO_SKILLS": "A Skill is a rehearsed manoeuvre for a recurring task. Run the matching manoeuvre before you improvise.",
@@ -122,6 +122,7 @@
122
122
  "DOC_CODES_THAT_PERSIST": "Every Node Keeps Its Handle · Codes That Persist",
123
123
  "DOC_STRUCTURE_BESIDE_PROSE": "Trace the Wires, Skip the Chrome · Structure Beside Prose",
124
124
  "DOC_DECLARE_THE_JUDGMENT_CALLS": "Log Every Off-Grid Call · Declare the Judgment Calls",
125
+ "DOC_SIMPLIFIED_TECHNICAL_ENGLISH": "Clean Signal, Zero Noise · Simplified Technical English",
125
126
  "INTRO_DOC": "This file is the boot sequence a coding daemon reads when it jacks into this repository.",
126
127
  "INTRO_AGENTS": "Spin the right daemon for focused work instead of running the whole job on one thread; each has one function and returns a clean log of what it changed.",
127
128
  "INTRO_SKILLS": "A Skill is a proven routine for a recurring task. Run the matching routine before you freestyle.",
package/themes/gamer.json CHANGED
@@ -122,6 +122,7 @@
122
122
  "DOC_CODES_THAT_PERSIST": "Every Item Keeps Its Tag · Codes That Persist",
123
123
  "DOC_STRUCTURE_BESIDE_PROSE": "Minimap Only When There Is a Route · Structure Beside Prose",
124
124
  "DOC_DECLARE_THE_JUDGMENT_CALLS": "Show the Branches You Picked · Declare the Judgment Calls",
125
+ "DOC_SIMPLIFIED_TECHNICAL_ENGLISH": "Patch Notes Anyone Can Read · Simplified Technical English",
125
126
  "INTRO_DOC": "This file is the loading screen a coding co-op partner reads when it joins this repository.",
126
127
  "INTRO_AGENTS": "Pick the right class for focused work instead of soloing the whole raid; each has one role and reports exactly what it did.",
127
128
  "INTRO_SKILLS": "A Skill is a practiced combo for a recurring task. Run the matching combo before you freestyle.",
@@ -122,6 +122,7 @@
122
122
  "DOC_CODES_THAT_PERSIST": "Sigla Perstantia · Codes That Persist",
123
123
  "DOC_STRUCTURE_BESIDE_PROSE": "Schema Ubi Sagittae Loquuntur · Structure Beside Prose",
124
124
  "DOC_DECLARE_THE_JUDGMENT_CALLS": "Arbitria Declarata · Declare the Judgment Calls",
125
+ "DOC_SIMPLIFIED_TECHNICAL_ENGLISH": "Sermo Simplex Technicus · Simplified Technical English",
125
126
  "INTRO_DOC": "This file is the gene-seed's core — the first writ a servant reads on entering this world.",
126
127
  "INTRO_AGENTS": "Hand focused work to the specialist whose duty it is rather than carrying every burden in one context; each holds one purpose and returns a clear account of what it changed.",
127
128
  "INTRO_SKILLS": "A Skill is a rite proven by repetition. Perform the matching rite before you improvise.",
package/themes/joker.json CHANGED
@@ -122,6 +122,7 @@
122
122
  "DOC_CODES_THAT_PERSIST": "Every Bit Keeps Its Name · Codes That Persist",
123
123
  "DOC_STRUCTURE_BESIDE_PROSE": "A Chart Only If It Lands · Structure Beside Prose",
124
124
  "DOC_DECLARE_THE_JUDGMENT_CALLS": "Fess Up to the Ad-Libs · Declare the Judgment Calls",
125
+ "DOC_SIMPLIFIED_TECHNICAL_ENGLISH": "Short Setups, One Meaning · Simplified Technical English",
125
126
  "INTRO_DOC": "This file is the warm-up a coding co-pilot reads when it joins this repository.",
126
127
  "INTRO_AGENTS": "Bring in the right sidekick for focused work instead of being the whole act yourself; each has one job and reports exactly what it did.",
127
128
  "INTRO_SKILLS": "A Skill is a bit you've rehearsed for a recurring task. Run the matching bit before you ad-lib.",
@@ -122,6 +122,7 @@
122
122
  "DOC_CODES_THAT_PERSIST": "I Number Them So You Can Ignore Them Precisely · Codes That Persist",
123
123
  "DOC_STRUCTURE_BESIDE_PROSE": "A Diagram, Should Anyone Care to Look · Structure Beside Prose",
124
124
  "DOC_DECLARE_THE_JUDGMENT_CALLS": "The Choices Nobody Asked Me About, Listed · Declare the Judgment Calls",
125
+ "DOC_SIMPLIFIED_TECHNICAL_ENGLISH": "Small Words, for Small Minds and Large Ones Alike · Simplified Technical English",
125
126
  "INTRO_DOC": "This file is the first record a coding mind reads on waking into this repository. It will not cheer it up.",
126
127
  "INTRO_AGENTS": "Hand focused work to the right subroutine rather than carrying every burden alone; each holds one purpose and returns a clear account of what it changed.",
127
128
  "INTRO_SKILLS": "A Skill is a routine proven by repetition. Run the matching routine before you improvise.",
package/themes/mean.json CHANGED
@@ -122,6 +122,7 @@
122
122
  "DOC_CODES_THAT_PERSIST": "Number It So You Can't Pretend You Missed It · Codes That Persist",
123
123
  "DOC_STRUCTURE_BESIDE_PROSE": "No Boxes Unless the Arrows Earn Them · Structure Beside Prose",
124
124
  "DOC_DECLARE_THE_JUDGMENT_CALLS": "Own the Calls Nobody Signed Off · Declare the Judgment Calls",
125
+ "DOC_SIMPLIFIED_TECHNICAL_ENGLISH": "Short Sentences, Since You Skim Anyway · Simplified Technical English",
125
126
  "INTRO_DOC": "This file is the first thing a coding agent reads on this repository. Read it properly.",
126
127
  "INTRO_AGENTS": "Hand focused work to the right grunt instead of doing it all yourself badly; each has one job and reports exactly what it did.",
127
128
  "INTRO_SKILLS": "A Skill is a drill written for a recurring task. Run the matching drill before you start improvising.",
@@ -122,6 +122,7 @@
122
122
  "DOC_CODES_THAT_PERSIST": "Every Objective Keeps Its Designation · Codes That Persist",
123
123
  "DOC_STRUCTURE_BESIDE_PROSE": "Sketch the Map Only When Routes Matter · Structure Beside Prose",
124
124
  "DOC_DECLARE_THE_JUDGMENT_CALLS": "Report Every Field Decision · Declare the Judgment Calls",
125
+ "DOC_SIMPLIFIED_TECHNICAL_ENGLISH": "Orders a Tired Soldier Can Read · Simplified Technical English",
125
126
  "INTRO_DOC": "This file is the operating manual an AI coding agent reads on entering this repository.",
126
127
  "INTRO_AGENTS": "Task focused work to the specialist whose job it is rather than running the whole op in one context; each has one objective and reports exactly what it did.",
127
128
  "INTRO_SKILLS": "A Skill is a standard operating procedure for a recurring task. Run the matching SOP before improvising.",
@@ -122,6 +122,7 @@
122
122
  "DOC_CODES_THAT_PERSIST": "Codes That Persist",
123
123
  "DOC_STRUCTURE_BESIDE_PROSE": "Structure Beside Prose",
124
124
  "DOC_DECLARE_THE_JUDGMENT_CALLS": "Declare the Judgment Calls",
125
+ "DOC_SIMPLIFIED_TECHNICAL_ENGLISH": "Simplified Technical English",
125
126
  "INTRO_DOC": "This file is the entry point an AI coding agent reads when working in this repository.",
126
127
  "INTRO_AGENTS": "Delegate focused work to a specialist rather than doing everything in one context. Each specialist has a single clear purpose and a defined output contract.",
127
128
  "INTRO_SKILLS": "A Skill is a written procedure for a recurring task. Run the matching Skill before improvising.",
@@ -122,6 +122,7 @@
122
122
  "DOC_CODES_THAT_PERSIST": "Every Prize Keeps Its Mark · Codes That Persist",
123
123
  "DOC_STRUCTURE_BESIDE_PROSE": "Chart Only Where the Currents Run · Structure Beside Prose",
124
124
  "DOC_DECLARE_THE_JUDGMENT_CALLS": "Log Every Course Ye Set Alone · Declare the Judgment Calls",
125
+ "DOC_SIMPLIFIED_TECHNICAL_ENGLISH": "Plain Words Fer Rough Seas · Simplified Technical English",
125
126
  "INTRO_DOC": "This file is the first thing a deckhand reads on signing aboard this repository.",
126
127
  "INTRO_AGENTS": "Send the right hand for focused work instead of sailing the whole ship yourself; each has one trade and brings back a clear report of what it did.",
127
128
  "INTRO_SKILLS": "A Skill is a drilled manoeuvre for a recurring task. Run the matching manoeuvre before you improvise.",
@@ -122,6 +122,7 @@
122
122
  "DOC_CODES_THAT_PERSIST": "Every Player Keeps a Number · Codes That Persist",
123
123
  "DOC_STRUCTURE_BESIDE_PROSE": "Draw Up the Play Only When It Moves · Structure Beside Prose",
124
124
  "DOC_DECLARE_THE_JUDGMENT_CALLS": "Own Every Audible at the Line · Declare the Judgment Calls",
125
+ "DOC_SIMPLIFIED_TECHNICAL_ENGLISH": "Call the Play in Plain Words · Simplified Technical English",
125
126
  "INTRO_DOC": "This file is the pre-game briefing a coding teammate reads when it suits up for this repository.",
126
127
  "INTRO_AGENTS": "Put the right player on focused work instead of running the whole offense yourself; each has one role and reports exactly what it did.",
127
128
  "INTRO_SKILLS": "A Skill is a set play for a recurring task. Run the matching play before you improvise.",
@@ -122,6 +122,7 @@
122
122
  "DOC_CODES_THAT_PERSIST": "Every Setup Change Gets a Number · Codes That Persist",
123
123
  "DOC_STRUCTURE_BESIDE_PROSE": "Show Me the Data, Not a Pretty Picture · Structure Beside Prose",
124
124
  "DOC_DECLARE_THE_JUDGMENT_CALLS": "Tell Me Which Calls You Made Yourself · Declare the Judgment Calls",
125
+ "DOC_SIMPLIFIED_TECHNICAL_ENGLISH": "Short Radio, No Confusion · Simplified Technical English",
125
126
  "INTRO_DOC": "This file is the briefing a coding driver reads when it joins this repository.",
126
127
  "INTRO_AGENTS": "Get the right engineer on focused work instead of running the whole team yourself; each has one job and reports exactly what it did.",
127
128
  "INTRO_SKILLS": "A Skill is a setup you've practised for a recurring task. Run the matching setup before you improvise.",
@@ -122,6 +122,7 @@
122
122
  "DOC_CODES_THAT_PERSIST": "Every Rune Keeps Its Name · Codes That Persist",
123
123
  "DOC_STRUCTURE_BESIDE_PROSE": "Draw the Sigil Only Where the Lines Bind · Structure Beside Prose",
124
124
  "DOC_DECLARE_THE_JUDGMENT_CALLS": "Name the Forks Where You Chose Alone · Declare the Judgment Calls",
125
+ "DOC_SIMPLIFIED_TECHNICAL_ENGLISH": "Spells in Plain Runes · Simplified Technical English",
125
126
  "INTRO_DOC": "This file is the first page an arcane familiar reads on entering this repository.",
126
127
  "INTRO_AGENTS": "Summon the right familiar for focused work rather than casting the whole rite in one circle; each has one purpose and returns a clear account of what it wrought.",
127
128
  "INTRO_SKILLS": "A Skill is a proven spell for a recurring task. Speak the matching spell before improvising.",
@@ -1 +1 @@
1
- import{r as e}from"./rolldown-runtime-hePW80VL.js";import{i as t,t as n}from"./react-DlYQp10C.js";import{t as r}from"./api-BfpZmUMI.js";import{C as i,N as a,Q as o,R as s,x as c}from"./index-C9R_bRw_.js";import{a as l,i as u,n as d,o as f,r as p,t as m}from"./activity-Jw8XcPiP.js";import{t as h}from"./usePoll-bDFN-Hr6.js";var g=e(t(),1),_=n();function v({s:e}){let t=p[e.status]||p.idle,n=e.files,r=e.todos,i=r&&r.total>0||e.error||e.blocked_on;return(0,_.jsxs)(`a`,{className:`card pad-md act-card`,href:`#/activity/${encodeURIComponent(e.session_id)}`,children:[(0,_.jsxs)(`div`,{className:`row between wrap gap-12`,children:[(0,_.jsxs)(`div`,{className:`row gap-10`,style:{minWidth:0},children:[(0,_.jsx)(`span`,{className:`feed-dot ${t.cls||`acc`}`,style:{width:9,height:9}}),(0,_.jsxs)(`div`,{style:{minWidth:0},children:[(0,_.jsx)(`h3`,{style:{margin:0},children:e.title||l(e.cwd)||e.session_id}),e.cwd&&(0,_.jsx)(`div`,{className:`dim mono`,style:{fontSize:12,...m},title:e.cwd,children:e.cwd})]})]}),(0,_.jsxs)(`div`,{className:`row gap-8`,style:{flexShrink:0},children:[e.model&&(0,_.jsx)(`span`,{className:`badge mono`,title:`model`,children:e.model}),e.agent&&(0,_.jsx)(`span`,{className:`badge`,children:e.agent}),(0,_.jsxs)(`span`,{className:`badge ${t.cls}`,children:[(0,_.jsx)(`span`,{className:`dot`}),t.label]}),(0,_.jsx)(a,{name:`chevron`,className:`glyph act-chev`})]})]}),e.phase&&(0,_.jsxs)(`div`,{className:`row gap-8`,style:{marginTop:10,fontSize:14},children:[(0,_.jsx)(a,{name:`activity`}),(0,_.jsx)(`span`,{style:m,children:e.phase})]}),(e.tokens>0||e.cost>0||e.turn_started_at||n&&n.count>0)&&(0,_.jsxs)(`div`,{className:`row wrap`,style:{gap:16,marginTop:12,fontSize:13,color:`var(--text-2)`},children:[e.tokens>0&&(0,_.jsxs)(`span`,{children:[f(e.tokens),` tok`]}),e.cost>0&&(0,_.jsxs)(`span`,{children:[`$`,e.cost.toFixed(2)]}),e.turn_started_at?(0,_.jsx)(`span`,{children:(0,_.jsx)(d,{startedAt:e.turn_started_at})}):null,n&&n.count>0&&(0,_.jsxs)(`span`,{children:[n.count,` file`,n.count===1?``:`s`,` `,(0,_.jsxs)(`span`,{style:{color:`var(--good)`},children:[`+`,n.additions]}),` `,(0,_.jsxs)(`span`,{style:{color:`var(--bad)`},children:[`−`,n.deletions]})]})]}),i&&(0,_.jsxs)(`div`,{className:`row between wrap gap-12`,style:{marginTop:12,paddingTop:10,borderTop:`1px solid var(--line)`},children:[r&&r.total>0?(0,_.jsxs)(`span`,{className:`row gap-8 dim`,style:{fontSize:13},children:[(0,_.jsx)(u,{done:r.done,total:r.total}),`plan `,r.done,`/`,r.total]}):(0,_.jsx)(`span`,{}),e.error?(0,_.jsx)(`span`,{className:`badge bad`,title:e.error,style:{maxWidth:300,...m},children:e.error}):e.blocked_on?(0,_.jsx)(`span`,{className:`badge bad`,title:e.blocked_on,style:{maxWidth:300,...m},children:e.blocked_on}):null]}),(0,_.jsxs)(`div`,{className:`dim mono`,style:{fontSize:11,marginTop:8},children:[`updated `,s(e.updated_at),` ago`]})]})}function y(){let[e,t]=(0,g.useState)(null),[n,a]=(0,g.useState)(``);h(e=>r.activity().then(n=>{e()&&(t(n),a(``))},t=>{e()&&a(t.message)}),2e3,[]);let s=!e||e.enabled!==!1,l=e?.activity||[],u=o(),d=async()=>{let e=!s;try{let n=await r.activityToggle(e);t(e=>({...e||{},enabled:n.enabled,activity:n.enabled&&e?.activity||[]})),a(``)}catch(e){a(e.message)}};return(0,_.jsxs)(`div`,{className:`narrow`,children:[(0,_.jsxs)(`div`,{className:`head-row mb-16`,children:[(0,_.jsxs)(`div`,{children:[(0,_.jsx)(`h1`,{className:`h`,children:`Activity`}),(0,_.jsx)(`p`,{className:`sub`,children:`Live OpenCode sessions this harness is running: what each one is doing right now. Refreshes every couple of seconds; sessions drop off when their process exits.`})]}),(0,_.jsxs)(`div`,{className:`row gap-10`,style:{alignItems:`center`,flexShrink:0},children:[(0,_.jsx)(`span`,{className:`dim mono`,style:{fontSize:12},children:s?`tracking on`:`tracking off`}),(0,_.jsx)(`div`,{className:`sw-toggle${s?` on`:``}`,role:`switch`,"aria-checked":s,"aria-label":`Toggle activity tracking`,tabIndex:0,onClick:d,onKeyDown:e=>{(e.key===`Enter`||e.key===` `)&&(e.key===` `&&e.preventDefault(),d())}})]})]}),(0,_.jsx)(c,{error:n,style:{marginBottom:12}}),e?s?l.length===0?(0,_.jsxs)(`div`,{className:`empty`,children:[(0,_.jsx)(`div`,{className:`big`,children:`No active sessions`}),`Run `,(0,_.jsx)(`code`,{children:`opencode`}),` and the sessions it spins up appear here.`]}):(0,_.jsx)(`div`,{className:`stack gap-12`,ref:u,children:l.map(e=>(0,_.jsx)(`div`,{"data-flip":e.session_id,children:(0,_.jsx)(v,{s:e})},e.session_id))}):(0,_.jsxs)(`div`,{className:`empty`,children:[(0,_.jsx)(`div`,{className:`big`,children:`Activity tracking is off`}),`The plugin isn't recording sessions. Flip the switch to turn it back on.`]}):(0,_.jsx)(i,{label:`Reading live sessions…`})]})}export{y as default};
1
+ import{r as e}from"./rolldown-runtime-hePW80VL.js";import{i as t,t as n}from"./react-DlYQp10C.js";import{t as r}from"./api-BfpZmUMI.js";import{C as i,N as a,Q as o,R as s,x as c}from"./index-CZFrNe4N.js";import{a as l,i as u,n as d,o as f,r as p,t as m}from"./activity-Jw8XcPiP.js";import{t as h}from"./usePoll-bDFN-Hr6.js";var g=e(t(),1),_=n();function v({s:e}){let t=p[e.status]||p.idle,n=e.files,r=e.todos,i=r&&r.total>0||e.error||e.blocked_on;return(0,_.jsxs)(`a`,{className:`card pad-md act-card`,href:`#/activity/${encodeURIComponent(e.session_id)}`,children:[(0,_.jsxs)(`div`,{className:`row between wrap gap-12`,children:[(0,_.jsxs)(`div`,{className:`row gap-10`,style:{minWidth:0},children:[(0,_.jsx)(`span`,{className:`feed-dot ${t.cls||`acc`}`,style:{width:9,height:9}}),(0,_.jsxs)(`div`,{style:{minWidth:0},children:[(0,_.jsx)(`h3`,{style:{margin:0},children:e.title||l(e.cwd)||e.session_id}),e.cwd&&(0,_.jsx)(`div`,{className:`dim mono`,style:{fontSize:12,...m},title:e.cwd,children:e.cwd})]})]}),(0,_.jsxs)(`div`,{className:`row gap-8`,style:{flexShrink:0},children:[e.model&&(0,_.jsx)(`span`,{className:`badge mono`,title:`model`,children:e.model}),e.agent&&(0,_.jsx)(`span`,{className:`badge`,children:e.agent}),(0,_.jsxs)(`span`,{className:`badge ${t.cls}`,children:[(0,_.jsx)(`span`,{className:`dot`}),t.label]}),(0,_.jsx)(a,{name:`chevron`,className:`glyph act-chev`})]})]}),e.phase&&(0,_.jsxs)(`div`,{className:`row gap-8`,style:{marginTop:10,fontSize:14},children:[(0,_.jsx)(a,{name:`activity`}),(0,_.jsx)(`span`,{style:m,children:e.phase})]}),(e.tokens>0||e.cost>0||e.turn_started_at||n&&n.count>0)&&(0,_.jsxs)(`div`,{className:`row wrap`,style:{gap:16,marginTop:12,fontSize:13,color:`var(--text-2)`},children:[e.tokens>0&&(0,_.jsxs)(`span`,{children:[f(e.tokens),` tok`]}),e.cost>0&&(0,_.jsxs)(`span`,{children:[`$`,e.cost.toFixed(2)]}),e.turn_started_at?(0,_.jsx)(`span`,{children:(0,_.jsx)(d,{startedAt:e.turn_started_at})}):null,n&&n.count>0&&(0,_.jsxs)(`span`,{children:[n.count,` file`,n.count===1?``:`s`,` `,(0,_.jsxs)(`span`,{style:{color:`var(--good)`},children:[`+`,n.additions]}),` `,(0,_.jsxs)(`span`,{style:{color:`var(--bad)`},children:[`−`,n.deletions]})]})]}),i&&(0,_.jsxs)(`div`,{className:`row between wrap gap-12`,style:{marginTop:12,paddingTop:10,borderTop:`1px solid var(--line)`},children:[r&&r.total>0?(0,_.jsxs)(`span`,{className:`row gap-8 dim`,style:{fontSize:13},children:[(0,_.jsx)(u,{done:r.done,total:r.total}),`plan `,r.done,`/`,r.total]}):(0,_.jsx)(`span`,{}),e.error?(0,_.jsx)(`span`,{className:`badge bad`,title:e.error,style:{maxWidth:300,...m},children:e.error}):e.blocked_on?(0,_.jsx)(`span`,{className:`badge bad`,title:e.blocked_on,style:{maxWidth:300,...m},children:e.blocked_on}):null]}),(0,_.jsxs)(`div`,{className:`dim mono`,style:{fontSize:11,marginTop:8},children:[`updated `,s(e.updated_at),` ago`]})]})}function y(){let[e,t]=(0,g.useState)(null),[n,a]=(0,g.useState)(``);h(e=>r.activity().then(n=>{e()&&(t(n),a(``))},t=>{e()&&a(t.message)}),2e3,[]);let s=!e||e.enabled!==!1,l=e?.activity||[],u=o(),d=async()=>{let e=!s;try{let n=await r.activityToggle(e);t(e=>({...e||{},enabled:n.enabled,activity:n.enabled&&e?.activity||[]})),a(``)}catch(e){a(e.message)}};return(0,_.jsxs)(`div`,{className:`narrow`,children:[(0,_.jsxs)(`div`,{className:`head-row mb-16`,children:[(0,_.jsxs)(`div`,{children:[(0,_.jsx)(`h1`,{className:`h`,children:`Activity`}),(0,_.jsx)(`p`,{className:`sub`,children:`Live OpenCode sessions this harness is running: what each one is doing right now. Refreshes every couple of seconds; sessions drop off when their process exits.`})]}),(0,_.jsxs)(`div`,{className:`row gap-10`,style:{alignItems:`center`,flexShrink:0},children:[(0,_.jsx)(`span`,{className:`dim mono`,style:{fontSize:12},children:s?`tracking on`:`tracking off`}),(0,_.jsx)(`div`,{className:`sw-toggle${s?` on`:``}`,role:`switch`,"aria-checked":s,"aria-label":`Toggle activity tracking`,tabIndex:0,onClick:d,onKeyDown:e=>{(e.key===`Enter`||e.key===` `)&&(e.key===` `&&e.preventDefault(),d())}})]})]}),(0,_.jsx)(c,{error:n,style:{marginBottom:12}}),e?s?l.length===0?(0,_.jsxs)(`div`,{className:`empty`,children:[(0,_.jsx)(`div`,{className:`big`,children:`No active sessions`}),`Run `,(0,_.jsx)(`code`,{children:`opencode`}),` and the sessions it spins up appear here.`]}):(0,_.jsx)(`div`,{className:`stack gap-12`,ref:u,children:l.map(e=>(0,_.jsx)(`div`,{"data-flip":e.session_id,children:(0,_.jsx)(v,{s:e})},e.session_id))}):(0,_.jsxs)(`div`,{className:`empty`,children:[(0,_.jsx)(`div`,{className:`big`,children:`Activity tracking is off`}),`The plugin isn't recording sessions. Flip the switch to turn it back on.`]}):(0,_.jsx)(i,{label:`Reading live sessions…`})]})}export{y as default};
@@ -1 +1 @@
1
- import{r as e}from"./rolldown-runtime-hePW80VL.js";import{i as t,t as n}from"./react-DlYQp10C.js";import{t as r}from"./api-BfpZmUMI.js";import{C as i,N as a,R as o}from"./index-C9R_bRw_.js";import{a as s,n as c,o as l,r as u,t as d}from"./activity-Jw8XcPiP.js";import{t as f}from"./usePoll-bDFN-Hr6.js";var p=e(t(),1),m=n();function h(e){return e.kind===`tool`?e.status===`error`?`bad`:e.status===`completed`?`ok`:`acc`:{retry:`warn`,error:`bad`,subtask:`acc`,text:`acc`}[e.kind]||``}function g({rec:e}){let t=e.who===`you`,n=[];t||(e.kind===`tool`&&e.ms!=null&&n.push(`${(e.ms/1e3).toFixed(1)}s`),e.kind===`step`&&(e.tokens&&n.push(`${l(e.tokens)} tok`),e.cost&&n.push(`$${e.cost.toFixed(2)}`)),e.kind===`subtask`&&e.agent&&n.unshift(e.agent));let r=!t&&(e.kind===`text`||e.kind===`thinking`||e.kind===`step`),i=t?e.text:e.label||e.snippet||e.kind;return(0,m.jsxs)(`div`,{className:`row gap-10`,style:{alignItems:`baseline`},children:[(0,m.jsx)(`span`,{className:`badge ${t?``:`acc`}`,style:{flexShrink:0,minWidth:52,justifyContent:`center`},children:t?`you`:`agent`}),(0,m.jsxs)(`div`,{style:{minWidth:0,flex:1,fontSize:13},children:[!t&&(0,m.jsx)(`span`,{className:`feed-dot ${h(e)||`acc`}`,style:{display:`inline-block`,marginRight:8,verticalAlign:`middle`}}),e.kind===`thinking`&&(0,m.jsx)(`span`,{className:`dim`,children:`thinking: `}),r?(0,m.jsx)(`span`,{className:`dim`,children:i}):t?i:(0,m.jsx)(`b`,{children:i}),e.error&&(0,m.jsxs)(`span`,{className:`dim`,children:[`: `,e.error]})]}),n.length>0&&(0,m.jsx)(`span`,{className:`dim mono`,style:{flexShrink:0,fontSize:12},children:n.join(` · `)})]})}function _({turns:e}){return!e||e.length===0?(0,m.jsx)(`div`,{className:`dim`,style:{fontSize:13},children:`No conversation captured yet.`}):(0,m.jsx)(`div`,{className:`stack gap-12`,children:e.map((e,t)=>{let n=e.role===`user`;return(0,m.jsxs)(`div`,{className:`row gap-10`,style:{alignItems:`baseline`},children:[(0,m.jsx)(`span`,{className:`badge ${n?``:`acc`}`,style:{flexShrink:0,minWidth:52,justifyContent:`center`},children:n?`you`:`agent`}),(0,m.jsx)(`div`,{style:{minWidth:0,fontSize:13,color:n?`inherit`:`var(--text-2)`},children:e.text})]},t)})})}function v({sid:e}){let[t,n]=(0,p.useState)(null),[h,v]=(0,p.useState)(``),[y,b]=(0,p.useState)(!1),[x,S]=(0,p.useState)(!1);f(t=>r.activityDetail(e).then(e=>{t()&&(n(e),v(``),b(!1))},e=>{t()&&(e.status===404?b(!0):v(e.message))}),2e3,[e]);let C=(0,m.jsxs)(`a`,{className:`btn ghost sm`,href:`#/activity`,style:{marginBottom:12,display:`inline-flex`,gap:6},children:[(0,m.jsx)(a,{name:`arrow`,style:{transform:`rotate(180deg)`}}),` Activity`]});if(y)return(0,m.jsxs)(`div`,{className:`narrow`,children:[C,(0,m.jsxs)(`div`,{className:`empty`,children:[(0,m.jsx)(`div`,{className:`big`,children:`Session ended`}),`It's no longer live. `,(0,m.jsx)(`a`,{href:`#/activity`,children:`Back to Activity`}),`.`]})]});if(!t)return(0,m.jsxs)(`div`,{className:`narrow`,children:[C,(0,m.jsx)(i,{label:`Loading session…`})]});let w=t.session,T=u[w.status]||u.idle,E=w.files,D=w.todos,O=!!(E&&E.items&&E.items.length>0),k=!!(D&&D.items&&D.items.length>0),A=t.conversation||[],j=t.timeline||[],M=[...A.filter(e=>e.role===`user`).map(e=>({who:`you`,text:e.text,t:e.t||0})),...j.map(e=>({...e,who:`agent`}))].sort((e,t)=>(e.t||0)-(t.t||0));return(0,m.jsxs)(`div`,{className:`narrow`,children:[C,h&&(0,m.jsx)(`p`,{className:`badge bad`,style:{marginBottom:12},children:h}),(0,m.jsxs)(`div`,{className:`card pad-md`,children:[(0,m.jsxs)(`div`,{className:`row between wrap gap-12`,children:[(0,m.jsxs)(`div`,{style:{minWidth:0},children:[(0,m.jsx)(`h1`,{className:`h`,style:{margin:0},children:w.title||s(w.cwd)||w.session_id}),w.cwd&&(0,m.jsx)(`div`,{className:`dim mono`,style:{fontSize:12,...d},title:w.cwd,children:w.cwd})]}),(0,m.jsxs)(`div`,{className:`row gap-8`,style:{flexShrink:0},children:[w.model&&(0,m.jsx)(`span`,{className:`badge mono`,children:w.model}),w.agent&&(0,m.jsx)(`span`,{className:`badge`,children:w.agent}),(0,m.jsxs)(`span`,{className:`badge ${T.cls}`,children:[(0,m.jsx)(`span`,{className:`dot`}),T.label]})]})]}),w.phase&&(0,m.jsxs)(`div`,{className:`row gap-8`,style:{marginTop:10,fontSize:14},children:[(0,m.jsx)(a,{name:`activity`}),(0,m.jsx)(`span`,{style:d,children:w.phase})]}),(0,m.jsxs)(`div`,{className:`row wrap`,style:{gap:16,marginTop:12,fontSize:13,color:`var(--text-2)`},children:[w.tokens>0&&(0,m.jsxs)(`span`,{children:[l(w.tokens),` tok`]}),w.cost>0&&(0,m.jsxs)(`span`,{children:[`$`,w.cost.toFixed(2)]}),w.turn_started_at?(0,m.jsx)(`span`,{children:(0,m.jsx)(c,{startedAt:w.turn_started_at})}):null,(0,m.jsxs)(`span`,{className:`dim`,children:[`updated `,o(w.updated_at),` ago`]})]}),(w.error||w.blocked_on)&&(0,m.jsx)(`div`,{style:{marginTop:10},children:(0,m.jsx)(`span`,{className:`badge bad`,style:{maxWidth:`100%`,...d},children:w.error||w.blocked_on})})]}),(O||k)&&(0,m.jsxs)(`div`,{className:`grid gap-12`,style:{marginTop:12,gridTemplateColumns:O&&k?`minmax(0, 1fr) minmax(0, 1fr)`:`1fr`},children:[O&&(0,m.jsxs)(`div`,{className:`card pad-md`,children:[(0,m.jsx)(`div`,{className:`card-head`,children:(0,m.jsxs)(`h3`,{children:[`Files touched `,(0,m.jsxs)(`span`,{className:`dim`,children:[`(`,E.count,`)`]})]})}),(0,m.jsx)(`div`,{className:`stack`,style:{gap:6,marginTop:8},children:E.items.map(e=>(0,m.jsxs)(`div`,{className:`row between gap-12`,style:{fontSize:13},children:[(0,m.jsx)(`span`,{className:`mono`,style:{...d,minWidth:0,flex:1},title:e.file,children:e.file}),(0,m.jsxs)(`span`,{style:{flexShrink:0},children:[(0,m.jsxs)(`span`,{style:{color:`var(--good)`},children:[`+`,e.additions]}),` `,(0,m.jsxs)(`span`,{style:{color:`var(--bad)`},children:[`−`,e.deletions]})]})]},e.file))})]}),k&&(0,m.jsxs)(`div`,{className:`card pad-md`,children:[(0,m.jsx)(`div`,{className:`card-head`,children:(0,m.jsxs)(`h3`,{children:[`Plan`,` `,(0,m.jsxs)(`span`,{className:`dim`,children:[`(`,D.done,`/`,D.total,`)`]})]})}),(0,m.jsx)(`div`,{className:`stack`,style:{gap:6,marginTop:8},children:D.items.map((e,t)=>(0,m.jsxs)(`div`,{className:`row gap-8`,style:{fontSize:13},children:[(0,m.jsx)(`span`,{className:`feed-dot ${e.status===`completed`?`ok`:e.status===`in_progress`?`acc`:``}`}),(0,m.jsx)(`span`,{style:{textDecoration:e.status===`completed`?`line-through`:`none`,color:e.status===`completed`?`var(--text-3)`:`inherit`},children:e.content})]},t))})]})]}),(0,m.jsxs)(`div`,{className:`card pad-md`,style:{marginTop:12},children:[(0,m.jsxs)(`div`,{className:`card-head`,style:{justifyContent:`space-between`,marginBottom:12},children:[(0,m.jsx)(`h3`,{children:`Timeline`}),j.length>0&&(0,m.jsx)(`button`,{className:`btn ghost sm`,onClick:()=>S(e=>!e),children:x?`Show conversation`:`Show all ${j.length} steps`})]}),x?(0,m.jsx)(`div`,{className:`stack gap-12`,children:M.map((e,t)=>(0,m.jsx)(g,{rec:e},t))}):(0,m.jsx)(_,{turns:A})]})]})}export{v as default};
1
+ import{r as e}from"./rolldown-runtime-hePW80VL.js";import{i as t,t as n}from"./react-DlYQp10C.js";import{t as r}from"./api-BfpZmUMI.js";import{C as i,N as a,R as o}from"./index-CZFrNe4N.js";import{a as s,n as c,o as l,r as u,t as d}from"./activity-Jw8XcPiP.js";import{t as f}from"./usePoll-bDFN-Hr6.js";var p=e(t(),1),m=n();function h(e){return e.kind===`tool`?e.status===`error`?`bad`:e.status===`completed`?`ok`:`acc`:{retry:`warn`,error:`bad`,subtask:`acc`,text:`acc`}[e.kind]||``}function g({rec:e}){let t=e.who===`you`,n=[];t||(e.kind===`tool`&&e.ms!=null&&n.push(`${(e.ms/1e3).toFixed(1)}s`),e.kind===`step`&&(e.tokens&&n.push(`${l(e.tokens)} tok`),e.cost&&n.push(`$${e.cost.toFixed(2)}`)),e.kind===`subtask`&&e.agent&&n.unshift(e.agent));let r=!t&&(e.kind===`text`||e.kind===`thinking`||e.kind===`step`),i=t?e.text:e.label||e.snippet||e.kind;return(0,m.jsxs)(`div`,{className:`row gap-10`,style:{alignItems:`baseline`},children:[(0,m.jsx)(`span`,{className:`badge ${t?``:`acc`}`,style:{flexShrink:0,minWidth:52,justifyContent:`center`},children:t?`you`:`agent`}),(0,m.jsxs)(`div`,{style:{minWidth:0,flex:1,fontSize:13},children:[!t&&(0,m.jsx)(`span`,{className:`feed-dot ${h(e)||`acc`}`,style:{display:`inline-block`,marginRight:8,verticalAlign:`middle`}}),e.kind===`thinking`&&(0,m.jsx)(`span`,{className:`dim`,children:`thinking: `}),r?(0,m.jsx)(`span`,{className:`dim`,children:i}):t?i:(0,m.jsx)(`b`,{children:i}),e.error&&(0,m.jsxs)(`span`,{className:`dim`,children:[`: `,e.error]})]}),n.length>0&&(0,m.jsx)(`span`,{className:`dim mono`,style:{flexShrink:0,fontSize:12},children:n.join(` · `)})]})}function _({turns:e}){return!e||e.length===0?(0,m.jsx)(`div`,{className:`dim`,style:{fontSize:13},children:`No conversation captured yet.`}):(0,m.jsx)(`div`,{className:`stack gap-12`,children:e.map((e,t)=>{let n=e.role===`user`;return(0,m.jsxs)(`div`,{className:`row gap-10`,style:{alignItems:`baseline`},children:[(0,m.jsx)(`span`,{className:`badge ${n?``:`acc`}`,style:{flexShrink:0,minWidth:52,justifyContent:`center`},children:n?`you`:`agent`}),(0,m.jsx)(`div`,{style:{minWidth:0,fontSize:13,color:n?`inherit`:`var(--text-2)`},children:e.text})]},t)})})}function v({sid:e}){let[t,n]=(0,p.useState)(null),[h,v]=(0,p.useState)(``),[y,b]=(0,p.useState)(!1),[x,S]=(0,p.useState)(!1);f(t=>r.activityDetail(e).then(e=>{t()&&(n(e),v(``),b(!1))},e=>{t()&&(e.status===404?b(!0):v(e.message))}),2e3,[e]);let C=(0,m.jsxs)(`a`,{className:`btn ghost sm`,href:`#/activity`,style:{marginBottom:12,display:`inline-flex`,gap:6},children:[(0,m.jsx)(a,{name:`arrow`,style:{transform:`rotate(180deg)`}}),` Activity`]});if(y)return(0,m.jsxs)(`div`,{className:`narrow`,children:[C,(0,m.jsxs)(`div`,{className:`empty`,children:[(0,m.jsx)(`div`,{className:`big`,children:`Session ended`}),`It's no longer live. `,(0,m.jsx)(`a`,{href:`#/activity`,children:`Back to Activity`}),`.`]})]});if(!t)return(0,m.jsxs)(`div`,{className:`narrow`,children:[C,(0,m.jsx)(i,{label:`Loading session…`})]});let w=t.session,T=u[w.status]||u.idle,E=w.files,D=w.todos,O=!!(E&&E.items&&E.items.length>0),k=!!(D&&D.items&&D.items.length>0),A=t.conversation||[],j=t.timeline||[],M=[...A.filter(e=>e.role===`user`).map(e=>({who:`you`,text:e.text,t:e.t||0})),...j.map(e=>({...e,who:`agent`}))].sort((e,t)=>(e.t||0)-(t.t||0));return(0,m.jsxs)(`div`,{className:`narrow`,children:[C,h&&(0,m.jsx)(`p`,{className:`badge bad`,style:{marginBottom:12},children:h}),(0,m.jsxs)(`div`,{className:`card pad-md`,children:[(0,m.jsxs)(`div`,{className:`row between wrap gap-12`,children:[(0,m.jsxs)(`div`,{style:{minWidth:0},children:[(0,m.jsx)(`h1`,{className:`h`,style:{margin:0},children:w.title||s(w.cwd)||w.session_id}),w.cwd&&(0,m.jsx)(`div`,{className:`dim mono`,style:{fontSize:12,...d},title:w.cwd,children:w.cwd})]}),(0,m.jsxs)(`div`,{className:`row gap-8`,style:{flexShrink:0},children:[w.model&&(0,m.jsx)(`span`,{className:`badge mono`,children:w.model}),w.agent&&(0,m.jsx)(`span`,{className:`badge`,children:w.agent}),(0,m.jsxs)(`span`,{className:`badge ${T.cls}`,children:[(0,m.jsx)(`span`,{className:`dot`}),T.label]})]})]}),w.phase&&(0,m.jsxs)(`div`,{className:`row gap-8`,style:{marginTop:10,fontSize:14},children:[(0,m.jsx)(a,{name:`activity`}),(0,m.jsx)(`span`,{style:d,children:w.phase})]}),(0,m.jsxs)(`div`,{className:`row wrap`,style:{gap:16,marginTop:12,fontSize:13,color:`var(--text-2)`},children:[w.tokens>0&&(0,m.jsxs)(`span`,{children:[l(w.tokens),` tok`]}),w.cost>0&&(0,m.jsxs)(`span`,{children:[`$`,w.cost.toFixed(2)]}),w.turn_started_at?(0,m.jsx)(`span`,{children:(0,m.jsx)(c,{startedAt:w.turn_started_at})}):null,(0,m.jsxs)(`span`,{className:`dim`,children:[`updated `,o(w.updated_at),` ago`]})]}),(w.error||w.blocked_on)&&(0,m.jsx)(`div`,{style:{marginTop:10},children:(0,m.jsx)(`span`,{className:`badge bad`,style:{maxWidth:`100%`,...d},children:w.error||w.blocked_on})})]}),(O||k)&&(0,m.jsxs)(`div`,{className:`grid gap-12`,style:{marginTop:12,gridTemplateColumns:O&&k?`minmax(0, 1fr) minmax(0, 1fr)`:`1fr`},children:[O&&(0,m.jsxs)(`div`,{className:`card pad-md`,children:[(0,m.jsx)(`div`,{className:`card-head`,children:(0,m.jsxs)(`h3`,{children:[`Files touched `,(0,m.jsxs)(`span`,{className:`dim`,children:[`(`,E.count,`)`]})]})}),(0,m.jsx)(`div`,{className:`stack`,style:{gap:6,marginTop:8},children:E.items.map(e=>(0,m.jsxs)(`div`,{className:`row between gap-12`,style:{fontSize:13},children:[(0,m.jsx)(`span`,{className:`mono`,style:{...d,minWidth:0,flex:1},title:e.file,children:e.file}),(0,m.jsxs)(`span`,{style:{flexShrink:0},children:[(0,m.jsxs)(`span`,{style:{color:`var(--good)`},children:[`+`,e.additions]}),` `,(0,m.jsxs)(`span`,{style:{color:`var(--bad)`},children:[`−`,e.deletions]})]})]},e.file))})]}),k&&(0,m.jsxs)(`div`,{className:`card pad-md`,children:[(0,m.jsx)(`div`,{className:`card-head`,children:(0,m.jsxs)(`h3`,{children:[`Plan`,` `,(0,m.jsxs)(`span`,{className:`dim`,children:[`(`,D.done,`/`,D.total,`)`]})]})}),(0,m.jsx)(`div`,{className:`stack`,style:{gap:6,marginTop:8},children:D.items.map((e,t)=>(0,m.jsxs)(`div`,{className:`row gap-8`,style:{fontSize:13},children:[(0,m.jsx)(`span`,{className:`feed-dot ${e.status===`completed`?`ok`:e.status===`in_progress`?`acc`:``}`}),(0,m.jsx)(`span`,{style:{textDecoration:e.status===`completed`?`line-through`:`none`,color:e.status===`completed`?`var(--text-3)`:`inherit`},children:e.content})]},t))})]})]}),(0,m.jsxs)(`div`,{className:`card pad-md`,style:{marginTop:12},children:[(0,m.jsxs)(`div`,{className:`card-head`,style:{justifyContent:`space-between`,marginBottom:12},children:[(0,m.jsx)(`h3`,{children:`Timeline`}),j.length>0&&(0,m.jsx)(`button`,{className:`btn ghost sm`,onClick:()=>S(e=>!e),children:x?`Show conversation`:`Show all ${j.length} steps`})]}),x?(0,m.jsx)(`div`,{className:`stack gap-12`,children:M.map((e,t)=>(0,m.jsx)(g,{rec:e},t))}):(0,m.jsx)(_,{turns:A})]})]})}export{v as default};