hammoc 1.7.2 → 1.8.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/README.md +2 -2
- package/package.json +1 -1
- package/packages/client/dist/assets/{agentExampleHighlight-De1SlBXH.js → agentExampleHighlight-B2IJxYzN.js} +1 -1
- package/packages/client/dist/assets/{commandTokenHighlight-COQi5YnH.js → commandTokenHighlight-BdGXkDR5.js} +1 -1
- package/packages/client/dist/assets/{index-D8XvfvVS.js → index-BFWGN51V.js} +1 -1
- package/packages/client/dist/assets/index-BX7y_Qt3.css +32 -0
- package/packages/client/dist/assets/{index-CQgqPGOG.js → index-lmx-m-iP.js} +552 -478
- package/packages/client/dist/assets/{snippetTokenHighlight-jMAHlrFd.js → snippetTokenHighlight-B9bH9Npx.js} +1 -1
- package/packages/client/dist/index.html +2 -2
- package/packages/client/dist/sw.js +1 -1
- package/packages/server/dist/app.d.ts.map +1 -1
- package/packages/server/dist/app.js +25 -1
- package/packages/server/dist/app.js.map +1 -1
- package/packages/server/dist/config/index.d.ts +10 -0
- package/packages/server/dist/config/index.d.ts.map +1 -1
- package/packages/server/dist/config/index.js +10 -0
- package/packages/server/dist/config/index.js.map +1 -1
- package/packages/server/dist/controllers/accountController.d.ts +19 -0
- package/packages/server/dist/controllers/accountController.d.ts.map +1 -0
- package/packages/server/dist/controllers/accountController.js +92 -0
- package/packages/server/dist/controllers/accountController.js.map +1 -0
- package/packages/server/dist/controllers/projectController.d.ts +1 -1
- package/packages/server/dist/controllers/projectController.d.ts.map +1 -1
- package/packages/server/dist/controllers/projectController.js +19 -5
- package/packages/server/dist/controllers/projectController.js.map +1 -1
- package/packages/server/dist/controllers/serverController.d.ts.map +1 -1
- package/packages/server/dist/controllers/serverController.js +8 -1
- package/packages/server/dist/controllers/serverController.js.map +1 -1
- package/packages/server/dist/handlers/websocket.d.ts.map +1 -1
- package/packages/server/dist/handlers/websocket.js +83 -6
- package/packages/server/dist/handlers/websocket.js.map +1 -1
- package/packages/server/dist/index.js +8 -0
- package/packages/server/dist/index.js.map +1 -1
- package/packages/server/dist/middleware/session.d.ts.map +1 -1
- package/packages/server/dist/middleware/session.js +7 -5
- package/packages/server/dist/middleware/session.js.map +1 -1
- package/packages/server/dist/routes/accounts.d.ts +13 -0
- package/packages/server/dist/routes/accounts.d.ts.map +1 -0
- package/packages/server/dist/routes/accounts.js +18 -0
- package/packages/server/dist/routes/accounts.js.map +1 -0
- package/packages/server/dist/routes/config.d.ts +3 -0
- package/packages/server/dist/routes/config.d.ts.map +1 -0
- package/packages/server/dist/routes/config.js +59 -0
- package/packages/server/dist/routes/config.js.map +1 -0
- package/packages/server/dist/routes/preferences.d.ts.map +1 -1
- package/packages/server/dist/routes/preferences.js +22 -4
- package/packages/server/dist/routes/preferences.js.map +1 -1
- package/packages/server/dist/services/accountInfoService.js +6 -6
- package/packages/server/dist/services/accountInfoService.js.map +1 -1
- package/packages/server/dist/services/accountStorageService.d.ts +119 -0
- package/packages/server/dist/services/accountStorageService.d.ts.map +1 -0
- package/packages/server/dist/services/accountStorageService.js +285 -0
- package/packages/server/dist/services/accountStorageService.js.map +1 -0
- package/packages/server/dist/services/chatService.d.ts.map +1 -1
- package/packages/server/dist/services/chatService.js +4 -8
- package/packages/server/dist/services/chatService.js.map +1 -1
- package/packages/server/dist/services/claudeLoginService.d.ts +124 -0
- package/packages/server/dist/services/claudeLoginService.d.ts.map +1 -0
- package/packages/server/dist/services/claudeLoginService.js +417 -0
- package/packages/server/dist/services/claudeLoginService.js.map +1 -0
- package/packages/server/dist/services/cliChatEngine.d.ts.map +1 -1
- package/packages/server/dist/services/cliChatEngine.js +85 -33
- package/packages/server/dist/services/cliChatEngine.js.map +1 -1
- package/packages/server/dist/services/cliGridCards.d.ts +30 -10
- package/packages/server/dist/services/cliGridCards.d.ts.map +1 -1
- package/packages/server/dist/services/cliGridCards.js +80 -34
- package/packages/server/dist/services/cliGridCards.js.map +1 -1
- package/packages/server/dist/services/cliModalDetect.d.ts +15 -7
- package/packages/server/dist/services/cliModalDetect.d.ts.map +1 -1
- package/packages/server/dist/services/cliModalDetect.js +16 -12
- package/packages/server/dist/services/cliModalDetect.js.map +1 -1
- package/packages/server/dist/services/cliScreenModel.d.ts +3 -2
- package/packages/server/dist/services/cliScreenModel.d.ts.map +1 -1
- package/packages/server/dist/services/cliScreenModel.js +2 -0
- package/packages/server/dist/services/cliScreenModel.js.map +1 -1
- package/packages/server/dist/services/cliService.d.ts.map +1 -1
- package/packages/server/dist/services/cliService.js +2 -7
- package/packages/server/dist/services/cliService.js.map +1 -1
- package/packages/server/dist/services/cliSessionPool.d.ts +13 -1
- package/packages/server/dist/services/cliSessionPool.d.ts.map +1 -1
- package/packages/server/dist/services/cliSessionPool.js +14 -4
- package/packages/server/dist/services/cliSessionPool.js.map +1 -1
- package/packages/server/dist/services/preferencesService.d.ts +1 -0
- package/packages/server/dist/services/preferencesService.d.ts.map +1 -1
- package/packages/server/dist/services/preferencesService.js +25 -2
- package/packages/server/dist/services/preferencesService.js.map +1 -1
- package/packages/server/dist/services/queueService.d.ts +1 -0
- package/packages/server/dist/services/queueService.d.ts.map +1 -1
- package/packages/server/dist/services/queueService.js +3 -0
- package/packages/server/dist/services/queueService.js.map +1 -1
- package/packages/server/dist/services/rateLimitProbeService.d.ts +7 -0
- package/packages/server/dist/services/rateLimitProbeService.d.ts.map +1 -1
- package/packages/server/dist/services/rateLimitProbeService.js +10 -0
- package/packages/server/dist/services/rateLimitProbeService.js.map +1 -1
- package/packages/server/dist/services/workspaceContext.d.ts +29 -3
- package/packages/server/dist/services/workspaceContext.d.ts.map +1 -1
- package/packages/server/dist/services/workspaceContext.js +100 -8
- package/packages/server/dist/services/workspaceContext.js.map +1 -1
- package/packages/server/dist/snippets/promote-to-story +86 -9
- package/packages/server/dist/utils/cliDebugLog.d.ts +6 -5
- package/packages/server/dist/utils/cliDebugLog.d.ts.map +1 -1
- package/packages/server/dist/utils/cliDebugLog.js +11 -8
- package/packages/server/dist/utils/cliDebugLog.js.map +1 -1
- package/packages/server/dist/utils/logger.d.ts +7 -0
- package/packages/server/dist/utils/logger.d.ts.map +1 -1
- package/packages/server/dist/utils/logger.js +13 -1
- package/packages/server/dist/utils/logger.js.map +1 -1
- package/packages/server/resources/manual/01-getting-started.md +11 -5
- package/packages/server/resources/manual/03-sessions.md +1 -0
- package/packages/server/resources/manual/04-slash-commands-favorites.md +1 -0
- package/packages/server/resources/manual/13-settings.md +34 -10
- package/packages/server/resources/manual/16-troubleshooting.md +4 -7
- package/packages/shared/dist/index.d.ts +2 -1
- package/packages/shared/dist/index.d.ts.map +1 -1
- package/packages/shared/dist/index.js +2 -0
- package/packages/shared/dist/index.js.map +1 -1
- package/packages/shared/dist/types/accounts.d.ts +104 -0
- package/packages/shared/dist/types/accounts.d.ts.map +1 -0
- package/packages/shared/dist/types/accounts.js +14 -0
- package/packages/shared/dist/types/accounts.js.map +1 -0
- package/packages/shared/dist/types/preferences.d.ts +39 -0
- package/packages/shared/dist/types/preferences.d.ts.map +1 -1
- package/packages/shared/dist/types/preferences.js.map +1 -1
- package/packages/shared/dist/types/sdk.d.ts +3 -1
- package/packages/shared/dist/types/sdk.d.ts.map +1 -1
- package/packages/shared/dist/types/sdk.js.map +1 -1
- package/packages/shared/dist/types/websocket.d.ts +23 -1
- package/packages/shared/dist/types/websocket.d.ts.map +1 -1
- package/packages/client/dist/assets/index-BsHMOF5r.css +0 -32
- package/packages/server/dist/snippets/promote-issue +0 -9
|
@@ -59,17 +59,18 @@ import fs from 'fs/promises';
|
|
|
59
59
|
import { watch, mkdirSync, writeFileSync, createWriteStream } from 'fs';
|
|
60
60
|
import { sessionService } from './sessionService.js';
|
|
61
61
|
import { cliSessionPool } from './cliSessionPool.js';
|
|
62
|
+
import { preferencesService } from './preferencesService.js';
|
|
62
63
|
import { createCliScreenModel, CLI_SCREEN_COLS, CLI_SCREEN_ROWS } from './cliScreenModel.js';
|
|
63
64
|
import { setCliScreen } from './cliScreenCache.js';
|
|
64
65
|
import { createTrailingThrottle } from '../utils/trailingThrottle.js';
|
|
65
66
|
import { readSpinnerProgress } from './cliSpinnerProgress.js';
|
|
66
67
|
import { detectPermissionDialog, detectUsageLimit, detectRateLimit, extractToolName, extractPromptSentence, detectQuestionModal, parseQuestionModal, parsePrecedingText, parsePrecedingPermissionText, countQuestionTabs, parseQuestionTabHeaders, parseQuestionTabBody, parseConfirmChoiceMenu, readPermissionMode, permissionModeCycleIndex, isIdleInputGrid, isGeneratingGrid, classifyPreInjectScreen, CLI_PERMISSION_MODE_CYCLE, } from './cliModalDetect.js';
|
|
67
68
|
import { liveFooterText, scrollbackBodyRows } from './cliGridRegion.js';
|
|
68
|
-
import { parseGridCards,
|
|
69
|
+
import { parseGridCards, collectToolLineKeys, restoreFlickeredToolBullets } from './cliGridCards.js';
|
|
69
70
|
import { rateLimitProbeService } from './rateLimitProbeService.js';
|
|
70
71
|
import { parseJSONLFile, parseTaskNotification } from './historyParser.js';
|
|
71
72
|
import { rewindSessionFiles } from './fileRewind.js';
|
|
72
|
-
import {
|
|
73
|
+
import { buildSystemPrompt, resolveTemplateVariables } from './workspaceContext.js';
|
|
73
74
|
import { createLogger } from '../utils/logger.js';
|
|
74
75
|
import { SDKError, SDKErrorCode } from '../utils/errors.js';
|
|
75
76
|
import { CliDebugLog } from '../utils/cliDebugLog.js';
|
|
@@ -154,6 +155,13 @@ const CLI_MAX_BOOT_WAIT_MS = 4000; // boot readiness re-poll interval (inject as
|
|
|
154
155
|
// "still painting"; injection is grid-gated so we keep polling. A genuine boot freeze is covered by the
|
|
155
156
|
// soft screen-stall affordance (boot is now mirrored), pty.onExit (crash), and the user's Stop.
|
|
156
157
|
const CLI_SUBMIT_GAP_MS = 1000; // Enter sent this long after the prompt text
|
|
158
|
+
// Prompt injection is PACED in small chunks, not one bulk write. A single large pty.write of a long
|
|
159
|
+
// prompt overruns the Windows ConPTY input path: only a fraction (~the last few hundred bytes) reaches
|
|
160
|
+
// claude's input box and the head/middle is silently dropped (실측 2026-06-22: a 2439-byte prompt arrived
|
|
161
|
+
// as 391 bytes; bracketed-paste framing did NOT help; paced chunks delivered 100%). Pacing lets the TUI
|
|
162
|
+
// drain between writes so the buffer never overruns. Tuned from the same measurement (50 cps / 8ms = 100%).
|
|
163
|
+
const CLI_INJECT_CHUNK_CHARS = 50; // code points per write (code-point sliced — never splits a char)
|
|
164
|
+
const CLI_INJECT_CHUNK_GAP_MS = 8; // pause between chunk writes so the TUI drains its input buffer
|
|
157
165
|
/**
|
|
158
166
|
* Tool-approval interception (Story 32.6 — *constrained*; keys verified against
|
|
159
167
|
* claude v2.1.162 by a real-PTY observation, Task 1). The interactive permission
|
|
@@ -672,6 +680,16 @@ export class CliChatEngine {
|
|
|
672
680
|
const projectSlug = sessionService.encodeProjectPath(cwd);
|
|
673
681
|
const sessionsDir = sessionService.getSessionsDir(cwd);
|
|
674
682
|
const resumeId = typeof options.resume === 'string' && options.resume.length > 0 ? options.resume : undefined;
|
|
683
|
+
// Story BS-6: resolve the CLI debug instrumentation flags once per spawn. Preference-first
|
|
684
|
+
// (set via the HAMMOC_DEBUG settings panel), falling back to the original env vars so
|
|
685
|
+
// existing HAMMOC_CLI_* usage keeps working when no preference is set. Tool trace rides
|
|
686
|
+
// along with PTY dump (the original env coupling preserved). These are referenced below at
|
|
687
|
+
// the four gated sites (debug-file, PTY dump, tool trace, and the pre-inject snapshot deep
|
|
688
|
+
// inside attemptInjectFromGrid).
|
|
689
|
+
const debugPrefs = await preferencesService.readPreferences();
|
|
690
|
+
const cliDebug = debugPrefs.debugCliTrace ?? !!process.env.HAMMOC_CLI_DEBUG;
|
|
691
|
+
const ptyDump = debugPrefs.debugPtyDump ?? !!process.env.HAMMOC_CLI_PTY_DUMP;
|
|
692
|
+
const toolTrace = debugPrefs.debugToolTrace ?? (!!process.env.HAMMOC_CLI_TOOL_TRACE || ptyDump);
|
|
675
693
|
// Build interactive claude args. NEVER --print / --output-format stream-json:
|
|
676
694
|
// headless output bills like the SDK, which is the whole reason CLI mode exists.
|
|
677
695
|
const args = [];
|
|
@@ -717,8 +735,8 @@ export class CliChatEngine {
|
|
|
717
735
|
// workspaceContext). Verified: node-pty delivers the multi-line arg intact to the PTY child, and
|
|
718
736
|
// billing is unaffected (the pool is set by interactive-vs-print, not prompt content — this only
|
|
719
737
|
// adds input tokens to the same subscription pool).
|
|
720
|
-
const
|
|
721
|
-
args.push('--append-system-prompt', resolveTemplateVariables(
|
|
738
|
+
const assembled = buildSystemPrompt('cli', options.isBmadProject ?? false, options.customSystemPrompt);
|
|
739
|
+
args.push('--append-system-prompt', resolveTemplateVariables(assembled, cwd, options.displayName));
|
|
722
740
|
// Session-scoped `--settings` JSON (the global ~/.claude/settings.json is never
|
|
723
741
|
// modified). Thinking summaries (default ON): Opus 4.7+ omit summaries unless asked.
|
|
724
742
|
const settingsObj = {};
|
|
@@ -750,7 +768,7 @@ export class CliChatEngine {
|
|
|
750
768
|
// captured per spawn, to diagnose why claude self-compacts on some long-idle resumes.
|
|
751
769
|
// No-op unless the env flag is set; *.log is gitignored. Best-effort — instrumentation
|
|
752
770
|
// must never break a turn.
|
|
753
|
-
if (
|
|
771
|
+
if (cliDebug) {
|
|
754
772
|
try {
|
|
755
773
|
const dbgDir = path.join(process.cwd(), 'logs', 'claude-debug');
|
|
756
774
|
mkdirSync(dbgDir, { recursive: true });
|
|
@@ -820,7 +838,7 @@ export class CliChatEngine {
|
|
|
820
838
|
// raw bytes (ANSI intact) are appended to a gitignored logs/cli-pty-dump/*.log.
|
|
821
839
|
// Best-effort — instrumentation must never break a turn.
|
|
822
840
|
let ptyDumpStream = null;
|
|
823
|
-
if (
|
|
841
|
+
if (ptyDump) {
|
|
824
842
|
try {
|
|
825
843
|
const dumpDir = path.join(process.cwd(), 'logs', 'cli-pty-dump');
|
|
826
844
|
mkdirSync(dumpDir, { recursive: true });
|
|
@@ -840,7 +858,7 @@ export class CliChatEngine {
|
|
|
840
858
|
// Activate when EITHER its own flag OR the PTY-dump flag is set — so when only HAMMOC_CLI_PTY_DUMP can
|
|
841
859
|
// be toggled (already on), the tool-completion trace rides along with no extra env. (Temporary debug
|
|
842
860
|
// convenience; both are gitignored *.log and OFF in normal runs.)
|
|
843
|
-
if (
|
|
861
|
+
if (toolTrace) {
|
|
844
862
|
try {
|
|
845
863
|
const traceDir = path.join(process.cwd(), 'logs', 'cli-tool-trace');
|
|
846
864
|
mkdirSync(traceDir, { recursive: true });
|
|
@@ -861,7 +879,7 @@ export class CliChatEngine {
|
|
|
861
879
|
}
|
|
862
880
|
};
|
|
863
881
|
// Unified CLI decision log — per-turn session file, opt-in via HAMMOC_CLI_DEBUG (no-op when unset).
|
|
864
|
-
const dlog = new CliDebugLog(resumeId ?? options.sessionId ?? 'new');
|
|
882
|
+
const dlog = new CliDebugLog(resumeId ?? options.sessionId ?? 'new', cliDebug);
|
|
865
883
|
this.currentDebugLog?.close();
|
|
866
884
|
this.currentDebugLog = dlog;
|
|
867
885
|
// Story 36.2: report the pre-generation phase so the UI shows progress through the
|
|
@@ -909,6 +927,7 @@ export class CliChatEngine {
|
|
|
909
927
|
let bootRecoverTimer = null; // Story 37.6: post-Esc re-classify timer
|
|
910
928
|
let choiceMenuHandled = false; // Story 37.6 follow-up: one-shot handoff of a drivable confirm menu (resume prompt)
|
|
911
929
|
let submitTimer = null;
|
|
930
|
+
let injectChunkTimer = null; // paces the chunked prompt write
|
|
912
931
|
// Permission-dialog state (Story 32.6 — post-injection only). Story 37.4: detection reads
|
|
913
932
|
// the settled screen grid (no rolling buffer); `permissionPending` is the per-modal re-fire
|
|
914
933
|
// guard that the old buffer-clear consume used to provide.
|
|
@@ -1074,6 +1093,10 @@ export class CliChatEngine {
|
|
|
1074
1093
|
clearTimeout(submitTimer);
|
|
1075
1094
|
submitTimer = null;
|
|
1076
1095
|
}
|
|
1096
|
+
if (injectChunkTimer) {
|
|
1097
|
+
clearTimeout(injectChunkTimer);
|
|
1098
|
+
injectChunkTimer = null;
|
|
1099
|
+
}
|
|
1077
1100
|
if (questionSettleTimer) {
|
|
1078
1101
|
clearTimeout(questionSettleTimer);
|
|
1079
1102
|
questionSettleTimer = null;
|
|
@@ -1224,19 +1247,43 @@ export class CliChatEngine {
|
|
|
1224
1247
|
bootRecoverTimer = null;
|
|
1225
1248
|
}
|
|
1226
1249
|
try {
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1250
|
+
// Inject the prompt in small paced chunks (see CLI_INJECT_CHUNK_* notes) instead of one bulk
|
|
1251
|
+
// write, which the Windows ConPTY input path truncates to its tail. The submit CR is sent as a
|
|
1252
|
+
// SEPARATE write only AFTER the final chunk + the usual gap, preserving bracketed-paste safety
|
|
1253
|
+
// (a CR close to typed text coalesces into a newline; the gap keeps it a real submit).
|
|
1254
|
+
const codePoints = Array.from(promptToInject);
|
|
1255
|
+
let chunkStart = 0;
|
|
1256
|
+
const writeNextChunk = () => {
|
|
1257
|
+
injectChunkTimer = null;
|
|
1230
1258
|
if (settled)
|
|
1231
1259
|
return;
|
|
1260
|
+
if (chunkStart >= codePoints.length) {
|
|
1261
|
+
// All chunks delivered — now schedule the submit Enter after the bracketed-paste-safe gap.
|
|
1262
|
+
submitTimer = setTimeout(() => {
|
|
1263
|
+
submitTimer = null;
|
|
1264
|
+
if (settled)
|
|
1265
|
+
return;
|
|
1266
|
+
try {
|
|
1267
|
+
pty.write('\r');
|
|
1268
|
+
onPhase?.('waiting'); // prompt submitted — awaiting the first response block
|
|
1269
|
+
}
|
|
1270
|
+
catch (err) {
|
|
1271
|
+
fail(err instanceof Error ? err : new Error(String(err)));
|
|
1272
|
+
}
|
|
1273
|
+
}, CLI_SUBMIT_GAP_MS);
|
|
1274
|
+
return;
|
|
1275
|
+
}
|
|
1232
1276
|
try {
|
|
1233
|
-
pty.write('
|
|
1234
|
-
onPhase?.('waiting'); // prompt submitted — awaiting the first response block
|
|
1277
|
+
pty.write(codePoints.slice(chunkStart, chunkStart + CLI_INJECT_CHUNK_CHARS).join(''));
|
|
1235
1278
|
}
|
|
1236
1279
|
catch (err) {
|
|
1237
1280
|
fail(err instanceof Error ? err : new Error(String(err)));
|
|
1281
|
+
return;
|
|
1238
1282
|
}
|
|
1239
|
-
|
|
1283
|
+
chunkStart += CLI_INJECT_CHUNK_CHARS;
|
|
1284
|
+
injectChunkTimer = setTimeout(writeNextChunk, CLI_INJECT_CHUNK_GAP_MS);
|
|
1285
|
+
};
|
|
1286
|
+
writeNextChunk();
|
|
1240
1287
|
}
|
|
1241
1288
|
catch (err) {
|
|
1242
1289
|
fail(err instanceof Error ? err : new Error(String(err)));
|
|
@@ -1331,7 +1378,7 @@ export class CliChatEngine {
|
|
|
1331
1378
|
return;
|
|
1332
1379
|
const grid = screen.readGrid();
|
|
1333
1380
|
const classification = classifyPreInjectScreen(grid);
|
|
1334
|
-
if (
|
|
1381
|
+
if (cliDebug)
|
|
1335
1382
|
snapshotPreInject(grid, classification);
|
|
1336
1383
|
if (classification === 'input-box') {
|
|
1337
1384
|
void alignModeThenInject(); // AC1 — verified input box (align mode → inject)
|
|
@@ -1562,7 +1609,7 @@ export class CliChatEngine {
|
|
|
1562
1609
|
// mid-repaint) BEFORE parsing, so it opens as its own tool card instead of fusing into the prose
|
|
1563
1610
|
// above. Content-gated on the cross-frame memory, so prose is never promoted. Row count is
|
|
1564
1611
|
// unchanged (glyph prepended), so `bodyColors` stays index-aligned.
|
|
1565
|
-
cards = parseGridCards(restoreFlickeredToolBullets(bodyRows, recentToolHeaderKeys), bodyColors);
|
|
1612
|
+
cards = parseGridCards(restoreFlickeredToolBullets(bodyRows, recentToolHeaderKeys, bodyColors), bodyColors);
|
|
1566
1613
|
// Stop once a known block is in view: it is the bounded boundary — everything above it is known.
|
|
1567
1614
|
if (knownSigs.size === 0 || cards.some((c) => knownSigs.has(cardSig(c))))
|
|
1568
1615
|
break;
|
|
@@ -1579,17 +1626,18 @@ export class CliChatEngine {
|
|
|
1579
1626
|
cards: cards.map((c) => ({ kind: c.kind, tool: c.toolName, color: c.bulletColor, textLen: c.text.length, preview: c.text.slice(0, 50) })),
|
|
1580
1627
|
});
|
|
1581
1628
|
}
|
|
1582
|
-
// Story 37.12: refresh the cross-frame tool-
|
|
1629
|
+
// Story 37.12: refresh the cross-frame tool-line memory from the FINAL (deepest) frame read.
|
|
1583
1630
|
// First drop keys whose line scrolled off — retention is scoped to on-screen text INCLUDING
|
|
1584
1631
|
// bullet-less rows, so a tool whose glyph is currently flickering stays remembered while a tool that
|
|
1585
1632
|
// truly scrolled away is forgotten (can't re-promote unrelated prose later). Then add this frame's
|
|
1586
|
-
//
|
|
1587
|
-
|
|
1633
|
+
// TOOL-COLORED bullet lines (green/red/gray) — the confident observations worth remembering; a white
|
|
1634
|
+
// text-body bullet is excluded by the color gate, so prose is never remembered as a flicker tool.
|
|
1635
|
+
const onScreenToolKeys = collectToolLineKeys(bodyRows, { includeBulletless: true });
|
|
1588
1636
|
for (const key of [...recentToolHeaderKeys]) {
|
|
1589
1637
|
if (!onScreenToolKeys.has(key))
|
|
1590
1638
|
recentToolHeaderKeys.delete(key);
|
|
1591
1639
|
}
|
|
1592
|
-
for (const key of
|
|
1640
|
+
for (const key of collectToolLineKeys(bodyRows, { bulletColors: bodyColors }))
|
|
1593
1641
|
recentToolHeaderKeys.add(key);
|
|
1594
1642
|
// CONTENT-SET dedup (replaces the position/suffix align that re-emitted the whole sequence the
|
|
1595
1643
|
// moment a card was inserted mid-list or mutated). Walk the visible cards; for each STABLE card:
|
|
@@ -1609,30 +1657,34 @@ export class CliChatEngine {
|
|
|
1609
1657
|
// output; if it scrolled off we still mark complete (the turn-end reload fills the canonical output).
|
|
1610
1658
|
// Fixes "tool spins forever though the mirror shows a green/done bullet" — the completion row scrolls
|
|
1611
1659
|
// out of the viewport but the color is enough.
|
|
1612
|
-
const
|
|
1613
|
-
|
|
1660
|
+
const flipToolSettled = (slot, bulletColor, cardIdx) => {
|
|
1661
|
+
// A tool's bullet SETTLES to green (success) or red (failure) when it finishes — both are terminal
|
|
1662
|
+
// and flip the provisional card running→complete in place. Gray/other = still running → leave pending
|
|
1663
|
+
// (the turn-end reload supplies the canonical result). node-pty실측: red = 255,107,128, steady.
|
|
1664
|
+
if ((bulletColor !== 'green' && bulletColor !== 'red') || slot < 0 || gridResultFlippedSlots.has(slot))
|
|
1614
1665
|
return;
|
|
1615
1666
|
const sid = provToolSlotIds[slot];
|
|
1616
1667
|
if (!sid)
|
|
1617
1668
|
return;
|
|
1618
1669
|
const next = cards[cardIdx + 1];
|
|
1619
1670
|
const output = next && next.kind === 'result' && next.text.trim() ? next.text : '';
|
|
1671
|
+
const success = bulletColor === 'green';
|
|
1620
1672
|
gridResultFlippedSlots.add(slot);
|
|
1621
|
-
trace(`flip-
|
|
1622
|
-
dlog.server('grid-tool-
|
|
1623
|
-
callbacks.onToolResult?.(sid, { success
|
|
1673
|
+
trace(`flip-settled slot=${slot} sid=${sid} color=${bulletColor} (screen ${bulletColor} → ${success ? 'done' : 'failed'})`);
|
|
1674
|
+
dlog.server('grid-tool-settle-flip', { slot, sid, color: bulletColor, success, hasOutput: !!output });
|
|
1675
|
+
callbacks.onToolResult?.(sid, { success, output }, true);
|
|
1624
1676
|
};
|
|
1625
1677
|
for (let cardIdx = 0; cardIdx < cards.length; cardIdx++) {
|
|
1626
1678
|
const card = cards[cardIdx];
|
|
1627
1679
|
if (card.kind === 'result') {
|
|
1628
|
-
// Flip the preceding tool running→complete
|
|
1629
|
-
// paints `⎿ Waiting…`/`⎿ Running…` under a still-running gray-bullet tool; those
|
|
1630
|
-
// result (flipping shows a premature "complete: Waiting…"). Gray/unreadable stays
|
|
1631
|
-
// the turn-end reload supplies the canonical result. Only a grid-provisioned slot.
|
|
1680
|
+
// Flip the preceding tool running→complete when its `●` bullet has SETTLED green (success) or red
|
|
1681
|
+
// (failure). claude paints `⎿ Waiting…`/`⎿ Running…` under a still-running gray-bullet tool; those
|
|
1682
|
+
// are NOT the result (flipping shows a premature "complete: Waiting…"). Gray/unreadable stays
|
|
1683
|
+
// pending → the turn-end reload supplies the canonical result. Only a grid-provisioned slot.
|
|
1632
1684
|
const sid = lastToolSlot >= 0 ? provToolSlotIds[lastToolSlot] : undefined;
|
|
1633
|
-
if (sid && !gridResultFlippedSlots.has(lastToolSlot) && lastToolColor === 'green' && card.text.trim()) {
|
|
1685
|
+
if (sid && !gridResultFlippedSlots.has(lastToolSlot) && (lastToolColor === 'green' || lastToolColor === 'red') && card.text.trim()) {
|
|
1634
1686
|
gridResultFlippedSlots.add(lastToolSlot);
|
|
1635
|
-
callbacks.onToolResult?.(sid, { success:
|
|
1687
|
+
callbacks.onToolResult?.(sid, { success: lastToolColor === 'green', output: card.text }, true);
|
|
1636
1688
|
}
|
|
1637
1689
|
continue;
|
|
1638
1690
|
}
|
|
@@ -1715,7 +1767,7 @@ export class CliChatEngine {
|
|
|
1715
1767
|
lastToolColor = card.bulletColor;
|
|
1716
1768
|
if (card.bulletColor === 'green')
|
|
1717
1769
|
h.seenGreen = true;
|
|
1718
|
-
|
|
1770
|
+
flipToolSettled(h.slot, card.bulletColor, cardIdx);
|
|
1719
1771
|
}
|
|
1720
1772
|
continue;
|
|
1721
1773
|
}
|
|
@@ -1763,7 +1815,7 @@ export class CliChatEngine {
|
|
|
1763
1815
|
});
|
|
1764
1816
|
lastToolSlot = slot;
|
|
1765
1817
|
lastToolColor = card.bulletColor;
|
|
1766
|
-
|
|
1818
|
+
flipToolSettled(slot, card.bulletColor, cardIdx); // a tool already green/red on first sight = settled
|
|
1767
1819
|
}
|
|
1768
1820
|
else if (card.kind === 'text') {
|
|
1769
1821
|
const slot = liveTextSlots++;
|