agent-coord-mcp 0.26.18 → 0.26.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +82 -0
- package/dist/build.js +113 -0
- package/dist/build.js.map +1 -0
- package/dist/capabilities.js +214 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/prefix.js +64 -0
- package/dist/prefix.js.map +1 -0
- package/dist/roles.js +132 -0
- package/dist/roles.js.map +1 -0
- package/dist/server-identity.js +82 -0
- package/dist/server-identity.js.map +1 -0
- package/dist/server.js +668 -0
- package/dist/server.js.map +1 -0
- package/dist/store.js +553 -0
- package/dist/store.js.map +1 -0
- package/dist/tools/admin.js +323 -0
- package/dist/tools/admin.js.map +1 -0
- package/dist/tools/attention.js +73 -0
- package/dist/tools/attention.js.map +1 -0
- package/dist/tools/away.js +285 -0
- package/dist/tools/away.js.map +1 -0
- package/dist/tools/board-ref.js +208 -0
- package/dist/tools/board-ref.js.map +1 -0
- package/dist/tools/event-kinds.js +39 -0
- package/dist/tools/event-kinds.js.map +1 -0
- package/dist/tools/events.js +234 -0
- package/dist/tools/events.js.map +1 -0
- package/dist/tools/index.js +14 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/logwatch.js +85 -0
- package/dist/tools/logwatch.js.map +1 -0
- package/dist/tools/messaging.js +706 -0
- package/dist/tools/messaging.js.map +1 -0
- package/dist/tools/record-events.js +380 -0
- package/dist/tools/record-events.js.map +1 -0
- package/dist/tools/records.js +1205 -0
- package/dist/tools/records.js.map +1 -0
- package/dist/tools/registry.js +529 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/render.js +2 -0
- package/dist/tools/render.js.map +1 -0
- package/dist/tools/rooms.js +210 -0
- package/dist/tools/rooms.js.map +1 -0
- package/dist/tools/rotate.js +143 -0
- package/dist/tools/rotate.js.map +1 -0
- package/dist/tools/scopes.js +126 -0
- package/dist/tools/scopes.js.map +1 -0
- package/dist/tools/shared.js +104 -0
- package/dist/tools/shared.js.map +1 -0
- package/dist/tools/stall.js +415 -0
- package/dist/tools/stall.js.map +1 -0
- package/dist/tools/transport.js +1918 -0
- package/dist/tools/transport.js.map +1 -0
- package/dist/tools/work.js +465 -0
- package/dist/tools/work.js.map +1 -0
- package/dist/tools/worktrees.js +581 -0
- package/dist/tools/worktrees.js.map +1 -0
- package/dist/transports/config.js +82 -0
- package/dist/transports/config.js.map +1 -0
- package/dist/transports/index.js +113 -0
- package/dist/transports/index.js.map +1 -0
- package/dist/transports/tmux.js +140 -0
- package/dist/transports/tmux.js.map +1 -0
- package/dist/transports/types.js +86 -0
- package/dist/transports/types.js.map +1 -0
- package/dist/typed-records.js +174 -0
- package/dist/typed-records.js.map +1 -0
- package/dist/work.js +2 -0
- package/dist/work.js.map +1 -0
- package/hooks/tmux-pusher.mjs +33 -3
- package/package.json +1 -1
- package/src/capabilities.ts +104 -1
- package/src/server.ts +30 -1
- package/src/tools/records.ts +221 -34
- package/src/tools/registry.ts +36 -5
- package/src/tools/shared.ts +12 -36
- package/src/tools/stall.ts +2 -1
- package/src/tools/transport.ts +96 -43
- package/src/tools/work.ts +95 -3
- package/src/tools/worktrees.ts +234 -32
- package/src/transports/config.ts +110 -0
- package/src/transports/index.ts +126 -0
- package/src/transports/tmux.ts +177 -0
- package/src/transports/types.ts +201 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WHICH TRANSPORT THIS FLEET IS CONFIGURED TO USE (Phase 5.4 Task 3.1–3.2).
|
|
3
|
+
*
|
|
4
|
+
* Whole-fleet, read ONCE at startup, per David 2026-09-11 — no per-seat
|
|
5
|
+
* branching in `send_command` or `attach`. Memoised for that reason and not for
|
|
6
|
+
* speed: a value that can be re-read mid-process is a value that can change
|
|
7
|
+
* mid-process, and then two calls in one session disagree about what the fleet
|
|
8
|
+
* is doing.
|
|
9
|
+
*
|
|
10
|
+
* ⛔ AN UNKNOWN VALUE REFUSES AT STARTUP. It does not fall back to `tmux-push`.
|
|
11
|
+
*
|
|
12
|
+
* The reason is not tidiness. A SILENT FALLBACK AND A CORRECT DEFAULT PRODUCE
|
|
13
|
+
* IDENTICAL EVIDENCE: both give you a fleet on tmux with nothing in any log, so
|
|
14
|
+
* a typo in the config reads exactly like a deliberate default, and the person
|
|
15
|
+
* who typed `heardr` spends the afternoon asking why their transport change did
|
|
16
|
+
* nothing. Refusing is louder than the bug it prevents.
|
|
17
|
+
*/
|
|
18
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
19
|
+
import path from "node:path";
|
|
20
|
+
import { ROOT } from "../store.js";
|
|
21
|
+
import { TMUX_PUSH, TRANSPORT_KINDS } from "./types.js";
|
|
22
|
+
/** `$AGENT_COORD_DIR/config.json`, the fleet-wide file. */
|
|
23
|
+
export const TRANSPORT_CONFIG_FILE = path.join(ROOT, "config.json");
|
|
24
|
+
export const TRANSPORT_ENV_VAR = "AGENT_COORD_TRANSPORT";
|
|
25
|
+
function refuse(value, origin) {
|
|
26
|
+
throw new Error(`[agent-coord-mcp] unknown transport ${JSON.stringify(value)} from ${origin}. ` +
|
|
27
|
+
`Valid: ${TRANSPORT_KINDS.join(", ")}. ` +
|
|
28
|
+
`REFUSING AT STARTUP rather than falling back to "${TMUX_PUSH}" — a silent fallback and a correct ` +
|
|
29
|
+
`default leave identical evidence, so a typo here would look exactly like a working default and the ` +
|
|
30
|
+
`transport change would appear to do nothing. Fix the value or remove it to get the default.`);
|
|
31
|
+
}
|
|
32
|
+
function asKind(value, origin) {
|
|
33
|
+
if (typeof value !== "string" || value.length === 0)
|
|
34
|
+
refuse(String(value), origin);
|
|
35
|
+
const match = TRANSPORT_KINDS.find((k) => k === value);
|
|
36
|
+
if (!match)
|
|
37
|
+
refuse(value, origin);
|
|
38
|
+
return match;
|
|
39
|
+
}
|
|
40
|
+
let cached;
|
|
41
|
+
/**
|
|
42
|
+
* Resolve the fleet's transport. Throws on an unknown value — call it once at
|
|
43
|
+
* startup so the refusal lands before any agent attaches.
|
|
44
|
+
*/
|
|
45
|
+
export function configuredTransport() {
|
|
46
|
+
if (cached)
|
|
47
|
+
return cached;
|
|
48
|
+
if (existsSync(TRANSPORT_CONFIG_FILE)) {
|
|
49
|
+
let parsed;
|
|
50
|
+
try {
|
|
51
|
+
parsed = JSON.parse(readFileSync(TRANSPORT_CONFIG_FILE, "utf8"));
|
|
52
|
+
}
|
|
53
|
+
catch (e) {
|
|
54
|
+
// A CONFIG FILE THAT CANNOT BE PARSED IS NOT AN ABSENT ONE. Treating it as
|
|
55
|
+
// absent would silently use the default while a file sits there stating
|
|
56
|
+
// otherwise — the same two-states-one-evidence defect as the fallback.
|
|
57
|
+
throw new Error(`[agent-coord-mcp] ${TRANSPORT_CONFIG_FILE} is unreadable (${e.message}). ` +
|
|
58
|
+
`REFUSING rather than treating it as absent: a file that exists and cannot be read is not the ` +
|
|
59
|
+
`same as no file, and defaulting here would hide a stated intent behind a working fleet.`);
|
|
60
|
+
}
|
|
61
|
+
const raw = parsed?.transport;
|
|
62
|
+
if (raw !== undefined) {
|
|
63
|
+
cached = { kind: asKind(raw, `${TRANSPORT_CONFIG_FILE} ("transport")`), source: "config", origin: TRANSPORT_CONFIG_FILE };
|
|
64
|
+
return cached;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const env = process.env[TRANSPORT_ENV_VAR];
|
|
68
|
+
if (env !== undefined && env !== "") {
|
|
69
|
+
cached = { kind: asKind(env, `$${TRANSPORT_ENV_VAR}`), source: "env", origin: `$${TRANSPORT_ENV_VAR}` };
|
|
70
|
+
return cached;
|
|
71
|
+
}
|
|
72
|
+
cached = { kind: TMUX_PUSH, source: "default", origin: `built-in default (${TMUX_PUSH})` };
|
|
73
|
+
return cached;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Drop the memo. FOR TESTS ONLY — the whole point of reading once is that
|
|
77
|
+
* production code cannot do this.
|
|
78
|
+
*/
|
|
79
|
+
export function resetConfiguredTransportForTests() {
|
|
80
|
+
cached = undefined;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/transports/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE REGISTRY — the one place a transport kind is turned into an implementation.
|
|
3
|
+
*
|
|
4
|
+
* `resolveTransport` is deliberately total over `TransportKind`: adding a kind to
|
|
5
|
+
* the union without registering it is a compile error here rather than a silent
|
|
6
|
+
* fall-through at a call site. Until Task 3, tmux is the only registered
|
|
7
|
+
* implementation and that is the rollback plan — the seam lands behind no config.
|
|
8
|
+
*/
|
|
9
|
+
import { HERDR, TMUX_PUSH, TMUX_PUSH_REMOTE } from "./types.js";
|
|
10
|
+
import { TmuxTransport } from "./tmux.js";
|
|
11
|
+
import { configuredTransport } from "./config.js";
|
|
12
|
+
export * from "./types.js";
|
|
13
|
+
export * from "./config.js";
|
|
14
|
+
export { TmuxTransport, tmuxAvailable, paneExists, probePane, tmuxVersion } from "./tmux.js";
|
|
15
|
+
let host;
|
|
16
|
+
/** Wire the process-layer implementation in once, at module init. */
|
|
17
|
+
export function registerTmuxHost(h) {
|
|
18
|
+
host = h;
|
|
19
|
+
}
|
|
20
|
+
export function resolveTransport(kind) {
|
|
21
|
+
if (!host) {
|
|
22
|
+
throw new Error("transport host not registered — call registerTmuxHost() before resolveTransport(); " +
|
|
23
|
+
"this is a wiring error, not a runtime condition");
|
|
24
|
+
}
|
|
25
|
+
switch (kind) {
|
|
26
|
+
case TMUX_PUSH:
|
|
27
|
+
case TMUX_PUSH_REMOTE:
|
|
28
|
+
return new TmuxTransport(host, kind);
|
|
29
|
+
case HERDR:
|
|
30
|
+
// Task 4. Named so the union stays total and the gap is a stated absence
|
|
31
|
+
// rather than a default that silently behaves like tmux.
|
|
32
|
+
throw new Error('transport "herdr" is not implemented yet (Phase 5.4 Task 4)');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/* ── the ACTIVE transport, and why `running` is not read from config ────────── */
|
|
36
|
+
/**
|
|
37
|
+
* The instance this process would actually use to deliver.
|
|
38
|
+
*
|
|
39
|
+
* Kept as an OBJECT rather than re-derived from the config value on each call,
|
|
40
|
+
* and that is the whole design. If `running` were computed by reading the config
|
|
41
|
+
* and resolving it, then `configured` and `running` would be two names for one
|
|
42
|
+
* fact and `agrees` could never be false — a check that cannot fail. The active
|
|
43
|
+
* instance is what startup actually installed, so the two can genuinely differ:
|
|
44
|
+
* a startup that refused, a process that never wired one, a test that installed
|
|
45
|
+
* another, a future path that falls back.
|
|
46
|
+
*/
|
|
47
|
+
let active;
|
|
48
|
+
export function setActiveTransport(t) {
|
|
49
|
+
active = t;
|
|
50
|
+
}
|
|
51
|
+
/** FOR TESTS ONLY — production wires the active transport once, at startup. */
|
|
52
|
+
export function clearActiveTransportForTests() {
|
|
53
|
+
active = undefined;
|
|
54
|
+
}
|
|
55
|
+
export function activeTransport() {
|
|
56
|
+
return active;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Wire the transport this fleet is configured for. Called once at startup, and
|
|
60
|
+
* it is where an unknown config value turns into a refusal.
|
|
61
|
+
*/
|
|
62
|
+
export function initTransportFromConfig() {
|
|
63
|
+
const conf = configuredTransport();
|
|
64
|
+
setActiveTransport(resolveTransport(conf.kind));
|
|
65
|
+
return { kind: conf.kind, source: conf.source };
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* WHAT THIS PROCESS IS ACTUALLY RUNNING, answered by CALLING the transport.
|
|
69
|
+
*
|
|
70
|
+
* *A config value is a label someone typed.* This asks the live object instead:
|
|
71
|
+
* it calls `available()` and puts a synthetic marker through `probe()`, and
|
|
72
|
+
* reports what came back as the evidence beside the answer. The probe is chosen
|
|
73
|
+
* to be discriminating rather than decorative — a tmux transport answers a
|
|
74
|
+
* bogus pane id with a reason that names the pane, and an implementation that
|
|
75
|
+
* does not talk to panes cannot produce that.
|
|
76
|
+
*
|
|
77
|
+
* Returns `undefined` for `kind` when nothing is wired, which is NOT the same as
|
|
78
|
+
* "tmux by default": a process with no transport delivers nothing, and reporting
|
|
79
|
+
* a default here would be the exact substitution this function exists to refuse.
|
|
80
|
+
*/
|
|
81
|
+
export async function runningTransport() {
|
|
82
|
+
const t = active;
|
|
83
|
+
if (!t) {
|
|
84
|
+
return {
|
|
85
|
+
kind: undefined,
|
|
86
|
+
evidence: "no transport is wired in this process — nothing was called, and no default is assumed",
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
const availability = (() => {
|
|
90
|
+
try {
|
|
91
|
+
return `available()=${t.available()}`;
|
|
92
|
+
}
|
|
93
|
+
catch (e) {
|
|
94
|
+
return `available() threw: ${e.message}`;
|
|
95
|
+
}
|
|
96
|
+
})();
|
|
97
|
+
let probeEvidence;
|
|
98
|
+
try {
|
|
99
|
+
const live = await t.probe({
|
|
100
|
+
agentId: "__capability_probe__",
|
|
101
|
+
transport: t.kind,
|
|
102
|
+
pid: process.pid,
|
|
103
|
+
target: "__no_such_target__",
|
|
104
|
+
since: Date.now(),
|
|
105
|
+
});
|
|
106
|
+
probeEvidence = `probe(bogus target)=${live.state}${live.state === "live" ? "" : `: ${live.reason}`}`;
|
|
107
|
+
}
|
|
108
|
+
catch (e) {
|
|
109
|
+
probeEvidence = `probe threw: ${e.message}`;
|
|
110
|
+
}
|
|
111
|
+
return { kind: t.kind, evidence: `${availability}, ${probeEvidence}` };
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/transports/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE TMUX TRANSPORT — every `tmux` shell-out in this package, in one file.
|
|
3
|
+
*
|
|
4
|
+
* Before this, `spawnSync("tmux", …)` appeared at 7 sites in `tools/transport.ts`
|
|
5
|
+
* and the `has-session` caveat below was written out TWICE, verbatim, at two of
|
|
6
|
+
* them. A rule duplicated in two comments is a rule that will be re-derived
|
|
7
|
+
* wrongly at the third site somebody adds.
|
|
8
|
+
*
|
|
9
|
+
* WHAT THIS FILE DOES NOT DO, on purpose: spawn or reap the pusher process,
|
|
10
|
+
* read receipts, or schedule reminders. Delivery is a second process for THIS
|
|
11
|
+
* transport and will not be for a socket one, so that machinery stays with the
|
|
12
|
+
* tools that own it and reaches this file through `TmuxHost` below. An interface
|
|
13
|
+
* that baked in a pusher could not host an implementation that has none.
|
|
14
|
+
*/
|
|
15
|
+
import { spawnSync } from "node:child_process";
|
|
16
|
+
import { TMUX_PUSH, isLocallyProbeable, isTmuxKind, targetOf } from "./types.js";
|
|
17
|
+
/** `tmux -V` — is tmux on this host at all? */
|
|
18
|
+
export function tmuxAvailable() {
|
|
19
|
+
return spawnSync("tmux", ["-V"]).status === 0;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* DOES THIS TARGET EXIST? The only tmux probe with discriminating power.
|
|
23
|
+
*
|
|
24
|
+
* `has-session` VALIDATES THE TARGET; `display-message -p -t <target> "ok"`
|
|
25
|
+
* DOES NOT — tmux exits 0 for any target, including a pane killed a moment ago,
|
|
26
|
+
* so that probe had ZERO discriminating power and reported every dead pane
|
|
27
|
+
* alive. Pinned to the BEHAVIOUR, not a version: measured identical on tmux
|
|
28
|
+
* 3.6b, 3.7b. A version-pinned claim rots on the next upgrade.
|
|
29
|
+
*
|
|
30
|
+
* Positive control, both directions, re-run on tmux 3.7b while extracting this:
|
|
31
|
+
* bogus target `%99999` -> has-session exit 1, display-message exit 0; live pane
|
|
32
|
+
* `%8` -> both exit 0. The wrong probe is wrong in only one direction, which is
|
|
33
|
+
* why it survived: it never reports a live pane dead.
|
|
34
|
+
*/
|
|
35
|
+
export function paneExists(target) {
|
|
36
|
+
return spawnSync("tmux", ["has-session", "-t", target]).status === 0;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* `has-session` with the failure text, for the one caller that reports it.
|
|
40
|
+
*
|
|
41
|
+
* `paneExists` is the boolean most sites want; attach quotes tmux's own stderr
|
|
42
|
+
* back to the user, and losing that text would make a refusal less useful while
|
|
43
|
+
* still typechecking — the exact class of silent regression this task guards.
|
|
44
|
+
*/
|
|
45
|
+
export function probePane(target) {
|
|
46
|
+
const probe = spawnSync("tmux", ["has-session", "-t", target]);
|
|
47
|
+
return { exists: probe.status === 0, stderr: (probe.stderr ?? "").toString().trim() };
|
|
48
|
+
}
|
|
49
|
+
/** `tmux -V` output, or undefined when tmux is absent. */
|
|
50
|
+
export function tmuxVersion() {
|
|
51
|
+
const probe = spawnSync("tmux", ["-V"]);
|
|
52
|
+
if (probe.status !== 0)
|
|
53
|
+
return undefined;
|
|
54
|
+
return (probe.stdout ?? "").toString().trim() || undefined;
|
|
55
|
+
}
|
|
56
|
+
export class TmuxTransport {
|
|
57
|
+
kind;
|
|
58
|
+
#host;
|
|
59
|
+
constructor(host, kind = TMUX_PUSH) {
|
|
60
|
+
this.#host = host;
|
|
61
|
+
this.kind = kind;
|
|
62
|
+
}
|
|
63
|
+
available() {
|
|
64
|
+
return tmuxAvailable();
|
|
65
|
+
}
|
|
66
|
+
attach(args) {
|
|
67
|
+
return this.#host.attach(args);
|
|
68
|
+
}
|
|
69
|
+
detach(agentId) {
|
|
70
|
+
return this.#host.detach(agentId);
|
|
71
|
+
}
|
|
72
|
+
push(marker, text) {
|
|
73
|
+
return this.#host.push(marker, text);
|
|
74
|
+
}
|
|
75
|
+
sendControl(marker, cmd) {
|
|
76
|
+
return this.#host.sendControl(marker, cmd);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* THREE ANSWERS, AND THE THIRD IS LOAD-BEARING.
|
|
80
|
+
*
|
|
81
|
+
* Each `unknown` below was previously a `continue` with a comment. The states
|
|
82
|
+
* are preserved exactly, because "we could not look" is not evidence of death
|
|
83
|
+
* and a reaper that cannot tell them apart kills live sessions:
|
|
84
|
+
*
|
|
85
|
+
* · not a tmux marker -> unknown (this transport cannot speak for it)
|
|
86
|
+
* · remote (foreign host) -> unknown (no local pane; heartbeat decides)
|
|
87
|
+
* · no target recorded -> unknown (nothing to probe)
|
|
88
|
+
* · tmux missing on host -> unknown (the instrument is absent, not the pane)
|
|
89
|
+
* · target absent -> dead
|
|
90
|
+
* · pusher gone, pane alive -> dead, and it says which half failed
|
|
91
|
+
*/
|
|
92
|
+
async probe(marker) {
|
|
93
|
+
if (!isTmuxKind(marker.transport)) {
|
|
94
|
+
return { state: "unknown", reason: `transport "${marker.transport}" is not tmux` };
|
|
95
|
+
}
|
|
96
|
+
if (!isLocallyProbeable(marker.transport)) {
|
|
97
|
+
return {
|
|
98
|
+
state: "unknown",
|
|
99
|
+
reason: `${marker.transport} runs on another host${marker.host ? ` (${marker.host})` : ""} — no local pane to probe; liveness is heartbeat-based`,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
const target = targetOf(marker);
|
|
103
|
+
if (!target)
|
|
104
|
+
return { state: "unknown", reason: "no target recorded on the marker" };
|
|
105
|
+
if (!tmuxAvailable())
|
|
106
|
+
return { state: "unknown", reason: "tmux is not available on this host" };
|
|
107
|
+
if (!paneExists(target))
|
|
108
|
+
return { state: "dead", reason: `pane ${target} does not exist` };
|
|
109
|
+
if (!this.#host.pusherAlive(marker)) {
|
|
110
|
+
return { state: "dead", reason: `pane ${target} is alive but its pusher (pid ${marker.pid}) is gone` };
|
|
111
|
+
}
|
|
112
|
+
return { state: "live" };
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Reap pushers whose pane has gone, and say which ones could not be judged.
|
|
116
|
+
*
|
|
117
|
+
* `unprobeable` is NOT a residual list nobody reads — it is the distinction
|
|
118
|
+
* PRODUCTION_ROADMAP Phase 5.3 requires between "dead" and "cannot probe".
|
|
119
|
+
* An agent whose liveness is unknown is left alone and reported, never reaped.
|
|
120
|
+
*/
|
|
121
|
+
async reapWedged(markers) {
|
|
122
|
+
const reaped = [];
|
|
123
|
+
const unprobeable = [];
|
|
124
|
+
for (const marker of markers) {
|
|
125
|
+
const live = await this.probe(marker);
|
|
126
|
+
if (live.state === "unknown") {
|
|
127
|
+
unprobeable.push(marker.agentId);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (live.state === "live")
|
|
131
|
+
continue;
|
|
132
|
+
if (this.#host.killPusher(marker))
|
|
133
|
+
reaped.push(marker.agentId);
|
|
134
|
+
else
|
|
135
|
+
unprobeable.push(marker.agentId);
|
|
136
|
+
}
|
|
137
|
+
return { reaped, unprobeable };
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=tmux.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tmux.js","sourceRoot":"","sources":["../../src/transports/tmux.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE TRANSPORT SEAM (Phase 5.4 Task 2).
|
|
3
|
+
*
|
|
4
|
+
* This interface is DERIVED from the 12 `"tmux-push"` literals and the 7
|
|
5
|
+
* `spawnSync("tmux", …)` shell-outs that were spread across `tools/`, not
|
|
6
|
+
* designed top-down. Where the phase doc's sketch and the code disagreed, the
|
|
7
|
+
* code won — three times, each recorded below, because the gate for this task is
|
|
8
|
+
* that a running fleet cannot tell the difference.
|
|
9
|
+
*
|
|
10
|
+
* 1. `tmux-push-remote` IS A LIVE KIND. The doc's sketch had two kinds; the code
|
|
11
|
+
* has three. `registry.ts` gives remote markers heartbeat-based liveness
|
|
12
|
+
* because their pid is 0 on a foreign host, and `server.ts` documents the
|
|
13
|
+
* value as the wire contract for `scripts/coord-pusher.mjs`. A union without
|
|
14
|
+
* it silently reclassifies every remote agent as not-tmux.
|
|
15
|
+
* 2. THE MARKER HAS NINE FIELDS, NOT SIX. `scriptMtime`, `serverBuildMtime` and
|
|
16
|
+
* `rooms` are absent from the sketch and each one closes a defect that
|
|
17
|
+
* actually shipped. They are kept verbatim, including the rule they share:
|
|
18
|
+
* ABSENT MEANS UNKNOWN, NEVER "ON" — the same principle as `Liveness.unknown`
|
|
19
|
+
* below, one field deeper.
|
|
20
|
+
* 3. `target` IS ADDITIVE, NOT A RENAME. See `targetOf`.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Every transport value that can appear in a marker on disk.
|
|
24
|
+
*
|
|
25
|
+
* Not `string`: the whole defect being fixed is code branching on a literal
|
|
26
|
+
* instead of reading the field, and a `string` discriminant cannot tell the
|
|
27
|
+
* compiler that a branch was missed. Not a two-member union either — see (1).
|
|
28
|
+
*/
|
|
29
|
+
export const TMUX_PUSH = "tmux-push";
|
|
30
|
+
export const TMUX_PUSH_REMOTE = "tmux-push-remote";
|
|
31
|
+
export const HERDR = "herdr";
|
|
32
|
+
export const TRANSPORT_KINDS = [TMUX_PUSH, TMUX_PUSH_REMOTE, HERDR];
|
|
33
|
+
/**
|
|
34
|
+
* The tmux family. Both members are delivered by a pusher process typing into a
|
|
35
|
+
* pane; they differ in WHERE that pane is, which is why liveness splits below.
|
|
36
|
+
*
|
|
37
|
+
* THIS IS THE ONE PLACE THE TMUX LITERALS LIVE. Twelve call sites used to spell
|
|
38
|
+
* them; they now ask.
|
|
39
|
+
*/
|
|
40
|
+
const TMUX_KINDS = new Set([TMUX_PUSH, TMUX_PUSH_REMOTE]);
|
|
41
|
+
/** Is this marker carried by the tmux family (local OR remote)? */
|
|
42
|
+
export function isTmuxKind(transport) {
|
|
43
|
+
return transport !== undefined && TMUX_KINDS.has(transport);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Is this marker's pane on THIS host, so that a local probe means anything?
|
|
47
|
+
*
|
|
48
|
+
* The distinction every `marker.transport !== "tmux-push"` site was making by
|
|
49
|
+
* hand, with a comment explaining "remote = can't verify". Naming it stops the
|
|
50
|
+
* next person re-deriving it — and re-deriving it wrongly is how a remote agent
|
|
51
|
+
* gets reported dead because a pane that was never local did not answer.
|
|
52
|
+
*/
|
|
53
|
+
export function isLocallyProbeable(transport) {
|
|
54
|
+
return transport === TMUX_PUSH;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Is liveness for this marker decided by the registry heartbeat rather than by a
|
|
58
|
+
* local pid? True only for the remote kind, whose pid is 0 on a foreign host.
|
|
59
|
+
*/
|
|
60
|
+
export function isRemoteTmuxKind(transport) {
|
|
61
|
+
return transport === TMUX_PUSH_REMOTE;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Read a marker's address, preferring the generic field and falling back to the
|
|
65
|
+
* tmux-specific one.
|
|
66
|
+
*
|
|
67
|
+
* This is the read half of the dual-write. Call it rather than touching either
|
|
68
|
+
* field: a consumer that reads only `tmuxTarget` goes blind the day a herdr
|
|
69
|
+
* marker appears, and one that reads only `target` is blind to every marker on
|
|
70
|
+
* disk today.
|
|
71
|
+
*/
|
|
72
|
+
export function targetOf(marker) {
|
|
73
|
+
return marker.target ?? marker.tmuxTarget;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Stamp an address into BOTH fields, so the marker is legible to the current
|
|
77
|
+
* code and to whatever a revert restores.
|
|
78
|
+
*/
|
|
79
|
+
export function withTarget(marker, target) {
|
|
80
|
+
if (target === undefined)
|
|
81
|
+
return marker;
|
|
82
|
+
return { ...marker, target, tmuxTarget: target };
|
|
83
|
+
}
|
|
84
|
+
/** Keystroke-shaped commands a transport must deliver to an interactive pane. */
|
|
85
|
+
export const CONTROL_COMMANDS = ["clear", "compact", "reload-skills"];
|
|
86
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/transports/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,WAAoB,CAAC;AAC9C,MAAM,CAAC,MAAM,gBAAgB,GAAG,kBAA2B,CAAC;AAC5D,MAAM,CAAC,MAAM,KAAK,GAAG,OAAgB,CAAC;AAItC,MAAM,CAAC,MAAM,eAAe,GAA6B,CAAC,SAAS,EAAE,gBAAgB,EAAE,KAAK,CAAU,CAAC;AAEvG;;;;;;GAMG;AACH,MAAM,UAAU,GAAG,IAAI,GAAG,CAAS,CAAC,SAAS,EAAE,gBAAgB,CAAC,CAAC,CAAC;AAElE,mEAAmE;AACnE,MAAM,UAAU,UAAU,CAAC,SAA6B;IACtD,OAAO,SAAS,KAAK,SAAS,IAAI,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AAC9D,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAC,SAA6B;IAC9D,OAAO,SAAS,KAAK,SAAS,CAAC;AACjC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,SAA6B;IAC5D,OAAO,SAAS,KAAK,gBAAgB,CAAC;AACxC,CAAC;AAoDD;;;;;;;;GAQG;AACH,MAAM,UAAU,QAAQ,CAAC,MAAsD;IAC7E,OAAO,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,UAAU,CAAC;AAC5C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAmB,MAAS,EAAE,MAA0B;IAChF,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC;IACxC,OAAO,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;AACnD,CAAC;AAeD,iFAAiF;AACjF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,OAAO,EAAE,SAAS,EAAE,eAAe,CAAU,CAAC"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
// Typed records become obligatory on agent→agent traffic (Phase 5.1 Task 12).
|
|
2
|
+
//
|
|
3
|
+
// THE MECHANISM ALREADY SHIPPED AND ADOPTION WAS THE WHOLE GAP.
|
|
4
|
+
// `hooks/tier.mjs:injectLine` slims a multi-line message to *first line +
|
|
5
|
+
// `[+N lines · record:<type> · retrieve_message id=<uuid>]`* — but ONLY when
|
|
6
|
+
// `record.type` is a string. Measured on the groundwork room 2026-08-30: 206
|
|
7
|
+
// messages, 175 multi-line, 33 typed, 33 slimmed; the coordinator had sent 101
|
|
8
|
+
// and typed 0. The renderer was never the problem.
|
|
9
|
+
//
|
|
10
|
+
// SO THE RULE IS ENFORCED AT THE SEND, NOT THE RENDER. An untyped agent→agent
|
|
11
|
+
// message must not be able to EXIST, or the next reader re-derives the type
|
|
12
|
+
// from prose and we are back to parsing English. Prose is a second source of
|
|
13
|
+
// truth about what a message meant.
|
|
14
|
+
//
|
|
15
|
+
// Three things this module is careful about, each one a stated acceptance
|
|
16
|
+
// property rather than an implementation taste:
|
|
17
|
+
//
|
|
18
|
+
// 1. STAGED, WITH A NAMED DATE. Five fleets share this bus and 7 of 13 server
|
|
19
|
+
// processes predate 0.26.7. A refusal shipped before a verified restart
|
|
20
|
+
// rejects messages from agents that CANNOT comply — the sender gets an
|
|
21
|
+
// error it has no code path to satisfy. So: WARN until the cutover, REFUSE
|
|
22
|
+
// after it, and the warning names the date in every single message so the
|
|
23
|
+
// deadline arrives having been announced ~every send rather than once.
|
|
24
|
+
//
|
|
25
|
+
// 2. THE REFUSAL NAMES A TYPE THE SENDER MAY ACTUALLY USE. `go` and `scope`
|
|
26
|
+
// are coordinator-restricted and `verdict` is gate-runner-restricted
|
|
27
|
+
// (RECORD_AUTHORITY): the aide hit exactly this, being told to use `scope`
|
|
28
|
+
// by one check and refused it by another. Naming a forbidden type is
|
|
29
|
+
// unusable guidance, so every suggestion is filtered through what this
|
|
30
|
+
// sender's role may emit before it is offered.
|
|
31
|
+
//
|
|
32
|
+
// 3. `fyi` IS AN HONEST CATCH-ALL. If the nine types do not cover a legitimate
|
|
33
|
+
// send, agents mistype into whatever passes and the type becomes noise —
|
|
34
|
+
// which costs more than the untyped message did. Anything unrecognised
|
|
35
|
+
// suggests `fyi` and says so plainly, never a forced `decision`/`risk`.
|
|
36
|
+
// The named cutover. Until this instant an untyped agent→agent send WARNS and
|
|
37
|
+
// is stored; from it, the same send is refused.
|
|
38
|
+
//
|
|
39
|
+
// A DATE, NOT A VERSION, because the constraint being waited on is a fleet-wide
|
|
40
|
+
// RESTART, and no version string on the wire can answer "has every server
|
|
41
|
+
// restarted" — 0.26.7 is published while servers predating it still run. Time
|
|
42
|
+
// is the only clock every one of those processes shares.
|
|
43
|
+
export const TYPED_RECORD_CUTOVER_ISO = "2026-09-15T00:00:00.000Z";
|
|
44
|
+
export const TYPED_RECORD_CUTOVER_MS = Date.parse(TYPED_RECORD_CUTOVER_ISO);
|
|
45
|
+
// `AGENT_COORD_TYPED_RECORDS=warn|refuse` overrides the date. It exists for two
|
|
46
|
+
// callers and no others: tests (which must exercise both sides of a date that
|
|
47
|
+
// has not arrived) and an operator who needs to pull the refusal back for a
|
|
48
|
+
// fleet mid-incident. It is NOT the exemption — an exemption is per-agent and
|
|
49
|
+
// visible (see `proseOnly`); this switch is fleet-wide and invisible, which is
|
|
50
|
+
// exactly why it is not how a less capable model gets on the bus.
|
|
51
|
+
export function typedRecordMode(now = Date.now()) {
|
|
52
|
+
const env = process.env.AGENT_COORD_TYPED_RECORDS;
|
|
53
|
+
if (env === "warn" || env === "refuse")
|
|
54
|
+
return env;
|
|
55
|
+
return now >= TYPED_RECORD_CUTOVER_MS ? "refuse" : "warn";
|
|
56
|
+
}
|
|
57
|
+
// ---------- suggesting the type the sender should have used ----------
|
|
58
|
+
// A rejection that says only "record required" costs a round trip; one that
|
|
59
|
+
// says "looks like a `done` — it cites a PR" costs none. A FAILURE PATH MUST BE
|
|
60
|
+
// MORE INFORMATIVE THAN THE SUCCESS PATH HERE, because it fires during a
|
|
61
|
+
// migration, at agents that are mid-slice and did nothing wrong yesterday.
|
|
62
|
+
//
|
|
63
|
+
// Read from the text the fleet already writes: the six canonical prefixes are
|
|
64
|
+
// in every card and on ~every message, so the type is usually already declared
|
|
65
|
+
// in English one token in.
|
|
66
|
+
const PREFIX_TYPES = [
|
|
67
|
+
[/^\s*DONE\b/i, "done", "the message opens with the DONE: prefix"],
|
|
68
|
+
[/^\s*BLOCKER\b/i, "blocker", "the message opens with the BLOCKER: prefix"],
|
|
69
|
+
[/^\s*RISK\b/i, "risk", "the message opens with the RISK: prefix"],
|
|
70
|
+
[/^\s*FYI\b/i, "fyi", "the message opens with the FYI: prefix"],
|
|
71
|
+
[/^\s*AGENT_ACTION\b/i, "action", "the message opens with the AGENT_ACTION: prefix"],
|
|
72
|
+
[/^\s*DAVID_DECISION\b/i, "decision", "the message opens with the DAVID_DECISION: prefix"],
|
|
73
|
+
[/^\s*GO\b/i, "go", "the message opens with the GO: prefix"],
|
|
74
|
+
[/^\s*SCOPE(?:\s+CHANGE)?\b/i, "scope", "the message opens with the SCOPE: prefix"],
|
|
75
|
+
];
|
|
76
|
+
// Weaker than a prefix and only consulted when there is no prefix at all.
|
|
77
|
+
const SHAPE_TYPES = [
|
|
78
|
+
[/\b(PASS|FAIL)\b.*\b[0-9a-f]{7,40}\b/, "verdict", "it reads as a gate verdict over a commit sha"],
|
|
79
|
+
[/^\s*(PASS|FAIL)\b/, "verdict", "it opens with a PASS/FAIL gate result"],
|
|
80
|
+
[/(^|\s)(#\d+|[\w.-]+\/[\w.-]+#\d+|https:\/\/github\.com\/\S+\/pull\/\d+)/, "done", "it cites a PR"],
|
|
81
|
+
];
|
|
82
|
+
/**
|
|
83
|
+
* The record type this message most likely should have carried, CONSTRAINED to
|
|
84
|
+
* what this sender is allowed to emit.
|
|
85
|
+
*
|
|
86
|
+
* `mayNotEmit` is the restricted set this role is refused (recordAuthorityFor).
|
|
87
|
+
* A suggestion landing in it is downgraded to `fyi` and the downgrade is
|
|
88
|
+
* reported rather than hidden — being quietly steered off `go` reads as the
|
|
89
|
+
* heuristic being bad, when in fact the role simply does not own that type.
|
|
90
|
+
*/
|
|
91
|
+
export function suggestRecordType(text, mayNotEmit = []) {
|
|
92
|
+
const forbidden = new Set(mayNotEmit);
|
|
93
|
+
const firstLine = (text ?? "").split("\n", 1)[0] ?? "";
|
|
94
|
+
const hit = PREFIX_TYPES.find(([re]) => re.test(firstLine)) ??
|
|
95
|
+
SHAPE_TYPES.find(([re]) => re.test(firstLine) || re.test(text ?? ""));
|
|
96
|
+
if (!hit) {
|
|
97
|
+
return {
|
|
98
|
+
type: "fyi",
|
|
99
|
+
why: "nothing in the text names a kind — 'fyi' is the honest catch-all and is never wrong on purpose",
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
const [, type, why] = hit;
|
|
103
|
+
if (forbidden.has(type)) {
|
|
104
|
+
return {
|
|
105
|
+
type: "fyi",
|
|
106
|
+
downgradedFrom: type,
|
|
107
|
+
why: `${why}, but '${type}' is restricted to roles this sender does not hold — 'fyi' carries the same slimming`,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
return { type, why };
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* The one line of guidance appended to both the warning and the refusal. Same
|
|
114
|
+
* words either side of the cutover ON PURPOSE: an agent that reads it during
|
|
115
|
+
* the warn window has already been told the exact call that will keep working.
|
|
116
|
+
*/
|
|
117
|
+
export function typedRecordGuidance(s) {
|
|
118
|
+
const cite = s.type === "done"
|
|
119
|
+
? `, cites: [{kind:'pr', ref:'<owner/repo#N>'}]` // a `done` is refused without one anyway
|
|
120
|
+
: "";
|
|
121
|
+
return (`looks like a '${s.type}' — ${s.why}. ` +
|
|
122
|
+
`Add record: {type:'${s.type}', payload:{summary:'<one line>'}${cite}} to this same call; ` +
|
|
123
|
+
`'text' is untouched by the record and your wording always wins.`);
|
|
124
|
+
}
|
|
125
|
+
// ---------- measuring adoption, and the inverse failure ----------
|
|
126
|
+
// SUCCESS IS THE TRAFFIC TABLE RE-MEASURED, NOT THIS TASK MERGED. The baseline
|
|
127
|
+
// captured the day the rule was made: 212 room messages, 37 typed (17%),
|
|
128
|
+
// 425,903 bytes sitting below line 1 — bytes every reader pays for and no
|
|
129
|
+
// reader asked for.
|
|
130
|
+
//
|
|
131
|
+
// AND THE INVERSE FAILURE LOOKS EXACTLY LIKE SUCCESS ON A COVERAGE NUMBER. If
|
|
132
|
+
// `fyi` becomes nearly everything, coverage reads ~100% while agents are
|
|
133
|
+
// mistyping to get past the gate and the type has stopped carrying
|
|
134
|
+
// information — that is the catch-all rule (12.5) failing, not holding. So the
|
|
135
|
+
// distribution is reported alongside the percentage and is what the verdict
|
|
136
|
+
// keys on. A coverage-only check would certify the failure it exists to catch.
|
|
137
|
+
//
|
|
138
|
+
// The threshold is deliberately generous: `fyi` IS the honest answer for a
|
|
139
|
+
// large share of real traffic, so this fires on domination, not on presence.
|
|
140
|
+
export const FYI_DOMINANCE_THRESHOLD = 0.75;
|
|
141
|
+
/** `messages` is any array of stored messages ({text, record?}). */
|
|
142
|
+
export function typedRecordStats(messages) {
|
|
143
|
+
const distribution = {};
|
|
144
|
+
let typed = 0;
|
|
145
|
+
let unslimmedBytes = 0;
|
|
146
|
+
for (const m of messages) {
|
|
147
|
+
const type = typeof m.record?.type === "string" ? m.record.type : undefined;
|
|
148
|
+
distribution[type ?? "untyped"] = (distribution[type ?? "untyped"] ?? 0) + 1;
|
|
149
|
+
if (type) {
|
|
150
|
+
typed++;
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
const text = m.text ?? "";
|
|
154
|
+
const nl = text.indexOf("\n");
|
|
155
|
+
if (nl !== -1)
|
|
156
|
+
unslimmedBytes += Buffer.byteLength(text.slice(nl + 1), "utf8");
|
|
157
|
+
}
|
|
158
|
+
const total = messages.length;
|
|
159
|
+
const coverage = total ? typed / total : 0;
|
|
160
|
+
const fyiShareOfTyped = typed ? (distribution.fyi ?? 0) / typed : 0;
|
|
161
|
+
// Order matters: degeneracy is checked BEFORE coverage, or a room that is
|
|
162
|
+
// 100% `fyi` reports "healthy" — the exact misreading this guards.
|
|
163
|
+
const degenerate = typed >= 10 && fyiShareOfTyped > FYI_DOMINANCE_THRESHOLD;
|
|
164
|
+
const verdict = degenerate ? "degenerate" : coverage >= 0.95 ? "healthy" : "adopting";
|
|
165
|
+
const note = degenerate
|
|
166
|
+
? `'fyi' is ${(fyiShareOfTyped * 100).toFixed(0)}% of typed messages — coverage looks solved while the type has ` +
|
|
167
|
+
`stopped carrying information. Agents are typing to pass the gate, which is 12.5 FAILING, not holding.`
|
|
168
|
+
: verdict === "healthy"
|
|
169
|
+
? `${(coverage * 100).toFixed(0)}% typed with a spread distribution — the reader-side cost is paid down and the type still discriminates.`
|
|
170
|
+
: `${(coverage * 100).toFixed(0)}% typed; ${unslimmedBytes.toLocaleString("en-US")} bytes still sit below line 1 in untyped messages, ` +
|
|
171
|
+
`carried by every reader.`;
|
|
172
|
+
return { total, typed, coverage: Number(coverage.toFixed(3)), unslimmedBytes, distribution, fyiShareOfTyped: Number(fyiShareOfTyped.toFixed(3)), verdict, note };
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=typed-records.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typed-records.js","sourceRoot":"","sources":["../src/typed-records.ts"],"names":[],"mappings":"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"}
|
package/dist/work.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { BOARD_ARITY, isMalformedRow, EM_DASH_SEP, MIDDOT_SEP, parseQueueLine, parseDoneLine, renderQueueLine, renderDoneLine, renderBoardRow, parseWorkDoc, renderWorkDoc, renderWorkDocForWrite, stampQueueIds, zeroIsUnparsed, substantiveLines, queueItemsOf, doneEntriesOf, boardRowsOf, workstreamsV1RowsOf, listWorkBoardOf, projectV1ToLanes, workDocIssues, workDocLegacyWriteIssues, LANES_V0_WRITE_ISSUE, } from "@davidbalzan/groundwork-seam/docs";
|
|
2
|
+
//# sourceMappingURL=work.js.map
|
package/dist/work.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"work.js","sourceRoot":"","sources":["../src/work.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,WAAW,EAEX,cAAc,EAGd,WAAW,EACX,UAAU,EACV,cAAc,EACd,aAAa,EACb,eAAe,EACf,cAAc,EACd,cAAc,EACd,YAAY,EACZ,aAAa,EACb,qBAAqB,EACrB,aAAa,EACb,cAAc,EACd,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,WAAW,EACX,mBAAmB,EACnB,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,wBAAwB,EACxB,oBAAoB,GAGrB,MAAM,mCAAmC,CAAC"}
|