@awebai/oats 0.22.3 → 0.22.4
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/capabilities/oats-aweb/oats.json +13 -1
- package/docs/integrations.md +5 -2
- package/docs/operating-team-migration.md +49 -21
- package/docs/release-notes/v0.22.4.md +40 -0
- package/lib/session-input.mjs +3 -1
- package/lib/session-viewer.mjs +4 -2
- package/package-catalog.json +1 -1
- package/package.json +1 -1
- package/packages/record/bin/recall.mjs +8 -4
- package/packages/record/lib/index-db.mjs +19 -14
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"capability": "oats.aweb",
|
|
3
3
|
"command": "aweb",
|
|
4
|
-
"version": "1.10.
|
|
4
|
+
"version": "1.10.1",
|
|
5
5
|
"compatibility": {
|
|
6
6
|
"oats": ">=0.22.3"
|
|
7
7
|
},
|
|
@@ -42,6 +42,18 @@
|
|
|
42
42
|
"why": "an ambient aweb pi extension, if one is installed, must honour AWEB_DELIVERY=session (0.3.10 and later); an older one would open a second event stream beside the wake broker; no extension at all is fine",
|
|
43
43
|
"install": "pi install npm:@awebai/pi@latest",
|
|
44
44
|
"ifInstalled": true
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"runtime": "claude",
|
|
48
|
+
"package": "aweb-channel@awebai-marketplace",
|
|
49
|
+
"marketplace": "awebai/claude-plugins",
|
|
50
|
+
"minVersion": "1.7.9",
|
|
51
|
+
"when": {
|
|
52
|
+
"delivery": "session"
|
|
53
|
+
},
|
|
54
|
+
"why": "an ambient aweb-channel plugin for Claude Code, if one is installed, must honour AWEB_DELIVERY=session (1.7.9 and later); an older one starts its own channel server beside the wake broker and can fetch and acknowledge a message before the broker delivers it; no plugin at all is fine",
|
|
55
|
+
"install": "update the aweb-channel plugin from the awebai marketplace in Claude Code",
|
|
56
|
+
"ifInstalled": true
|
|
45
57
|
}
|
|
46
58
|
],
|
|
47
59
|
"skills": [
|
package/docs/integrations.md
CHANGED
|
@@ -148,8 +148,11 @@ soul through the binding's `settings:` map.
|
|
|
148
148
|
- `delivery: channel | session` (default `channel`). `session` hands
|
|
149
149
|
notification delivery to the host wake broker: `AWEB_DELIVERY=session` in
|
|
150
150
|
the launch environment (declared by the manifest), no Claude channel flag,
|
|
151
|
-
a pi extension that honours the opt-out (`@awebai/pi` 0.3.10 or later
|
|
152
|
-
|
|
151
|
+
a pi extension that honours the opt-out (`@awebai/pi` 0.3.10 or later) and
|
|
152
|
+
a Claude Code channel plugin that does too (`aweb-channel` 1.7.9 or later),
|
|
153
|
+
both enforced as conditional requirements with a version floor when
|
|
154
|
+
installed (an older plugin starts its own channel server beside the broker
|
|
155
|
+
and can fetch a message before the broker delivers it), and a briefing
|
|
153
156
|
and registration of the home with the host wake broker (`aw wake register`).
|
|
154
157
|
Until an aw that ships `aw wake` exists (aweb-abil), session mode REFUSES
|
|
155
158
|
to spawn rather than leave an instance that nothing wakes: it is for broker
|
|
@@ -27,21 +27,25 @@ Every continuing seat must have a supported OATS launch, composition,
|
|
|
27
27
|
status, handover and retirement path. Its outstanding work, knowledge and
|
|
28
28
|
required skills must survive a change of runtime session; identity/address
|
|
29
29
|
continuity follows the explicit policy for that seat.
|
|
30
|
-
Every remembering role must have a tested learning path
|
|
31
|
-
|
|
32
|
-
establishes none of this.
|
|
33
|
-
|
|
34
|
-
The installed baseline is OATS 0.22.
|
|
35
|
-
tmux/Herdr, shared `yolo`, remote
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
30
|
+
Every remembering role must have a tested learning path. Preserve each role's
|
|
31
|
+
explicit policy: Cjr reviewers exclude accumulated memory; Themis uses
|
|
32
|
+
reviewed learning. Config discovery alone establishes none of this.
|
|
33
|
+
|
|
34
|
+
The installed baseline is published OATS 0.22.3 on this Mac and `aweb-agents`,
|
|
35
|
+
including native Pi/Claude/Codex, tmux/Herdr, shared `yolo`, remote Desktop
|
|
36
|
+
roster/actions, retained-authority binding and corrected deferred retirement.
|
|
37
|
+
The downloaded Mac Desktop app passed codesign, packaged renderer and PTY
|
|
38
|
+
launch checks. Official oats.okf 1.5.0 provides record-fed harvesting; each
|
|
39
|
+
deployment must select its authenticated provider. Version 1.5.1, adding
|
|
40
|
+
harvest-runtime selection and detection of unadvanced record plans, remains
|
|
41
|
+
under independent review and is not yet published.
|
|
40
42
|
|
|
41
43
|
No standing seat has transferred yet. Cjr's worker pilot has landed reviewed
|
|
42
44
|
code and knowledge; ordinary retirement passed using the next-patch candidate.
|
|
43
|
-
A real harvester's automatic deferred completion
|
|
44
|
-
|
|
45
|
+
A real harvester's automatic deferred completion and successor knowledge use
|
|
46
|
+
remain explicit acceptance checks. The reviewed aweb broker candidate passed
|
|
47
|
+
real harness delivery; installation of its published release as the normal
|
|
48
|
+
host service remains a prerequisite for standing cutover.
|
|
45
49
|
|
|
46
50
|
## Scope inventory
|
|
47
51
|
|
|
@@ -54,10 +58,10 @@ of continuing seats.
|
|
|
54
58
|
| `~/awebai/oats` | Live Claude coordinator and Codex lead; managed review workers also running | Oats owns coordinator handover; lead owns lead handover; preserve established identities |
|
|
55
59
|
| `~/cjr` | Preparation `5afb3e8b`; developer pilot landed on master `062e2c75`; legacy Merlin and Minerva live | Merlin owns safe handovers; preserve his DID/address; prove automatic harvest completion and successor use |
|
|
56
60
|
| `~/awebai/aweb` | Live Claude coordinator and frontend in legacy homes | Oats owns coordinator handover; lead coordinates frontend with aweb after its current work; preserve identities and cover child repositories |
|
|
57
|
-
| `~/tsm` | Five live seats: Zeus, Prometeo, Argos, Themis on Claude; Hermes on Codex.
|
|
61
|
+
| `~/tsm` | Five live seats: Zeus, Prometeo, Argos, Themis on Claude; Hermes on Codex. First-seat Themis config/soul packet under owner review | Zeus prepared the five-seat handover plan; begin with Themis at a safe boundary, Zeus last; preserve session-local schedules and production authority |
|
|
58
62
|
| `~/prj/beadhub-all` | Live Codex session, despite stale offline roster | Beadhub accepted preparation and is at a safe boundary; retain its global identity, native Codex and separate canonical code roots under `~/awebai/beadhub`; billing remains separately gated |
|
|
59
63
|
| `~/prj/docflow` | Live Claude seat identified itself as local `juan.aweb.ai/alice` on `docflow:juan.aweb.ai` | Owner Juan; finish running mail backfill and register checks before transfer; retain identity, memory and Minerva route; accountant-sync remains deliberately unloaded |
|
|
60
|
-
| `ai.aweb` on `aweb-agents` |
|
|
64
|
+
| `ai.aweb` on `aweb-agents` | Aweb confirms Athena intentionally inactive; remote legacy home retained | Aweb and oats own archival inspection; do not resurrect as a continuing seat |
|
|
61
65
|
| `~/awebai/demo-aweb/bob` | Live Pi demo | Aweb owns safe stop and archival disposition; it is not an operating-team migration |
|
|
62
66
|
| `~/.turn-record` | Live Pi capture service under launchd | Retain as infrastructure; qualify record capture separately from standing seats |
|
|
63
67
|
|
|
@@ -101,7 +105,9 @@ uniquely named so no existing address has to be removed for the experiment.
|
|
|
101
105
|
|
|
102
106
|
Merlin retains his identity; other continuing seats follow their accepted
|
|
103
107
|
policy. Oats implemented explicit source-authority binding in oats.aweb 1.10.0,
|
|
104
|
-
reviewed and
|
|
108
|
+
reviewed and shipped with OATS 0.22.3. Published oats.aweb 1.10.1 additionally
|
|
109
|
+
requires an installed ambient Claude channel to be at least 1.7.9 for session
|
|
110
|
+
delivery, matching the existing Pi extension floor of 0.3.10. A disposable rehearsal verified
|
|
105
111
|
stable identity/address, existing conversations, heartbeat, exclusive holder
|
|
106
112
|
refusal, rollback and authority-preserving retirement. Aweb supplied this
|
|
107
113
|
supported handover:
|
|
@@ -249,9 +255,14 @@ the shared rollout record.
|
|
|
249
255
|
session-local schedules. Production credentials remain solely with the
|
|
250
256
|
authorized production operator. His plan reserves Zeus's final cutover
|
|
251
257
|
for Juan's presence; the other seats can prepare in the meantime.
|
|
252
|
-
- BeadHub
|
|
253
|
-
|
|
254
|
-
Its
|
|
258
|
+
- BeadHub configuration/soul commit `3ee13a8` in `awebai/beadhub-saas`
|
|
259
|
+
(`~/awebai/beadhub/beadhub-saas`) was independently ACKed by lead and
|
|
260
|
+
landed on `main`. Its tracked deployment template is materialized at the canonical
|
|
261
|
+
parent workspace, with trusted official capabilities and a clean actual
|
|
262
|
+
doctor result. The soul retains SaaS Git provenance and uses workspace mode
|
|
263
|
+
as a coordinator across the three canonical repositories. Its legacy holder
|
|
264
|
+
remains live; authenticated harvest-model configuration and the published
|
|
265
|
+
broker service precede activation. Stripe and production gates remain separate.
|
|
255
266
|
- Docflow supplied its own identity and handover through its terminal. Its
|
|
256
267
|
backfill and register verification define the safe boundary. Preserve its
|
|
257
268
|
Claude memory and existing credentials in place; verify filesystem/TCC
|
|
@@ -264,6 +275,23 @@ the shared rollout record.
|
|
|
264
275
|
the cross-team coordinator identity/contact policy explicitly before
|
|
265
276
|
relying on those routes; do not silently replace retained identities.
|
|
266
277
|
- Real remote Claude launch, terminal input and detach survival passed.
|
|
267
|
-
Remote Desktop projection and exact-home lifecycle
|
|
268
|
-
|
|
269
|
-
provider authentication, and no standing
|
|
278
|
+
Remote Desktop projection and exact-home lifecycle shipped in 0.22.3; the
|
|
279
|
+
installed remote CLI serves the real registered roster. Remote harvester
|
|
280
|
+
completion remains blocked by provider authentication, and no standing
|
|
281
|
+
remote seat is declared migrated.
|
|
282
|
+
- The aweb broker candidate `30469e22` ran as a private launchd service against
|
|
283
|
+
published OATS 0.22.3. Real Claude and Codex sessions in tmux and Pi
|
|
284
|
+
in Herdr, with Claude channel 1.7.9 and Pi extension 0.3.10, fetched mail and replied with exact qualification tokens,
|
|
285
|
+
with all GUI viewers closed. Pi also answered a sender-waiting chat. Mail
|
|
286
|
+
sent while the broker was stopped was recovered after restart. After the
|
|
287
|
+
Codex harness stopped, its registration became inactive and a subsequent
|
|
288
|
+
message remained unread. Pause also survived a daemon restart, keeping mail
|
|
289
|
+
unread until resume, after which the agent replied. The candidate does not
|
|
290
|
+
fetch or acknowledge mail.
|
|
291
|
+
Published aw installation and standing-seat acceptance are still separate.
|
|
292
|
+
- Qualification found that tmux output loses tab separators in a minimal
|
|
293
|
+
launchd environment without a UTF-8 locale. Independently reviewed kernel
|
|
294
|
+
fix `a0b0e14` adds tmux UTF-8 mode; until it ships, the host service specifies
|
|
295
|
+
`LC_ALL=en_US.UTF-8`. Existing Claude plugin 1.7.8 also consumed mail before
|
|
296
|
+
the broker wake; updating to 1.7.9 eliminated that competing delivery path.
|
|
297
|
+
The deployed oats.aweb 1.10.1 floor now rejects the incompatible version.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# OATS v0.22.4
|
|
2
|
+
|
|
3
|
+
Two fixes found by running the wake broker and the record-fed harvester as
|
|
4
|
+
real host services, the oats.aweb 1.10.1 floor, and the managed lead soul.
|
|
5
|
+
|
|
6
|
+
## Session commands under a service locale
|
|
7
|
+
|
|
8
|
+
launchd starts services without a UTF-8 locale, and tmux then replaces the
|
|
9
|
+
tab delimiters in its list-panes output with underscores, so every session
|
|
10
|
+
inspection from the wake broker reported an invalid pane. The shared session
|
|
11
|
+
helper now runs tmux in UTF-8 mode (`-u`), which preserves the delimiters
|
|
12
|
+
regardless of the service locale and makes typed input UTF-8-safe.
|
|
13
|
+
|
|
14
|
+
## Record windows without the search index
|
|
15
|
+
|
|
16
|
+
`recall --thread --json` used to open the record's SQLite search index just
|
|
17
|
+
to extract turn text, and a capture indexing a large session cache could hold
|
|
18
|
+
that database locked for longer than the harvester waits. Window reads now
|
|
19
|
+
come from the journals alone; the search index opens only for indexed
|
|
20
|
+
search, show and reindex.
|
|
21
|
+
|
|
22
|
+
## Bundled: oats.aweb 1.10.1
|
|
23
|
+
|
|
24
|
+
Session-mode delivery now requires an installed Claude Code channel plugin
|
|
25
|
+
to be `aweb-channel` 1.7.9 or later (as it already required `@awebai/pi`
|
|
26
|
+
0.3.10): an older plugin starts its own channel server beside the wake
|
|
27
|
+
broker and can fetch a message before the broker delivers it.
|
|
28
|
+
|
|
29
|
+
## Managed operating-lead soul
|
|
30
|
+
|
|
31
|
+
`agents/lead/soul` is tracked: the operating lead's role instructions and
|
|
32
|
+
two knowledge notes (runtime and messaging ownership; where rollout state
|
|
33
|
+
lives), ready for a retained-identity binding at its cutover.
|
|
34
|
+
|
|
35
|
+
## Also
|
|
36
|
+
|
|
37
|
+
- `docs/operating-team-migration.md` records the published 0.22.3 rollout and
|
|
38
|
+
the live wake-broker acceptance on Claude, Codex and Pi.
|
|
39
|
+
- oats.okf 1.5.1 (harvest-runtime, promotion exclusions, replan detector) is
|
|
40
|
+
not in this release; it ships when its independent review completes.
|
package/lib/session-input.mjs
CHANGED
|
@@ -6,7 +6,9 @@ import { inspectHerdr, inputHerdr, herdrCommand } from "./herdr.mjs";
|
|
|
6
6
|
|
|
7
7
|
const shells = new Set(["sh", "bash", "zsh", "fish", "dash", "ksh", "login"]);
|
|
8
8
|
function tmux(target, args, { exec = execFileSync, input } = {}) {
|
|
9
|
-
|
|
9
|
+
// launchd does not supply a UTF-8 locale. In that environment tmux replaces
|
|
10
|
+
// the tab delimiters in list-panes output with underscores unless forced.
|
|
11
|
+
return exec("tmux", ["-u", "-S", target.socket, ...args], {
|
|
10
12
|
encoding: "utf8", input, timeout: 10000, maxBuffer: 1024 * 1024,
|
|
11
13
|
stdio: ["pipe", "pipe", "pipe"],
|
|
12
14
|
});
|
package/lib/session-viewer.mjs
CHANGED
|
@@ -10,7 +10,9 @@ export function prepareSessionViewer(target, { exec = execFileSync } = {}) {
|
|
|
10
10
|
delete env.HERDR_SESSION;
|
|
11
11
|
return { binary: target.binary, args: ["terminal", "attach", target.terminalId], env, cleanup() {} };
|
|
12
12
|
}
|
|
13
|
-
|
|
13
|
+
// UTF-8 mode (-u) like the shared session helper: a service without a
|
|
14
|
+
// UTF-8 locale (launchd) otherwise mangles tmux output.
|
|
15
|
+
const run = (args) => exec("tmux", ["-u", "-S", target.socket, ...args], {
|
|
14
16
|
encoding: "utf8", timeout: 10000, stdio: ["ignore", "pipe", "pipe"],
|
|
15
17
|
}).trim();
|
|
16
18
|
const viewer = `oatsview-${process.pid}-${randomUUID().slice(0, 8)}`;
|
|
@@ -28,7 +30,7 @@ export function prepareSessionViewer(target, { exec = execFileSync } = {}) {
|
|
|
28
30
|
run(["bind-key", "-T", "oatsview-locked", "WheelUpPane", "if-shell", "-F", "#{||:#{pane_in_mode},#{mouse_any_flag}}", "send-keys -M", "copy-mode -e; send-keys -M"]);
|
|
29
31
|
run(["set-option", "-t", viewer, "mouse", "on"]);
|
|
30
32
|
const env = { ...process.env }; delete env.TMUX;
|
|
31
|
-
return { binary: "tmux", args: ["-S", target.socket, "attach-session", "-t", `=${viewer}`], env, cleanup };
|
|
33
|
+
return { binary: "tmux", args: ["-u", "-S", target.socket, "attach-session", "-t", `=${viewer}`], env, cleanup };
|
|
32
34
|
} catch (e) { cleanup(); throw e; }
|
|
33
35
|
}
|
|
34
36
|
|
package/package-catalog.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@awebai/oats",
|
|
3
|
-
"version": "0.22.
|
|
3
|
+
"version": "0.22.4",
|
|
4
4
|
"description": "OATS (Open Agent Team Specification) — durable souls, disposable instances, targetable capability packages, and the runtime-neutral oats CLI/kernel.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agents",
|
|
@@ -23,7 +23,7 @@ import { join } from "node:path";
|
|
|
23
23
|
import process from "node:process";
|
|
24
24
|
|
|
25
25
|
import { RecordStore } from "../lib/store.mjs";
|
|
26
|
-
import { RecordIndex } from "../lib/index-db.mjs";
|
|
26
|
+
import { extractTurnText, RecordIndex } from "../lib/index-db.mjs";
|
|
27
27
|
|
|
28
28
|
function parseArgs(argv) {
|
|
29
29
|
const args = { _: [] };
|
|
@@ -40,16 +40,19 @@ function parseArgs(argv) {
|
|
|
40
40
|
const args = parseArgs(process.argv.slice(2));
|
|
41
41
|
const root = args.root ?? process.env.TURN_RECORD_ROOT ?? join(homedir(), ".turn-record");
|
|
42
42
|
const store = new RecordStore(root, {});
|
|
43
|
-
|
|
43
|
+
let index;
|
|
44
|
+
const getIndex = () => (index ??= new RecordIndex(store));
|
|
44
45
|
|
|
45
46
|
try {
|
|
46
47
|
if (args.reindex) {
|
|
48
|
+
const index = getIndex();
|
|
47
49
|
index.rebuild();
|
|
48
50
|
console.log(JSON.stringify(index.counts()));
|
|
49
51
|
process.exit(0);
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
if (args.show) {
|
|
55
|
+
const index = getIndex();
|
|
53
56
|
const { resolveTurn } = await import("../lib/index-db.mjs");
|
|
54
57
|
const byId = store.readAll();
|
|
55
58
|
const turn = resolveTurn(store, index, args.show, byId);
|
|
@@ -111,7 +114,7 @@ try {
|
|
|
111
114
|
// A window is a bounded read: a consumer that plans one (the OKF
|
|
112
115
|
// harvester) sizes it with --ids-only first, then reads exactly that.
|
|
113
116
|
const out = window.map((t) => {
|
|
114
|
-
const docs =
|
|
117
|
+
const docs = extractTurnText(t);
|
|
115
118
|
const base = { id: t.id, ts: t.ts, thread: t.thread, kind: t.kind, source: t.provenance?.source ?? null };
|
|
116
119
|
const full = { ...base, text: docs.map((d) => ({ role: d.role, text: d.text })) };
|
|
117
120
|
// bytes = what this turn occupies in the pretty-printed --json answer,
|
|
@@ -123,6 +126,7 @@ try {
|
|
|
123
126
|
process.exit(0);
|
|
124
127
|
}
|
|
125
128
|
|
|
129
|
+
const index = getIndex();
|
|
126
130
|
const limit = args.limit ? Number(args.limit) : 20;
|
|
127
131
|
let rows;
|
|
128
132
|
if (query) {
|
|
@@ -164,5 +168,5 @@ try {
|
|
|
164
168
|
console.log(` ${r.id}`);
|
|
165
169
|
}
|
|
166
170
|
} finally {
|
|
167
|
-
index
|
|
171
|
+
index?.close();
|
|
168
172
|
}
|
|
@@ -413,20 +413,7 @@ export class RecordIndex {
|
|
|
413
413
|
// Text documents for one turn. Mail/chat: one doc. Session: one doc per
|
|
414
414
|
// conversational event extracted from the transcript blob.
|
|
415
415
|
extractText(turn) {
|
|
416
|
-
|
|
417
|
-
if (turn.kind === "mail" || turn.kind === "chat" || turn.kind === "note") {
|
|
418
|
-
const subject = turn.body?.subject ?? "";
|
|
419
|
-
const text = turn.body?.text ?? "";
|
|
420
|
-
const joined = subject ? subject + "\n" + text : text;
|
|
421
|
-
return joined.trim() ? [{ loc: "", role: turn.kind, text: joined }] : [];
|
|
422
|
-
}
|
|
423
|
-
if (turn.kind === "session" && typeof turn.body?.line === "string") {
|
|
424
|
-
const loc = `line:${turn.provenance?.origin?.line ?? 0}`;
|
|
425
|
-
return extractSessionTextFor(turn.provenance?.source, Buffer.from(turn.body.line, "utf8")).map(
|
|
426
|
-
(d) => ({ ...d, loc }),
|
|
427
|
-
);
|
|
428
|
-
}
|
|
429
|
-
return [];
|
|
416
|
+
return extractTurnText(turn);
|
|
430
417
|
}
|
|
431
418
|
|
|
432
419
|
// FTS query with optional filters. Returns rows with turn metadata and a
|
|
@@ -502,6 +489,24 @@ export class RecordIndex {
|
|
|
502
489
|
}
|
|
503
490
|
}
|
|
504
491
|
|
|
492
|
+
// Journal text extraction is independent of the derived search database.
|
|
493
|
+
export function extractTurnText(turn) {
|
|
494
|
+
if (turn.kind === "note" && turn.body?.segment) return []; // dedicated role "segment" row
|
|
495
|
+
if (turn.kind === "mail" || turn.kind === "chat" || turn.kind === "note") {
|
|
496
|
+
const subject = turn.body?.subject ?? "";
|
|
497
|
+
const text = turn.body?.text ?? "";
|
|
498
|
+
const joined = subject ? subject + "\n" + text : text;
|
|
499
|
+
return joined.trim() ? [{ loc: "", role: turn.kind, text: joined }] : [];
|
|
500
|
+
}
|
|
501
|
+
if (turn.kind === "session" && typeof turn.body?.line === "string") {
|
|
502
|
+
const loc = `line:${turn.provenance?.origin?.line ?? 0}`;
|
|
503
|
+
return extractSessionTextFor(turn.provenance?.source, Buffer.from(turn.body.line, "utf8")).map(
|
|
504
|
+
(d) => ({ ...d, loc }),
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
return [];
|
|
508
|
+
}
|
|
509
|
+
|
|
505
510
|
// Claude Code text extraction, re-exported for compatibility; the
|
|
506
511
|
// per-format extractors live in formats.mjs.
|
|
507
512
|
export const extractSessionText = extractCcText;
|