github-issue-tower-defence-management 1.151.6 → 1.152.0
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/.github/workflows/create-pr.yml +1 -1
- package/CHANGELOG.md +14 -0
- package/bin/adapter/entry-points/console/ui-dist/assets/index-DR_LIP9n.js +83 -0
- package/bin/adapter/entry-points/console/ui-dist/assets/index-lemrqYKZ.css +2 -0
- package/bin/adapter/entry-points/console/ui-dist/index.html +2 -2
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js +0 -19
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js.map +1 -1
- package/bin/adapter/entry-points/handlers/inTmuxByHumanDataWriter.js +1 -13
- package/bin/adapter/entry-points/handlers/inTmuxByHumanDataWriter.js.map +1 -1
- package/bin/adapter/repositories/TranscriptOwnerCallStatusProvider.js +0 -41
- package/bin/adapter/repositories/TranscriptOwnerCallStatusProvider.js.map +1 -1
- package/bin/domain/usecases/intmux/GenerateInTmuxByHumanDataUseCase.js +2 -21
- package/bin/domain/usecases/intmux/GenerateInTmuxByHumanDataUseCase.js.map +1 -1
- package/package.json +1 -1
- package/src/adapter/entry-points/console/ui/e2e/consoleScenario.spec.ts +23 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentComposer.stories.tsx +0 -1
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentComposer.test.tsx +6 -15
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentComposer.tsx +1 -33
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemDetail.tsx +4 -1
- package/src/adapter/entry-points/console/ui/src/features/console/logic/postedComments.test.ts +44 -0
- package/src/adapter/entry-points/console/ui/src/features/console/logic/postedComments.ts +17 -0
- package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleItemDetailContainer.test.tsx +77 -0
- package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleItemDetailContainer.tsx +14 -3
- package/src/adapter/entry-points/console/ui/src/index.css +0 -7
- package/src/adapter/entry-points/console/ui-dist/assets/index-DR_LIP9n.js +83 -0
- package/src/adapter/entry-points/console/ui-dist/assets/index-lemrqYKZ.css +2 -0
- package/src/adapter/entry-points/console/ui-dist/index.html +2 -2
- package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts +0 -33
- package/src/adapter/entry-points/handlers/inTmuxByHumanDataWriter.test.ts +3 -104
- package/src/adapter/entry-points/handlers/inTmuxByHumanDataWriter.ts +0 -19
- package/src/adapter/repositories/TranscriptOwnerCallStatusProvider.singleScan.test.ts +10 -12
- package/src/adapter/repositories/TranscriptOwnerCallStatusProvider.test.ts +0 -178
- package/src/adapter/repositories/TranscriptOwnerCallStatusProvider.ts +0 -61
- package/src/domain/usecases/intmux/GenerateInTmuxByHumanDataUseCase.test.ts +0 -99
- package/src/domain/usecases/intmux/GenerateInTmuxByHumanDataUseCase.ts +1 -47
- package/types/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.d.ts.map +1 -1
- package/types/adapter/entry-points/handlers/inTmuxByHumanDataWriter.d.ts +0 -2
- package/types/adapter/entry-points/handlers/inTmuxByHumanDataWriter.d.ts.map +1 -1
- package/types/adapter/repositories/TranscriptOwnerCallStatusProvider.d.ts +0 -3
- package/types/adapter/repositories/TranscriptOwnerCallStatusProvider.d.ts.map +1 -1
- package/types/domain/usecases/intmux/GenerateInTmuxByHumanDataUseCase.d.ts +0 -19
- package/types/domain/usecases/intmux/GenerateInTmuxByHumanDataUseCase.d.ts.map +1 -1
- package/bin/adapter/entry-points/console/ui-dist/assets/index-ChJztBsb.js +0 -83
- package/bin/adapter/entry-points/console/ui-dist/assets/index-wDluFyBz.css +0 -2
- package/src/adapter/entry-points/console/ui-dist/assets/index-ChJztBsb.js +0 -83
- package/src/adapter/entry-points/console/ui-dist/assets/index-wDluFyBz.css +0 -2
- package/src/adapter/entry-points/handlers/resolveUnansweredOwnerCalls.test.ts +0 -111
- package/src/adapter/entry-points/handlers/resolveUnansweredOwnerCalls.ts +0 -33
- package/src/domain/entities/UnansweredOwnerCall.ts +0 -4
- package/src/domain/usecases/intmux/UnansweredOwnerCallSessionKeyContract.test.ts +0 -141
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
|
|
2
|
+
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style:solid;--tw-font-weight:initial;--tw-outline-style:solid;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--spacing:.25rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--font-weight-medium:500;--font-weight-semibold:600;--radius-md:.375rem;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-input:#e5e5e5;--color-ring:#0a0a0a;--color-background:#fff;--color-foreground:#0a0a0a;--color-primary:#171717;--color-primary-foreground:#fafafa;--color-secondary:#f5f5f5;--color-secondary-foreground:#171717;--color-accent:#f5f5f5;--color-accent-foreground:#171717}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.visible{visibility:visible}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.container{width:100%}@media (width>=40rem){.container{max-width:40rem}}@media (width>=48rem){.container{max-width:48rem}}@media (width>=64rem){.container{max-width:64rem}}@media (width>=80rem){.container{max-width:80rem}}@media (width>=96rem){.container{max-width:96rem}}.block{display:block}.hidden{display:none}.inline{display:inline}.inline-flex{display:inline-flex}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.h-10{height:calc(var(--spacing) * 10)}.grow{flex-grow:1}.items-center{align-items:center}.justify-center{justify-content:center}.gap-2{gap:calc(var(--spacing) * 2)}.rounded-md{border-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-input{border-color:var(--color-input)}.border-transparent{border-color:#0000}.bg-background{background-color:var(--color-background)}.bg-primary{background-color:var(--color-primary)}.bg-secondary{background-color:var(--color-secondary)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-8{padding-inline:calc(var(--spacing) * 8)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-2{padding-block:calc(var(--spacing) * 2)}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.whitespace-nowrap{white-space:nowrap}.text-foreground{color:var(--color-foreground)}.text-primary-foreground{color:var(--color-primary-foreground)}.text-secondary-foreground{color:var(--color-secondary-foreground)}.lowercase{text-transform:lowercase}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media (hover:hover){.hover\:bg-accent:hover{background-color:var(--color-accent)}.hover\:bg-primary\/90:hover{background-color:#171717e6}@supports (color:color-mix(in lab, red, red)){.hover\:bg-primary\/90:hover{background-color:color-mix(in oklab, var(--color-primary) 90%, transparent)}}.hover\:bg-secondary\/80:hover{background-color:#f5f5f5cc}@supports (color:color-mix(in lab, red, red)){.hover\:bg-secondary\/80:hover{background-color:color-mix(in oklab, var(--color-secondary) 80%, transparent)}}.hover\:text-accent-foreground:hover{color:var(--color-accent-foreground)}}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus-visible\:ring-ring:focus-visible{--tw-ring-color:var(--color-ring)}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:opacity-50:disabled{opacity:.5}}html{font-size:clamp(15px,2.1vw,26px)}body{color:#e6edf3;background-color:#0d1117;margin:0;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif}.console-app{flex-direction:column;width:100%;min-height:100dvh;display:flex}.console-tabbar{-webkit-overflow-scrolling:touch;background:#161b22;border-bottom:2px solid #30363d;flex-wrap:nowrap;align-items:stretch;gap:0;min-height:42px;padding:0 8px;display:flex;overflow-x:auto}.console-tab{color:#8b949e;white-space:nowrap;cursor:pointer;background:0 0;border:none;border-bottom:2px solid #0000;align-items:center;gap:6px;margin-bottom:-2px;padding:10px 14px;font-size:.8125rem;font-weight:500;line-height:1.2;text-decoration:none;display:inline-flex}.console-tab:hover{color:#e6edf3}.console-tab[data-active=true]{color:#e6edf3;border-bottom-color:#2f81f7;font-weight:700}.console-tab-badge{text-align:center;color:#e6edf3;background:#484f58;border-radius:20px;min-width:20px;padding:1px 7px;font-size:.6875rem;font-weight:700;line-height:1.5}.console-tab-badge[data-zero=true]{color:#8b949e;background:#30363d}.console-tab-pjname{color:#8b949e;align-self:center;margin-left:auto;padding:0 8px;font-size:.7188rem}.console-tab-geninfo{color:#8b949e;align-self:center;padding:0 4px;font-size:.6875rem}.console-list{margin:0;padding:12px 18px 18px;list-style:none}.console-list-group{list-style:none}.console-item-row .console-item-icon{flex:none;margin-top:3px}.console-group-header{background:#0b0f14;border-bottom:1px solid #21262d;justify-content:space-between;align-items:center;padding:6px 12px;display:flex}.console-storytag{align-items:center;gap:8px;font-size:.8125rem;font-weight:700;display:inline-flex}.console-story-dot{border-radius:999px;width:10px;height:10px;display:inline-block}.console-group-count{color:#8b949e;font-size:.75rem}.console-item-row{color:#e6edf3;text-align:left;cursor:pointer;background:#161b22;border:1px solid #30363d;border-radius:8px;align-items:flex-start;gap:14px;width:100%;margin-bottom:10px;padding:12px 16px;display:flex}.console-item-row:hover{background:#1a2029;border-color:#484f58}.console-item-row[data-active=true]{background:#1a2029;border-color:#4493f8}.console-item-meta{flex:1;min-width:0}.console-item-title{font-size:.9063rem;font-weight:600;display:block}.console-item-sub{color:#8b949e;margin-top:3px;font-size:.7813rem;display:block}.console-item-pill{color:#8b949e;border:1px solid #30363d;border-radius:20px;margin-right:6px;padding:1px 8px;font-size:.6875rem;display:inline-block}.console-item-createdat{color:#8b949e;cursor:help}.console-item-fields{color:#adbac7;flex-wrap:wrap;gap:4px 10px;margin-top:4px;font-size:.7188rem;display:flex}.console-item-field{word-break:break-all;align-items:baseline;gap:4px;min-width:0;display:inline-flex}.console-item-field-label{color:#6e7681;text-transform:uppercase;letter-spacing:.03em;font-size:.625rem}.console-list-message{color:#8b949e;padding:16px;font-size:.875rem}.console-list-empty{text-align:center;color:#8b949e;padding:40px;font-size:.875rem}.console-list-error,.console-comment-error,.console-files-error,.console-commits-error,.console-detail-fetch-error,.console-detail-body-error{color:#f85149}.console-detail{flex-direction:column;flex:1 0 auto;gap:12px;padding:16px 16px 0;display:flex}.console-detail-topline{flex-wrap:wrap;align-items:center;gap:8px;display:flex}.console-detail-title{align-items:center;gap:8px;margin:0;font-size:1.25rem;display:flex}.console-detail-title-text{flex:1}.console-detail-number{color:#8b949e;font-weight:400}.console-detail-closed-label{color:#a371f7;font-size:.8125rem}.console-detail-subbar{align-items:center;gap:12px;font-size:.8125rem;display:flex}.console-detail-link{color:#4493f8}.console-detail-repo{color:#8b949e;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.console-copy-url-button{color:#c9d1d9;border:1px solid #30363d;border-radius:6px;height:24px;padding:0 8px;font-size:.75rem}.console-copy-url-button:hover{color:#e6edf3;background-color:#21262d}.console-detail-pill,.console-label-chip,.console-detail-status-chip{border:1px solid #30363d;border-radius:999px;padding:2px 8px;font-size:.75rem;display:inline-block}.console-detail-pr-status{flex-wrap:wrap;align-items:center;gap:6px;display:inline-flex}.console-detail-ci-missing{opacity:.85;font-weight:400}.console-detail-mergeable-chip{font-weight:600}.console-detail-mergeable-chip-unknown{opacity:.7;font-weight:400}.console-detail-labels{flex-wrap:wrap;gap:6px;display:flex}.console-detail-createdat{color:#8b949e;font-size:.75rem}.console-panel{border:1px solid #30363d;border-radius:8px;overflow:hidden}.console-panel-header{background:#161b22;justify-content:space-between;align-items:center;display:flex}.console-panel-toggle{color:#e6edf3;text-align:left;cursor:pointer;background:0 0;border:none;flex:1;align-items:center;gap:8px;padding:6px 12px;font-size:.875rem;font-weight:600;display:flex}.console-panel-toggle:hover{background:#1a2029}.console-panel-action{padding:6px 12px}.console-panel-body{padding:12px}.console-markdown{word-break:break-word;font-size:.875rem;line-height:1.5}.console-markdown ul{margin:.5em 0;padding-left:1.5em;list-style:outside}.console-markdown ol{margin:.5em 0;padding-left:1.5em;list-style:decimal}.console-markdown li{margin:.25em 0}.console-markdown table{border-collapse:collapse;margin:.5em 0}.console-markdown th,.console-markdown td{border:1px solid #30363d;padding:4px 8px}.console-markdown th{background:#161b22}.console-markdown h1,.console-markdown h2,.console-markdown h3,.console-markdown h4,.console-markdown h5,.console-markdown h6{margin:1em 0 .5em;font-weight:700;line-height:1.25}.console-markdown h1{font-size:1.6em}.console-markdown h2{font-size:1.4em}.console-markdown h3{font-size:1.2em}.console-markdown h4{font-size:1.05em}.console-markdown h5{font-size:.95em}.console-markdown h6{color:#8b949e;font-size:.9em}.console-markdown pre{background:#161b22;border:1px solid #30363d;border-radius:6px;margin:.5em 0;padding:12px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.8125rem;line-height:1.45;overflow-x:auto}.console-markdown code{background:#6e768166;border-radius:4px;padding:.15em .4em;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.9em}.console-markdown pre code{font-size:inherit;background:0 0;border-radius:0;padding:0}.console-markdown-code-block{flex-direction:column;margin:.5em 0;display:flex}.console-markdown-code-block pre{margin:0}.console-markdown-code-copy-host{justify-content:flex-end;margin-bottom:4px;display:flex}.console-copy-code-button{color:#c9d1d9;background-color:#161b22;border:1px solid #30363d;border-radius:6px;height:24px;padding:0 8px;font-size:.75rem}.console-copy-code-button:hover{color:#e6edf3;background-color:#21262d}.console-markdown blockquote{color:#8b949e;border-left:3px solid #30363d;margin:.5em 0;padding:0 1em}.console-markdown a{color:#4493f8;text-decoration:none}.console-markdown a:hover{text-decoration:underline}.console-markdown-reference-host{display:inline}.console-markdown-reference{vertical-align:baseline;align-items:center;gap:.25em;display:inline-flex}.console-markdown-reference .console-item-icon{flex-shrink:0}.console-markdown-reference-title{text-decoration:none}.console-markdown-reference:hover .console-markdown-reference-title{text-decoration:underline}.console-markdown hr{border:none;border-top:1px solid #30363d;margin:1em 0}.console-mermaid-error{color:#f85149;font-size:.8125rem}.console-comment{background:#161b22;border:1px solid #30363d;border-radius:8px;margin-bottom:10px;padding:12px 16px}.console-comment-header{color:#8b949e;gap:8px;font-size:.75rem;display:flex}.console-comment-show-all{color:#e6edf3;cursor:pointer;background:#21262d;border:1px solid #30363d;border-radius:6px;margin-bottom:10px;padding:6px 12px;font-size:.75rem;font-weight:600}.console-comment-show-all:hover{background-color:#262c34;border-color:#484f58}.console-comment-author{color:#e6edf3;font-weight:600}.console-files,.console-commits{margin:0;padding:0;list-style:none}.console-commit{align-items:center;gap:8px;padding:4px 0;font-size:.8125rem;display:flex}.console-file{flex-direction:column;font-size:.8125rem;display:flex}.console-file-tree,.console-file-tree-children{margin:0;padding:0;list-style:none}.console-file-tree-dir{flex-direction:column;display:flex}.console-file-tree-dir-name{color:#8b949e;align-items:center;gap:6px;padding:4px 0;font-weight:600;display:flex}.console-file-tree-dir-icon{flex:none}.console-file-badge{text-align:center;border:1px solid;border-radius:4px;width:18px;font-size:.6875rem}.console-file-path,.console-commit-message{flex:1;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.console-file-add,.console-pr-add{color:#3fb950}.console-file-del,.console-pr-del{color:#f85149}.console-commit-sha{color:#8b949e;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.console-detail-dock{z-index:100;background:#161b22;border-top:2px solid #30363d;margin-top:auto;position:sticky;bottom:0}.console-detail-dock-composer{max-height:50vh;padding:8px 16px 0;overflow-y:auto}.console-actionbar{padding:10px 16px;padding-bottom:calc(10px + env(safe-area-inset-bottom));background:#161b22}.console-operation-bar{flex-direction:column;gap:8px;width:100%;display:flex}.console-op-group{flex-wrap:wrap;justify-content:flex-end;align-items:center;gap:8px;display:flex}.console-op-group-review{gap:10px}.console-op-group-stories{max-height:33vh;overflow-y:auto}.console-op-button{color:#e6edf3;white-space:nowrap;cursor:pointer;background:#21262d;border:1px solid #30363d;border-radius:6px;padding:9px 16px;font-size:.8125rem;font-weight:600}.console-op-button:hover{border-color:#484f58}.console-op-button:disabled{opacity:.4;cursor:not-allowed}.console-op-requirement{text-align:right;color:#8b949e;width:100%;margin:0;font-size:.75rem}.console-op-button-approve{color:#fff;background:#238636;border-color:#2ea043}.console-op-button-reject{color:#ffd166;background:#7d5000;border-color:#a06800}.console-op-button-wrong{color:#f85149;background:#3a1518;border-color:#f85149}.console-op-button-unneeded{color:#aab0b8;background:#2a2d31;border-color:#6e7681}.console-op-button-snooze{color:#79c0ff;background:#1c2b4a;border-color:#4493f8}.console-pr-section{border:1px solid #30363d;border-radius:8px;flex-direction:column;gap:10px;padding:12px;display:flex}.console-pr-statbar{color:#8b949e;gap:12px;font-size:.75rem;display:flex}.console-detail-screen{flex-direction:column;flex:1 0 auto;display:flex}.console-panel-open-link{color:#4493f8;font-size:.8125rem;font-weight:400}.console-composer{margin-top:4px}.console-composer-toggle{color:#8b949e;cursor:pointer;background:0 0;border:none;margin-left:auto;padding:2px 0;font-size:.7813rem;display:block}.console-composer-toggle:hover{color:#e6edf3}.console-composer-form{margin-top:8px}.console-composer-input{box-sizing:border-box;color:#e6edf3;width:100%;font:inherit;resize:vertical;background:#21262d;border:1px solid #30363d;border-radius:6px;padding:8px;font-size:.875rem}.console-composer-attach{align-items:center;gap:8px;padding:0 10px;display:flex}.console-composer-file-input{display:none}.console-composer-attach-button{color:#c9d1d9;cursor:pointer;background:#21262d;border:1px solid #30363d;border-radius:4px;padding:4px 8px;font-size:.75rem}.console-composer-attach-button:disabled{opacity:.6;cursor:default}.console-composer-row{justify-content:flex-end;align-items:center;gap:8px;margin-top:6px;display:flex}.console-composer-submit{color:#fff;cursor:pointer;background:#238636;border:1px solid #2ea043;border-radius:6px;padding:7px 16px;font-size:.8125rem;font-weight:600}.console-composer-submit:disabled{opacity:.6;cursor:default}.console-composer-status{color:#8b949e;font-size:.75rem}.console-composer-error{color:#f85149}.console-undo-toast{z-index:9998;white-space:nowrap;border-radius:10px;align-items:center;gap:12px;max-width:92vw;padding:12px 18px;font-size:.8438rem;display:flex;position:fixed;top:12px;left:50%;transform:translate(-50%);box-shadow:0 4px 20px #0009}.console-undo-toast-green{color:#3fb950;background:#0d3320;border:1px solid #2ea043}.console-undo-toast-amber{color:#ffd166;background:#3a2800;border:1px solid #a06800}.console-undo-toast-red{color:#f85149;background:#3a1518;border:1px solid #da3633}.console-undo-toast-gray{color:#aab0b8;background:#2a2d31;border:1px solid #6e7681}.console-undo-toast-blue{color:#79c0ff;background:#0d1f40;border:1px solid #4493f8}.console-undo-toast-error{color:#f85149;background:#3a1518;border:1px solid #f85149}.console-undo-toast-message{text-overflow:ellipsis;flex:1;min-width:0;overflow:hidden}.console-undo-toast-undo{cursor:pointer;color:inherit;background:#ffffff1f;border:none;border-radius:6px;flex:none;padding:4px 12px;font-size:.8125rem;font-weight:700}.console-undo-toast-undo:hover{background:#ffffff38}.console-undo-toast-countdown{opacity:.7;flex:none;font-size:.6875rem}.console-undo-toast-bar{opacity:.5;background:currentColor;border-radius:0 0 10px 10px;height:3px;transition:width .1s linear;position:absolute;bottom:0;left:0}.console-file-row{width:100%;color:inherit;font:inherit;text-align:left;cursor:pointer;background:0 0;border:none;align-items:center;gap:8px;padding:4px 0;font-size:.8125rem;display:flex}.console-file-row:hover{background:#1a2029}.console-file-caret{color:#8b949e;flex:none;width:1em}.console-file-diff{border-collapse:collapse;width:100%;margin:4px 0 8px;font:.8125rem/1.6 ui-monospace,SFMono-Regular,Menlo,monospace}.console-file-diff td{white-space:pre-wrap;word-break:break-word;vertical-align:top;padding:0 10px}.console-diff-ln{text-align:right;color:#6e7681;-webkit-user-select:none;user-select:none;white-space:nowrap;border-right:1px solid #30363d;width:1%;padding:0 8px}.console-file-diff td.console-diff-ln{white-space:nowrap;word-break:normal;overflow-wrap:normal}.console-diff-add td{background:#2ea04326}.console-diff-add .console-diff-code{color:#aff5b4}.console-diff-del td{background:#f8514926}.console-diff-del .console-diff-code{color:#ffdcd7}.console-diff-hunk td{color:#79c0ff;background:#161b22}.console-diff-ctx .console-diff-code{color:#c9d1d9}.console-file-diff-empty{color:#8b949e;padding:8px 14px;font-size:.75rem}.console-file-diff-image{padding:8px 14px}.console-file-diff-image-content{object-fit:contain;object-position:left top;border:1px solid #30363d;border-radius:4px;max-width:100%;max-height:420px}.console-diff-comment-cell{text-align:center;-webkit-user-select:none;user-select:none;width:1%;padding:0 2px}.console-diff-comment-button{color:#6e7681;cursor:pointer;opacity:.6;background:0 0;border:1px solid #30363d;border-radius:4px;width:18px;height:18px;padding:0;font-size:.875rem;line-height:16px}.console-diff-row:hover .console-diff-comment-button,.console-diff-comment-button:focus-visible,.console-diff-comment-button[aria-expanded=true]{opacity:1;color:#fff;background:#1f6feb;border-color:#2f81f7}.console-diff-composer-row td{background:#0d1117;padding:6px 10px}.console-diff-composer{background:#161b22;border:1px solid #30363d;border-radius:6px;flex-direction:column;gap:6px;padding:8px;display:flex}.console-diff-composer-anchor{color:#8b949e;font-size:.6875rem}.console-diff-composer-input{resize:vertical;color:#c9d1d9;width:100%;font:inherit;background:#0d1117;border:1px solid #30363d;border-radius:6px;padding:6px 8px}.console-diff-composer-controls{align-items:center;gap:8px;display:flex}.console-diff-composer-submit{color:#fff;cursor:pointer;background:#238636;border:1px solid #238636;border-radius:6px;padding:4px 12px}.console-diff-composer-submit:disabled{opacity:.6;cursor:default}.console-diff-composer-cancel{color:#c9d1d9;cursor:pointer;background:0 0;border:1px solid #30363d;border-radius:6px;padding:4px 12px}.console-diff-composer-status{color:#8b949e;font-size:.75rem}.console-diff-composer-error{color:#ff7b72}.console-diff-composer-posted{color:#3fb950;margin:0;font-size:.75rem}.console-pr-header{flex-wrap:wrap;align-items:center;gap:10px;padding-top:4px;display:flex}.console-pr-section-title{color:#e6edf3;font-size:.9375rem;font-weight:600}.console-pr-section-state{color:#8b949e;border:1px solid #6e7681;border-radius:999px;padding:1px 8px;font-size:.6875rem}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>TDPM Console</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-DR_LIP9n.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="/assets/index-lemrqYKZ.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
11
11
|
<div id="root"></div>
|
|
@@ -19,14 +19,9 @@ import {
|
|
|
19
19
|
DEFAULT_NOTIFY_SILENT_TMUX_SESSIONS_PARAMS,
|
|
20
20
|
} from './notifySilentTmuxSessions';
|
|
21
21
|
import { ownerReplyMarkerDirectoryResolve } from './ownerReplyMarkerDirectoryResolve';
|
|
22
|
-
import { resolveUnansweredOwnerCallsByTmuxSessionName } from './resolveUnansweredOwnerCalls';
|
|
23
|
-
import { LocalProcessLiveSessionProcessSnapshotProvider } from '../../repositories/LocalProcessLiveSessionProcessSnapshotProvider';
|
|
24
|
-
import { ProcFsProcessEnvironReader } from '../../repositories/ProcFsProcessEnvironReader';
|
|
25
|
-
import { FileSystemInteractiveLiveSessionTranscriptResolver } from '../../repositories/FileSystemInteractiveLiveSessionTranscriptResolver';
|
|
26
22
|
import { TranscriptOwnerCallStatusProvider } from '../../repositories/TranscriptOwnerCallStatusProvider';
|
|
27
23
|
import { NoUnansweredOwnerCallStatusProvider } from '../../repositories/NoUnansweredOwnerCallStatusProvider';
|
|
28
24
|
import { OwnerCallStatusProvider } from '../../../domain/usecases/adapter-interfaces/OwnerCallStatusProvider';
|
|
29
|
-
import { UnansweredOwnerCall } from '../../../domain/entities/UnansweredOwnerCall';
|
|
30
25
|
import {
|
|
31
26
|
resetDegeneratedTmuxSessions,
|
|
32
27
|
DEFAULT_RESET_DEGENERATED_TMUX_SESSIONS_PARAMS,
|
|
@@ -661,33 +656,6 @@ export class HandleScheduledEventUseCaseHandler {
|
|
|
661
656
|
transcriptOwnerCallStatusProvider ??
|
|
662
657
|
new NoUnansweredOwnerCallStatusProvider();
|
|
663
658
|
|
|
664
|
-
let unansweredCallsByTmuxSessionName = new Map<
|
|
665
|
-
string,
|
|
666
|
-
UnansweredOwnerCall[]
|
|
667
|
-
>();
|
|
668
|
-
try {
|
|
669
|
-
if (transcriptOwnerCallStatusProvider !== null) {
|
|
670
|
-
unansweredCallsByTmuxSessionName =
|
|
671
|
-
await resolveUnansweredOwnerCallsByTmuxSessionName({
|
|
672
|
-
liveSessionProcessSnapshotProvider:
|
|
673
|
-
new LocalProcessLiveSessionProcessSnapshotProvider(
|
|
674
|
-
nodeLocalCommandRunner,
|
|
675
|
-
new ProcFsProcessEnvironReader(),
|
|
676
|
-
),
|
|
677
|
-
interactiveLiveSessionTranscriptResolver:
|
|
678
|
-
new FileSystemInteractiveLiveSessionTranscriptResolver(),
|
|
679
|
-
unansweredOwnerCallListProvider:
|
|
680
|
-
transcriptOwnerCallStatusProvider,
|
|
681
|
-
});
|
|
682
|
-
}
|
|
683
|
-
} catch (error) {
|
|
684
|
-
console.error(
|
|
685
|
-
`Failed to resolve unanswered owner calls: ${
|
|
686
|
-
error instanceof Error ? error.message : String(error)
|
|
687
|
-
}`,
|
|
688
|
-
);
|
|
689
|
-
}
|
|
690
|
-
|
|
691
659
|
try {
|
|
692
660
|
writeInTmuxByHumanData({
|
|
693
661
|
inTmuxDataOutputDir: mergedInput.inTmuxDataOutputDir ?? null,
|
|
@@ -704,7 +672,6 @@ export class HandleScheduledEventUseCaseHandler {
|
|
|
704
672
|
newIssueRepo: mergedInput.newIssueRepo ?? undefined,
|
|
705
673
|
project: result.project,
|
|
706
674
|
issues: result.issues,
|
|
707
|
-
unansweredCallsByTmuxSessionName,
|
|
708
675
|
now: inTmuxNow,
|
|
709
676
|
});
|
|
710
677
|
} catch (error) {
|
|
@@ -4,7 +4,6 @@ import path from 'path';
|
|
|
4
4
|
import { Issue } from '../../../domain/entities/Issue';
|
|
5
5
|
import { FieldOption, Project } from '../../../domain/entities/Project';
|
|
6
6
|
import { writeInTmuxByHumanData } from './inTmuxByHumanDataWriter';
|
|
7
|
-
import { toTmuxSessionName } from '../../../domain/usecases/intmux/InTmuxByHumanSessionReconcileUseCase';
|
|
8
7
|
|
|
9
8
|
const ASSIGNEE = 'owner-login';
|
|
10
9
|
const CONSOLE_BASE_URL = 'https://console.example.test';
|
|
@@ -99,122 +98,22 @@ describe('writeInTmuxByHumanData', () => {
|
|
|
99
98
|
|
|
100
99
|
const file = (name: string): string => path.join(outDir, name);
|
|
101
100
|
|
|
102
|
-
it('writes the
|
|
101
|
+
it('writes the four per-project files and the four index files without producing any v5 files', () => {
|
|
103
102
|
writeInTmuxByHumanData(baseParams(outDir));
|
|
104
103
|
for (const name of [
|
|
105
104
|
'demo.json',
|
|
106
105
|
'demo.v2.json',
|
|
107
106
|
'demo.v3.json',
|
|
108
107
|
'demo.v4.json',
|
|
109
|
-
'demo.v5.json',
|
|
110
108
|
'index.json',
|
|
111
109
|
'index.v2.json',
|
|
112
110
|
'index.v3.json',
|
|
113
111
|
'index.v4.json',
|
|
114
|
-
'index.v5.json',
|
|
115
112
|
]) {
|
|
116
113
|
expect(fs.existsSync(file(name))).toBe(true);
|
|
117
114
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
it('writes the v5 shape carrying the unanswered calls of the session', () => {
|
|
121
|
-
writeInTmuxByHumanData({
|
|
122
|
-
...baseParams(outDir),
|
|
123
|
-
unansweredCallsByTmuxSessionName: new Map([
|
|
124
|
-
[
|
|
125
|
-
toTmuxSessionName('https://github.com/demo/repo/issues/1'),
|
|
126
|
-
[
|
|
127
|
-
{
|
|
128
|
-
calledAt: '2026-08-13T10:14:00.000Z',
|
|
129
|
-
body: 'Please decide whether to merge the release branch',
|
|
130
|
-
},
|
|
131
|
-
],
|
|
132
|
-
],
|
|
133
|
-
]),
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
expect(readJson(file('demo.v5.json'))).toEqual({
|
|
137
|
-
version: 5,
|
|
138
|
-
overviewUrl: 'https://github.com/orgs/demo/projects/1',
|
|
139
|
-
tdpmConsoleUrl: `${CONSOLE_BASE_URL}/projects/demo?k=${CONSOLE_TOKEN}`,
|
|
140
|
-
newIssueUrl: `https://github.com/demo-org/demo-repo/issues/new?assignees=${ASSIGNEE}`,
|
|
141
|
-
groups: [
|
|
142
|
-
{
|
|
143
|
-
story: 'Story Alpha',
|
|
144
|
-
sessions: [
|
|
145
|
-
{
|
|
146
|
-
name: 'https://github.com/demo/repo/issues/1',
|
|
147
|
-
description: 'Issue 1',
|
|
148
|
-
unansweredCalls: [
|
|
149
|
-
{
|
|
150
|
-
calledAt: '2026-08-13T10:14:00.000Z',
|
|
151
|
-
body: 'Please decide whether to merge the release branch',
|
|
152
|
-
},
|
|
153
|
-
],
|
|
154
|
-
},
|
|
155
|
-
],
|
|
156
|
-
},
|
|
157
|
-
],
|
|
158
|
-
});
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
it('writes an empty unansweredCalls array when no call is supplied', () => {
|
|
162
|
-
writeInTmuxByHumanData(baseParams(outDir));
|
|
163
|
-
expect(readJson(file('demo.v5.json'))).toMatchObject({
|
|
164
|
-
groups: [
|
|
165
|
-
{
|
|
166
|
-
sessions: [
|
|
167
|
-
{
|
|
168
|
-
name: 'https://github.com/demo/repo/issues/1',
|
|
169
|
-
unansweredCalls: [],
|
|
170
|
-
},
|
|
171
|
-
],
|
|
172
|
-
},
|
|
173
|
-
],
|
|
174
|
-
});
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
it('points the v5 index entry at the v5 document of each project', () => {
|
|
178
|
-
writeInTmuxByHumanData(baseParams(outDir));
|
|
179
|
-
expect(readJson(file('index.v5.json'))).toEqual({
|
|
180
|
-
version: 5,
|
|
181
|
-
projects: [
|
|
182
|
-
{
|
|
183
|
-
name: 'demo',
|
|
184
|
-
path: `/${path.basename(outDir)}/demo.v5.json?k=${CONSOLE_TOKEN}`,
|
|
185
|
-
},
|
|
186
|
-
],
|
|
187
|
-
});
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
it('keeps the v4 document free of unanswered call data when calls are supplied', () => {
|
|
191
|
-
writeInTmuxByHumanData({
|
|
192
|
-
...baseParams(outDir),
|
|
193
|
-
unansweredCallsByTmuxSessionName: new Map([
|
|
194
|
-
[
|
|
195
|
-
toTmuxSessionName('https://github.com/demo/repo/issues/1'),
|
|
196
|
-
[{ calledAt: '2026-08-13T10:14:00.000Z', body: 'first call body' }],
|
|
197
|
-
],
|
|
198
|
-
]),
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
expect(readJson(file('demo.v4.json'))).toEqual({
|
|
202
|
-
version: 4,
|
|
203
|
-
overviewUrl: 'https://github.com/orgs/demo/projects/1',
|
|
204
|
-
tdpmConsoleUrl: `${CONSOLE_BASE_URL}/projects/demo?k=${CONSOLE_TOKEN}`,
|
|
205
|
-
newIssueUrl: `https://github.com/demo-org/demo-repo/issues/new?assignees=${ASSIGNEE}`,
|
|
206
|
-
groups: [
|
|
207
|
-
{
|
|
208
|
-
story: 'Story Alpha',
|
|
209
|
-
sessions: [
|
|
210
|
-
{
|
|
211
|
-
name: 'https://github.com/demo/repo/issues/1',
|
|
212
|
-
description: 'Issue 1',
|
|
213
|
-
},
|
|
214
|
-
],
|
|
215
|
-
},
|
|
216
|
-
],
|
|
217
|
-
});
|
|
115
|
+
expect(fs.existsSync(file('demo.v5.json'))).toBe(false);
|
|
116
|
+
expect(fs.existsSync(file('index.v5.json'))).toBe(false);
|
|
218
117
|
});
|
|
219
118
|
|
|
220
119
|
it('writes pretty-printed JSON ending with a trailing newline', () => {
|
|
@@ -2,7 +2,6 @@ import fs from 'fs';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import type { Issue } from '../../../domain/entities/Issue';
|
|
4
4
|
import type { Project } from '../../../domain/entities/Project';
|
|
5
|
-
import type { UnansweredOwnerCall } from '../../../domain/entities/UnansweredOwnerCall';
|
|
6
5
|
import {
|
|
7
6
|
GenerateInTmuxByHumanDataUseCase,
|
|
8
7
|
InTmuxByHumanData,
|
|
@@ -20,7 +19,6 @@ export type InTmuxByHumanDataWriterParams = {
|
|
|
20
19
|
newIssueRepo?: string | null | undefined;
|
|
21
20
|
project: Project;
|
|
22
21
|
issues: Issue[];
|
|
23
|
-
unansweredCallsByTmuxSessionName?: Map<string, UnansweredOwnerCall[]>;
|
|
24
22
|
now: Date;
|
|
25
23
|
};
|
|
26
24
|
|
|
@@ -47,7 +45,6 @@ export const writeInTmuxByHumanData = (
|
|
|
47
45
|
newIssueRepo,
|
|
48
46
|
project,
|
|
49
47
|
issues,
|
|
50
|
-
unansweredCallsByTmuxSessionName,
|
|
51
48
|
now,
|
|
52
49
|
} = params;
|
|
53
50
|
if (!inTmuxDataOutputDir || !pjcode || !assigneeLogin) {
|
|
@@ -64,9 +61,6 @@ export const writeInTmuxByHumanData = (
|
|
|
64
61
|
newIssueRepo: newIssueRepo ?? undefined,
|
|
65
62
|
consoleBaseUrl: inTmuxConsoleBaseUrl ?? null,
|
|
66
63
|
consoleToken: inTmuxConsoleToken ?? null,
|
|
67
|
-
unansweredCallsByTmuxSessionName:
|
|
68
|
-
unansweredCallsByTmuxSessionName ??
|
|
69
|
-
new Map<string, UnansweredOwnerCall[]>(),
|
|
70
64
|
now,
|
|
71
65
|
});
|
|
72
66
|
|
|
@@ -84,12 +78,6 @@ export const writeInTmuxByHumanData = (
|
|
|
84
78
|
data.v4,
|
|
85
79
|
);
|
|
86
80
|
}
|
|
87
|
-
if (data.v5) {
|
|
88
|
-
writeJsonAtomic(
|
|
89
|
-
path.join(inTmuxDataOutputDir, `${pjcode}.v5.json`),
|
|
90
|
-
data.v5,
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
81
|
|
|
94
82
|
if (!inTmuxProjectOrder || inTmuxProjectOrder.length === 0) {
|
|
95
83
|
return;
|
|
@@ -117,12 +105,5 @@ export const writeInTmuxByHumanData = (
|
|
|
117
105
|
path: `/${outputDirBasename}/${name}.v4.json?k=${inTmuxConsoleToken}`,
|
|
118
106
|
})),
|
|
119
107
|
});
|
|
120
|
-
writeJsonAtomic(path.join(inTmuxDataOutputDir, 'index.v5.json'), {
|
|
121
|
-
version: 5,
|
|
122
|
-
projects: presentProjects.map((name) => ({
|
|
123
|
-
name,
|
|
124
|
-
path: `/${outputDirBasename}/${name}.v5.json?k=${inTmuxConsoleToken}`,
|
|
125
|
-
})),
|
|
126
|
-
});
|
|
127
108
|
}
|
|
128
109
|
};
|
|
@@ -39,33 +39,31 @@ describe('TranscriptOwnerCallStatusProvider single transcript scan', () => {
|
|
|
39
39
|
const epochSecondsOf = (timestamp: string): number =>
|
|
40
40
|
Math.floor(Date.parse(timestamp) / 1000);
|
|
41
41
|
|
|
42
|
-
it('serves
|
|
42
|
+
it('serves a repeated reminder path request from one read of the transcript', async () => {
|
|
43
43
|
const transcriptPath = writeTranscript('session.jsonl', [
|
|
44
44
|
assistantWithMarker('2026-08-13T10:00:00.000Z'),
|
|
45
45
|
]);
|
|
46
46
|
const transcriptPathBySessionName = new Map([['session', transcriptPath]]);
|
|
47
47
|
const provider = new TranscriptOwnerCallStatusProvider('<<OWNER_CALL>>');
|
|
48
48
|
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
const firstEpochSeconds =
|
|
50
|
+
await provider.listUnansweredOwnerCallEpochSecondsBySessionName(
|
|
51
|
+
transcriptPathBySessionName,
|
|
52
|
+
);
|
|
52
53
|
|
|
53
54
|
writeTranscript('session.jsonl', [
|
|
54
55
|
assistantWithMarker('2026-08-13T11:00:00.000Z'),
|
|
55
56
|
]);
|
|
56
57
|
|
|
57
|
-
const
|
|
58
|
+
const secondEpochSeconds =
|
|
58
59
|
await provider.listUnansweredOwnerCallEpochSecondsBySessionName(
|
|
59
60
|
transcriptPathBySessionName,
|
|
60
61
|
);
|
|
61
62
|
|
|
62
|
-
expect(
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
},
|
|
67
|
-
]);
|
|
68
|
-
expect(epochSeconds.get('session')).toBe(
|
|
63
|
+
expect(firstEpochSeconds.get('session')).toBe(
|
|
64
|
+
epochSecondsOf('2026-08-13T10:00:00.000Z'),
|
|
65
|
+
);
|
|
66
|
+
expect(secondEpochSeconds.get('session')).toBe(
|
|
69
67
|
epochSecondsOf('2026-08-13T10:00:00.000Z'),
|
|
70
68
|
);
|
|
71
69
|
});
|
|
@@ -820,184 +820,6 @@ describe('TranscriptOwnerCallStatusProvider', () => {
|
|
|
820
820
|
Math.floor(Date.parse('2026-06-27T10:20:00.000Z') / 1000),
|
|
821
821
|
);
|
|
822
822
|
});
|
|
823
|
-
|
|
824
|
-
describe('listUnansweredOwnerCallsBySessionName', () => {
|
|
825
|
-
const assistantCallSaying = (timestamp: string, text: string): object => ({
|
|
826
|
-
type: 'assistant',
|
|
827
|
-
timestamp,
|
|
828
|
-
message: {
|
|
829
|
-
role: 'assistant',
|
|
830
|
-
stop_reason: 'end_turn',
|
|
831
|
-
content: [{ type: 'text', text: `${text} <<OWNER_CALL>>` }],
|
|
832
|
-
},
|
|
833
|
-
});
|
|
834
|
-
|
|
835
|
-
it('returns the time and the body of an unanswered owner call', async () => {
|
|
836
|
-
const transcriptPath = writeTranscript('workbench.jsonl', [
|
|
837
|
-
ownerReply('2026-06-27T09:00:00.000Z'),
|
|
838
|
-
assistantCallSaying(
|
|
839
|
-
'2026-06-27T10:00:00.000Z',
|
|
840
|
-
'Please decide whether to merge the release branch',
|
|
841
|
-
),
|
|
842
|
-
]);
|
|
843
|
-
const provider = new TranscriptOwnerCallStatusProvider('<<OWNER_CALL>>');
|
|
844
|
-
|
|
845
|
-
const result = await provider.listUnansweredOwnerCallsBySessionName(
|
|
846
|
-
new Map([[sessionName, transcriptPath]]),
|
|
847
|
-
);
|
|
848
|
-
|
|
849
|
-
expect(result.get(sessionName)).toEqual([
|
|
850
|
-
{
|
|
851
|
-
calledAt: '2026-06-27T10:00:00.000Z',
|
|
852
|
-
body: 'Please decide whether to merge the release branch <<OWNER_CALL>>',
|
|
853
|
-
},
|
|
854
|
-
]);
|
|
855
|
-
});
|
|
856
|
-
|
|
857
|
-
it('returns every owner call raised since the last owner reply, oldest first', async () => {
|
|
858
|
-
const transcriptPath = writeTranscript('workbench.jsonl', [
|
|
859
|
-
assistantCallSaying('2026-06-27T08:00:00.000Z', 'answered call'),
|
|
860
|
-
ownerReply('2026-06-27T09:00:00.000Z'),
|
|
861
|
-
assistantCallSaying('2026-06-27T10:00:00.000Z', 'first waiting call'),
|
|
862
|
-
assistantCallSaying('2026-06-27T11:30:00.000Z', 'second waiting call'),
|
|
863
|
-
]);
|
|
864
|
-
const provider = new TranscriptOwnerCallStatusProvider('<<OWNER_CALL>>');
|
|
865
|
-
|
|
866
|
-
const result = await provider.listUnansweredOwnerCallsBySessionName(
|
|
867
|
-
new Map([[sessionName, transcriptPath]]),
|
|
868
|
-
);
|
|
869
|
-
|
|
870
|
-
expect(
|
|
871
|
-
result.get(sessionName)?.map((ownerCall) => ownerCall.calledAt),
|
|
872
|
-
).toEqual(['2026-06-27T10:00:00.000Z', '2026-06-27T11:30:00.000Z']);
|
|
873
|
-
expect(result.get(sessionName)?.[0].body).toContain('first waiting call');
|
|
874
|
-
expect(result.get(sessionName)?.[1].body).toContain(
|
|
875
|
-
'second waiting call',
|
|
876
|
-
);
|
|
877
|
-
});
|
|
878
|
-
|
|
879
|
-
it('gives each call of one session a distinct time so the calls stay separable', async () => {
|
|
880
|
-
const transcriptPath = writeTranscript('workbench.jsonl', [
|
|
881
|
-
assistantCallSaying('2026-06-27T10:00:00.000Z', 'same text'),
|
|
882
|
-
assistantCallSaying('2026-06-27T11:30:00.000Z', 'same text'),
|
|
883
|
-
]);
|
|
884
|
-
const provider = new TranscriptOwnerCallStatusProvider('<<OWNER_CALL>>');
|
|
885
|
-
|
|
886
|
-
const result = await provider.listUnansweredOwnerCallsBySessionName(
|
|
887
|
-
new Map([[sessionName, transcriptPath]]),
|
|
888
|
-
);
|
|
889
|
-
|
|
890
|
-
const calls = result.get(sessionName) ?? [];
|
|
891
|
-
expect(calls).toHaveLength(2);
|
|
892
|
-
expect(calls[0].body).toBe(calls[1].body);
|
|
893
|
-
expect(calls[0].calledAt).not.toBe(calls[1].calledAt);
|
|
894
|
-
});
|
|
895
|
-
|
|
896
|
-
it('keeps a delivered call listed while the newest call is a candidate the status line has not rendered yet', async () => {
|
|
897
|
-
const transcriptPath = writeTranscript('workbench.jsonl', [
|
|
898
|
-
ownerReply('2026-06-27T09:00:00.000Z'),
|
|
899
|
-
assistantCallSaying('2026-06-27T10:00:00.000Z', 'delivered call'),
|
|
900
|
-
assistantWithCandidateMarker('2026-06-27T11:00:00.000Z'),
|
|
901
|
-
]);
|
|
902
|
-
const markerDirectory = path.join(rootDirectory, 'markers');
|
|
903
|
-
fs.mkdirSync(markerDirectory, { recursive: true });
|
|
904
|
-
const provider = new TranscriptOwnerCallStatusProvider(
|
|
905
|
-
'<<OWNER_CALL>>',
|
|
906
|
-
markerDirectory,
|
|
907
|
-
);
|
|
908
|
-
|
|
909
|
-
const result = await provider.listUnansweredOwnerCallsBySessionName(
|
|
910
|
-
new Map([[sessionName, transcriptPath]]),
|
|
911
|
-
);
|
|
912
|
-
|
|
913
|
-
expect(
|
|
914
|
-
result.get(sessionName)?.map((ownerCall) => ownerCall.calledAt),
|
|
915
|
-
).toEqual(['2026-06-27T10:00:00.000Z']);
|
|
916
|
-
});
|
|
917
|
-
|
|
918
|
-
it('omits a call the format gate held and never approved from the listed calls', async () => {
|
|
919
|
-
const transcriptPath = writeTranscript('workbench.jsonl', [
|
|
920
|
-
ownerReply('2026-06-27T09:00:00.000Z'),
|
|
921
|
-
assistantCallSaying('2026-06-27T10:00:00.000Z', 'held by the gate'),
|
|
922
|
-
assistantCallSaying('2026-06-27T11:00:00.000Z', 'delivered call'),
|
|
923
|
-
]);
|
|
924
|
-
const markerDirectory = path.join(rootDirectory, 'markers');
|
|
925
|
-
writeCallGateMarker(
|
|
926
|
-
markerDirectory,
|
|
927
|
-
'workbench.jsonl',
|
|
928
|
-
'.suppress_ts',
|
|
929
|
-
'2026-06-27T10:00:00.000Z',
|
|
930
|
-
);
|
|
931
|
-
const provider = new TranscriptOwnerCallStatusProvider(
|
|
932
|
-
'<<OWNER_CALL>>',
|
|
933
|
-
markerDirectory,
|
|
934
|
-
);
|
|
935
|
-
|
|
936
|
-
const result = await provider.listUnansweredOwnerCallsBySessionName(
|
|
937
|
-
new Map([[sessionName, transcriptPath]]),
|
|
938
|
-
);
|
|
939
|
-
|
|
940
|
-
expect(
|
|
941
|
-
result.get(sessionName)?.map((ownerCall) => ownerCall.calledAt),
|
|
942
|
-
).toEqual(['2026-06-27T11:00:00.000Z']);
|
|
943
|
-
});
|
|
944
|
-
|
|
945
|
-
it('lists the calls oldest first even when the transcript records a later call before an earlier one', async () => {
|
|
946
|
-
const transcriptPath = writeTranscript('workbench.jsonl', [
|
|
947
|
-
assistantCallSaying('2026-06-27T11:00:00.000Z', 'later call'),
|
|
948
|
-
assistantCallSaying('2026-06-27T10:00:00.000Z', 'earlier call'),
|
|
949
|
-
]);
|
|
950
|
-
const provider = new TranscriptOwnerCallStatusProvider('<<OWNER_CALL>>');
|
|
951
|
-
|
|
952
|
-
const result = await provider.listUnansweredOwnerCallsBySessionName(
|
|
953
|
-
new Map([[sessionName, transcriptPath]]),
|
|
954
|
-
);
|
|
955
|
-
|
|
956
|
-
expect(
|
|
957
|
-
result.get(sessionName)?.map((ownerCall) => ownerCall.calledAt),
|
|
958
|
-
).toEqual(['2026-06-27T10:00:00.000Z', '2026-06-27T11:00:00.000Z']);
|
|
959
|
-
});
|
|
960
|
-
|
|
961
|
-
it('returns no entry for a session whose last owner call the owner already answered', async () => {
|
|
962
|
-
const transcriptPath = writeTranscript('workbench.jsonl', [
|
|
963
|
-
assistantCallSaying('2026-06-27T10:00:00.000Z', 'please decide'),
|
|
964
|
-
ownerReply('2026-06-27T10:30:00.000Z'),
|
|
965
|
-
]);
|
|
966
|
-
const provider = new TranscriptOwnerCallStatusProvider('<<OWNER_CALL>>');
|
|
967
|
-
|
|
968
|
-
const result = await provider.listUnansweredOwnerCallsBySessionName(
|
|
969
|
-
new Map([[sessionName, transcriptPath]]),
|
|
970
|
-
);
|
|
971
|
-
|
|
972
|
-
expect(result.has(sessionName)).toBe(false);
|
|
973
|
-
});
|
|
974
|
-
|
|
975
|
-
it('returns no entry for a session that raised no owner call', async () => {
|
|
976
|
-
const transcriptPath = writeTranscript('workbench.jsonl', [
|
|
977
|
-
assistantPlain('2026-06-27T10:00:00.000Z'),
|
|
978
|
-
]);
|
|
979
|
-
const provider = new TranscriptOwnerCallStatusProvider('<<OWNER_CALL>>');
|
|
980
|
-
|
|
981
|
-
const result = await provider.listUnansweredOwnerCallsBySessionName(
|
|
982
|
-
new Map([[sessionName, transcriptPath]]),
|
|
983
|
-
);
|
|
984
|
-
|
|
985
|
-
expect(result.has(sessionName)).toBe(false);
|
|
986
|
-
});
|
|
987
|
-
|
|
988
|
-
it('returns nothing when no owner call marker is configured', async () => {
|
|
989
|
-
const transcriptPath = writeTranscript('workbench.jsonl', [
|
|
990
|
-
assistantCallSaying('2026-06-27T10:00:00.000Z', 'please decide'),
|
|
991
|
-
]);
|
|
992
|
-
const provider = new TranscriptOwnerCallStatusProvider(null);
|
|
993
|
-
|
|
994
|
-
const result = await provider.listUnansweredOwnerCallsBySessionName(
|
|
995
|
-
new Map([[sessionName, transcriptPath]]),
|
|
996
|
-
);
|
|
997
|
-
|
|
998
|
-
expect(result.size).toBe(0);
|
|
999
|
-
});
|
|
1000
|
-
});
|
|
1001
823
|
});
|
|
1002
824
|
|
|
1003
825
|
describe('ownerCallMarkerFamilyResolve', () => {
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import { OwnerCallStatusProvider } from '../../domain/usecases/adapter-interfaces/OwnerCallStatusProvider';
|
|
4
|
-
import { UnansweredOwnerCall } from '../../domain/entities/UnansweredOwnerCall';
|
|
5
4
|
import { SILENT_SESSION_REMINDER_SENTINEL } from '../../domain/usecases/silentSessionReminderSentinel';
|
|
6
5
|
|
|
7
6
|
type TranscriptOwnerCall = {
|
|
8
7
|
epochMs: number;
|
|
9
|
-
body: string;
|
|
10
8
|
candidateOnly: boolean;
|
|
11
9
|
};
|
|
12
10
|
|
|
@@ -196,44 +194,6 @@ export class TranscriptOwnerCallStatusProvider implements OwnerCallStatusProvide
|
|
|
196
194
|
return unansweredOwnerCallEpochSecondsBySessionName;
|
|
197
195
|
};
|
|
198
196
|
|
|
199
|
-
listUnansweredOwnerCallsBySessionName = async (
|
|
200
|
-
transcriptPathBySessionName: Map<string, string>,
|
|
201
|
-
): Promise<Map<string, UnansweredOwnerCall[]>> => {
|
|
202
|
-
const unansweredOwnerCallsBySessionName = new Map<
|
|
203
|
-
string,
|
|
204
|
-
UnansweredOwnerCall[]
|
|
205
|
-
>();
|
|
206
|
-
if (this.ownerCallMarkerFamily.length === 0) {
|
|
207
|
-
return unansweredOwnerCallsBySessionName;
|
|
208
|
-
}
|
|
209
|
-
for (const [sessionName, transcriptPath] of transcriptPathBySessionName) {
|
|
210
|
-
const transcript = this.scanTranscript(transcriptPath);
|
|
211
|
-
if (transcript === null) {
|
|
212
|
-
continue;
|
|
213
|
-
}
|
|
214
|
-
const answeredBeforeEpochMs = this.resolveReplyEpochMs(
|
|
215
|
-
transcriptPath,
|
|
216
|
-
transcript.lastOwnerReplyEpochMs,
|
|
217
|
-
);
|
|
218
|
-
const unansweredCalls = transcript.ownerCalls
|
|
219
|
-
.filter(
|
|
220
|
-
(ownerCall) =>
|
|
221
|
-
(answeredBeforeEpochMs === null ||
|
|
222
|
-
ownerCall.epochMs > answeredBeforeEpochMs) &&
|
|
223
|
-
this.isCallDeliveredToOwner(transcriptPath, ownerCall),
|
|
224
|
-
)
|
|
225
|
-
.sort((oneCall, otherCall) => oneCall.epochMs - otherCall.epochMs)
|
|
226
|
-
.map((ownerCall) => ({
|
|
227
|
-
calledAt: new Date(ownerCall.epochMs).toISOString(),
|
|
228
|
-
body: ownerCall.body,
|
|
229
|
-
}));
|
|
230
|
-
if (unansweredCalls.length > 0) {
|
|
231
|
-
unansweredOwnerCallsBySessionName.set(sessionName, unansweredCalls);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
return unansweredOwnerCallsBySessionName;
|
|
235
|
-
};
|
|
236
|
-
|
|
237
197
|
private scanTranscript = (
|
|
238
198
|
transcriptPath: string,
|
|
239
199
|
): TranscriptOwnerCallScan | null => {
|
|
@@ -287,7 +247,6 @@ export class TranscriptOwnerCallStatusProvider implements OwnerCallStatusProvide
|
|
|
287
247
|
if (matchedMarkers.length > 0) {
|
|
288
248
|
ownerCalls.push({
|
|
289
249
|
epochMs,
|
|
290
|
-
body: assistantText,
|
|
291
250
|
candidateOnly: matchedMarkers.every(isCandidateOwnerCallMarker),
|
|
292
251
|
});
|
|
293
252
|
}
|
|
@@ -365,26 +324,6 @@ export class TranscriptOwnerCallStatusProvider implements OwnerCallStatusProvide
|
|
|
365
324
|
): number | null =>
|
|
366
325
|
this.readMarkerEpochMs(transcriptPath, OWNER_REPLY_MARKER_FILE_EXTENSION);
|
|
367
326
|
|
|
368
|
-
// The format gate the owner's sessions run under can hold an owner call instead of delivering
|
|
369
|
-
// it, and it records that decision beside the reply marker: the held call's timestamp in the
|
|
370
|
-
// suppression marker, and the timestamp of any call it later approved in the approval marker. A
|
|
371
|
-
// held call the approval marker does not name never reached the owner, so treating it as a wait
|
|
372
|
-
// on the owner would silence the session's stall reminder for good — the session would keep its
|
|
373
|
-
// task and never be woken again. Such a call is therefore not an outstanding owner call here. A
|
|
374
|
-
// delivered call, and a newer call the suppression marker does not name, are untouched.
|
|
375
|
-
private isCallDeliveredToOwner = (
|
|
376
|
-
transcriptPath: string,
|
|
377
|
-
ownerCall: TranscriptOwnerCall,
|
|
378
|
-
): boolean => {
|
|
379
|
-
if (this.isCallSuppressedUndelivered(transcriptPath, ownerCall.epochMs)) {
|
|
380
|
-
return false;
|
|
381
|
-
}
|
|
382
|
-
return (
|
|
383
|
-
!ownerCall.candidateOnly ||
|
|
384
|
-
this.isCandidateCallDelivered(transcriptPath, ownerCall.epochMs)
|
|
385
|
-
);
|
|
386
|
-
};
|
|
387
|
-
|
|
388
327
|
private isCallSuppressedUndelivered = (
|
|
389
328
|
transcriptPath: string,
|
|
390
329
|
ownerCallEpochMs: number,
|