@zenera/neo 1.1.21 → 1.1.23

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.
@@ -1,2 +1,2 @@
1
- export declare const CSS = "\n:root {\n --bg: #0f1115; --panel: #161922; --line: #262b36; --fg: #d7dbe3; --dim: #8a92a6;\n --accent: #6ea8fe; --ok: #4ec9a0; --err: #f0776c; --warn: #e2b341; --mono: ui-monospace, SFMono-Regular, Menlo, monospace;\n}\n* { box-sizing: border-box; }\nbody { margin: 0; background: var(--bg); color: var(--fg);\n font: 14px/1.5 system-ui, -apple-system, Segoe UI, sans-serif; }\nheader { padding: 12px 16px; border-bottom: 1px solid var(--line); background: var(--panel);\n display: flex; flex-wrap: wrap; gap: 8px 20px; align-items: baseline; }\nheader h1 { font-size: 15px; margin: 0 12px 0 0; font-weight: 600; }\nheader .kv { color: var(--dim); font-size: 12px; }\nheader .kv b { color: var(--fg); font-weight: 600; }\nmain { display: grid; grid-template-columns: 340px 1fr; height: calc(100vh - 49px); }\naside { border-right: 1px solid var(--line); overflow: auto; background: var(--panel); }\n.pad { padding: 10px; position: sticky; top: 0; background: var(--panel); border-bottom: 1px solid var(--line); }\n#filter { width: 100%; padding: 6px 8px; background: var(--bg); color: var(--fg);\n border: 1px solid var(--line); border-radius: 6px; }\n#timeline { list-style: none; margin: 0; padding: 4px 0; }\n#timeline li { padding: 6px 10px; border-left: 3px solid transparent; cursor: pointer; }\n#timeline li:hover { background: #1c2130; }\n#timeline li.sel { background: #1f2637; border-left-color: var(--accent); }\n#timeline li.covered { opacity: .45; }\n#timeline .row { display: flex; gap: 6px; align-items: baseline; }\n#timeline .t { color: var(--fg); font: 11px var(--mono); min-width: 38px; text-align: right; }\n#timeline .t.same { color: #3d4453; }\n#timeline .dur { margin-left: auto; color: var(--dim); font: 11px var(--mono); }\n#timeline .idx { color: var(--dim); font: 11px var(--mono); min-width: 22px; }\n#timeline .prev { color: var(--dim); font-size: 11px; white-space: nowrap;\n overflow: hidden; text-overflow: ellipsis; padding-left: 44px; }\n/* Branch histories are contiguous in this list, so a coloured edge plus a\n header is enough to stop ten parallel branches reading as one long run. */\n#timeline li.sep { cursor: default; padding: 10px 10px 4px; display: flex; gap: 7px;\n align-items: center; font-size: 11px; letter-spacing: .06em; text-transform: uppercase;\n color: var(--dim); border-left-color: transparent; }\n#timeline li.sep:hover { background: none; }\n#timeline li.sep .dot { width: 8px; height: 8px; border-radius: 2px;\n background: var(--bc, var(--dim)); }\n/* The group header shouts in uppercase; its elapsed time should not. */\n#timeline li.sep .dur { text-transform: none; letter-spacing: 0; }\n/* The branch the join actually waited for. */\n#timeline li.sep.slow { background: #232a38; color: var(--fg); }\n#timeline li.sep.slow .dur { color: var(--fg); }\n#timeline li.branch { padding-left: 20px; border-left-color: var(--bc, var(--line)); }\n#timeline li.branch.sel { border-left-color: var(--bc, var(--accent)); }\n#middle { display: flex; flex-direction: column; overflow: hidden; }\n.tabs { display: flex; gap: 4px; padding: 8px 12px; border-bottom: 1px solid var(--line); }\n.tabs button { background: none; border: 1px solid transparent; color: var(--dim);\n padding: 4px 12px; border-radius: 6px; cursor: pointer; font-size: 13px; }\n.tabs button.on { color: var(--fg); background: var(--panel); border-color: var(--line); }\n#detail { overflow: auto; padding: 16px; flex: 1; }\n#stats { overflow: auto; padding: 16px; flex: 1; }\ntable.st { width: 100%; border-collapse: collapse; font: 12px var(--mono); }\ntable.st th { text-align: right; color: var(--dim); font-weight: 600; padding: 6px 12px;\n border-bottom: 1px solid var(--line); white-space: nowrap; }\ntable.st td { text-align: right; padding: 5px 12px; border-bottom: 1px solid #1d2230;\n white-space: nowrap; }\ntable.st th:first-child, table.st td:first-child { text-align: left; }\ntable.st tbody tr:hover { background: #1c2130; }\ntable.st tbody tr:last-child td { border-bottom: none; }\n/* The total is a different kind of row, not one more of the same: it gets its\n own band so nobody reads it as another model or another agent. */\ntable.st tfoot td { color: var(--fg); font-weight: 600; background: #191e2a;\n border-top: 1px solid #2b3346; border-bottom: none; padding: 7px 12px; }\ntable.st tfoot td:first-child { color: var(--dim); text-transform: uppercase;\n letter-spacing: .07em; font-size: 11px; }\n/* Two panes hold a zoomable diagram (the run graph and the architecture), so\n everything about one is written for both. */\n.pane { display: flex; flex-direction: column; overflow: hidden; flex: 1; }\n/* An explicit display on the pane would otherwise beat the hidden attribute\n and both views would show at once. */\n.pane[hidden], #detail[hidden], #stats[hidden] { display: none; }\n.pane svg { max-width: none; display: block; }\n.pane .node { cursor: pointer; }\n/* A node with nothing behind it in this run is not clickable, and says so. */\n.pane .node.idle { cursor: default; }\n.gtools { display: flex; align-items: center; gap: 6px; padding: 6px 10px;\n border-bottom: 1px solid var(--line); background: var(--panel); }\n.gtools button { background: var(--bg); border: 1px solid var(--line); color: var(--fg);\n border-radius: 6px; padding: 2px 10px; cursor: pointer; font: 12px var(--mono); }\n.gtools button:hover { border-color: var(--accent); }\n.gtools .hint:first-of-type { margin-left: 8px; }\n.gtools .lvl { margin-left: auto; font-family: var(--mono); }\n.gtools label.check { display: flex; align-items: center; gap: 5px; margin-left: 8px;\n color: var(--dim); font-size: 12px; cursor: pointer; user-select: none; }\n/* The memory view reads left to right: the graph is the index, the panel is\n the page. Putting the body under a tab of its own would mean losing the\n picture every time you asked what a node actually says. */\n#memory .split { display: flex; flex: 1; overflow: hidden; }\n#mdetail { width: 380px; flex: none; padding: 16px; overflow: auto;\n border-right: none; border-left: 1px solid var(--line); }\n#mdetail .dt { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 0 0 10px; }\n#mdetail .idrow { display: flex; gap: 8px; align-items: center; margin: 0 0 14px; }\n#mdetail .idrow code { flex: 1; min-width: 0; font: 12px var(--mono); color: var(--fg);\n background: var(--bg); border: 1px solid var(--line); border-radius: 6px;\n padding: 4px 8px; word-break: break-all; }\n#mdetail .idrow button { flex: 0 0 auto; background: var(--bg); border: 1px solid var(--line);\n color: var(--dim); border-radius: 6px; padding: 4px 9px; font: 11px var(--mono);\n cursor: pointer; }\n#mdetail .idrow button:hover { border-color: var(--accent); color: var(--fg); }\n#mdetail dl.props { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 5px 14px;\n margin: 0 0 14px; font: 12px var(--mono); }\n#mdetail dl.props dt { color: var(--dim); }\n#mdetail dl.props dd { margin: 0; word-break: break-word; }\n/* A block that shares the panel's own background has no edges to show. */\n#mdetail section.blk { background: var(--bg); }\n#mdetail ul.links { list-style: none; margin: 0; padding: 0; }\n#mdetail ul.links li { display: grid; grid-template-columns: 96px auto minmax(0, 1fr);\n gap: 8px; align-items: baseline; padding: 6px 12px; }\n#mdetail ul.links li + li { border-top: 1px solid var(--line); }\n#mdetail ul.links .rel { font: 11px var(--mono); color: var(--warn); white-space: nowrap; }\n#mdetail button.link { font: 12px/1.4 system-ui, sans-serif; padding: 0; text-align: left;\n overflow-wrap: anywhere; }\n#mdetail button.link:hover { text-decoration: underline; }\n#mcount .ok { color: var(--ok); }\n#mcount .bad { color: var(--err); }\n/* The kind palette is the memory page's, so a node reads the same in both. */\n.kind { font-size: 11px; letter-spacing: .04em; text-transform: uppercase;\n padding: 1px 6px; border-radius: 4px; background: #232a38; color: var(--dim); }\n.kind.task { background: #1e3357; color: #9dc1ff; }\n.kind.plan { background: #3a2450; color: #d1a6ff; }\n.kind.fact { background: #14392c; color: #8ee0c2; }\n.kind.snippet { background: #1e3d33; color: #8ee0c2; }\n.kind.file { background: #40331a; color: #f0dfb4; }\n.kind.operation { background: #3d2a1b; color: #f2d9c2; }\n.kind.preference { background: #3d1f2c; color: #f5b8cd; }\n/* Selection is a class rather than attributes written onto the shapes: the\n previous pick has to lose its outline without a redraw. White, because the\n accent already means \"the recall matched this one\". */\n#memory .node.picked rect, #memory .node.picked polygon,\n#memory .node.picked path, #memory .node.picked circle {\n stroke: var(--fg) !important; stroke-width: 4px !important; }\n/* The canvas is transformed, not scrolled: panning has to work past the edges\n of the diagram, which overflow:auto would forbid. */\n.viewport { flex: 1; overflow: hidden; position: relative; cursor: grab; touch-action: none;\n user-select: none; -webkit-user-select: none; }\n.viewport.drag { cursor: grabbing; }\n.canvas { position: absolute; top: 0; left: 0; transform-origin: 0 0; }\n.hint { color: var(--dim); font-size: 12px; }\n.hint.pad { padding: 16px; }\n.type { font-size: 11px; letter-spacing: .04em; text-transform: uppercase;\n padding: 1px 6px; border-radius: 4px; background: #232a38; color: var(--dim); }\n.type.llm_call { background: #1e3357; color: #9dc1ff; }\n.type.tool_call, .type.tool_result { background: #1e3d33; color: #8ee0c2; }\n.type.compaction { background: #40331a; color: var(--warn); }\n.type.fork, .type.join { background: #3a2450; color: #d1a6ff; }\n.type.final_output { background: #14392c; color: var(--ok); }\n.type.err { background: #4a1f1c; color: var(--err); }\nh2.dt { font-size: 16px; margin: 0 0 4px; display: flex; gap: 10px; align-items: center; }\n.meta { color: var(--dim); font-size: 12px; font-family: var(--mono); margin-bottom: 14px;\n display: flex; flex-wrap: wrap; gap: 4px 14px; }\nsection.blk { border: 1px solid var(--line); border-radius: 8px; margin-bottom: 12px;\n background: var(--panel); overflow: hidden; }\nsection.blk > h3 { margin: 0; padding: 7px 12px; font-size: 12px; font-weight: 600;\n color: var(--dim); border-bottom: 1px solid var(--line); letter-spacing: .03em; }\nsection.blk > h3 .tag { float: right; font-weight: 400; }\nsection.blk.sys { border-left: 3px solid var(--warn); }\n/* An LLM call has three things a reader wants to tell apart at a glance: what\n went in, what came back, and what it cost. Groups carry the first two. */\n.group { border: 1px solid var(--line); border-radius: 10px; margin-bottom: 14px;\n background: #12151d; overflow: hidden; }\n.group > .ghdr { display: flex; align-items: center; gap: 10px; padding: 8px 12px;\n font-size: 11px; font-weight: 700; letter-spacing: .09em; text-transform: uppercase;\n background: #1b2130; border-bottom: 1px solid var(--line); }\n.group > .ghdr .tag { margin-left: auto; font: 11px var(--mono); font-weight: 400;\n letter-spacing: 0; text-transform: none; color: var(--dim); }\n.group > .body { padding: 10px; }\n.group > .body > .blk:last-child { margin-bottom: 0; }\n.group.req { border-left: 3px solid var(--accent); }\n.group.req > .ghdr { color: #9dc1ff; }\n.group.res { border-left: 3px solid var(--ok); }\n.group.res > .ghdr { color: #8ee0c2; }\n.stats { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }\n.stat { background: var(--panel); border: 1px solid var(--line); border-radius: 999px;\n padding: 3px 12px; font: 12px var(--mono); color: var(--dim); }\n.stat b { color: var(--fg); font-weight: 600; }\npre { margin: 0; padding: 10px 12px; font: 12px/1.55 var(--mono); white-space: pre-wrap;\n word-break: break-word; overflow-wrap: anywhere; }\npre.text { white-space: pre-wrap; }\n/* Section tags inside a prompt (<house_rules src=\"...\">, <role>, ...): the only\n structure a composed prompt has, and invisible without this. */\npre .xtag { color: #ffb0cd; background: #2d1b27; border: 1px solid #4e2c40;\n border-radius: 4px; padding: 0 4px; font-weight: 600; }\npre .xtag.end { color: #a07f92; background: #201a1e; border-color: #3b2b35;\n font-weight: 400; }\nimg.media { display: block; max-width: 100%; max-height: 420px; margin: 10px 12px;\n border: 1px solid var(--line); border-radius: 6px; background: #0d1017; }\nimg.media.thumb { max-height: 88px; margin: 8px 12px; cursor: zoom-in; }\n.msg { border-top: 1px solid var(--line); border-left: 3px solid transparent; }\n.msg:first-of-type { border-top: none; }\n.msg > .role { padding: 5px 12px; font-size: 11px; text-transform: uppercase;\n letter-spacing: .05em; color: var(--dim); background: #1b202c;\n display: flex; justify-content: space-between; }\n.msg.user > .role { color: #9dc1ff; }\n.msg.user { border-left-color: #35507f; }\n.msg.assistant > .role { color: #c3a6ff; }\n.msg.assistant { border-left-color: #5a4380; }\n.msg.tool > .role { color: #8ee0c2; }\n.msg.tool { border-left-color: #2f6b58; }\n.msg.system > .role { color: var(--warn); }\n.msg.system { border-left-color: #7a6535; }\n.msg.err > .role { color: var(--err); }\n.msg.err { border-left-color: #8a3a33; }\n.call { border-top: 1px dashed var(--line); }\n.call .name { padding: 5px 12px; font: 12px var(--mono); color: #8ee0c2; }\ndetails > summary { cursor: pointer; padding: 6px 12px; color: var(--dim); font-size: 12px; }\n.badge { font: 11px var(--mono); color: var(--dim); }\n.badge.ok { color: var(--ok); } .badge.bad { color: var(--err); }\n.empty { padding: 10px 12px; color: var(--dim); font-size: 12px; font-style: italic; }\nbutton.link { background: none; border: none; color: var(--accent); cursor: pointer;\n font: 12px var(--mono); padding: 0 6px 0 0; }\n";
1
+ export declare const CSS = "\n:root {\n --bg: #0f1115; --panel: #161922; --line: #262b36; --fg: #d7dbe3; --dim: #8a92a6;\n --accent: #6ea8fe; --ok: #4ec9a0; --err: #f0776c; --warn: #e2b341; --mono: ui-monospace, SFMono-Regular, Menlo, monospace;\n}\n* { box-sizing: border-box; }\nbody { margin: 0; background: var(--bg); color: var(--fg);\n font: 14px/1.5 system-ui, -apple-system, Segoe UI, sans-serif; }\nheader { padding: 12px 16px; border-bottom: 1px solid var(--line); background: var(--panel);\n display: flex; flex-wrap: wrap; gap: 8px 20px; align-items: baseline; }\nheader h1 { font-size: 15px; margin: 0 12px 0 0; font-weight: 600; }\nheader .kv { color: var(--dim); font-size: 12px; }\nheader .kv b { color: var(--fg); font-weight: 600; }\nmain { display: grid; grid-template-columns: 340px 1fr; height: calc(100vh - 49px); }\naside { border-right: 1px solid var(--line); overflow: auto; background: var(--panel); }\n.pad { padding: 10px; position: sticky; top: 0; background: var(--panel); border-bottom: 1px solid var(--line); }\n#filter { width: 100%; padding: 6px 8px; background: var(--bg); color: var(--fg);\n border: 1px solid var(--line); border-radius: 6px; }\n#timeline { list-style: none; margin: 0; padding: 4px 0; }\n#timeline li { padding: 6px 10px; border-left: 3px solid transparent; cursor: pointer; }\n#timeline li:hover { background: #1c2130; }\n#timeline li.sel { background: #1f2637; border-left-color: var(--accent); }\n#timeline li.covered { opacity: .45; }\n#timeline .row { display: flex; gap: 6px; align-items: baseline; }\n#timeline .t { color: var(--fg); font: 11px var(--mono); min-width: 38px; text-align: right; }\n#timeline .t.same { color: #3d4453; }\n#timeline .dur { margin-left: auto; color: var(--dim); font: 11px var(--mono); }\n#timeline .idx { color: var(--dim); font: 11px var(--mono); min-width: 22px; }\n#timeline .prev { color: var(--dim); font-size: 11px; white-space: nowrap;\n overflow: hidden; text-overflow: ellipsis; padding-left: 44px; }\n/* Branch histories are contiguous in this list, so a coloured edge plus a\n header is enough to stop ten parallel branches reading as one long run. */\n#timeline li.sep { cursor: default; padding: 10px 10px 4px; display: flex; gap: 7px;\n align-items: center; font-size: 11px; letter-spacing: .06em; text-transform: uppercase;\n color: var(--dim); border-left-color: transparent; }\n#timeline li.sep:hover { background: none; }\n#timeline li.sep .dot { width: 8px; height: 8px; border-radius: 2px;\n background: var(--bc, var(--dim)); }\n/* The group header shouts in uppercase; its elapsed time should not. */\n#timeline li.sep .dur { text-transform: none; letter-spacing: 0; }\n/* The branch the join actually waited for. */\n#timeline li.sep.slow { background: #232a38; color: var(--fg); }\n#timeline li.sep.slow .dur { color: var(--fg); }\n#timeline li.branch { padding-left: 20px; border-left-color: var(--bc, var(--line)); }\n#timeline li.branch.sel { border-left-color: var(--bc, var(--accent)); }\n#middle { display: flex; flex-direction: column; overflow: hidden; }\n.tabs { display: flex; gap: 4px; padding: 8px 12px; border-bottom: 1px solid var(--line); }\n.tabs button { background: none; border: 1px solid transparent; color: var(--dim);\n padding: 4px 12px; border-radius: 6px; cursor: pointer; font-size: 13px; }\n.tabs button.on { color: var(--fg); background: var(--panel); border-color: var(--line); }\n#detail { overflow: auto; padding: 16px; flex: 1; }\n#stats { overflow: auto; padding: 16px; flex: 1; }\ntable.st { width: 100%; border-collapse: collapse; font: 12px var(--mono); }\ntable.st th { text-align: right; color: var(--dim); font-weight: 600; padding: 6px 12px;\n border-bottom: 1px solid var(--line); white-space: nowrap; }\ntable.st td { text-align: right; padding: 5px 12px; border-bottom: 1px solid #1d2230;\n white-space: nowrap; }\ntable.st th:first-child, table.st td:first-child { text-align: left; }\ntable.st tbody tr:hover { background: #1c2130; }\ntable.st tbody tr:last-child td { border-bottom: none; }\n/* The total is a different kind of row, not one more of the same: it gets its\n own band so nobody reads it as another model or another agent. */\ntable.st tfoot td { color: var(--fg); font-weight: 600; background: #191e2a;\n border-top: 1px solid #2b3346; border-bottom: none; padding: 7px 12px; }\ntable.st tfoot td:first-child { color: var(--dim); text-transform: uppercase;\n letter-spacing: .07em; font-size: 11px; }\n/* Two panes hold a zoomable diagram (the run graph and the architecture), so\n everything about one is written for both. */\n.pane { display: flex; flex-direction: column; overflow: hidden; flex: 1; }\n/* An explicit display on the pane would otherwise beat the hidden attribute\n and both views would show at once. */\n.pane[hidden], #detail[hidden], #stats[hidden] { display: none; }\n.pane svg { max-width: none; display: block; }\n.pane .node { cursor: pointer; }\n/* A node with nothing behind it in this run is not clickable, and says so. */\n.pane .node.idle { cursor: default; }\n.gtools { display: flex; align-items: center; gap: 6px; padding: 6px 10px;\n border-bottom: 1px solid var(--line); background: var(--panel); }\n.gtools button { background: var(--bg); border: 1px solid var(--line); color: var(--fg);\n border-radius: 6px; padding: 2px 10px; cursor: pointer; font: 12px var(--mono); }\n.gtools button:hover { border-color: var(--accent); }\n.gtools .hint:first-of-type { margin-left: 8px; }\n.gtools .lvl { margin-left: auto; font-family: var(--mono); }\n.gtools label.check { display: flex; align-items: center; gap: 5px; margin-left: 8px;\n color: var(--dim); font-size: 12px; cursor: pointer; user-select: none; }\n/* The memory view reads left to right: the graph is the index, the panel is\n the page. Putting the body under a tab of its own would mean losing the\n picture every time you asked what a node actually says. */\n#memory .split { display: flex; flex: 1; overflow: hidden; }\n#mdetail { width: 380px; flex: none; padding: 16px; overflow: auto;\n border-right: none; border-left: 1px solid var(--line); }\n#mdetail .dt { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 0 0 10px; }\n#mdetail .idrow { display: flex; gap: 8px; align-items: center; margin: 0 0 14px; }\n#mdetail .idrow code { flex: 1; min-width: 0; font: 12px var(--mono); color: var(--fg);\n background: var(--bg); border: 1px solid var(--line); border-radius: 6px;\n padding: 4px 8px; word-break: break-all; }\n#mdetail .idrow button { flex: 0 0 auto; background: var(--bg); border: 1px solid var(--line);\n color: var(--dim); border-radius: 6px; padding: 4px 9px; font: 11px var(--mono);\n cursor: pointer; }\n#mdetail .idrow button:hover { border-color: var(--accent); color: var(--fg); }\n#mdetail dl.props { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 5px 14px;\n margin: 0 0 14px; font: 12px var(--mono); }\n#mdetail dl.props dt { color: var(--dim); }\n#mdetail dl.props dd { margin: 0; word-break: break-word; }\n/* A block that shares the panel's own background has no edges to show. */\n#mdetail section.blk { background: var(--bg); }\n#mdetail ul.links { list-style: none; margin: 0; padding: 0; }\n#mdetail ul.links li { display: grid; grid-template-columns: 96px auto minmax(0, 1fr);\n gap: 8px; align-items: baseline; padding: 6px 12px; }\n#mdetail ul.links li + li { border-top: 1px solid var(--line); }\n#mdetail ul.links .rel { font: 11px var(--mono); color: var(--warn); white-space: nowrap; }\n#mdetail button.link { font: 12px/1.4 system-ui, sans-serif; padding: 0; text-align: left;\n overflow-wrap: anywhere; }\n#mdetail button.link:hover { text-decoration: underline; }\n#mcount .ok { color: var(--ok); }\n#mcount .bad { color: var(--err); }\n/* The kind palette is the memory page's, so a node reads the same in both. */\n.kind { font-size: 11px; letter-spacing: .04em; text-transform: uppercase;\n padding: 1px 6px; border-radius: 4px; background: #232a38; color: var(--dim); }\n.kind.task { background: #1e3357; color: #9dc1ff; }\n.kind.plan { background: #3a2450; color: #d1a6ff; }\n.kind.fact { background: #14392c; color: #8ee0c2; }\n.kind.snippet { background: #1e3d33; color: #8ee0c2; }\n.kind.file { background: #40331a; color: #f0dfb4; }\n.kind.operation { background: #3d2a1b; color: #f2d9c2; }\n.kind.preference { background: #3d1f2c; color: #f5b8cd; }\n/* Selection is a class rather than attributes written onto the shapes: the\n previous pick has to lose its outline without a redraw. White, because the\n accent already means \"the recall matched this one\". */\n#memory .node.picked rect, #memory .node.picked polygon,\n#memory .node.picked path, #memory .node.picked circle {\n stroke: var(--fg) !important; stroke-width: 4px !important; }\n/* The canvas is transformed, not scrolled: panning has to work past the edges\n of the diagram, which overflow:auto would forbid. */\n.viewport { flex: 1; overflow: hidden; position: relative; cursor: grab; touch-action: none;\n user-select: none; -webkit-user-select: none; }\n.viewport.drag { cursor: grabbing; }\n.canvas { position: absolute; top: 0; left: 0; transform-origin: 0 0; }\n.hint { color: var(--dim); font-size: 12px; }\n.hint.pad { padding: 16px; }\n.type { font-size: 11px; letter-spacing: .04em; text-transform: uppercase;\n padding: 1px 6px; border-radius: 4px; background: #232a38; color: var(--dim); }\n.type.llm_call { background: #1e3357; color: #9dc1ff; }\n.type.tool_call, .type.tool_result { background: #1e3d33; color: #8ee0c2; }\n.type.compaction { background: #40331a; color: var(--warn); }\n.type.fork, .type.join { background: #3a2450; color: #d1a6ff; }\n.type.final_output { background: #14392c; color: var(--ok); }\n.type.err { background: #4a1f1c; color: var(--err); }\nh2.dt { font-size: 16px; margin: 0 0 4px; display: flex; gap: 10px; align-items: center; }\n.meta { color: var(--dim); font-size: 12px; font-family: var(--mono); margin-bottom: 14px;\n display: flex; flex-wrap: wrap; gap: 4px 14px; }\nsection.blk { border: 1px solid var(--line); border-radius: 8px; margin-bottom: 12px;\n background: var(--panel); overflow: hidden; }\nsection.blk > h3 { margin: 0; padding: 7px 12px; font-size: 12px; font-weight: 600;\n color: var(--dim); border-bottom: 1px solid var(--line); letter-spacing: .03em; }\nsection.blk > h3 .tag { float: right; font-weight: 400; }\nsection.blk.sys { border-left: 3px solid var(--warn); }\n/* An LLM call has three things a reader wants to tell apart at a glance: what\n went in, what came back, and what it cost. Groups carry the first two. */\n.group { border: 1px solid var(--line); border-radius: 10px; margin-bottom: 14px;\n background: #12151d; overflow: hidden; }\n.group > .ghdr { display: flex; align-items: center; gap: 10px; padding: 8px 12px;\n font-size: 11px; font-weight: 700; letter-spacing: .09em; text-transform: uppercase;\n background: #1b2130; border-bottom: 1px solid var(--line); }\n.group > .ghdr .tag { margin-left: auto; font: 11px var(--mono); font-weight: 400;\n letter-spacing: 0; text-transform: none; color: var(--dim); }\n.group > .body { padding: 10px; }\n.group > .body > .blk:last-child { margin-bottom: 0; }\n.group.req { border-left: 3px solid var(--accent); }\n.group.req > .ghdr { color: #9dc1ff; }\n.group.res { border-left: 3px solid var(--ok); }\n.group.res > .ghdr { color: #8ee0c2; }\n.stats { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }\n.stat { background: var(--panel); border: 1px solid var(--line); border-radius: 999px;\n padding: 3px 12px; font: 12px var(--mono); color: var(--dim); }\n.stat b { color: var(--fg); font-weight: 600; }\npre { margin: 0; padding: 10px 12px; font: 12px/1.55 var(--mono); white-space: pre-wrap;\n word-break: break-word; overflow-wrap: anywhere; }\npre.text { white-space: pre-wrap; }\n.tagged-pre { margin: 4px 0; }\n.tagged-pre > pre.text { margin: 0; padding: 6px 12px; }\n/* Section tags inside a prompt (<house_rules src=\"...\">, <role>, ...): the only\n structure a composed prompt has, and invisible without this. */\n.xtag { color: #ffb0cd; background: #2d1b27; border: 1px solid #4e2c40;\n border-radius: 4px; padding: 1px 5px; font-weight: 600; font: 12px var(--mono); }\n.xtag.end { color: #a07f92; background: #201a1e; border-color: #3b2b35;\n font-weight: 400; }\ndetails.xtag-section {\n border: 1px solid var(--line); border-radius: 6px; margin: 6px 12px;\n background: var(--panel);\n}\ndetails.xtag-section > summary {\n cursor: pointer; padding: 6px 10px; font: 12px/1.55 var(--mono);\n color: var(--dim); white-space: nowrap; overflow: hidden;\n text-overflow: ellipsis; user-select: none;\n}\ndetails.xtag-section > summary:hover { background: rgba(255, 255, 255, 0.03); }\ndetails.xtag-section > summary .preview {\n color: var(--dim); margin-left: 8px; font-size: 11px;\n}\ndetails.xtag-section[open] > summary {\n white-space: normal; border-bottom: 1px solid var(--line);\n background: rgba(0, 0, 0, 0.15);\n}\ndetails.xtag-section[open] > summary .preview { display: none; }\ndetails.xtag-section > pre.xtag-body {\n margin: 0; padding: 8px 12px 10px; background: #0d1017;\n border-radius: 0 0 5px 5px; font: 12px/1.55 var(--mono);\n white-space: pre-wrap; word-break: break-word; overflow-wrap: anywhere;\n}\nimg.media { display: block; max-width: 100%; max-height: 420px; margin: 10px 12px;\n border: 1px solid var(--line); border-radius: 6px; background: #0d1017; }\nimg.media.thumb { max-height: 88px; margin: 8px 12px; cursor: zoom-in; }\n.msg { border-top: 1px solid var(--line); border-left: 3px solid transparent; }\n.msg:first-of-type { border-top: none; }\n.msg > .role { padding: 5px 12px; font-size: 11px; text-transform: uppercase;\n letter-spacing: .05em; color: var(--dim); background: #1b202c;\n display: flex; justify-content: space-between; }\n.msg.user > .role { color: #9dc1ff; }\n.msg.user { border-left-color: #35507f; }\n.msg.assistant > .role { color: #c3a6ff; }\n.msg.assistant { border-left-color: #5a4380; }\n.msg.tool > .role { color: #8ee0c2; }\n.msg.tool { border-left-color: #2f6b58; }\n.msg.system > .role { color: var(--warn); }\n.msg.system { border-left-color: #7a6535; }\n.msg.err > .role { color: var(--err); }\n.msg.err { border-left-color: #8a3a33; }\n.call { border-top: 1px dashed var(--line); }\n.call .name { padding: 5px 12px; font: 12px var(--mono); color: #8ee0c2; }\ndetails > summary { cursor: pointer; padding: 6px 12px; color: var(--dim); font-size: 12px; }\n.badge { font: 11px var(--mono); color: var(--dim); }\n.badge.ok { color: var(--ok); } .badge.bad { color: var(--err); }\n.empty { padding: 10px 12px; color: var(--dim); font-size: 12px; font-style: italic; }\nbutton.link { background: none; border: none; color: var(--accent); cursor: pointer;\n font: 12px var(--mono); padding: 0 6px 0 0; }\n";
2
2
  //# sourceMappingURL=css.d.ts.map
