@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.
- package/README.md +1 -1
- package/dist/fork.js +4 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/inspect/client.d.ts +1 -1
- package/dist/inspect/client.js +95 -14
- package/dist/inspect/css.d.ts +1 -1
- package/dist/inspect/css.js +28 -3
- package/dist/kernel.js +22 -1
- package/dist/memory/files.d.ts +2 -0
- package/dist/memory/files.js +10 -9
- package/dist/memory/graph.d.ts +14 -0
- package/dist/memory/graph.js +28 -0
- package/dist/memory/index.d.ts +3 -0
- package/dist/memory/index.js +49 -17
- package/dist/memory/merge.d.ts +65 -0
- package/dist/memory/merge.js +379 -0
- package/dist/memory/store.d.ts +11 -0
- package/dist/memory/store.js +9 -0
- package/dist/memory/tools.js +66 -16
- package/dist/memory/vectors.d.ts +2 -0
- package/dist/memory/vectors.js +7 -0
- package/dist/project/config.d.ts +12 -0
- package/dist/project/config.js +14 -0
- package/dist/project/load.js +61 -0
- package/dist/tools/sandbox.d.ts +25 -0
- package/dist/tools/sandbox.js +58 -8
- package/dist/tools/workspace.js +2 -2
- package/dist/types.js +25 -10
- package/package.json +1 -1
package/dist/inspect/css.d.ts
CHANGED
|
@@ -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
|
package/dist/inspect/css.js
CHANGED
|
@@ -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
|
-
|
|
189
|
-
border-radius: 4px; padding:
|
|
190
|
-
|
|
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
|
-
|
|
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 = [];
|
package/dist/memory/files.d.ts
CHANGED
|
@@ -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.
|
package/dist/memory/files.js
CHANGED
|
@@ -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
|
|
38
|
+
const name = opts.path ?? source;
|
|
39
|
+
const info = await describe(source, name);
|
|
39
40
|
if (!info.isFile()) {
|
|
40
|
-
throw new MemoryError(`${
|
|
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(`${
|
|
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(`${
|
|
48
|
+
throw new MemoryError(`${name} grew past the ${maxBytes} byte limit while being read`);
|
|
48
49
|
}
|
|
49
50
|
const format = formatOf(source);
|
|
50
|
-
const
|
|
51
|
+
const fileName = id + (format ? `.${format}` : '');
|
|
51
52
|
await mkdir(join(dir, FILES_DIR), { recursive: true });
|
|
52
|
-
await writeFile(join(dir, FILES_DIR,
|
|
53
|
+
await writeFile(join(dir, FILES_DIR, fileName), bytes);
|
|
53
54
|
return {
|
|
54
|
-
path: `${MEMORY_MOUNT}/${
|
|
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(`${
|
|
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
|
/**
|
package/dist/memory/graph.d.ts
CHANGED
|
@@ -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. */
|
package/dist/memory/graph.js
CHANGED
|
@@ -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)) {
|
package/dist/memory/index.d.ts
CHANGED
|
@@ -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
|
package/dist/memory/index.js
CHANGED
|
@@ -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
|
-
|
|
243
|
-
|
|
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 (
|
|
248
|
-
const existing = this.graph.get(
|
|
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[
|
|
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
|
|
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
|
|
292
|
+
embed.push({ id, text: node.text });
|
|
270
293
|
}
|
|
271
294
|
if (node.file) {
|
|
272
|
-
files.push({
|
|
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
|
|
285
|
-
ids[ref] =
|
|
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({
|
|
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
|