@theihtisham/agent-shadow-brain 7.22.0 → 7.22.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/CHANGELOG.md +7 -1
  2. package/README.md +7 -3
  3. package/dist/brain/auto-update.js +1 -1
  4. package/dist/brain/brain-portability.js +1 -1
  5. package/dist/brain/client-integrations.js +1 -1
  6. package/dist/brain/context-efficiency.d.ts.map +1 -1
  7. package/dist/brain/mcp-presence.js +2 -2
  8. package/dist/brain/mcp-server.js +5 -5
  9. package/dist/brain/native-codex.js +1 -1
  10. package/dist/brain/orchestrator.js +1 -1
  11. package/dist/brain/project-hub-pdf-worker.js +57 -57
  12. package/dist/brain/project-hub-store.js +11 -11
  13. package/dist/cli.js +1 -1
  14. package/dist/dashboard/brain-hub-agent-dock.js +252 -252
  15. package/dist/dashboard/brain-hub-controls.js +61 -61
  16. package/dist/dashboard/brain-hub-exchanges.js +80 -80
  17. package/dist/dashboard/brain-hub-graph.js +336 -336
  18. package/dist/dashboard/brain-hub-html.js +78 -78
  19. package/dist/dashboard/brain-hub-live.js +149 -149
  20. package/dist/dashboard/brain-hub-managed-skills.js +18 -18
  21. package/dist/dashboard/brain-hub-native.js +42 -42
  22. package/dist/dashboard/live-workspace-html.js +1 -1
  23. package/dist/dashboard/project-feedback-html.js +211 -211
  24. package/dist/dashboard/project-kit-html.js +353 -353
  25. package/dist/dashboard/server.js +1 -1
  26. package/dist/dashboard/v6-dashboard-html.js +4261 -4261
  27. package/dist/dashboard/workspace-api.js +1 -1
  28. package/dist/dashboard/workspace-html.js +2534 -2534
  29. package/dist/dashboard/workspace-status.js +1 -1
  30. package/dist/project-cli.js +1 -1
  31. package/docs/BRAND_ASSETS.md +36 -36
  32. package/docs/COMPARISON.md +22 -22
  33. package/docs/COMPONENTS.md +66 -66
  34. package/docs/CONTEXT_LAB.md +79 -79
  35. package/docs/INTEGRATIONS.md +69 -69
  36. package/docs/LEARNING.md +45 -45
  37. package/docs/PROJECT_KIT.md +97 -97
  38. package/docs/SHARING.md +13 -13
  39. package/docs/SHOWCASE.md +87 -87
  40. package/docs/STATS.md +21 -21
  41. package/docs/TROUBLESHOOTING.md +42 -42
  42. package/docs/UPGRADE_PLAN_v7.0.0.md +203 -203
  43. package/docs/WORKFLOW_INTEGRATION.md +125 -125
  44. package/docs/evaluation/CONTEXT_EVALUATION_v7.1.4.json +1818 -1818
  45. package/docs/evaluation/CONTEXT_EVALUATION_v7.1.4.md +36 -36
  46. package/docs/launch/COMPONENT_MAP_v7.1.4.md +122 -122
  47. package/docs/launch/PROJECT_KIT_PLAN_v7.1.4.md +110 -110
  48. package/docs/launch/PROJECT_LEARNING_PLAN_v7.1.4.md +63 -63
  49. package/docs/launch/RELEASE_ACCEPTANCE_v7.22.0.md +48 -48
  50. package/docs/launch/RELEASE_CHECKLIST_v7.0.0.md +24 -24
  51. package/docs/launch/RELEASE_NOTES_v7.0.0.md +44 -44
  52. package/docs/launch/RELEASE_NOTES_v7.1.0.md +32 -32
  53. package/docs/launch/RELEASE_NOTES_v7.1.3.md +40 -40
  54. package/docs/launch/RELEASE_NOTES_v7.1.4.md +30 -30
  55. package/docs/launch/RELEASE_NOTES_v7.2.0.md +24 -24
  56. package/docs/launch/RELEASE_NOTES_v7.22.0.md +53 -53
  57. package/docs/launch/RELEASE_NOTES_v7.22.1.md +11 -0
  58. package/docs/launch/RESEARCH_AND_PLAN_v7.1.4.md +255 -255
  59. package/docs/launch/RESEARCH_AND_PLAN_v7.22.0.md +672 -672
  60. package/package.json +7 -3
  61. package/skills/shadow-debug/SKILL.md +16 -16
  62. package/skills/shadow-implement/SKILL.md +16 -16
  63. package/skills/shadow-release/SKILL.md +16 -16
  64. package/skills/shadow-research/SKILL.md +14 -14
  65. package/skills/shadow-review/SKILL.md +14 -14
  66. package/skills/shadow-workflow/SKILL.md +39 -39
