chapterhouse 0.3.13 → 0.3.15
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 +2 -69
- package/dist/api/server.js +8 -155
- package/dist/api/server.test.js +1 -1
- package/dist/cli.js +0 -30
- package/dist/config.js +11 -3
- package/dist/copilot/agent-event-bus.js +41 -0
- package/dist/copilot/agent-event-bus.test.js +23 -0
- package/dist/copilot/agents.js +4 -59
- package/dist/copilot/orchestrator.js +20 -39
- package/dist/copilot/orchestrator.test.js +73 -158
- package/dist/copilot/system-message.js +7 -0
- package/dist/copilot/task-event-log.js +5 -5
- package/dist/copilot/task-event-log.test.js +68 -142
- package/dist/copilot/tools.js +72 -132
- package/dist/daemon.js +6 -22
- package/dist/store/db.js +2 -50
- package/dist/store/db.test.js +0 -45
- package/dist/wiki/fs.js +5 -0
- package/dist/wiki/index-manager.js +92 -17
- package/dist/wiki/index-manager.test.js +19 -0
- package/dist/wiki/migrate-topics.js +132 -0
- package/dist/wiki/migrate-topics.test.js +57 -0
- package/dist/wiki/topic-structure.js +167 -0
- package/dist/wiki/topic-structure.test.js +74 -0
- package/package.json +1 -3
- package/web/dist/assets/index-BlIWCM11.js +217 -0
- package/web/dist/assets/index-BlIWCM11.js.map +1 -0
- package/web/dist/assets/{index-BtAcw3EP.css → index-lvHFM_ut.css} +1 -1
- package/web/dist/index.html +2 -2
- package/dist/api/ralph.js +0 -153
- package/dist/api/ralph.test.js +0 -101
- package/dist/copilot/agents.squad.test.js +0 -72
- package/dist/copilot/hooks.js +0 -157
- package/dist/copilot/hooks.test.js +0 -315
- package/dist/copilot/squad-event-bus.js +0 -27
- package/dist/copilot/tools.squad.test.js +0 -168
- package/dist/squad/charter.js +0 -125
- package/dist/squad/charter.test.js +0 -89
- package/dist/squad/context.js +0 -48
- package/dist/squad/context.test.js +0 -59
- package/dist/squad/discovery.js +0 -268
- package/dist/squad/discovery.test.js +0 -154
- package/dist/squad/index.js +0 -9
- package/dist/squad/init-cli.js +0 -109
- package/dist/squad/init.js +0 -395
- package/dist/squad/init.test.js +0 -351
- package/dist/squad/mirror.js +0 -83
- package/dist/squad/mirror.scheduler.js +0 -80
- package/dist/squad/mirror.scheduler.test.js +0 -197
- package/dist/squad/mirror.test.js +0 -172
- package/dist/squad/registry.js +0 -162
- package/dist/squad/registry.test.js +0 -31
- package/dist/squad/squad-coordinator-system-message.test.js +0 -190
- package/dist/squad/squad-session-routing.test.js +0 -260
- package/dist/squad/types.js +0 -4
- package/dist/squad/worktree.js +0 -295
- package/dist/squad/worktree.test.js +0 -189
- package/dist/store/squad-sessions.test.js +0 -341
- package/web/dist/assets/index-IgSOXx_a.js +0 -219
- package/web/dist/assets/index-IgSOXx_a.js.map +0 -1
|
@@ -7,4 +7,4 @@ pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5p
|
|
|
7
7
|
|
|
8
8
|
Outdated base version: https://github.com/primer/github-syntax-dark
|
|
9
9
|
Current colors taken from GitHub's CSS
|
|
10
|
-
*/.hljs{color:#c9d1d9;background:#0d1117}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#ff7b72}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#d2a8ff}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-variable,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id{color:#79c0ff}.hljs-regexp,.hljs-string,.hljs-meta .hljs-string{color:#a5d6ff}.hljs-built_in,.hljs-symbol{color:#ffa657}.hljs-comment,.hljs-code,.hljs-formula{color:#8b949e}.hljs-name,.hljs-quote,.hljs-selector-tag,.hljs-selector-pseudo{color:#7ee787}.hljs-subst{color:#c9d1d9}.hljs-section{color:#1f6feb;font-weight:700}.hljs-bullet{color:#f2cc60}.hljs-emphasis{color:#c9d1d9;font-style:italic}.hljs-strong{color:#c9d1d9;font-weight:700}.hljs-addition{color:#aff5b4;background-color:#033a16}.hljs-deletion{color:#ffdcd7;background-color:#67060c}:root{color-scheme:dark;--bg: #0e1116;--bg-elev: #161b22;--bg-elev-2: #21262d;--fg: #e6edf3;--fg-dim: #8b949e;--border: #30363d;--accent: #3b82f6;--accent-fg: #ffffff;--danger: #f87171;--user-bubble: #1e293b}*{box-sizing:border-box}html,body,#root{height:100%;margin:0}body{background:var(--bg);color:var(--fg);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,sans-serif;font-size:14px;line-height:1.5}a,.link{color:var(--accent);text-decoration:none}a:hover,.link:hover{text-decoration:underline}button,input,textarea,select{font:inherit}button:focus-visible,a:focus-visible,input:focus-visible,textarea:focus-visible,select:focus-visible{outline:2px solid var(--accent);outline-offset:2px}code{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:.9em;background:var(--bg-elev-2);padding:1px 5px;border-radius:4px}.dim{color:var(--fg-dim)}.small{font-size:12px}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.skip-link{position:absolute;left:16px;top:-48px;z-index:10;padding:10px 14px;border-radius:8px;background:var(--accent);color:var(--accent-fg)}.skip-link:focus{top:16px}.layout{display:grid;grid-template-columns:220px 1fr;height:100%}.sidebar{background:var(--bg-elev);border-right:1px solid var(--border);padding:16px 0;display:flex;flex-direction:column}.sidebar-brand{display:flex;align-items:center;gap:10px;padding:0 18px 18px;font-weight:600;font-size:16px;border-bottom:1px solid var(--border);margin-bottom:12px}.sidebar nav{display:flex;flex-direction:column}.nav-link{padding:9px 18px;color:var(--fg);border-left:2px solid transparent}.nav-link:hover{background:var(--bg-elev-2);text-decoration:none}.nav-link.active{background:var(--bg-elev-2);border-left-color:var(--accent);color:var(--fg)}.nav-group{display:flex;flex-direction:column}.nav-group-row{display:flex;align-items:stretch}.nav-group-label{flex:1}.nav-group-toggle{background:none;border:none;cursor:pointer;padding:0 14px 0 4px;color:var(--fg-muted, var(--fg));display:flex;align-items:center;justify-content:center;border-left:2px solid transparent}.nav-group-toggle:hover{background:var(--bg-elev-2)}.nav-chevron{display:inline-block;font-size:18px;line-height:1;transition:transform .18s ease;transform:rotate(0)}.nav-chevron-open{transform:rotate(90deg)}.nav-recents{list-style:none;margin:0;padding:0}.nav-recent-link{display:flex;align-items:baseline;justify-content:space-between;gap:6px;width:100%;background:none;border:none;border-left:2px solid transparent;padding:6px 18px 6px 28px;cursor:pointer;color:var(--fg);text-align:left;font-size:13px}.nav-recent-link:hover{background:var(--bg-elev-2);text-decoration:none;border-left-color:var(--accent)}.nav-recent-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}.nav-recent-hint{font-size:11px;flex-shrink:0;opacity:.6}.nav-recents-empty{padding:4px 28px 6px;font-size:12px;margin:0}.main{overflow:hidden;display:flex;flex-direction:column}.app-header{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:16px 24px;border-bottom:1px solid var(--border);background:var(--bg-elev)}.app-header-title{font-size:16px;font-weight:600;margin:0}.app-header-title-row{display:inline-flex;align-items:center;gap:10px}.app-header-user{color:var(--fg-dim);font-size:13px}.mode-badge{display:inline-flex;align-items:center;border-radius:999px;padding:3px 10px;font-size:12px;font-weight:600;line-height:1;border:1px solid transparent}.mode-standalone{background:var(--bg-elev-2);border-color:var(--border);color:var(--fg-dim)}.mode-team{background:color-mix(in srgb,var(--accent) 14%,transparent);border-color:color-mix(in srgb,var(--accent) 35%,var(--border));color:var(--accent)}.sse-badge{display:inline-flex;align-items:center;gap:6px;border-radius:999px;padding:3px 10px;font-size:12px;font-weight:600;line-height:1;border:1px solid transparent}.sse-badge__dot{display:inline-block;width:7px;height:7px;border-radius:50%;flex-shrink:0}.sse-badge--reconnecting{background:color-mix(in srgb,#f59e0b 12%,transparent);border-color:color-mix(in srgb,#f59e0b 35%,var(--border));color:#b45309}.sse-badge--reconnecting .sse-badge__dot{background:#f59e0b;animation:sse-pulse 1.2s ease-in-out infinite}.sse-badge--disconnected{background:color-mix(in srgb,#ef4444 12%,transparent);border-color:color-mix(in srgb,#ef4444 35%,var(--border));color:#b91c1c}.sse-badge--disconnected .sse-badge__dot{background:#ef4444}.sse-badge__reconnect-btn{background:none;border:none;padding:0;margin-left:4px;font-size:12px;font-weight:600;color:inherit;cursor:pointer;text-decoration:underline;text-underline-offset:2px}.sse-badge__reconnect-btn:hover{opacity:.8}@keyframes sse-pulse{0%,to{opacity:1}50%{opacity:.35}}max-width: 760px; margin: 0 auto; padding: 32px; } .loading,.empty-state{padding:32px;color:var(--fg-dim)}.empty-state h2{color:var(--fg);margin-top:0;margin-bottom:8px}.empty-state p{margin:0 0 12px}.empty-state-icon{font-size:28px;margin-bottom:8px;line-height:1}.empty-state-action{margin-top:4px}.auth-screen{min-height:100%;display:grid;place-items:center;padding:32px}.auth-card{width:min(420px,100%);background:var(--bg-elev);border:1px solid var(--border);border-radius:12px;padding:24px}.auth-card h1{margin-top:0;margin-bottom:8px}.auth-card p{margin-top:0;margin-bottom:20px;color:var(--fg-dim)}.page{padding:24px 32px;overflow:auto;flex:1;min-width:0}.page-header{margin-bottom:16px}.page-header h1{margin:0 0 4px;font-size:22px}.error-notice{background:#f871711a;border:1px solid var(--danger);color:var(--danger);padding:12px 14px;border-radius:8px;margin-bottom:16px}.error-notice.inline{margin-bottom:12px}.error-notice-title{margin:0 0 4px;font-size:16px}.error-notice-message{margin:0}.error-notice-actions{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px}.error-details{background:var(--bg-elev-2);padding:12px;border-radius:8px;overflow:auto}.loading-state{display:flex;align-items:flex-start;gap:12px;padding:16px 0;color:var(--fg-dim)}.loading-state.inline{padding:10px 0}.loading-state.centered{justify-content:center;padding:48px 32px}.loading-spinner{width:18px;height:18px;border:2px solid rgba(59,130,246,.25);border-top-color:var(--accent);border-radius:999px;flex:none;margin-top:2px;animation:spin .9s linear infinite}.loading-state-label{color:var(--fg);font-weight:500}.loading-state-detail{margin-top:2px}.btn{background:var(--bg-elev-2);color:var(--fg);border:1px solid var(--border);border-radius:6px;padding:6px 14px;font-size:13px;cursor:pointer}.btn:hover{background:var(--bg-elev)}.btn.primary{background:var(--accent);color:var(--accent-fg);border-color:var(--accent)}.btn.primary:hover{filter:brightness(1.1)}.btn.danger{border-color:var(--danger);color:var(--danger)}.btn.cancel{background:var(--danger);border-color:var(--danger);color:var(--accent-fg)}.chat{display:flex;flex-direction:column;height:100%}.chat-scroll{flex:1;overflow:auto;padding:24px 32px 0}.chat-log{display:flex;flex-direction:column}.turn-wrapper{margin-bottom:18px}.turn-wrapper+.turn-wrapper:not(.has-separator) .bubble{border-top:1px solid var(--border);padding-top:14px}.turn-header{display:flex;align-items:center;gap:8px;margin-bottom:6px;font-size:12px}.turn-header--user{justify-content:flex-end}.turn-actor-badge{display:inline-flex;align-items:center;gap:4px;background:var(--bg-elev);border:1px solid var(--border);border-radius:999px;padding:2px 8px;font-size:11px;font-weight:500;-webkit-user-select:none;user-select:none}.turn-actor-icon{font-size:11px;line-height:1}.turn-ts{font-size:11px;color:var(--fg-dim);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;cursor:default;-webkit-user-select:none;user-select:none}.time-separator{display:flex;align-items:center;gap:10px;margin:16px 0 10px;color:var(--fg-dim);font-size:11px;font-style:italic;-webkit-user-select:none;user-select:none}.time-separator-line{flex:1;height:1px;background:var(--border);opacity:.5}.time-separator-label{white-space:nowrap;opacity:.7;letter-spacing:.02em}.bubble{max-width:800px}.bubble.user{margin-left:auto;text-align:right}.bubble--agent-activity{opacity:.88;border-left:2px solid var(--accent, #6366f1);padding-left:10px}.bubble.user .user-text{display:inline-block;background:var(--user-bubble);border:1px solid var(--border);padding:8px 14px;border-radius:14px;white-space:pre-wrap;text-align:left;margin:0}.route-tag{font-size:11px;color:var(--fg-dim);margin-top:4px}.copy-btn-wrap{position:relative}.copy-btn{position:absolute;top:6px;right:6px;display:flex;align-items:center;justify-content:center;padding:4px;background:var(--bg-elev);border:1px solid var(--border);border-radius:6px;color:var(--fg-dim);cursor:pointer;z-index:1;line-height:0;transition:color .15s,background .15s}.copy-btn:hover{background:var(--bg-elev-2);color:var(--fg)}.copy-btn--copied{color:#4ade80;border-color:#4ade80}@media (hover: hover){.copy-btn{opacity:0;pointer-events:none;transition:opacity .15s,color .15s,background .15s}.copy-btn-wrap:hover .copy-btn,.copy-btn-wrap:focus-within .copy-btn{opacity:1;pointer-events:auto}}.copy-btn--code{top:8px;right:8px}.activity-heartbeat{display:flex;align-items:center;gap:6px;padding:4px 8px;margin:0 0 6px;border-radius:6px;border:1px solid rgba(59,130,246,.35);background:var(--bg-elev-2);font-size:12px;color:var(--fg);transition:opacity .3s}.activity-heartbeat.stale{opacity:.5;border-color:var(--border);color:var(--fg-dim)}.heartbeat-spinner{font-family:ui-monospace,monospace;font-size:11px;color:var(--accent);display:inline-block;animation:spin 1.4s linear infinite}.activity-heartbeat.stale .heartbeat-spinner{animation:none;color:var(--fg-dim)}.heartbeat-agent{font-weight:600}.heartbeat-sep{color:var(--fg-dim)}.heartbeat-action{color:var(--fg-dim);flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.heartbeat-elapsed{font-family:ui-monospace,monospace;font-size:11px;color:var(--fg-dim);white-space:nowrap}.activity-strip{margin:0 0 8px;font-size:12px}.activity-summary{display:flex;flex-wrap:wrap;gap:6px}.activity-pill{display:inline-flex;align-items:center;gap:6px;background:var(--bg-elev);border:1px solid var(--border);color:var(--fg-dim);padding:3px 10px;border-radius:999px;cursor:pointer;font-size:12px}.activity-pill:hover{background:var(--bg-elev-2)}.activity-pill.running{color:var(--accent);border-color:#3b82f673}.activity-pill .glyph{font-family:ui-monospace,monospace;font-size:11px}.activity-pill.running .glyph{display:inline-block;animation:spin 1s linear infinite}.activity-pill .caret{color:var(--fg-dim);font-size:10px}.activity-headlines{display:flex;flex-direction:column;gap:2px;margin-top:6px}.activity-headline{display:inline-flex;align-items:center;gap:6px;padding:2px 4px;color:var(--fg-dim)}.activity-headline.status-running{color:var(--accent)}.activity-headline.status-failed{color:var(--danger)}.activity-headline .glyph{font-family:ui-monospace,monospace;font-size:11px;width:12px;text-align:center}.activity-headline.status-running .glyph{animation:spin 1s linear infinite}.agent-tag{font-size:10px;text-transform:lowercase;background:#3b82f629;color:#93c5fd;border:1px solid rgba(59,130,246,.35);padding:1px 6px;border-radius:4px;letter-spacing:.02em}.activity-thinking,.activity-details{margin-top:8px;padding:10px 12px;background:var(--bg-elev);border:1px solid var(--border);border-radius:6px}.activity-details{display:flex;flex-direction:column;gap:6px}.thinking-block{margin:0;padding:8px;background:var(--bg-elev-2);border-radius:4px;white-space:pre-wrap;font-size:12px;line-height:1.5;max-height:280px;overflow:auto}.activity-row{border:1px solid var(--border);border-radius:6px;background:var(--bg-elev-2)}.activity-row.status-running{border-color:#3b82f673}.activity-row.status-failed{border-color:var(--danger)}.activity-row-head{width:100%;display:flex;align-items:center;gap:8px;background:transparent;border:0;color:var(--fg);text-align:left;padding:6px 10px;cursor:pointer;font-size:12px}.activity-row.status-running .activity-row-head .glyph{animation:spin 1s linear infinite;color:var(--accent)}.activity-row.status-failed .activity-row-head .glyph{color:var(--danger)}.activity-row .glyph{font-family:ui-monospace,monospace;width:12px;text-align:center}.activity-row .caret{margin-left:auto;color:var(--fg-dim)}.activity-row-body{padding:0 10px 10px;display:flex;flex-direction:column;gap:6px}.row-label{font-size:10px;text-transform:uppercase;letter-spacing:.06em;color:var(--fg-dim)}.composer{border-top:1px solid var(--border);background:var(--bg-elev);padding:14px 32px;display:flex;flex-direction:column;gap:8px}.composer textarea{width:100%;background:var(--bg);border:1px solid var(--border);border-radius:8px;color:var(--fg);padding:10px;resize:vertical}.composer-help{margin-top:-2px}.dreaming-indicator{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--fg-dim);padding:4px 0;animation:pulse 2s ease-in-out infinite}.dreaming-indicator-glyph{color:#c4b5fd}.composer-actions{display:flex;justify-content:flex-end;gap:6px}.md{line-height:1.55}.md p:first-child{margin-top:0}.md p:last-child{margin-bottom:0}.md pre{background:var(--bg-elev-2);border-radius:6px;padding:12px;overflow:auto}.md pre code{background:transparent;padding:0}.md table{border-collapse:collapse;margin:1em 0}.md th,.md td{border:1px solid var(--border);padding:6px 10px}.workers-layout{display:grid;grid-template-columns:320px 1fr;gap:18px;align-items:start}.workers-list{display:flex;flex-direction:column;gap:6px}.worker-row{text-align:left;background:var(--bg-elev);border:1px solid var(--border);border-radius:8px;padding:10px 12px;cursor:pointer;color:var(--fg)}.worker-row.selected,.worker-row:hover{background:var(--bg-elev-2)}.worker-row-head{display:flex;justify-content:space-between;align-items:center}.worker-status{font-size:11px;font-weight:600;padding:2px 7px;border-radius:10px;text-transform:uppercase;letter-spacing:.04em}.worker-status--running{background:color-mix(in srgb,var(--accent) 15%,transparent);color:var(--accent)}.worker-status--completed{background:color-mix(in srgb,#4caf50 15%,transparent);color:#4caf50}.worker-status--error{background:color-mix(in srgb,#f44336 15%,transparent);color:#f44336}.worker-row-desc{margin-top:4px;font-size:13px;color:var(--fg)}.workers-detail{background:var(--bg-elev);border:1px solid var(--border);border-radius:8px;padding:18px}.worker-detail-description{margin:4px 0 8px;font-size:15px}.worker-detail-slug,.worker-detail-taskid{font-size:.75em;font-family:var(--font-mono, monospace)}.worker-detail-meta{display:flex;flex-wrap:wrap;align-items:center;gap:4px;margin-bottom:8px}.worker-events{display:flex;flex-direction:column;gap:4px;max-height:320px;overflow-y:auto;background:var(--bg-elev-2);border-radius:6px;padding:10px 12px;margin-bottom:12px;font-size:12px;font-family:var(--font-mono, monospace)}.worker-event{display:flex;gap:8px;align-items:baseline;line-height:1.5}.worker-event-ts{color:var(--text-dim, #888);flex-shrink:0;font-size:11px}.worker-event-body{display:flex;gap:4px;align-items:baseline;flex-wrap:wrap;overflow:hidden}.worker-event-icon{flex-shrink:0}.worker-event--tool_complete .worker-event-icon{opacity:.7}.msg-queued-indicator{font-size:.8em;opacity:.6;vertical-align:middle;-webkit-user-select:none;user-select:none}.msg-queued-backend-indicator{display:inline-block;font-size:.78em;opacity:.75;margin-top:4px;color:var(--fg-dim);-webkit-user-select:none;user-select:none}.output{background:var(--bg-elev-2);padding:12px;border-radius:6px;overflow:auto;white-space:pre-wrap;font-size:13px}.projects-toolbar{margin-bottom:16px}.projects-register-form{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.projects-path-input{background:var(--bg-elev);border:1px solid var(--border);border-radius:6px;color:var(--fg);font-size:13px;padding:6px 10px;width:380px;max-width:100%}.projects-path-input:focus{outline:none;border-color:var(--accent)}.projects-register-error{font-size:12px}.projects-disabled{background:var(--bg-elev);border:1px solid var(--border);border-radius:8px;padding:18px}.projects-empty{padding:24px 0}.projects-list{display:flex;flex-direction:column;gap:8px}.project-row{background:var(--bg-elev);border:1px solid var(--border);border-radius:8px;padding:12px 16px;display:flex;align-items:center;justify-content:space-between;gap:12px}.project-row-info{display:flex;flex-direction:column;gap:4px;min-width:0}.project-root{font-size:14px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.project-meta{display:flex;gap:12px;font-size:12px;flex-wrap:wrap}.project-badge{background:var(--bg-elev-2);border:1px solid var(--border);border-radius:10px;padding:1px 8px;font-size:11px;color:var(--fg)}.project-row-actions{display:flex;gap:6px;flex-shrink:0}.project-context-banner{display:flex;align-items:center;gap:8px;padding:6px 16px;background:color-mix(in srgb,var(--accent) 10%,var(--bg-elev));border-bottom:1px solid color-mix(in srgb,var(--accent) 25%,var(--border));font-size:12px;color:var(--fg-dim);flex-shrink:0}.project-context-icon{font-size:13px;flex-shrink:0}.project-context-name{font-weight:600;color:var(--fg);flex-shrink:0}.project-context-path{color:var(--fg-dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0}.project-context-clear{background:none;border:none;color:var(--fg-dim);cursor:pointer;font-size:16px;line-height:1;padding:0 2px;flex-shrink:0;border-radius:4px}.project-context-clear:hover{color:var(--fg);background:var(--bg-hover)}.project-chat-header{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:8px 16px;background:color-mix(in srgb,var(--accent) 8%,var(--bg-elev));border-bottom:1px solid color-mix(in srgb,var(--accent) 20%,var(--border));flex-shrink:0}.project-chat-header-identity{display:flex;align-items:center;gap:8px;min-width:0;overflow:hidden}.project-chat-icon{font-size:16px;flex-shrink:0}.project-chat-title{font-size:14px;white-space:nowrap;flex-shrink:0}.project-chat-path{font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.wiki{display:flex;flex-direction:column;min-height:100%}.wiki-layout{display:grid;grid-template-columns:minmax(320px,360px) minmax(0,1fr);gap:20px;flex:1;min-height:0}.wiki-sidebar,.wiki-main{min-height:0}.wiki-sidebar{display:flex;flex-direction:column;background:var(--bg-elev);border:1px solid var(--border);border-radius:12px;overflow:hidden}.wiki-sidebar-header{position:sticky;top:0;z-index:1;display:flex;flex-direction:column;gap:14px;padding:16px;border-bottom:1px solid var(--border);background:linear-gradient(180deg,var(--bg-elev) 0%,rgba(22,27,34,.98) 100%)}.wiki-sidebar-header-row{display:flex;justify-content:space-between;align-items:flex-start;gap:12px}.wiki-sidebar-header-row h2{margin:0 0 4px;font-size:16px}.wiki-sidebar-header-row p{margin:0}.wiki-search{display:flex;flex-direction:column;gap:12px}.wiki-search-field input,.wiki-filter select{width:100%;background:var(--bg);border:1px solid var(--border);color:var(--fg);padding:9px 10px;border-radius:8px}.wiki-filter{display:flex;flex-direction:column;gap:6px;font-size:12px;color:var(--fg-dim)}.wiki-search-meta,.wiki-shortcuts,.wiki-scope-legend{color:var(--fg-dim)}.wiki-scope-header-row{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.wiki-scope-header-row h1{margin:0}.wiki-shortcuts{border-top:1px solid var(--border);padding-top:12px}.wiki-scope-legend{display:flex;flex-wrap:wrap;gap:8px}.wiki-scope-legend>span{display:inline-flex;align-items:center;gap:4px}.wiki-sidebar-body{flex:1;min-height:0;overflow:auto;padding:12px}.wiki-tree,.wiki-tree-children{list-style:none;margin:0;padding:0}.wiki-tree-children{margin-top:4px}.wiki-node{margin:2px 0}.wiki-node-button{width:100%;display:flex;align-items:center;gap:8px;padding:7px 10px;background:transparent;border:1px solid transparent;border-radius:8px;color:var(--fg);text-align:left;cursor:pointer}.wiki-node-folder-button{color:var(--fg-dim)}.wiki-node-folder-button:hover,.wiki-node-folder-button.expanded,.wiki-node-page-button:hover{background:var(--bg-elev-2);border-color:var(--border);color:var(--fg)}.wiki-node-page-button{align-items:flex-start}.wiki-node-page-button.selected{background:#3b82f61f;border-color:#3b82f659;box-shadow:inset 2px 0 0 var(--accent)}.wiki-node-icon{width:14px;flex:none;text-align:center;color:var(--fg-dim)}.wiki-node-page-button.selected .wiki-node-icon{color:#93c5fd}.wiki-node-page-button.selected .wiki-node-scope-icon-personal{color:#ddd6fe}.wiki-node-page-button.selected .wiki-node-scope-icon-team{color:#a7f3d0}.wiki-node-content{min-width:0;display:flex;flex:1;flex-direction:column;gap:4px}.wiki-node-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wiki-node-meta{display:flex;flex-wrap:wrap;gap:6px;font-size:11px}.wiki-node-count{margin-left:auto;border:1px solid var(--border);border-radius:999px;padding:0 6px;font-size:11px;color:var(--fg-dim)}.wiki-main{min-width:0;display:flex;background:var(--bg-elev);border:1px solid var(--border);border-radius:12px;overflow:hidden}.wiki-main>.wiki-empty-state{width:100%}.wiki-document{width:100%;min-height:0;display:flex;flex-direction:column}.wiki-page-header{position:sticky;top:0;z-index:1;padding:18px 22px 16px;border-bottom:1px solid var(--border);background:linear-gradient(180deg,var(--bg-elev) 0%,rgba(22,27,34,.98) 100%)}.wiki-page-header-main{display:flex;justify-content:space-between;align-items:flex-start;gap:16px}.wiki-page-title-block h2{margin:0;font-size:28px;line-height:1.2}.wiki-page-summary{margin:8px 0 0;max-width:72ch;color:var(--fg-dim)}.wiki-page-actions{display:flex;gap:8px;flex:none}.wiki-breadcrumbs ol{display:flex;flex-wrap:wrap;gap:8px;list-style:none;margin:0 0 12px;padding:0}.wiki-breadcrumbs li{display:flex;align-items:center}.wiki-breadcrumbs li+li:before{content:"/";margin-right:8px;color:var(--fg-dim)}.wiki-breadcrumb-button{padding:0;border:0;background:transparent;color:var(--fg-dim);cursor:pointer}.wiki-breadcrumb-button:hover{color:var(--fg);text-decoration:underline}.wiki-meta{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-top:14px;font-size:12px;color:var(--fg-dim)}.wiki-badge,.wiki-tag,.wiki-meta-item{display:inline-flex;align-items:center;border:1px solid var(--border);border-radius:999px;padding:3px 8px;background:var(--bg-elev-2)}.wiki-badge{color:#93c5fd;border-color:#3b82f659}.wiki-scope-badge{display:inline-flex;align-items:center;gap:4px}.wiki-scope-badge-personal{color:#c4b5fd;border-color:#c4b5fd59;background:#c4b5fd14}.wiki-scope-badge-team{color:#6ee7b7;border-color:#6ee7b759;background:#6ee7b714}.wiki-node-scope-icon-personal{color:#c4b5fd}.wiki-node-scope-icon-team{color:#6ee7b7}.wiki-tag{color:var(--fg)}.wiki-meta-path{max-width:100%;overflow:auto;white-space:nowrap}.wiki-document-body{flex:1;min-height:0;overflow:auto}.wiki-article{max-width:76ch;padding:24px 22px 32px}.wiki-empty-state{display:flex;flex-direction:column;align-items:flex-start;justify-content:center;gap:12px;margin:auto;max-width:56ch;padding:32px}.wiki-empty-state.compact{margin:0;max-width:none;padding:20px 12px}.wiki-empty-state h2{margin:0;font-size:20px}.wiki-empty-state p{margin:0;color:var(--fg-dim)}.wiki-empty-state-actions{display:flex;flex-wrap:wrap;gap:8px}@media (max-width: 960px){.wiki-layout{grid-template-columns:1fr}.wiki-sidebar{max-height:50vh}.wiki-page-header-main,.wiki-sidebar-header-row,.wiki-scope-legend{flex-direction:column}.wiki-page-actions{width:100%}.wiki-page-actions .btn{flex:1}}.wiki-edit .row{display:flex;gap:12px;margin-bottom:12px}.wiki-edit input[type=text]{flex:1;background:var(--bg);border:1px solid var(--border);color:var(--fg);padding:8px;border-radius:6px}.wiki-edit label{display:block;width:100%;font-size:12px;color:var(--fg-dim)}.wiki-editor{margin-bottom:16px}.skill-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:14px}.skill-card{background:var(--bg-elev);border:1px solid var(--border);border-radius:8px;padding:14px}.skill-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:4px}.tag{font-size:10px;text-transform:uppercase;padding:2px 6px;border-radius:4px;letter-spacing:.05em;background:var(--bg-elev-2);color:var(--fg-dim)}.tag-bundled{color:#93c5fd}.tag-local{color:#86efac}.tag-global{color:#fcd34d}.history-list{list-style:none;padding:0}.history-list li{padding:6px 0;border-bottom:1px solid var(--border)}.settings section{margin-bottom:28px}.settings-field{display:flex;flex-direction:column;gap:6px}.settings-field-label{font-size:12px;color:var(--fg-dim)}.settings select{background:var(--bg);color:var(--fg);border:1px solid var(--border);border-radius:6px;padding:6px 10px}.row{display:flex;align-items:center;gap:8px}.settings-row{align-items:flex-end}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes pulse{0%,to{opacity:.4}50%{opacity:1}}.ralph{max-width:760px}.ralph-section{margin-top:24px}.ralph-section h2{font-size:15px;font-weight:600;margin-bottom:10px;color:var(--fg)}.ralph-status-card{background:var(--bg-elev);border:1px solid var(--border);border-radius:8px;padding:14px 16px;display:flex;flex-direction:column;gap:8px}.ralph-status-row{display:flex;align-items:center;gap:10px;font-size:14px}.ralph-status-label{min-width:90px;color:var(--text-dim, #888);font-size:12px;text-transform:uppercase;letter-spacing:.04em}.ralph-badge{font-size:11px;font-weight:600;padding:2px 8px;border-radius:10px;text-transform:uppercase;letter-spacing:.04em}.ralph-badge--running{background:color-mix(in srgb,var(--accent) 15%,transparent);color:var(--accent)}.ralph-badge--stopped{background:color-mix(in srgb,#888 15%,transparent);color:#888}.ralph-mono{font-family:var(--font-mono, monospace);font-size:13px}.ralph-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:400px}.ralph-controls{background:var(--bg-elev);border:1px solid var(--border);border-radius:8px;padding:14px 16px;display:flex;flex-direction:column;gap:12px}.ralph-form-row{display:flex;align-items:center;gap:10px;font-size:14px}.ralph-form-row label{min-width:140px;color:var(--text-dim, #888);font-size:12px;text-transform:uppercase;letter-spacing:.04em}.ralph-select,.ralph-input{background:var(--bg-elev-2, var(--bg-elev));border:1px solid var(--border);border-radius:5px;color:var(--fg);font-size:13px;padding:5px 8px;min-width:240px}.ralph-input--narrow{min-width:80px;width:80px}.ralph-select:focus,.ralph-input:focus{outline:2px solid var(--accent);outline-offset:1px}.btn{border:none;border-radius:6px;font-size:13px;font-weight:600;padding:6px 14px;cursor:pointer;transition:opacity .1s;align-self:flex-start}.btn:disabled{opacity:.5;cursor:not-allowed}.btn-primary{background:var(--accent);color:#fff}.btn-primary:hover:not(:disabled){opacity:.85}.btn-danger{background:#f44336;color:#fff}.btn-danger:hover:not(:disabled){opacity:.85}.ralph-queue-count{font-weight:400;color:var(--text-dim, #888);font-size:13px}.ralph-queue-source{margin-bottom:8px}.ralph-queue{display:flex;flex-direction:column;gap:6px}.ralph-issue{display:block;text-decoration:none;color:inherit;background:var(--bg-elev);border:1px solid var(--border);border-radius:8px;padding:10px 12px;transition:background .1s}.ralph-issue:hover{background:var(--bg-elev-2)}.ralph-issue-head{display:flex;align-items:baseline;gap:8px;margin-bottom:4px}.ralph-issue-number{font-family:var(--font-mono, monospace);font-size:12px;color:var(--text-dim, #888);flex-shrink:0}.ralph-issue-title{font-size:14px;font-weight:500}.ralph-issue-meta{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.ralph-labels{display:flex;flex-wrap:wrap;gap:4px}.ralph-label{font-size:11px;padding:1px 6px;border-radius:8px;background:color-mix(in srgb,#888 12%,transparent);color:var(--fg)}.ralph-label--agent{background:color-mix(in srgb,var(--accent) 12%,transparent);color:var(--accent)}.ralph-label--triage{background:color-mix(in srgb,#9c27b0 12%,transparent);color:#9c27b0}.ralph-label--urgent{background:color-mix(in srgb,#f44336 12%,transparent);color:#f44336}.link-btn{background:none;border:none;color:var(--accent);font-size:inherit;cursor:pointer;padding:0;text-decoration:underline}
|
|
10
|
+
*/.hljs{color:#c9d1d9;background:#0d1117}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#ff7b72}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#d2a8ff}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-variable,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id{color:#79c0ff}.hljs-regexp,.hljs-string,.hljs-meta .hljs-string{color:#a5d6ff}.hljs-built_in,.hljs-symbol{color:#ffa657}.hljs-comment,.hljs-code,.hljs-formula{color:#8b949e}.hljs-name,.hljs-quote,.hljs-selector-tag,.hljs-selector-pseudo{color:#7ee787}.hljs-subst{color:#c9d1d9}.hljs-section{color:#1f6feb;font-weight:700}.hljs-bullet{color:#f2cc60}.hljs-emphasis{color:#c9d1d9;font-style:italic}.hljs-strong{color:#c9d1d9;font-weight:700}.hljs-addition{color:#aff5b4;background-color:#033a16}.hljs-deletion{color:#ffdcd7;background-color:#67060c}:root{color-scheme:dark;--bg: #0e1116;--bg-elev: #161b22;--bg-elev-2: #21262d;--fg: #e6edf3;--fg-dim: #8b949e;--border: #30363d;--accent: #3b82f6;--accent-fg: #ffffff;--danger: #f87171;--user-bubble: #1e293b}*{box-sizing:border-box}html,body,#root{height:100%;margin:0}body{background:var(--bg);color:var(--fg);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,sans-serif;font-size:14px;line-height:1.5}a,.link{color:var(--accent);text-decoration:none}a:hover,.link:hover{text-decoration:underline}button,input,textarea,select{font:inherit}button:focus-visible,a:focus-visible,input:focus-visible,textarea:focus-visible,select:focus-visible{outline:2px solid var(--accent);outline-offset:2px}code{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:.9em;background:var(--bg-elev-2);padding:1px 5px;border-radius:4px}.dim{color:var(--fg-dim)}.small{font-size:12px}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.skip-link{position:absolute;left:16px;top:-48px;z-index:10;padding:10px 14px;border-radius:8px;background:var(--accent);color:var(--accent-fg)}.skip-link:focus{top:16px}.layout{display:grid;grid-template-columns:220px 1fr;height:100%}.sidebar{background:var(--bg-elev);border-right:1px solid var(--border);padding:16px 0;display:flex;flex-direction:column}.sidebar-brand{display:flex;align-items:center;gap:10px;padding:0 18px 18px;font-weight:600;font-size:16px;border-bottom:1px solid var(--border);margin-bottom:12px}.sidebar nav{display:flex;flex-direction:column}.nav-link{padding:9px 18px;color:var(--fg);border-left:2px solid transparent}.nav-link:hover{background:var(--bg-elev-2);text-decoration:none}.nav-link.active{background:var(--bg-elev-2);border-left-color:var(--accent);color:var(--fg)}.nav-group{display:flex;flex-direction:column}.nav-group-row{display:flex;align-items:stretch}.nav-group-label{flex:1}.nav-group-toggle{background:none;border:none;cursor:pointer;padding:0 14px 0 4px;color:var(--fg-muted, var(--fg));display:flex;align-items:center;justify-content:center;border-left:2px solid transparent}.nav-group-toggle:hover{background:var(--bg-elev-2)}.nav-chevron{display:inline-block;font-size:18px;line-height:1;transition:transform .18s ease;transform:rotate(0)}.nav-chevron-open{transform:rotate(90deg)}.nav-recents{list-style:none;margin:0;padding:0}.nav-recent-link{display:flex;align-items:baseline;justify-content:space-between;gap:6px;width:100%;background:none;border:none;border-left:2px solid transparent;padding:6px 18px 6px 28px;cursor:pointer;color:var(--fg);text-align:left;font-size:13px}.nav-recent-link:hover{background:var(--bg-elev-2);text-decoration:none;border-left-color:var(--accent)}.nav-recent-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}.nav-recent-hint{font-size:11px;flex-shrink:0;opacity:.6}.nav-recents-empty{padding:4px 28px 6px;font-size:12px;margin:0}.main{overflow:hidden;display:flex;flex-direction:column}.app-header{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:16px 24px;border-bottom:1px solid var(--border);background:var(--bg-elev)}.app-header-title{font-size:16px;font-weight:600;margin:0}.app-header-title-row{display:inline-flex;align-items:center;gap:10px}.app-header-user{color:var(--fg-dim);font-size:13px}.mode-badge{display:inline-flex;align-items:center;border-radius:999px;padding:3px 10px;font-size:12px;font-weight:600;line-height:1;border:1px solid transparent}.mode-standalone{background:var(--bg-elev-2);border-color:var(--border);color:var(--fg-dim)}.mode-team{background:color-mix(in srgb,var(--accent) 14%,transparent);border-color:color-mix(in srgb,var(--accent) 35%,var(--border));color:var(--accent)}.sse-badge{display:inline-flex;align-items:center;gap:6px;border-radius:999px;padding:3px 10px;font-size:12px;font-weight:600;line-height:1;border:1px solid transparent}.sse-badge__dot{display:inline-block;width:7px;height:7px;border-radius:50%;flex-shrink:0}.sse-badge--reconnecting{background:color-mix(in srgb,#f59e0b 12%,transparent);border-color:color-mix(in srgb,#f59e0b 35%,var(--border));color:#b45309}.sse-badge--reconnecting .sse-badge__dot{background:#f59e0b;animation:sse-pulse 1.2s ease-in-out infinite}.sse-badge--disconnected{background:color-mix(in srgb,#ef4444 12%,transparent);border-color:color-mix(in srgb,#ef4444 35%,var(--border));color:#b91c1c}.sse-badge--disconnected .sse-badge__dot{background:#ef4444}.sse-badge__reconnect-btn{background:none;border:none;padding:0;margin-left:4px;font-size:12px;font-weight:600;color:inherit;cursor:pointer;text-decoration:underline;text-underline-offset:2px}.sse-badge__reconnect-btn:hover{opacity:.8}@keyframes sse-pulse{0%,to{opacity:1}50%{opacity:.35}}max-width: 760px; margin: 0 auto; padding: 32px; } .loading,.empty-state{padding:32px;color:var(--fg-dim)}.empty-state h2{color:var(--fg);margin-top:0;margin-bottom:8px}.empty-state p{margin:0 0 12px}.empty-state-icon{font-size:28px;margin-bottom:8px;line-height:1}.empty-state-action{margin-top:4px}.auth-screen{min-height:100%;display:grid;place-items:center;padding:32px}.auth-card{width:min(420px,100%);background:var(--bg-elev);border:1px solid var(--border);border-radius:12px;padding:24px}.auth-card h1{margin-top:0;margin-bottom:8px}.auth-card p{margin-top:0;margin-bottom:20px;color:var(--fg-dim)}.page{padding:24px 32px;overflow:auto;flex:1;min-width:0}.page-header{margin-bottom:16px}.page-header h1{margin:0 0 4px;font-size:22px}.error-notice{background:#f871711a;border:1px solid var(--danger);color:var(--danger);padding:12px 14px;border-radius:8px;margin-bottom:16px}.error-notice.inline{margin-bottom:12px}.error-notice-title{margin:0 0 4px;font-size:16px}.error-notice-message{margin:0}.error-notice-actions{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px}.error-details{background:var(--bg-elev-2);padding:12px;border-radius:8px;overflow:auto}.loading-state{display:flex;align-items:flex-start;gap:12px;padding:16px 0;color:var(--fg-dim)}.loading-state.inline{padding:10px 0}.loading-state.centered{justify-content:center;padding:48px 32px}.loading-spinner{width:18px;height:18px;border:2px solid rgba(59,130,246,.25);border-top-color:var(--accent);border-radius:999px;flex:none;margin-top:2px;animation:spin .9s linear infinite}.loading-state-label{color:var(--fg);font-weight:500}.loading-state-detail{margin-top:2px}.btn{background:var(--bg-elev-2);color:var(--fg);border:1px solid var(--border);border-radius:6px;padding:6px 14px;font-size:13px;cursor:pointer}.btn:hover{background:var(--bg-elev)}.btn.primary{background:var(--accent);color:var(--accent-fg);border-color:var(--accent)}.btn.primary:hover{filter:brightness(1.1)}.btn.danger{border-color:var(--danger);color:var(--danger)}.btn.cancel{background:var(--danger);border-color:var(--danger);color:var(--accent-fg)}.chat{display:flex;flex-direction:column;height:100%}.chat-scroll{flex:1;overflow:auto;padding:24px 32px 0}.chat-log{display:flex;flex-direction:column}.turn-wrapper{margin-bottom:18px}.turn-wrapper+.turn-wrapper:not(.has-separator) .bubble{border-top:1px solid var(--border);padding-top:14px}.turn-header{display:flex;align-items:center;gap:8px;margin-bottom:6px;font-size:12px}.turn-header--user{justify-content:flex-end}.turn-actor-badge{display:inline-flex;align-items:center;gap:4px;background:var(--bg-elev);border:1px solid var(--border);border-radius:999px;padding:2px 8px;font-size:11px;font-weight:500;-webkit-user-select:none;user-select:none}.turn-actor-icon{font-size:11px;line-height:1}.turn-ts{font-size:11px;color:var(--fg-dim);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;cursor:default;-webkit-user-select:none;user-select:none}.time-separator{display:flex;align-items:center;gap:10px;margin:16px 0 10px;color:var(--fg-dim);font-size:11px;font-style:italic;-webkit-user-select:none;user-select:none}.time-separator-line{flex:1;height:1px;background:var(--border);opacity:.5}.time-separator-label{white-space:nowrap;opacity:.7;letter-spacing:.02em}.bubble{max-width:800px}.bubble.user{margin-left:auto;text-align:right}.bubble--agent-activity{opacity:.88;border-left:2px solid var(--accent, #6366f1);padding-left:10px}.bubble.user .user-text{display:inline-block;background:var(--user-bubble);border:1px solid var(--border);padding:8px 14px;border-radius:14px;white-space:pre-wrap;text-align:left;margin:0}.route-tag{font-size:11px;color:var(--fg-dim);margin-top:4px}.copy-btn-wrap{position:relative}.copy-btn{position:absolute;top:6px;right:6px;display:flex;align-items:center;justify-content:center;padding:4px;background:var(--bg-elev);border:1px solid var(--border);border-radius:6px;color:var(--fg-dim);cursor:pointer;z-index:1;line-height:0;transition:color .15s,background .15s}.copy-btn:hover{background:var(--bg-elev-2);color:var(--fg)}.copy-btn--copied{color:#4ade80;border-color:#4ade80}@media (hover: hover){.copy-btn{opacity:0;pointer-events:none;transition:opacity .15s,color .15s,background .15s}.copy-btn-wrap:hover .copy-btn,.copy-btn-wrap:focus-within .copy-btn{opacity:1;pointer-events:auto}}.copy-btn--code{top:8px;right:8px}.activity-heartbeat{display:flex;align-items:center;gap:6px;padding:4px 8px;margin:0 0 6px;border-radius:6px;border:1px solid rgba(59,130,246,.35);background:var(--bg-elev-2);font-size:12px;color:var(--fg);transition:opacity .3s}.activity-heartbeat.stale{opacity:.5;border-color:var(--border);color:var(--fg-dim)}.heartbeat-spinner{font-family:ui-monospace,monospace;font-size:11px;color:var(--accent);display:inline-block;animation:spin 1.4s linear infinite}.activity-heartbeat.stale .heartbeat-spinner{animation:none;color:var(--fg-dim)}.heartbeat-agent{font-weight:600}.heartbeat-sep{color:var(--fg-dim)}.heartbeat-action{color:var(--fg-dim);flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.heartbeat-elapsed{font-family:ui-monospace,monospace;font-size:11px;color:var(--fg-dim);white-space:nowrap}.activity-strip{margin:0 0 8px;font-size:12px}.activity-summary{display:flex;flex-wrap:wrap;gap:6px}.activity-pill{display:inline-flex;align-items:center;gap:6px;background:var(--bg-elev);border:1px solid var(--border);color:var(--fg-dim);padding:3px 10px;border-radius:999px;cursor:pointer;font-size:12px}.activity-pill:hover{background:var(--bg-elev-2)}.activity-pill.running{color:var(--accent);border-color:#3b82f673}.activity-pill .glyph{font-family:ui-monospace,monospace;font-size:11px}.activity-pill.running .glyph{display:inline-block;animation:spin 1s linear infinite}.activity-pill .caret{color:var(--fg-dim);font-size:10px}.activity-headlines{display:flex;flex-direction:column;gap:2px;margin-top:6px}.activity-headline{display:inline-flex;align-items:center;gap:6px;padding:2px 4px;color:var(--fg-dim)}.activity-headline.status-running{color:var(--accent)}.activity-headline.status-failed{color:var(--danger)}.activity-headline .glyph{font-family:ui-monospace,monospace;font-size:11px;width:12px;text-align:center}.activity-headline.status-running .glyph{animation:spin 1s linear infinite}.agent-tag{font-size:10px;text-transform:lowercase;background:#3b82f629;color:#93c5fd;border:1px solid rgba(59,130,246,.35);padding:1px 6px;border-radius:4px;letter-spacing:.02em}.activity-thinking,.activity-details{margin-top:8px;padding:10px 12px;background:var(--bg-elev);border:1px solid var(--border);border-radius:6px}.activity-details{display:flex;flex-direction:column;gap:6px}.thinking-block{margin:0;padding:8px;background:var(--bg-elev-2);border-radius:4px;white-space:pre-wrap;font-size:12px;line-height:1.5;max-height:280px;overflow:auto}.activity-row{border:1px solid var(--border);border-radius:6px;background:var(--bg-elev-2)}.activity-row.status-running{border-color:#3b82f673}.activity-row.status-failed{border-color:var(--danger)}.activity-row-head{width:100%;display:flex;align-items:center;gap:8px;background:transparent;border:0;color:var(--fg);text-align:left;padding:6px 10px;cursor:pointer;font-size:12px}.activity-row.status-running .activity-row-head .glyph{animation:spin 1s linear infinite;color:var(--accent)}.activity-row.status-failed .activity-row-head .glyph{color:var(--danger)}.activity-row .glyph{font-family:ui-monospace,monospace;width:12px;text-align:center}.activity-row .caret{margin-left:auto;color:var(--fg-dim)}.activity-row-body{padding:0 10px 10px;display:flex;flex-direction:column;gap:6px}.row-label{font-size:10px;text-transform:uppercase;letter-spacing:.06em;color:var(--fg-dim)}.composer{border-top:1px solid var(--border);background:var(--bg-elev);padding:14px 32px;display:flex;flex-direction:column;gap:8px}.composer textarea{width:100%;background:var(--bg);border:1px solid var(--border);border-radius:8px;color:var(--fg);padding:10px;resize:vertical}.composer-help{margin-top:-2px}.dreaming-indicator{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--fg-dim);padding:4px 0;animation:pulse 2s ease-in-out infinite}.dreaming-indicator-glyph{color:#c4b5fd}.composer-actions{display:flex;justify-content:flex-end;gap:6px}.md{line-height:1.55}.md p:first-child{margin-top:0}.md p:last-child{margin-bottom:0}.md pre{background:var(--bg-elev-2);border-radius:6px;padding:12px;overflow:auto}.md pre code{background:transparent;padding:0}.md table{border-collapse:collapse;margin:1em 0}.md th,.md td{border:1px solid var(--border);padding:6px 10px}.workers-layout{display:grid;grid-template-columns:320px 1fr;gap:18px;align-items:start}.workers-list{display:flex;flex-direction:column;gap:6px}.worker-row{text-align:left;background:var(--bg-elev);border:1px solid var(--border);border-radius:8px;padding:10px 12px;cursor:pointer;color:var(--fg)}.worker-row.selected,.worker-row:hover{background:var(--bg-elev-2)}.worker-row-head{display:flex;justify-content:space-between;align-items:center}.worker-status{font-size:11px;font-weight:600;padding:2px 7px;border-radius:10px;text-transform:uppercase;letter-spacing:.04em}.worker-status--running{background:color-mix(in srgb,var(--accent) 15%,transparent);color:var(--accent)}.worker-status--completed{background:color-mix(in srgb,#4caf50 15%,transparent);color:#4caf50}.worker-status--error{background:color-mix(in srgb,#f44336 15%,transparent);color:#f44336}.worker-row-desc{margin-top:4px;font-size:13px;color:var(--fg)}.workers-detail{background:var(--bg-elev);border:1px solid var(--border);border-radius:8px;padding:18px}.worker-detail-description{margin:4px 0 8px;font-size:15px}.worker-detail-slug,.worker-detail-taskid{font-size:.75em;font-family:var(--font-mono, monospace)}.worker-detail-meta{display:flex;flex-wrap:wrap;align-items:center;gap:4px;margin-bottom:8px}.worker-events{display:flex;flex-direction:column;gap:4px;max-height:320px;overflow-y:auto;background:var(--bg-elev-2);border-radius:6px;padding:10px 12px;margin-bottom:12px;font-size:12px;font-family:var(--font-mono, monospace)}.worker-event{display:flex;gap:8px;align-items:baseline;line-height:1.5}.worker-event-ts{color:var(--text-dim, #888);flex-shrink:0;font-size:11px}.worker-event-body{display:flex;gap:4px;align-items:baseline;flex-wrap:wrap;overflow:hidden}.worker-event-icon{flex-shrink:0}.worker-event--tool_complete .worker-event-icon{opacity:.7}.msg-queued-indicator{font-size:.8em;opacity:.6;vertical-align:middle;-webkit-user-select:none;user-select:none}.msg-queued-backend-indicator{display:inline-block;font-size:.78em;opacity:.75;margin-top:4px;color:var(--fg-dim);-webkit-user-select:none;user-select:none}.output{background:var(--bg-elev-2);padding:12px;border-radius:6px;overflow:auto;white-space:pre-wrap;font-size:13px}.projects-toolbar{margin-bottom:16px}.projects-register-form{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.projects-path-input{background:var(--bg-elev);border:1px solid var(--border);border-radius:6px;color:var(--fg);font-size:13px;padding:6px 10px;width:380px;max-width:100%}.projects-path-input:focus{outline:none;border-color:var(--accent)}.projects-register-error{font-size:12px}.projects-disabled{background:var(--bg-elev);border:1px solid var(--border);border-radius:8px;padding:18px}.projects-empty{padding:24px 0}.projects-list{display:flex;flex-direction:column;gap:8px}.project-row{background:var(--bg-elev);border:1px solid var(--border);border-radius:8px;padding:12px 16px;display:flex;align-items:center;justify-content:space-between;gap:12px}.project-row-info{display:flex;flex-direction:column;gap:4px;min-width:0}.project-root{font-size:14px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.project-meta{display:flex;gap:12px;font-size:12px;flex-wrap:wrap}.project-badge{background:var(--bg-elev-2);border:1px solid var(--border);border-radius:10px;padding:1px 8px;font-size:11px;color:var(--fg)}.project-row-actions{display:flex;gap:6px;flex-shrink:0}.project-chat-header{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:8px 16px;background:color-mix(in srgb,var(--accent) 8%,var(--bg-elev));border-bottom:1px solid color-mix(in srgb,var(--accent) 20%,var(--border));flex-shrink:0}.project-chat-header-identity{display:flex;align-items:center;gap:8px;min-width:0;overflow:hidden}.project-chat-icon{font-size:16px;flex-shrink:0}.project-chat-title{font-size:14px;white-space:nowrap;flex-shrink:0}.project-chat-path{font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.wiki{display:flex;flex-direction:column;min-height:100%}.wiki-layout{display:grid;grid-template-columns:minmax(320px,360px) minmax(0,1fr);gap:20px;flex:1;min-height:0}.wiki-sidebar,.wiki-main{min-height:0}.wiki-sidebar{display:flex;flex-direction:column;background:var(--bg-elev);border:1px solid var(--border);border-radius:12px;overflow:hidden}.wiki-sidebar-header{position:sticky;top:0;z-index:1;display:flex;flex-direction:column;gap:14px;padding:16px;border-bottom:1px solid var(--border);background:linear-gradient(180deg,var(--bg-elev) 0%,rgba(22,27,34,.98) 100%)}.wiki-sidebar-header-row{display:flex;justify-content:space-between;align-items:flex-start;gap:12px}.wiki-sidebar-header-row h2{margin:0 0 4px;font-size:16px}.wiki-sidebar-header-row p{margin:0}.wiki-search{display:flex;flex-direction:column;gap:12px}.wiki-search-field input,.wiki-filter select{width:100%;background:var(--bg);border:1px solid var(--border);color:var(--fg);padding:9px 10px;border-radius:8px}.wiki-filter{display:flex;flex-direction:column;gap:6px;font-size:12px;color:var(--fg-dim)}.wiki-search-meta,.wiki-shortcuts,.wiki-scope-legend{color:var(--fg-dim)}.wiki-scope-header-row{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.wiki-scope-header-row h1{margin:0}.wiki-shortcuts{border-top:1px solid var(--border);padding-top:12px}.wiki-scope-legend{display:flex;flex-wrap:wrap;gap:8px}.wiki-scope-legend>span{display:inline-flex;align-items:center;gap:4px}.wiki-sidebar-body{flex:1;min-height:0;overflow:auto;padding:12px}.wiki-tree,.wiki-tree-children{list-style:none;margin:0;padding:0}.wiki-tree-children{margin-top:4px}.wiki-node{margin:2px 0}.wiki-node-button{width:100%;display:flex;align-items:center;gap:8px;padding:7px 10px;background:transparent;border:1px solid transparent;border-radius:8px;color:var(--fg);text-align:left;cursor:pointer}.wiki-node-folder-button{color:var(--fg-dim)}.wiki-node-folder-button:hover,.wiki-node-folder-button.expanded,.wiki-node-page-button:hover{background:var(--bg-elev-2);border-color:var(--border);color:var(--fg)}.wiki-node-page-button{align-items:flex-start}.wiki-node-page-button.selected{background:#3b82f61f;border-color:#3b82f659;box-shadow:inset 2px 0 0 var(--accent)}.wiki-node-icon{width:14px;flex:none;text-align:center;color:var(--fg-dim)}.wiki-node-page-button.selected .wiki-node-icon{color:#93c5fd}.wiki-node-page-button.selected .wiki-node-scope-icon-personal{color:#ddd6fe}.wiki-node-page-button.selected .wiki-node-scope-icon-team{color:#a7f3d0}.wiki-node-content{min-width:0;display:flex;flex:1;flex-direction:column;gap:4px}.wiki-node-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wiki-node-meta{display:flex;flex-wrap:wrap;gap:6px;font-size:11px}.wiki-node-count{margin-left:auto;border:1px solid var(--border);border-radius:999px;padding:0 6px;font-size:11px;color:var(--fg-dim)}.wiki-main{min-width:0;display:flex;background:var(--bg-elev);border:1px solid var(--border);border-radius:12px;overflow:hidden}.wiki-main>.wiki-empty-state{width:100%}.wiki-document{width:100%;min-height:0;display:flex;flex-direction:column}.wiki-page-header{position:sticky;top:0;z-index:1;padding:18px 22px 16px;border-bottom:1px solid var(--border);background:linear-gradient(180deg,var(--bg-elev) 0%,rgba(22,27,34,.98) 100%)}.wiki-page-header-main{display:flex;justify-content:space-between;align-items:flex-start;gap:16px}.wiki-page-title-block h2{margin:0;font-size:28px;line-height:1.2}.wiki-page-summary{margin:8px 0 0;max-width:72ch;color:var(--fg-dim)}.wiki-page-actions{display:flex;gap:8px;flex:none}.wiki-breadcrumbs ol{display:flex;flex-wrap:wrap;gap:8px;list-style:none;margin:0 0 12px;padding:0}.wiki-breadcrumbs li{display:flex;align-items:center}.wiki-breadcrumbs li+li:before{content:"/";margin-right:8px;color:var(--fg-dim)}.wiki-breadcrumb-button{padding:0;border:0;background:transparent;color:var(--fg-dim);cursor:pointer}.wiki-breadcrumb-button:hover{color:var(--fg);text-decoration:underline}.wiki-meta{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-top:14px;font-size:12px;color:var(--fg-dim)}.wiki-badge,.wiki-tag,.wiki-meta-item{display:inline-flex;align-items:center;border:1px solid var(--border);border-radius:999px;padding:3px 8px;background:var(--bg-elev-2)}.wiki-badge{color:#93c5fd;border-color:#3b82f659}.wiki-scope-badge{display:inline-flex;align-items:center;gap:4px}.wiki-scope-badge-personal{color:#c4b5fd;border-color:#c4b5fd59;background:#c4b5fd14}.wiki-scope-badge-team{color:#6ee7b7;border-color:#6ee7b759;background:#6ee7b714}.wiki-node-scope-icon-personal{color:#c4b5fd}.wiki-node-scope-icon-team{color:#6ee7b7}.wiki-tag{color:var(--fg)}.wiki-meta-path{max-width:100%;overflow:auto;white-space:nowrap}.wiki-document-body{flex:1;min-height:0;overflow:auto}.wiki-article{max-width:76ch;padding:24px 22px 32px}.wiki-empty-state{display:flex;flex-direction:column;align-items:flex-start;justify-content:center;gap:12px;margin:auto;max-width:56ch;padding:32px}.wiki-empty-state.compact{margin:0;max-width:none;padding:20px 12px}.wiki-empty-state h2{margin:0;font-size:20px}.wiki-empty-state p{margin:0;color:var(--fg-dim)}.wiki-empty-state-actions{display:flex;flex-wrap:wrap;gap:8px}@media (max-width: 960px){.wiki-layout{grid-template-columns:1fr}.wiki-sidebar{max-height:50vh}.wiki-page-header-main,.wiki-sidebar-header-row,.wiki-scope-legend{flex-direction:column}.wiki-page-actions{width:100%}.wiki-page-actions .btn{flex:1}}.wiki-edit .row{display:flex;gap:12px;margin-bottom:12px}.wiki-edit input[type=text]{flex:1;background:var(--bg);border:1px solid var(--border);color:var(--fg);padding:8px;border-radius:6px}.wiki-edit label{display:block;width:100%;font-size:12px;color:var(--fg-dim)}.wiki-editor{margin-bottom:16px}.skill-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:14px}.skill-card{background:var(--bg-elev);border:1px solid var(--border);border-radius:8px;padding:14px}.skill-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:4px}.tag{font-size:10px;text-transform:uppercase;padding:2px 6px;border-radius:4px;letter-spacing:.05em;background:var(--bg-elev-2);color:var(--fg-dim)}.tag-bundled{color:#93c5fd}.tag-local{color:#86efac}.tag-global{color:#fcd34d}.history-list{list-style:none;padding:0}.history-list li{padding:6px 0;border-bottom:1px solid var(--border)}.settings section{margin-bottom:28px}.settings-field{display:flex;flex-direction:column;gap:6px}.settings-field-label{font-size:12px;color:var(--fg-dim)}.settings select{background:var(--bg);color:var(--fg);border:1px solid var(--border);border-radius:6px;padding:6px 10px}.row{display:flex;align-items:center;gap:8px}.settings-row{align-items:flex-end}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes pulse{0%,to{opacity:.4}50%{opacity:1}}.ralph{max-width:760px}.ralph-section{margin-top:24px}.ralph-section h2{font-size:15px;font-weight:600;margin-bottom:10px;color:var(--fg)}.ralph-status-card{background:var(--bg-elev);border:1px solid var(--border);border-radius:8px;padding:14px 16px;display:flex;flex-direction:column;gap:8px}.ralph-status-row{display:flex;align-items:center;gap:10px;font-size:14px}.ralph-status-label{min-width:90px;color:var(--text-dim, #888);font-size:12px;text-transform:uppercase;letter-spacing:.04em}.ralph-badge{font-size:11px;font-weight:600;padding:2px 8px;border-radius:10px;text-transform:uppercase;letter-spacing:.04em}.ralph-badge--running{background:color-mix(in srgb,var(--accent) 15%,transparent);color:var(--accent)}.ralph-badge--stopped{background:color-mix(in srgb,#888 15%,transparent);color:#888}.ralph-mono{font-family:var(--font-mono, monospace);font-size:13px}.ralph-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:400px}.ralph-controls{background:var(--bg-elev);border:1px solid var(--border);border-radius:8px;padding:14px 16px;display:flex;flex-direction:column;gap:12px}.ralph-form-row{display:flex;align-items:center;gap:10px;font-size:14px}.ralph-form-row label{min-width:140px;color:var(--text-dim, #888);font-size:12px;text-transform:uppercase;letter-spacing:.04em}.ralph-select,.ralph-input{background:var(--bg-elev-2, var(--bg-elev));border:1px solid var(--border);border-radius:5px;color:var(--fg);font-size:13px;padding:5px 8px;min-width:240px}.ralph-input--narrow{min-width:80px;width:80px}.ralph-select:focus,.ralph-input:focus{outline:2px solid var(--accent);outline-offset:1px}.btn{border:none;border-radius:6px;font-size:13px;font-weight:600;padding:6px 14px;cursor:pointer;transition:opacity .1s;align-self:flex-start}.btn:disabled{opacity:.5;cursor:not-allowed}.btn-primary{background:var(--accent);color:#fff}.btn-primary:hover:not(:disabled){opacity:.85}.btn-danger{background:#f44336;color:#fff}.btn-danger:hover:not(:disabled){opacity:.85}.ralph-queue-count{font-weight:400;color:var(--text-dim, #888);font-size:13px}.ralph-queue-source{margin-bottom:8px}.ralph-queue{display:flex;flex-direction:column;gap:6px}.ralph-issue{display:block;text-decoration:none;color:inherit;background:var(--bg-elev);border:1px solid var(--border);border-radius:8px;padding:10px 12px;transition:background .1s}.ralph-issue:hover{background:var(--bg-elev-2)}.ralph-issue-head{display:flex;align-items:baseline;gap:8px;margin-bottom:4px}.ralph-issue-number{font-family:var(--font-mono, monospace);font-size:12px;color:var(--text-dim, #888);flex-shrink:0}.ralph-issue-title{font-size:14px;font-weight:500}.ralph-issue-meta{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.ralph-labels{display:flex;flex-wrap:wrap;gap:4px}.ralph-label{font-size:11px;padding:1px 6px;border-radius:8px;background:color-mix(in srgb,#888 12%,transparent);color:var(--fg)}.ralph-label--agent{background:color-mix(in srgb,var(--accent) 12%,transparent);color:var(--accent)}.ralph-label--triage{background:color-mix(in srgb,#9c27b0 12%,transparent);color:#9c27b0}.ralph-label--urgent{background:color-mix(in srgb,#f44336 12%,transparent);color:#f44336}.link-btn{background:none;border:none;color:var(--accent);font-size:inherit;cursor:pointer;padding:0;text-decoration:underline}
|
package/web/dist/index.html
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
<link rel="icon" type="image/png" href="/chapterhouse-icon.png" />
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
8
8
|
<title>Chapterhouse</title>
|
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
|
10
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-BlIWCM11.js"></script>
|
|
10
|
+
<link rel="stylesheet" crossorigin href="/assets/index-lvHFM_ut.css">
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
|
13
13
|
<div id="root"></div>
|
package/dist/api/ralph.js
DELETED
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ralph watch process manager.
|
|
3
|
-
*
|
|
4
|
-
* Wraps `squad loop` (the CLI's "Ralph mode" — continuous work loop) so the
|
|
5
|
-
* web UI can start, stop, and inspect the local watch process.
|
|
6
|
-
*
|
|
7
|
-
* Single-process assumption: only one Ralph loop runs per daemon instance.
|
|
8
|
-
* Cross-project Ralph (WS bridge) is out of scope — see #101 follow-ups.
|
|
9
|
-
*
|
|
10
|
-
* @module api/ralph
|
|
11
|
-
*/
|
|
12
|
-
import { spawn, execFile } from "node:child_process";
|
|
13
|
-
import { promisify } from "node:util";
|
|
14
|
-
import { childLogger } from "../util/logger.js";
|
|
15
|
-
const log = childLogger("ralph");
|
|
16
|
-
const execFileAsync = promisify(execFile);
|
|
17
|
-
const state = {
|
|
18
|
-
process: null,
|
|
19
|
-
pid: null,
|
|
20
|
-
startedAt: null,
|
|
21
|
-
projectRoot: null,
|
|
22
|
-
interval: null,
|
|
23
|
-
lastPollAt: null,
|
|
24
|
-
};
|
|
25
|
-
// ---------------------------------------------------------------------------
|
|
26
|
-
// Status
|
|
27
|
-
// ---------------------------------------------------------------------------
|
|
28
|
-
export function getRalphStatus() {
|
|
29
|
-
return {
|
|
30
|
-
running: state.process !== null && state.pid !== null,
|
|
31
|
-
pid: state.pid,
|
|
32
|
-
startedAt: state.startedAt?.toISOString() ?? null,
|
|
33
|
-
projectRoot: state.projectRoot,
|
|
34
|
-
interval: state.interval,
|
|
35
|
-
lastPollAt: state.lastPollAt?.toISOString() ?? null,
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
// ---------------------------------------------------------------------------
|
|
39
|
-
// Start
|
|
40
|
-
// ---------------------------------------------------------------------------
|
|
41
|
-
export function startRalph(projectRoot, interval) {
|
|
42
|
-
if (state.process !== null) {
|
|
43
|
-
return { started: false, error: "Ralph watch is already running" };
|
|
44
|
-
}
|
|
45
|
-
const proc = spawn("squad", ["loop", "--interval", String(interval)], {
|
|
46
|
-
cwd: projectRoot,
|
|
47
|
-
detached: false,
|
|
48
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
49
|
-
});
|
|
50
|
-
if (!proc.pid) {
|
|
51
|
-
return { started: false, error: "Failed to spawn squad process" };
|
|
52
|
-
}
|
|
53
|
-
state.process = proc;
|
|
54
|
-
state.pid = proc.pid;
|
|
55
|
-
state.startedAt = new Date();
|
|
56
|
-
state.projectRoot = projectRoot;
|
|
57
|
-
state.interval = interval;
|
|
58
|
-
proc.stdout?.on("data", (_data) => {
|
|
59
|
-
state.lastPollAt = new Date();
|
|
60
|
-
});
|
|
61
|
-
proc.stderr?.on("data", (data) => {
|
|
62
|
-
log.warn({ data: data.toString().trim() }, "ralph stderr");
|
|
63
|
-
});
|
|
64
|
-
proc.on("exit", (code, signal) => {
|
|
65
|
-
log.info({ code, signal }, "Ralph process exited");
|
|
66
|
-
state.process = null;
|
|
67
|
-
state.pid = null;
|
|
68
|
-
state.startedAt = null;
|
|
69
|
-
state.projectRoot = null;
|
|
70
|
-
state.interval = null;
|
|
71
|
-
});
|
|
72
|
-
log.info({ pid: proc.pid, projectRoot, interval }, "Ralph watch started");
|
|
73
|
-
return { started: true, pid: proc.pid };
|
|
74
|
-
}
|
|
75
|
-
// ---------------------------------------------------------------------------
|
|
76
|
-
// Stop
|
|
77
|
-
// ---------------------------------------------------------------------------
|
|
78
|
-
export function stopRalph() {
|
|
79
|
-
if (!state.process || !state.pid) {
|
|
80
|
-
return { stopped: false, error: "Ralph watch is not running" };
|
|
81
|
-
}
|
|
82
|
-
try {
|
|
83
|
-
state.process.kill("SIGTERM");
|
|
84
|
-
// State cleanup happens in the exit handler; clear eagerly for immediate
|
|
85
|
-
// status visibility.
|
|
86
|
-
state.process = null;
|
|
87
|
-
state.pid = null;
|
|
88
|
-
state.startedAt = null;
|
|
89
|
-
state.projectRoot = null;
|
|
90
|
-
state.interval = null;
|
|
91
|
-
return { stopped: true };
|
|
92
|
-
}
|
|
93
|
-
catch (err) {
|
|
94
|
-
return { stopped: false, error: err instanceof Error ? err.message : String(err) };
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Fetch open squad-related issues from the GitHub repo rooted at projectRoot.
|
|
99
|
-
* Combines squad-labeled issues and issues labeled squad:* (any agent assignment).
|
|
100
|
-
*/
|
|
101
|
-
export async function getRalphQueue(projectRoot) {
|
|
102
|
-
const run = async (label) => {
|
|
103
|
-
const { stdout } = await execFileAsync("gh", ["issue", "list", "--label", label, "--json", "number,title,url,state,createdAt,labels", "--limit", "50"], { cwd: projectRoot });
|
|
104
|
-
return JSON.parse(stdout);
|
|
105
|
-
};
|
|
106
|
-
// Fetch squad (triage inbox) and squad:* (agent-assigned) in parallel.
|
|
107
|
-
// Merge and deduplicate by issue number.
|
|
108
|
-
const [squadIssues, squadStarIssues] = await Promise.allSettled([
|
|
109
|
-
run("squad"),
|
|
110
|
-
// gh doesn't support wildcard label filters; use a common prefix label.
|
|
111
|
-
// Most squad-labeled issues carry both "squad" and "squad:<agent>" labels,
|
|
112
|
-
// so the first query already captures them. Keep the second as belt-and-suspenders
|
|
113
|
-
// using any known squad-prefixed label.
|
|
114
|
-
run("squad:*").catch(() => []),
|
|
115
|
-
]);
|
|
116
|
-
const all = [
|
|
117
|
-
...(squadIssues.status === "fulfilled" ? squadIssues.value : []),
|
|
118
|
-
...(squadStarIssues.status === "fulfilled" ? squadStarIssues.value : []),
|
|
119
|
-
];
|
|
120
|
-
const seen = new Set();
|
|
121
|
-
const deduped = all.filter((issue) => {
|
|
122
|
-
if (seen.has(issue.number))
|
|
123
|
-
return false;
|
|
124
|
-
seen.add(issue.number);
|
|
125
|
-
return true;
|
|
126
|
-
});
|
|
127
|
-
return deduped.map((issue) => ({
|
|
128
|
-
number: issue.number,
|
|
129
|
-
title: issue.title,
|
|
130
|
-
url: issue.url,
|
|
131
|
-
state: issue.state,
|
|
132
|
-
createdAt: issue.createdAt,
|
|
133
|
-
labels: issue.labels.map((l) => l.name),
|
|
134
|
-
}));
|
|
135
|
-
}
|
|
136
|
-
// ---------------------------------------------------------------------------
|
|
137
|
-
// Daemon shutdown hook
|
|
138
|
-
// ---------------------------------------------------------------------------
|
|
139
|
-
/** Kill Ralph watch process on daemon shutdown — call from shutdown() in daemon.ts. */
|
|
140
|
-
export function killRalphOnShutdown() {
|
|
141
|
-
if (state.process) {
|
|
142
|
-
log.info({ pid: state.pid }, "Stopping Ralph watch on daemon shutdown");
|
|
143
|
-
try {
|
|
144
|
-
state.process.kill("SIGTERM");
|
|
145
|
-
}
|
|
146
|
-
catch {
|
|
147
|
-
/* best effort */
|
|
148
|
-
}
|
|
149
|
-
state.process = null;
|
|
150
|
-
state.pid = null;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
//# sourceMappingURL=ralph.js.map
|
package/dist/api/ralph.test.js
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for Ralph watch process manager.
|
|
3
|
-
*
|
|
4
|
-
* We test the module's exported functions directly. For process-spawn paths
|
|
5
|
-
* we verify state-machine behavior (status transitions, error returns) rather
|
|
6
|
-
* than trying to mock child_process across the ESM module cache boundary —
|
|
7
|
-
* the existing test harness doesn't support that cleanly for singleton modules.
|
|
8
|
-
*
|
|
9
|
-
* The getRalphQueue integration is exercised at the HTTP endpoint level in
|
|
10
|
-
* server.test.ts via supertest.
|
|
11
|
-
*/
|
|
12
|
-
import assert from "node:assert/strict";
|
|
13
|
-
import test from "node:test";
|
|
14
|
-
import { getRalphStatus, startRalph, stopRalph } from "./ralph.js";
|
|
15
|
-
// ---------------------------------------------------------------------------
|
|
16
|
-
// Ensure a clean slate before each test group.
|
|
17
|
-
// ---------------------------------------------------------------------------
|
|
18
|
-
function ensureStopped() {
|
|
19
|
-
stopRalph(); // idempotent — ignores "not running" error
|
|
20
|
-
}
|
|
21
|
-
test("getRalphStatus — returns not-running when idle", () => {
|
|
22
|
-
ensureStopped();
|
|
23
|
-
const status = getRalphStatus();
|
|
24
|
-
assert.equal(status.running, false);
|
|
25
|
-
assert.equal(status.pid, null);
|
|
26
|
-
assert.equal(status.startedAt, null);
|
|
27
|
-
assert.equal(status.projectRoot, null);
|
|
28
|
-
});
|
|
29
|
-
test("stopRalph — returns error when not running", () => {
|
|
30
|
-
ensureStopped();
|
|
31
|
-
const result = stopRalph();
|
|
32
|
-
assert.equal(result.stopped, false);
|
|
33
|
-
if (!result.stopped) {
|
|
34
|
-
assert.match(result.error, /not running/i);
|
|
35
|
-
}
|
|
36
|
-
});
|
|
37
|
-
test("startRalph — spawns process and updates status", () => {
|
|
38
|
-
ensureStopped();
|
|
39
|
-
// We spawn 'squad loop' against the project root. In test environment squad
|
|
40
|
-
// will likely exit immediately (no .squad dir), but the process is created
|
|
41
|
-
// and state is updated synchronously before the exit handler fires.
|
|
42
|
-
// Use the worktree path itself — it has a .squad dir via the main repo's
|
|
43
|
-
// git worktree setup, so squad can at least start.
|
|
44
|
-
const projectRoot = process.cwd();
|
|
45
|
-
const result = startRalph(projectRoot, 10);
|
|
46
|
-
// Either it started (pid assigned) or it failed to spawn — either way must be well-typed.
|
|
47
|
-
if (result.started) {
|
|
48
|
-
assert.ok(typeof result.pid === "number", "pid should be a number");
|
|
49
|
-
const status = getRalphStatus();
|
|
50
|
-
// Status may have already transitioned back to stopped if process exited instantly.
|
|
51
|
-
// The important thing is that the call succeeded and we got a pid.
|
|
52
|
-
assert.ok(result.pid > 0, "pid should be positive");
|
|
53
|
-
}
|
|
54
|
-
else {
|
|
55
|
-
// Spawn failed (e.g., squad not found in PATH during test) — that's acceptable.
|
|
56
|
-
assert.ok(typeof result.error === "string");
|
|
57
|
-
}
|
|
58
|
-
// Cleanup regardless.
|
|
59
|
-
ensureStopped();
|
|
60
|
-
});
|
|
61
|
-
test("startRalph — returns error if already running", () => {
|
|
62
|
-
ensureStopped();
|
|
63
|
-
const projectRoot = process.cwd();
|
|
64
|
-
const first = startRalph(projectRoot, 10);
|
|
65
|
-
if (!first.started) {
|
|
66
|
-
// Can't test double-start if first spawn fails — skip gracefully.
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
const second = startRalph(projectRoot, 10);
|
|
70
|
-
assert.equal(second.started, false);
|
|
71
|
-
if (!second.started) {
|
|
72
|
-
assert.match(second.error, /already running/i);
|
|
73
|
-
}
|
|
74
|
-
ensureStopped();
|
|
75
|
-
});
|
|
76
|
-
test("stopRalph — transitions status back to stopped", () => {
|
|
77
|
-
ensureStopped();
|
|
78
|
-
const projectRoot = process.cwd();
|
|
79
|
-
const result = startRalph(projectRoot, 10);
|
|
80
|
-
if (!result.started) {
|
|
81
|
-
// spawn unavailable in test env — skip.
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
const stopResult = stopRalph();
|
|
85
|
-
assert.ok(stopResult.stopped, "stop should succeed when running");
|
|
86
|
-
const status = getRalphStatus();
|
|
87
|
-
assert.equal(status.running, false);
|
|
88
|
-
assert.equal(status.pid, null);
|
|
89
|
-
});
|
|
90
|
-
test("getRalphStatus shape — all fields present", () => {
|
|
91
|
-
ensureStopped();
|
|
92
|
-
const status = getRalphStatus();
|
|
93
|
-
assert.ok(Object.prototype.hasOwnProperty.call(status, "running"));
|
|
94
|
-
assert.ok(Object.prototype.hasOwnProperty.call(status, "pid"));
|
|
95
|
-
assert.ok(Object.prototype.hasOwnProperty.call(status, "startedAt"));
|
|
96
|
-
assert.ok(Object.prototype.hasOwnProperty.call(status, "projectRoot"));
|
|
97
|
-
assert.ok(Object.prototype.hasOwnProperty.call(status, "interval"));
|
|
98
|
-
assert.ok(Object.prototype.hasOwnProperty.call(status, "lastPollAt"));
|
|
99
|
-
assert.equal(typeof status.running, "boolean");
|
|
100
|
-
});
|
|
101
|
-
//# sourceMappingURL=ralph.test.js.map
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { mkdirSync, rmSync } from "node:fs";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import test from "node:test";
|
|
5
|
-
const repoRoot = process.cwd();
|
|
6
|
-
const fixtureRoot = join(repoRoot, "src/test/fixtures/mock-squad-repo");
|
|
7
|
-
const sandboxRoot = join(repoRoot, ".test-work", `agents-squad-${process.pid}`);
|
|
8
|
-
process.env.CHAPTERHOUSE_HOME = sandboxRoot;
|
|
9
|
-
async function loadAgentsModule() {
|
|
10
|
-
return await import(new URL(`./agents.js?v=${Date.now()}-${Math.random()}`, import.meta.url).href);
|
|
11
|
-
}
|
|
12
|
-
test.before(() => {
|
|
13
|
-
mkdirSync(join(repoRoot, ".test-work"), { recursive: true });
|
|
14
|
-
});
|
|
15
|
-
test.beforeEach(() => {
|
|
16
|
-
rmSync(sandboxRoot, { recursive: true, force: true });
|
|
17
|
-
mkdirSync(join(sandboxRoot, ".chapterhouse"), { recursive: true });
|
|
18
|
-
});
|
|
19
|
-
test.after(() => {
|
|
20
|
-
rmSync(sandboxRoot, { recursive: true, force: true });
|
|
21
|
-
});
|
|
22
|
-
test("buildAgentRoster includes squad agents when a project root is active", async () => {
|
|
23
|
-
const m = await loadAgentsModule();
|
|
24
|
-
m.ensureDefaultAgents();
|
|
25
|
-
m.loadAgents();
|
|
26
|
-
const roster = m.buildAgentRoster(fixtureRoot);
|
|
27
|
-
assert.match(roster, /@coder/);
|
|
28
|
-
assert.match(roster, /@ripley/);
|
|
29
|
-
});
|
|
30
|
-
test("parseAtMention resolves squad agents when project context is active", async () => {
|
|
31
|
-
const m = await loadAgentsModule();
|
|
32
|
-
m.ensureDefaultAgents();
|
|
33
|
-
m.loadAgents();
|
|
34
|
-
const result = m.parseAtMention("@ripley trace squad decisions", fixtureRoot);
|
|
35
|
-
assert.deepEqual(result, {
|
|
36
|
-
agentSlug: "ripley",
|
|
37
|
-
message: "trace squad decisions",
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
test("createEphemeralAgentSession prepends an optional system message prefix", async () => {
|
|
41
|
-
const m = await loadAgentsModule();
|
|
42
|
-
m.ensureDefaultAgents();
|
|
43
|
-
m.loadAgents();
|
|
44
|
-
let options;
|
|
45
|
-
const client = {
|
|
46
|
-
async createSession(nextOptions) {
|
|
47
|
-
options = nextOptions;
|
|
48
|
-
return { sessionId: "ephemeral-1" };
|
|
49
|
-
},
|
|
50
|
-
};
|
|
51
|
-
await m.createEphemeralAgentSession("coder", client, [{ name: "wiki_read" }], undefined, "PROJECT CONTEXT PREFIX");
|
|
52
|
-
assert.match(String(options?.systemMessage?.content || ""), /PROJECT CONTEXT PREFIX/);
|
|
53
|
-
});
|
|
54
|
-
test("createSquadAgentSession uses the provided system prefix and strips management tools", async () => {
|
|
55
|
-
const m = await loadAgentsModule();
|
|
56
|
-
assert.equal(typeof m.createSquadAgentSession, "function", "createSquadAgentSession should be exported");
|
|
57
|
-
let options;
|
|
58
|
-
const client = {
|
|
59
|
-
async createSession(nextOptions) {
|
|
60
|
-
options = nextOptions;
|
|
61
|
-
return { sessionId: "squad-1" };
|
|
62
|
-
},
|
|
63
|
-
};
|
|
64
|
-
await m.createSquadAgentSession("ripley", client, [
|
|
65
|
-
{ name: "delegate_to_agent" },
|
|
66
|
-
{ name: "wiki_read" },
|
|
67
|
-
{ name: "bash" },
|
|
68
|
-
], "SQUAD PREFIX");
|
|
69
|
-
assert.equal(options?.systemMessage?.content, "SQUAD PREFIX");
|
|
70
|
-
assert.deepEqual((options?.tools).map((tool) => tool.name).sort(), ["bash", "wiki_read"]);
|
|
71
|
-
});
|
|
72
|
-
//# sourceMappingURL=agents.squad.test.js.map
|
package/dist/copilot/hooks.js
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Hook Pipeline — Governance enforcement for Chapterhouse sessions.
|
|
3
|
-
*
|
|
4
|
-
* Bridges the squad-sdk's HookPipeline to the Copilot SDK's SessionHooks interface.
|
|
5
|
-
* All session creation sites call createSessionHooks() to get a SessionHooks object
|
|
6
|
-
* that runs the four Phase 1 policy hooks:
|
|
7
|
-
*
|
|
8
|
-
* 1. File-write guard — block writes outside allowed paths
|
|
9
|
-
* 2. Shell restriction — block dangerous commands (rm -rf, force-push, etc.)
|
|
10
|
-
* 3. PII scrubber — redact emails from tool output before agent sees it
|
|
11
|
-
* 4. Reviewer lockout — block locked-out agents from re-authoring rejected artifacts
|
|
12
|
-
*
|
|
13
|
-
* SPIKE FINDING (2026-05-08): onPermissionRequest only receives a coarse `kind`
|
|
14
|
-
* ("shell"|"write"|...) with no tool name or arguments. The correct interception
|
|
15
|
-
* point is `hooks.onPreToolUse` / `hooks.onPostToolUse` in SessionConfig.
|
|
16
|
-
*/
|
|
17
|
-
import { HookPipeline } from "@bradygaster/squad-sdk/hooks";
|
|
18
|
-
import { childLogger } from "../util/logger.js";
|
|
19
|
-
const log = childLogger("hooks");
|
|
20
|
-
/**
|
|
21
|
-
* Paths agents are allowed to write into by default.
|
|
22
|
-
* This covers the full project tree. Per-agent restrictions come from
|
|
23
|
-
* charter frontmatter `allowed_paths` (Phase 2).
|
|
24
|
-
*/
|
|
25
|
-
export const DEFAULT_ALLOWED_PATHS = [
|
|
26
|
-
"src/**",
|
|
27
|
-
".squad/**",
|
|
28
|
-
"docs/**",
|
|
29
|
-
"web/**",
|
|
30
|
-
".github/**",
|
|
31
|
-
".worktrees/**",
|
|
32
|
-
"agents/**",
|
|
33
|
-
"scripts/**",
|
|
34
|
-
"skills/**",
|
|
35
|
-
];
|
|
36
|
-
let pipeline;
|
|
37
|
-
export function getHookPipeline() {
|
|
38
|
-
return pipeline;
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Initialize the singleton HookPipeline. Called once from initOrchestrator()
|
|
42
|
-
* before any sessions are created. Safe to call multiple times (reinitializes).
|
|
43
|
-
*/
|
|
44
|
-
export function initHookPipeline(overrides) {
|
|
45
|
-
const config = {
|
|
46
|
-
allowedWritePaths: DEFAULT_ALLOWED_PATHS,
|
|
47
|
-
scrubPii: true,
|
|
48
|
-
reviewerLockout: true,
|
|
49
|
-
// blockedCommands: undefined → HookPipeline uses DEFAULT_BLOCKED_COMMANDS
|
|
50
|
-
...overrides,
|
|
51
|
-
};
|
|
52
|
-
pipeline = new HookPipeline(config);
|
|
53
|
-
log.info({ scrubPii: config.scrubPii, reviewerLockout: config.reviewerLockout, paths: config.allowedWritePaths?.length }, "HookPipeline initialized");
|
|
54
|
-
return pipeline;
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Normalize a file path: strip leading CWD so that absolute paths match
|
|
58
|
-
* relative glob patterns. HookPipeline.matchGlob anchors with ^...$, so
|
|
59
|
-
* "/home/user/repo/src/foo.ts" won't match "src/**" without this step.
|
|
60
|
-
*/
|
|
61
|
-
function normalizePath(filePath) {
|
|
62
|
-
const cwd = process.cwd().replace(/\\/g, "/");
|
|
63
|
-
const normalized = filePath.replace(/\\/g, "/");
|
|
64
|
-
if (normalized.startsWith(cwd + "/")) {
|
|
65
|
-
return normalized.slice(cwd.length + 1);
|
|
66
|
-
}
|
|
67
|
-
return normalized;
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Normalize all path arguments in a tool args object before running hooks.
|
|
71
|
-
* The file-write guard reads `path` or `file_path` from arguments.
|
|
72
|
-
*/
|
|
73
|
-
function normalizeToolArgs(args) {
|
|
74
|
-
const out = { ...args };
|
|
75
|
-
if (typeof out.path === "string")
|
|
76
|
-
out.path = normalizePath(out.path);
|
|
77
|
-
if (typeof out.file_path === "string")
|
|
78
|
-
out.file_path = normalizePath(out.file_path);
|
|
79
|
-
return out;
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Create a SessionHooks object for use in createSession / resumeSession calls.
|
|
83
|
-
*
|
|
84
|
-
* @param agentName - Used for reviewer-lockout and log context. Pass "orchestrator"
|
|
85
|
-
* for coordinator sessions.
|
|
86
|
-
* @param agentAllowedPaths - If provided, overrides the default allowed paths for the
|
|
87
|
-
* file-write guard. Derived from charter frontmatter in Phase 2.
|
|
88
|
-
* Currently unused (no charter has allowed_paths yet).
|
|
89
|
-
*/
|
|
90
|
-
export function createSessionHooks(agentName, agentAllowedPaths) {
|
|
91
|
-
// If agent has specific paths, create a temporary per-session pipeline for file-write
|
|
92
|
-
// checking. All other hooks (shell, PII, lockout) use the singleton.
|
|
93
|
-
const fileWritePipeline = agentAllowedPaths && agentAllowedPaths.length > 0
|
|
94
|
-
? new HookPipeline({ allowedWritePaths: agentAllowedPaths })
|
|
95
|
-
: undefined;
|
|
96
|
-
return {
|
|
97
|
-
onPreToolUse: async (input, invocation) => {
|
|
98
|
-
const p = getHookPipeline();
|
|
99
|
-
if (!p)
|
|
100
|
-
return;
|
|
101
|
-
const args = normalizeToolArgs((input.toolArgs ?? {}));
|
|
102
|
-
const ctx = {
|
|
103
|
-
toolName: input.toolName,
|
|
104
|
-
arguments: args,
|
|
105
|
-
agentName,
|
|
106
|
-
sessionId: invocation.sessionId,
|
|
107
|
-
};
|
|
108
|
-
// If agent has specific allowed paths, check file-write guard with those first.
|
|
109
|
-
if (fileWritePipeline) {
|
|
110
|
-
const pathResult = await fileWritePipeline.runPreToolHooks(ctx);
|
|
111
|
-
if (pathResult.action === "block") {
|
|
112
|
-
log.warn({ tool: input.toolName, agent: agentName, reason: pathResult.reason }, "Hook blocked (agent paths)");
|
|
113
|
-
return {
|
|
114
|
-
permissionDecision: "deny",
|
|
115
|
-
permissionDecisionReason: `[BLOCKED] ${pathResult.reason}`,
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
const result = await p.runPreToolHooks(ctx);
|
|
120
|
-
if (result.action === "block") {
|
|
121
|
-
log.warn({ tool: input.toolName, agent: agentName, reason: result.reason }, "Hook blocked tool call");
|
|
122
|
-
return {
|
|
123
|
-
permissionDecision: "deny",
|
|
124
|
-
permissionDecisionReason: `[BLOCKED] ${result.reason}`,
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
if (result.action === "modify" && result.modifiedArguments) {
|
|
128
|
-
return { modifiedArgs: result.modifiedArguments };
|
|
129
|
-
}
|
|
130
|
-
},
|
|
131
|
-
onPostToolUse: async (input, invocation) => {
|
|
132
|
-
const p = getHookPipeline();
|
|
133
|
-
if (!p)
|
|
134
|
-
return;
|
|
135
|
-
const args = normalizeToolArgs((input.toolArgs ?? {}));
|
|
136
|
-
const originalText = input.toolResult.textResultForLlm;
|
|
137
|
-
const postResult = await p.runPostToolHooks({
|
|
138
|
-
toolName: input.toolName,
|
|
139
|
-
arguments: args,
|
|
140
|
-
result: originalText,
|
|
141
|
-
agentName,
|
|
142
|
-
sessionId: invocation.sessionId,
|
|
143
|
-
});
|
|
144
|
-
const scrubbed = postResult.result;
|
|
145
|
-
if (typeof scrubbed === "string" && scrubbed !== originalText) {
|
|
146
|
-
log.info({ tool: input.toolName, agent: agentName }, "PII scrubbed from tool output");
|
|
147
|
-
return {
|
|
148
|
-
modifiedResult: {
|
|
149
|
-
...input.toolResult,
|
|
150
|
-
textResultForLlm: scrubbed,
|
|
151
|
-
},
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
},
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
//# sourceMappingURL=hooks.js.map
|