mainflow 0.43.0 → 0.44.0-alpha.2
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/dist/cli.js +7 -5
- package/dist/{dist-D5rZ0MQH.js → dist-CXrUfRQp.js} +111 -10
- package/dist/facts.d.ts +22 -5
- package/dist/facts.js +28 -7
- package/dist/grant-check.d.ts +45 -0
- package/dist/grant-check.js +572 -0
- package/dist/grant-commands.d.ts +31 -3
- package/dist/grant-commands.js +14 -4
- package/dist/hooks.d.ts +2 -18
- package/dist/hooks.js +103 -15
- package/dist/{host-BJvqZy-i.js → host-C74j2Qox.js} +174 -57
- package/dist/host.d.ts +1 -1
- package/dist/host.js +1 -1
- package/dist/{index-DRpamPge.d.ts → index-jq7lN8V8.d.ts} +9 -3
- package/dist/{index-BqPQp6TF.d.ts → index-l8QUG80k.d.ts} +11 -0
- package/dist/launch.d.ts +1 -1
- package/dist/launch.js +2 -2
- package/dist/native/mainflow-swap-darwin-arm64 +0 -0
- package/dist/native/mainflow-swap-darwin-x64 +0 -0
- package/dist/operation-BroV98VB.d.ts +19 -0
- package/dist/{operation-BhBbGauo.js → operation-CUeOFEaf.js} +504 -151
- package/dist/{ui-xxzmhAqF.js → ui-qqVCewFz.js} +3 -3
- package/package.json +1 -1
package/dist/hooks.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { I as configSchema, Y as parseRef, ft as describeRequest, rt as taskSchema, tt as repoPaths } from "./dist-
|
|
2
|
-
import {
|
|
3
|
-
import { n as hubWriter, r as writtenLines } from "./operation-
|
|
1
|
+
import { I as configSchema, Y as parseRef, ft as describeRequest, rt as taskSchema, tt as repoPaths } from "./dist-CXrUfRQp.js";
|
|
2
|
+
import { $ as isOpenMainline, $t as realPath, Bt as notRegisteredMessage, C as turnReminder, F as headerLine, Gt as resolveHub, I as mayDecorate, Jt as nodeStorage, Lt as hubHoldingFile, M as matchGrants, N as decorateReply, Nt as SESSIONS_DIR, O as loadSessions, Ot as formatMismatch, P as endsWithMarker, Q as HubError, Qt as hubPath, Rt as inRepositoryHubAt, S as sessionBriefing, Xt as dataFile, Yt as currentBranch, dt as taskFile, en as worktreesOf, i as hostName, j as grantScope, k as readSession, kt as hubFormatVersion, mt as readYaml, nn as DATA_DIR, nt as loadMainlines, pt as readFrontMatter, tt as loadHub } from "./host-C74j2Qox.js";
|
|
3
|
+
import { n as hubWriter, r as writtenLines } from "./operation-CUeOFEaf.js";
|
|
4
4
|
import { SCENARIO_LINES, SCENARIO_SKIP_KINDS } from "./facts.js";
|
|
5
5
|
import { resolveFixed, scanCommand, scopedCwd } from "./grant-commands.js";
|
|
6
|
+
import { createHash } from "node:crypto";
|
|
6
7
|
import { appendFileSync, existsSync, readFileSync } from "node:fs";
|
|
7
8
|
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
8
9
|
//#region src/hooks.ts
|
|
@@ -124,11 +125,11 @@ const displayReply = (dirs, { payload }) => {
|
|
|
124
125
|
};
|
|
125
126
|
/** Mainline ids a reply mentions, e.g. m001 in `m001/t003`; not inside a longer word or path. */
|
|
126
127
|
const MAINLINE_MENTION = /(?<![\w/#.[-])(m\d{3})(?![\w\]])/g;
|
|
127
|
-
/** The open mainlines the text mentions;
|
|
128
|
+
/** The open mainlines the text mentions; reads the mainlines' mainline.md only when the text mentions one. */
|
|
128
129
|
function openMainlinesIn(dirs, text) {
|
|
129
130
|
const mentioned = new Set([...text.matchAll(MAINLINE_MENTION)].map((m) => m[1]));
|
|
130
131
|
if (mentioned.size === 0) return [];
|
|
131
|
-
return
|
|
132
|
+
return loadMainlines(dirs).filter((m) => mentioned.has(m.id) && isOpenMainline(m)).map((m) => m.id);
|
|
132
133
|
}
|
|
133
134
|
/** Tools whose `file_path` input writes a file. */
|
|
134
135
|
const WRITE_TOOLS = /* @__PURE__ */ new Set([
|
|
@@ -219,6 +220,10 @@ const CLAUDE_CODE_EVENTS = {
|
|
|
219
220
|
id,
|
|
220
221
|
digest: null
|
|
221
222
|
});
|
|
223
|
+
if (session.checkReported?.length) await write("session.checked", {
|
|
224
|
+
id,
|
|
225
|
+
reported: null
|
|
226
|
+
});
|
|
222
227
|
if (envFile) appendFileSync(envFile, `export MAINFLOW_SESSION=${id}\n`);
|
|
223
228
|
startUi?.(dirs);
|
|
224
229
|
return sessionBriefing(dirs, session, {
|
|
@@ -423,10 +428,50 @@ function decideStartGate(dirs, { payload, cli, env }) {
|
|
|
423
428
|
permissionDecisionReason: `[mainflow] ${reason}`
|
|
424
429
|
} });
|
|
425
430
|
}
|
|
426
|
-
/** How many problems
|
|
427
|
-
const
|
|
431
|
+
/** How many problems the reminder lists when the session is unknown, before it only gives the count. */
|
|
432
|
+
const UNSCOPED_PROBLEMS_SHOWN = 5;
|
|
428
433
|
/** A problem as the reminder lists it, its file as the absolute path the model opens. */
|
|
429
434
|
const problemLine = (dirs, p) => `- ${dataFile(dirs, p.file)}${p.field ? ` › ${p.field}` : ""}:${p.message}`;
|
|
435
|
+
/** What a reported problem is remembered by: the same file, field and message is the same problem. */
|
|
436
|
+
const problemKey = (p) => createHash("sha1").update(`${p.file}\0${p.field}\0${p.message}`).digest("hex").slice(0, 16);
|
|
437
|
+
/** The mainlines the session's unfinished actions serve, as the ids their refs start with. */
|
|
438
|
+
function servedMainlines(session) {
|
|
439
|
+
const ids = /* @__PURE__ */ new Set();
|
|
440
|
+
for (const a of session.actions) {
|
|
441
|
+
if (a.status === "done" || a.status === "dropped") continue;
|
|
442
|
+
for (const ref of a.serves) {
|
|
443
|
+
const id = /^m\d+/.exec(ref)?.[0];
|
|
444
|
+
if (id) ids.add(id);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
return ids;
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Whether a problem's file is the session's to fix: a mainline its unfinished actions serve, the session's own file,
|
|
451
|
+
* or a file of the hub itself (issues, findings, groups, config.yaml and the like), which no mainline owns.
|
|
452
|
+
*/
|
|
453
|
+
function inSessionScope(file, session) {
|
|
454
|
+
const mainlines = `${DATA_DIR}/mainlines/`;
|
|
455
|
+
const sessions = `${DATA_DIR}/${SESSIONS_DIR}/`;
|
|
456
|
+
if (file.startsWith(mainlines)) return [...servedMainlines(session)].some((m) => file.startsWith(`${mainlines}${m}-`));
|
|
457
|
+
if (file.startsWith(sessions)) return file === `${sessions}${session.id}.yaml`;
|
|
458
|
+
return true;
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* What a list of in-scope problems lies in, for the line that heads it: only the kinds it holds, so a problem of the
|
|
462
|
+
* session's own file alone is not called one of a mainline or of the hub's own files.
|
|
463
|
+
*/
|
|
464
|
+
function scopeLabel(problems) {
|
|
465
|
+
const kindOf = (file) => file.startsWith(`.mainflow/mainlines/`) ? 0 : file.startsWith(`.mainflow/local/sessions/`) ? 2 : 1;
|
|
466
|
+
const kinds = new Set(problems.map((p) => kindOf(p.file)));
|
|
467
|
+
const parts = [
|
|
468
|
+
"目前行動服務的主脈絡",
|
|
469
|
+
"hub 層的檔",
|
|
470
|
+
"這場會話的會話檔"
|
|
471
|
+
].filter((_, i) => kinds.has(i));
|
|
472
|
+
const last = parts.at(-1) ?? "";
|
|
473
|
+
return parts.length > 1 ? `${parts.slice(0, -1).join("、")}與${/^[A-Za-z0-9]/.test(last) ? " " : ""}${last}` : last;
|
|
474
|
+
}
|
|
430
475
|
/**
|
|
431
476
|
* After a tool writes into a hub's data directory, run the same checks as `mainflow check` and hand the problems
|
|
432
477
|
* to the model in the same turn. Edit, Write and MultiEdit name the file; Bash only counts when its command mentions
|
|
@@ -434,8 +479,16 @@ const problemLine = (dirs, p) => `- ${dataFile(dirs, p.file)}${p.field ? ` › $
|
|
|
434
479
|
* write elsewhere, adds nothing. The hub a written file belongs to is the registered hub whose data directory
|
|
435
480
|
* holds it, found from the file itself rather than from the session's directory, because a session may write
|
|
436
481
|
* into another registered hub.
|
|
482
|
+
*
|
|
483
|
+
* With the session known, only the problems of the written file, of the mainlines its unfinished actions serve, of the
|
|
484
|
+
* hub's own files (everything outside `mainlines/` and `local/sessions/`) and of the session's own file are listed, the
|
|
485
|
+
* rest counted in one line, and a problem handed over once is not handed over again while it is still there: other
|
|
486
|
+
* sessions' mainlines and session files are theirs to fix, and the model still holds what an earlier write reported.
|
|
487
|
+
* SessionStart forgets what was reported, since a resume, clear or compaction drops it. A subagent's write (the
|
|
488
|
+
* payload has agent_id) is scoped by the main session's actions but neither skips nor records what was reported,
|
|
489
|
+
* because the subagent has not seen what the main session was told.
|
|
437
490
|
*/
|
|
438
|
-
function checkAfterWrite(dirs, { payload, cli, env }) {
|
|
491
|
+
async function checkAfterWrite(dirs, { payload, cli, env, write }) {
|
|
439
492
|
const input = payload.tool_input ?? {};
|
|
440
493
|
let hub = dirs;
|
|
441
494
|
let written = null;
|
|
@@ -463,15 +516,50 @@ function checkAfterWrite(dirs, { payload, cli, env }) {
|
|
|
463
516
|
message: error.message
|
|
464
517
|
}];
|
|
465
518
|
}
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
519
|
+
const sessionId = payload.session_id ? claudeCodeSessionId(payload.session_id) : null;
|
|
520
|
+
let session = null;
|
|
521
|
+
try {
|
|
522
|
+
session = sessionId ? readSession(hub, sessionId) : null;
|
|
523
|
+
} catch {}
|
|
469
524
|
const line = (p) => problemLine(hub, p);
|
|
525
|
+
const own = written ? problems.filter((p) => p.file === written) : [];
|
|
526
|
+
if (!session) {
|
|
527
|
+
if (problems.length === 0) return null;
|
|
528
|
+
const others = problems.filter((p) => !own.includes(p));
|
|
529
|
+
const lines = [
|
|
530
|
+
written && own.length ? `[mainflow] 剛寫入的 ${dataFile(hub, written)} 有 ${own.length} 個問題,改好再繼續:` : `[mainflow] 寫入 hub 的資料目錄 ${hub.data} 後,check 找到 ${problems.length} 個問題:`,
|
|
531
|
+
...own.map(line),
|
|
532
|
+
...others.length ? [...own.length ? [`其他檔案還有 ${others.length} 個問題:`] : [], ...others.slice(0, UNSCOPED_PROBLEMS_SHOWN).map(line)] : [],
|
|
533
|
+
...others.length > UNSCOPED_PROBLEMS_SHOWN ? [`還有 ${others.length - UNSCOPED_PROBLEMS_SHOWN} 個沒列出,跑 ${cli} check 看全部。`] : []
|
|
534
|
+
];
|
|
535
|
+
return JSON.stringify({ hookSpecificOutput: {
|
|
536
|
+
hookEventName: "PostToolUse",
|
|
537
|
+
additionalContext: lines.join("\n")
|
|
538
|
+
} });
|
|
539
|
+
}
|
|
540
|
+
const ofScope = problems.filter((p) => !own.includes(p) && inSessionScope(p.file, session));
|
|
541
|
+
const relevant = [...own, ...ofScope];
|
|
542
|
+
const subagent = Boolean(payload.agent_id);
|
|
543
|
+
const reported = new Set(subagent ? [] : session.checkReported ?? []);
|
|
544
|
+
const fresh = (list) => list.filter((p) => !reported.has(problemKey(p)));
|
|
545
|
+
const newOwn = fresh(own);
|
|
546
|
+
const newScope = fresh(ofScope);
|
|
547
|
+
if (!subagent) {
|
|
548
|
+
const present = new Set(problems.map(problemKey));
|
|
549
|
+
const keys = [.../* @__PURE__ */ new Set([...[...reported].filter((k) => present.has(k)), ...[...newOwn, ...newScope].map(problemKey)])].sort();
|
|
550
|
+
if (keys.length !== reported.size || keys.some((k) => !reported.has(k))) await write("session.checked", {
|
|
551
|
+
id: session.id,
|
|
552
|
+
reported: keys.length ? keys : null
|
|
553
|
+
}).catch(() => void 0);
|
|
554
|
+
}
|
|
555
|
+
if (newOwn.length + newScope.length === 0) return null;
|
|
556
|
+
const elsewhere = problems.length - relevant.length;
|
|
470
557
|
const lines = [
|
|
471
|
-
|
|
472
|
-
...
|
|
473
|
-
...
|
|
474
|
-
...
|
|
558
|
+
newOwn.length ? `[mainflow] 剛寫入的 ${dataFile(hub, written)} 有 ${newOwn.length} 個問題,改好再繼續:` : `[mainflow] 寫入 hub 的資料目錄 ${hub.data} 後,${scopeLabel(newScope)}有 ${newScope.length} 個問題:`,
|
|
559
|
+
...newOwn.map(line),
|
|
560
|
+
...newOwn.length && newScope.length ? [`${scopeLabel(newScope)}還有 ${newScope.length} 個問題:`] : [],
|
|
561
|
+
...newScope.map(line),
|
|
562
|
+
...elsewhere > 0 ? [`其他還有 ${elsewhere} 條,屬於別的主脈絡或別的會話的會話檔,跑 ${cli} check 看全部。`] : []
|
|
475
563
|
];
|
|
476
564
|
return JSON.stringify({ hookSpecificOutput: {
|
|
477
565
|
hookEventName: "PostToolUse",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as removedUntil, A as SURVEY_LINES, B as groupSchema, C as PRIORITIES, D as SESSION_ID, E as SERVES_REF, F as WORK_STATUS, G as mainlineSchema, H as isMainflowRange, I as configSchema, J as parseOpsLine, K as opsEntrySchema, L as decisionSchema, M as TASK_REQUIRED_SECTIONS, N as TASK_SECTIONS, O as SESSION_STATUS, P as UNTIL_FORMS, Q as refMainline, R as findingSchema, S as MERGE_STATES, T as RESERVED_HUB_NAMES, U as issueSchema, V as hubRegistrySchema, W as listedRepos, X as parseUntil, Y as parseRef, Z as refExample, a as DECISION_SECTIONS, at as waitingForAction, b as MAINLINE_SECTIONS, c as FINDING_SECTIONS, ct as stampMs, d as GROUP_SECTIONS, et as repoPath, f as GROUP_STATUS, ft as describeRequest, g as ISSUE_SECTIONS,
|
|
1
|
+
import { $ as removedUntil, A as SURVEY_LINES, B as groupSchema, C as PRIORITIES, D as SESSION_ID, E as SERVES_REF, F as WORK_STATUS, G as mainlineSchema, H as isMainflowRange, I as configSchema, J as parseOpsLine, K as opsEntrySchema, L as decisionSchema, M as TASK_REQUIRED_SECTIONS, N as TASK_SECTIONS, O as SESSION_STATUS, P as UNTIL_FORMS, Q as refMainline, R as findingSchema, S as MERGE_STATES, T as RESERVED_HUB_NAMES, U as issueSchema, V as hubRegistrySchema, W as listedRepos, X as parseUntil, Y as parseRef, Z as refExample, _t as parseGrantTarget, a as DECISION_SECTIONS, at as waitingForAction, b as MAINLINE_SECTIONS, c as FINDING_SECTIONS, ct as stampMs, d as GROUP_SECTIONS, et as repoPath, f as GROUP_STATUS, ft as describeRequest, g as ISSUE_SECTIONS, ht as grantRepo, i as DECISION_BASES, j as SURVEY_SECTION, k as SLUG, l as FINDING_STATUS, m as HUB_NAME, mt as grantForm, n as CLOSED_STATUS, nt as sessionSchema, ot as waitingForRef, pt as grantCovers, q as parseActionRef, r as DECIDERS, rt as taskSchema, s as DEFAULT_CATEGORIES, st as OFFSET, vt as pathInPatterns, w as REF_EXAMPLES, x as MAINLINE_STATUS, y as MAINFLOW_RANGE, z as formatOpsEntry } from "./dist-CXrUfRQp.js";
|
|
2
2
|
import { a as LABEL_ROLES, i as FINDING_SEVERITIES, n as FINDING_CLASSES, r as FINDING_FIELDS, t as FINDINGS_FENCE } from "./retro-formats-ECy0gGOL.js";
|
|
3
3
|
import { createHash } from "node:crypto";
|
|
4
4
|
import { appendFileSync, closeSync, cpSync, existsSync, lstatSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, readlinkSync, realpathSync, renameSync, rmSync, rmdirSync, statSync, symlinkSync, writeFileSync, writeSync } from "node:fs";
|
|
@@ -1566,10 +1566,15 @@ function mainlineOf(ref) {
|
|
|
1566
1566
|
const m = parsed ? refMainline(parsed) : void 0;
|
|
1567
1567
|
return m && (parsed.hub ? `${parsed.hub}:${m}` : m);
|
|
1568
1568
|
}
|
|
1569
|
-
/**
|
|
1569
|
+
/**
|
|
1570
|
+
* The order mainlines are pushed forward in, for a map of how mainlines wait for one another. Only a mainline's
|
|
1571
|
+
* own `depends-on` counts: a task waiting on another mainline's task is a detail of that task, and lifting it
|
|
1572
|
+
* would bury the order under every file two tasks happen to share. The target may still be a task or criterion.
|
|
1573
|
+
*/
|
|
1570
1574
|
function mainlineEdges(graph) {
|
|
1571
1575
|
const out = /* @__PURE__ */ new Map();
|
|
1572
1576
|
for (const e of graph.edges) {
|
|
1577
|
+
if (parseRef(e.from)?.kind !== "mainline") continue;
|
|
1573
1578
|
const from = mainlineOf(e.from);
|
|
1574
1579
|
const to = mainlineOf(e.to);
|
|
1575
1580
|
if (!from || !to || from === to) continue;
|
|
@@ -1753,6 +1758,30 @@ function loadConfig(dirs) {
|
|
|
1753
1758
|
if (mismatch) throw new HubError(mismatch, []);
|
|
1754
1759
|
return config;
|
|
1755
1760
|
}
|
|
1761
|
+
/**
|
|
1762
|
+
* The mainlines loadHub lists, in the same order, with only what each mainline.md's front matter holds: no tasks,
|
|
1763
|
+
* decisions, artifacts or checks. For the callers that only need ids, titles and statuses, such as the hooks that run
|
|
1764
|
+
* on every turn, since loadHub reads and parses every document of the hub. Like loadHub it throws when config.yaml
|
|
1765
|
+
* cannot be used, and it leaves out a mainline whose mainline.md is missing, has front matter that does not parse, or
|
|
1766
|
+
* does not fit the schema. Every call reads the files again, so a status written in between is what it returns.
|
|
1767
|
+
*/
|
|
1768
|
+
function loadMainlines(dirs) {
|
|
1769
|
+
loadConfig(dirs);
|
|
1770
|
+
return subdirs$1(join(dirs.data, "mainlines")).flatMap((dir) => {
|
|
1771
|
+
const text = readText$1(join(dir, MAINLINE_FILE));
|
|
1772
|
+
if (text === null) return [];
|
|
1773
|
+
let data;
|
|
1774
|
+
try {
|
|
1775
|
+
data = splitFrontMatter(text).data;
|
|
1776
|
+
} catch {
|
|
1777
|
+
return [];
|
|
1778
|
+
}
|
|
1779
|
+
const result = mainlineSchema.safeParse(data);
|
|
1780
|
+
return result.success ? [result.data] : [];
|
|
1781
|
+
});
|
|
1782
|
+
}
|
|
1783
|
+
/** Whether a session could be pushing the mainline forward: it is active or planning, and not archived. */
|
|
1784
|
+
const isOpenMainline = (m) => !m.archivedAt && (m.status === "active" || m.status === "planning");
|
|
1756
1785
|
function loadHub(dirs, options = {}) {
|
|
1757
1786
|
const problems = [];
|
|
1758
1787
|
const warnings = [];
|
|
@@ -1877,6 +1906,14 @@ function loadHub(dirs, options = {}) {
|
|
|
1877
1906
|
const taskIds = new Set(tasks.map((t) => t.id));
|
|
1878
1907
|
const criterionIds = new Set(mainline.criteria.map((c) => c.id));
|
|
1879
1908
|
const itemRefs = new Set(tasks.flatMap((t) => t.checklist.map((c) => `${mainline.id}/${t.id}#${c.id}`)));
|
|
1909
|
+
mainline.grants?.forEach((grant, i) => {
|
|
1910
|
+
const task = grant.scope?.task;
|
|
1911
|
+
if (task && !taskIds.has(task) && !unreadable.has(`${mainline.id}/${task}`)) problems.push({
|
|
1912
|
+
file: mainline.file,
|
|
1913
|
+
field: `grants.${i}.scope.task`,
|
|
1914
|
+
message: `task ${mainline.id}/${task} does not exist; a grant's scope.task names a task of its own mainline`
|
|
1915
|
+
});
|
|
1916
|
+
});
|
|
1880
1917
|
for (const task of tasks) {
|
|
1881
1918
|
if (task.parent && !taskIds.has(task.parent)) problems.push({
|
|
1882
1919
|
file: task.file,
|
|
@@ -1944,7 +1981,16 @@ function loadHub(dirs, options = {}) {
|
|
|
1944
1981
|
const findings = hubDocs(join(dataDir, "findings"), findingSchema);
|
|
1945
1982
|
const groups = hubDocs(join(dataDir, "groups"), groupSchema);
|
|
1946
1983
|
problems.push(...groupProblems(config.hub, groups, mainlines, hubIssues, unreadable));
|
|
1947
|
-
for (const g of groups)
|
|
1984
|
+
for (const g of groups) {
|
|
1985
|
+
problems.push(...grantProblems(g, config), ...listGaps(g.file, "criteria", g.criteria ?? [], g.status === "open"));
|
|
1986
|
+
g.grants?.forEach((grant, i) => {
|
|
1987
|
+
if (grant.scope?.task) problems.push({
|
|
1988
|
+
file: g.file,
|
|
1989
|
+
field: `grants.${i}.scope.task`,
|
|
1990
|
+
message: `a group has no tasks, so ${g.id}#${grant.id} would never apply; limit it with session or paths, or move it to the mainline that holds ${grant.scope.task}`
|
|
1991
|
+
});
|
|
1992
|
+
});
|
|
1993
|
+
}
|
|
1948
1994
|
const ops = [];
|
|
1949
1995
|
const opsPath = join(dataDir, "ops");
|
|
1950
1996
|
for (const name of existsSync(opsPath) ? readdirSync(opsPath).filter((n) => n.endsWith(".log")).sort() : []) readFileSync(join(opsPath, name), "utf8").split("\n").forEach((line, index) => {
|
|
@@ -3462,7 +3508,7 @@ function setSessionInfo(hub, input) {
|
|
|
3462
3508
|
for (const [key, value] of Object.entries(input.info)) if (value !== void 0 && value !== null && value !== "") s[key] = value;
|
|
3463
3509
|
}));
|
|
3464
3510
|
}
|
|
3465
|
-
/** Remember the digest of
|
|
3511
|
+
/** Remember the digest of the whole turn reminder last sent; null forgets it, so the next reminder is sent again. */
|
|
3466
3512
|
function setReminderDigest$1(hub, input) {
|
|
3467
3513
|
const draft = new Draft(hub);
|
|
3468
3514
|
return draft.result(editYaml(draft, requireSession(draft, input.id), sessionSchema, (s) => {
|
|
@@ -3470,6 +3516,14 @@ function setReminderDigest$1(hub, input) {
|
|
|
3470
3516
|
else s.reminderDigest = input.digest;
|
|
3471
3517
|
}));
|
|
3472
3518
|
}
|
|
3519
|
+
/** Remember the digests of the check problems last handed over after a write; null or an empty list forgets them, so every problem is reported again. */
|
|
3520
|
+
function setCheckReported(hub, input) {
|
|
3521
|
+
const draft = new Draft(hub);
|
|
3522
|
+
return draft.result(editYaml(draft, requireSession(draft, input.id), sessionSchema, (s) => {
|
|
3523
|
+
if (!input.reported?.length) delete s.checkReported;
|
|
3524
|
+
else s.checkReported = input.reported;
|
|
3525
|
+
}));
|
|
3526
|
+
}
|
|
3473
3527
|
function setSessionStatus(hub, input) {
|
|
3474
3528
|
const value = requireOneOf(input.status, SESSION_STATUS, "session status");
|
|
3475
3529
|
const draft = new Draft(hub);
|
|
@@ -3919,6 +3973,7 @@ const OPERATIONS = {
|
|
|
3919
3973
|
"session.handoff": handoffSession,
|
|
3920
3974
|
"session.info": setSessionInfo,
|
|
3921
3975
|
"session.reminder": setReminderDigest$1,
|
|
3976
|
+
"session.checked": setCheckReported,
|
|
3922
3977
|
"session.set": setSessionStatus,
|
|
3923
3978
|
"session.settle": settleSession,
|
|
3924
3979
|
"session.archive": setSessionArchived,
|
|
@@ -4506,7 +4561,7 @@ function grantScope(dirs, session) {
|
|
|
4506
4561
|
ok: false,
|
|
4507
4562
|
reason: `${action.id} 沒有服務任何 checklist 項目或解決條件`
|
|
4508
4563
|
};
|
|
4509
|
-
const
|
|
4564
|
+
const all = [];
|
|
4510
4565
|
const served = {};
|
|
4511
4566
|
const closed = [];
|
|
4512
4567
|
const hubName = readConfig(dirs).hub;
|
|
@@ -4519,7 +4574,7 @@ function grantScope(dirs, session) {
|
|
|
4519
4574
|
continue;
|
|
4520
4575
|
}
|
|
4521
4576
|
served[id] = action.serves.find((ref) => mainlineOf(ref) === id);
|
|
4522
|
-
for (const grant of mainline.grants ?? [])
|
|
4577
|
+
for (const grant of mainline.grants ?? []) all.push({
|
|
4523
4578
|
owner: id,
|
|
4524
4579
|
grant,
|
|
4525
4580
|
ref: `${id}#${grant.id}`
|
|
@@ -4543,7 +4598,7 @@ function grantScope(dirs, session) {
|
|
|
4543
4598
|
continue;
|
|
4544
4599
|
}
|
|
4545
4600
|
served[id] = via;
|
|
4546
|
-
for (const grant of group.grants ?? [])
|
|
4601
|
+
for (const grant of group.grants ?? []) all.push({
|
|
4547
4602
|
owner: id,
|
|
4548
4603
|
grant,
|
|
4549
4604
|
ref: `${id}#${grant.id}`
|
|
@@ -4556,27 +4611,74 @@ function grantScope(dirs, session) {
|
|
|
4556
4611
|
reason: `${action.id} 服務的${what}已不在進行中(${closed.join("、")}),授權不再生效`
|
|
4557
4612
|
};
|
|
4558
4613
|
}
|
|
4614
|
+
const grants = [];
|
|
4615
|
+
const outOfScope = [];
|
|
4616
|
+
for (const g of all) {
|
|
4617
|
+
const why = limitMiss(g, session.id, action);
|
|
4618
|
+
if (why) outOfScope.push({
|
|
4619
|
+
grant: g,
|
|
4620
|
+
why
|
|
4621
|
+
});
|
|
4622
|
+
else grants.push(g);
|
|
4623
|
+
}
|
|
4559
4624
|
return {
|
|
4560
4625
|
ok: true,
|
|
4561
4626
|
scope: {
|
|
4562
4627
|
action,
|
|
4563
4628
|
grants,
|
|
4564
|
-
|
|
4629
|
+
outOfScope,
|
|
4630
|
+
served,
|
|
4631
|
+
root: realPath(dirs.root) ?? dirs.root
|
|
4565
4632
|
}
|
|
4566
4633
|
};
|
|
4567
4634
|
}
|
|
4568
|
-
/**
|
|
4635
|
+
/** Why a grant's scope leaves out this session or action, or null when its task and session hold. */
|
|
4636
|
+
function limitMiss({ owner, grant }, sessionId, action) {
|
|
4637
|
+
const limits = grant.scope;
|
|
4638
|
+
if (!limits) return null;
|
|
4639
|
+
if (limits.session !== void 0 && limits.session !== sessionId) return `限定會話 ${limits.session},這次是 ${sessionId}`;
|
|
4640
|
+
if (limits.task !== void 0) {
|
|
4641
|
+
if (!action.serves.some((ref) => {
|
|
4642
|
+
const parsed = parseRef(ref);
|
|
4643
|
+
return parsed?.kind === "checklistItem" && parsed.mainline === owner && parsed.task === limits.task;
|
|
4644
|
+
})) return `限定行動服務 ${owner}/${limits.task} 的項目,${action.id} 沒有服務它`;
|
|
4645
|
+
}
|
|
4646
|
+
return null;
|
|
4647
|
+
}
|
|
4648
|
+
/** Why a grant's paths leave out the request's directory, or null when it has none or the directory lies in them. */
|
|
4649
|
+
function pathsMiss(grant, request, root) {
|
|
4650
|
+
const paths = grant.scope?.paths;
|
|
4651
|
+
if (!paths) return null;
|
|
4652
|
+
if (request.dir === void 0) return `限定路徑 ${paths.join("、")},這次沒有可比對的目錄`;
|
|
4653
|
+
const rel = relative(root, request.dir).split(sep).join("/");
|
|
4654
|
+
return pathInPatterns(paths, rel) ? null : `限定路徑 ${paths.join("、")},這次的目錄是 ${request.dir}`;
|
|
4655
|
+
}
|
|
4656
|
+
/**
|
|
4657
|
+
* Every request must be covered by some grant in scope whose paths, if it has any, hold the request's directory; the
|
|
4658
|
+
* first request that is not covered is named in the reason, with the grants whose scope left it out.
|
|
4659
|
+
*/
|
|
4569
4660
|
function matchGrants(scope, requests) {
|
|
4570
4661
|
const matches = [];
|
|
4662
|
+
const covers = (g, request) => {
|
|
4663
|
+
const parsed = parseGrantTarget(g.grant.action, g.grant.target);
|
|
4664
|
+
return parsed.ok && grantCovers(parsed.target, request);
|
|
4665
|
+
};
|
|
4571
4666
|
for (const request of requests) {
|
|
4667
|
+
const misses = [];
|
|
4572
4668
|
const grant = scope.grants.find((g) => {
|
|
4573
|
-
|
|
4574
|
-
|
|
4669
|
+
if (!covers(g, request)) return false;
|
|
4670
|
+
const why = pathsMiss(g.grant, request, scope.root);
|
|
4671
|
+
if (why) misses.push(`${g.ref} ${why}`);
|
|
4672
|
+
return why === null;
|
|
4575
4673
|
});
|
|
4576
|
-
if (!grant)
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4674
|
+
if (!grant) {
|
|
4675
|
+
const owners = Object.keys(scope.served).join("、");
|
|
4676
|
+
misses.push(...scope.outOfScope.filter((o) => covers(o.grant, request)).map((o) => `${o.grant.ref} ${o.why}`));
|
|
4677
|
+
return {
|
|
4678
|
+
ok: false,
|
|
4679
|
+
reason: `${owners} 沒有涵蓋 ${describeRequest(request)} 的授權${misses.length ? `(${misses.join(";")})` : ""}`
|
|
4680
|
+
};
|
|
4681
|
+
}
|
|
4580
4682
|
matches.push({
|
|
4581
4683
|
request,
|
|
4582
4684
|
grant
|
|
@@ -4599,7 +4701,13 @@ function grantLines(dirs, session) {
|
|
|
4599
4701
|
return result.scope.grants.map((g) => {
|
|
4600
4702
|
const parsed = parseGrantTarget(g.grant.action, g.grant.target);
|
|
4601
4703
|
const form = parsed.ok ? grantForm(parsed.target) : void 0;
|
|
4602
|
-
|
|
4704
|
+
const limits = g.grant.scope;
|
|
4705
|
+
const limited = limits ? [
|
|
4706
|
+
limits.task && `任務 ${g.owner}/${limits.task}`,
|
|
4707
|
+
limits.session && `會話 ${limits.session}`,
|
|
4708
|
+
limits.paths && `路徑 ${limits.paths.join("、")}`
|
|
4709
|
+
].filter(Boolean).join("、") : "";
|
|
4710
|
+
return `- ${g.ref} ${g.grant.action} ${g.grant.target}${limited ? `(限${limited})` : ""}${form ? `:${form}` : ""}`;
|
|
4603
4711
|
});
|
|
4604
4712
|
} catch {
|
|
4605
4713
|
return [];
|
|
@@ -4625,7 +4733,7 @@ function resolveAction(dirs, ref) {
|
|
|
4625
4733
|
function transcriptPath(dirs, transcript) {
|
|
4626
4734
|
return isAbsolute(transcript) ? transcript : join(dirs.root, transcript);
|
|
4627
4735
|
}
|
|
4628
|
-
/** Remember the digest of
|
|
4736
|
+
/** Remember the digest of the whole turn reminder last sent; null forgets it, so the next reminder is sent again. */
|
|
4629
4737
|
function setReminderDigest(dirs, id, digest) {
|
|
4630
4738
|
runOp(dirs, (hub) => setReminderDigest$1(hub, {
|
|
4631
4739
|
id,
|
|
@@ -4878,8 +4986,8 @@ function waitingLine(a) {
|
|
|
4878
4986
|
] : [];
|
|
4879
4987
|
return `${a.id} ${a.do}${parts.length > 0 ? `(${parts.join(";")})` : ""}`;
|
|
4880
4988
|
}
|
|
4881
|
-
/** Mainlines a session could be pushing forward. */
|
|
4882
|
-
const openMainlines = (dirs) =>
|
|
4989
|
+
/** Mainlines a session could be pushing forward; only their mainline.md is read, as the turn hooks need nothing else. */
|
|
4990
|
+
const openMainlines = (dirs) => loadMainlines(dirs).filter(isOpenMainline);
|
|
4883
4991
|
/** How recently a session's transcript must have been written for the session to count as possibly still running. */
|
|
4884
4992
|
const RUNNING_WINDOW_MS = 9e5;
|
|
4885
4993
|
/** Whether the session's transcript was written within the running window; false when no transcript is recorded or readable. */
|
|
@@ -5025,7 +5133,7 @@ function sessionBriefing(dirs, session, options) {
|
|
|
5025
5133
|
}
|
|
5026
5134
|
lines.push(...waiterLines(dirs, session));
|
|
5027
5135
|
const overlaps = overlapLines(dirs, session);
|
|
5028
|
-
if (overlaps.length > 0) lines.push("
|
|
5136
|
+
if (overlaps.length > 0) lines.push("目前行動服務的主脈絡和其他進行中的主脈絡預計動到同樣的路徑,開始改之前先和對方協調先後:", ...overlaps);
|
|
5029
5137
|
if (!session.actions.some(isOpen)) lines.push(...handoffLines(dirs, session, cli));
|
|
5030
5138
|
const warnings = queueWarnings(dirs, session, cli);
|
|
5031
5139
|
if (warnings.length > 0) lines.push("需要處理:", ...warnings.map((w) => `- ${w}`));
|
|
@@ -5034,36 +5142,33 @@ function sessionBriefing(dirs, session, options) {
|
|
|
5034
5142
|
return lines.join("\n");
|
|
5035
5143
|
}
|
|
5036
5144
|
/**
|
|
5037
|
-
*
|
|
5038
|
-
* Records a digest of the
|
|
5145
|
+
* The reminder for every user turn; null when there is nothing to say, or when it would be the same text as the
|
|
5146
|
+
* reminder the model was last given. Records a digest of the whole text in the session file, only when it changes;
|
|
5039
5147
|
* `saveDigest` records it instead of writing the file here, as a host hook in remote mode sends it to the sync server.
|
|
5040
5148
|
*/
|
|
5041
5149
|
function turnReminder(dirs, session, options) {
|
|
5042
|
-
|
|
5150
|
+
const text = reminderText(dirs, session, options.cli);
|
|
5151
|
+
if (text === null) return null;
|
|
5152
|
+
const digest = contentHash(text);
|
|
5153
|
+
if (session.reminderDigest === digest) return null;
|
|
5154
|
+
if (readSession(dirs, session.id)) try {
|
|
5155
|
+
if (options.saveDigest) options.saveDigest(digest);
|
|
5156
|
+
else setReminderDigest(dirs, session.id, digest);
|
|
5157
|
+
} catch {}
|
|
5158
|
+
return text;
|
|
5159
|
+
}
|
|
5160
|
+
function reminderText(dirs, session, cli) {
|
|
5161
|
+
if (session.actions.length === 0) return openMainlines(dirs).length > 0 ? `[mainflow] ${session.id} 還沒有行動。這一輪要推進主脈絡時,先用 ${cli} action add 列出行動並標為 doing,或用 ${cli} session handoff <會話> 接手上一場沒做完的行動,再開始做。回覆結尾的標頭只看這場會話進行中的行動。` : null;
|
|
5043
5162
|
const doing = current(session);
|
|
5044
5163
|
const open = session.actions.filter(isOpen);
|
|
5045
5164
|
const blocked = session.actions.filter((a) => a.status === "blocked");
|
|
5046
|
-
const head = doing ? `[mainflow] 目前行動 ${doing.id} ${doing.do},服務 ${doing.serves.join("、") || "(未指定)"}。這一輪的工作若不屬於它,先調整佇列再做。` : open.length > 0 ? `[mainflow] ${session.id} 目前沒有進行中的行動。` : blocked.length > 0 ? `[mainflow] ${session.id} 的行動在等外部條件:${blocked.map(waitingLine).join(";")}。有條件(until)的可以在背景執行 ${
|
|
5165
|
+
const head = doing ? `[mainflow] 目前行動 ${doing.id} ${doing.do},服務 ${doing.serves.join("、") || "(未指定)"}。這一輪的工作若不屬於它,先調整佇列再做。` : open.length > 0 ? `[mainflow] ${session.id} 目前沒有進行中的行動。` : blocked.length > 0 ? `[mainflow] ${session.id} 的行動在等外部條件:${blocked.map(waitingLine).join(";")}。有條件(until)的可以在背景執行 ${cli} wait <a編號> 等它成立。條件出現後用 ${cli} action set <a編號> --status doing 繼續;要先做別的事,用 ${cli} action add 列出行動。` : `[mainflow] ${session.id} 的行動都已結束。繼續工作前先用 ${cli} action add 列出下一個行動。`;
|
|
5047
5166
|
const header = headerLine(session);
|
|
5048
5167
|
const marker = header ? [`- 這一輪最後一則回覆的最後一行寫「${header}」,回覆範例等其他內容都放在它前面。有顯示 hook 時,它會換成標題與連結。`] : [];
|
|
5049
5168
|
const grants = grantLines(dirs, session);
|
|
5050
|
-
const
|
|
5051
|
-
const
|
|
5052
|
-
const
|
|
5053
|
-
doing?.id ?? null,
|
|
5054
|
-
grants,
|
|
5055
|
-
waiters,
|
|
5056
|
-
warnings
|
|
5057
|
-
]));
|
|
5058
|
-
const repeated = session.reminderDigest === digest;
|
|
5059
|
-
if (!repeated && (session.reminderDigest !== void 0 || grants.length + waiters.length + warnings.length > 0) && readSession(dirs, session.id)) try {
|
|
5060
|
-
if (options.saveDigest) options.saveDigest(digest);
|
|
5061
|
-
else setReminderDigest(dirs, session.id, digest);
|
|
5062
|
-
} catch {}
|
|
5063
|
-
const once = (lines, summary, full) => lines.length === 0 ? [] : repeated ? [summary] : full;
|
|
5064
|
-
const granted = once(grants, `- 預先授權同上一輪,共 ${grants.length} 條。`, ["- 目前行動服務的主脈絡與所屬的組預先授權了這些動作:", ...grants.map((g) => ` ${g}`)]);
|
|
5065
|
-
const waiting = once(waiters, `- 等待行同上一輪,共 ${waiters.length} 條。`, waiters.map((w) => `- ${w}`));
|
|
5066
|
-
const warned = once(warnings, `- 佇列提醒同上一輪,共 ${warnings.length} 條。`, warnings.map((w) => `- ${w}`));
|
|
5169
|
+
const granted = grants.length > 0 ? ["- 目前行動服務的主脈絡與所屬的組預先授權了這些動作:", ...grants.map((g) => ` ${g}`)] : [];
|
|
5170
|
+
const waiting = waiterLines(dirs, session).map((w) => `- ${w}`);
|
|
5171
|
+
const warned = queueWarnings(dirs, session, cli).map((w) => `- ${w}`);
|
|
5067
5172
|
return [
|
|
5068
5173
|
head,
|
|
5069
5174
|
...waiting,
|
|
@@ -8404,6 +8509,7 @@ function resolveTurns(session, t) {
|
|
|
8404
8509
|
};
|
|
8405
8510
|
const first = t.turns.findIndex((x) => x.head !== void 0);
|
|
8406
8511
|
const out = [];
|
|
8512
|
+
let latest;
|
|
8407
8513
|
t.turns.forEach((turn, i) => {
|
|
8408
8514
|
const span = {
|
|
8409
8515
|
from: turn.at,
|
|
@@ -8421,39 +8527,50 @@ function resolveTurns(session, t) {
|
|
|
8421
8527
|
});
|
|
8422
8528
|
return;
|
|
8423
8529
|
}
|
|
8424
|
-
|
|
8425
|
-
|
|
8530
|
+
if (turn.head) latest = turn.head;
|
|
8531
|
+
const head = latest;
|
|
8532
|
+
const started = turn.started.find((id) => byId.has(id));
|
|
8533
|
+
const next = turn.handoff ? nextNamed(i) : void 0;
|
|
8534
|
+
const call = turn.head || turn.reportsOn === void 0 ? void 0 : t.agentCalls.get(turn.reportsOn);
|
|
8535
|
+
const spawner = call === void 0 ? void 0 : [...out].reverse().find((r) => r.from <= call);
|
|
8536
|
+
const switchedFrom = (mainline) => mainline !== null && turn.started.some((id) => {
|
|
8537
|
+
const a = byId.get(id);
|
|
8538
|
+
return a !== void 0 && primaryMainline(a.serves) !== mainline;
|
|
8539
|
+
});
|
|
8540
|
+
if (spawner && ("action" in head || !(started || next))) {
|
|
8541
|
+
out.push({
|
|
8542
|
+
...span,
|
|
8543
|
+
mainline: spawner.mainline,
|
|
8544
|
+
source: "report",
|
|
8545
|
+
shared: spawner.shared,
|
|
8546
|
+
switched: switchedFrom(spawner.mainline)
|
|
8547
|
+
});
|
|
8548
|
+
return;
|
|
8549
|
+
}
|
|
8550
|
+
if ("action" in head) {
|
|
8426
8551
|
serves = servesOf(head);
|
|
8427
|
-
source = "head";
|
|
8428
|
-
} else if (head) {
|
|
8429
|
-
const started = turn.started.find((id) => byId.has(id));
|
|
8430
|
-
const next = turn.handoff ? nextNamed(i) : void 0;
|
|
8552
|
+
source = turn.head ? "head" : "previous";
|
|
8553
|
+
} else if (turn.head || started || next) {
|
|
8431
8554
|
serves = started ? byId.get(started).serves : next ? servesOf(next) : void 0;
|
|
8432
8555
|
source = started ? "started" : next ? "handoff" : "none";
|
|
8433
8556
|
} else {
|
|
8434
|
-
const
|
|
8435
|
-
const spawner = call === void 0 ? void 0 : [...out].reverse().find((r) => r.from <= call);
|
|
8436
|
-
const from = spawner ?? out.at(-1);
|
|
8557
|
+
const from = out.at(-1);
|
|
8437
8558
|
out.push({
|
|
8438
8559
|
...span,
|
|
8439
8560
|
mainline: from.mainline,
|
|
8440
|
-
source:
|
|
8561
|
+
source: "previous",
|
|
8441
8562
|
shared: from.shared,
|
|
8442
8563
|
switched: false
|
|
8443
8564
|
});
|
|
8444
8565
|
return;
|
|
8445
8566
|
}
|
|
8446
8567
|
const mainline = serves ? primaryMainline(serves) ?? null : null;
|
|
8447
|
-
const switched = mainline !== null && turn.started.some((id) => {
|
|
8448
|
-
const a = byId.get(id);
|
|
8449
|
-
return a !== void 0 && primaryMainline(a.serves) !== mainline;
|
|
8450
|
-
});
|
|
8451
8568
|
out.push({
|
|
8452
8569
|
...span,
|
|
8453
8570
|
mainline,
|
|
8454
8571
|
source,
|
|
8455
8572
|
shared: serves !== void 0 && mainlinesOf(serves).length > 1,
|
|
8456
|
-
switched
|
|
8573
|
+
switched: switchedFrom(mainline)
|
|
8457
8574
|
});
|
|
8458
8575
|
});
|
|
8459
8576
|
return out;
|
|
@@ -9482,4 +9599,4 @@ function openInApp(hub, target, closeAfterMs = 150) {
|
|
|
9482
9599
|
return true;
|
|
9483
9600
|
}
|
|
9484
9601
|
//#endregion
|
|
9485
|
-
export {
|
|
9602
|
+
export { isOpenMainline as $, realPath as $t, transcriptPath as A, hubRequirements as At, applyOffline as B, notRegisteredMessage as Bt, turnReminder as C, comparePaths as Ct, readNotices as D, textBytes as Dt, parseWarn as E, requestTime as Et, headerLine as F, requireSlug as Ft, scopeOfPath as G, resolveHub as Gt, renumberFiles as H, registerHub as Ht, mayDecorate as I, hubAtRoot as It, syncedDir as J, nodeStorage as Jt, hubDigest as K, unregisterHub as Kt, attributeTrace as L, hubHoldingFile as Lt, matchGrants as M, MAINLINE_FILE as Mt, decorateReply as N, SESSIONS_DIR as Nt, loadSessions as O, formatMismatch as Ot, endsWithMarker as P, TASK_FILE as Pt, HubError as Q, hubPath as Qt, traceTranscript as R, inRepositoryHubAt as Rt, sessionBriefing as S, VERSION_HEADER as St, markNoticesHandled as T, readsAsText as Tt, renumberPath as U, registeredName as Ut, renumberRequest as V, readRegistry as Vt, renumberText as W, registryFile as Wt, applyOperation as X, dataFile as Xt, syncedPath as Y, currentBranch as Yt, checkHubName as Z, gitCheckout as Zt, ARCHIVE_KINDS as _, MAX_BLOB_BYTES as _t, openInApp as a, splitFrontMatter as an, mainlineEdges as at, untilState as b, ROUTES as bt, siteBlock as c, refIndex as ct, migrateHubData as d, taskFile as dt, worktreesOf as en, loadConfig as et, migrateHub as f, nowMinute as ft, RETRY_RULES as g, BLOB_HASH as gt, receiverFromEnv as h, ACCESS_HEADERS as ht, hostName as i, contentHash as in, dependencyGraph as it, grantScope as j, versionMismatch as jt, readSession as k, hubFormatVersion as kt, syncHost as l, remoteMembers as lt, retroReportFromReceiver as m, readYaml as mt, DEFAULT_UI_PORT as n, DATA_DIR as nn, loadMainlines as nt, removeDanglingLinks as o, linksInto as ot, retroReport as p, readFrontMatter as pt, mayHoldSynced as q, userDataDir as qt, caddySites as r, WriteError as rn, sectionText as rt, removeHostLink as s, memberOfGroup as st, APP_BUNDLE_ID as t, ConflictError as tn, loadHub as tt, groupPlan as u, unavailableHubs as ut, setArchivedMany as v, MAX_CHANGE_PAGE as vt, HANDLED_FILE as w, exceedsFileLimit as wt, duplicateClaimLines as x, SOCKET_MAX_AGE_CLOSE as xt, initHub as y, MAX_FILE_BYTES as yt, transcriptTitle as z, listHubs as zt };
|
package/dist/host.d.ts
CHANGED
package/dist/host.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as openInApp, c as siteBlock, i as hostName, l as syncHost, n as DEFAULT_UI_PORT, o as removeDanglingLinks, r as caddySites, s as removeHostLink, t as APP_BUNDLE_ID } from "./host-
|
|
1
|
+
import { a as openInApp, c as siteBlock, i as hostName, l as syncHost, n as DEFAULT_UI_PORT, o as removeDanglingLinks, r as caddySites, s as removeHostLink, t as APP_BUNDLE_ID } from "./host-C74j2Qox.js";
|
|
2
2
|
export { APP_BUNDLE_ID, DEFAULT_UI_PORT, caddySites, hostName, openInApp, removeDanglingLinks, removeHostLink, siteBlock, syncHost };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as Task, a as Decision, c as Group, d as Mainline, f as OpsEntry, h as Session, i as ChecklistItem, n as ActionBlock, s as Finding, t as Action, u as Issue } from "./index-
|
|
1
|
+
import { _ as Task, a as Decision, c as Group, d as Mainline, f as OpsEntry, h as Session, i as ChecklistItem, n as ActionBlock, s as Finding, t as Action, u as Issue } from "./index-l8QUG80k.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
//#region ../core/dist/ids/allocators.d.ts
|
|
4
4
|
/** The letters ids start with. */
|
|
@@ -437,11 +437,16 @@ declare function setSessionInfo(hub: HubReader, input: {
|
|
|
437
437
|
id: string;
|
|
438
438
|
info: SessionInfo;
|
|
439
439
|
}): OpResult<Session>;
|
|
440
|
-
/** Remember the digest of
|
|
440
|
+
/** Remember the digest of the whole turn reminder last sent; null forgets it, so the next reminder is sent again. */
|
|
441
441
|
declare function setReminderDigest(hub: HubReader, input: {
|
|
442
442
|
id: string;
|
|
443
443
|
digest: string | null;
|
|
444
444
|
}): OpResult<Session>;
|
|
445
|
+
/** Remember the digests of the check problems last handed over after a write; null or an empty list forgets them, so every problem is reported again. */
|
|
446
|
+
declare function setCheckReported(hub: HubReader, input: {
|
|
447
|
+
id: string;
|
|
448
|
+
reported: string[] | null;
|
|
449
|
+
}): OpResult<Session>;
|
|
445
450
|
declare function setSessionStatus(hub: HubReader, input: {
|
|
446
451
|
id: string;
|
|
447
452
|
status: string;
|
|
@@ -490,7 +495,7 @@ declare function updateAction(hub: HubReader, input: {
|
|
|
490
495
|
* (`item check` and `item uncheck` send `item.check`, with done true or false). The operations no command sends
|
|
491
496
|
* are listed in COMMANDLESS_OPERATIONS with who sends them.
|
|
492
497
|
*/
|
|
493
|
-
declare const OPERATION_NAMES: readonly ['repo.add', 'hub.add', 'mainline.new', 'mainline.set', 'mainline.title', 'mainline.archive', 'criterion.add', 'criterion.check', 'criterion.set', 'task.new', 'task.set', 'item.add', 'item.check', 'item.drop', 'item.undrop', 'decision.new', 'decision.accept', 'decision.supersede', 'issue.new', 'issue.set', 'issue.plan', 'issue.close', 'issue.reopen', 'issue.archive', 'finding.new', 'finding.set', 'finding.archive', 'group.new', 'group.set', 'ops.add', 'section.append', 'session.start', 'session.ensure', 'session.handoff', 'session.info', 'session.reminder', 'session.set', 'session.settle', 'session.archive', 'action.add', 'action.set', 'file.remove'];
|
|
498
|
+
declare const OPERATION_NAMES: readonly ['repo.add', 'hub.add', 'mainline.new', 'mainline.set', 'mainline.title', 'mainline.archive', 'criterion.add', 'criterion.check', 'criterion.set', 'task.new', 'task.set', 'item.add', 'item.check', 'item.drop', 'item.undrop', 'decision.new', 'decision.accept', 'decision.supersede', 'issue.new', 'issue.set', 'issue.plan', 'issue.close', 'issue.reopen', 'issue.archive', 'finding.new', 'finding.set', 'finding.archive', 'group.new', 'group.set', 'ops.add', 'section.append', 'session.start', 'session.ensure', 'session.handoff', 'session.info', 'session.reminder', 'session.checked', 'session.set', 'session.settle', 'session.archive', 'action.add', 'action.set', 'file.remove'];
|
|
494
499
|
type OperationName = (typeof OPERATION_NAMES)[number];
|
|
495
500
|
//#endregion
|
|
496
501
|
//#region ../core/dist/ops/operations.d.ts
|
|
@@ -537,6 +542,7 @@ declare const OPERATIONS: {
|
|
|
537
542
|
readonly 'session.handoff': typeof handoffSession;
|
|
538
543
|
readonly 'session.info': typeof setSessionInfo;
|
|
539
544
|
readonly 'session.reminder': typeof setReminderDigest;
|
|
545
|
+
readonly 'session.checked': typeof setCheckReported;
|
|
540
546
|
readonly 'session.set': typeof setSessionStatus;
|
|
541
547
|
readonly 'session.settle': typeof settleSession;
|
|
542
548
|
readonly 'session.archive': typeof setSessionArchived;
|