@@ -183,12 +183,37 @@ section.blk.sys { border-left: 3px solid var(--warn); }
183
183
  pre { margin: 0; padding: 10px 12px; font: 12px/1.55 var(--mono); white-space: pre-wrap;
184
184
  word-break: break-word; overflow-wrap: anywhere; }
185
185
  pre.text { white-space: pre-wrap; }
186
+ .tagged-pre { margin: 4px 0; }
187
+ .tagged-pre > pre.text { margin: 0; padding: 6px 12px; }
186
188
  /* Section tags inside a prompt (<house_rules src="...">, <role>, ...): the only
187
189
  structure a composed prompt has, and invisible without this. */
188
- pre .xtag { color: #ffb0cd; background: #2d1b27; border: 1px solid #4e2c40;
189
- border-radius: 4px; padding: 0 4px; font-weight: 600; }
190
- pre .xtag.end { color: #a07f92; background: #201a1e; border-color: #3b2b35;
190
+ .xtag { color: #ffb0cd; background: #2d1b27; border: 1px solid #4e2c40;
191
+ border-radius: 4px; padding: 1px 5px; font-weight: 600; font: 12px var(--mono); }
192
+ .xtag.end { color: #a07f92; background: #201a1e; border-color: #3b2b35;
191
193
  font-weight: 400; }
194
+ details.xtag-section {
195
+ border: 1px solid var(--line); border-radius: 6px; margin: 6px 12px;
196
+ background: var(--panel);
197
+ }
198
+ details.xtag-section > summary {
199
+ cursor: pointer; padding: 6px 10px; font: 12px/1.55 var(--mono);
200
+ color: var(--dim); white-space: nowrap; overflow: hidden;
201
+ text-overflow: ellipsis; user-select: none;
202
+ }
203
+ details.xtag-section > summary:hover { background: rgba(255, 255, 255, 0.03); }
204
+ details.xtag-section > summary .preview {
205
+ color: var(--dim); margin-left: 8px; font-size: 11px;
206
+ }
207
+ details.xtag-section[open] > summary {
208
+ white-space: normal; border-bottom: 1px solid var(--line);
209
+ background: rgba(0, 0, 0, 0.15);
210
+ }
211
+ details.xtag-section[open] > summary .preview { display: none; }
212
+ details.xtag-section > pre.xtag-body {
213
+ margin: 0; padding: 8px 12px 10px; background: #0d1017;
214
+ border-radius: 0 0 5px 5px; font: 12px/1.55 var(--mono);
215
+ white-space: pre-wrap; word-break: break-word; overflow-wrap: anywhere;
216
+ }
192
217
  img.media { display: block; max-width: 100%; max-height: 420px; margin: 10px 12px;
193
218
  border: 1px solid var(--line); border-radius: 6px; background: #0d1017; }
194
219
  img.media.thumb { max-height: 88px; margin: 8px 12px; cursor: zoom-in; }
package/dist/kernel.js CHANGED
@@ -378,11 +378,32 @@ export async function applySystemPrompt(state, reg, env) {
378
378
  }
379
379
  function parseForkArgs(raw) {
380
380
  const args = parseArgs(raw);
381
- const branches = args.branches;
381
+ let branches = args.branches;
382
+ // When a model delegates a single task, it frequently flattens the branch
383
+ // fields directly into the root arguments instead of wrapping them in a
384
+ // 1-element array. Coerce it if name and instructions are present at the root.
385
+ if (!Array.isArray(branches) &&
386
+ typeof args.name === 'string' &&
387
+ typeof args.instructions === 'string') {
388
+ branches = [
389
+ {
390
+ name: args.name,
391
+ instructions: args.instructions,
392
+ agent: typeof args.agent === 'string' ? args.agent : undefined,
393
+ },
394
+ ];
395
+ }
382
396
  // Spelled out rather than "invalid arguments": this string is fed back to
383
397
  // the model as the tool result, and it is the only chance it gets to work
384
398
  // out what to do differently.
385
399
  if (!Array.isArray(branches) || branches.length === 0) {
400
+ if (args.name !== undefined ||
401
+ args.instructions !== undefined ||
402
+ args.agent !== undefined) {
403
+ return ('the "branches" argument is missing; "fork" requires an array of branches: ' +
404
+ '{ "branches": [{ "name": "...", "instructions": "...", "agent": "..." }] }. ' +
405
+ 'Do not pass branch fields at the top level.');
406
+ }
386
407
  return 'the "branches" argument is missing or empty; it must be an array of at least one branch';
387
408
  }
388
409
  const parsed = [];
@@ -8,6 +8,8 @@ export declare const MEMORY_MOUNT = "/memory";
8
8
  export declare const MAX_FILE_BYTES: number;
9
9
  export interface RememberOptions {
10
10
  maxBytes?: number;
11
+ /** Display path as passed to the tool, for reporting errors without leaking the host filesystem */
12
+ path?: string;
11
13
  }
12
14
  /**
13
15
  * Copy `source` into the memory directory under `id` and describe it.
@@ -35,23 +35,24 @@ const MAX_FORMAT_LEN = 16;
35
35
  */
36
36
  export async function rememberFile(dir, source, id, opts = {}) {
37
37
  const maxBytes = opts.maxBytes ?? MAX_FILE_BYTES;
38
- const info = await describe(source);
38
+ const name = opts.path ?? source;
39
+ const info = await describe(source, name);
39
40
  if (!info.isFile()) {
40
- throw new MemoryError(`${source} is not a regular file`, 'only files can be remembered; write a `fact` node describing a directory instead');
41
+ throw new MemoryError(`${name} is not a regular file`, 'only files can be remembered; write a `fact` node describing a directory instead');
41
42
  }
42
43
  if (info.size > maxBytes) {
43
- throw new MemoryError(`${source} is ${info.size} bytes, over the ${maxBytes} byte limit for a remembered file`, 'keep the artifact in the workspace and remember a `snippet` of the part that matters');
44
+ throw new MemoryError(`${name} is ${info.size} bytes, over the ${maxBytes} byte limit for a remembered file`, 'keep the artifact in the workspace and remember a `snippet` of the part that matters');
44
45
  }
45
46
  const bytes = await readFile(source);
46
47
  if (bytes.byteLength > maxBytes) {
47
- throw new MemoryError(`${source} grew past the ${maxBytes} byte limit while being read`);
48
+ throw new MemoryError(`${name} grew past the ${maxBytes} byte limit while being read`);
48
49
  }
49
50
  const format = formatOf(source);
50
- const name = id + (format ? `.${format}` : '');
51
+ const fileName = id + (format ? `.${format}` : '');
51
52
  await mkdir(join(dir, FILES_DIR), { recursive: true });
52
- await writeFile(join(dir, FILES_DIR, name), bytes);
53
+ await writeFile(join(dir, FILES_DIR, fileName), bytes);
53
54
  return {
54
- path: `${MEMORY_MOUNT}/${name}`,
55
+ path: `${MEMORY_MOUNT}/${fileName}`,
55
56
  bytes: bytes.byteLength,
56
57
  sha256: createHash('sha256').update(bytes).digest('hex'),
57
58
  format,
@@ -66,12 +67,12 @@ export function hostPath(dir, file) {
66
67
  export async function forgetFile(dir, file) {
67
68
  await rm(hostPath(dir, file), { force: true });
68
69
  }
69
- async function describe(source) {
70
+ async function describe(source, name = source) {
70
71
  try {
71
72
  return await stat(source);
72
73
  }
73
74
  catch {
74
- throw new MemoryError(`${source} does not exist`, 'remember a file the run actually wrote, by the path the file tools use');
75
+ throw new MemoryError(`${name} does not exist`, 'remember a file the run actually wrote, by the path the file tools use');
75
76
  }
76
77
  }
77
78
  /**
@@ -29,14 +29,28 @@ export declare class MemoryGraph {
29
29
  has(id: string): boolean;
30
30
  get(id: string): MemoryNode | undefined;
31
31
  add(draft: NodeDraft, at: string): MemoryNode;
32
+ /**
33
+ * Put a node in exactly as it stands — id, timestamps, revision and counters
34
+ * included. `add` mints a new memory; this carries one across from another
35
+ * graph, and replaces the node already under that id if there is one.
36
+ */
37
+ adopt(node: MemoryNode): void;
32
38
  update(id: string, patch: NodePatch, at: string): MemoryNode;
33
39
  /** Reading is what counts as use — see `memory_load`, not `memory_search`. */
34
40
  touch(id: string, at: string): MemoryNode | undefined;
35
41
  link(source: string, target: string, relation: Relation, at: string): void;
42
+ /** `link` for an edge that already exists elsewhere: it keeps the stamp it had. */
43
+ adoptEdge(source: string, target: string, attrs: MemoryEdgeAttrs): void;
36
44
  /** Drops the node and every edge touching it; the file it names is the store's to unlink. */
37
45
  forget(id: string): MemoryNode | undefined;
38
46
  nodes(sees?: readonly string[]): MemoryNode[];
39
47
  edges(sees?: readonly string[]): MemoryEdge[];
48
+ /** Edges with their attributes and no mask — `edges` is the view, this is the record. */
49
+ links(): {
50
+ source: string;
51
+ target: string;
52
+ attrs: MemoryEdgeAttrs;
53
+ }[];
40
54
  /** Both directions: a memory is as often reached from its artifact as from its request. */
41
55
  neighbors(id: string, sees?: readonly string[]): MemoryEdge[];
42
56
  /** Edges between nodes already chosen — what makes a materialized subgraph closed. */
@@ -41,6 +41,20 @@ export class MemoryGraph {
41
41
  this.#graph.addNode(node.id, node);
42
42
  return node;
43
43
  }
44
+ /**
45
+ * Put a node in exactly as it stands — id, timestamps, revision and counters
46
+ * included. `add` mints a new memory; this carries one across from another
47
+ * graph, and replaces the node already under that id if there is one.
48
+ */
49
+ adopt(node) {
50
+ const next = { ...node };
51
+ if (this.#graph.hasNode(node.id)) {
52
+ this.#graph.replaceNodeAttributes(node.id, next);
53
+ }
54
+ else {
55
+ this.#graph.addNode(node.id, next);
56
+ }
57
+ }
44
58
  update(id, patch, at) {
45
59
  const current = this.#graph.getNodeAttributes(id);
46
60
  if (patch.expectedRevision !== undefined && patch.expectedRevision !== current.revision) {
@@ -75,6 +89,12 @@ export class MemoryGraph {
75
89
  createdAt: at,
76
90
  });
77
91
  }
92
+ /** `link` for an edge that already exists elsewhere: it keeps the stamp it had. */
93
+ adoptEdge(source, target, attrs) {
94
+ this.#graph.mergeEdgeWithKey(edgeKey(source, target, attrs.relation), source, target, {
95
+ ...attrs,
96
+ });
97
+ }
78
98
  /** Drops the node and every edge touching it; the file it names is the store's to unlink. */
79
99
  forget(id) {
80
100
  const node = this.get(id);
@@ -102,6 +122,14 @@ export class MemoryGraph {
102
122
  });
103
123
  return out;
104
124
  }
125
+ /** Edges with their attributes and no mask — `edges` is the view, this is the record. */
126
+ links() {
127
+ const out = [];
128
+ this.#graph.forEachEdge((_key, attrs, source, target) => {
129
+ out.push({ source, target, attrs });
130
+ });
131
+ return out;
132
+ }
105
133
  /** Both directions: a memory is as often reached from its artifact as from its request. */
106
134
  neighbors(id, sees) {
107
135
  if (!this.#graph.hasNode(id)) {
@@ -31,6 +31,7 @@ export interface CommitNode {
31
31
  /** absolute host path of a file to copy in, already resolved by the caller */
32
32
  file?: {
33
33
  source: string;
34
+ path?: string;
34
35
  };
35
36
  expectedRevision?: number;
36
37
  }
@@ -90,4 +91,6 @@ export declare class MemoryIndex {
90
91
  commit(tx: MemoryTransaction, opts: CommitOptions): Promise<CommitResult>;
91
92
  forget(ids: readonly string[], sees: readonly string[]): Promise<MemoryNode[]>;
92
93
  }
94
+ /** Whitespace and case are not what makes two memories different. */
95
+ export declare function flatten(text: string): string;
93
96
  //# sourceMappingURL=index.d.ts.map
@@ -111,7 +111,9 @@ export class MemoryIndex {
111
111
  const written = [];
112
112
  try {
113
113
  for (const item of plan.files) {
114
- const file = await rememberFile(this.store.dir, item.source, item.id);
114
+ const file = await rememberFile(this.store.dir, item.source, item.id, {
115
+ path: item.path,
116
+ });
115
117
  written.push(file);
116
118
  item.target.file = file;
117
119
  }
@@ -239,20 +241,41 @@ export class MemoryIndex {
239
241
  const files = [];
240
242
  const embed = [];
241
243
  for (const [i, node] of nodes.entries()) {
242
- const where = node.ref ?? node.id ?? `nodes[${i}]`;
243
- if ((node.ref === undefined) === (node.id === undefined)) {
244
+ let ref = node.ref;
245
+ let id = node.id;
246
+ const where = ref ?? id ?? `nodes[${i}]`;
247
+ // If the model passed both ref and id with the same value (e.g. ref: "x", id: "x"),
248
+ // and id is not an existing node, treat it as a new node create.
249
+ if (ref !== undefined && id !== undefined) {
250
+ if (ref === id && !this.graph.has(id)) {
251
+ id = undefined;
252
+ }
253
+ else {
254
+ throw new MemoryError(`${where}: give either a ref (to create) or an id (to update), not both`);
255
+ }
256
+ }
257
+ else if (ref === undefined &&
258
+ id !== undefined &&
259
+ !this.graph.has(id) &&
260
+ (node.kind !== undefined || (node.text !== undefined && node.text.trim()))) {
261
+ // Models often assign "id" to new nodes instead of "ref". If the id does not
262
+ // match an existing node in memory, treat it as the local ref for creation.
263
+ ref = id;
264
+ id = undefined;
265
+ }
266
+ if (ref === undefined && id === undefined) {
244
267
  throw new MemoryError(`${where}: give either a ref (to create) or an id (to update), not both`);
245
268
  }
246
269
  const audience = this.#audience(node, opts.writes, where);
247
- if (node.id) {
248
- const existing = this.graph.get(node.id);
270
+ if (id) {
271
+ const existing = this.graph.get(id);
249
272
  if (!existing) {
250
273
  throw new MemoryError(`${where}: no such memory`);
251
274
  }
252
275
  if (node.kind !== undefined) {
253
276
  this.#kind(node.kind, where);
254
277
  }
255
- ids[node.id] = node.id;
278
+ ids[id] = id;
256
279
  const patch = {
257
280
  ...(node.kind !== undefined ? { kind: node.kind } : {}),
258
281
  ...(node.text !== undefined ? { text: node.text } : {}),
@@ -262,18 +285,22 @@ export class MemoryIndex {
262
285
  ? { expectedRevision: node.expectedRevision }
263
286
  : {}),
264
287
  };
265
- planned.push({ existing: true, id: node.id, patch });
288
+ planned.push({ existing: true, id, patch });
266
289
  // Only a text change invalidates the vector; re-embedding on an
267
290
  // audience edit would burn a call to store the same numbers.
268
291
  if (node.text !== undefined && node.text !== existing.text) {
269
- embed.push({ id: node.id, text: node.text });
292
+ embed.push({ id, text: node.text });
270
293
  }
271
294
  if (node.file) {
272
- files.push({ id: node.id, source: node.file.source, target: patch });
295
+ files.push({
296
+ id,
297
+ source: node.file.source,
298
+ path: node.file.path,
299
+ target: patch,
300
+ });
273
301
  }
274
302
  continue;
275
303
  }
276
- const ref = node.ref;
277
304
  if (ref in ids) {
278
305
  throw new MemoryError(`${where}: two nodes share the ref "${ref}"`);
279
306
  }
@@ -281,19 +308,24 @@ export class MemoryIndex {
281
308
  throw new MemoryError(`${where}: a new node needs text`, 'text is the only thing a search can match, so it has to describe the node');
282
309
  }
283
310
  const kind = this.#kind(node.kind, where);
284
- const id = opts.clock.newId();
285
- ids[ref] = id;
311
+ const newId = opts.clock.newId();
312
+ ids[ref] = newId;
286
313
  const draft = {
287
- id,
314
+ id: newId,
288
315
  kind,
289
316
  text: node.text,
290
317
  audience: audience ?? [ALL_AGENTS],
291
318
  ...(node.metadata !== undefined ? { metadata: node.metadata } : {}),
292
319
  };
293
- planned.push({ existing: false, id, draft });
294
- embed.push({ id, text: node.text });
320
+ planned.push({ existing: false, id: newId, draft });
321
+ embed.push({ id: newId, text: node.text });
295
322
  if (node.file) {
296
- files.push({ id, source: node.file.source, target: draft });
323
+ files.push({
324
+ id: newId,
325
+ source: node.file.source,
326
+ path: node.file.path,
327
+ target: draft,
328
+ });
297
329
  }
298
330
  }
299
331
  const resolved = edges.map((edge, i) => {
@@ -351,7 +383,7 @@ export class MemoryIndex {
351
383
  }
352
384
  }
353
385
  /** Whitespace and case are not what makes two memories different. */
354
- function flatten(text) {
386
+ export function flatten(text) {
355
387
  return text.replace(/\s+/g, ' ').trim().toLowerCase();
356
388
  }
357
389
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,65 @@
1
+ import { MemoryStore, type MemoryEmbedding } from './store.ts';
2
+ import { MemoryError } from './types.ts';
3
+ export interface MergeOptions {
4
+ /** fold near-identical memories onto the one already there; on by default */
5
+ dedupe?: boolean;
6
+ /** resolve divergence by taking the highest revision instead of refusing */
7
+ force?: boolean;
8
+ /** plan and report, write nothing */
9
+ dryRun?: boolean;
10
+ }
11
+ export interface MergeSource {
12
+ dir: string;
13
+ /** what the source holds */
14
+ nodes: number;
15
+ edges: number;
16
+ /** what it contributed */
17
+ added: number;
18
+ /** ids the target already had, reconciled rather than duplicated */
19
+ shared: number;
20
+ /** new ids that turned out to be a memory the target already held */
21
+ twins: number;
22
+ files: number;
23
+ }
24
+ export interface MergeConflict {
25
+ id: string;
26
+ /** the source that disagreed */
27
+ dir: string;
28
+ /** revision already in the merge, and the one the source brought */
29
+ mine: number;
30
+ theirs: number;
31
+ text: string;
32
+ }
33
+ export interface MergeReport {
34
+ /** the memory everything was folded into */
35
+ dir: string;
36
+ sources: MergeSource[];
37
+ added: number;
38
+ shared: number;
39
+ twins: number;
40
+ /** edges the merge added, after re-pointing folded ends */
41
+ edges: number;
42
+ files: number;
43
+ embedding?: MemoryEmbedding;
44
+ dryRun: boolean;
45
+ }
46
+ /**
47
+ * Refusal carrying what to look at. Thrown before anything is written, so a
48
+ * conflicted merge leaves the target exactly as it was.
49
+ */
50
+ export declare class MergeConflicts extends MemoryError {
51
+ readonly conflicts: readonly MergeConflict[];
52
+ constructor(conflicts: readonly MergeConflict[]);
53
+ }
54
+ /**
55
+ * Fold `sources` into `target`, left to right.
56
+ *
57
+ * Order is visible: a memory folded onto a twin keeps the id of whichever copy
58
+ * arrived first, so `merge a b` and `merge b a` can differ in which id
59
+ * survives. They never differ in what is remembered.
60
+ *
61
+ * On `dryRun` the target is left with the merge applied in memory and nothing
62
+ * on disk. Report the result and discard the store; do not commit it.
63
+ */
64
+ export declare function mergeMemories(target: MemoryStore, sources: readonly string[], opts?: MergeOptions): Promise<MergeReport>;
65
+ //# sourceMappingURL=merge.d.ts.map