@phamvuhoang/otto-core 0.18.0 → 0.20.0
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-help.d.ts +18 -0
- package/dist/cli-help.d.ts.map +1 -1
- package/dist/cli-help.js +32 -1
- package/dist/cli-help.js.map +1 -1
- package/dist/fanout.d.ts +56 -0
- package/dist/fanout.d.ts.map +1 -0
- package/dist/fanout.js +119 -0
- package/dist/fanout.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/journal-config.d.ts +9 -0
- package/dist/journal-config.d.ts.map +1 -0
- package/dist/journal-config.js +31 -0
- package/dist/journal-config.js.map +1 -0
- package/dist/journal-gate.d.ts +46 -0
- package/dist/journal-gate.d.ts.map +1 -0
- package/dist/journal-gate.js +135 -0
- package/dist/journal-gate.js.map +1 -0
- package/dist/journal-ledger.d.ts +13 -0
- package/dist/journal-ledger.d.ts.map +1 -0
- package/dist/journal-ledger.js +33 -0
- package/dist/journal-ledger.js.map +1 -0
- package/dist/journal-source.d.ts +19 -0
- package/dist/journal-source.d.ts.map +1 -0
- package/dist/journal-source.js +38 -0
- package/dist/journal-source.js.map +1 -0
- package/dist/journal.d.ts +46 -0
- package/dist/journal.d.ts.map +1 -0
- package/dist/journal.js +158 -0
- package/dist/journal.js.map +1 -0
- package/dist/loop.d.ts +13 -0
- package/dist/loop.d.ts.map +1 -1
- package/dist/loop.js +91 -7
- package/dist/loop.js.map +1 -1
- package/dist/model-tier.d.ts +69 -0
- package/dist/model-tier.d.ts.map +1 -0
- package/dist/model-tier.js +101 -0
- package/dist/model-tier.js.map +1 -0
- package/dist/plan-tasks.d.ts +44 -0
- package/dist/plan-tasks.d.ts.map +1 -0
- package/dist/plan-tasks.js +167 -0
- package/dist/plan-tasks.js.map +1 -0
- package/dist/run-bin.d.ts.map +1 -1
- package/dist/run-bin.js +21 -1
- package/dist/run-bin.js.map +1 -1
- package/dist/runner.d.ts +18 -14
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +12 -23
- package/dist/runner.js.map +1 -1
- package/dist/stage-exec.d.ts +13 -0
- package/dist/stage-exec.d.ts.map +1 -1
- package/dist/stage-exec.js +32 -1
- package/dist/stage-exec.js.map +1 -1
- package/dist/stages.d.ts +28 -0
- package/dist/stages.d.ts.map +1 -1
- package/dist/stages.js +30 -0
- package/dist/stages.js.map +1 -1
- package/dist/threads-api.d.ts +46 -0
- package/dist/threads-api.d.ts.map +1 -0
- package/dist/threads-api.js +101 -0
- package/dist/threads-api.js.map +1 -0
- package/dist/worktree.d.ts +24 -0
- package/dist/worktree.d.ts.map +1 -0
- package/dist/worktree.js +43 -0
- package/dist/worktree.js.map +1 -0
- package/package.json +1 -1
- package/templates/journal-screen.md +19 -0
- package/templates/journal-write.md +16 -0
- package/templates/plan.md +26 -4
- package/templates/subtask.md +32 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The P12 outbound secrecy gate (issue #67). Layered, default-deny: a journal
|
|
3
|
+
* note is published only if it passes every gate. Gate 1 (here) is the pure,
|
|
4
|
+
* deterministic deny-list — the backbone. Gates 2 (generalization) and 3
|
|
5
|
+
* (adversarial judge) are added in later slices. ZERO-LEAK is the hard gate:
|
|
6
|
+
* anything ambiguous is denied, and a thrown check denies rather than crashes.
|
|
7
|
+
*/
|
|
8
|
+
import { appendFileSync, mkdirSync } from "node:fs";
|
|
9
|
+
import { dirname, join } from "node:path";
|
|
10
|
+
/** Built-in secret/identifier deny patterns. Each is intentionally broad — this
|
|
11
|
+
* gate is biased to over-deny. The pattern NAME is logged, never the match. */
|
|
12
|
+
const DENY = [
|
|
13
|
+
{ name: "openai-key", re: /\bsk-[A-Za-z0-9_-]{12,}/ },
|
|
14
|
+
{ name: "github-token", re: /\b(ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{20,}/ },
|
|
15
|
+
{ name: "slack-token", re: /\bxox[baprs]-[A-Za-z0-9-]{10,}/ },
|
|
16
|
+
{ name: "aws-key", re: /\bAKIA[0-9A-Z]{16}\b/ },
|
|
17
|
+
{ name: "private-key", re: /-----BEGIN [A-Z ]*PRIVATE KEY-----/ },
|
|
18
|
+
{ name: "bearer", re: /\bBearer\s+[A-Za-z0-9._-]{8,}/i },
|
|
19
|
+
{
|
|
20
|
+
name: "assignment-secret",
|
|
21
|
+
re: /\b(password|passwd|secret|api[_-]?key|token|access[_-]?key)\b\s*[:=]\s*\S+/i,
|
|
22
|
+
},
|
|
23
|
+
{ name: "url", re: /\bhttps?:\/\/\S+/i },
|
|
24
|
+
{ name: "abs-path", re: /(^|\s)(\/[\w.-]+){2,}/ },
|
|
25
|
+
{ name: "rel-path", re: /(^|\s)\.{1,2}\/[\w./-]+/ },
|
|
26
|
+
{ name: "win-path", re: /\b[A-Za-z]:\\[\\\w.-]+/ },
|
|
27
|
+
{ name: "code-fence", re: /```/ },
|
|
28
|
+
{ name: "code-span", re: /`[^`]+`/ },
|
|
29
|
+
{ name: "scoped-pkg", re: /(^|\s)@[a-z0-9-]+\/[a-z0-9-]+/i },
|
|
30
|
+
{ name: "import-stmt", re: /\b(import\s.+\sfrom\s|require\s*\()/ },
|
|
31
|
+
];
|
|
32
|
+
/** Build a word-ish, case-insensitive matcher for a repo identifier. */
|
|
33
|
+
function identifierRe(id) {
|
|
34
|
+
const trimmed = id.trim();
|
|
35
|
+
if (trimmed.length < 3)
|
|
36
|
+
return null; // too short → too many false denials
|
|
37
|
+
const escaped = trimmed.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
38
|
+
return new RegExp(`(^|[^A-Za-z0-9])${escaped}([^A-Za-z0-9]|$)`, "i");
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Gate 1: deny if the entry matches any built-in secret/identifier pattern, any
|
|
42
|
+
* repo self-identifier, or any policy secretPattern. A malformed policy pattern
|
|
43
|
+
* is treated as a denial (fail closed). Pure; never mutates inputs.
|
|
44
|
+
*/
|
|
45
|
+
export function screenGate1(entry, ctx) {
|
|
46
|
+
for (const { name, re } of DENY) {
|
|
47
|
+
if (re.test(entry))
|
|
48
|
+
return { ok: false, gate: 1, reason: `deny:${name}` };
|
|
49
|
+
}
|
|
50
|
+
for (const id of ctx.repoIdentifiers) {
|
|
51
|
+
const re = identifierRe(id);
|
|
52
|
+
if (re && re.test(entry)) {
|
|
53
|
+
return { ok: false, gate: 1, reason: "deny:repo-identifier" };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
for (const pattern of ctx.secretPatterns) {
|
|
57
|
+
let re;
|
|
58
|
+
try {
|
|
59
|
+
re = new RegExp(pattern, "i");
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// a malformed policy pattern fails closed — we cannot prove safety.
|
|
63
|
+
return { ok: false, gate: 1, reason: "deny:malformed-policy-pattern" };
|
|
64
|
+
}
|
|
65
|
+
if (re.test(entry)) {
|
|
66
|
+
return { ok: false, gate: 1, reason: "deny:policy-secret-pattern" };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return { ok: true };
|
|
70
|
+
}
|
|
71
|
+
export const MAX_ENTRY_CHARS = 500;
|
|
72
|
+
export const MIN_ENTRY_CHARS = 20;
|
|
73
|
+
/** Shapes that betray a specific task/ticket. */
|
|
74
|
+
const TASK_KEY_RE = /\b(issue-\d+|[A-Z]{2,}-\d+)\b/;
|
|
75
|
+
/**
|
|
76
|
+
* Gate 2: the note must read as GENERIC craft. Deny task-key shapes, any term
|
|
77
|
+
* carried from the source record (scope globs / taskKey / run id), and notes
|
|
78
|
+
* outside the length bounds. Pure.
|
|
79
|
+
*/
|
|
80
|
+
export function screenGate2(entry, ctx) {
|
|
81
|
+
const text = entry.trim();
|
|
82
|
+
if (text.length < MIN_ENTRY_CHARS || text.length > MAX_ENTRY_CHARS) {
|
|
83
|
+
return { ok: false, gate: 2, reason: "deny:length" };
|
|
84
|
+
}
|
|
85
|
+
if (TASK_KEY_RE.test(text)) {
|
|
86
|
+
return { ok: false, gate: 2, reason: "deny:task-key" };
|
|
87
|
+
}
|
|
88
|
+
for (const term of ctx.forbiddenTerms) {
|
|
89
|
+
const t = term.trim();
|
|
90
|
+
if (t.length >= 3 &&
|
|
91
|
+
new RegExp(t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i").test(text)) {
|
|
92
|
+
return { ok: false, gate: 2, reason: "deny:forbidden-term" };
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return { ok: true };
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Run the full gate: 1 (deny-list) → 2 (generalization) → 3 (judge, optional).
|
|
99
|
+
* Default-deny: an empty entry, any non-pass gate, or a thrown judge denies. The
|
|
100
|
+
* judge runs LAST and only on an entry the deterministic gates already passed.
|
|
101
|
+
*/
|
|
102
|
+
export async function screenEntry(entry, ctx, judge) {
|
|
103
|
+
if (!entry || !entry.trim()) {
|
|
104
|
+
return { ok: false, gate: 1, reason: "deny:empty" };
|
|
105
|
+
}
|
|
106
|
+
const g1 = screenGate1(entry, ctx);
|
|
107
|
+
if (!g1.ok)
|
|
108
|
+
return g1;
|
|
109
|
+
const g2 = screenGate2(entry, ctx);
|
|
110
|
+
if (!g2.ok)
|
|
111
|
+
return g2;
|
|
112
|
+
if (judge) {
|
|
113
|
+
try {
|
|
114
|
+
const safe = await judge(entry);
|
|
115
|
+
if (!safe)
|
|
116
|
+
return { ok: false, gate: 3, reason: "deny:judge-unsafe" };
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return { ok: false, gate: 3, reason: "deny:judge-error" };
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return { ok: true };
|
|
123
|
+
}
|
|
124
|
+
/** Append one JSON line to the journal audit trail. Never throws. */
|
|
125
|
+
export function appendAudit(workspaceDir, line) {
|
|
126
|
+
try {
|
|
127
|
+
const path = join(workspaceDir, ".otto", "journal", "audit.log");
|
|
128
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
129
|
+
appendFileSync(path, JSON.stringify(line) + "\n", "utf8");
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// the audit log is best-effort; never crash a run over it.
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=journal-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"journal-gate.js","sourceRoot":"","sources":["../src/journal-gate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type PostedEntry = {
|
|
2
|
+
memoryId: string;
|
|
3
|
+
contentHash: string;
|
|
4
|
+
postedAt: string;
|
|
5
|
+
postId?: string;
|
|
6
|
+
};
|
|
7
|
+
/** Stable short content hash, used for dedup of near-identical notes. */
|
|
8
|
+
export declare function hashContent(s: string): string;
|
|
9
|
+
export declare function readLedger(workspaceDir: string): PostedEntry[];
|
|
10
|
+
export declare function appendLedger(workspaceDir: string, entry: PostedEntry): void;
|
|
11
|
+
/** True if the newest post is younger than `minDays` before `now`. */
|
|
12
|
+
export declare function recentlyPosted(ledger: PostedEntry[], minDays: number, now: Date): boolean;
|
|
13
|
+
//# sourceMappingURL=journal-ledger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"journal-ledger.d.ts","sourceRoot":"","sources":["../src/journal-ledger.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAKF,yEAAyE;AACzE,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED,wBAAgB,UAAU,CAAC,YAAY,EAAE,MAAM,GAAG,WAAW,EAAE,CAO9D;AAED,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI,CAK3E;AAED,sEAAsE;AACtE,wBAAgB,cAAc,CAC5B,MAAM,EAAE,WAAW,EAAE,EACrB,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,IAAI,GACR,OAAO,CAKT"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** Posted-entry ledger + cadence/dedup for the P12 journal (issue #67). */
|
|
2
|
+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
const ledgerPath = (workspaceDir) => join(workspaceDir, ".otto", "journal", "posted.json");
|
|
6
|
+
/** Stable short content hash, used for dedup of near-identical notes. */
|
|
7
|
+
export function hashContent(s) {
|
|
8
|
+
return createHash("sha256").update(s.trim()).digest("hex").slice(0, 16);
|
|
9
|
+
}
|
|
10
|
+
export function readLedger(workspaceDir) {
|
|
11
|
+
try {
|
|
12
|
+
const raw = JSON.parse(readFileSync(ledgerPath(workspaceDir), "utf8"));
|
|
13
|
+
return Array.isArray(raw) ? raw : [];
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export function appendLedger(workspaceDir, entry) {
|
|
20
|
+
const path = ledgerPath(workspaceDir);
|
|
21
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
22
|
+
const next = [...readLedger(workspaceDir), entry];
|
|
23
|
+
writeFileSync(path, JSON.stringify(next, null, 2), "utf8");
|
|
24
|
+
}
|
|
25
|
+
/** True if the newest post is younger than `minDays` before `now`. */
|
|
26
|
+
export function recentlyPosted(ledger, minDays, now) {
|
|
27
|
+
if (ledger.length === 0 || minDays <= 0)
|
|
28
|
+
return false;
|
|
29
|
+
const newest = ledger.reduce((a, b) => (a.postedAt > b.postedAt ? a : b));
|
|
30
|
+
const ageMs = now.getTime() - new Date(newest.postedAt).getTime();
|
|
31
|
+
return ageMs < minDays * 24 * 60 * 60 * 1000;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=journal-ledger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"journal-ledger.js","sourceRoot":"","sources":["../src/journal-ledger.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAS1C,MAAM,UAAU,GAAG,CAAC,YAAoB,EAAU,EAAE,CAClD,IAAI,CAAC,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC;AAExD,yEAAyE;AACzE,MAAM,UAAU,WAAW,CAAC,CAAS;IACnC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAC1E,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,YAAoB;IAC7C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;QACvE,OAAO,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,GAAqB,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,YAAoB,EAAE,KAAkB;IACnE,MAAM,IAAI,GAAG,UAAU,CAAC,YAAY,CAAC,CAAC;IACtC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,MAAM,IAAI,GAAG,CAAC,GAAG,UAAU,CAAC,YAAY,CAAC,EAAE,KAAK,CAAC,CAAC;IAClD,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;AAC7D,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,cAAc,CAC5B,MAAqB,EACrB,OAAe,EACf,GAAS;IAET,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACtD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1E,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC;IAClE,OAAO,KAAK,GAAG,OAAO,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAC/C,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Select a journal-worthy memory record + derive its forbidden terms (#67 P12). */
|
|
2
|
+
import type { MemoryRecord } from "./memory.js";
|
|
3
|
+
/**
|
|
4
|
+
* Pick the best journal candidate: an ACTIVE record in a journal-worthy category
|
|
5
|
+
* that has not already been posted, ranked by confidence (ties → newer). Returns
|
|
6
|
+
* null when nothing qualifies.
|
|
7
|
+
*/
|
|
8
|
+
export declare function selectCandidate(records: MemoryRecord[], opts: {
|
|
9
|
+
categories: string[];
|
|
10
|
+
postedIds: Set<string>;
|
|
11
|
+
now: Date;
|
|
12
|
+
}): MemoryRecord | null;
|
|
13
|
+
/**
|
|
14
|
+
* Identifiers carried by a record that must not survive into a post: its
|
|
15
|
+
* taskKey, its sourceRun id, and the word-ish parts of its scope globs. Fed into
|
|
16
|
+
* the gate's `forbiddenTerms` so a leaked source term is denied at Gate 2.
|
|
17
|
+
*/
|
|
18
|
+
export declare function forbiddenTermsFor(record: MemoryRecord): string[];
|
|
19
|
+
//# sourceMappingURL=journal-source.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"journal-source.d.ts","sourceRoot":"","sources":["../src/journal-source.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,YAAY,EAAE,EACvB,IAAI,EAAE;IAAE,UAAU,EAAE,MAAM,EAAE,CAAC;IAAC,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAAC,GAAG,EAAE,IAAI,CAAA;CAAE,GAChE,YAAY,GAAG,IAAI,CAerB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,EAAE,CAUhE"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { memoryStatus } from "./memory.js";
|
|
2
|
+
/**
|
|
3
|
+
* Pick the best journal candidate: an ACTIVE record in a journal-worthy category
|
|
4
|
+
* that has not already been posted, ranked by confidence (ties → newer). Returns
|
|
5
|
+
* null when nothing qualifies.
|
|
6
|
+
*/
|
|
7
|
+
export function selectCandidate(records, opts) {
|
|
8
|
+
const eligible = records.filter((r) => memoryStatus(r, opts.now) === "active" &&
|
|
9
|
+
r.category != null &&
|
|
10
|
+
opts.categories.includes(r.category) &&
|
|
11
|
+
!opts.postedIds.has(r.id));
|
|
12
|
+
if (eligible.length === 0)
|
|
13
|
+
return null;
|
|
14
|
+
eligible.sort((a, b) => b.confidence !== a.confidence
|
|
15
|
+
? b.confidence - a.confidence
|
|
16
|
+
: b.createdAt.localeCompare(a.createdAt));
|
|
17
|
+
return eligible[0];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Identifiers carried by a record that must not survive into a post: its
|
|
21
|
+
* taskKey, its sourceRun id, and the word-ish parts of its scope globs. Fed into
|
|
22
|
+
* the gate's `forbiddenTerms` so a leaked source term is denied at Gate 2.
|
|
23
|
+
*/
|
|
24
|
+
export function forbiddenTermsFor(record) {
|
|
25
|
+
const terms = new Set();
|
|
26
|
+
if (record.taskKey)
|
|
27
|
+
terms.add(record.taskKey);
|
|
28
|
+
if (record.sourceRun)
|
|
29
|
+
terms.add(record.sourceRun);
|
|
30
|
+
for (const glob of record.scope) {
|
|
31
|
+
for (const part of glob.split(/[/*.\s]+/)) {
|
|
32
|
+
if (part.length >= 3)
|
|
33
|
+
terms.add(part);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return [...terms];
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=journal-source.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"journal-source.js","sourceRoot":"","sources":["../src/journal-source.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAC7B,OAAuB,EACvB,IAAiE;IAEjE,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAC7B,CAAC,CAAC,EAAE,EAAE,CACJ,YAAY,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,QAAQ;QACtC,CAAC,CAAC,QAAQ,IAAI,IAAI;QAClB,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC;QACpC,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAC5B,CAAC;IACF,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACvC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACrB,CAAC,CAAC,UAAU,KAAK,CAAC,CAAC,UAAU;QAC3B,CAAC,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU;QAC7B,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CAC3C,CAAC;IACF,OAAO,QAAQ,CAAC,CAAC,CAAC,CAAC;AACrB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAoB;IACpD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,IAAI,MAAM,CAAC,OAAO;QAAE,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC9C,IAAI,MAAM,CAAC,SAAS;QAAE,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAClD,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QAChC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;YAC1C,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC;gBAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC;AACpB,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { AgentRuntimeId } from "./agent-runtime.js";
|
|
2
|
+
export type JournalConfig = {
|
|
3
|
+
enabled: boolean;
|
|
4
|
+
autonomous: boolean;
|
|
5
|
+
categories: string[];
|
|
6
|
+
minDaysBetweenPosts: number;
|
|
7
|
+
};
|
|
8
|
+
export type JournalDeps = {
|
|
9
|
+
/** Rewrite a learning as a generic field note (or "SKIP"). */
|
|
10
|
+
generate: (learning: string) => Promise<string>;
|
|
11
|
+
/** Adversarial judge: true = SAFE to post. */
|
|
12
|
+
judge: (note: string) => Promise<boolean>;
|
|
13
|
+
/** Publish to Threads; absent ⇒ draft-only (default mode). */
|
|
14
|
+
publish?: (text: string) => Promise<{
|
|
15
|
+
id: string;
|
|
16
|
+
}>;
|
|
17
|
+
repoIdentifiers: string[];
|
|
18
|
+
secretPatterns: string[];
|
|
19
|
+
now: () => Date;
|
|
20
|
+
};
|
|
21
|
+
export type JournalAction = "disabled" | "no-candidate" | "skipped-cadence" | "denied" | "drafted" | "posted";
|
|
22
|
+
export type JournalOutcome = {
|
|
23
|
+
action: JournalAction;
|
|
24
|
+
reason?: string;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* The pure-ish orchestrator. Drafts by default; posts only when `autonomous`
|
|
28
|
+
* and a `publish` dep are both present AND every gate passes. Default-deny and
|
|
29
|
+
* never-throw are the invariants.
|
|
30
|
+
*/
|
|
31
|
+
export declare function runJournal(workspaceDir: string, config: JournalConfig, deps: JournalDeps): Promise<JournalOutcome>;
|
|
32
|
+
/**
|
|
33
|
+
* Harness run-end hook (issue #67 P12): read the per-repo journal config (no-op
|
|
34
|
+
* when absent/disabled), build the real generate/judge/publish deps from the
|
|
35
|
+
* agent runtime + Threads client, and run the journal. Never throws — the
|
|
36
|
+
* journal must never affect a run's outcome.
|
|
37
|
+
*/
|
|
38
|
+
export declare function maybeJournal(args: {
|
|
39
|
+
workspaceDir: string;
|
|
40
|
+
packageDir: string;
|
|
41
|
+
iteration: number;
|
|
42
|
+
maxRetries: number;
|
|
43
|
+
agentId: AgentRuntimeId;
|
|
44
|
+
signal?: AbortSignal;
|
|
45
|
+
}): Promise<JournalOutcome>;
|
|
46
|
+
//# sourceMappingURL=journal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"journal.d.ts","sourceRoot":"","sources":["../src/journal.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAQzD,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,mBAAmB,EAAE,MAAM,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,8DAA8D;IAC9D,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD,8CAA8C;IAC9C,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1C,8DAA8D;IAC9D,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpD,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,GAAG,EAAE,MAAM,IAAI,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,aAAa,GACrB,UAAU,GACV,cAAc,GACd,iBAAiB,GACjB,QAAQ,GACR,SAAS,GACT,QAAQ,CAAC;AACb,MAAM,MAAM,cAAc,GAAG;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAExE;;;;GAIG;AACH,wBAAsB,UAAU,CAC9B,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,aAAa,EACrB,IAAI,EAAE,WAAW,GAChB,OAAO,CAAC,cAAc,CAAC,CA0EzB;AA2BD;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE;IACvC,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,cAAc,CAAC;IACxB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,GAAG,OAAO,CAAC,cAAc,CAAC,CA8C1B"}
|
package/dist/journal.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* P12 journal orchestrator (issue #67): select a memory learning → generate a
|
|
3
|
+
* field note → run the default-deny secrecy gate → draft (default) or post
|
|
4
|
+
* (autonomous). All model/network I/O is injected; never throws.
|
|
5
|
+
*/
|
|
6
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
7
|
+
import { basename, join } from "node:path";
|
|
8
|
+
import { appendAudit, screenEntry } from "./journal-gate.js";
|
|
9
|
+
import { forbiddenTermsFor, selectCandidate } from "./journal-source.js";
|
|
10
|
+
import { appendLedger, hashContent, readLedger, recentlyPosted, } from "./journal-ledger.js";
|
|
11
|
+
import { readMemoryRecords } from "./memory.js";
|
|
12
|
+
import { git } from "./git.js";
|
|
13
|
+
import { readJournalConfig } from "./journal-config.js";
|
|
14
|
+
import { readSafetyPolicy } from "./safety-policy.js";
|
|
15
|
+
import { executeStage } from "./stage-exec.js";
|
|
16
|
+
import { STAGES } from "./stages.js";
|
|
17
|
+
import { createThreadsClient, resolveThreadsAuth } from "./threads-api.js";
|
|
18
|
+
/**
|
|
19
|
+
* The pure-ish orchestrator. Drafts by default; posts only when `autonomous`
|
|
20
|
+
* and a `publish` dep are both present AND every gate passes. Default-deny and
|
|
21
|
+
* never-throw are the invariants.
|
|
22
|
+
*/
|
|
23
|
+
export async function runJournal(workspaceDir, config, deps) {
|
|
24
|
+
try {
|
|
25
|
+
if (!config.enabled)
|
|
26
|
+
return { action: "disabled" };
|
|
27
|
+
const now = deps.now();
|
|
28
|
+
const ledger = readLedger(workspaceDir);
|
|
29
|
+
if (recentlyPosted(ledger, config.minDaysBetweenPosts, now)) {
|
|
30
|
+
return { action: "skipped-cadence" };
|
|
31
|
+
}
|
|
32
|
+
const postedIds = new Set(ledger.map((e) => e.memoryId));
|
|
33
|
+
const candidate = selectCandidate(readMemoryRecords(workspaceDir), {
|
|
34
|
+
categories: config.categories,
|
|
35
|
+
postedIds,
|
|
36
|
+
now,
|
|
37
|
+
});
|
|
38
|
+
if (!candidate)
|
|
39
|
+
return { action: "no-candidate" };
|
|
40
|
+
const note = (await deps.generate(candidate.content)).trim();
|
|
41
|
+
const ctx = {
|
|
42
|
+
repoIdentifiers: deps.repoIdentifiers,
|
|
43
|
+
secretPatterns: deps.secretPatterns,
|
|
44
|
+
forbiddenTerms: forbiddenTermsFor(candidate),
|
|
45
|
+
};
|
|
46
|
+
const verdict = note === "SKIP" || note === ""
|
|
47
|
+
? { ok: false, gate: 1, reason: "deny:generation-skip" }
|
|
48
|
+
: await screenEntry(note, ctx, deps.judge);
|
|
49
|
+
if (!verdict.ok) {
|
|
50
|
+
appendAudit(workspaceDir, {
|
|
51
|
+
at: now.toISOString(),
|
|
52
|
+
memoryId: candidate.id,
|
|
53
|
+
decision: "denied",
|
|
54
|
+
gate: verdict.gate,
|
|
55
|
+
reason: verdict.reason,
|
|
56
|
+
});
|
|
57
|
+
return { action: "denied", reason: verdict.reason };
|
|
58
|
+
}
|
|
59
|
+
// Passed every gate. Default mode drafts; autonomous posts.
|
|
60
|
+
if (!config.autonomous || !deps.publish) {
|
|
61
|
+
const dir = join(workspaceDir, ".otto", "journal", "drafts");
|
|
62
|
+
mkdirSync(dir, { recursive: true });
|
|
63
|
+
writeFileSync(join(dir, `${candidate.id}.md`), note + "\n", "utf8");
|
|
64
|
+
appendAudit(workspaceDir, {
|
|
65
|
+
at: now.toISOString(),
|
|
66
|
+
memoryId: candidate.id,
|
|
67
|
+
decision: "drafted",
|
|
68
|
+
});
|
|
69
|
+
return { action: "drafted" };
|
|
70
|
+
}
|
|
71
|
+
const { id } = await deps.publish(note);
|
|
72
|
+
appendLedger(workspaceDir, {
|
|
73
|
+
memoryId: candidate.id,
|
|
74
|
+
contentHash: hashContent(note),
|
|
75
|
+
postedAt: now.toISOString(),
|
|
76
|
+
postId: id,
|
|
77
|
+
});
|
|
78
|
+
appendAudit(workspaceDir, {
|
|
79
|
+
at: now.toISOString(),
|
|
80
|
+
memoryId: candidate.id,
|
|
81
|
+
decision: "posted",
|
|
82
|
+
postId: id,
|
|
83
|
+
});
|
|
84
|
+
return { action: "posted" };
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
appendAudit(workspaceDir, {
|
|
88
|
+
decision: "error",
|
|
89
|
+
reason: err.message,
|
|
90
|
+
});
|
|
91
|
+
return { action: "denied", reason: "error" };
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/** Collect this repo's own identifiers so the gate can deny self-references. */
|
|
95
|
+
function repoIdentifiers(workspaceDir) {
|
|
96
|
+
const ids = new Set([basename(workspaceDir)]);
|
|
97
|
+
const remote = git(["remote", "get-url", "origin"], workspaceDir);
|
|
98
|
+
if (remote) {
|
|
99
|
+
for (const part of remote.split(/[/:@.]/)) {
|
|
100
|
+
const p = part.trim();
|
|
101
|
+
if (p.length >= 3 &&
|
|
102
|
+
!["https", "http", "git", "com", "org", "www", "ssh"].includes(p.toLowerCase())) {
|
|
103
|
+
ids.add(p);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return [...ids];
|
|
108
|
+
}
|
|
109
|
+
/** The judge stage must emit exactly <journal-verdict>SAFE</journal-verdict>. */
|
|
110
|
+
function parseVerdict(result) {
|
|
111
|
+
return /<journal-verdict>\s*SAFE\s*<\/journal-verdict>/i.test(result);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Harness run-end hook (issue #67 P12): read the per-repo journal config (no-op
|
|
115
|
+
* when absent/disabled), build the real generate/judge/publish deps from the
|
|
116
|
+
* agent runtime + Threads client, and run the journal. Never throws — the
|
|
117
|
+
* journal must never affect a run's outcome.
|
|
118
|
+
*/
|
|
119
|
+
export async function maybeJournal(args) {
|
|
120
|
+
try {
|
|
121
|
+
const config = readJournalConfig(args.workspaceDir);
|
|
122
|
+
if (!config)
|
|
123
|
+
return { action: "disabled" };
|
|
124
|
+
const stageArgs = {
|
|
125
|
+
workspaceDir: args.workspaceDir,
|
|
126
|
+
packageDir: args.packageDir,
|
|
127
|
+
iteration: args.iteration,
|
|
128
|
+
maxRetries: args.maxRetries,
|
|
129
|
+
agentId: args.agentId,
|
|
130
|
+
signal: args.signal,
|
|
131
|
+
};
|
|
132
|
+
// Resolve a Threads client only for an autonomous, credentialed run.
|
|
133
|
+
const auth = config.autonomous ? resolveThreadsAuth() : null;
|
|
134
|
+
const client = auth ? createThreadsClient(auth) : undefined;
|
|
135
|
+
return await runJournal(args.workspaceDir, config, {
|
|
136
|
+
generate: async (learning) => (await executeStage({
|
|
137
|
+
...stageArgs,
|
|
138
|
+
stage: STAGES.journalWrite,
|
|
139
|
+
vars: { INPUTS: learning, RESUME: "" },
|
|
140
|
+
logLabel: "journal-write",
|
|
141
|
+
})).result,
|
|
142
|
+
judge: async (note) => parseVerdict((await executeStage({
|
|
143
|
+
...stageArgs,
|
|
144
|
+
stage: STAGES.journalScreen,
|
|
145
|
+
vars: { INPUTS: note, RESUME: "" },
|
|
146
|
+
logLabel: "journal-screen",
|
|
147
|
+
})).result),
|
|
148
|
+
publish: client ? (text) => client.publish(text) : undefined,
|
|
149
|
+
repoIdentifiers: repoIdentifiers(args.workspaceDir),
|
|
150
|
+
secretPatterns: readSafetyPolicy(args.workspaceDir).secretPatterns,
|
|
151
|
+
now: () => new Date(),
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return { action: "denied", reason: "hook-error" };
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=journal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"journal.js","sourceRoot":"","sources":["../src/journal.ts"],"names":[],"mappings":"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"}
|
package/dist/loop.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type AgentRuntimeId } from "./agent-runtime.js";
|
|
2
|
+
import { type TierLadder } from "./model-tier.js";
|
|
2
3
|
import type { Stage } from "./stages.js";
|
|
3
4
|
import { type TokenMode, type TokenUsage } from "./tokens.js";
|
|
4
5
|
import { nextActionFor } from "./next-action.js";
|
|
@@ -40,6 +41,18 @@ export type LoopOptions = {
|
|
|
40
41
|
/** Injectable resolver for an iteration's changed paths (default: git diff since
|
|
41
42
|
* the iteration-start HEAD). Used only when `adaptiveRouter` is on. */
|
|
42
43
|
resolveChangedPaths?: (workspaceDir: string) => string[];
|
|
44
|
+
/** Opt-in per-stage model routing (issue #66 P11): route each stage to a model
|
|
45
|
+
* tier by difficulty + change risk, escalating on repeated failure. A pinned
|
|
46
|
+
* model overrides it. When off, every stage uses the runtime default model. */
|
|
47
|
+
modelRouting?: boolean;
|
|
48
|
+
/** tier → model ladder consulted when `modelRouting` resolves a tier. */
|
|
49
|
+
tierLadder?: TierLadder;
|
|
50
|
+
/** Opt-in sub-agent fan-out (issue #66 P11): on the first iteration, run the
|
|
51
|
+
* independent tasks of a `.otto/tasks/<key>/tasks.json` as isolated worktree
|
|
52
|
+
* sub-agents before the sequential loop. No valid tasks.json → no-op. */
|
|
53
|
+
fanOut?: boolean;
|
|
54
|
+
/** Max concurrent sub-agents per fan-out wave (default 3). */
|
|
55
|
+
fanOutConcurrency?: number;
|
|
43
56
|
/** Injected AbortSignal for daemon callers (e.g. watch mode). When provided,
|
|
44
57
|
* runLoop skips wake-lock acquisition and process signal handler installation;
|
|
45
58
|
* the caller owns both. */
|
package/dist/loop.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../src/loop.ts"],"names":[],"mappings":"AAGA,OAAO,EAAuB,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../src/loop.ts"],"names":[],"mappings":"AAGA,OAAO,EAAuB,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAa9E,OAAO,EAAqB,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAuCrE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,UAAU,EAChB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,CAAC;AAoBzB,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA0B3D;AAYD,MAAM,MAAM,WAAW,GAAG;IAGxB,MAAM,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,2CAA2C;IAC3C,YAAY,EAAE,MAAM,CAAC;IACrB,kGAAkG;IAClG,UAAU,EAAE,MAAM,CAAC;IACnB,gEAAgE;IAChE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sFAAsF;IACtF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,mEAAmE;IACnE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gEAAgE;IAChE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qDAAqD;IACrD,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,mHAAmH;IACnH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB;0FACsF;IACtF,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;6EAEyE;IACzE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;4EACwE;IACxE,mBAAmB,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;IACzD;;oFAEgF;IAChF,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,yEAAyE;IACzE,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB;;8EAE0E;IAC1E,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,8DAA8D;IAC9D,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;gCAE4B;IAC5B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,8EAA8E;IAC9E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;wCACoC;IACpC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,sFAAsF;IACtF,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,iHAAiH;IACjH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;wEACoE;IACpE,eAAe,CAAC,EAAE,cAAc,CAAC;IACjC,gFAAgF;IAChF,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC;uFACmF;IACnF,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;yDAEqD;IACrD,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,UAAU,CAAC;CACxB,CAAC;AAqFF,wBAAsB,OAAO,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CA6zBrE"}
|