@luckydraw/cumulus 1.0.43 → 1.0.45
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/CHANGELOG.md +21 -0
- package/README.md +1 -0
- package/dist/gateway/adapters/webchat.d.ts.map +1 -1
- package/dist/gateway/adapters/webchat.js +22 -1
- package/dist/gateway/adapters/webchat.js.map +1 -1
- package/dist/gateway/config.d.ts +8 -6
- package/dist/gateway/config.d.ts.map +1 -1
- package/dist/gateway/config.js +2 -2
- package/dist/gateway/config.js.map +1 -1
- package/dist/gateway/jobs.d.ts +6 -0
- package/dist/gateway/jobs.d.ts.map +1 -1
- package/dist/gateway/jobs.js +12 -0
- package/dist/gateway/jobs.js.map +1 -1
- package/dist/gateway/one-shot-model.d.ts +23 -8
- package/dist/gateway/one-shot-model.d.ts.map +1 -1
- package/dist/gateway/one-shot-model.js +104 -26
- package/dist/gateway/one-shot-model.js.map +1 -1
- package/dist/gateway/server.d.ts +2 -0
- package/dist/gateway/server.d.ts.map +1 -1
- package/dist/gateway/server.js +108 -0
- package/dist/gateway/server.js.map +1 -1
- package/dist/gateway/stall-detection.d.ts +9 -3
- package/dist/gateway/stall-detection.d.ts.map +1 -1
- package/dist/gateway/stall-detection.js +9 -3
- package/dist/gateway/stall-detection.js.map +1 -1
- package/dist/gateway/static/widget.js +233 -25
- package/dist/gateway/thread-rename.d.ts +108 -0
- package/dist/gateway/thread-rename.d.ts.map +1 -0
- package/dist/gateway/thread-rename.js +208 -0
- package/dist/gateway/thread-rename.js.map +1 -0
- package/dist/gateway/worker.d.ts +20 -3
- package/dist/gateway/worker.d.ts.map +1 -1
- package/dist/gateway/worker.js +0 -0
- package/dist/gateway/worker.js.map +1 -1
- package/dist/lib/content-store.d.ts +11 -0
- package/dist/lib/content-store.d.ts.map +1 -1
- package/dist/lib/content-store.js +34 -0
- package/dist/lib/content-store.js.map +1 -1
- package/dist/lib/worktree.d.ts +18 -0
- package/dist/lib/worktree.d.ts.map +1 -1
- package/dist/lib/worktree.js +32 -0
- package/dist/lib/worktree.js.map +1 -1
- package/package.json +1 -1
|
@@ -139,13 +139,12 @@
|
|
|
139
139
|
// and re-adding it does not quietly clear the voice setting. The gateway's
|
|
140
140
|
// resolveVoiceModel treats an unmatched id as "no override" anyway.
|
|
141
141
|
voiceModel: typeof config.voiceModel === 'string' ? config.voiceModel : '',
|
|
142
|
-
// Task 164. Same verbatim rule as voiceModel: an id naming nothing in
|
|
143
|
-
//
|
|
144
|
-
//
|
|
142
|
+
// Task 164. Same verbatim rule as voiceModel: an id naming nothing in the catalog is
|
|
143
|
+
// preserved on round-trip; the gateway reads an unmatched id as NOT CONFIGURED
|
|
144
|
+
// (task 184), which is what the dropdown shows for it too.
|
|
145
145
|
stallClassifierModel:
|
|
146
146
|
typeof config.stallClassifierModel === 'string' ? config.stallClassifierModel : '',
|
|
147
|
-
// Task 179. Same verbatim rule again
|
|
148
|
-
// back to "haiku" for an unmatched id.
|
|
147
|
+
// Task 179. Same verbatim rule again, same not-configured reading.
|
|
149
148
|
workerModel: typeof config.workerModel === 'string' ? config.workerModel : '',
|
|
150
149
|
};
|
|
151
150
|
}
|
|
@@ -208,11 +207,11 @@
|
|
|
208
207
|
// Task 152: '' means "same as thread" and must reach the server as an explicit
|
|
209
208
|
// null (clear), not as absent — absent would leave the stored value untouched.
|
|
210
209
|
voiceModel: state.voiceModel ? state.voiceModel : null,
|
|
211
|
-
// Task 164: '' means "
|
|
212
|
-
// not as absent — absent would leave a previously-set classifier model untouched.
|
|
210
|
+
// Task 164: '' means "not configured" and must reach the server as an explicit null
|
|
211
|
+
// (clear), not as absent — absent would leave a previously-set classifier model untouched.
|
|
213
212
|
stallClassifierModel: state.stallClassifierModel ? state.stallClassifierModel : null,
|
|
214
|
-
// Task 179: same explicit-null rule — '' means "
|
|
215
|
-
// leave a previously-set worker model untouched.
|
|
213
|
+
// Task 179: same explicit-null rule — '' means "not configured", and absent
|
|
214
|
+
// would leave a previously-set worker model untouched.
|
|
216
215
|
workerModel: state.workerModel ? state.workerModel : null,
|
|
217
216
|
// Registry travels without credentials — addCredentialPatch fills those
|
|
218
217
|
// in, so an untouched key round-trips as absent and the server preserves it.
|
|
@@ -5653,6 +5652,16 @@
|
|
|
5653
5652
|
menu.appendChild(coItem);
|
|
5654
5653
|
}
|
|
5655
5654
|
|
|
5655
|
+
var renameItem = document.createElement('button');
|
|
5656
|
+
renameItem.className = 'cumulus-context-menu-item';
|
|
5657
|
+
renameItem.textContent = 'Rename thread…';
|
|
5658
|
+
renameItem.setAttribute('data-testid', 'webchat-thread-rename');
|
|
5659
|
+
renameItem.addEventListener('click', function () {
|
|
5660
|
+
dismissContextMenu();
|
|
5661
|
+
showThreadRenameDialog(threadName, master);
|
|
5662
|
+
});
|
|
5663
|
+
menu.appendChild(renameItem);
|
|
5664
|
+
|
|
5656
5665
|
var deleteItem = document.createElement('button');
|
|
5657
5666
|
deleteItem.className = 'cumulus-context-menu-item danger';
|
|
5658
5667
|
deleteItem.textContent = 'Delete thread';
|
|
@@ -5836,6 +5845,190 @@
|
|
|
5836
5845
|
xhr.send(JSON.stringify({ label: label }));
|
|
5837
5846
|
}
|
|
5838
5847
|
|
|
5848
|
+
// ── Thread rename (task 186) ──
|
|
5849
|
+
|
|
5850
|
+
/**
|
|
5851
|
+
* Re-key every piece of client state that names a thread. One function so
|
|
5852
|
+
* a `thread_renamed` from the server and a rename this tab just made take
|
|
5853
|
+
* the same path; a list missed here is a sidebar row that 404s on click.
|
|
5854
|
+
*/
|
|
5855
|
+
function applyThreadRename(from, to) {
|
|
5856
|
+
for (var i = 0; i < allThreads.length; i++) {
|
|
5857
|
+
if (allThreads[i].name === from) allThreads[i].name = to;
|
|
5858
|
+
if (allThreads[i].master === from) allThreads[i].master = to;
|
|
5859
|
+
}
|
|
5860
|
+
if (threadStates[from]) {
|
|
5861
|
+
threadStates[to] = threadStates[from];
|
|
5862
|
+
delete threadStates[from];
|
|
5863
|
+
}
|
|
5864
|
+
var vi = visibleThreads.indexOf(from);
|
|
5865
|
+
if (vi !== -1) visibleThreads[vi] = to;
|
|
5866
|
+
if (busyThreads[from]) {
|
|
5867
|
+
busyThreads[to] = true;
|
|
5868
|
+
delete busyThreads[from];
|
|
5869
|
+
}
|
|
5870
|
+
if (unreadThreads[from]) {
|
|
5871
|
+
unreadThreads[to] = unreadThreads[from];
|
|
5872
|
+
delete unreadThreads[from];
|
|
5873
|
+
}
|
|
5874
|
+
if (collapsedMasters[from]) {
|
|
5875
|
+
collapsedMasters[to] = true;
|
|
5876
|
+
delete collapsedMasters[from];
|
|
5877
|
+
try {
|
|
5878
|
+
localStorage.setItem('cumulus-collapsed-masters', JSON.stringify(collapsedMasters));
|
|
5879
|
+
} catch {
|
|
5880
|
+
/* storage unavailable — the collapse state is cosmetic */
|
|
5881
|
+
}
|
|
5882
|
+
}
|
|
5883
|
+
}
|
|
5884
|
+
|
|
5885
|
+
function renameThread(from, to, cb) {
|
|
5886
|
+
var xhr = new XMLHttpRequest();
|
|
5887
|
+
xhr.open('POST', '/api/thread/' + encodeURIComponent(from) + '/rename');
|
|
5888
|
+
xhr.setRequestHeader('Content-Type', 'application/json');
|
|
5889
|
+
xhr.setRequestHeader('X-API-Key', activeApiKey);
|
|
5890
|
+
xhr.onload = function () {
|
|
5891
|
+
var body = {};
|
|
5892
|
+
try {
|
|
5893
|
+
body = JSON.parse(xhr.responseText || '{}');
|
|
5894
|
+
} catch {
|
|
5895
|
+
/* fall through to the status-based message below */
|
|
5896
|
+
}
|
|
5897
|
+
if (xhr.status === 200) {
|
|
5898
|
+
cb(null, body);
|
|
5899
|
+
return;
|
|
5900
|
+
}
|
|
5901
|
+
cb(body.error || 'Could not rename the thread (' + xhr.status + ').');
|
|
5902
|
+
};
|
|
5903
|
+
xhr.onerror = function () {
|
|
5904
|
+
cb('Could not reach the gateway.');
|
|
5905
|
+
};
|
|
5906
|
+
xhr.send(JSON.stringify({ name: to }));
|
|
5907
|
+
}
|
|
5908
|
+
|
|
5909
|
+
function showThreadRenameDialog(threadName, master) {
|
|
5910
|
+
var existing = document.querySelector('.cumulus-confirm-dialog[data-context="rename"]');
|
|
5911
|
+
if (existing) existing.remove();
|
|
5912
|
+
|
|
5913
|
+
// A co-thread keeps its master's prefix (the server enforces it); the
|
|
5914
|
+
// dialog shows the prefix as fixed text and edits only the label.
|
|
5915
|
+
var prefix = master ? master + '-' : '';
|
|
5916
|
+
var current = threadName.slice(prefix.length);
|
|
5917
|
+
|
|
5918
|
+
var dialog = document.createElement('div');
|
|
5919
|
+
dialog.className = 'cumulus-confirm-dialog';
|
|
5920
|
+
dialog.setAttribute('data-context', 'rename');
|
|
5921
|
+
dialog.style.cssText =
|
|
5922
|
+
'position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.6); z-index: 200; display: flex; align-items: center; justify-content: center;';
|
|
5923
|
+
|
|
5924
|
+
var box = document.createElement('div');
|
|
5925
|
+
box.className = 'cumulus-confirm-dialog-box';
|
|
5926
|
+
|
|
5927
|
+
var title = document.createElement('div');
|
|
5928
|
+
title.className = 'cumulus-confirm-dialog-title';
|
|
5929
|
+
title.textContent = master ? 'Rename co-thread' : 'Rename thread';
|
|
5930
|
+
box.appendChild(title);
|
|
5931
|
+
|
|
5932
|
+
var text = document.createElement('div');
|
|
5933
|
+
text.className = 'cumulus-confirm-dialog-text';
|
|
5934
|
+
text.textContent = master
|
|
5935
|
+
? 'Keeps "' + master + '-" so it stays a co-thread of ' + master + '.'
|
|
5936
|
+
: 'Everything the thread has — history, memory, settings, jobs, schedules — moves with it.';
|
|
5937
|
+
box.appendChild(text);
|
|
5938
|
+
|
|
5939
|
+
var input = document.createElement('input');
|
|
5940
|
+
input.type = 'text';
|
|
5941
|
+
input.className = 'cumulus-co-thread-input';
|
|
5942
|
+
input.value = current;
|
|
5943
|
+
input.setAttribute('data-testid', 'webchat-thread-rename-input');
|
|
5944
|
+
box.appendChild(input);
|
|
5945
|
+
|
|
5946
|
+
var preview = document.createElement('div');
|
|
5947
|
+
preview.className = 'cumulus-confirm-dialog-warn';
|
|
5948
|
+
preview.setAttribute('data-testid', 'webchat-thread-rename-preview');
|
|
5949
|
+
box.appendChild(preview);
|
|
5950
|
+
|
|
5951
|
+
// Mirrors THREAD_NAME_RE / CO_THREAD_LABEL_RE in thread-rename.ts. The
|
|
5952
|
+
// server is authoritative; this only turns a 400 into a message written
|
|
5953
|
+
// before the request is sent.
|
|
5954
|
+
var NAME_OK = master ? /^[a-zA-Z0-9_]+$/ : /^[a-zA-Z0-9_-]+$/;
|
|
5955
|
+
function refreshPreview() {
|
|
5956
|
+
var value = input.value.trim();
|
|
5957
|
+
if (!value) {
|
|
5958
|
+
preview.textContent = master
|
|
5959
|
+
? 'Letters, numbers and underscores.'
|
|
5960
|
+
: 'Letters, numbers, hyphens and underscores.';
|
|
5961
|
+
return false;
|
|
5962
|
+
}
|
|
5963
|
+
if (!NAME_OK.test(value)) {
|
|
5964
|
+
preview.textContent = master
|
|
5965
|
+
? 'Letters, numbers and underscores only — no spaces or hyphens.'
|
|
5966
|
+
: 'Letters, numbers, hyphens and underscores only — no spaces.';
|
|
5967
|
+
return false;
|
|
5968
|
+
}
|
|
5969
|
+
if (prefix + value === threadName) {
|
|
5970
|
+
preview.textContent = 'That is the current name.';
|
|
5971
|
+
return false;
|
|
5972
|
+
}
|
|
5973
|
+
preview.textContent = 'Renames to: ' + prefix + value;
|
|
5974
|
+
return true;
|
|
5975
|
+
}
|
|
5976
|
+
refreshPreview();
|
|
5977
|
+
input.addEventListener('input', refreshPreview);
|
|
5978
|
+
|
|
5979
|
+
var actions = document.createElement('div');
|
|
5980
|
+
actions.className = 'cumulus-confirm-dialog-actions';
|
|
5981
|
+
|
|
5982
|
+
var cancelBtn = document.createElement('button');
|
|
5983
|
+
cancelBtn.className = 'cumulus-confirm-dialog-cancel';
|
|
5984
|
+
cancelBtn.textContent = 'Cancel';
|
|
5985
|
+
cancelBtn.addEventListener('click', function () {
|
|
5986
|
+
dialog.remove();
|
|
5987
|
+
});
|
|
5988
|
+
actions.appendChild(cancelBtn);
|
|
5989
|
+
|
|
5990
|
+
var renameBtn = document.createElement('button');
|
|
5991
|
+
renameBtn.className = 'cumulus-confirm-dialog-confirm';
|
|
5992
|
+
renameBtn.setAttribute('data-testid', 'webchat-thread-rename-confirm');
|
|
5993
|
+
renameBtn.textContent = 'Rename';
|
|
5994
|
+
function submit() {
|
|
5995
|
+
if (!refreshPreview()) return;
|
|
5996
|
+
renameBtn.disabled = true;
|
|
5997
|
+
renameBtn.textContent = 'Renaming…';
|
|
5998
|
+
var to = prefix + input.value.trim();
|
|
5999
|
+
renameThread(threadName, to, function (err) {
|
|
6000
|
+
if (err) {
|
|
6001
|
+
renameBtn.disabled = false;
|
|
6002
|
+
renameBtn.textContent = 'Rename';
|
|
6003
|
+
preview.textContent = err;
|
|
6004
|
+
return;
|
|
6005
|
+
}
|
|
6006
|
+
dialog.remove();
|
|
6007
|
+
// The server also broadcasts thread_renamed to every tab, this one
|
|
6008
|
+
// included; applying it here too means no flash of the old name.
|
|
6009
|
+
applyThreadRename(threadName, to);
|
|
6010
|
+
renderSidebar();
|
|
6011
|
+
renderContentArea();
|
|
6012
|
+
if (connection) connection.send({ type: 'threads' });
|
|
6013
|
+
});
|
|
6014
|
+
}
|
|
6015
|
+
renameBtn.addEventListener('click', submit);
|
|
6016
|
+
input.addEventListener('keydown', function (e) {
|
|
6017
|
+
if (e.key === 'Enter') submit();
|
|
6018
|
+
if (e.key === 'Escape') dialog.remove();
|
|
6019
|
+
});
|
|
6020
|
+
actions.appendChild(renameBtn);
|
|
6021
|
+
|
|
6022
|
+
box.appendChild(actions);
|
|
6023
|
+
dialog.appendChild(box);
|
|
6024
|
+
dialog.addEventListener('click', function (e) {
|
|
6025
|
+
if (e.target === dialog) dialog.remove();
|
|
6026
|
+
});
|
|
6027
|
+
document.body.appendChild(dialog);
|
|
6028
|
+
input.focus();
|
|
6029
|
+
input.select();
|
|
6030
|
+
}
|
|
6031
|
+
|
|
5839
6032
|
// ── Thread delete confirmation ──
|
|
5840
6033
|
function showThreadDeleteConfirm(threadName) {
|
|
5841
6034
|
// Remove any existing confirm dialogs
|
|
@@ -6679,10 +6872,10 @@
|
|
|
6679
6872
|
voiceCard.appendChild(voiceSelect);
|
|
6680
6873
|
body.appendChild(voiceCard);
|
|
6681
6874
|
|
|
6682
|
-
// Task 164: the stall-detection classifier
|
|
6683
|
-
//
|
|
6684
|
-
//
|
|
6685
|
-
//
|
|
6875
|
+
// Task 164/165: the stall-detection classifier runs on any catalog model, so this
|
|
6876
|
+
// dropdown lists the full catalog with a leading "Not configured" option that clears
|
|
6877
|
+
// the setting — and clearing it really does turn the feature off (task 184). Same
|
|
6878
|
+
// card shape as the voice model above.
|
|
6686
6879
|
var stallCard = document.createElement('div');
|
|
6687
6880
|
stallCard.className = 'cumulus-settings-default-card';
|
|
6688
6881
|
var stallCardLabel = document.createElement('label');
|
|
@@ -7341,27 +7534,31 @@
|
|
|
7341
7534
|
|
|
7342
7535
|
// Task 165: the classifier runs on ANY catalog model (a Claude CLI sub-model or a
|
|
7343
7536
|
// direct-provider model), so this lists the FULL catalog grouped by provider — same
|
|
7344
|
-
// shape as the voice selector — plus a leading "
|
|
7345
|
-
//
|
|
7346
|
-
//
|
|
7537
|
+
// shape as the voice selector — plus a leading "Not configured" clear option.
|
|
7538
|
+
//
|
|
7539
|
+
// Task 184: that option used to read "Haiku (default)", which was a claim about a
|
|
7540
|
+
// model in no catalog that the operator never chose and that a fresh install has no
|
|
7541
|
+
// way to run. Empty now means empty, on the screen and in the gateway alike, so the
|
|
7542
|
+
// label says what the feature actually does when nothing is picked. A stored id
|
|
7543
|
+
// naming nothing in either catalog lands here too — the gateway reads it the same way.
|
|
7347
7544
|
function renderStallSelect() {
|
|
7348
7545
|
stallSelect.innerHTML = '';
|
|
7349
|
-
var
|
|
7350
|
-
|
|
7351
|
-
|
|
7352
|
-
stallSelect.appendChild(
|
|
7546
|
+
var unset = document.createElement('option');
|
|
7547
|
+
unset.value = '';
|
|
7548
|
+
unset.textContent = 'Not configured — no stall detection';
|
|
7549
|
+
stallSelect.appendChild(unset);
|
|
7353
7550
|
fillModelOptions(stallSelect);
|
|
7354
7551
|
stallSelect.value = gwState.stallClassifierModel || '';
|
|
7355
7552
|
if (stallSelect.value !== (gwState.stallClassifierModel || '')) stallSelect.value = '';
|
|
7356
7553
|
}
|
|
7357
7554
|
|
|
7358
|
-
// Task 179 — same rule, same
|
|
7555
|
+
// Task 179 — same rule, same not-configured reading, its own setting.
|
|
7359
7556
|
function renderWorkerSelect() {
|
|
7360
7557
|
workerSelect.innerHTML = '';
|
|
7361
|
-
var
|
|
7362
|
-
|
|
7363
|
-
|
|
7364
|
-
workerSelect.appendChild(
|
|
7558
|
+
var workerUnset = document.createElement('option');
|
|
7559
|
+
workerUnset.value = '';
|
|
7560
|
+
workerUnset.textContent = 'Not configured — Refresh unavailable';
|
|
7561
|
+
workerSelect.appendChild(workerUnset);
|
|
7365
7562
|
fillModelOptions(workerSelect);
|
|
7366
7563
|
workerSelect.value = gwState.workerModel || '';
|
|
7367
7564
|
if (workerSelect.value !== (gwState.workerModel || '')) workerSelect.value = '';
|
|
@@ -10592,6 +10789,17 @@
|
|
|
10592
10789
|
}
|
|
10593
10790
|
break;
|
|
10594
10791
|
|
|
10792
|
+
case 'thread_renamed':
|
|
10793
|
+
// Task 186 — re-key this tab's state, then ask for the listing, which
|
|
10794
|
+
// is what carries `master` and the per-thread counts.
|
|
10795
|
+
if (data.from && data.to) {
|
|
10796
|
+
applyThreadRename(data.from, data.to);
|
|
10797
|
+
renderSidebar();
|
|
10798
|
+
renderContentArea();
|
|
10799
|
+
if (connection) connection.send({ type: 'threads' });
|
|
10800
|
+
}
|
|
10801
|
+
break;
|
|
10802
|
+
|
|
10595
10803
|
case 'thread_deleted':
|
|
10596
10804
|
// Server confirms thread deleted; remove from sidebar and panels
|
|
10597
10805
|
if (data.threadName) {
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task 186 — rename a thread or a co-thread.
|
|
3
|
+
*
|
|
4
|
+
* The thread's NAME is the key of every artefact the gateway keeps about it
|
|
5
|
+
* (task 185 lists them; the task doc for 186 repeats the list). So a rename is
|
|
6
|
+
* not a field update: it is one migration that moves every name-keyed row at
|
|
7
|
+
* once, and this module is the only place that migration is written. A second
|
|
8
|
+
* caller — an MCP tool, the CLI — calls `executeThreadRename`; there is no
|
|
9
|
+
* other path, because a path that moved most of the rows would leave a thread
|
|
10
|
+
* that half-exists under two names (Rule #8).
|
|
11
|
+
*
|
|
12
|
+
* Two phases, deliberately separate:
|
|
13
|
+
*
|
|
14
|
+
* 1. `planThreadRename` — validates and computes every move, including the
|
|
15
|
+
* cascade when a master is renamed. Pure apart from the context callbacks,
|
|
16
|
+
* so the six rules it enforces are testable without a filesystem, and so
|
|
17
|
+
* nothing moves until the WHOLE plan is legal.
|
|
18
|
+
* 2. `executeThreadRename` — moves files, rewrites the master's `coThreads`,
|
|
19
|
+
* moves worktrees, appends corrected attribution lines. The caller (server)
|
|
20
|
+
* then re-keys its own in-memory maps and tells the sockets.
|
|
21
|
+
*/
|
|
22
|
+
import type { NamespaceConfig } from './config.js';
|
|
23
|
+
/** The alphabet `create_thread` sanitises to. A rename refuses instead of sanitising. */
|
|
24
|
+
export declare const THREAD_NAME_RE: RegExp;
|
|
25
|
+
/** Mirrors CO_THREAD_LABEL in server.ts: the part of a co-thread's name after `<master>-`. */
|
|
26
|
+
export declare const CO_THREAD_LABEL_RE: RegExp;
|
|
27
|
+
/**
|
|
28
|
+
* Every per-thread file or directory under the threads dir, by extension. The
|
|
29
|
+
* same list `handleDeleteThread` walks, plus the streaming scratch file. A
|
|
30
|
+
* rename that missed one would leave an orphan that reattaches to nothing.
|
|
31
|
+
*/
|
|
32
|
+
export declare const THREAD_FILE_EXTENSIONS: readonly [".jsonl", ".embeddings.json", ".embeddings.bin", ".segments.json", ".adaptive.json", ".config.json", ".sessions", ".content", ".prompts", ".streaming.tmp"];
|
|
33
|
+
/** A refused plan. `status` is the HTTP status the endpoint answers with. */
|
|
34
|
+
export declare class ThreadRenameError extends Error {
|
|
35
|
+
readonly status: number;
|
|
36
|
+
constructor(status: number, message: string);
|
|
37
|
+
}
|
|
38
|
+
export interface RenameMove {
|
|
39
|
+
from: string;
|
|
40
|
+
to: string;
|
|
41
|
+
}
|
|
42
|
+
export interface ThreadRenamePlan {
|
|
43
|
+
from: string;
|
|
44
|
+
to: string;
|
|
45
|
+
/** Set when the renamed thread is a co-thread: whose list to rewrite. */
|
|
46
|
+
coThreadMaster?: string;
|
|
47
|
+
/** Cascaded co-thread renames when a master is renamed (empty otherwise). */
|
|
48
|
+
coThreads: RenameMove[];
|
|
49
|
+
/** Every move, the thread itself first. */
|
|
50
|
+
moves: RenameMove[];
|
|
51
|
+
}
|
|
52
|
+
export interface PlanContext {
|
|
53
|
+
namespaces: NamespaceConfig[];
|
|
54
|
+
/** Any artefact under any extension exists for this name. */
|
|
55
|
+
threadExists(name: string): boolean;
|
|
56
|
+
/** The thread is mid-turn right now. */
|
|
57
|
+
threadBusy(name: string): boolean;
|
|
58
|
+
/** Master of a registered co-thread, else undefined. */
|
|
59
|
+
coThreadMasterOf(name: string): string | undefined;
|
|
60
|
+
/** The master's registered co-thread list (its EXACT config). */
|
|
61
|
+
coThreadsOf(master: string): Promise<string[]>;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Validate a rename and compute every move it implies.
|
|
65
|
+
*
|
|
66
|
+
* Rules, in the order they are checked (each one's failure names it):
|
|
67
|
+
* 1. `from` exists.
|
|
68
|
+
* 2. `to` has the thread-name shape and differs from `from`.
|
|
69
|
+
* 3. Neither name is a namespace base, and both fall in the same namespace.
|
|
70
|
+
* 4. A co-thread keeps `<master>-` and its new label has the label shape.
|
|
71
|
+
* 5. A master's co-threads cascade: `<from>-x` → `<to>-x`.
|
|
72
|
+
* 6. No target name exists; no source is mid-turn. Checked for the WHOLE
|
|
73
|
+
* cascade before anything is reported legal.
|
|
74
|
+
*/
|
|
75
|
+
export declare function planThreadRename(from: string, to: string, ctx: PlanContext): Promise<ThreadRenamePlan>;
|
|
76
|
+
export interface ExecuteContext {
|
|
77
|
+
threadsDir: string;
|
|
78
|
+
/** The project directory a master's worktrees hang off. */
|
|
79
|
+
projectDirFor(master: string): Promise<string | undefined>;
|
|
80
|
+
}
|
|
81
|
+
export interface ThreadRenameResult {
|
|
82
|
+
from: string;
|
|
83
|
+
to: string;
|
|
84
|
+
coThreads: RenameMove[];
|
|
85
|
+
/** Extensions actually moved, per thread. */
|
|
86
|
+
moved: Record<string, string[]>;
|
|
87
|
+
/** Attribution entries rewritten in the affected content store. */
|
|
88
|
+
attributionUpdated: number;
|
|
89
|
+
/** Worktree outcomes for every co-thread move. */
|
|
90
|
+
worktrees: Array<RenameMove & {
|
|
91
|
+
moved: boolean;
|
|
92
|
+
detail?: string;
|
|
93
|
+
}>;
|
|
94
|
+
}
|
|
95
|
+
/** Move every existing artefact of `from` to `to`. Returns the extensions moved. */
|
|
96
|
+
export declare function renameThreadFiles(threadsDir: string, from: string, to: string): string[];
|
|
97
|
+
/**
|
|
98
|
+
* Carry out a legal plan. Order matters:
|
|
99
|
+
*
|
|
100
|
+
* - The project dir is read BEFORE the files move: for a master it lives in
|
|
101
|
+
* the config file that is about to change name.
|
|
102
|
+
* - Files move before the `coThreads` rewrite, because for a master the list
|
|
103
|
+
* is written into the config under its NEW name.
|
|
104
|
+
* - Worktrees move from the master's checkout, which does not move.
|
|
105
|
+
* - Attribution is appended last, into the store under its new path.
|
|
106
|
+
*/
|
|
107
|
+
export declare function executeThreadRename(plan: ThreadRenamePlan, ctx: ExecuteContext): Promise<ThreadRenameResult>;
|
|
108
|
+
//# sourceMappingURL=thread-rename.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"thread-rename.d.ts","sourceRoot":"","sources":["../../src/gateway/thread-rename.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AASH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAGnD,yFAAyF;AACzF,eAAO,MAAM,cAAc,QAAqB,CAAC;AACjD,8FAA8F;AAC9F,eAAO,MAAM,kBAAkB,QAAoB,CAAC;AAEpD;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,uKAWzB,CAAC;AAEX,6EAA6E;AAC7E,qBAAa,iBAAkB,SAAQ,KAAK;aAExB,MAAM,EAAE,MAAM;gBAAd,MAAM,EAAE,MAAM,EAC9B,OAAO,EAAE,MAAM;CAKlB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,yEAAyE;IACzE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,6EAA6E;IAC7E,SAAS,EAAE,UAAU,EAAE,CAAC;IACxB,2CAA2C;IAC3C,KAAK,EAAE,UAAU,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,eAAe,EAAE,CAAC;IAC9B,6DAA6D;IAC7D,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACpC,wCAAwC;IACxC,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAClC,wDAAwD;IACxD,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IACnD,iEAAiE;IACjE,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CAChD;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,GAAG,EAAE,WAAW,GACf,OAAO,CAAC,gBAAgB,CAAC,CAuB3B;AA4ED,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CAC5D;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,UAAU,EAAE,CAAC;IACxB,6CAA6C;IAC7C,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAChC,mEAAmE;IACnE,kBAAkB,EAAE,MAAM,CAAC;IAC3B,kDAAkD;IAClD,SAAS,EAAE,KAAK,CAAC,UAAU,GAAG;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpE;AAED,oFAAoF;AACpF,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,CASxF;AAED;;;;;;;;;GASG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,gBAAgB,EACtB,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,kBAAkB,CAAC,CA4C7B"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task 186 — rename a thread or a co-thread.
|
|
3
|
+
*
|
|
4
|
+
* The thread's NAME is the key of every artefact the gateway keeps about it
|
|
5
|
+
* (task 185 lists them; the task doc for 186 repeats the list). So a rename is
|
|
6
|
+
* not a field update: it is one migration that moves every name-keyed row at
|
|
7
|
+
* once, and this module is the only place that migration is written. A second
|
|
8
|
+
* caller — an MCP tool, the CLI — calls `executeThreadRename`; there is no
|
|
9
|
+
* other path, because a path that moved most of the rows would leave a thread
|
|
10
|
+
* that half-exists under two names (Rule #8).
|
|
11
|
+
*
|
|
12
|
+
* Two phases, deliberately separate:
|
|
13
|
+
*
|
|
14
|
+
* 1. `planThreadRename` — validates and computes every move, including the
|
|
15
|
+
* cascade when a master is renamed. Pure apart from the context callbacks,
|
|
16
|
+
* so the six rules it enforces are testable without a filesystem, and so
|
|
17
|
+
* nothing moves until the WHOLE plan is legal.
|
|
18
|
+
* 2. `executeThreadRename` — moves files, rewrites the master's `coThreads`,
|
|
19
|
+
* moves worktrees, appends corrected attribution lines. The caller (server)
|
|
20
|
+
* then re-keys its own in-memory maps and tells the sockets.
|
|
21
|
+
*/
|
|
22
|
+
import * as fs from 'fs';
|
|
23
|
+
import * as path from 'path';
|
|
24
|
+
import { loadThreadConfigExact, saveThreadConfig } from '../lib/config.js';
|
|
25
|
+
import { ContentStore } from '../lib/content-store.js';
|
|
26
|
+
import { moveCoThreadWorktree } from '../lib/worktree.js';
|
|
27
|
+
import { namespaceForThread } from './namespaces.js';
|
|
28
|
+
/** The alphabet `create_thread` sanitises to. A rename refuses instead of sanitising. */
|
|
29
|
+
export const THREAD_NAME_RE = /^[a-zA-Z0-9_-]+$/;
|
|
30
|
+
/** Mirrors CO_THREAD_LABEL in server.ts: the part of a co-thread's name after `<master>-`. */
|
|
31
|
+
export const CO_THREAD_LABEL_RE = /^[a-zA-Z0-9_]+$/;
|
|
32
|
+
/**
|
|
33
|
+
* Every per-thread file or directory under the threads dir, by extension. The
|
|
34
|
+
* same list `handleDeleteThread` walks, plus the streaming scratch file. A
|
|
35
|
+
* rename that missed one would leave an orphan that reattaches to nothing.
|
|
36
|
+
*/
|
|
37
|
+
export const THREAD_FILE_EXTENSIONS = [
|
|
38
|
+
'.jsonl',
|
|
39
|
+
'.embeddings.json',
|
|
40
|
+
'.embeddings.bin',
|
|
41
|
+
'.segments.json',
|
|
42
|
+
'.adaptive.json',
|
|
43
|
+
'.config.json',
|
|
44
|
+
'.sessions',
|
|
45
|
+
'.content',
|
|
46
|
+
'.prompts',
|
|
47
|
+
'.streaming.tmp',
|
|
48
|
+
];
|
|
49
|
+
/** A refused plan. `status` is the HTTP status the endpoint answers with. */
|
|
50
|
+
export class ThreadRenameError extends Error {
|
|
51
|
+
status;
|
|
52
|
+
constructor(status, message) {
|
|
53
|
+
super(message);
|
|
54
|
+
this.status = status;
|
|
55
|
+
this.name = 'ThreadRenameError';
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Validate a rename and compute every move it implies.
|
|
60
|
+
*
|
|
61
|
+
* Rules, in the order they are checked (each one's failure names it):
|
|
62
|
+
* 1. `from` exists.
|
|
63
|
+
* 2. `to` has the thread-name shape and differs from `from`.
|
|
64
|
+
* 3. Neither name is a namespace base, and both fall in the same namespace.
|
|
65
|
+
* 4. A co-thread keeps `<master>-` and its new label has the label shape.
|
|
66
|
+
* 5. A master's co-threads cascade: `<from>-x` → `<to>-x`.
|
|
67
|
+
* 6. No target name exists; no source is mid-turn. Checked for the WHOLE
|
|
68
|
+
* cascade before anything is reported legal.
|
|
69
|
+
*/
|
|
70
|
+
export async function planThreadRename(from, to, ctx) {
|
|
71
|
+
if (!ctx.threadExists(from)) {
|
|
72
|
+
throw new ThreadRenameError(404, `Thread "${from}" does not exist.`);
|
|
73
|
+
}
|
|
74
|
+
if (!THREAD_NAME_RE.test(to)) {
|
|
75
|
+
throw new ThreadRenameError(400, 'A thread name is letters, numbers, hyphens and underscores only (no spaces or slashes).');
|
|
76
|
+
}
|
|
77
|
+
if (to === from) {
|
|
78
|
+
throw new ThreadRenameError(400, `"${from}" is already this thread's name.`);
|
|
79
|
+
}
|
|
80
|
+
assertNamespacePreserved(from, to, ctx.namespaces);
|
|
81
|
+
const coThreadMaster = ctx.coThreadMasterOf(from);
|
|
82
|
+
if (coThreadMaster)
|
|
83
|
+
assertCoThreadShape(from, to, coThreadMaster);
|
|
84
|
+
const coThreads = coThreadMaster ? [] : cascadeMoves(from, to, await ctx.coThreadsOf(from));
|
|
85
|
+
const moves = [{ from, to }, ...coThreads];
|
|
86
|
+
assertMovesLegal(from, moves, ctx);
|
|
87
|
+
return { from, to, ...(coThreadMaster ? { coThreadMaster } : {}), coThreads, moves };
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Rule 3 — a namespace is a thread-name prefix and a capability boundary
|
|
91
|
+
* (task 097): a rename must not move a thread across one, and the base
|
|
92
|
+
* thread's name IS the namespace.
|
|
93
|
+
*/
|
|
94
|
+
function assertNamespacePreserved(from, to, namespaces) {
|
|
95
|
+
const nsBases = new Set(namespaces.map(ns => ns.name));
|
|
96
|
+
if (nsBases.has(from)) {
|
|
97
|
+
throw new ThreadRenameError(400, `"${from}" is the base thread of a namespace; its name is the namespace and cannot change.`);
|
|
98
|
+
}
|
|
99
|
+
if (nsBases.has(to)) {
|
|
100
|
+
throw new ThreadRenameError(400, `"${to}" is the name of a namespace.`);
|
|
101
|
+
}
|
|
102
|
+
const nsFrom = namespaceForThread(from, namespaces);
|
|
103
|
+
const nsTo = namespaceForThread(to, namespaces);
|
|
104
|
+
if (nsFrom !== nsTo) {
|
|
105
|
+
throw new ThreadRenameError(400, nsFrom
|
|
106
|
+
? `"${from}" belongs to the "${nsFrom}" namespace, so its new name must keep the "${nsFrom}-" prefix.`
|
|
107
|
+
: `"${to}" would move the thread into the "${nsTo}" namespace.`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Rule 4 — a co-thread resolves to its master by prefix chain plus
|
|
112
|
+
* registration. A name that stops starting with `<master>-` loses the shared
|
|
113
|
+
* store and the worktree in one stroke.
|
|
114
|
+
*/
|
|
115
|
+
function assertCoThreadShape(from, to, master) {
|
|
116
|
+
const prefix = `${master}-`;
|
|
117
|
+
const label = to.startsWith(prefix) ? to.slice(prefix.length) : '';
|
|
118
|
+
if (!label || !CO_THREAD_LABEL_RE.test(label)) {
|
|
119
|
+
throw new ThreadRenameError(400, `"${from}" is a co-thread of "${master}", so its new name must be "${prefix}<label>" with a label of letters, numbers or underscores.`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/** Rule 5 — a master's registered co-threads follow it: `<from>-x` → `<to>-x`. */
|
|
123
|
+
function cascadeMoves(from, to, registered) {
|
|
124
|
+
return registered.map(name => {
|
|
125
|
+
if (!name.startsWith(`${from}-`)) {
|
|
126
|
+
throw new ThreadRenameError(409, `Registered co-thread "${name}" does not carry the "${from}-" prefix; fix the registration before renaming.`);
|
|
127
|
+
}
|
|
128
|
+
return { from: name, to: `${to}${name.slice(from.length)}` };
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
/** Rule 6 — over the WHOLE plan, before anything is reported legal. */
|
|
132
|
+
function assertMovesLegal(from, moves, ctx) {
|
|
133
|
+
for (const move of moves) {
|
|
134
|
+
if (ctx.threadExists(move.to)) {
|
|
135
|
+
throw new ThreadRenameError(409, `A thread named "${move.to}" already exists.`);
|
|
136
|
+
}
|
|
137
|
+
if (ctx.threadBusy(move.from)) {
|
|
138
|
+
throw new ThreadRenameError(409, move.from === from
|
|
139
|
+
? `"${from}" is running a turn; rename it once it is idle.`
|
|
140
|
+
: `Co-thread "${move.from}" is running a turn; rename once it is idle.`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/** Move every existing artefact of `from` to `to`. Returns the extensions moved. */
|
|
145
|
+
export function renameThreadFiles(threadsDir, from, to) {
|
|
146
|
+
const moved = [];
|
|
147
|
+
for (const ext of THREAD_FILE_EXTENSIONS) {
|
|
148
|
+
const source = path.join(threadsDir, `${from}${ext}`);
|
|
149
|
+
if (!fs.existsSync(source))
|
|
150
|
+
continue;
|
|
151
|
+
fs.renameSync(source, path.join(threadsDir, `${to}${ext}`));
|
|
152
|
+
moved.push(ext);
|
|
153
|
+
}
|
|
154
|
+
return moved;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Carry out a legal plan. Order matters:
|
|
158
|
+
*
|
|
159
|
+
* - The project dir is read BEFORE the files move: for a master it lives in
|
|
160
|
+
* the config file that is about to change name.
|
|
161
|
+
* - Files move before the `coThreads` rewrite, because for a master the list
|
|
162
|
+
* is written into the config under its NEW name.
|
|
163
|
+
* - Worktrees move from the master's checkout, which does not move.
|
|
164
|
+
* - Attribution is appended last, into the store under its new path.
|
|
165
|
+
*/
|
|
166
|
+
export async function executeThreadRename(plan, ctx) {
|
|
167
|
+
const master = plan.coThreadMaster ?? plan.from;
|
|
168
|
+
const masterDir = await ctx.projectDirFor(master);
|
|
169
|
+
const moved = {};
|
|
170
|
+
for (const move of plan.moves) {
|
|
171
|
+
moved[move.from] = renameThreadFiles(ctx.threadsDir, move.from, move.to);
|
|
172
|
+
}
|
|
173
|
+
if (plan.coThreadMaster) {
|
|
174
|
+
const config = await loadThreadConfigExact(plan.coThreadMaster);
|
|
175
|
+
await saveThreadConfig(plan.coThreadMaster, {
|
|
176
|
+
coThreads: (config.coThreads ?? []).map(n => (n === plan.from ? plan.to : n)),
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
else if (plan.coThreads.length > 0) {
|
|
180
|
+
await saveThreadConfig(plan.to, { coThreads: plan.coThreads.map(m => m.to) });
|
|
181
|
+
}
|
|
182
|
+
const worktrees = [];
|
|
183
|
+
const coMoves = plan.coThreadMaster ? [{ from: plan.from, to: plan.to }] : plan.coThreads;
|
|
184
|
+
for (const move of coMoves) {
|
|
185
|
+
const outcome = await moveCoThreadWorktree(move.from, move.to, masterDir);
|
|
186
|
+
worktrees.push({ ...move, ...outcome });
|
|
187
|
+
}
|
|
188
|
+
// The store that carries this thread's tags: its own for a plain thread or a
|
|
189
|
+
// master (now under the new name), the master's for a co-thread.
|
|
190
|
+
const storeOwner = plan.coThreadMaster ?? plan.to;
|
|
191
|
+
const storePath = path.join(ctx.threadsDir, `${storeOwner}.content`);
|
|
192
|
+
let attributionUpdated = 0;
|
|
193
|
+
if (fs.existsSync(storePath)) {
|
|
194
|
+
const mapping = {};
|
|
195
|
+
for (const move of plan.moves)
|
|
196
|
+
mapping[move.from] = move.to;
|
|
197
|
+
attributionUpdated = await new ContentStore(storePath).renameThreads(mapping);
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
from: plan.from,
|
|
201
|
+
to: plan.to,
|
|
202
|
+
coThreads: plan.coThreads,
|
|
203
|
+
moved,
|
|
204
|
+
attributionUpdated,
|
|
205
|
+
worktrees,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=thread-rename.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"thread-rename.js","sourceRoot":"","sources":["../../src/gateway/thread-rename.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAC3E,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAG1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAErD,yFAAyF;AACzF,MAAM,CAAC,MAAM,cAAc,GAAG,kBAAkB,CAAC;AACjD,8FAA8F;AAC9F,MAAM,CAAC,MAAM,kBAAkB,GAAG,iBAAiB,CAAC;AAEpD;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,QAAQ;IACR,kBAAkB;IAClB,iBAAiB;IACjB,gBAAgB;IAChB,gBAAgB;IAChB,cAAc;IACd,WAAW;IACX,UAAU;IACV,UAAU;IACV,gBAAgB;CACR,CAAC;AAEX,6EAA6E;AAC7E,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IAExB;IADlB,YACkB,MAAc,EAC9B,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAC;QAHC,WAAM,GAAN,MAAM,CAAQ;QAI9B,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;AA8BD;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,IAAY,EACZ,EAAU,EACV,GAAgB;IAEhB,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,iBAAiB,CAAC,GAAG,EAAE,WAAW,IAAI,mBAAmB,CAAC,CAAC;IACvE,CAAC;IACD,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,iBAAiB,CACzB,GAAG,EACH,yFAAyF,CAC1F,CAAC;IACJ,CAAC;IACD,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;QAChB,MAAM,IAAI,iBAAiB,CAAC,GAAG,EAAE,IAAI,IAAI,kCAAkC,CAAC,CAAC;IAC/E,CAAC;IACD,wBAAwB,CAAC,IAAI,EAAE,EAAE,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;IAEnD,MAAM,cAAc,GAAG,GAAG,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;IAClD,IAAI,cAAc;QAAE,mBAAmB,CAAC,IAAI,EAAE,EAAE,EAAE,cAAc,CAAC,CAAC;IAElE,MAAM,SAAS,GAAG,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,EAAE,MAAM,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;IAC5F,MAAM,KAAK,GAAiB,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,GAAG,SAAS,CAAC,CAAC;IACzD,gBAAgB,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;IAEnC,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;AACvF,CAAC;AAED;;;;GAIG;AACH,SAAS,wBAAwB,CAAC,IAAY,EAAE,EAAU,EAAE,UAA6B;IACvF,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IACvD,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,iBAAiB,CACzB,GAAG,EACH,IAAI,IAAI,mFAAmF,CAC5F,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;QACpB,MAAM,IAAI,iBAAiB,CAAC,GAAG,EAAE,IAAI,EAAE,+BAA+B,CAAC,CAAC;IAC1E,CAAC;IACD,MAAM,MAAM,GAAG,kBAAkB,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,kBAAkB,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;IAChD,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,MAAM,IAAI,iBAAiB,CACzB,GAAG,EACH,MAAM;YACJ,CAAC,CAAC,IAAI,IAAI,qBAAqB,MAAM,+CAA+C,MAAM,YAAY;YACtG,CAAC,CAAC,IAAI,EAAE,qCAAqC,IAAI,cAAc,CAClE,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,SAAS,mBAAmB,CAAC,IAAY,EAAE,EAAU,EAAE,MAAc;IACnE,MAAM,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC;IAC5B,MAAM,KAAK,GAAG,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACnE,IAAI,CAAC,KAAK,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC9C,MAAM,IAAI,iBAAiB,CACzB,GAAG,EACH,IAAI,IAAI,wBAAwB,MAAM,+BAA+B,MAAM,2DAA2D,CACvI,CAAC;IACJ,CAAC;AACH,CAAC;AAED,kFAAkF;AAClF,SAAS,YAAY,CAAC,IAAY,EAAE,EAAU,EAAE,UAAoB;IAClE,OAAO,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;QAC3B,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,IAAI,GAAG,CAAC,EAAE,CAAC;YACjC,MAAM,IAAI,iBAAiB,CACzB,GAAG,EACH,yBAAyB,IAAI,yBAAyB,IAAI,kDAAkD,CAC7G,CAAC;QACJ,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;IAC/D,CAAC,CAAC,CAAC;AACL,CAAC;AAED,uEAAuE;AACvE,SAAS,gBAAgB,CAAC,IAAY,EAAE,KAAmB,EAAE,GAAgB;IAC3E,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,iBAAiB,CAAC,GAAG,EAAE,mBAAmB,IAAI,CAAC,EAAE,mBAAmB,CAAC,CAAC;QAClF,CAAC;QACD,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,iBAAiB,CACzB,GAAG,EACH,IAAI,CAAC,IAAI,KAAK,IAAI;gBAChB,CAAC,CAAC,IAAI,IAAI,iDAAiD;gBAC3D,CAAC,CAAC,cAAc,IAAI,CAAC,IAAI,8CAA8C,CAC1E,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC;AAoBD,oFAAoF;AACpF,MAAM,UAAU,iBAAiB,CAAC,UAAkB,EAAE,IAAY,EAAE,EAAU;IAC5E,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,GAAG,IAAI,sBAAsB,EAAE,CAAC;QACzC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,IAAI,GAAG,GAAG,EAAE,CAAC,CAAC;QACtD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC;YAAE,SAAS;QACrC,EAAE,CAAC,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,EAAE,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC;QAC5D,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,IAAsB,EACtB,GAAmB;IAEnB,MAAM,MAAM,GAAG,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,IAAI,CAAC;IAChD,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IAElD,MAAM,KAAK,GAA6B,EAAE,CAAC;IAC3C,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QAC9B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,iBAAiB,CAAC,GAAG,CAAC,UAAU,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;IAC3E,CAAC;IAED,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;QACxB,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAChE,MAAM,gBAAgB,CAAC,IAAI,CAAC,cAAc,EAAE;YAC1C,SAAS,EAAE,CAAC,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;SAC9E,CAAC,CAAC;IACL,CAAC;SAAM,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrC,MAAM,gBAAgB,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IAChF,CAAC;IAED,MAAM,SAAS,GAAoC,EAAE,CAAC;IACtD,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC;IAC1F,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,MAAM,oBAAoB,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;QAC1E,SAAS,CAAC,IAAI,CAAC,EAAE,GAAG,IAAI,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;IAC1C,CAAC;IAED,6EAA6E;IAC7E,iEAAiE;IACjE,MAAM,UAAU,GAAG,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,EAAE,CAAC;IAClD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,UAAU,UAAU,CAAC,CAAC;IACrE,IAAI,kBAAkB,GAAG,CAAC,CAAC;IAC3B,IAAI,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC7B,MAAM,OAAO,GAA2B,EAAE,CAAC;QAC3C,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK;YAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC;QAC5D,kBAAkB,GAAG,MAAM,IAAI,YAAY,CAAC,SAAS,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IAChF,CAAC;IAED,OAAO;QACL,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,EAAE,EAAE,IAAI,CAAC,EAAE;QACX,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,KAAK;QACL,kBAAkB;QAClB,SAAS;KACV,CAAC;AACJ,CAAC"}
|