claudish 14.11.0 → 14.12.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/dist/index.js +246 -17
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -5491,7 +5491,7 @@ var init_onepassword_config = __esm(() => {
|
|
|
5491
5491
|
});
|
|
5492
5492
|
|
|
5493
5493
|
// src/version.ts
|
|
5494
|
-
var VERSION = "14.
|
|
5494
|
+
var VERSION = "14.12.0";
|
|
5495
5495
|
|
|
5496
5496
|
// src/redact.ts
|
|
5497
5497
|
function isPlaceholder(value) {
|
|
@@ -73048,7 +73048,8 @@ var init_contract = __esm(() => {
|
|
|
73048
73048
|
"capture_since_seq",
|
|
73049
73049
|
"capture_spans",
|
|
73050
73050
|
"resize",
|
|
73051
|
-
"allowance"
|
|
73051
|
+
"allowance",
|
|
73052
|
+
"scroll"
|
|
73052
73053
|
];
|
|
73053
73054
|
SLOT_STATES = [
|
|
73054
73055
|
"STARTING",
|
|
@@ -93473,6 +93474,39 @@ var init_screen_classifier = __esm(() => {
|
|
|
93473
93474
|
NON_ANSWER_ROW = /^\s+\u23BF|^\s{2}(Ran|Read|Searched|Wrote|Edited|Listed|Updated) \d+ |^\s*[\u00B7\u2722\u2733\u2736\u273B\u273D*\u25CF\u2217]\s|^\s*\(ctrl\+/;
|
|
93474
93475
|
});
|
|
93475
93476
|
|
|
93477
|
+
// src/pane/scroll-input.ts
|
|
93478
|
+
function isLiveBottom(pos) {
|
|
93479
|
+
return pos.atBottom && pos.lastDirection === null;
|
|
93480
|
+
}
|
|
93481
|
+
function scrollRowsOf(by) {
|
|
93482
|
+
if (!Number.isFinite(by))
|
|
93483
|
+
return 0;
|
|
93484
|
+
return Math.min(Math.abs(Math.trunc(by)), MAX_SCROLL_ROWS);
|
|
93485
|
+
}
|
|
93486
|
+
function planScroll(pos, req) {
|
|
93487
|
+
if ("to" in req)
|
|
93488
|
+
return { bytes: CTRL_END, events: 1, next: LIVE_BOTTOM };
|
|
93489
|
+
const n = scrollRowsOf(req.by);
|
|
93490
|
+
if (n === 0)
|
|
93491
|
+
return { bytes: "", events: 0, next: pos };
|
|
93492
|
+
const dir = req.by < 0 ? "up" : "down";
|
|
93493
|
+
const extra = pos.atBottom || pos.lastDirection !== dir ? 1 : 0;
|
|
93494
|
+
const events = n + extra;
|
|
93495
|
+
return {
|
|
93496
|
+
bytes: (dir === "up" ? WHEEL_UP : WHEEL_DOWN).repeat(events),
|
|
93497
|
+
events,
|
|
93498
|
+
next: { atBottom: false, lastDirection: dir }
|
|
93499
|
+
};
|
|
93500
|
+
}
|
|
93501
|
+
var MAX_SCROLL_ROWS = 200, WHEEL_UP = "\x1B[<64;1;1M", WHEEL_DOWN = "\x1B[<65;1;1M", CTRL_END = "\x1B[1;5F", LIVE_BOTTOM, UNKNOWN_POSITION;
|
|
93502
|
+
var init_scroll_input = __esm(() => {
|
|
93503
|
+
LIVE_BOTTOM = Object.freeze({ atBottom: true, lastDirection: null });
|
|
93504
|
+
UNKNOWN_POSITION = Object.freeze({
|
|
93505
|
+
atBottom: false,
|
|
93506
|
+
lastDirection: null
|
|
93507
|
+
});
|
|
93508
|
+
});
|
|
93509
|
+
|
|
93476
93510
|
// src/pane/session-registry.ts
|
|
93477
93511
|
import { readFileSync as readFileSync40, readdirSync as readdirSync6 } from "fs";
|
|
93478
93512
|
import { join as join48 } from "path";
|
|
@@ -94617,6 +94651,9 @@ class PaneSessionImpl {
|
|
|
94617
94651
|
resizeQuietFrom = 0;
|
|
94618
94652
|
resizeQuietUntil = 0;
|
|
94619
94653
|
activityChangedAt = 0;
|
|
94654
|
+
scrollPos = LIVE_BOTTOM;
|
|
94655
|
+
scrollEndsInFlight = 0;
|
|
94656
|
+
scrollEndConfirmedAt = 0;
|
|
94620
94657
|
gate = null;
|
|
94621
94658
|
gateTrusted = false;
|
|
94622
94659
|
gateInFlight = false;
|
|
@@ -95407,8 +95444,8 @@ ${gate}`
|
|
|
95407
95444
|
await this.pause(adm, TICK_MS);
|
|
95408
95445
|
continue;
|
|
95409
95446
|
}
|
|
95410
|
-
if (!this.
|
|
95411
|
-
await this.
|
|
95447
|
+
if (!this.trustedConnection() || this.needsAdmissionLiveBottom(adm)) {
|
|
95448
|
+
await this.connectionStep(adm);
|
|
95412
95449
|
continue;
|
|
95413
95450
|
}
|
|
95414
95451
|
if (await this.beforeSend(adm) !== "send")
|
|
@@ -95420,8 +95457,22 @@ ${gate}`
|
|
|
95420
95457
|
adm.inFlight = false;
|
|
95421
95458
|
}
|
|
95422
95459
|
}
|
|
95460
|
+
needsAdmissionLiveBottom(adm) {
|
|
95461
|
+
if (adm.liveBottomClient === this.client)
|
|
95462
|
+
return false;
|
|
95463
|
+
adm.liveBottomClient = this.client;
|
|
95464
|
+
return !isLiveBottom(this.scrollPos);
|
|
95465
|
+
}
|
|
95466
|
+
trustedConnection() {
|
|
95467
|
+
return !!this.client && !this.client.isClosed && this.gateTrusted;
|
|
95468
|
+
}
|
|
95469
|
+
connectionStep(adm) {
|
|
95470
|
+
if (!this.trustedConnection())
|
|
95471
|
+
return this.awaitTrustedClient(adm);
|
|
95472
|
+
return this.returnToLiveBottom(Math.max(0, adm.deadline - Date.now()));
|
|
95473
|
+
}
|
|
95423
95474
|
awaitTrustedClient(adm) {
|
|
95424
|
-
return this.waitFor(() => !this.admitting(adm) ||
|
|
95475
|
+
return this.waitFor(() => !this.admitting(adm) || this.trustedConnection(), Math.max(0, adm.deadline - Date.now()));
|
|
95425
95476
|
}
|
|
95426
95477
|
async attemptOnce(adm) {
|
|
95427
95478
|
const c = this.client;
|
|
@@ -95846,6 +95897,7 @@ ${screenTailOf(this.screen)}` });
|
|
|
95846
95897
|
accept() {
|
|
95847
95898
|
this.admission = null;
|
|
95848
95899
|
this.initialDelivery = false;
|
|
95900
|
+
this.scrollPos = LIVE_BOTTOM;
|
|
95849
95901
|
const now = Date.now();
|
|
95850
95902
|
this.lastChatSeenAt = now;
|
|
95851
95903
|
this.turnAcceptedAt = now;
|
|
@@ -95922,6 +95974,7 @@ ${screenTailOf(this.screen)}`
|
|
|
95922
95974
|
const d = this.currentDelivery;
|
|
95923
95975
|
this.turnSource = "screen";
|
|
95924
95976
|
this.anomaly("degraded_mode");
|
|
95977
|
+
this.returnToLiveBottom(SCROLL_REPLY_MS);
|
|
95925
95978
|
this.screenTurn = {
|
|
95926
95979
|
echo: d && d.mode !== "refused" ? d.line : "",
|
|
95927
95980
|
acceptedAt: at,
|
|
@@ -96399,11 +96452,21 @@ ${screenTailOf(this.screen)}`
|
|
|
96399
96452
|
const g = this.gateNow();
|
|
96400
96453
|
if (!g || !g.ready || g.dialog !== null || workingAboveBox(this.screen) === true)
|
|
96401
96454
|
return false;
|
|
96402
|
-
|
|
96455
|
+
let quiet = this.aboveBoxQuietMs() ?? Date.now() - this.screen.changedAt;
|
|
96456
|
+
if (this.scrollEndConfirmedAt > 0)
|
|
96457
|
+
quiet = Math.min(quiet, Date.now() - this.scrollEndConfirmedAt);
|
|
96403
96458
|
if (view.availability === "off" && quiet >= this.secondaryQuietMs)
|
|
96404
96459
|
return true;
|
|
96405
96460
|
return (!!answer || !!apiError) && quiet >= 2 * this.t("screenSettleQuietMs", SCREEN_SETTLE_QUIET_MS);
|
|
96406
96461
|
}
|
|
96462
|
+
screenViewLive() {
|
|
96463
|
+
if (this.scrollEndsInFlight > 0)
|
|
96464
|
+
return false;
|
|
96465
|
+
if (isLiveBottom(this.scrollPos))
|
|
96466
|
+
return true;
|
|
96467
|
+
this.returnToLiveBottom(SCROLL_REPLY_MS);
|
|
96468
|
+
return false;
|
|
96469
|
+
}
|
|
96407
96470
|
evalScreenTurn(view) {
|
|
96408
96471
|
if (this.witnessed(view.current)) {
|
|
96409
96472
|
this.turnSource = "transcript";
|
|
@@ -96418,7 +96481,7 @@ ${screenTailOf(this.screen)}`
|
|
|
96418
96481
|
const answer = screenAnswer(this.screen, st.echo);
|
|
96419
96482
|
const apiError = this.screenApiError(st, answer);
|
|
96420
96483
|
this.activity = "thinking";
|
|
96421
|
-
if (!this.screenSettleReady(view, answer, apiError))
|
|
96484
|
+
if (!this.screenViewLive() || !this.screenSettleReady(view, answer, apiError))
|
|
96422
96485
|
return;
|
|
96423
96486
|
this.follower.poll();
|
|
96424
96487
|
if (this.witnessed(this.follower.view().current))
|
|
@@ -96749,6 +96812,69 @@ ${screenTailOf(this.screen)}`
|
|
|
96749
96812
|
answer({ ...set, changed: true, seq: this.screen.seq });
|
|
96750
96813
|
}
|
|
96751
96814
|
}
|
|
96815
|
+
async scroll(req) {
|
|
96816
|
+
const refusal = this.scrollRefusal();
|
|
96817
|
+
if (refusal)
|
|
96818
|
+
throw new ContractErrorException("scroll_unavailable", refusal);
|
|
96819
|
+
const c = this.client;
|
|
96820
|
+
if (!c || c.isClosed)
|
|
96821
|
+
throw new ContractErrorException("scroll_unavailable", "the pane's socket is reconnecting");
|
|
96822
|
+
const plan = planScroll(this.scrollPos, req);
|
|
96823
|
+
if (plan.bytes === "")
|
|
96824
|
+
return { seq: this.screen.seq };
|
|
96825
|
+
this.scrollPos = plan.next;
|
|
96826
|
+
const seq0 = this.screen.seq;
|
|
96827
|
+
this.extendResizeQuiet();
|
|
96828
|
+
const r = await c.request({ type: "send", pane: 0, text: plan.bytes, enter: false, noGate: true }, SCROLL_REPLY_MS);
|
|
96829
|
+
if (!r.ok) {
|
|
96830
|
+
this.scrollPos = UNKNOWN_POSITION;
|
|
96831
|
+
if (r.code === "client_lost" || r.code === "client_timeout")
|
|
96832
|
+
this.extendResizeQuiet();
|
|
96833
|
+
throw new ContractErrorException("scroll_unavailable", `magmux did not confirm the scroll: ${r.code}${r.error ? `: ${r.error}` : ""}`);
|
|
96834
|
+
}
|
|
96835
|
+
this.extendResizeQuiet();
|
|
96836
|
+
await this.waitFor(() => isTerminalPhase(this.phase) || this.screen.seq !== seq0, this.t("scrollScreenMs", SCROLL_SCREEN_MS));
|
|
96837
|
+
return { seq: this.screen.seq };
|
|
96838
|
+
}
|
|
96839
|
+
screenTurnActive() {
|
|
96840
|
+
if (this.turnSource !== "screen")
|
|
96841
|
+
return false;
|
|
96842
|
+
return this.phase === "RUNNING" || this.phase === "QUESTION" || this.phase === "PERMISSION";
|
|
96843
|
+
}
|
|
96844
|
+
scrollRefusal() {
|
|
96845
|
+
if (isTerminalPhase(this.phase) || this.final)
|
|
96846
|
+
return "the session has finished";
|
|
96847
|
+
if (this.phase === "BOOTING" || this.screen.seq === 0)
|
|
96848
|
+
return "the session is still starting";
|
|
96849
|
+
if (this.phase === "ADMITTING")
|
|
96850
|
+
return "a prompt is being delivered to the session";
|
|
96851
|
+
if (this.screenTurnActive())
|
|
96852
|
+
return "the session's turn is being read from its screen (no transcript)";
|
|
96853
|
+
if (!this.client || this.client.isClosed || !this.gateTrusted)
|
|
96854
|
+
return "the pane's socket is reconnecting";
|
|
96855
|
+
return null;
|
|
96856
|
+
}
|
|
96857
|
+
async returnToLiveBottom(ms) {
|
|
96858
|
+
if (isLiveBottom(this.scrollPos))
|
|
96859
|
+
return;
|
|
96860
|
+
const c = this.client;
|
|
96861
|
+
if (!c || c.isClosed || !this.gateTrusted || ms <= 0)
|
|
96862
|
+
return;
|
|
96863
|
+
this.scrollPos = LIVE_BOTTOM;
|
|
96864
|
+
this.extendResizeQuiet();
|
|
96865
|
+
this.scrollEndsInFlight++;
|
|
96866
|
+
try {
|
|
96867
|
+
const r = await c.request({ type: "send", pane: 0, text: CTRL_END, enter: false, noGate: true }, Math.min(SCROLL_REPLY_MS, ms));
|
|
96868
|
+
if (r.ok) {
|
|
96869
|
+
this.scrollEndConfirmedAt = Date.now();
|
|
96870
|
+
} else {
|
|
96871
|
+
this.scrollPos = UNKNOWN_POSITION;
|
|
96872
|
+
this.anomaly(`scroll_end_unconfirmed:${r.code}`);
|
|
96873
|
+
}
|
|
96874
|
+
} finally {
|
|
96875
|
+
this.scrollEndsInFlight--;
|
|
96876
|
+
}
|
|
96877
|
+
}
|
|
96752
96878
|
turnAnswer(index) {
|
|
96753
96879
|
const done = this.answers.get(index);
|
|
96754
96880
|
if (done !== undefined)
|
|
@@ -97001,7 +97127,8 @@ function newAdmission(a) {
|
|
|
97001
97127
|
clearAfter: 0,
|
|
97002
97128
|
lostType: null,
|
|
97003
97129
|
last: null,
|
|
97004
|
-
noted: new Set
|
|
97130
|
+
noted: new Set,
|
|
97131
|
+
liveBottomClient: null
|
|
97005
97132
|
};
|
|
97006
97133
|
}
|
|
97007
97134
|
function markClearFirst(adm) {
|
|
@@ -97168,7 +97295,7 @@ async function startPaneSession(o) {
|
|
|
97168
97295
|
await session.start({ magmuxBinary: magmux.binary, env: p.magmuxEnv, cwd: p.cwd });
|
|
97169
97296
|
return session;
|
|
97170
97297
|
}
|
|
97171
|
-
var BOOT_TIMEOUT_MS = 90000, ADMIT_TIMEOUT_MS = 30000, DEGRADED_ENTRY_MS = 1e4, SCREEN_SETTLE_QUIET_MS = 3000, PANEL_SETTLE_MS = 500, SEND_CLIENT_SLACK_MS = 2000, MIN_READY_TIMEOUT_MS = 1000, BUSY_RETRY_MS = 250, MISMATCH_SETTLE_MS = 1000, CLEAR_WAIT_MS = 1000, BOOT_STATIC_MS = 5000, TICK_MS = 250, WATCH_FPS = 4, POLL_BACKSTOP_MS = 1000, TURN_END_MISSING_FACTOR = 3, RESIZE_REPLY_MS = 1e4, RESIZE_SCREEN_MS = 1000, GATE_POLL_MS = 250, GATE_REPLY_MS = 1000, GATE_STALE_MS2 = 1000, GATE_GRACE_MS, GATE_DEAD_BACKSTOP_MS = 1000, EXIT_CODE_QUERY_MS = 2000, EXIT_CODE_RETRY_MS = 50, GATE_KINDLESS_REASONS, RUNTIME_DIALOG_KINDS, REGISTRY_READ_MS = 250, REGISTRY_DISAGREE_MS = 2000, REGISTRY_GATE_BOOT_DISAGREE_MS = 5000, REGISTRY_MISSING_MS = 5000, MAX_ANOMALY_KEYS = 64, KEPT_ANSWERS = 4, DEFAULT_MAX_CONSECUTIVE = 3, EVENT_REASON, VERDICT_EVENT, PaneStartError;
|
|
97298
|
+
var BOOT_TIMEOUT_MS = 90000, ADMIT_TIMEOUT_MS = 30000, DEGRADED_ENTRY_MS = 1e4, SCREEN_SETTLE_QUIET_MS = 3000, PANEL_SETTLE_MS = 500, SEND_CLIENT_SLACK_MS = 2000, MIN_READY_TIMEOUT_MS = 1000, BUSY_RETRY_MS = 250, MISMATCH_SETTLE_MS = 1000, CLEAR_WAIT_MS = 1000, BOOT_STATIC_MS = 5000, TICK_MS = 250, WATCH_FPS = 4, POLL_BACKSTOP_MS = 1000, TURN_END_MISSING_FACTOR = 3, RESIZE_REPLY_MS = 1e4, RESIZE_SCREEN_MS = 1000, SCROLL_REPLY_MS = 5000, SCROLL_SCREEN_MS = 1000, GATE_POLL_MS = 250, GATE_REPLY_MS = 1000, GATE_STALE_MS2 = 1000, GATE_GRACE_MS, GATE_DEAD_BACKSTOP_MS = 1000, EXIT_CODE_QUERY_MS = 2000, EXIT_CODE_RETRY_MS = 50, GATE_KINDLESS_REASONS, RUNTIME_DIALOG_KINDS, REGISTRY_READ_MS = 250, REGISTRY_DISAGREE_MS = 2000, REGISTRY_GATE_BOOT_DISAGREE_MS = 5000, REGISTRY_MISSING_MS = 5000, MAX_ANOMALY_KEYS = 64, KEPT_ANSWERS = 4, DEFAULT_MAX_CONSECUTIVE = 3, EVENT_REASON, VERDICT_EVENT, PaneStartError;
|
|
97172
97299
|
var init_pane_session = __esm(() => {
|
|
97173
97300
|
init_queue();
|
|
97174
97301
|
init_parent_session();
|
|
@@ -97185,6 +97312,7 @@ var init_pane_session = __esm(() => {
|
|
|
97185
97312
|
init_prompt_delivery();
|
|
97186
97313
|
init_screen_classifier();
|
|
97187
97314
|
init_screen_model();
|
|
97315
|
+
init_scroll_input();
|
|
97188
97316
|
init_session_registry();
|
|
97189
97317
|
init_settle_rule();
|
|
97190
97318
|
init_slot_state();
|
|
@@ -97239,6 +97367,7 @@ var init_pane = __esm(() => {
|
|
|
97239
97367
|
init_pane_launch();
|
|
97240
97368
|
init_pane_registry();
|
|
97241
97369
|
init_pane_session();
|
|
97370
|
+
init_scroll_input();
|
|
97242
97371
|
init_contract();
|
|
97243
97372
|
});
|
|
97244
97373
|
|
|
@@ -97967,6 +98096,20 @@ async function resizeTeamSlot(path, slot, cols, rows, runId) {
|
|
|
97967
98096
|
return { ...where, cols: DEFAULT_PANE_COLS, rows: DEFAULT_PANE_ROWS, changed: false, seq: 0 };
|
|
97968
98097
|
return { ...where, ...await e.session.resize(cols, rows) };
|
|
97969
98098
|
}
|
|
98099
|
+
async function scrollTeamSlot(path, slot, req, runId) {
|
|
98100
|
+
const run = addressRun(path, runId);
|
|
98101
|
+
if (!run)
|
|
98102
|
+
throw unknownRun(path, runId);
|
|
98103
|
+
const e = run.slots.get(slot);
|
|
98104
|
+
if (!e)
|
|
98105
|
+
throw new ContractErrorException("unknown_slot", `run ${run.runId} has no slot ${JSON.stringify(slot)}`);
|
|
98106
|
+
if (!e.session) {
|
|
98107
|
+
const state = run.status.models[e.id]?.state;
|
|
98108
|
+
throw new ContractErrorException("scroll_unavailable", state === undefined || isTerminalState(state) ? "the slot has finished" : "the slot is still starting");
|
|
98109
|
+
}
|
|
98110
|
+
const r = await e.session.scroll(req);
|
|
98111
|
+
return { run_id: run.runId, path: run.path, slot, seq: r.seq };
|
|
98112
|
+
}
|
|
97970
98113
|
function teamLiveMaps(path, runId) {
|
|
97971
98114
|
const run = addressRun(path, runId);
|
|
97972
98115
|
if (!run)
|
|
@@ -100007,6 +100150,15 @@ class SessionManager {
|
|
|
100007
100150
|
const r = await entry.session.resize(cols, rows);
|
|
100008
100151
|
return { session_id: sessionId, ...r };
|
|
100009
100152
|
}
|
|
100153
|
+
async scrollSession(sessionId, req) {
|
|
100154
|
+
const entry = this.liveEntry(sessionId);
|
|
100155
|
+
if (!entry)
|
|
100156
|
+
throw unknownSession(sessionId);
|
|
100157
|
+
if (!entry.session)
|
|
100158
|
+
throw new ContractErrorException("scroll_unavailable", isTerminalState(entry.info.state) ? "the session has finished" : "the session is still starting");
|
|
100159
|
+
const r = await entry.session.scroll(req);
|
|
100160
|
+
return { session_id: sessionId, seq: r.seq };
|
|
100161
|
+
}
|
|
100010
100162
|
captureSession(sessionId, sinceSeq, spans) {
|
|
100011
100163
|
const entry = this.liveEntry(sessionId);
|
|
100012
100164
|
if (!entry)
|
|
@@ -100442,7 +100594,7 @@ var NESTED_MCP_TOOL_NAMES, NESTED_INSTRUCTIONS = `Claudish MCP server provides a
|
|
|
100442
100594
|
|
|
100443
100595
|
## Nested mode \u2014 no sessions
|
|
100444
100596
|
|
|
100445
|
-
This server runs inside a session that a claudish MCP server started (CLAUDISH_PANE_CHILD=1). One level of nesting is the limit, so these tools are not registered: create_session, team, send_input, get_output, cancel_session, list_sessions, get_diagnostics, capture_session, preflight, report_error. No channel notifications are sent.
|
|
100597
|
+
This server runs inside a session that a claudish MCP server started (CLAUDISH_PANE_CHILD=1). One level of nesting is the limit, so these tools are not registered: create_session, team, send_input, get_output, cancel_session, list_sessions, get_diagnostics, capture_session, resize_session, scroll_session, preflight, report_error. No channel notifications are sent.
|
|
100446
100598
|
|
|
100447
100599
|
- run_prompt or compare_models: a one-shot answer from another model.
|
|
100448
100600
|
- list_models, search_models: find a model.
|
|
@@ -101539,6 +101691,22 @@ function requiredInteger(raw, name, mode) {
|
|
|
101539
101691
|
throw new ContractErrorException("invalid_args", `'${name}' is required${mode ? ` for mode '${mode}'` : ""}`);
|
|
101540
101692
|
return n;
|
|
101541
101693
|
}
|
|
101694
|
+
function scrollArgs(args) {
|
|
101695
|
+
const by = optionalInteger(args.by, "by");
|
|
101696
|
+
const to = args.to;
|
|
101697
|
+
if (to !== undefined && to !== null && to !== "end")
|
|
101698
|
+
throw new ContractErrorException("invalid_args", `'to' must be "end"`);
|
|
101699
|
+
const hasTo = to === "end";
|
|
101700
|
+
if (by !== undefined && hasTo)
|
|
101701
|
+
throw new ContractErrorException("invalid_args", "give exactly one of 'by' and 'to', not both");
|
|
101702
|
+
if (hasTo)
|
|
101703
|
+
return { to: "end" };
|
|
101704
|
+
if (by === undefined)
|
|
101705
|
+
throw new ContractErrorException("invalid_args", "one of 'by' and 'to' is required");
|
|
101706
|
+
if (by === 0)
|
|
101707
|
+
throw new ContractErrorException("invalid_args", "'by' must not be 0");
|
|
101708
|
+
return { by };
|
|
101709
|
+
}
|
|
101542
101710
|
function teamStatusAnswer(path, runId) {
|
|
101543
101711
|
let row = null;
|
|
101544
101712
|
if (runId) {
|
|
@@ -101583,6 +101751,7 @@ async function teamContractVerb(mode, args) {
|
|
|
101583
101751
|
const spans = optionalBoolean(args.spans, "spans");
|
|
101584
101752
|
const cols = mode === "resize" ? requiredInteger(args.cols, "cols", mode) : undefined;
|
|
101585
101753
|
const rows = mode === "resize" ? requiredInteger(args.rows, "rows", mode) : undefined;
|
|
101754
|
+
const scroll = mode === "scroll" ? scrollArgs(args) : undefined;
|
|
101586
101755
|
switch (mode) {
|
|
101587
101756
|
case "status":
|
|
101588
101757
|
return contractAnswer(teamStatusAnswer(path, runId));
|
|
@@ -101598,6 +101767,11 @@ async function teamContractVerb(mode, args) {
|
|
|
101598
101767
|
throw new ContractErrorException("invalid_args", "'slot' is required for mode 'resize'");
|
|
101599
101768
|
return contractAnswer(await resizeTeamSlot(path, slot, cols, rows, runId));
|
|
101600
101769
|
}
|
|
101770
|
+
case "scroll": {
|
|
101771
|
+
if (slot === undefined)
|
|
101772
|
+
throw new ContractErrorException("invalid_args", "'slot' is required for mode 'scroll'");
|
|
101773
|
+
return contractAnswer(await scrollTeamSlot(path, slot, scroll, runId));
|
|
101774
|
+
}
|
|
101601
101775
|
}
|
|
101602
101776
|
} catch (e) {
|
|
101603
101777
|
return contractErrorAnswer(e);
|
|
@@ -101945,18 +102119,28 @@ This searched OpenRouter's listing only. Subscription wire ids and catalog alias
|
|
|
101945
102119
|
});
|
|
101946
102120
|
tools.push({
|
|
101947
102121
|
name: "team",
|
|
101948
|
-
description: "Run AI models on a task with anonymized outputs and optional blind judging. Every slot is an interactive Claude Code session in its own headless magmux pane. " + "Modes: 'run' (START the models and return once each has taken its prompt \u2014 it does " + "NOT wait for answers), 'status' (per-slot state; read `run.slots`), 'list' (every run this server holds), 'capture' (one slot's screen), 'resize' (set one slot's terminal size), 'cancel' (stop one slot or the whole run), 'judge'(blind-vote on existing outputs), 'run-and-judge' (the blocking pipeline). Poll 'status' (read run.slots) or 'list'. NO SLOT IS EVER KILLED ON A TIMER once its prompt is accepted. A team slot is a full Claude Code session and may work for a long time; a slot inside a build or test suite emits nothing for minutes and is working, not stuck. Judge the silence against the task you set, and use 'cancel' if you decide a slot is wedged. A slot that stops on a question it cannot answer is FAILED 'blocked' (team has no way to answer it). JUDGING A WORKING SLOT: read live_output_bytes_by_slot, idle_seconds_by_slot and activity_by_slot (or each row's idle_seconds and " + "activity). Do NOT use 'outputSize' \u2014 it is the size of the final answer, written " + "only when a slot finishes, so it is 0 for every working slot no matter how much that slot has produced.",
|
|
102122
|
+
description: "Run AI models on a task with anonymized outputs and optional blind judging. Every slot is an interactive Claude Code session in its own headless magmux pane. " + "Modes: 'run' (START the models and return once each has taken its prompt \u2014 it does " + "NOT wait for answers), 'status' (per-slot state; read `run.slots`), 'list' (every run this server holds), 'capture' (one slot's screen), 'resize' (set one slot's terminal size), 'scroll' (scroll one slot's transcript view), 'cancel' (stop one slot or the whole run), 'judge'(blind-vote on existing outputs), 'run-and-judge' (the blocking pipeline). Poll 'status' (read run.slots) or 'list'. NO SLOT IS EVER KILLED ON A TIMER once its prompt is accepted. A team slot is a full Claude Code session and may work for a long time; a slot inside a build or test suite emits nothing for minutes and is working, not stuck. Judge the silence against the task you set, and use 'cancel' if you decide a slot is wedged. A slot that stops on a question it cannot answer is FAILED 'blocked' (team has no way to answer it). JUDGING A WORKING SLOT: read live_output_bytes_by_slot, idle_seconds_by_slot and activity_by_slot (or each row's idle_seconds and " + "activity). Do NOT use 'outputSize' \u2014 it is the size of the final answer, written " + "only when a slot finishes, so it is 0 for every working slot no matter how much that slot has produced.",
|
|
101949
102123
|
inputSchema: {
|
|
101950
102124
|
type: "object",
|
|
101951
102125
|
properties: {
|
|
101952
102126
|
mode: {
|
|
101953
102127
|
type: "string",
|
|
101954
|
-
enum: [
|
|
101955
|
-
|
|
102128
|
+
enum: [
|
|
102129
|
+
"run",
|
|
102130
|
+
"judge",
|
|
102131
|
+
"run-and-judge",
|
|
102132
|
+
"status",
|
|
102133
|
+
"cancel",
|
|
102134
|
+
"list",
|
|
102135
|
+
"capture",
|
|
102136
|
+
"resize",
|
|
102137
|
+
"scroll"
|
|
102138
|
+
],
|
|
102139
|
+
description: "Operation mode. 'run' STARTS the models and returns once every slot has taken " + "its prompt (or failed to) \u2014 it does not wait for answers. Poll 'status' (read " + "`run.slots`) or 'list' for progress, then 'judge' once the slots have finished. 'capture' returns one slot's screen; 'resize' sets its terminal size; 'scroll' scrolls its transcript view (`by` or `to`). 'run-and-judge' is the blocking pipeline and holds the call open for the whole run. 'cancel' stops one slot or the whole run."
|
|
101956
102140
|
},
|
|
101957
102141
|
slot: {
|
|
101958
102142
|
type: "string",
|
|
101959
|
-
description: "For 'cancel', 'capture' and '
|
|
102143
|
+
description: "For 'cancel', 'capture', 'resize' and 'scroll': the anonymised slot id (e.g. '02') from the slot map 'run' returned. 'capture', 'resize' and 'scroll' require it; for 'cancel', omit it to stop every slot in the run."
|
|
101960
102144
|
},
|
|
101961
102145
|
path: {
|
|
101962
102146
|
type: "string",
|
|
@@ -101964,7 +102148,7 @@ This searched OpenRouter's listing only. Subscription wire ids and catalog alias
|
|
|
101964
102148
|
},
|
|
101965
102149
|
run_id: {
|
|
101966
102150
|
type: "string",
|
|
101967
|
-
description: "For 'status', 'cancel', 'capture' and '
|
|
102151
|
+
description: "For 'status', 'cancel', 'capture', 'resize' and 'scroll': the run_id a 'run' answer returned. It addresses THAT run while this server retains it, even after a newer run reused the path. Omit it to address the newest run at 'path'."
|
|
101968
102152
|
},
|
|
101969
102153
|
since_seq: {
|
|
101970
102154
|
type: "number",
|
|
@@ -101982,6 +102166,15 @@ This searched OpenRouter's listing only. Subscription wire ids and catalog alias
|
|
|
101982
102166
|
type: "integer",
|
|
101983
102167
|
description: "For 'resize' (required): the terminal height in rows, clamped to 24\u20139999 and to " + "at most 4 000 000 cells (cols \xD7 rows)."
|
|
101984
102168
|
},
|
|
102169
|
+
by: {
|
|
102170
|
+
type: "integer",
|
|
102171
|
+
description: "For 'scroll': rows to scroll the slot's transcript view; negative = toward older " + `content, positive = toward the live bottom. A target, \xB1 1 row; |by| is capped at ${MAX_SCROLL_ROWS}. Not 0. Give exactly one of 'by' and 'to'. The answer {run_id, path, slot, seq} carries the screen's seq after the scroll.`
|
|
102172
|
+
},
|
|
102173
|
+
to: {
|
|
102174
|
+
type: "string",
|
|
102175
|
+
enum: ["end"],
|
|
102176
|
+
description: `For 'scroll': "end" returns the slot's view to the live bottom. Give exactly one of 'by' and 'to'.`
|
|
102177
|
+
},
|
|
101985
102178
|
models: {
|
|
101986
102179
|
type: "array",
|
|
101987
102180
|
items: { type: "string" },
|
|
@@ -102038,7 +102231,7 @@ This searched OpenRouter's listing only. Subscription wire ids and catalog alias
|
|
|
102038
102231
|
heartbeat: true,
|
|
102039
102232
|
handler: async (args, ctx) => {
|
|
102040
102233
|
const mode = args.mode;
|
|
102041
|
-
if (mode === "list" || mode === "status" || mode === "cancel" || mode === "capture" || mode === "resize") {
|
|
102234
|
+
if (mode === "list" || mode === "status" || mode === "cancel" || mode === "capture" || mode === "resize" || mode === "scroll") {
|
|
102042
102235
|
return teamContractVerb(mode, args);
|
|
102043
102236
|
}
|
|
102044
102237
|
try {
|
|
@@ -102739,6 +102932,38 @@ Report manually at https://github.com/anthropics/claudish/issues${autoSendHint}`
|
|
|
102739
102932
|
}
|
|
102740
102933
|
}
|
|
102741
102934
|
});
|
|
102935
|
+
tools.push({
|
|
102936
|
+
name: "scroll_session",
|
|
102937
|
+
description: "Scroll a session's transcript view, as a mouse wheel over the child's Claude Code would: the next capture_session shows the child scrolled. `by` is a signed row count (negative = toward older content, positive = toward the live bottom); it is a " + `target, \xB1 1 row, and |by| is capped at ${MAX_SCROLL_ROWS} per call. \`to:"end"\` returns to the live bottom. Give exactly one of \`by\` and \`to\`; \`by:0\` is refused. It never types into the prompt, submits or answers a dialog, and a prompt claudish delivers next returns the view to the bottom first. The answer {session_id, seq} carries the screen's seq after the scroll (the first changed screen within 1 s, else the current one). Errors are a JSON ContractError: \`scroll_unavailable\` while the session is still starting, finished, delivering a prompt, reading its turn from the screen or reconnecting, or when magmux did not confirm the send (the scroll may still land). Nothing is queued.`,
|
|
102938
|
+
inputSchema: {
|
|
102939
|
+
type: "object",
|
|
102940
|
+
properties: {
|
|
102941
|
+
session_id: { type: "string", description: "Session ID from create_session" },
|
|
102942
|
+
by: {
|
|
102943
|
+
type: "integer",
|
|
102944
|
+
description: `Rows to scroll; negative = older content. Not 0; |by| capped at ${MAX_SCROLL_ROWS}; a target, \xB1 1 row`
|
|
102945
|
+
},
|
|
102946
|
+
to: {
|
|
102947
|
+
type: "string",
|
|
102948
|
+
enum: ["end"],
|
|
102949
|
+
description: '"end" returns the view to the live bottom'
|
|
102950
|
+
}
|
|
102951
|
+
},
|
|
102952
|
+
required: ["session_id"]
|
|
102953
|
+
},
|
|
102954
|
+
group: "session",
|
|
102955
|
+
handler: async (args) => {
|
|
102956
|
+
try {
|
|
102957
|
+
const sessionId = optionalString(args.session_id, "session_id");
|
|
102958
|
+
if (sessionId === undefined)
|
|
102959
|
+
throw new ContractErrorException("invalid_args", "'session_id' is required");
|
|
102960
|
+
const req = scrollArgs(args);
|
|
102961
|
+
return contractAnswer(await sessionManager.scrollSession(sessionId, req));
|
|
102962
|
+
} catch (e) {
|
|
102963
|
+
return contractErrorAnswer(e);
|
|
102964
|
+
}
|
|
102965
|
+
}
|
|
102966
|
+
});
|
|
102742
102967
|
return tools;
|
|
102743
102968
|
}
|
|
102744
102969
|
function resolveToolGroups(mode) {
|
|
@@ -102882,7 +103107,7 @@ Each session is an interactive Claude Code running the model in its own headless
|
|
|
102882
103107
|
3. send_input may be called at any time; it is queued until the session is idle.
|
|
102883
103108
|
4. COMPLETED: call get_output for the answer.
|
|
102884
103109
|
5. FAILED or TIMEOUT \u2014 or COMPLETED with an empty or surprising answer \u2014 call get_diagnostics. It returns the child's final screen, the upstream error bodies, the recent state records, the resolved model chain and the paths to the full records. It needs no re-run and no debug flag.
|
|
102885
|
-
6. capture_session reads a session's current screen; resize_session sets its terminal size (any time, mid-turn included); cancel_session stops it. Nothing stops a session for being idle: a child inside a long build is silent and working.
|
|
103110
|
+
6. capture_session reads a session's current screen; resize_session sets its terminal size (any time, mid-turn included); scroll_session scrolls the child's transcript view (by rows, or back to the live bottom); cancel_session stops it. Nothing stops a session for being idle: a child inside a long build is silent and working.
|
|
102886
103111
|
|
|
102887
103112
|
The session_id from create_session is the key for every session tool.`, proxyInstance = null, proxyStarting = null, SCORE_TIE_EPSILON = 0.000001, NEXT_STEP, KEEP_EFFORT = "default", USER_REQUIRED_MODEL_DESCRIPTION, sanitize2, TOOL_MODES;
|
|
102888
103113
|
var init_mcp_server = __esm(() => {
|
|
@@ -116084,6 +116309,7 @@ function buildClaudishSettingsOverlay(statusLine, proxyAuthMode, paneChild = isP
|
|
|
116084
116309
|
if (paneChild) {
|
|
116085
116310
|
settings.skipDangerousModePermissionPrompt = true;
|
|
116086
116311
|
settings.editorMode = "normal";
|
|
116312
|
+
settings.wheelScrollAccelerationEnabled = false;
|
|
116087
116313
|
}
|
|
116088
116314
|
return settings;
|
|
116089
116315
|
}
|
|
@@ -116108,6 +116334,9 @@ function mergeUserSettingsIfPresent(config, tempSettingsPath, statusLine, proxyA
|
|
|
116108
116334
|
if (paneChild) {
|
|
116109
116335
|
userSettings.editorMode = "normal";
|
|
116110
116336
|
}
|
|
116337
|
+
if (paneChild) {
|
|
116338
|
+
userSettings.wheelScrollAccelerationEnabled = false;
|
|
116339
|
+
}
|
|
116111
116340
|
chmodSync5(tempSettingsPath, 384);
|
|
116112
116341
|
writeFileSync27(tempSettingsPath, JSON.stringify(userSettings, null, 2), {
|
|
116113
116342
|
encoding: "utf-8",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claudish",
|
|
3
|
-
"version": "14.
|
|
3
|
+
"version": "14.12.0",
|
|
4
4
|
"description": "Run Claude Code with any model - OpenRouter, Ollama, LM Studio & local models",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -71,10 +71,10 @@
|
|
|
71
71
|
"ai"
|
|
72
72
|
],
|
|
73
73
|
"optionalDependencies": {
|
|
74
|
-
"@claudish/magmux-darwin-arm64": "14.
|
|
75
|
-
"@claudish/magmux-darwin-x64": "14.
|
|
76
|
-
"@claudish/magmux-linux-arm64": "14.
|
|
77
|
-
"@claudish/magmux-linux-x64": "14.
|
|
74
|
+
"@claudish/magmux-darwin-arm64": "14.12.0",
|
|
75
|
+
"@claudish/magmux-darwin-x64": "14.12.0",
|
|
76
|
+
"@claudish/magmux-linux-arm64": "14.12.0",
|
|
77
|
+
"@claudish/magmux-linux-x64": "14.12.0"
|
|
78
78
|
},
|
|
79
79
|
"author": "Jack Rudenko <i@madappgang.com>",
|
|
80
80
|
"license": "MIT",
|