@@ -1,44 +1,44 @@
1
- export const BRAIN_HUB_NATIVE = String.raw `
2
- let nativeTimer=0,nativeBusy=false,nativeRequestEpoch=0,nativeReload=false;
3
- function invalidateNativeRequest(){nativeRequestEpoch++;clearTimeout(nativeTimer);}
4
- async function refreshNativeChats(manual=false){
5
- if(document.hidden||state.view!=='sessions')return;
6
- if(nativeBusy){nativeReload ||= manual;return;}
7
- clearTimeout(nativeTimer);nativeBusy=true;
8
- const epoch=state.dataEpoch,requestEpoch=nativeRequestEpoch;
9
- const current=()=>epoch===state.dataEpoch&&requestEpoch===nativeRequestEpoch;
10
- try{
11
- const connection=await api('sessions/codex/status');
12
- if(!current())return;
13
- state.nativeConnection=connection;
14
- if(connection.connected){
15
- const query=new URLSearchParams({archived:String(!!state.nativeArchived),...(state.nativeListCursor?{cursor:state.nativeListCursor}:{})});
16
- const result=await api('sessions/codex/threads?'+query,{timeoutMs:15000});
17
- if(!current())return;
18
- const selected=state.nativeThread?.thread;
19
- state.nativeThreads=result;
20
- const latest=arr(result.threads).find(item=>item.id===selected?.id);
21
- if(selected&&latest&&(manual||latest.updatedAt!==selected.updatedAt)){
22
- const page=await api('sessions/codex/threads/'+encodeURIComponent(selected.id)+(state.nativeThreadCursor?'?cursor='+encodeURIComponent(state.nativeThreadCursor):''),{timeoutMs:15000});
23
- if(!current())return;
24
- state.nativeThread=page;
25
- }
26
- }
27
- delete state.errors.native;
28
- }catch(failure){if(current())state.errors.native=failure.message;}
29
- finally{
30
- nativeBusy=false;
31
- if(nativeReload){nativeReload=false;void refreshNativeChats(true);}
32
- else if(current()&&state.view==='sessions'){render(true);if(state.nativeConnection?.connected)nativeTimer=setTimeout(()=>void refreshNativeChats(),10000);}
33
- }
34
- }
35
- function nativeHistoryPanel(){
36
- const connection=state.nativeConnection,threads=arr(state.nativeThreads?.threads),selected=state.nativeThread;
37
- const actions=connection?.connected?action('native-refresh','Refresh original chats')+action('native-disconnect','Disconnect Codex'):action('native-connect','Connect installed Codex','primary');
38
- const listMarkup=threads.length?list(threads.map(thread=>'<li><button class="entry" type="button" data-native-thread="'+esc(thread.id)+'"><strong>'+esc(thread.name??'Original title unavailable')+'</strong><small class="mono muted">'+esc(thread.id)+'</small><small>'+esc(isoTime(thread.updatedAt))+'</small></button></li>').join('')):empty(connection?.connected?'No conversations in this project':'Native history is disconnected',connection?.connected?'This list is scoped to the selected project directory. Check archived conversations if needed.':'Connect explicitly to read names and selected history through the installed Codex app-server.');
39
- const preview=selected?'<div class="panel-body stack"><h3>'+esc(selected.thread.name??'Original title unavailable')+'</h3><dl class="facts">'+fact('Original conversation ID',selected.thread.id)+fact('Session group ID',selected.thread.sessionId??'Not supplied')+fact('Original workspace',selected.thread.cwd)+fact('History API runtime state',selected.thread.status?.type??'unknown')+fact('Observed',isoTime(selected.observedAt))+'</dl><p class="small muted">This separate app-server’s runtime state does not establish whether a desktop chat is working. Names are supplied by Codex; preview text is never substituted for a missing title.</p>'+list(arr(selected.messages).map(message=>'<li class="item"><strong>'+esc(message.role)+'</strong><p class="preserve">'+esc(message.text)+'</p><small class="mono muted">'+esc(message.locator)+'</small></li>').join(''))+'<p class="small muted">'+arr(selected.messages).length+' visible messages in this page. '+fmt(selected.omitted)+' non-text or unsupported items omitted. '+(selected.nextCursor?'Earlier history is available.':'No earlier page reported.')+'</p><div class="actions">'+action('native-import','Save this history page','primary')+(selected.nextCursor?action('native-earlier','Earlier history','','data-cursor="'+esc(selected.nextCursor)+'"'):'')+'</div><p class="small muted">Saving creates an immutable project snapshot of this page. It does not automatically approve the chat as project knowledge or resume a model.</p></div>':empty('Choose an original conversation','Inspect its exact title and visible history before saving a shared snapshot.');
40
- const exports='<div class="panel-body"><form class="form" data-form="native-export"><label>Original tool<select name="tool"><option value="codex">Codex rollout / indexed export</option><option value="claude-code">Claude Code SDK / JSONL export</option><option value="opencode">OpenCode session export</option><option value="deepseek-harness">DeepSeek Harness canonical export</option></select></label><label>Native export file<input type="file" name="export" accept=".json,.jsonl,.ndjson,.zip" required></label><p class="small muted">Up to 2 MiB. DeepSeek ZIP imports read only the supported session record, never its attachments. Original names are kept when the export contains them; missing names stay unavailable.</p>'+submit('Import native history')+'</form></div>';
41
- return panel('Original tool history','<div class="panel-body stack"><div class="actions">'+actions+chip(connection?.connected?'Read-only Codex API':'Explicit connection required',connection?.connected?'green':'amber')+'</div><p class="small muted">Connected lists refresh every 10 seconds while this view is open. History is read on selection or when the selected conversation’s timestamp changes. No model is started. The connection expires after 15 minutes.</p>'+error('native','native history')+(connection?.connected?'<div class="actions">'+action('native-archive',state.nativeArchived?'Show current chats':'Show archived chats')+(state.nativeThreads?.nextCursor?action('native-next','Next conversations','','data-cursor="'+esc(state.nativeThreads.nextCursor)+'"'):'')+(state.nativeListCursor?action('native-first','First conversations'):'')+'</div>':'')+'</div><div class="two"><div>'+listMarkup+'</div>'+preview+'</div><details><summary class="panel-body">Import an export from another supported tool</summary>'+exports+'</details>');
42
- }
1
+ export const BRAIN_HUB_NATIVE = String.raw `
2
+ let nativeTimer=0,nativeBusy=false,nativeRequestEpoch=0,nativeReload=false;
3
+ function invalidateNativeRequest(){nativeRequestEpoch++;clearTimeout(nativeTimer);}
4
+ async function refreshNativeChats(manual=false){
5
+ if(document.hidden||state.view!=='sessions')return;
6
+ if(nativeBusy){nativeReload ||= manual;return;}
7
+ clearTimeout(nativeTimer);nativeBusy=true;
8
+ const epoch=state.dataEpoch,requestEpoch=nativeRequestEpoch;
9
+ const current=()=>epoch===state.dataEpoch&&requestEpoch===nativeRequestEpoch;
10
+ try{
11
+ const connection=await api('sessions/codex/status');
12
+ if(!current())return;
13
+ state.nativeConnection=connection;
14
+ if(connection.connected){
15
+ const query=new URLSearchParams({archived:String(!!state.nativeArchived),...(state.nativeListCursor?{cursor:state.nativeListCursor}:{})});
16
+ const result=await api('sessions/codex/threads?'+query,{timeoutMs:15000});
17
+ if(!current())return;
18
+ const selected=state.nativeThread?.thread;
19
+ state.nativeThreads=result;
20
+ const latest=arr(result.threads).find(item=>item.id===selected?.id);
21
+ if(selected&&latest&&(manual||latest.updatedAt!==selected.updatedAt)){
22
+ const page=await api('sessions/codex/threads/'+encodeURIComponent(selected.id)+(state.nativeThreadCursor?'?cursor='+encodeURIComponent(state.nativeThreadCursor):''),{timeoutMs:15000});
23
+ if(!current())return;
24
+ state.nativeThread=page;
25
+ }
26
+ }
27
+ delete state.errors.native;
28
+ }catch(failure){if(current())state.errors.native=failure.message;}
29
+ finally{
30
+ nativeBusy=false;
31
+ if(nativeReload){nativeReload=false;void refreshNativeChats(true);}
32
+ else if(current()&&state.view==='sessions'){render(true);if(state.nativeConnection?.connected)nativeTimer=setTimeout(()=>void refreshNativeChats(),10000);}
33
+ }
34
+ }
35
+ function nativeHistoryPanel(){
36
+ const connection=state.nativeConnection,threads=arr(state.nativeThreads?.threads),selected=state.nativeThread;
37
+ const actions=connection?.connected?action('native-refresh','Refresh original chats')+action('native-disconnect','Disconnect Codex'):action('native-connect','Connect installed Codex','primary');
38
+ const listMarkup=threads.length?list(threads.map(thread=>'<li><button class="entry" type="button" data-native-thread="'+esc(thread.id)+'"><strong>'+esc(thread.name??'Original title unavailable')+'</strong><small class="mono muted">'+esc(thread.id)+'</small><small>'+esc(isoTime(thread.updatedAt))+'</small></button></li>').join('')):empty(connection?.connected?'No conversations in this project':'Native history is disconnected',connection?.connected?'This list is scoped to the selected project directory. Check archived conversations if needed.':'Connect explicitly to read names and selected history through the installed Codex app-server.');
39
+ const preview=selected?'<div class="panel-body stack"><h3>'+esc(selected.thread.name??'Original title unavailable')+'</h3><dl class="facts">'+fact('Original conversation ID',selected.thread.id)+fact('Session group ID',selected.thread.sessionId??'Not supplied')+fact('Original workspace',selected.thread.cwd)+fact('History API runtime state',selected.thread.status?.type??'unknown')+fact('Observed',isoTime(selected.observedAt))+'</dl><p class="small muted">This separate app-server’s runtime state does not establish whether a desktop chat is working. Names are supplied by Codex; preview text is never substituted for a missing title.</p>'+list(arr(selected.messages).map(message=>'<li class="item"><strong>'+esc(message.role)+'</strong><p class="preserve">'+esc(message.text)+'</p><small class="mono muted">'+esc(message.locator)+'</small></li>').join(''))+'<p class="small muted">'+arr(selected.messages).length+' visible messages in this page. '+fmt(selected.omitted)+' non-text or unsupported items omitted. '+(selected.nextCursor?'Earlier history is available.':'No earlier page reported.')+'</p><div class="actions">'+action('native-import','Save this history page','primary')+(selected.nextCursor?action('native-earlier','Earlier history','','data-cursor="'+esc(selected.nextCursor)+'"'):'')+'</div><p class="small muted">Saving creates an immutable project snapshot of this page. It does not automatically approve the chat as project knowledge or resume a model.</p></div>':empty('Choose an original conversation','Inspect its exact title and visible history before saving a shared snapshot.');
40
+ const exports='<div class="panel-body"><form class="form" data-form="native-export"><label>Original tool<select name="tool"><option value="codex">Codex rollout / indexed export</option><option value="claude-code">Claude Code SDK / JSONL export</option><option value="opencode">OpenCode session export</option><option value="deepseek-harness">DeepSeek Harness canonical export</option></select></label><label>Native export file<input type="file" name="export" accept=".json,.jsonl,.ndjson,.zip" required></label><p class="small muted">Up to 2 MiB. DeepSeek ZIP imports read only the supported session record, never its attachments. Original names are kept when the export contains them; missing names stay unavailable.</p>'+submit('Import native history')+'</form></div>';
41
+ return panel('Original tool history','<div class="panel-body stack"><div class="actions">'+actions+chip(connection?.connected?'Read-only Codex API':'Explicit connection required',connection?.connected?'green':'amber')+'</div><p class="small muted">Connected lists refresh every 10 seconds while this view is open. History is read on selection or when the selected conversation’s timestamp changes. No model is started. The connection expires after 15 minutes.</p>'+error('native','native history')+(connection?.connected?'<div class="actions">'+action('native-archive',state.nativeArchived?'Show current chats':'Show archived chats')+(state.nativeThreads?.nextCursor?action('native-next','Next conversations','','data-cursor="'+esc(state.nativeThreads.nextCursor)+'"'):'')+(state.nativeListCursor?action('native-first','First conversations'):'')+'</div>':'')+'</div><div class="two"><div>'+listMarkup+'</div>'+preview+'</div><details><summary class="panel-body">Import an export from another supported tool</summary>'+exports+'</details>');
42
+ }
43
43
  `;
44
44
  //# sourceMappingURL=brain-hub-native.js.map
@@ -223,7 +223,7 @@ export const LIVE_WORKSPACE_HTML = String.raw `<!doctype html>
223
223
  <div class="top-inner">
