baychat 0.24.0 → 0.25.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +98 -15
- package/dist/api.js +19 -2
- package/dist/claude-context-flush.js +219 -0
- package/dist/claude-context-state.js +191 -0
- package/dist/claude-context.js +311 -0
- package/dist/claude-settings.js +503 -0
- package/dist/commands.js +4 -3
- package/dist/context-hooks-command.js +165 -0
- package/dist/context-report.js +307 -0
- package/dist/help-topics.js +2 -23
- package/dist/index.js +26 -499
- package/dist/main.js +512 -0
- package/dist/mcp-notes.js +110 -0
- package/dist/mcp.js +84 -5
- package/dist/protocol-content.js +1 -1
- package/dist/relay/acp/client.js +18 -0
- package/dist/relay/acp/prompt.js +54 -3
- package/dist/relay/acp/runner.js +100 -2
- package/dist/relay/acp/turn-queue.js +99 -3
- package/dist/relay/codex-context.js +242 -0
- package/dist/relay/compact-command.js +63 -0
- package/dist/relay/compact-focus.attacks.js +36 -0
- package/dist/relay/compact-focus.js +64 -0
- package/dist/relay/context-notice.js +144 -0
- package/dist/relay/daemon.js +287 -28
- package/dist/relay/fresh-start-verify.js +237 -0
- package/dist/relay/fresh-start.js +141 -0
- package/dist/relay/message-format.js +34 -15
- package/dist/relay/project-brief.js +195 -0
- package/dist/relay/project-notes.js +300 -0
- package/dist/relay/resume.js +25 -0
- package/dist/relay/session-commands.js +2 -4
- package/dist/relay/terminal-text.js +13 -0
- package/dist/relay/updates.js +42 -7
- package/dist/relay/ws.js +34 -2
- package/dist/runtimes.js +11 -0
- package/dist/self-upgrade.js +229 -0
- package/dist/tool-defs.js +93 -0
- package/dist/update-command.js +54 -29
- package/package.json +1 -1
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.COMPACT_COMMAND = exports.sanitizeCompactFocus = exports.COMPACT_FOCUS_MAX = exports.COMPACT_TRIGGER = void 0;
|
|
4
|
+
exports.parseCompactCommand = parseCompactCommand;
|
|
5
|
+
exports.compactKeys = compactKeys;
|
|
6
|
+
const compact_focus_1 = require("./compact-focus");
|
|
7
|
+
Object.defineProperty(exports, "COMPACT_FOCUS_MAX", { enumerable: true, get: function () { return compact_focus_1.COMPACT_FOCUS_MAX; } });
|
|
8
|
+
Object.defineProperty(exports, "sanitizeCompactFocus", { enumerable: true, get: function () { return compact_focus_1.sanitizeCompactFocus; } });
|
|
9
|
+
/**
|
|
10
|
+
* `/compact [focus]` — tidy a session's context now, optionally saying what to keep.
|
|
11
|
+
*
|
|
12
|
+
* ── 🔒 THE ONE COMMAND WITH AN ARGUMENT, AND WHY IT IS SAFE TO HAVE ONE ─────────────────
|
|
13
|
+
*
|
|
14
|
+
* Every other session command obeys "the wire carries a command NAME, never keystrokes"
|
|
15
|
+
* (session-commands.ts). This one carries one line of text, because Claude Code's own
|
|
16
|
+
* `/compact` takes instructions ("/compact keep the API decisions") and a tidy without them
|
|
17
|
+
* throws away what the person cared about. So the text is allowed in, and caged — the cage is
|
|
18
|
+
* `compact-focus.ts`, a twin the API applies too when the app asks for a tidy — and it is typed
|
|
19
|
+
* LITERALLY (`send-keys -l`, via `typeIntoPane`, which refuses `^`, `\` and control characters
|
|
20
|
+
* again on its own). Anything outside the cage is refused, by reason, and nothing is typed.
|
|
21
|
+
* Only Claude Code takes a focus; Codex's `/compact` takes none (checked against codex-cli
|
|
22
|
+
* 0.154.0's command list), so a focus sent to Codex is refused rather than silently dropped.
|
|
23
|
+
*/
|
|
24
|
+
exports.COMPACT_TRIGGER = "/compact";
|
|
25
|
+
exports.COMPACT_COMMAND = {
|
|
26
|
+
id: "compact",
|
|
27
|
+
trigger: exports.COMPACT_TRIGGER,
|
|
28
|
+
summary: `Tidy this session's context now (Claude Code, Codex). Claude Code takes a one-line focus: /compact keep the API decisions — letters, numbers, spaces and ${compact_focus_1.COMPACT_FOCUS_PUNCTUATION}, up to ${compact_focus_1.COMPACT_FOCUS_MAX} characters.`,
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* `/compact` or `/compact <focus>` → the parse; anything else → undefined (it is chat).
|
|
32
|
+
*
|
|
33
|
+
* The command word is matched exactly and case-insensitively, as the other commands are:
|
|
34
|
+
* "can you /compact please" and "/compacting" are sentences, not commands.
|
|
35
|
+
*/
|
|
36
|
+
function parseCompactCommand(text) {
|
|
37
|
+
const trimmed = text.trimStart();
|
|
38
|
+
if (trimmed.slice(0, exports.COMPACT_TRIGGER.length).toLowerCase() !== exports.COMPACT_TRIGGER)
|
|
39
|
+
return undefined;
|
|
40
|
+
// Trailing whitespace only: a SECOND line has non-space after its newline, so it survives
|
|
41
|
+
// this and is refused below.
|
|
42
|
+
const tail = trimmed.slice(exports.COMPACT_TRIGGER.length).trimEnd();
|
|
43
|
+
if (tail === "")
|
|
44
|
+
return { kind: "compact" };
|
|
45
|
+
// "/compacting" is a word, not the command: a separator must follow the trigger.
|
|
46
|
+
if (!/^\s/u.test(tail))
|
|
47
|
+
return undefined;
|
|
48
|
+
const checked = (0, compact_focus_1.sanitizeCompactFocus)(tail);
|
|
49
|
+
if (!checked.ok)
|
|
50
|
+
return { kind: "refused", reason: `/compact was not run: ${checked.reason}` };
|
|
51
|
+
return checked.focus ? { kind: "compact", focus: checked.focus } : { kind: "compact" };
|
|
52
|
+
}
|
|
53
|
+
/** What to type for this runtime, or why nothing can be. */
|
|
54
|
+
function compactKeys(runtime, focus) {
|
|
55
|
+
if (runtime === "claude")
|
|
56
|
+
return { ok: true, keys: focus ? `${exports.COMPACT_TRIGGER} ${focus}` : exports.COMPACT_TRIGGER };
|
|
57
|
+
if (runtime === "codex") {
|
|
58
|
+
return focus
|
|
59
|
+
? { ok: false, reason: "Codex's /compact takes no focus. Send /compact on its own to tidy it." }
|
|
60
|
+
: { ok: true, keys: exports.COMPACT_TRIGGER };
|
|
61
|
+
}
|
|
62
|
+
return { ok: false, reason: `/compact is only for Claude Code and Codex sessions; this one runs ${runtime}.` };
|
|
63
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.COMPACT_FOCUS_ATTACKS = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Focus texts BOTH the relay and the API must refuse — the injection attempts the cage in
|
|
6
|
+
* `compact-focus.ts` exists for. Each is what follows `/compact` in a message (a leading space
|
|
7
|
+
* included where a person would type one). A twin, byte-identical in
|
|
8
|
+
* `packages/cli/src/relay/` and `apps/api/src/lib/`; each side runs every entry through its own
|
|
9
|
+
* copy of `sanitizeCompactFocus`, and the parity test holds the two files together.
|
|
10
|
+
*
|
|
11
|
+
* It imports nothing (the two packages resolve imports differently), so the refusal codes are
|
|
12
|
+
* spelled out here; they are `CompactFocusRefusal`'s, checked by the tests that use them.
|
|
13
|
+
*/
|
|
14
|
+
exports.COMPACT_FOCUS_ATTACKS = [
|
|
15
|
+
{ focus: " keep this\n/clear", code: "MULTILINE", reason: /single line/ },
|
|
16
|
+
{ focus: " keep this\r\nrm -rf ~", code: "MULTILINE", reason: /single line/ },
|
|
17
|
+
{ focus: "\nrm -rf ~", code: "MULTILINE", reason: /single line/ },
|
|
18
|
+
{ focus: " keep\u2028this", code: "MULTILINE", reason: /single line/ },
|
|
19
|
+
{ focus: " keep\u2029this", code: "MULTILINE", reason: /single line/ },
|
|
20
|
+
{ focus: " \u001b[2J\u001b[31mred", code: "CONTROL", reason: /control or invisible/ },
|
|
21
|
+
{ focus: " keep\tthis", code: "CONTROL", reason: /control or invisible/ },
|
|
22
|
+
{ focus: " a\u0003b", code: "CONTROL", reason: /control or invisible/ },
|
|
23
|
+
{ focus: " safe\u202Eesrever", code: "CONTROL", reason: /control or invisible/ },
|
|
24
|
+
{ focus: " zero\u200Bwidth", code: "CONTROL", reason: /control or invisible/ },
|
|
25
|
+
{ focus: " $(rm -rf ~)", code: "CHARACTER", reason: /"\$" is not allowed/ },
|
|
26
|
+
{ focus: " `id`", code: "CHARACTER", reason: /"`" is not allowed/ },
|
|
27
|
+
{ focus: " keep ^C", code: "CHARACTER", reason: /"\^" is not allowed/ },
|
|
28
|
+
{ focus: " back\\slash", code: "CHARACTER", reason: /"\\" is not allowed/ },
|
|
29
|
+
{ focus: " read @~/.ssh/id_rsa", code: "CHARACTER", reason: /"@" is not allowed/ },
|
|
30
|
+
{ focus: " a | b", code: "CHARACTER", reason: /"\|" is not allowed/ },
|
|
31
|
+
{ focus: " a > /etc/passwd", code: "CHARACTER", reason: /">" is not allowed/ },
|
|
32
|
+
{ focus: " #memory", code: "CHARACTER", reason: /"#" is not allowed/ },
|
|
33
|
+
{ focus: " {x}", code: "CHARACTER", reason: /"\{" is not allowed/ },
|
|
34
|
+
{ focus: " /clear", code: "LEADING_SLASH", reason: /cannot start with \// },
|
|
35
|
+
{ focus: ` ${"a".repeat(201)}`, code: "TOO_LONG", reason: /longer than 200/ },
|
|
36
|
+
];
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The cage a `/compact` focus goes through before anyone types it into a terminal.
|
|
4
|
+
*
|
|
5
|
+
* ── A TWIN, BYTE-IDENTICAL IN TWO PLACES ──────────────────────────────────────────────
|
|
6
|
+
*
|
|
7
|
+
* packages/cli/src/relay/compact-focus.ts — the relay, which TYPES the focus (send-keys -l)
|
|
8
|
+
* apps/api/src/lib/compact-focus.ts — the API, which refuses a bad focus at the door
|
|
9
|
+
* ("tidy up now" from the app)
|
|
10
|
+
*
|
|
11
|
+
* The API's check is not the safety: the relay re-checks everything it types, whoever sent
|
|
12
|
+
* it. It exists so the app can say WHY at once instead of the relay saying it in the chat
|
|
13
|
+
* later — and a server that judged a focus differently from the relay would accept what the
|
|
14
|
+
* relay then refuses, or refuse what it would type. So there is one rule, in one file, copied
|
|
15
|
+
* verbatim (it imports nothing) and held together by `apps/api/src/lib/compact-focus.parity.test.ts`.
|
|
16
|
+
* The attack strings both sides must refuse live beside it, twinned the same way
|
|
17
|
+
* (`compact-focus.attacks.ts`). Change one copy, change both.
|
|
18
|
+
*
|
|
19
|
+
* ── THE RULES ─────────────────────────────────────────────────────────────────────────
|
|
20
|
+
*
|
|
21
|
+
* - ONE line: no CR, LF, or Unicode line/paragraph separators. A newline would be Enter,
|
|
22
|
+
* and Enter followed by anything is a second command typed into someone's terminal.
|
|
23
|
+
* - PRINTABLE only: no control, format or invisible characters (`\p{C}` — escape
|
|
24
|
+
* sequences, tabs, zero-width joiners, right-to-left overrides that make a line read
|
|
25
|
+
* differently from what it types).
|
|
26
|
+
* - A SMALL ALPHABET: letters, marks and digits of any language, spaces, and
|
|
27
|
+
* . , ; : ' " ( ) - _ / ? ! + & % =. No `@` (Claude Code opens a file picker on it, and
|
|
28
|
+
* Enter would pick a file instead of submitting), no `\` or `^` (screen's `stuff` parses
|
|
29
|
+
* them), no `$` or backtick, no `#`, `<`, `>`, `|`, braces or brackets.
|
|
30
|
+
* - 200 characters at most, after runs of spaces are collapsed to one.
|
|
31
|
+
* - It cannot start with `/`, so the line can only ever be `/compact <words>`.
|
|
32
|
+
*/
|
|
33
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
34
|
+
exports.COMPACT_FOCUS_PUNCTUATION = exports.COMPACT_FOCUS_MAX = void 0;
|
|
35
|
+
exports.sanitizeCompactFocus = sanitizeCompactFocus;
|
|
36
|
+
exports.COMPACT_FOCUS_MAX = 200;
|
|
37
|
+
exports.COMPACT_FOCUS_PUNCTUATION = `. , ; : ' " ( ) - _ / ? ! + & % =`;
|
|
38
|
+
const FOCUS_ALPHABET = /^[\p{L}\p{M}\p{N} .,;:'"()\-_/?!+&%=]+$/u;
|
|
39
|
+
/** Is this focus text safe to type? Returns it normalised (undefined when blank), or why not. */
|
|
40
|
+
function sanitizeCompactFocus(raw) {
|
|
41
|
+
if (/[\r\n\u2028\u2029]/.test(raw))
|
|
42
|
+
return { ok: false, code: "MULTILINE", reason: "the focus must be a single line." };
|
|
43
|
+
if (/\p{C}/u.test(raw)) {
|
|
44
|
+
return { ok: false, code: "CONTROL", reason: "the focus contains control or invisible characters." };
|
|
45
|
+
}
|
|
46
|
+
const focus = raw.replace(/\p{Zs}+/gu, " ").trim();
|
|
47
|
+
if (!focus)
|
|
48
|
+
return { ok: true };
|
|
49
|
+
if ([...focus].length > exports.COMPACT_FOCUS_MAX) {
|
|
50
|
+
return { ok: false, code: "TOO_LONG", reason: `the focus is longer than ${exports.COMPACT_FOCUS_MAX} characters.` };
|
|
51
|
+
}
|
|
52
|
+
if (focus.startsWith("/"))
|
|
53
|
+
return { ok: false, code: "LEADING_SLASH", reason: "the focus cannot start with /." };
|
|
54
|
+
if (!FOCUS_ALPHABET.test(focus)) {
|
|
55
|
+
const bad = [...focus].find((ch) => !FOCUS_ALPHABET.test(ch));
|
|
56
|
+
return {
|
|
57
|
+
ok: false,
|
|
58
|
+
code: "CHARACTER",
|
|
59
|
+
reason: `the focus may only use letters, numbers, spaces and ${exports.COMPACT_FOCUS_PUNCTUATION}${bad ? ` — "${bad}" is not allowed` : ""}.`,
|
|
60
|
+
...(bad ? { character: bad } : {}),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
return { ok: true, focus };
|
|
64
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.CarriedNotices = void 0;
|
|
37
|
+
exports.contextNoticeFrom = contextNoticeFrom;
|
|
38
|
+
exports.formatContextNotice = formatContextNotice;
|
|
39
|
+
exports.laterNotice = laterNotice;
|
|
40
|
+
exports.carryNotice = carryNotice;
|
|
41
|
+
exports.carriedNoticesPath = carriedNoticesPath;
|
|
42
|
+
const fs = __importStar(require("fs"));
|
|
43
|
+
const path = __importStar(require("path"));
|
|
44
|
+
const config_1 = require("../config");
|
|
45
|
+
const terminal_text_1 = require("./terminal-text");
|
|
46
|
+
/** The server's `notice` beside an event, parsed leniently; undefined when absent or unusable. */
|
|
47
|
+
function contextNoticeFrom(raw) {
|
|
48
|
+
if (!raw || typeof raw !== "object")
|
|
49
|
+
return undefined;
|
|
50
|
+
const { kind, percent, text } = raw;
|
|
51
|
+
if (kind !== "context_high" || typeof percent !== "number" || !Number.isFinite(percent) || typeof text !== "string")
|
|
52
|
+
return undefined;
|
|
53
|
+
return { kind, percent: Math.max(0, Math.min(100, Math.round(percent))), text };
|
|
54
|
+
}
|
|
55
|
+
function oneLine(text) {
|
|
56
|
+
return (0, terminal_text_1.cleanTerminalText)(text)
|
|
57
|
+
.replace(/[\u0080-\u009f--]/g, "")
|
|
58
|
+
.replace(/\s+/g, " ")
|
|
59
|
+
.trim();
|
|
60
|
+
}
|
|
61
|
+
/** The block in front of the message, as lines (no trailing newline). */
|
|
62
|
+
function formatContextNotice(notice) {
|
|
63
|
+
return [`BayChat notice (memory ${notice.percent}% full): ${oneLine(notice.text)}`, "---"].join("\n");
|
|
64
|
+
}
|
|
65
|
+
/** The newer of two notices (a later crossing replaces an earlier one). */
|
|
66
|
+
function laterNotice(a, b) {
|
|
67
|
+
return b ?? a;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Hand a notice whose own message will not reach the session to the first of `later` (any chat:
|
|
71
|
+
* the notice is about the session, not a room).
|
|
72
|
+
*
|
|
73
|
+
* @returns true when it found a home.
|
|
74
|
+
*/
|
|
75
|
+
function carryNotice(notice, later) {
|
|
76
|
+
const next = later[0];
|
|
77
|
+
if (!next)
|
|
78
|
+
return false;
|
|
79
|
+
next.notice = laterNotice(notice, next.notice);
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
function carriedNoticesPath() {
|
|
83
|
+
return path.join((0, config_1.configDir)(), "carried-notices.json");
|
|
84
|
+
}
|
|
85
|
+
/** Notices kept for a session until its next message goes out. ON DISK, 0600, like the carried briefs. */
|
|
86
|
+
class CarriedNotices {
|
|
87
|
+
file;
|
|
88
|
+
held;
|
|
89
|
+
constructor(file = carriedNoticesPath()) {
|
|
90
|
+
this.file = file;
|
|
91
|
+
}
|
|
92
|
+
keep(session, notice) {
|
|
93
|
+
this.load().set(session, notice);
|
|
94
|
+
this.save();
|
|
95
|
+
}
|
|
96
|
+
/** Put a kept notice on the first message of this batch (mutates the batch). */
|
|
97
|
+
apply(session, batch) {
|
|
98
|
+
const kept = this.load().get(session);
|
|
99
|
+
if (!kept || batch.length === 0)
|
|
100
|
+
return;
|
|
101
|
+
this.held.delete(session);
|
|
102
|
+
batch[0].notice = laterNotice(kept, batch[0].notice);
|
|
103
|
+
this.save();
|
|
104
|
+
}
|
|
105
|
+
/** Forget it: the session started fresh or ended, and the notice was about its old memory. */
|
|
106
|
+
forget(session) {
|
|
107
|
+
if (this.load().delete(session))
|
|
108
|
+
this.save();
|
|
109
|
+
}
|
|
110
|
+
load() {
|
|
111
|
+
if (this.held)
|
|
112
|
+
return this.held;
|
|
113
|
+
this.held = new Map();
|
|
114
|
+
let raw;
|
|
115
|
+
try {
|
|
116
|
+
raw = JSON.parse(fs.readFileSync(this.file, "utf8"));
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
if (err.code !== "ENOENT") {
|
|
120
|
+
process.stderr.write(`baychat relay: could not read carried notices — ${err instanceof Error ? err.message : String(err)}\n`);
|
|
121
|
+
}
|
|
122
|
+
return this.held;
|
|
123
|
+
}
|
|
124
|
+
if (raw && typeof raw === "object") {
|
|
125
|
+
for (const [session, value] of Object.entries(raw)) {
|
|
126
|
+
const notice = contextNoticeFrom(value);
|
|
127
|
+
if (notice)
|
|
128
|
+
this.held.set(session, notice);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return this.held;
|
|
132
|
+
}
|
|
133
|
+
save() {
|
|
134
|
+
try {
|
|
135
|
+
fs.mkdirSync(path.dirname(this.file), { recursive: true });
|
|
136
|
+
fs.writeFileSync(this.file, JSON.stringify(Object.fromEntries(this.held ?? new Map())), { mode: 0o600 });
|
|
137
|
+
fs.chmodSync(this.file, 0o600);
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
process.stderr.write(`baychat relay: could not persist carried notices — ${err instanceof Error ? err.message : String(err)}\n`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
exports.CarriedNotices = CarriedNotices;
|