224
224
  <a class="brand" href="/" aria-label="Agent Shadow Brain workspace"><span class="brand-mark" data-icon="brand"></span><span>Agent Shadow Brain</span></a>
225
225
  <nav class="main-nav" aria-label="Navigation"><a href="/">Workspace</a><a href="/live" aria-current="page">Live</a></nav>
226
- <div class="top-state"><span class="small muted mono top-version">7.22.0</span><span id="demo-tag" class="tag warn" hidden>Synthetic demo</span><span id="stream-tag" class="tag"><span class="dot"></span><span id="stream-label">Connecting</span></span></div>
226
+ <div class="top-state"><span class="small muted mono top-version">7.22.1</span><span id="demo-tag" class="tag warn" hidden>Synthetic demo</span><span id="stream-tag" class="tag"><span class="dot"></span><span id="stream-label">Connecting</span></span></div>
227
227
  </div>
228
228
  </header>
229
229
  <main id="live-main" tabindex="-1">
@@ -1,216 +1,216 @@
1
1
  /** Project-local task outcomes share the workspace memory browser and API helpers. */
2
- export const PROJECT_FEEDBACK_STYLE = String.raw `
3
- .kit-outcomes { scroll-margin-top: 56px; }
4
- .outcomes-layout { display: grid; grid-template-columns: minmax(280px,.9fr) minmax(0,1.1fr); align-items: start; }
5
- .outcome-editor { display: grid; gap: 10px; }
6
- .outcome-history { border-left: 1px solid var(--line); min-width: 0; }
7
- .outcome-history .panel-head { background: var(--inset); }
8
- .outcome-list { max-height: 560px; overflow: auto; }
9
- .outcome-row { display: grid; gap: 7px; padding: 11px 15px; }
10
- .outcome-row h4 { margin: 0; font-size: .93rem; overflow-wrap: anywhere; }
11
- .outcome-row .button { min-height: 32px; }
12
- .outcome-search { display: grid; grid-template-columns: minmax(0,1fr) auto; align-items: end; gap: 6px; }
13
- .outcome-reason { white-space: pre-wrap; }
14
- .kit-identity .kit-outcomes-jump { margin-top: 8px; color: var(--signal); }
15
- .kit-identity .kit-outcomes-jump:hover { background: #254350; }
16
- @media (max-width: 985px) {
17
- .outcomes-layout { grid-template-columns: minmax(0,1fr); }
18
- .outcome-history { border-left: 0; border-top: 1px solid var(--line); }
19
- }
2
+ export const PROJECT_FEEDBACK_STYLE = String.raw `
3
+ .kit-outcomes { scroll-margin-top: 56px; }
4
+ .outcomes-layout { display: grid; grid-template-columns: minmax(280px,.9fr) minmax(0,1.1fr); align-items: start; }
5
+ .outcome-editor { display: grid; gap: 10px; }
6
+ .outcome-history { border-left: 1px solid var(--line); min-width: 0; }
7
+ .outcome-history .panel-head { background: var(--inset); }
8
+ .outcome-list { max-height: 560px; overflow: auto; }
9
+ .outcome-row { display: grid; gap: 7px; padding: 11px 15px; }
10
+ .outcome-row h4 { margin: 0; font-size: .93rem; overflow-wrap: anywhere; }
11
+ .outcome-row .button { min-height: 32px; }
12
+ .outcome-search { display: grid; grid-template-columns: minmax(0,1fr) auto; align-items: end; gap: 6px; }
13
+ .outcome-reason { white-space: pre-wrap; }
14
+ .kit-identity .kit-outcomes-jump { margin-top: 8px; color: var(--signal); }
15
+ .kit-identity .kit-outcomes-jump:hover { background: #254350; }
16
+ @media (max-width: 985px) {
17
+ .outcomes-layout { grid-template-columns: minmax(0,1fr); }
18
+ .outcome-history { border-left: 0; border-top: 1px solid var(--line); }
19
+ }
20
20
  `;
21
- export const PROJECT_FEEDBACK_MARKUP = String.raw `
22
- <section class="panel kit-outcomes" aria-labelledby="kit-outcomes-heading" id="kit-outcomes">
23
- <div class="panel-head">
24
- <div><h2 id="kit-outcomes-heading" tabindex="-1">Memory outcomes</h2><p class="small muted" id="kit-outcomes-policy">Adaptive retrieval can use reported outcomes when a future task matches.</p></div>
25
- <button class="button switch" type="button" role="switch" id="kit-feedback-enable" aria-checked="false" aria-label="Use task outcomes in Adaptive retrieval" aria-describedby="kit-outcomes-policy" disabled><span id="kit-feedback-enabled-label" aria-hidden="true">Loading</span><span class="switch-track" aria-hidden="true"></span></button>
26
- </div>
27
- <div class="outcomes-layout">
28
- <div class="panel-body outcome-editor">
29
- <form id="kit-outcome-search-form" class="outcome-search" role="search" aria-label="Find a memory to rate">
30
- <label class="label" for="kit-outcome-query">Find project memory<input type="search" maxlength="500" id="kit-outcome-query" placeholder="Search by title or keyword"></label>
31
- <button class="button" id="kit-outcome-search" type="submit"><span data-icon="search"></span>Find</button>
32
- </form>
33
- <p class="small muted" id="kit-outcome-memory-status" role="status"></p>
34
- <form id="kit-outcome-form" class="kit-form">
35
- <label class="label" for="kit-outcome-memory">Memory<select id="kit-outcome-memory" required><option value="">Choose a memory</option></select></label>
36
- <label class="label" for="kit-outcome-task">Task<textarea id="kit-outcome-task" rows="2" maxlength="1000" required placeholder="What were you working on?"></textarea></label>
37
- <label class="label" for="kit-outcome-rating">Outcome<select id="kit-outcome-rating" required><option value="">Choose an outcome</option><option value="useful">Useful</option><option value="not-useful">Not useful</option><option value="incorrect">Incorrect</option></select></label>
38
- <label class="label" for="kit-outcome-reason">Reason<textarea id="kit-outcome-reason" rows="3" maxlength="600" required placeholder="How did this memory affect the task?"></textarea></label>
39
- <label class="label" for="kit-outcome-evidence">Recorded references <span class="small muted">Optional. Up to 3, one per line.</span><textarea id="kit-outcome-evidence" rows="2" maxlength="542" placeholder="tests/checkout-retry.test.ts"></textarea></label>
40
- <button class="button primary" id="kit-outcome-save" type="submit" disabled>Save reported outcome</button>
41
- <div id="kit-outcome-status" class="kit-form-status" role="status"></div>
42
- </form>
43
- </div>
44
- <div class="outcome-history">
45
- <div class="panel-head"><h3>Reported outcomes</h3><span class="small muted" id="kit-outcome-count"></span></div>
46
- <div id="kit-outcome-records" aria-busy="true"></div>
47
- </div>
48
- </div>
49
- </section>
21
+ export const PROJECT_FEEDBACK_MARKUP = String.raw `
22
+ <section class="panel kit-outcomes" aria-labelledby="kit-outcomes-heading" id="kit-outcomes">
23
+ <div class="panel-head">
24
+ <div><h2 id="kit-outcomes-heading" tabindex="-1">Memory outcomes</h2><p class="small muted" id="kit-outcomes-policy">Adaptive retrieval can use reported outcomes when a future task matches.</p></div>
25
+ <button class="button switch" type="button" role="switch" id="kit-feedback-enable" aria-checked="false" aria-label="Use task outcomes in Adaptive retrieval" aria-describedby="kit-outcomes-policy" disabled><span id="kit-feedback-enabled-label" aria-hidden="true">Loading</span><span class="switch-track" aria-hidden="true"></span></button>
26
+ </div>
27
+ <div class="outcomes-layout">
28
+ <div class="panel-body outcome-editor">
29
+ <form id="kit-outcome-search-form" class="outcome-search" role="search" aria-label="Find a memory to rate">
30
+ <label class="label" for="kit-outcome-query">Find project memory<input type="search" maxlength="500" id="kit-outcome-query" placeholder="Search by title or keyword"></label>
31
+ <button class="button" id="kit-outcome-search" type="submit"><span data-icon="search"></span>Find</button>
32
+ </form>
33
+ <p class="small muted" id="kit-outcome-memory-status" role="status"></p>
34
+ <form id="kit-outcome-form" class="kit-form">
35
+ <label class="label" for="kit-outcome-memory">Memory<select id="kit-outcome-memory" required><option value="">Choose a memory</option></select></label>
36
+ <label class="label" for="kit-outcome-task">Task<textarea id="kit-outcome-task" rows="2" maxlength="1000" required placeholder="What were you working on?"></textarea></label>
37
+ <label class="label" for="kit-outcome-rating">Outcome<select id="kit-outcome-rating" required><option value="">Choose an outcome</option><option value="useful">Useful</option><option value="not-useful">Not useful</option><option value="incorrect">Incorrect</option></select></label>
38
+ <label class="label" for="kit-outcome-reason">Reason<textarea id="kit-outcome-reason" rows="3" maxlength="600" required placeholder="How did this memory affect the task?"></textarea></label>
39
+ <label class="label" for="kit-outcome-evidence">Recorded references <span class="small muted">Optional. Up to 3, one per line.</span><textarea id="kit-outcome-evidence" rows="2" maxlength="542" placeholder="tests/checkout-retry.test.ts"></textarea></label>
40
+ <button class="button primary" id="kit-outcome-save" type="submit" disabled>Save reported outcome</button>
41
+ <div id="kit-outcome-status" class="kit-form-status" role="status"></div>
42
+ </form>
43
+ </div>
44
+ <div class="outcome-history">
45
+ <div class="panel-head"><h3>Reported outcomes</h3><span class="small muted" id="kit-outcome-count"></span></div>
46
+ <div id="kit-outcome-records" aria-busy="true"></div>
47
+ </div>
48
+ </div>
49
+ </section>
50
50
  `;
51
- export const PROJECT_FEEDBACK_SCRIPT = String.raw `
52
- const outcomes = { snapshot: null, saving: false, loading: false, memories: [], memoryRequest: 0, project: '' };
53
- const outcomeRatings = { useful: ['ok', 'Useful'], 'not-useful': ['warn', 'Not useful'], incorrect: ['error', 'Incorrect'] };
54
-
55
- function syncOutcomeControls() {
56
- const current = outcomes.snapshot;
57
- const toggle = $('kit-feedback-enable');
58
- toggle.disabled = !current || outcomes.saving;
59
- toggle.setAttribute('aria-checked', String(current?.enabled === true));
60
- $('kit-feedback-enabled-label').textContent = !current ? 'Unavailable' : current.enabled ? 'On' : 'Off';
61
- $('kit-outcome-save').disabled = !current || outcomes.saving || !$('kit-outcome-memory').value;
62
- $('kit-outcomes-policy').textContent = current?.enabled === false ?
63
- 'Learning is off. Reports remain available for review.' : 'Adaptive retrieval can use reported outcomes when a future task matches.';
64
- $('kit-outcome-records').querySelectorAll('[data-outcome-remove]').forEach((button) => { button.disabled = outcomes.saving || !current; });
65
- }
66
-
67
- function renderOutcomeHistory() {
68
- const current = outcomes.snapshot;
69
- const target = $('kit-outcome-records');
70
- if (!current) return;
71
- const records = [...list(current.records)].sort((a, b) => Date.parse(b.updatedAt) - Date.parse(a.updatedAt));
72
- $('kit-outcome-count').textContent = countLabel(records.length, 'report');
73
- const titles = new Map(outcomes.memories.map((memory) => [memory.id, memory.title]));
74
- target.innerHTML = records.length ? '<ul class="list outcome-list" aria-label="Task outcomes">' + records.map((record) => {
75
- const [tone, label] = Object.hasOwn(outcomeRatings, record.rating) ? outcomeRatings[record.rating] : ['', 'Reported'];
76
- return '<li class="outcome-row"><h4>' + esc(record.task) + '</h4><div class="form-actions">' +
77
- tags([['check-' + tone, label], ['memory', record.reporter === 'agent' ? 'Agent report' : 'User report']]) + dateMarkup(record.updatedAt, true) + '</div>' +
78
- '<p class="small outcome-reason">' + esc(record.reason) + '</p>' + sourceMarkup(record.evidence) + '<div class="form-actions">' +
79
- memoryLink(record.memoryId, record.projectId, titles.get(record.memoryId) || 'Open rated memory') +
80
- '<button type="button" class="button quiet" data-outcome-remove="' + esc(record.id) + '">Remove report</button></div></li>';
81
- }).join('') + '</ul>' : blank('activity', 'No task outcomes yet', 'Record whether a project memory helped with a specific task.');
82
- syncOutcomeControls();
83
- }
84
-
85
- async function loadProjectFeedback() {
86
- if (outcomes.loading || outcomes.saving || state.projectKit?.available !== true) return;
87
- const target = $('kit-outcome-records');
88
- outcomes.loading = true;
89
- setBusy(target, true);
90
- if (!outcomes.snapshot) target.innerHTML = skeleton('Reading reported outcomes…');
91
- try {
92
- const snapshot = await api('project-feedback');
93
- if (!Number.isSafeInteger(snapshot.revision) || typeof snapshot.enabled !== 'boolean' || !Array.isArray(snapshot.records)) {
94
- throw new Error('The server returned unreadable task outcomes.');
95
- }
96
- if (!outcomes.snapshot || snapshot.revision >= outcomes.snapshot.revision) outcomes.snapshot = snapshot;
97
- renderOutcomeHistory();
98
- } catch (error) {
99
- outcomes.snapshot = null;
100
- target.innerHTML = errorMarkup(error.message, 'kit-feedback');
101
- $('kit-outcome-count').textContent = '';
102
- } finally {
103
- outcomes.loading = false;
104
- setBusy(target, false);
105
- syncOutcomeControls();
106
- }
107
- }
108
-
109
- async function loadFeedbackMemories(focusMemory = '') {
110
- const info = state.projectKit;
111
- if (info?.available !== true) return;
112
- const request = ++outcomes.memoryRequest;
113
- const select = $('kit-outcome-memory');
114
- const status = $('kit-outcome-memory-status');
115
- const search = $('kit-outcome-search');
116
- const q = $('kit-outcome-query').value.trim();
117
- const selected = focusMemory || select.value;
118
- status.textContent = 'Finding project memories…';
119
- search.disabled = true;
120
- try {
121
- const result = await api(queryPath('memories', { project: info.projectId, q, limit: 25 }));
122
- if (request !== outcomes.memoryRequest) return;
123
- const memories = list(result.items).filter((item) => item.project === info.projectId);
124
- if (focusMemory && !memories.some((item) => item.id === focusMemory)) {
125
- try {
126
- const detail = await api(queryPath('memory', { project: info.projectId, id: focusMemory }));
127
- if (detail.memory?.project === info.projectId) memories.unshift(detail.memory);
128
- } catch { /* The selected memory may no longer be available. */ }
129
- }
130
- if (request !== outcomes.memoryRequest) return;
131
- outcomes.memories = memories;
132
- outcomes.project = info.projectId;
133
- optionSet('kit-outcome-memory', 'Choose a memory', memories.map((item) => ({ id: item.id, name: item.title || item.id })), selected);
134
- status.textContent = memories.length ? countLabel(memories.length, 'memory', 'memories') + ' shown. Search to narrow the list.' :
135
- q ? 'No memories matched this search.' : 'Save project knowledge to record a task outcome.';
136
- if (focusMemory && !select.value) status.textContent = 'The requested memory is unavailable in this project.';
137
- renderOutcomeHistory();
138
- } catch (error) {
139
- if (request === outcomes.memoryRequest) status.textContent = error.message;
140
- } finally {
141
- if (request === outcomes.memoryRequest) {
142
- search.disabled = false;
143
- syncOutcomeControls();
144
- }
145
- }
146
- }
147
-
148
- function openOutcomeEditor() {
149
- $('kit-outcomes-heading').focus();
150
- }
151
-
152
- async function updateProjectFeedback(command) {
153
- if (!outcomes.snapshot || outcomes.saving) return false;
154
- const status = $('kit-outcome-status');
155
- const before = outcomes.snapshot;
156
- outcomes.saving = true;
157
- syncOutcomeControls();
158
- status.textContent = 'Saving task outcomes…';
159
- let conflict = false;
160
- try {
161
- const result = await api('project-feedback', { body: { ...command, expectedRevision: before.revision } });
162
- const snapshot = result.snapshot || result;
163
- if (!Number.isSafeInteger(snapshot.revision) || typeof snapshot.enabled !== 'boolean' || !Array.isArray(snapshot.records)) {
164
- throw new Error('The server returned unreadable task outcomes.');
165
- }
166
- outcomes.snapshot = snapshot;
167
- state.context.dirty = !!state.context.pack || state.context.pending;
168
- if (state.context.pack && !state.context.pending) renderContext();
169
- renderOutcomeHistory();
170
- status.textContent = command.kind === 'record' ? 'Reported outcome saved.' : command.kind === 'remove' ? 'Report removed.' :
171
- command.enabled ? 'Outcome influence enabled.' : 'Outcome influence paused.';
172
- return true;
173
- } catch (error) {
174
- conflict = error.status === 409;
175
- status.innerHTML = errorMarkup(error.message);
176
- return false;
177
- } finally {
178
- outcomes.saving = false;
179
- syncOutcomeControls();
180
- if (conflict) void loadProjectFeedback();
181
- }
182
- }
183
-
184
- async function saveOutcome() {
185
- const memoryId = $('kit-outcome-memory').value;
186
- const task = $('kit-outcome-task').value.trim();
187
- const rating = $('kit-outcome-rating').value;
188
- const reason = $('kit-outcome-reason').value.trim();
189
- const referenceInput = $('kit-outcome-evidence').value;
190
- const evidence = referenceInput.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
191
- if (!memoryId || !task || !reason || !Object.hasOwn(outcomeRatings, rating)) {
192
- $('kit-outcome-status').innerHTML = errorMarkup('Choose a memory and provide the task, outcome, and reason.');
193
- return;
194
- }
195
- if (evidence.length > 3 || evidence.some((line) => line.length > 180)) {
196
- $('kit-outcome-status').innerHTML = errorMarkup('Enter up to three references with at most 180 characters each.');
197
- return;
198
- }
199
- if (await updateProjectFeedback({ kind: 'record', memoryId, task, rating, reason, ...(evidence.length ? { evidence } : {}) })) {
200
- if ($('kit-outcome-reason').value.trim() === reason) $('kit-outcome-reason').value = '';
201
- if ($('kit-outcome-evidence').value === referenceInput) $('kit-outcome-evidence').value = '';
202
- }
203
- }
204
-
205
- $('kit-feedback-enable').addEventListener('click', () => {
206
- if (outcomes.snapshot) void updateProjectFeedback({ kind: 'enable', enabled: !outcomes.snapshot.enabled });
207
- });
208
- $('kit-outcome-search-form').addEventListener('submit', (event) => { event.preventDefault(); void loadFeedbackMemories(); });
209
- $('kit-outcome-form').addEventListener('submit', (event) => { event.preventDefault(); void saveOutcome(); });
210
- $('kit-outcome-memory').addEventListener('change', syncOutcomeControls);
211
- $('kit-outcome-records').addEventListener('click', (event) => {
212
- const button = event.target.closest('[data-outcome-remove]');
213
- if (button) void updateProjectFeedback({ kind: 'remove', id: button.dataset.outcomeRemove });
214
- });
51
+ export const PROJECT_FEEDBACK_SCRIPT = String.raw `
52
+ const outcomes = { snapshot: null, saving: false, loading: false, memories: [], memoryRequest: 0, project: '' };
53
+ const outcomeRatings = { useful: ['ok', 'Useful'], 'not-useful': ['warn', 'Not useful'], incorrect: ['error', 'Incorrect'] };
54
+
55
+ function syncOutcomeControls() {
56
+ const current = outcomes.snapshot;
57
+ const toggle = $('kit-feedback-enable');
58
+ toggle.disabled = !current || outcomes.saving;
59
+ toggle.setAttribute('aria-checked', String(current?.enabled === true));
60
+ $('kit-feedback-enabled-label').textContent = !current ? 'Unavailable' : current.enabled ? 'On' : 'Off';
61
+ $('kit-outcome-save').disabled = !current || outcomes.saving || !$('kit-outcome-memory').value;
62
+ $('kit-outcomes-policy').textContent = current?.enabled === false ?
63
+ 'Learning is off. Reports remain available for review.' : 'Adaptive retrieval can use reported outcomes when a future task matches.';
64
+ $('kit-outcome-records').querySelectorAll('[data-outcome-remove]').forEach((button) => { button.disabled = outcomes.saving || !current; });
65
+ }
66
+
67
+ function renderOutcomeHistory() {
68
+ const current = outcomes.snapshot;
69
+ const target = $('kit-outcome-records');
70
+ if (!current) return;
71
+ const records = [...list(current.records)].sort((a, b) => Date.parse(b.updatedAt) - Date.parse(a.updatedAt));
72
+ $('kit-outcome-count').textContent = countLabel(records.length, 'report');
73
+ const titles = new Map(outcomes.memories.map((memory) => [memory.id, memory.title]));
74
+ target.innerHTML = records.length ? '<ul class="list outcome-list" aria-label="Task outcomes">' + records.map((record) => {
75
+ const [tone, label] = Object.hasOwn(outcomeRatings, record.rating) ? outcomeRatings[record.rating] : ['', 'Reported'];
76
+ return '<li class="outcome-row"><h4>' + esc(record.task) + '</h4><div class="form-actions">' +
77
+ tags([['check-' + tone, label], ['memory', record.reporter === 'agent' ? 'Agent report' : 'User report']]) + dateMarkup(record.updatedAt, true) + '</div>' +
78
+ '<p class="small outcome-reason">' + esc(record.reason) + '</p>' + sourceMarkup(record.evidence) + '<div class="form-actions">' +
79
+ memoryLink(record.memoryId, record.projectId, titles.get(record.memoryId) || 'Open rated memory') +
80
+ '<button type="button" class="button quiet" data-outcome-remove="' + esc(record.id) + '">Remove report</button></div></li>';
81
+ }).join('') + '</ul>' : blank('activity', 'No task outcomes yet', 'Record whether a project memory helped with a specific task.');
82
+ syncOutcomeControls();
83
+ }
84
+
85
+ async function loadProjectFeedback() {
86
+ if (outcomes.loading || outcomes.saving || state.projectKit?.available !== true) return;
87
+ const target = $('kit-outcome-records');
88
+ outcomes.loading = true;
89
+ setBusy(target, true);
90
+ if (!outcomes.snapshot) target.innerHTML = skeleton('Reading reported outcomes…');
91
+ try {
92
+ const snapshot = await api('project-feedback');
93
+ if (!Number.isSafeInteger(snapshot.revision) || typeof snapshot.enabled !== 'boolean' || !Array.isArray(snapshot.records)) {
94
+ throw new Error('The server returned unreadable task outcomes.');
95
+ }
96
+ if (!outcomes.snapshot || snapshot.revision >= outcomes.snapshot.revision) outcomes.snapshot = snapshot;
97
+ renderOutcomeHistory();
98
+ } catch (error) {
99
+ outcomes.snapshot = null;
100
+ target.innerHTML = errorMarkup(error.message, 'kit-feedback');
101
+ $('kit-outcome-count').textContent = '';
102
+ } finally {
103
+ outcomes.loading = false;
104
+ setBusy(target, false);
105
+ syncOutcomeControls();
106
+ }
107
+ }
108
+
109
+ async function loadFeedbackMemories(focusMemory = '') {
110
+ const info = state.projectKit;
111
+ if (info?.available !== true) return;
112
+ const request = ++outcomes.memoryRequest;
113
+ const select = $('kit-outcome-memory');
114
+ const status = $('kit-outcome-memory-status');
115
+ const search = $('kit-outcome-search');
116
+ const q = $('kit-outcome-query').value.trim();
117
+ const selected = focusMemory || select.value;
118
+ status.textContent = 'Finding project memories…';
119
+ search.disabled = true;
120
+ try {
121
+ const result = await api(queryPath('memories', { project: info.projectId, q, limit: 25 }));
122
+ if (request !== outcomes.memoryRequest) return;
123
+ const memories = list(result.items).filter((item) => item.project === info.projectId);
124
+ if (focusMemory && !memories.some((item) => item.id === focusMemory)) {
125
+ try {
126
+ const detail = await api(queryPath('memory', { project: info.projectId, id: focusMemory }));
127
+ if (detail.memory?.project === info.projectId) memories.unshift(detail.memory);
128
+ } catch { /* The selected memory may no longer be available. */ }
129
+ }
130
+ if (request !== outcomes.memoryRequest) return;
131
+ outcomes.memories = memories;
132
+ outcomes.project = info.projectId;
133
+ optionSet('kit-outcome-memory', 'Choose a memory', memories.map((item) => ({ id: item.id, name: item.title || item.id })), selected);
134
+ status.textContent = memories.length ? countLabel(memories.length, 'memory', 'memories') + ' shown. Search to narrow the list.' :
135
+ q ? 'No memories matched this search.' : 'Save project knowledge to record a task outcome.';
136
+ if (focusMemory && !select.value) status.textContent = 'The requested memory is unavailable in this project.';
137
+ renderOutcomeHistory();
138
+ } catch (error) {
139
+ if (request === outcomes.memoryRequest) status.textContent = error.message;
140
+ } finally {
141
+ if (request === outcomes.memoryRequest) {
142
+ search.disabled = false;
143
+ syncOutcomeControls();
144
+ }
145
+ }
146
+ }
147
+
148
+ function openOutcomeEditor() {
149
+ $('kit-outcomes-heading').focus();
150
+ }
151
+
152
+ async function updateProjectFeedback(command) {
153
+ if (!outcomes.snapshot || outcomes.saving) return false;
154
+ const status = $('kit-outcome-status');
155
+ const before = outcomes.snapshot;
156
+ outcomes.saving = true;
157
+ syncOutcomeControls();
158
+ status.textContent = 'Saving task outcomes…';
159
+ let conflict = false;
160
+ try {
161
+ const result = await api('project-feedback', { body: { ...command, expectedRevision: before.revision } });
162
+ const snapshot = result.snapshot || result;
163
+ if (!Number.isSafeInteger(snapshot.revision) || typeof snapshot.enabled !== 'boolean' || !Array.isArray(snapshot.records)) {
164
+ throw new Error('The server returned unreadable task outcomes.');
165
+ }
166
+ outcomes.snapshot = snapshot;
167
+ state.context.dirty = !!state.context.pack || state.context.pending;
168
+ if (state.context.pack && !state.context.pending) renderContext();
169
+ renderOutcomeHistory();
170
+ status.textContent = command.kind === 'record' ? 'Reported outcome saved.' : command.kind === 'remove' ? 'Report removed.' :
171
+ command.enabled ? 'Outcome influence enabled.' : 'Outcome influence paused.';
172
+ return true;
173
+ } catch (error) {
174
+ conflict = error.status === 409;
175
+ status.innerHTML = errorMarkup(error.message);
176
+ return false;
177
+ } finally {
178
+ outcomes.saving = false;
179
+ syncOutcomeControls();
180
+ if (conflict) void loadProjectFeedback();
181
+ }
182
+ }
183
+
184
+ async function saveOutcome() {
185
+ const memoryId = $('kit-outcome-memory').value;
186
+ const task = $('kit-outcome-task').value.trim();
187
+ const rating = $('kit-outcome-rating').value;
188
+ const reason = $('kit-outcome-reason').value.trim();
189
+ const referenceInput = $('kit-outcome-evidence').value;
190
+ const evidence = referenceInput.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
191
+ if (!memoryId || !task || !reason || !Object.hasOwn(outcomeRatings, rating)) {
192
+ $('kit-outcome-status').innerHTML = errorMarkup('Choose a memory and provide the task, outcome, and reason.');
193
+ return;
194
+ }
195
+ if (evidence.length > 3 || evidence.some((line) => line.length > 180)) {
196
+ $('kit-outcome-status').innerHTML = errorMarkup('Enter up to three references with at most 180 characters each.');
197
+ return;
198
+ }
199
+ if (await updateProjectFeedback({ kind: 'record', memoryId, task, rating, reason, ...(evidence.length ? { evidence } : {}) })) {
200
+ if ($('kit-outcome-reason').value.trim() === reason) $('kit-outcome-reason').value = '';
201
+ if ($('kit-outcome-evidence').value === referenceInput) $('kit-outcome-evidence').value = '';
202
+ }
203
+ }
204
+
205
+ $('kit-feedback-enable').addEventListener('click', () => {
206
+ if (outcomes.snapshot) void updateProjectFeedback({ kind: 'enable', enabled: !outcomes.snapshot.enabled });
207
+ });
208
+ $('kit-outcome-search-form').addEventListener('submit', (event) => { event.preventDefault(); void loadFeedbackMemories(); });
209
+ $('kit-outcome-form').addEventListener('submit', (event) => { event.preventDefault(); void saveOutcome(); });
210
+ $('kit-outcome-memory').addEventListener('change', syncOutcomeControls);
211
+ $('kit-outcome-records').addEventListener('click', (event) => {
212
+ const button = event.target.closest('[data-outcome-remove]');
213
+ if (button) void updateProjectFeedback({ kind: 'remove', id: button.dataset.outcomeRemove });
214
+ });
215
215
  `;
216
216
  //# sourceMappingURL=project-feedback-html.js.map