spacecode 2.0.2 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/repl.mjs +1733 -587
- package/package.json +1 -1
package/dist/repl.mjs
CHANGED
|
@@ -1396,7 +1396,7 @@ var require_react_development = __commonJS({
|
|
|
1396
1396
|
}
|
|
1397
1397
|
return dispatcher.useContext(Context);
|
|
1398
1398
|
}
|
|
1399
|
-
function
|
|
1399
|
+
function useState10(initialState) {
|
|
1400
1400
|
var dispatcher = resolveDispatcher();
|
|
1401
1401
|
return dispatcher.useState(initialState);
|
|
1402
1402
|
}
|
|
@@ -1404,11 +1404,11 @@ var require_react_development = __commonJS({
|
|
|
1404
1404
|
var dispatcher = resolveDispatcher();
|
|
1405
1405
|
return dispatcher.useReducer(reducer, initialArg, init);
|
|
1406
1406
|
}
|
|
1407
|
-
function
|
|
1407
|
+
function useRef4(initialValue) {
|
|
1408
1408
|
var dispatcher = resolveDispatcher();
|
|
1409
1409
|
return dispatcher.useRef(initialValue);
|
|
1410
1410
|
}
|
|
1411
|
-
function
|
|
1411
|
+
function useEffect9(create2, deps) {
|
|
1412
1412
|
var dispatcher = resolveDispatcher();
|
|
1413
1413
|
return dispatcher.useEffect(create2, deps);
|
|
1414
1414
|
}
|
|
@@ -1420,11 +1420,11 @@ var require_react_development = __commonJS({
|
|
|
1420
1420
|
var dispatcher = resolveDispatcher();
|
|
1421
1421
|
return dispatcher.useLayoutEffect(create2, deps);
|
|
1422
1422
|
}
|
|
1423
|
-
function
|
|
1423
|
+
function useCallback4(callback, deps) {
|
|
1424
1424
|
var dispatcher = resolveDispatcher();
|
|
1425
1425
|
return dispatcher.useCallback(callback, deps);
|
|
1426
1426
|
}
|
|
1427
|
-
function
|
|
1427
|
+
function useMemo8(create2, deps) {
|
|
1428
1428
|
var dispatcher = resolveDispatcher();
|
|
1429
1429
|
return dispatcher.useMemo(create2, deps);
|
|
1430
1430
|
}
|
|
@@ -2187,19 +2187,19 @@ var require_react_development = __commonJS({
|
|
|
2187
2187
|
exports.memo = memo;
|
|
2188
2188
|
exports.startTransition = startTransition;
|
|
2189
2189
|
exports.unstable_act = act;
|
|
2190
|
-
exports.useCallback =
|
|
2190
|
+
exports.useCallback = useCallback4;
|
|
2191
2191
|
exports.useContext = useContext7;
|
|
2192
2192
|
exports.useDebugValue = useDebugValue;
|
|
2193
2193
|
exports.useDeferredValue = useDeferredValue;
|
|
2194
|
-
exports.useEffect =
|
|
2194
|
+
exports.useEffect = useEffect9;
|
|
2195
2195
|
exports.useId = useId;
|
|
2196
2196
|
exports.useImperativeHandle = useImperativeHandle;
|
|
2197
2197
|
exports.useInsertionEffect = useInsertionEffect;
|
|
2198
2198
|
exports.useLayoutEffect = useLayoutEffect2;
|
|
2199
|
-
exports.useMemo =
|
|
2199
|
+
exports.useMemo = useMemo8;
|
|
2200
2200
|
exports.useReducer = useReducer;
|
|
2201
|
-
exports.useRef =
|
|
2202
|
-
exports.useState =
|
|
2201
|
+
exports.useRef = useRef4;
|
|
2202
|
+
exports.useState = useState10;
|
|
2203
2203
|
exports.useSyncExternalStore = useSyncExternalStore;
|
|
2204
2204
|
exports.useTransition = useTransition;
|
|
2205
2205
|
exports.version = ReactVersion;
|
|
@@ -2255,7 +2255,7 @@ var require_react_jsx_runtime_development = __commonJS({
|
|
|
2255
2255
|
if (process.env.NODE_ENV !== "production") {
|
|
2256
2256
|
(function() {
|
|
2257
2257
|
"use strict";
|
|
2258
|
-
var
|
|
2258
|
+
var React19 = require_react();
|
|
2259
2259
|
var REACT_ELEMENT_TYPE = /* @__PURE__ */ Symbol.for("react.element");
|
|
2260
2260
|
var REACT_PORTAL_TYPE = /* @__PURE__ */ Symbol.for("react.portal");
|
|
2261
2261
|
var REACT_FRAGMENT_TYPE = /* @__PURE__ */ Symbol.for("react.fragment");
|
|
@@ -2281,7 +2281,7 @@ var require_react_jsx_runtime_development = __commonJS({
|
|
|
2281
2281
|
}
|
|
2282
2282
|
return null;
|
|
2283
2283
|
}
|
|
2284
|
-
var ReactSharedInternals =
|
|
2284
|
+
var ReactSharedInternals = React19.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
|
|
2285
2285
|
function error(format) {
|
|
2286
2286
|
{
|
|
2287
2287
|
{
|
|
@@ -3131,11 +3131,11 @@ var require_react_jsx_runtime_development = __commonJS({
|
|
|
3131
3131
|
return jsxWithValidation(type, props, key, false);
|
|
3132
3132
|
}
|
|
3133
3133
|
}
|
|
3134
|
-
var
|
|
3135
|
-
var
|
|
3134
|
+
var jsx12 = jsxWithValidationDynamic;
|
|
3135
|
+
var jsxs12 = jsxWithValidationStatic;
|
|
3136
3136
|
exports.Fragment = REACT_FRAGMENT_TYPE;
|
|
3137
|
-
exports.jsx =
|
|
3138
|
-
exports.jsxs =
|
|
3137
|
+
exports.jsx = jsx12;
|
|
3138
|
+
exports.jsxs = jsxs12;
|
|
3139
3139
|
})();
|
|
3140
3140
|
}
|
|
3141
3141
|
}
|
|
@@ -10823,9 +10823,9 @@ var require_react_reconciler_development = __commonJS({
|
|
|
10823
10823
|
module.exports = function $$$reconciler($$$hostConfig) {
|
|
10824
10824
|
var exports2 = {};
|
|
10825
10825
|
"use strict";
|
|
10826
|
-
var
|
|
10826
|
+
var React19 = require_react();
|
|
10827
10827
|
var Scheduler = require_scheduler();
|
|
10828
|
-
var ReactSharedInternals =
|
|
10828
|
+
var ReactSharedInternals = React19.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
|
|
10829
10829
|
var suppressWarning = false;
|
|
10830
10830
|
function setSuppressWarning(newSuppressWarning) {
|
|
10831
10831
|
{
|
|
@@ -25702,10 +25702,10 @@ var require_react_reconciler_development = __commonJS({
|
|
|
25702
25702
|
var setErrorHandler = null;
|
|
25703
25703
|
var setSuspenseHandler = null;
|
|
25704
25704
|
{
|
|
25705
|
-
var copyWithDeleteImpl = function(obj,
|
|
25706
|
-
var key =
|
|
25705
|
+
var copyWithDeleteImpl = function(obj, path11, index2) {
|
|
25706
|
+
var key = path11[index2];
|
|
25707
25707
|
var updated = isArray(obj) ? obj.slice() : assign({}, obj);
|
|
25708
|
-
if (index2 + 1 ===
|
|
25708
|
+
if (index2 + 1 === path11.length) {
|
|
25709
25709
|
if (isArray(updated)) {
|
|
25710
25710
|
updated.splice(key, 1);
|
|
25711
25711
|
} else {
|
|
@@ -25713,11 +25713,11 @@ var require_react_reconciler_development = __commonJS({
|
|
|
25713
25713
|
}
|
|
25714
25714
|
return updated;
|
|
25715
25715
|
}
|
|
25716
|
-
updated[key] = copyWithDeleteImpl(obj[key],
|
|
25716
|
+
updated[key] = copyWithDeleteImpl(obj[key], path11, index2 + 1);
|
|
25717
25717
|
return updated;
|
|
25718
25718
|
};
|
|
25719
|
-
var copyWithDelete = function(obj,
|
|
25720
|
-
return copyWithDeleteImpl(obj,
|
|
25719
|
+
var copyWithDelete = function(obj, path11) {
|
|
25720
|
+
return copyWithDeleteImpl(obj, path11, 0);
|
|
25721
25721
|
};
|
|
25722
25722
|
var copyWithRenameImpl = function(obj, oldPath, newPath, index2) {
|
|
25723
25723
|
var oldKey = oldPath[index2];
|
|
@@ -25755,17 +25755,17 @@ var require_react_reconciler_development = __commonJS({
|
|
|
25755
25755
|
}
|
|
25756
25756
|
return copyWithRenameImpl(obj, oldPath, newPath, 0);
|
|
25757
25757
|
};
|
|
25758
|
-
var copyWithSetImpl = function(obj,
|
|
25759
|
-
if (index2 >=
|
|
25758
|
+
var copyWithSetImpl = function(obj, path11, index2, value) {
|
|
25759
|
+
if (index2 >= path11.length) {
|
|
25760
25760
|
return value;
|
|
25761
25761
|
}
|
|
25762
|
-
var key =
|
|
25762
|
+
var key = path11[index2];
|
|
25763
25763
|
var updated = isArray(obj) ? obj.slice() : assign({}, obj);
|
|
25764
|
-
updated[key] = copyWithSetImpl(obj[key],
|
|
25764
|
+
updated[key] = copyWithSetImpl(obj[key], path11, index2 + 1, value);
|
|
25765
25765
|
return updated;
|
|
25766
25766
|
};
|
|
25767
|
-
var copyWithSet = function(obj,
|
|
25768
|
-
return copyWithSetImpl(obj,
|
|
25767
|
+
var copyWithSet = function(obj, path11, value) {
|
|
25768
|
+
return copyWithSetImpl(obj, path11, 0, value);
|
|
25769
25769
|
};
|
|
25770
25770
|
var findHook = function(fiber, id) {
|
|
25771
25771
|
var currentHook2 = fiber.memoizedState;
|
|
@@ -25775,10 +25775,10 @@ var require_react_reconciler_development = __commonJS({
|
|
|
25775
25775
|
}
|
|
25776
25776
|
return currentHook2;
|
|
25777
25777
|
};
|
|
25778
|
-
overrideHookState = function(fiber, id,
|
|
25778
|
+
overrideHookState = function(fiber, id, path11, value) {
|
|
25779
25779
|
var hook = findHook(fiber, id);
|
|
25780
25780
|
if (hook !== null) {
|
|
25781
|
-
var newState = copyWithSet(hook.memoizedState,
|
|
25781
|
+
var newState = copyWithSet(hook.memoizedState, path11, value);
|
|
25782
25782
|
hook.memoizedState = newState;
|
|
25783
25783
|
hook.baseState = newState;
|
|
25784
25784
|
fiber.memoizedProps = assign({}, fiber.memoizedProps);
|
|
@@ -25788,10 +25788,10 @@ var require_react_reconciler_development = __commonJS({
|
|
|
25788
25788
|
}
|
|
25789
25789
|
}
|
|
25790
25790
|
};
|
|
25791
|
-
overrideHookStateDeletePath = function(fiber, id,
|
|
25791
|
+
overrideHookStateDeletePath = function(fiber, id, path11) {
|
|
25792
25792
|
var hook = findHook(fiber, id);
|
|
25793
25793
|
if (hook !== null) {
|
|
25794
|
-
var newState = copyWithDelete(hook.memoizedState,
|
|
25794
|
+
var newState = copyWithDelete(hook.memoizedState, path11);
|
|
25795
25795
|
hook.memoizedState = newState;
|
|
25796
25796
|
hook.baseState = newState;
|
|
25797
25797
|
fiber.memoizedProps = assign({}, fiber.memoizedProps);
|
|
@@ -25814,8 +25814,8 @@ var require_react_reconciler_development = __commonJS({
|
|
|
25814
25814
|
}
|
|
25815
25815
|
}
|
|
25816
25816
|
};
|
|
25817
|
-
overrideProps = function(fiber,
|
|
25818
|
-
fiber.pendingProps = copyWithSet(fiber.memoizedProps,
|
|
25817
|
+
overrideProps = function(fiber, path11, value) {
|
|
25818
|
+
fiber.pendingProps = copyWithSet(fiber.memoizedProps, path11, value);
|
|
25819
25819
|
if (fiber.alternate) {
|
|
25820
25820
|
fiber.alternate.pendingProps = fiber.pendingProps;
|
|
25821
25821
|
}
|
|
@@ -25824,8 +25824,8 @@ var require_react_reconciler_development = __commonJS({
|
|
|
25824
25824
|
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
|
25825
25825
|
}
|
|
25826
25826
|
};
|
|
25827
|
-
overridePropsDeletePath = function(fiber,
|
|
25828
|
-
fiber.pendingProps = copyWithDelete(fiber.memoizedProps,
|
|
25827
|
+
overridePropsDeletePath = function(fiber, path11) {
|
|
25828
|
+
fiber.pendingProps = copyWithDelete(fiber.memoizedProps, path11);
|
|
25829
25829
|
if (fiber.alternate) {
|
|
25830
25830
|
fiber.alternate.pendingProps = fiber.pendingProps;
|
|
25831
25831
|
}
|
|
@@ -28903,24 +28903,24 @@ var require_sender = __commonJS({
|
|
|
28903
28903
|
* @public
|
|
28904
28904
|
*/
|
|
28905
28905
|
ping(data, mask, cb) {
|
|
28906
|
-
let
|
|
28906
|
+
let byteLength2;
|
|
28907
28907
|
let readOnly;
|
|
28908
28908
|
if (typeof data === "string") {
|
|
28909
|
-
|
|
28909
|
+
byteLength2 = Buffer.byteLength(data);
|
|
28910
28910
|
readOnly = false;
|
|
28911
28911
|
} else if (isBlob(data)) {
|
|
28912
|
-
|
|
28912
|
+
byteLength2 = data.size;
|
|
28913
28913
|
readOnly = false;
|
|
28914
28914
|
} else {
|
|
28915
28915
|
data = toBuffer(data);
|
|
28916
|
-
|
|
28916
|
+
byteLength2 = data.length;
|
|
28917
28917
|
readOnly = toBuffer.readOnly;
|
|
28918
28918
|
}
|
|
28919
|
-
if (
|
|
28919
|
+
if (byteLength2 > 125) {
|
|
28920
28920
|
throw new RangeError("The data size must not be greater than 125 bytes");
|
|
28921
28921
|
}
|
|
28922
28922
|
const options = {
|
|
28923
|
-
[kByteLength]:
|
|
28923
|
+
[kByteLength]: byteLength2,
|
|
28924
28924
|
fin: true,
|
|
28925
28925
|
generateMask: this._generateMask,
|
|
28926
28926
|
mask,
|
|
@@ -28950,24 +28950,24 @@ var require_sender = __commonJS({
|
|
|
28950
28950
|
* @public
|
|
28951
28951
|
*/
|
|
28952
28952
|
pong(data, mask, cb) {
|
|
28953
|
-
let
|
|
28953
|
+
let byteLength2;
|
|
28954
28954
|
let readOnly;
|
|
28955
28955
|
if (typeof data === "string") {
|
|
28956
|
-
|
|
28956
|
+
byteLength2 = Buffer.byteLength(data);
|
|
28957
28957
|
readOnly = false;
|
|
28958
28958
|
} else if (isBlob(data)) {
|
|
28959
|
-
|
|
28959
|
+
byteLength2 = data.size;
|
|
28960
28960
|
readOnly = false;
|
|
28961
28961
|
} else {
|
|
28962
28962
|
data = toBuffer(data);
|
|
28963
|
-
|
|
28963
|
+
byteLength2 = data.length;
|
|
28964
28964
|
readOnly = toBuffer.readOnly;
|
|
28965
28965
|
}
|
|
28966
|
-
if (
|
|
28966
|
+
if (byteLength2 > 125) {
|
|
28967
28967
|
throw new RangeError("The data size must not be greater than 125 bytes");
|
|
28968
28968
|
}
|
|
28969
28969
|
const options = {
|
|
28970
|
-
[kByteLength]:
|
|
28970
|
+
[kByteLength]: byteLength2,
|
|
28971
28971
|
fin: true,
|
|
28972
28972
|
generateMask: this._generateMask,
|
|
28973
28973
|
mask,
|
|
@@ -29008,23 +29008,23 @@ var require_sender = __commonJS({
|
|
|
29008
29008
|
const perMessageDeflate = this._extensions[PerMessageDeflate2.extensionName];
|
|
29009
29009
|
let opcode = options.binary ? 2 : 1;
|
|
29010
29010
|
let rsv1 = options.compress;
|
|
29011
|
-
let
|
|
29011
|
+
let byteLength2;
|
|
29012
29012
|
let readOnly;
|
|
29013
29013
|
if (typeof data === "string") {
|
|
29014
|
-
|
|
29014
|
+
byteLength2 = Buffer.byteLength(data);
|
|
29015
29015
|
readOnly = false;
|
|
29016
29016
|
} else if (isBlob(data)) {
|
|
29017
|
-
|
|
29017
|
+
byteLength2 = data.size;
|
|
29018
29018
|
readOnly = false;
|
|
29019
29019
|
} else {
|
|
29020
29020
|
data = toBuffer(data);
|
|
29021
|
-
|
|
29021
|
+
byteLength2 = data.length;
|
|
29022
29022
|
readOnly = toBuffer.readOnly;
|
|
29023
29023
|
}
|
|
29024
29024
|
if (this._firstFragment) {
|
|
29025
29025
|
this._firstFragment = false;
|
|
29026
29026
|
if (rsv1 && perMessageDeflate && perMessageDeflate.params[perMessageDeflate._isServer ? "server_no_context_takeover" : "client_no_context_takeover"]) {
|
|
29027
|
-
rsv1 =
|
|
29027
|
+
rsv1 = byteLength2 >= perMessageDeflate._threshold;
|
|
29028
29028
|
}
|
|
29029
29029
|
this._compress = rsv1;
|
|
29030
29030
|
} else {
|
|
@@ -29033,7 +29033,7 @@ var require_sender = __commonJS({
|
|
|
29033
29033
|
}
|
|
29034
29034
|
if (options.fin) this._firstFragment = true;
|
|
29035
29035
|
const opts = {
|
|
29036
|
-
[kByteLength]:
|
|
29036
|
+
[kByteLength]: byteLength2,
|
|
29037
29037
|
fin: options.fin,
|
|
29038
29038
|
generateMask: this._generateMask,
|
|
29039
29039
|
mask: options.mask,
|
|
@@ -30543,7 +30543,7 @@ var require_stream = __commonJS({
|
|
|
30543
30543
|
};
|
|
30544
30544
|
duplex._final = function(callback) {
|
|
30545
30545
|
if (ws.readyState === ws.CONNECTING) {
|
|
30546
|
-
ws.once("open", function
|
|
30546
|
+
ws.once("open", function open3() {
|
|
30547
30547
|
duplex._final(callback);
|
|
30548
30548
|
});
|
|
30549
30549
|
return;
|
|
@@ -30564,7 +30564,7 @@ var require_stream = __commonJS({
|
|
|
30564
30564
|
};
|
|
30565
30565
|
duplex._write = function(chunk, encoding, callback) {
|
|
30566
30566
|
if (ws.readyState === ws.CONNECTING) {
|
|
30567
|
-
ws.once("open", function
|
|
30567
|
+
ws.once("open", function open3() {
|
|
30568
30568
|
duplex._write(chunk, encoding, callback);
|
|
30569
30569
|
});
|
|
30570
30570
|
return;
|
|
@@ -31950,18 +31950,18 @@ var init_source = __esm({
|
|
|
31950
31950
|
}
|
|
31951
31951
|
}
|
|
31952
31952
|
});
|
|
31953
|
-
createStyler = (
|
|
31953
|
+
createStyler = (open3, close, parent) => {
|
|
31954
31954
|
let openAll;
|
|
31955
31955
|
let closeAll;
|
|
31956
31956
|
if (parent === void 0) {
|
|
31957
|
-
openAll =
|
|
31957
|
+
openAll = open3;
|
|
31958
31958
|
closeAll = close;
|
|
31959
31959
|
} else {
|
|
31960
|
-
openAll = parent.openAll +
|
|
31960
|
+
openAll = parent.openAll + open3;
|
|
31961
31961
|
closeAll = close + parent.closeAll;
|
|
31962
31962
|
}
|
|
31963
31963
|
return {
|
|
31964
|
-
open:
|
|
31964
|
+
open: open3,
|
|
31965
31965
|
close,
|
|
31966
31966
|
openAll,
|
|
31967
31967
|
closeAll,
|
|
@@ -33530,8 +33530,8 @@ var init_ErrorOverview = __esm({
|
|
|
33530
33530
|
init_dist3();
|
|
33531
33531
|
init_Box();
|
|
33532
33532
|
init_Text();
|
|
33533
|
-
cleanupPath = (
|
|
33534
|
-
return
|
|
33533
|
+
cleanupPath = (path11) => {
|
|
33534
|
+
return path11?.replace(`file://${cwd()}/`, "");
|
|
33535
33535
|
};
|
|
33536
33536
|
stackUtils = new import_stack_utils.default({
|
|
33537
33537
|
cwd: cwd(),
|
|
@@ -34606,6 +34606,47 @@ var init_build2 = __esm({
|
|
|
34606
34606
|
}
|
|
34607
34607
|
});
|
|
34608
34608
|
|
|
34609
|
+
// src/render/theme.ts
|
|
34610
|
+
function roleProps(role, theme) {
|
|
34611
|
+
switch (theme) {
|
|
34612
|
+
case "plain":
|
|
34613
|
+
return {};
|
|
34614
|
+
case "high-contrast":
|
|
34615
|
+
return role === "secondary" ? {} : { color: ROLE_COLORS[role], bold: true };
|
|
34616
|
+
case "auto":
|
|
34617
|
+
return { color: ROLE_COLORS[role] };
|
|
34618
|
+
}
|
|
34619
|
+
}
|
|
34620
|
+
function resolveThemeName(env3, warn = (m2) => console.warn(m2)) {
|
|
34621
|
+
const raw = env3.SPACECODE_THEME;
|
|
34622
|
+
if (raw === void 0 || raw === "") return "auto";
|
|
34623
|
+
if (THEME_NAMES.includes(raw)) return raw;
|
|
34624
|
+
warn(`[theme] unknown SPACECODE_THEME "${raw}"; using "auto". Accepted values: ${THEME_NAMES.join(", ")}.`);
|
|
34625
|
+
return "auto";
|
|
34626
|
+
}
|
|
34627
|
+
function activeTheme() {
|
|
34628
|
+
active ??= resolveThemeName(process.env);
|
|
34629
|
+
return active;
|
|
34630
|
+
}
|
|
34631
|
+
function paint(role) {
|
|
34632
|
+
return roleProps(role, activeTheme());
|
|
34633
|
+
}
|
|
34634
|
+
var THEME_NAMES, ROLE_COLORS, active;
|
|
34635
|
+
var init_theme = __esm({
|
|
34636
|
+
"src/render/theme.ts"() {
|
|
34637
|
+
"use strict";
|
|
34638
|
+
THEME_NAMES = ["auto", "plain", "high-contrast"];
|
|
34639
|
+
ROLE_COLORS = {
|
|
34640
|
+
agent: "magenta",
|
|
34641
|
+
add: "green",
|
|
34642
|
+
delete: "red",
|
|
34643
|
+
needsYou: "yellow",
|
|
34644
|
+
action: "blue",
|
|
34645
|
+
secondary: "gray"
|
|
34646
|
+
};
|
|
34647
|
+
}
|
|
34648
|
+
});
|
|
34649
|
+
|
|
34609
34650
|
// src/components/WelcomeBanner.tsx
|
|
34610
34651
|
function getSubtitle(mode, launchCount2) {
|
|
34611
34652
|
switch (mode) {
|
|
@@ -34622,26 +34663,238 @@ function getSubtitle(mode, launchCount2) {
|
|
|
34622
34663
|
return "Ready to code. What's next?";
|
|
34623
34664
|
}
|
|
34624
34665
|
}
|
|
34625
|
-
function WelcomeBanner({
|
|
34626
|
-
|
|
34627
|
-
|
|
34628
|
-
|
|
34629
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: "cyan", bold: true, children: "SpaceCode Agent" }),
|
|
34630
|
-
repoName2 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [
|
|
34631
|
-
"Project: ",
|
|
34632
|
-
repoName2
|
|
34633
|
-
] }),
|
|
34634
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: "gray", children: subtitle }),
|
|
34635
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: "cyan", children: BORDER })
|
|
34666
|
+
function WelcomeBanner({ onboardingMode: onboardingMode2, launchCount: launchCount2 }) {
|
|
34667
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { ...paint("secondary"), children: [
|
|
34668
|
+
getSubtitle(onboardingMode2, launchCount2),
|
|
34669
|
+
" /help for commands, @ to attach a file."
|
|
34636
34670
|
] });
|
|
34637
34671
|
}
|
|
34638
|
-
var import_jsx_runtime
|
|
34672
|
+
var import_jsx_runtime;
|
|
34639
34673
|
var init_WelcomeBanner = __esm({
|
|
34640
34674
|
async "src/components/WelcomeBanner.tsx"() {
|
|
34641
34675
|
"use strict";
|
|
34642
34676
|
await init_build2();
|
|
34677
|
+
init_theme();
|
|
34643
34678
|
import_jsx_runtime = __toESM(require_jsx_runtime(), 1);
|
|
34644
|
-
|
|
34679
|
+
}
|
|
34680
|
+
});
|
|
34681
|
+
|
|
34682
|
+
// src/render/hints.ts
|
|
34683
|
+
function formatHints({ running, asking, columns }, sep2 = "\xB7") {
|
|
34684
|
+
columns = Math.min(columns, MAX_STATIC_LINE);
|
|
34685
|
+
const parts = asking ? ["1 2 3 answer", "esc refuse"] : running ? ["esc interrupt", "tab step", "shift+tab mode", "ctrl+o turns"] : ["enter send", "shift+tab mode", "/ commands", "@ file", "! shell", "ctrl+o turns"];
|
|
34686
|
+
const kept = [...parts];
|
|
34687
|
+
while (kept.length > 1 && kept.join(` ${sep2} `).length > columns) kept.pop();
|
|
34688
|
+
return kept.join(` ${sep2} `);
|
|
34689
|
+
}
|
|
34690
|
+
var MAX_STATIC_LINE;
|
|
34691
|
+
var init_hints = __esm({
|
|
34692
|
+
"src/render/hints.ts"() {
|
|
34693
|
+
"use strict";
|
|
34694
|
+
MAX_STATIC_LINE = 58;
|
|
34695
|
+
}
|
|
34696
|
+
});
|
|
34697
|
+
|
|
34698
|
+
// src/render/header.ts
|
|
34699
|
+
function formatDuration(ms) {
|
|
34700
|
+
const min = Math.floor(ms / 6e4);
|
|
34701
|
+
return min < 1 ? "<1 min" : min < 60 ? `${min} min` : `${Math.floor(min / 60)}h ${min % 60}m`;
|
|
34702
|
+
}
|
|
34703
|
+
function formatHeader({ project, git, otherSessions, turns, elapsedMs, columns: terminalColumns }, sep2 = "\xB7") {
|
|
34704
|
+
const columns = Math.min(terminalColumns, MAX_STATIC_LINE);
|
|
34705
|
+
const optional = [];
|
|
34706
|
+
const branch = [];
|
|
34707
|
+
if (git?.kind === "ok") {
|
|
34708
|
+
branch.push({ text: git.branch });
|
|
34709
|
+
if (git.dirty > 0) branch.push({ text: `${sep2 === "\xB7" ? "\xB1" : "+-"}${git.dirty}`, role: "needsYou" });
|
|
34710
|
+
} else if (git?.kind === "unknown") {
|
|
34711
|
+
branch.push({ text: "git ?", role: "needsYou" });
|
|
34712
|
+
}
|
|
34713
|
+
if (otherSessions !== void 0 && otherSessions > 0) optional.push({ text: `+${otherSessions} other session${otherSessions === 1 ? "" : "s"}`, role: "secondary" });
|
|
34714
|
+
optional.push({ text: `${turns} turn${turns === 1 ? "" : "s"}`, role: "secondary" });
|
|
34715
|
+
optional.push({ text: formatDuration(elapsedMs), role: "secondary" });
|
|
34716
|
+
const base = [{ text: "spacecode", bold: true }, { text: project }];
|
|
34717
|
+
const all = [...base, ...branch, ...optional];
|
|
34718
|
+
const width = (segs) => segs.reduce((n, s, i) => n + s.text.length + (i > 0 ? sep2.length + 2 : 0), 0);
|
|
34719
|
+
const kept = [...all];
|
|
34720
|
+
while (kept.length > base.length && width(kept) > columns) kept.pop();
|
|
34721
|
+
return kept;
|
|
34722
|
+
}
|
|
34723
|
+
var init_header = __esm({
|
|
34724
|
+
"src/render/header.ts"() {
|
|
34725
|
+
"use strict";
|
|
34726
|
+
init_hints();
|
|
34727
|
+
}
|
|
34728
|
+
});
|
|
34729
|
+
|
|
34730
|
+
// src/render/rail.ts
|
|
34731
|
+
function unicodeAvailable(env3, stdoutIsTTY) {
|
|
34732
|
+
if (!stdoutIsTTY) return false;
|
|
34733
|
+
const locale = env3.LC_ALL || env3.LC_CTYPE || env3.LANG || "";
|
|
34734
|
+
return /utf-?8/i.test(locale);
|
|
34735
|
+
}
|
|
34736
|
+
function railGlyphs(unicode) {
|
|
34737
|
+
return unicode ? UNICODE : ASCII;
|
|
34738
|
+
}
|
|
34739
|
+
function truncatePath(target, max, ellipsis = "\u2026") {
|
|
34740
|
+
if (max <= 0) return "";
|
|
34741
|
+
const e = ellipsis.length;
|
|
34742
|
+
if (target.length <= max) return target;
|
|
34743
|
+
const parts = target.split("/");
|
|
34744
|
+
const file = parts[parts.length - 1];
|
|
34745
|
+
if (parts.length > 1 && file.length + e + 1 <= max) {
|
|
34746
|
+
let tail = file;
|
|
34747
|
+
for (let i = parts.length - 2; i >= 0; i--) {
|
|
34748
|
+
const next = `${parts[i]}/${tail}`;
|
|
34749
|
+
if (next.length + e + 1 > max) break;
|
|
34750
|
+
tail = next;
|
|
34751
|
+
}
|
|
34752
|
+
return `${ellipsis}/${tail}`;
|
|
34753
|
+
}
|
|
34754
|
+
return max <= e ? ellipsis.slice(0, max) : `${target.slice(0, max - e)}${ellipsis}`;
|
|
34755
|
+
}
|
|
34756
|
+
function fitStepLine({ verb, target, meta, columns, indent = 4, ellipsis = "\u2026", sep: sep2 = "\xB7" }) {
|
|
34757
|
+
const room = Math.max(10, columns - indent);
|
|
34758
|
+
const metaText = meta ? ` ${sep2} ${meta}` : "";
|
|
34759
|
+
const fixed = verb.length + 1;
|
|
34760
|
+
if (fixed + target.length + metaText.length <= room) return { verb, target, meta };
|
|
34761
|
+
const targetRoom = room - fixed - metaText.length;
|
|
34762
|
+
const fileName = target.split("/").pop() ?? target;
|
|
34763
|
+
if (meta && targetRoom >= Math.min(fileName.length, 12) + 2) {
|
|
34764
|
+
return { verb, target: truncatePath(target, targetRoom, ellipsis), meta };
|
|
34765
|
+
}
|
|
34766
|
+
return { verb, target: truncatePath(target, Math.max(1, room - fixed), ellipsis) };
|
|
34767
|
+
}
|
|
34768
|
+
function plainOutput() {
|
|
34769
|
+
return unicodeOverride === void 0 && !process.stdout.isTTY;
|
|
34770
|
+
}
|
|
34771
|
+
function activeGlyphs() {
|
|
34772
|
+
return railGlyphs(unicodeOverride ?? unicodeAvailable(process.env, Boolean(process.stdout.isTTY)));
|
|
34773
|
+
}
|
|
34774
|
+
var UNICODE, ASCII, unicodeOverride;
|
|
34775
|
+
var init_rail = __esm({
|
|
34776
|
+
"src/render/rail.ts"() {
|
|
34777
|
+
"use strict";
|
|
34778
|
+
UNICODE = { rail: "\u2502", result: "\u23BF", ellipsis: "\u2026", sep: "\xB7", marker: { running: "\u25D0", waiting: "\u25C7", ok: "\u25CF", failed: "\u2717" } };
|
|
34779
|
+
ASCII = { rail: "|", result: ">", ellipsis: "...", sep: "-", marker: { running: "o", waiting: "?", ok: "*", failed: "x" } };
|
|
34780
|
+
}
|
|
34781
|
+
});
|
|
34782
|
+
|
|
34783
|
+
// src/components/Header.tsx
|
|
34784
|
+
function Header(props) {
|
|
34785
|
+
const sep2 = activeGlyphs().sep;
|
|
34786
|
+
const segments2 = formatHeader(props, sep2);
|
|
34787
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Box_default, { children: segments2.map((seg, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react22.default.Fragment, { children: [
|
|
34788
|
+
i > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { ...paint("secondary"), children: ` ${sep2} ` }),
|
|
34789
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { bold: seg.bold, ...seg.role ? paint(seg.role) : {}, children: seg.text })
|
|
34790
|
+
] }, i)) });
|
|
34791
|
+
}
|
|
34792
|
+
var import_react22, import_jsx_runtime2;
|
|
34793
|
+
var init_Header = __esm({
|
|
34794
|
+
async "src/components/Header.tsx"() {
|
|
34795
|
+
"use strict";
|
|
34796
|
+
import_react22 = __toESM(require_react(), 1);
|
|
34797
|
+
await init_build2();
|
|
34798
|
+
init_header();
|
|
34799
|
+
init_rail();
|
|
34800
|
+
init_theme();
|
|
34801
|
+
import_jsx_runtime2 = __toESM(require_jsx_runtime(), 1);
|
|
34802
|
+
}
|
|
34803
|
+
});
|
|
34804
|
+
|
|
34805
|
+
// src/components/ErrorBlock.tsx
|
|
34806
|
+
function ErrorBlock({ view }) {
|
|
34807
|
+
const glyphs = activeGlyphs();
|
|
34808
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Box_default, { flexDirection: "column", children: [
|
|
34809
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { ...paint("delete"), bold: true, children: `${glyphs.marker.failed} ${view.title}` }),
|
|
34810
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { children: ` ${view.cause}` }),
|
|
34811
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { ...paint("secondary"), children: ` ${view.action}` })
|
|
34812
|
+
] });
|
|
34813
|
+
}
|
|
34814
|
+
var import_jsx_runtime3;
|
|
34815
|
+
var init_ErrorBlock = __esm({
|
|
34816
|
+
async "src/components/ErrorBlock.tsx"() {
|
|
34817
|
+
"use strict";
|
|
34818
|
+
await init_build2();
|
|
34819
|
+
init_rail();
|
|
34820
|
+
init_theme();
|
|
34821
|
+
import_jsx_runtime3 = __toESM(require_jsx_runtime(), 1);
|
|
34822
|
+
}
|
|
34823
|
+
});
|
|
34824
|
+
|
|
34825
|
+
// src/render/git-status.ts
|
|
34826
|
+
function parseGitStatus(output) {
|
|
34827
|
+
const lines2 = output.split("\n").filter((l) => l !== "");
|
|
34828
|
+
const head = lines2.find((l) => l.startsWith("## "));
|
|
34829
|
+
const dirty = lines2.filter((l) => !l.startsWith("## ")).length;
|
|
34830
|
+
if (!head) throw new Error("git status output has no branch line");
|
|
34831
|
+
const rest = head.slice(3);
|
|
34832
|
+
let branch;
|
|
34833
|
+
if (rest.startsWith("No commits yet on ")) branch = rest.slice("No commits yet on ".length);
|
|
34834
|
+
else if (rest.startsWith("HEAD (no branch)")) branch = "detached HEAD";
|
|
34835
|
+
else if (rest.startsWith("Initial commit on ")) branch = rest.slice("Initial commit on ".length);
|
|
34836
|
+
else branch = rest.split("...")[0].split(" ")[0];
|
|
34837
|
+
return { branch, dirty };
|
|
34838
|
+
}
|
|
34839
|
+
async function readGitState(run) {
|
|
34840
|
+
try {
|
|
34841
|
+
const { branch, dirty } = parseGitStatus(await run(["status", "--porcelain=v1", "-b"]));
|
|
34842
|
+
return { kind: "ok", branch, dirty };
|
|
34843
|
+
} catch (err) {
|
|
34844
|
+
const text = `${err instanceof Error ? err.message : String(err)} ${err?.stderr ?? ""}`;
|
|
34845
|
+
return /not a git repository/i.test(text) ? { kind: "none" } : { kind: "unknown" };
|
|
34846
|
+
}
|
|
34847
|
+
}
|
|
34848
|
+
var init_git_status = __esm({
|
|
34849
|
+
"src/render/git-status.ts"() {
|
|
34850
|
+
"use strict";
|
|
34851
|
+
}
|
|
34852
|
+
});
|
|
34853
|
+
|
|
34854
|
+
// src/render/node-git.ts
|
|
34855
|
+
import { execFile } from "node:child_process";
|
|
34856
|
+
function createNodeGitRunner(cwd2, timeoutMs = 1e3) {
|
|
34857
|
+
return (args) => new Promise((resolve2, reject) => {
|
|
34858
|
+
execFile("git", args, { cwd: cwd2, timeout: timeoutMs, maxBuffer: 1024 * 1024, env: { ...process.env, LC_ALL: "C", GIT_OPTIONAL_LOCKS: "0" } }, (err, stdout, stderr) => {
|
|
34859
|
+
if (err) reject(Object.assign(err, { stderr }));
|
|
34860
|
+
else resolve2(stdout);
|
|
34861
|
+
});
|
|
34862
|
+
});
|
|
34863
|
+
}
|
|
34864
|
+
var init_node_git = __esm({
|
|
34865
|
+
"src/render/node-git.ts"() {
|
|
34866
|
+
"use strict";
|
|
34867
|
+
}
|
|
34868
|
+
});
|
|
34869
|
+
|
|
34870
|
+
// src/render/mode-line.ts
|
|
34871
|
+
function modeGlyph(mode, unicode = true) {
|
|
34872
|
+
return unicode ? GLYPHS[mode].unicode : GLYPHS[mode].ascii;
|
|
34873
|
+
}
|
|
34874
|
+
function promptGlyph(mode, unicode = true) {
|
|
34875
|
+
return mode === "plan" || mode === "yolo" ? modeGlyph(mode, unicode) : ">";
|
|
34876
|
+
}
|
|
34877
|
+
function modeLine(mode, unicode = true) {
|
|
34878
|
+
const label = unicode ? LABELS[mode] : LABELS[mode].replace(" \xB7 ", " - ");
|
|
34879
|
+
const role = mode === "accept-edits" ? "add" : mode === "plan" ? "action" : mode === "yolo" ? "delete" : void 0;
|
|
34880
|
+
return { text: `${modeGlyph(mode, unicode)} ${label}`, role };
|
|
34881
|
+
}
|
|
34882
|
+
var GLYPHS, LABELS;
|
|
34883
|
+
var init_mode_line = __esm({
|
|
34884
|
+
"src/render/mode-line.ts"() {
|
|
34885
|
+
"use strict";
|
|
34886
|
+
GLYPHS = {
|
|
34887
|
+
default: { unicode: "\u25C7", ascii: "-" },
|
|
34888
|
+
"accept-edits": { unicode: "\u25C8", ascii: "+" },
|
|
34889
|
+
plan: { unicode: "\u25A2", ascii: "#" },
|
|
34890
|
+
yolo: { unicode: "!", ascii: "!" }
|
|
34891
|
+
};
|
|
34892
|
+
LABELS = {
|
|
34893
|
+
default: "default",
|
|
34894
|
+
"accept-edits": "accept edits",
|
|
34895
|
+
plan: "plan \xB7 read-only",
|
|
34896
|
+
yolo: "yolo"
|
|
34897
|
+
};
|
|
34645
34898
|
}
|
|
34646
34899
|
});
|
|
34647
34900
|
|
|
@@ -34764,13 +35017,13 @@ function processEvents(events) {
|
|
|
34764
35017
|
return { renderOrder, statusMessage };
|
|
34765
35018
|
}
|
|
34766
35019
|
function useEventAccumulator(events) {
|
|
34767
|
-
return (0,
|
|
35020
|
+
return (0, import_react23.useMemo)(() => processEvents(events), [events]);
|
|
34768
35021
|
}
|
|
34769
|
-
var
|
|
35022
|
+
var import_react23, MAX_OUTPUT_LINES, MAX_RESULT_OUTPUT;
|
|
34770
35023
|
var init_useEventAccumulator = __esm({
|
|
34771
35024
|
"src/components/useEventAccumulator.ts"() {
|
|
34772
35025
|
"use strict";
|
|
34773
|
-
|
|
35026
|
+
import_react23 = __toESM(require_react(), 1);
|
|
34774
35027
|
MAX_OUTPUT_LINES = 3;
|
|
34775
35028
|
MAX_RESULT_OUTPUT = 500;
|
|
34776
35029
|
}
|
|
@@ -38638,7 +38891,7 @@ var require_core = __commonJS({
|
|
|
38638
38891
|
}
|
|
38639
38892
|
return highlighted[0].event === "start" ? original : highlighted;
|
|
38640
38893
|
}
|
|
38641
|
-
function
|
|
38894
|
+
function open3(node) {
|
|
38642
38895
|
function attributeString(attr) {
|
|
38643
38896
|
return " " + attr.nodeName + '="' + escapeHTML(attr.value) + '"';
|
|
38644
38897
|
}
|
|
@@ -38648,7 +38901,7 @@ var require_core = __commonJS({
|
|
|
38648
38901
|
result += "</" + tag(node) + ">";
|
|
38649
38902
|
}
|
|
38650
38903
|
function render3(event) {
|
|
38651
|
-
(event.event === "start" ?
|
|
38904
|
+
(event.event === "start" ? open3 : close)(event.node);
|
|
38652
38905
|
}
|
|
38653
38906
|
while (original.length || highlighted.length) {
|
|
38654
38907
|
let stream = selectStream();
|
|
@@ -38660,7 +38913,7 @@ var require_core = __commonJS({
|
|
|
38660
38913
|
render3(stream.splice(0, 1)[0]);
|
|
38661
38914
|
stream = selectStream();
|
|
38662
38915
|
} while (stream === original && stream.length && stream[0].offset === processed);
|
|
38663
|
-
nodeStack.reverse().forEach(
|
|
38916
|
+
nodeStack.reverse().forEach(open3);
|
|
38664
38917
|
} else {
|
|
38665
38918
|
if (stream[0].event === "start") {
|
|
38666
38919
|
nodeStack.push(stream[0].node);
|
|
@@ -61906,11 +62159,11 @@ var require_perl = __commonJS({
|
|
|
61906
62159
|
/#/
|
|
61907
62160
|
// valid but infrequent and weird
|
|
61908
62161
|
];
|
|
61909
|
-
const PAIRED_DOUBLE_RE = (prefix,
|
|
61910
|
-
const middle = close === "\\1" ? close : concat(close,
|
|
62162
|
+
const PAIRED_DOUBLE_RE = (prefix, open3, close = "\\1") => {
|
|
62163
|
+
const middle = close === "\\1" ? close : concat(close, open3);
|
|
61911
62164
|
return concat(
|
|
61912
62165
|
concat("(?:", prefix, ")"),
|
|
61913
|
-
|
|
62166
|
+
open3,
|
|
61914
62167
|
/(?:\\.|[^\\\/])*?/,
|
|
61915
62168
|
middle,
|
|
61916
62169
|
/(?:\\.|[^\\\/])*?/,
|
|
@@ -61918,10 +62171,10 @@ var require_perl = __commonJS({
|
|
|
61918
62171
|
REGEX_MODIFIERS
|
|
61919
62172
|
);
|
|
61920
62173
|
};
|
|
61921
|
-
const PAIRED_RE = (prefix,
|
|
62174
|
+
const PAIRED_RE = (prefix, open3, close) => {
|
|
61922
62175
|
return concat(
|
|
61923
62176
|
concat("(?:", prefix, ")"),
|
|
61924
|
-
|
|
62177
|
+
open3,
|
|
61925
62178
|
/(?:\\.|[^\\\/])*?/,
|
|
61926
62179
|
close,
|
|
61927
62180
|
REGEX_MODIFIERS
|
|
@@ -80629,15 +80882,15 @@ var require_route = __commonJS({
|
|
|
80629
80882
|
};
|
|
80630
80883
|
}
|
|
80631
80884
|
function wrapConversion(toModel, graph) {
|
|
80632
|
-
const
|
|
80885
|
+
const path11 = [graph[toModel].parent, toModel];
|
|
80633
80886
|
let fn = conversions[graph[toModel].parent][toModel];
|
|
80634
80887
|
let cur = graph[toModel].parent;
|
|
80635
80888
|
while (graph[cur].parent) {
|
|
80636
|
-
|
|
80889
|
+
path11.unshift(graph[cur].parent);
|
|
80637
80890
|
fn = link2(conversions[graph[cur].parent][cur], fn);
|
|
80638
80891
|
cur = graph[cur].parent;
|
|
80639
80892
|
}
|
|
80640
|
-
fn.conversion =
|
|
80893
|
+
fn.conversion = path11;
|
|
80641
80894
|
return fn;
|
|
80642
80895
|
}
|
|
80643
80896
|
module.exports = function(fromModel) {
|
|
@@ -81226,18 +81479,18 @@ var require_source = __commonJS({
|
|
|
81226
81479
|
}
|
|
81227
81480
|
}
|
|
81228
81481
|
});
|
|
81229
|
-
var createStyler2 = (
|
|
81482
|
+
var createStyler2 = (open3, close, parent) => {
|
|
81230
81483
|
let openAll;
|
|
81231
81484
|
let closeAll;
|
|
81232
81485
|
if (parent === void 0) {
|
|
81233
|
-
openAll =
|
|
81486
|
+
openAll = open3;
|
|
81234
81487
|
closeAll = close;
|
|
81235
81488
|
} else {
|
|
81236
|
-
openAll = parent.openAll +
|
|
81489
|
+
openAll = parent.openAll + open3;
|
|
81237
81490
|
closeAll = close + parent.closeAll;
|
|
81238
81491
|
}
|
|
81239
81492
|
return {
|
|
81240
|
-
open:
|
|
81493
|
+
open: open3,
|
|
81241
81494
|
close,
|
|
81242
81495
|
openAll,
|
|
81243
81496
|
closeAll,
|
|
@@ -94055,8 +94308,8 @@ var init_markdown = __esm({
|
|
|
94055
94308
|
// src/useTerminalSize.ts
|
|
94056
94309
|
function useTerminalSize(fallback = { columns: 80, rows: 24 }) {
|
|
94057
94310
|
const { stdout } = use_stdout_default();
|
|
94058
|
-
const [size, setSize] = (0,
|
|
94059
|
-
(0,
|
|
94311
|
+
const [size, setSize] = (0, import_react24.useState)({ columns: stdout?.columns || fallback.columns, rows: stdout?.rows || fallback.rows });
|
|
94312
|
+
(0, import_react24.useEffect)(() => {
|
|
94060
94313
|
if (!stdout) return;
|
|
94061
94314
|
const update = () => setSize({ columns: stdout.columns || fallback.columns, rows: stdout.rows || fallback.rows });
|
|
94062
94315
|
update();
|
|
@@ -94067,11 +94320,11 @@ function useTerminalSize(fallback = { columns: 80, rows: 24 }) {
|
|
|
94067
94320
|
}, [stdout, fallback.columns, fallback.rows]);
|
|
94068
94321
|
return size;
|
|
94069
94322
|
}
|
|
94070
|
-
var
|
|
94323
|
+
var import_react24;
|
|
94071
94324
|
var init_useTerminalSize = __esm({
|
|
94072
94325
|
async "src/useTerminalSize.ts"() {
|
|
94073
94326
|
"use strict";
|
|
94074
|
-
|
|
94327
|
+
import_react24 = __toESM(require_react(), 1);
|
|
94075
94328
|
await init_build2();
|
|
94076
94329
|
}
|
|
94077
94330
|
});
|
|
@@ -94085,22 +94338,22 @@ function tailLines(text, maxLines) {
|
|
|
94085
94338
|
}
|
|
94086
94339
|
function MarkdownBlock({ content, maxLines }) {
|
|
94087
94340
|
const { columns } = useTerminalSize();
|
|
94088
|
-
const rendered = (0,
|
|
94341
|
+
const rendered = (0, import_react25.useMemo)(() => {
|
|
94089
94342
|
const out = renderMarkdown(content, { columns });
|
|
94090
94343
|
return (maxLines && maxLines >= 2 ? tailLines(out, maxLines) : out) || null;
|
|
94091
94344
|
}, [content, columns, maxLines]);
|
|
94092
94345
|
if (rendered === null) return null;
|
|
94093
|
-
return /* @__PURE__ */ (0,
|
|
94346
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { children: rendered });
|
|
94094
94347
|
}
|
|
94095
|
-
var
|
|
94348
|
+
var import_react25, import_jsx_runtime4;
|
|
94096
94349
|
var init_MarkdownBlock = __esm({
|
|
94097
94350
|
async "src/components/MarkdownBlock.tsx"() {
|
|
94098
94351
|
"use strict";
|
|
94099
|
-
|
|
94352
|
+
import_react25 = __toESM(require_react(), 1);
|
|
94100
94353
|
await init_build2();
|
|
94101
94354
|
init_markdown();
|
|
94102
94355
|
await init_useTerminalSize();
|
|
94103
|
-
|
|
94356
|
+
import_jsx_runtime4 = __toESM(require_jsx_runtime(), 1);
|
|
94104
94357
|
}
|
|
94105
94358
|
});
|
|
94106
94359
|
|
|
@@ -95751,9 +96004,9 @@ var require_cli_spinners = __commonJS({
|
|
|
95751
96004
|
|
|
95752
96005
|
// ../../node_modules/ink-spinner/build/index.js
|
|
95753
96006
|
function Spinner({ type = "dots" }) {
|
|
95754
|
-
const [frame, setFrame] = (0,
|
|
96007
|
+
const [frame, setFrame] = (0, import_react26.useState)(0);
|
|
95755
96008
|
const spinner = import_cli_spinners.default[type];
|
|
95756
|
-
(0,
|
|
96009
|
+
(0, import_react26.useEffect)(() => {
|
|
95757
96010
|
const timer = setInterval(() => {
|
|
95758
96011
|
setFrame((previousFrame) => {
|
|
95759
96012
|
const isLastFrame = previousFrame === spinner.frames.length - 1;
|
|
@@ -95764,12 +96017,12 @@ function Spinner({ type = "dots" }) {
|
|
|
95764
96017
|
clearInterval(timer);
|
|
95765
96018
|
};
|
|
95766
96019
|
}, [spinner]);
|
|
95767
|
-
return
|
|
96020
|
+
return import_react26.default.createElement(Text, null, spinner.frames[frame]);
|
|
95768
96021
|
}
|
|
95769
|
-
var
|
|
96022
|
+
var import_react26, import_cli_spinners, build_default;
|
|
95770
96023
|
var init_build3 = __esm({
|
|
95771
96024
|
async "../../node_modules/ink-spinner/build/index.js"() {
|
|
95772
|
-
|
|
96025
|
+
import_react26 = __toESM(require_react(), 1);
|
|
95773
96026
|
await init_build2();
|
|
95774
96027
|
import_cli_spinners = __toESM(require_cli_spinners(), 1);
|
|
95775
96028
|
build_default = Spinner;
|
|
@@ -95777,48 +96030,238 @@ var init_build3 = __esm({
|
|
|
95777
96030
|
});
|
|
95778
96031
|
|
|
95779
96032
|
// src/render/tool-header.ts
|
|
95780
|
-
function
|
|
96033
|
+
function stepParts(tool, input) {
|
|
95781
96034
|
if (tool === "todo_write") {
|
|
95782
96035
|
const n = Array.isArray(input.todos) ? input.todos.length : 0;
|
|
95783
|
-
return
|
|
95784
|
-
}
|
|
95785
|
-
const LABELS = {
|
|
95786
|
-
read_file: { label: "Read", field: "path" },
|
|
95787
|
-
write_file: { label: "Write", field: "path" },
|
|
95788
|
-
edit_file: { label: "Update", field: "path" },
|
|
95789
|
-
list_directory: { label: "List", field: "path" },
|
|
95790
|
-
search: { label: "Search", field: "query" },
|
|
95791
|
-
bash: { label: "Bash", field: "command" }
|
|
95792
|
-
};
|
|
95793
|
-
const mapping = LABELS[tool];
|
|
95794
|
-
if (mapping) {
|
|
95795
|
-
const rawValue = input[mapping.field];
|
|
95796
|
-
let value2 = "";
|
|
95797
|
-
if (typeof rawValue === "string") {
|
|
95798
|
-
if (tool === "bash") {
|
|
95799
|
-
const firstLine = rawValue.split("\n")[0];
|
|
95800
|
-
value2 = firstLine.length > 60 ? firstLine.slice(0, 60) + "\u2026" : firstLine;
|
|
95801
|
-
} else {
|
|
95802
|
-
value2 = rawValue;
|
|
95803
|
-
}
|
|
95804
|
-
}
|
|
95805
|
-
return `${mapping.label}(${value2})`;
|
|
96036
|
+
return { verb: "Todos", target: `${n} item${n === 1 ? "" : "s"}` };
|
|
95806
96037
|
}
|
|
95807
|
-
|
|
95808
|
-
|
|
95809
|
-
|
|
95810
|
-
|
|
95811
|
-
|
|
95812
|
-
}
|
|
95813
|
-
}
|
|
95814
|
-
if (value.length > 40) {
|
|
95815
|
-
value = value.slice(0, 40);
|
|
95816
|
-
}
|
|
95817
|
-
return `${tool}(${value})`;
|
|
96038
|
+
const field = { read_file: "path", write_file: "path", edit_file: "path", list_directory: "path", search: "query", bash: "command" }[tool];
|
|
96039
|
+
const raw = field ? input[field] : Object.values(input).find((v2) => typeof v2 === "string");
|
|
96040
|
+
let target = typeof raw === "string" ? raw : "";
|
|
96041
|
+
if (tool === "bash") target = target.split("\n")[0];
|
|
96042
|
+
return { verb: STEP_VERBS[tool] ?? tool, target };
|
|
95818
96043
|
}
|
|
96044
|
+
var STEP_VERBS;
|
|
95819
96045
|
var init_tool_header = __esm({
|
|
95820
96046
|
"src/render/tool-header.ts"() {
|
|
95821
96047
|
"use strict";
|
|
96048
|
+
STEP_VERBS = {
|
|
96049
|
+
read_file: "Read",
|
|
96050
|
+
write_file: "Write",
|
|
96051
|
+
edit_file: "Edit",
|
|
96052
|
+
list_directory: "List",
|
|
96053
|
+
search: "Search",
|
|
96054
|
+
bash: "Run",
|
|
96055
|
+
todo_write: "Todos"
|
|
96056
|
+
};
|
|
96057
|
+
}
|
|
96058
|
+
});
|
|
96059
|
+
|
|
96060
|
+
// src/editor/credential-filter.ts
|
|
96061
|
+
function containsCredential(entry) {
|
|
96062
|
+
return PATTERNS.some((re3) => re3.test(entry));
|
|
96063
|
+
}
|
|
96064
|
+
var PATTERNS;
|
|
96065
|
+
var init_credential_filter = __esm({
|
|
96066
|
+
"src/editor/credential-filter.ts"() {
|
|
96067
|
+
"use strict";
|
|
96068
|
+
PATTERNS = [
|
|
96069
|
+
/\bsk[_-][A-Za-z0-9_-]{16,}/,
|
|
96070
|
+
/\bgh[pos]_[A-Za-z0-9]{20,}/,
|
|
96071
|
+
/\bgithub_pat_[A-Za-z0-9_]{20,}/,
|
|
96072
|
+
/\bAKIA[0-9A-Z]{16}\b/,
|
|
96073
|
+
/\bBearer\s+[A-Za-z0-9._~+/=-]{20,}/,
|
|
96074
|
+
/-----BEGIN [A-Z ]*PRIVATE KEY-----/,
|
|
96075
|
+
/\bxox[abp]-[A-Za-z0-9-]{10,}/,
|
|
96076
|
+
/\bnpm_[A-Za-z0-9]{30,}/,
|
|
96077
|
+
/\b[A-Za-z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD)[A-Za-z0-9_]*=\S{8,}/i
|
|
96078
|
+
];
|
|
96079
|
+
}
|
|
96080
|
+
});
|
|
96081
|
+
|
|
96082
|
+
// src/session/session-store.ts
|
|
96083
|
+
import { chmod, mkdir, open, readdir, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
96084
|
+
import { randomBytes } from "node:crypto";
|
|
96085
|
+
import path from "node:path";
|
|
96086
|
+
function redactCredentials(value) {
|
|
96087
|
+
if (typeof value === "string") return containsCredential(value) ? REDACTED : value;
|
|
96088
|
+
if (Array.isArray(value)) return value.map(redactCredentials);
|
|
96089
|
+
if (value && typeof value === "object") {
|
|
96090
|
+
return Object.fromEntries(Object.entries(value).map(([k2, v2]) => [k2, redactCredentials(v2)]));
|
|
96091
|
+
}
|
|
96092
|
+
return value;
|
|
96093
|
+
}
|
|
96094
|
+
function parseSession(file, text) {
|
|
96095
|
+
const lines2 = text.split("\n").filter((l) => l.length > 0);
|
|
96096
|
+
if (lines2.length === 0) throw new SessionError(file, "Session file is empty");
|
|
96097
|
+
let parsed;
|
|
96098
|
+
try {
|
|
96099
|
+
parsed = lines2.map((l) => JSON.parse(l));
|
|
96100
|
+
} catch (err) {
|
|
96101
|
+
throw new SessionError(file, `Corrupt session file: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
96102
|
+
}
|
|
96103
|
+
const meta = parsed[0];
|
|
96104
|
+
if (meta.v !== 1 || typeof meta.id !== "string" || typeof meta.projectRoot !== "string" || typeof meta.updatedAt !== "string") {
|
|
96105
|
+
throw new SessionError(file, "Unrecognized session header (expected v1 with id, projectRoot, updatedAt)");
|
|
96106
|
+
}
|
|
96107
|
+
const warnings = [];
|
|
96108
|
+
if (meta.todos !== void 0 && !(Array.isArray(meta.todos) && meta.todos.every((t) => t && typeof t.content === "string" && ["pending", "in_progress", "completed"].includes(t.status)))) {
|
|
96109
|
+
delete meta.todos;
|
|
96110
|
+
warnings.push(`The saved checklist in ${file} was not understood (saved by a different version?) and was left out.`);
|
|
96111
|
+
}
|
|
96112
|
+
const messages = parsed.slice(1);
|
|
96113
|
+
if (!messages.every((m2) => m2 && (m2.role === "user" || m2.role === "assistant"))) {
|
|
96114
|
+
throw new SessionError(file, "Corrupt session file: a message has no valid role");
|
|
96115
|
+
}
|
|
96116
|
+
return warnings.length > 0 ? { meta, messages, warnings } : { meta, messages };
|
|
96117
|
+
}
|
|
96118
|
+
function formatAge(fromIso, now) {
|
|
96119
|
+
const secs = Math.max(0, Math.floor((now.getTime() - new Date(fromIso).getTime()) / 1e3));
|
|
96120
|
+
if (secs < 60) return `${secs}s ago`;
|
|
96121
|
+
if (secs < 3600) return `${Math.floor(secs / 60)}m ago`;
|
|
96122
|
+
if (secs < 86400) return `${Math.floor(secs / 3600)}h ago`;
|
|
96123
|
+
return `${Math.floor(secs / 86400)}d ago`;
|
|
96124
|
+
}
|
|
96125
|
+
function titleFromMessages(messages) {
|
|
96126
|
+
const first = messages.find((m2) => m2.role === "user");
|
|
96127
|
+
const text = !first ? "" : typeof first.content === "string" ? first.content : first.content.map((b2) => b2.text ?? "").join(" ");
|
|
96128
|
+
const oneLine = text.replace(/\s+/g, " ").trim();
|
|
96129
|
+
return oneLine.length > 60 ? `${oneLine.slice(0, 59)}\u2026` : oneLine || "(untitled)";
|
|
96130
|
+
}
|
|
96131
|
+
function countTurns(messages) {
|
|
96132
|
+
return messages.filter((m2) => m2.role === "user" && (typeof m2.content === "string" || m2.content.some((b2) => b2.type === "text"))).length;
|
|
96133
|
+
}
|
|
96134
|
+
var MAX_SESSION_BYTES, PREVIEW_TAIL_BYTES, REDACTED, SessionError, ID, SessionStore;
|
|
96135
|
+
var init_session_store = __esm({
|
|
96136
|
+
"src/session/session-store.ts"() {
|
|
96137
|
+
"use strict";
|
|
96138
|
+
init_credential_filter();
|
|
96139
|
+
MAX_SESSION_BYTES = 5 * 1024 * 1024;
|
|
96140
|
+
PREVIEW_TAIL_BYTES = 256 * 1024;
|
|
96141
|
+
REDACTED = "[redacted: credential-shaped content]";
|
|
96142
|
+
SessionError = class extends Error {
|
|
96143
|
+
constructor(file, message, options) {
|
|
96144
|
+
super(`${message} (${file})`, options);
|
|
96145
|
+
this.file = file;
|
|
96146
|
+
this.name = "SessionError";
|
|
96147
|
+
}
|
|
96148
|
+
};
|
|
96149
|
+
ID = /^[A-Za-z0-9_-]{8,64}$/;
|
|
96150
|
+
SessionStore = class {
|
|
96151
|
+
constructor(dir, now = () => /* @__PURE__ */ new Date()) {
|
|
96152
|
+
this.dir = dir;
|
|
96153
|
+
this.now = now;
|
|
96154
|
+
}
|
|
96155
|
+
newId() {
|
|
96156
|
+
return `${this.now().toISOString().replace(/[-:.TZ]/g, "").slice(0, 14)}-${randomBytes(4).toString("hex")}`;
|
|
96157
|
+
}
|
|
96158
|
+
fileFor(id) {
|
|
96159
|
+
if (!ID.test(id)) throw new SessionError(this.dir, `Invalid session id ${JSON.stringify(id)}`);
|
|
96160
|
+
return path.join(this.dir, `${id}.jsonl`);
|
|
96161
|
+
}
|
|
96162
|
+
async save(session) {
|
|
96163
|
+
const file = this.fileFor(session.meta.id);
|
|
96164
|
+
const meta = { ...session.meta, updatedAt: this.now().toISOString() };
|
|
96165
|
+
const body = [meta, ...session.messages].map((entry) => JSON.stringify(redactCredentials(entry))).join("\n") + "\n";
|
|
96166
|
+
const bytes = Buffer.byteLength(body);
|
|
96167
|
+
if (bytes > MAX_SESSION_BYTES) {
|
|
96168
|
+
throw new SessionError(file, `Session is ${bytes} bytes, over the ${MAX_SESSION_BYTES}-byte cap; use /compact or /clear`);
|
|
96169
|
+
}
|
|
96170
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
96171
|
+
try {
|
|
96172
|
+
await mkdir(this.dir, { recursive: true, mode: 448 });
|
|
96173
|
+
await chmod(this.dir, 448);
|
|
96174
|
+
await writeFile(tmp, body, { mode: 384 });
|
|
96175
|
+
await chmod(tmp, 384);
|
|
96176
|
+
await rename(tmp, file);
|
|
96177
|
+
} catch (err) {
|
|
96178
|
+
throw new SessionError(file, `Could not save session: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
96179
|
+
}
|
|
96180
|
+
}
|
|
96181
|
+
async load(id) {
|
|
96182
|
+
const file = this.fileFor(id);
|
|
96183
|
+
let text;
|
|
96184
|
+
try {
|
|
96185
|
+
const { size } = await stat(file);
|
|
96186
|
+
if (size > MAX_SESSION_BYTES) {
|
|
96187
|
+
throw new SessionError(file, `Session is ${size} bytes, over the ${MAX_SESSION_BYTES}-byte cap; not loaded`);
|
|
96188
|
+
}
|
|
96189
|
+
text = await readFile(file, "utf8");
|
|
96190
|
+
} catch (err) {
|
|
96191
|
+
if (err instanceof SessionError) throw err;
|
|
96192
|
+
throw new SessionError(file, `Could not read session: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
96193
|
+
}
|
|
96194
|
+
const session = parseSession(file, text);
|
|
96195
|
+
if (session.meta.id !== id) throw new SessionError(file, `Session header id "${session.meta.id}" does not match the file name`);
|
|
96196
|
+
return session;
|
|
96197
|
+
}
|
|
96198
|
+
/** Newest first. Unreadable files are reported, not skipped silently. */
|
|
96199
|
+
async list() {
|
|
96200
|
+
let names;
|
|
96201
|
+
try {
|
|
96202
|
+
names = await readdir(this.dir);
|
|
96203
|
+
} catch (err) {
|
|
96204
|
+
if (err.code === "ENOENT") return { sessions: [], errors: [] };
|
|
96205
|
+
throw new SessionError(this.dir, `Could not list sessions: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
96206
|
+
}
|
|
96207
|
+
const sessions = [];
|
|
96208
|
+
const errors = [];
|
|
96209
|
+
for (const name of names.filter((n) => n.endsWith(".jsonl"))) {
|
|
96210
|
+
try {
|
|
96211
|
+
sessions.push((await this.load(name.slice(0, -".jsonl".length))).meta);
|
|
96212
|
+
} catch (err) {
|
|
96213
|
+
errors.push(err instanceof SessionError ? err : new SessionError(path.join(this.dir, name), String(err), { cause: err }));
|
|
96214
|
+
}
|
|
96215
|
+
}
|
|
96216
|
+
sessions.sort((a, b2) => b2.updatedAt.localeCompare(a.updatedAt));
|
|
96217
|
+
return { sessions, errors };
|
|
96218
|
+
}
|
|
96219
|
+
/**
|
|
96220
|
+
* The last assistant text of a session (first 120 chars, credential-redacted), read from the end of the file so a
|
|
96221
|
+
* large session is not loaded whole. null when none is found in the tail.
|
|
96222
|
+
*/
|
|
96223
|
+
async preview(id) {
|
|
96224
|
+
const file = this.fileFor(id);
|
|
96225
|
+
try {
|
|
96226
|
+
const { size } = await stat(file);
|
|
96227
|
+
const length = Math.min(size, PREVIEW_TAIL_BYTES);
|
|
96228
|
+
const handle = await open(file, "r");
|
|
96229
|
+
let tail;
|
|
96230
|
+
try {
|
|
96231
|
+
const buf = Buffer.alloc(length);
|
|
96232
|
+
await handle.read(buf, 0, length, size - length);
|
|
96233
|
+
tail = buf.toString("utf8");
|
|
96234
|
+
} finally {
|
|
96235
|
+
await handle.close();
|
|
96236
|
+
}
|
|
96237
|
+
const lines2 = tail.split("\n").filter((l) => l.length > 0);
|
|
96238
|
+
if (length < size) lines2.shift();
|
|
96239
|
+
for (let i = lines2.length - 1; i >= 0; i--) {
|
|
96240
|
+
let msg;
|
|
96241
|
+
try {
|
|
96242
|
+
msg = JSON.parse(lines2[i]);
|
|
96243
|
+
} catch {
|
|
96244
|
+
continue;
|
|
96245
|
+
}
|
|
96246
|
+
if (msg.role !== "assistant") continue;
|
|
96247
|
+
const text = typeof msg.content === "string" ? msg.content : msg.content.map((b2) => b2.text ?? "").join(" ");
|
|
96248
|
+
const oneLine = redactCredentials(text.replace(/\s+/g, " ").trim());
|
|
96249
|
+
if (oneLine) return oneLine.length > 120 ? `${oneLine.slice(0, 119)}\u2026` : oneLine;
|
|
96250
|
+
}
|
|
96251
|
+
return null;
|
|
96252
|
+
} catch (err) {
|
|
96253
|
+
throw new SessionError(file, `Could not read session preview: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
96254
|
+
}
|
|
96255
|
+
}
|
|
96256
|
+
async delete(id) {
|
|
96257
|
+
const file = this.fileFor(id);
|
|
96258
|
+
try {
|
|
96259
|
+
await rm(file);
|
|
96260
|
+
} catch (err) {
|
|
96261
|
+
throw new SessionError(file, `Could not delete session: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
96262
|
+
}
|
|
96263
|
+
}
|
|
96264
|
+
};
|
|
95822
96265
|
}
|
|
95823
96266
|
});
|
|
95824
96267
|
|
|
@@ -95865,6 +96308,287 @@ var init_diff_rows = __esm({
|
|
|
95865
96308
|
}
|
|
95866
96309
|
});
|
|
95867
96310
|
|
|
96311
|
+
// src/render/step-record.ts
|
|
96312
|
+
function cutToBytes(text, max) {
|
|
96313
|
+
let out = text;
|
|
96314
|
+
while (byteLength(out) > max) out = out.slice(0, Math.max(0, Math.floor(out.length * (max / byteLength(out)) * 0.98) - 1));
|
|
96315
|
+
const last = out.charCodeAt(out.length - 1);
|
|
96316
|
+
if (last >= 55296 && last <= 56319) out = out.slice(0, -1);
|
|
96317
|
+
return out;
|
|
96318
|
+
}
|
|
96319
|
+
function redactLines(raw) {
|
|
96320
|
+
let inKey = false;
|
|
96321
|
+
return raw.split("\n").map((line) => {
|
|
96322
|
+
if (inKey) {
|
|
96323
|
+
if (PEM_END.test(line)) inKey = false;
|
|
96324
|
+
return REDACTED;
|
|
96325
|
+
}
|
|
96326
|
+
if (PEM_BEGIN.test(line)) {
|
|
96327
|
+
inKey = !PEM_END.test(line);
|
|
96328
|
+
return REDACTED;
|
|
96329
|
+
}
|
|
96330
|
+
return containsCredential(line) ? REDACTED : line;
|
|
96331
|
+
});
|
|
96332
|
+
}
|
|
96333
|
+
function capDetail(raw) {
|
|
96334
|
+
const lines2 = redactLines(raw);
|
|
96335
|
+
const kept = lines2.slice(0, MAX_DETAIL_LINES);
|
|
96336
|
+
let text = kept.join("\n");
|
|
96337
|
+
const notes = [];
|
|
96338
|
+
if (lines2.length > kept.length) notes.push(`${lines2.length - kept.length} more line${lines2.length - kept.length === 1 ? "" : "s"} not kept`);
|
|
96339
|
+
if (byteLength(text) > MAX_DETAIL_BYTES) {
|
|
96340
|
+
text = cutToBytes(text, MAX_DETAIL_BYTES);
|
|
96341
|
+
notes.push(`truncated to ${MAX_DETAIL_BYTES / 1024} KB`);
|
|
96342
|
+
}
|
|
96343
|
+
return notes.length > 0 ? `${text}
|
|
96344
|
+
\u2026 ${notes.join("; ")}` : text;
|
|
96345
|
+
}
|
|
96346
|
+
function detailOf(state) {
|
|
96347
|
+
const { tool, input, result, outputLines } = state;
|
|
96348
|
+
if (tool === "edit_file" && typeof input.old_string === "string" && typeof input.new_string === "string") {
|
|
96349
|
+
const { rows, hidden } = diffRows(input.old_string, input.new_string, MAX_DETAIL_LINES);
|
|
96350
|
+
const body = rows.map((r) => `${String(r.n).padStart(4)} ${r.kind === "add" ? "+" : "-"} ${r.text}`).join("\n");
|
|
96351
|
+
return hidden > 0 ? `${body}
|
|
96352
|
+
\u2026 ${hidden} more line${hidden === 1 ? "" : "s"} not kept` : body;
|
|
96353
|
+
}
|
|
96354
|
+
if (tool === "write_file" && typeof input.content === "string") return input.content;
|
|
96355
|
+
if (result) return result.output;
|
|
96356
|
+
return outputLines.length > 0 ? outputLines.join("\n") : null;
|
|
96357
|
+
}
|
|
96358
|
+
function metaOf(state) {
|
|
96359
|
+
const { result } = state;
|
|
96360
|
+
const parts = [];
|
|
96361
|
+
if (state.status === "error") parts.push("failed");
|
|
96362
|
+
if (result?.duration_ms != null) parts.push(`${(result.duration_ms / 1e3).toFixed(1)}s`);
|
|
96363
|
+
return parts.join(" \xB7 ");
|
|
96364
|
+
}
|
|
96365
|
+
function toStepRecord(state) {
|
|
96366
|
+
const { verb, target } = stepParts(state.tool, state.input);
|
|
96367
|
+
const raw = detailOf(state);
|
|
96368
|
+
return {
|
|
96369
|
+
id: state.id,
|
|
96370
|
+
tool: state.tool,
|
|
96371
|
+
verb,
|
|
96372
|
+
target,
|
|
96373
|
+
state: state.status === "running" ? "running" : state.status === "error" ? "failed" : "ok",
|
|
96374
|
+
meta: metaOf(state),
|
|
96375
|
+
durationMs: state.result?.duration_ms,
|
|
96376
|
+
detail: raw === null ? null : capDetail(raw)
|
|
96377
|
+
};
|
|
96378
|
+
}
|
|
96379
|
+
var MAX_DETAIL_LINES, MAX_DETAIL_BYTES, MAX_SESSION_BYTES2, NOT_KEPT, encoder, byteLength, PEM_BEGIN, PEM_END, StepStore;
|
|
96380
|
+
var init_step_record = __esm({
|
|
96381
|
+
"src/render/step-record.ts"() {
|
|
96382
|
+
"use strict";
|
|
96383
|
+
init_session_store();
|
|
96384
|
+
init_credential_filter();
|
|
96385
|
+
init_diff_rows();
|
|
96386
|
+
init_tool_header();
|
|
96387
|
+
MAX_DETAIL_LINES = 200;
|
|
96388
|
+
MAX_DETAIL_BYTES = 20 * 1024;
|
|
96389
|
+
MAX_SESSION_BYTES2 = 5 * 1024 * 1024;
|
|
96390
|
+
NOT_KEPT = "output not kept";
|
|
96391
|
+
encoder = new TextEncoder();
|
|
96392
|
+
byteLength = (s) => encoder.encode(s).length;
|
|
96393
|
+
PEM_BEGIN = /-----BEGIN [A-Z ]*PRIVATE KEY-----/;
|
|
96394
|
+
PEM_END = /-----END [A-Z ]*PRIVATE KEY-----/;
|
|
96395
|
+
StepStore = class {
|
|
96396
|
+
constructor(maxBytes = MAX_SESSION_BYTES2) {
|
|
96397
|
+
this.maxBytes = maxBytes;
|
|
96398
|
+
}
|
|
96399
|
+
records = [];
|
|
96400
|
+
bytes = 0;
|
|
96401
|
+
/** Insert or replace by id (a running step is updated as it finishes). */
|
|
96402
|
+
upsert(record) {
|
|
96403
|
+
const at = this.records.findIndex((r) => r.id === record.id);
|
|
96404
|
+
if (at >= 0) {
|
|
96405
|
+
this.bytes -= byteLength(this.records[at].detail ?? "");
|
|
96406
|
+
this.records[at] = record;
|
|
96407
|
+
} else {
|
|
96408
|
+
this.records.push(record);
|
|
96409
|
+
}
|
|
96410
|
+
this.bytes += byteLength(record.detail ?? "");
|
|
96411
|
+
for (let i = 0; i < this.records.length && this.bytes > this.maxBytes; i++) {
|
|
96412
|
+
const old = this.records[i];
|
|
96413
|
+
if (old.id === record.id || old.detail === null) continue;
|
|
96414
|
+
this.bytes -= byteLength(old.detail);
|
|
96415
|
+
this.records[i] = { ...old, detail: null };
|
|
96416
|
+
}
|
|
96417
|
+
}
|
|
96418
|
+
get(id) {
|
|
96419
|
+
return this.records.find((r) => r.id === id);
|
|
96420
|
+
}
|
|
96421
|
+
all() {
|
|
96422
|
+
return this.records;
|
|
96423
|
+
}
|
|
96424
|
+
get detailBytes() {
|
|
96425
|
+
return this.bytes;
|
|
96426
|
+
}
|
|
96427
|
+
};
|
|
96428
|
+
}
|
|
96429
|
+
});
|
|
96430
|
+
|
|
96431
|
+
// src/render/describe-error.ts
|
|
96432
|
+
function sanitizeCause(raw) {
|
|
96433
|
+
const lines2 = raw.split("\n").map((l) => l.trim()).filter(Boolean).filter((l) => !/^\s*at\s+\S+\s+\(.*:\d+:\d+\)\s*$|^\s*at\s+\S+:\d+:\d+\s*$/.test(l)).slice(0, MAX_CAUSE_LINES);
|
|
96434
|
+
const safe = lines2.map((l) => containsCredential(l) ? "[redacted: credential-shaped content]" : l).join(" ");
|
|
96435
|
+
return safe.length > MAX_CAUSE_CHARS ? `${safe.slice(0, MAX_CAUSE_CHARS - 1)}\u2026` : safe || "No details were reported.";
|
|
96436
|
+
}
|
|
96437
|
+
function classifyError(message) {
|
|
96438
|
+
const api = /^API error (\d{3})\b/.exec(message);
|
|
96439
|
+
if (api) {
|
|
96440
|
+
const status = Number(api[1]);
|
|
96441
|
+
if (status === 401 || status === 403) return { kind: "auth", status };
|
|
96442
|
+
if (status === 402) return { kind: "payment", status };
|
|
96443
|
+
if (status === 429) return { kind: "rate-limit", status };
|
|
96444
|
+
if (status >= 500) return { kind: "gateway", status };
|
|
96445
|
+
return { kind: "unknown", status };
|
|
96446
|
+
}
|
|
96447
|
+
if (/^No API key found/.test(message)) return { kind: "missing-key" };
|
|
96448
|
+
if (/^Not approved\b/.test(message)) return { kind: "policy" };
|
|
96449
|
+
if (NETWORK.test(message)) return { kind: "network" };
|
|
96450
|
+
return { kind: "unknown" };
|
|
96451
|
+
}
|
|
96452
|
+
function bodyMessage(cause) {
|
|
96453
|
+
const json = cause.slice(cause.indexOf(":") + 1).trim();
|
|
96454
|
+
try {
|
|
96455
|
+
const parsed = JSON.parse(json);
|
|
96456
|
+
const m2 = parsed.error?.message ?? parsed.message;
|
|
96457
|
+
return typeof m2 === "string" && m2.trim() ? m2.trim() : null;
|
|
96458
|
+
} catch {
|
|
96459
|
+
return null;
|
|
96460
|
+
}
|
|
96461
|
+
}
|
|
96462
|
+
function defaultHost() {
|
|
96463
|
+
try {
|
|
96464
|
+
return new URL(process.env.API_PROXY_URL || "https://api.spacecode.app").host;
|
|
96465
|
+
} catch {
|
|
96466
|
+
return "the gateway";
|
|
96467
|
+
}
|
|
96468
|
+
}
|
|
96469
|
+
function describeError(kind, cause, ctx = {}) {
|
|
96470
|
+
const retained = ctx.retained === true;
|
|
96471
|
+
const shown = sanitizeCause(cause);
|
|
96472
|
+
const status = ctx.status;
|
|
96473
|
+
switch (kind) {
|
|
96474
|
+
case "gateway":
|
|
96475
|
+
return {
|
|
96476
|
+
title: `Gateway didn't answer${status ? ` (${status})` : ""}`,
|
|
96477
|
+
cause: `${ctx.host ?? defaultHost()} is unavailable.${retained ? " Your prompt is saved." : ""}`,
|
|
96478
|
+
action: retained ? "Press enter to retry, or run /status to check the gateway." : "Run /status to check the gateway, then send your prompt again."
|
|
96479
|
+
};
|
|
96480
|
+
case "rate-limit":
|
|
96481
|
+
return {
|
|
96482
|
+
title: `Rate limited${status ? ` (${status})` : ""}`,
|
|
96483
|
+
cause: `The gateway is getting too many requests; it already retried a few times.${retained ? " Your prompt is saved." : ""}`,
|
|
96484
|
+
action: retained ? "Wait a moment, then press enter to retry." : "Wait a moment, then send your prompt again."
|
|
96485
|
+
};
|
|
96486
|
+
case "auth":
|
|
96487
|
+
return {
|
|
96488
|
+
title: `Sign-in rejected${status ? ` (${status})` : ""}`,
|
|
96489
|
+
cause: "The gateway did not accept your key.",
|
|
96490
|
+
action: retained ? "Run `spacecode login` in another terminal, then press enter to retry." : "Run `spacecode login`, then send your prompt again."
|
|
96491
|
+
};
|
|
96492
|
+
case "missing-key":
|
|
96493
|
+
return {
|
|
96494
|
+
title: "No API key",
|
|
96495
|
+
cause: "The REPL needs a key to call the gateway.",
|
|
96496
|
+
action: retained ? "Run `spacecode login` in another terminal or set SPACECODE_API_KEY, then press enter to retry." : "Run `spacecode login` or set SPACECODE_API_KEY, then send your prompt again."
|
|
96497
|
+
};
|
|
96498
|
+
case "payment":
|
|
96499
|
+
return {
|
|
96500
|
+
title: `Payment required${status ? ` (${status})` : ""}`,
|
|
96501
|
+
cause: sanitizeCause(bodyMessage(cause) ?? "Your plan does not include this."),
|
|
96502
|
+
action: `Open ${BILLING_URL} to choose a plan.`
|
|
96503
|
+
};
|
|
96504
|
+
case "network":
|
|
96505
|
+
return {
|
|
96506
|
+
title: "Can't reach the gateway",
|
|
96507
|
+
cause: `${shown}${retained ? " Your prompt is saved." : ""}`,
|
|
96508
|
+
action: retained ? "Check your connection, then press enter to retry." : "Check your connection, then send your prompt again."
|
|
96509
|
+
};
|
|
96510
|
+
case "session-save":
|
|
96511
|
+
return {
|
|
96512
|
+
title: "Session not saved",
|
|
96513
|
+
cause: shown,
|
|
96514
|
+
action: "Your conversation continues. Free some space or run /compact; the next turn tries to save again."
|
|
96515
|
+
};
|
|
96516
|
+
case "policy":
|
|
96517
|
+
return {
|
|
96518
|
+
title: "Not approved",
|
|
96519
|
+
cause: shown.replace(/^Not approved\s*\(([^)]*)\):\s*(.*)$/, "$2 ($1)").replace(/^Not approved\s*/, ""),
|
|
96520
|
+
action: "Press shift+tab to change the permission mode, or ask the agent to take another approach."
|
|
96521
|
+
};
|
|
96522
|
+
case "unknown":
|
|
96523
|
+
return { title: "Something went wrong", cause: shown, action: "Run /status for the session state, or send your prompt again." };
|
|
96524
|
+
}
|
|
96525
|
+
}
|
|
96526
|
+
function describeEventError(message, ctx = {}) {
|
|
96527
|
+
const { kind, status } = classifyError(message);
|
|
96528
|
+
return describeError(kind, message, { ...ctx, status: ctx.status ?? status });
|
|
96529
|
+
}
|
|
96530
|
+
function isRetryable(message) {
|
|
96531
|
+
const { kind } = classifyError(message);
|
|
96532
|
+
return kind === "gateway" || kind === "rate-limit" || kind === "network" || kind === "missing-key" || kind === "auth";
|
|
96533
|
+
}
|
|
96534
|
+
var BILLING_URL, MAX_CAUSE_LINES, MAX_CAUSE_CHARS, NETWORK;
|
|
96535
|
+
var init_describe_error = __esm({
|
|
96536
|
+
"src/render/describe-error.ts"() {
|
|
96537
|
+
"use strict";
|
|
96538
|
+
init_credential_filter();
|
|
96539
|
+
BILLING_URL = "https://spacecode.g-a-l-a-c-t-i-c.com";
|
|
96540
|
+
MAX_CAUSE_LINES = 3;
|
|
96541
|
+
MAX_CAUSE_CHARS = 300;
|
|
96542
|
+
NETWORK = /fetch failed|ENOTFOUND|ECONNREFUSED|ECONNRESET|ETIMEDOUT|EAI_AGAIN|network (error|request failed|is unreachable)|socket hang up/i;
|
|
96543
|
+
}
|
|
96544
|
+
});
|
|
96545
|
+
|
|
96546
|
+
// src/render/step-view.ts
|
|
96547
|
+
function stepsOfEvents(events) {
|
|
96548
|
+
return processEvents(events).renderOrder.flatMap((item) => item.type === "tool" ? [toStepRecord(item.state)] : []);
|
|
96549
|
+
}
|
|
96550
|
+
function stepsOfTurns(turns, maxBytes) {
|
|
96551
|
+
const store = new StepStore(maxBytes);
|
|
96552
|
+
for (const turn of turns) for (const record of stepsOfEvents(turn.events)) store.upsert({ ...record, id: `${turn.id}:${record.id}` });
|
|
96553
|
+
const byTurn = /* @__PURE__ */ new Map();
|
|
96554
|
+
for (const record of store.all()) {
|
|
96555
|
+
const turnId = Number(record.id.slice(0, record.id.indexOf(":")));
|
|
96556
|
+
const list2 = byTurn.get(turnId) ?? [];
|
|
96557
|
+
list2.push({ ...record, id: record.id.slice(record.id.indexOf(":") + 1) });
|
|
96558
|
+
byTurn.set(turnId, list2);
|
|
96559
|
+
}
|
|
96560
|
+
return byTurn;
|
|
96561
|
+
}
|
|
96562
|
+
function detailWindow(detail, maxLines, width, offset = 0) {
|
|
96563
|
+
if (detail === null) return { lines: [NOT_KEPT], above: 0, below: 0 };
|
|
96564
|
+
const rows = [];
|
|
96565
|
+
for (const line of detail.split("\n")) {
|
|
96566
|
+
if (line.length <= width) rows.push(line);
|
|
96567
|
+
else for (let i = 0; i < line.length; i += width) rows.push(line.slice(i, i + width));
|
|
96568
|
+
}
|
|
96569
|
+
const room = Math.max(1, maxLines);
|
|
96570
|
+
const start = Math.min(Math.max(0, offset), Math.max(0, rows.length - room));
|
|
96571
|
+
return { lines: rows.slice(start, start + room), above: start, below: Math.max(0, rows.length - start - room) };
|
|
96572
|
+
}
|
|
96573
|
+
function findBlockedStep(steps, req) {
|
|
96574
|
+
const tools = TOOLS_FOR_KIND[req.kind];
|
|
96575
|
+
if (!tools) return void 0;
|
|
96576
|
+
const subject = req.kind === "exec" ? req.subject.split("\n")[0] : req.subject;
|
|
96577
|
+
return steps.find((r) => {
|
|
96578
|
+
if (r.state !== "running" || !tools.includes(r.tool)) return false;
|
|
96579
|
+
return r.target === subject;
|
|
96580
|
+
});
|
|
96581
|
+
}
|
|
96582
|
+
var TOOLS_FOR_KIND;
|
|
96583
|
+
var init_step_view = __esm({
|
|
96584
|
+
"src/render/step-view.ts"() {
|
|
96585
|
+
"use strict";
|
|
96586
|
+
init_useEventAccumulator();
|
|
96587
|
+
init_step_record();
|
|
96588
|
+
TOOLS_FOR_KIND = { exec: ["bash"], edit_file: ["edit_file"], write_file: ["write_file"] };
|
|
96589
|
+
}
|
|
96590
|
+
});
|
|
96591
|
+
|
|
95868
96592
|
// src/render/highlight.ts
|
|
95869
96593
|
function languageFor(filePath) {
|
|
95870
96594
|
if (!filePath) return null;
|
|
@@ -95916,111 +96640,156 @@ var init_highlight = __esm({
|
|
|
95916
96640
|
});
|
|
95917
96641
|
|
|
95918
96642
|
// src/components/ToolCallBlock.tsx
|
|
95919
|
-
function
|
|
96643
|
+
function formatDuration2(ms) {
|
|
95920
96644
|
return (ms / 1e3).toFixed(1) + "s";
|
|
95921
96645
|
}
|
|
95922
|
-
function summarize(output) {
|
|
96646
|
+
function summarize(output, ellipsis = "\u2026") {
|
|
95923
96647
|
const lines2 = output.split("\n").filter((l) => l.trim().length > 0);
|
|
95924
96648
|
if (lines2.length === 0) return "(no output)";
|
|
95925
|
-
const first = lines2[0].length > MAX_SUMMARY_CHARS ? lines2[0].slice(0, MAX_SUMMARY_CHARS) +
|
|
96649
|
+
const first = lines2[0].length > MAX_SUMMARY_CHARS ? lines2[0].slice(0, MAX_SUMMARY_CHARS) + ellipsis : lines2[0];
|
|
95926
96650
|
return lines2.length > 1 ? `${first} (+${lines2.length - 1} lines)` : first;
|
|
95927
96651
|
}
|
|
95928
96652
|
function DiffBlock({ oldStr, newStr, filePath }) {
|
|
95929
96653
|
const { rows, hidden } = diffRows(oldStr, newStr);
|
|
95930
|
-
|
|
96654
|
+
const ellipsis = activeGlyphs().ellipsis;
|
|
96655
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Box_default, { flexDirection: "column", children: [
|
|
95931
96656
|
rows.map((row, i) => {
|
|
95932
96657
|
const isAdd = row.kind === "add";
|
|
95933
|
-
const
|
|
95934
|
-
return /* @__PURE__ */ (0,
|
|
95935
|
-
/* @__PURE__ */ (0,
|
|
96658
|
+
const role = isAdd ? "add" : "delete";
|
|
96659
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { children: [
|
|
96660
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { ...paint(role), children: [
|
|
95936
96661
|
" ",
|
|
95937
96662
|
String(row.n).padStart(4),
|
|
95938
96663
|
" ",
|
|
95939
96664
|
isAdd ? "+" : "-",
|
|
95940
96665
|
" "
|
|
95941
96666
|
] }),
|
|
95942
|
-
isAdd ? highlightLine(row.text, filePath) : /* @__PURE__ */ (0,
|
|
96667
|
+
isAdd ? highlightLine(row.text, filePath) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { ...paint(role), children: row.text })
|
|
95943
96668
|
] }, i);
|
|
95944
96669
|
}),
|
|
95945
|
-
hidden > 0 && /* @__PURE__ */ (0,
|
|
95946
|
-
|
|
96670
|
+
hidden > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { ...paint("secondary"), children: [
|
|
96671
|
+
` ${ellipsis} +`,
|
|
95947
96672
|
hidden,
|
|
95948
96673
|
" lines"
|
|
95949
96674
|
] })
|
|
95950
96675
|
] });
|
|
95951
96676
|
}
|
|
95952
|
-
function ToolCallBlock({ state }) {
|
|
96677
|
+
function ToolCallBlock({ state, focused = false, open: open3 = false, detailRows = 12, waiting = false }) {
|
|
95953
96678
|
const { tool, input, status, outputLines, result } = state;
|
|
95954
|
-
const
|
|
96679
|
+
const { columns } = useTerminalSize();
|
|
96680
|
+
const glyphs = activeGlyphs();
|
|
96681
|
+
const plain = plainOutput();
|
|
96682
|
+
const unicode = glyphs.rail === "\u2502";
|
|
96683
|
+
const ellipsis = glyphs.ellipsis;
|
|
96684
|
+
const parts = stepParts(tool, input);
|
|
96685
|
+
const twist = open3 ? unicode ? "\u25BE" : "v" : unicode ? "\u25B8" : ">";
|
|
96686
|
+
if (status === "running" && waiting) {
|
|
96687
|
+
const line2 = fitStepLine({ ...parts, meta: "waiting for you", columns, indent: plain ? 0 : 4, ellipsis, sep: glyphs.sep });
|
|
96688
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Box_default, { children: [
|
|
96689
|
+
!plain && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { ...paint("needsYou"), children: [
|
|
96690
|
+
glyphs.marker.waiting,
|
|
96691
|
+
" "
|
|
96692
|
+
] }),
|
|
96693
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { children: [
|
|
96694
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { ...paint("action"), children: line2.verb }),
|
|
96695
|
+
" ",
|
|
96696
|
+
line2.target
|
|
96697
|
+
] }),
|
|
96698
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { ...paint("needsYou"), children: ` ${glyphs.sep} waiting for you` })
|
|
96699
|
+
] });
|
|
96700
|
+
}
|
|
95955
96701
|
if (status === "running") {
|
|
95956
|
-
return /* @__PURE__ */ (0,
|
|
95957
|
-
/* @__PURE__ */ (0,
|
|
95958
|
-
/* @__PURE__ */ (0,
|
|
95959
|
-
/* @__PURE__ */ (0,
|
|
96702
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Box_default, { flexDirection: "column", children: [
|
|
96703
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Box_default, { children: [
|
|
96704
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { ...paint("action"), children: [
|
|
96705
|
+
plain ? null : unicode ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(build_default, { type: "dots" }) : glyphs.marker.running,
|
|
96706
|
+
focused ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { bold: true, children: twist }) : plain ? "" : " ",
|
|
96707
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { bold: true, inverse: focused, children: parts.verb }),
|
|
95960
96708
|
" ",
|
|
95961
|
-
|
|
96709
|
+
fitStepLine({ ...parts, meta: "running", columns, indent: plain ? 0 : 4, ellipsis, sep: glyphs.sep }).target
|
|
95962
96710
|
] }),
|
|
95963
|
-
/* @__PURE__ */ (0,
|
|
96711
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { ...paint("secondary"), children: ` ${glyphs.sep} running` })
|
|
95964
96712
|
] }),
|
|
95965
|
-
|
|
96713
|
+
open3 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(StepDetail, { state, rows: detailRows, columns }),
|
|
96714
|
+
!open3 && outputLines.map((line2, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { ...paint("secondary"), children: [
|
|
95966
96715
|
" ",
|
|
95967
|
-
|
|
96716
|
+
glyphs.rail,
|
|
95968
96717
|
" ",
|
|
95969
|
-
|
|
96718
|
+
line2.length > MAX_RUNNING_LINE_CHARS ? `${line2.slice(0, MAX_RUNNING_LINE_CHARS)}${ellipsis} (+${line2.length - MAX_RUNNING_LINE_CHARS} chars)` : line2
|
|
95970
96719
|
] }, i))
|
|
95971
96720
|
] });
|
|
95972
96721
|
}
|
|
95973
96722
|
const isError = status === "error";
|
|
95974
|
-
const
|
|
96723
|
+
const metaText = [isError ? "failed" : "", result?.duration_ms != null ? formatDuration2(result.duration_ms) : ""].filter(Boolean).join(` ${glyphs.sep} `);
|
|
96724
|
+
const line = fitStepLine({ ...parts, meta: metaText || void 0, columns, indent: plain ? 0 : 4, ellipsis, sep: glyphs.sep });
|
|
95975
96725
|
const output = result?.output ?? "";
|
|
95976
96726
|
let body = null;
|
|
95977
96727
|
if (isError && output) {
|
|
95978
96728
|
const lines2 = output.split("\n");
|
|
96729
|
+
const policyRefusal = /^Not approved\b/.test(output);
|
|
95979
96730
|
const shown = lines2.slice(0, MAX_ERROR_LINES);
|
|
95980
96731
|
const more = lines2.length - shown.length;
|
|
95981
|
-
body = /* @__PURE__ */ (0,
|
|
95982
|
-
shown.map((l, i) => /* @__PURE__ */ (0,
|
|
95983
|
-
|
|
96732
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Box_default, { flexDirection: "column", children: [
|
|
96733
|
+
shown.map((l, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { ...paint("delete"), children: [
|
|
96734
|
+
` ${glyphs.result} `,
|
|
95984
96735
|
l
|
|
95985
96736
|
] }, i)),
|
|
95986
|
-
more > 0 && /* @__PURE__ */ (0,
|
|
95987
|
-
|
|
96737
|
+
more > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { ...paint("secondary"), children: [
|
|
96738
|
+
` ${ellipsis} `,
|
|
95988
96739
|
more,
|
|
95989
96740
|
" more"
|
|
95990
|
-
] })
|
|
96741
|
+
] }),
|
|
96742
|
+
policyRefusal && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { ...paint("secondary"), children: ` ${glyphs.result} ${describeError("policy", output).action}` })
|
|
95991
96743
|
] });
|
|
95992
96744
|
} else if (tool === "edit_file" && typeof input.old_string === "string" && typeof input.new_string === "string") {
|
|
95993
|
-
body = /* @__PURE__ */ (0,
|
|
96745
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(DiffBlock, { oldStr: input.old_string, newStr: input.new_string, filePath: typeof input.path === "string" ? input.path : void 0 });
|
|
95994
96746
|
} else if (tool === "write_file" && typeof input.content === "string") {
|
|
95995
|
-
body = /* @__PURE__ */ (0,
|
|
96747
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(DiffBlock, { oldStr: "", newStr: input.content, filePath: typeof input.path === "string" ? input.path : void 0 });
|
|
95996
96748
|
} else if (output) {
|
|
95997
|
-
body = /* @__PURE__ */ (0,
|
|
95998
|
-
|
|
95999
|
-
summarize(output)
|
|
96749
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { ...paint("secondary"), children: [
|
|
96750
|
+
` ${glyphs.result} `,
|
|
96751
|
+
summarize(output, ellipsis)
|
|
96000
96752
|
] });
|
|
96001
96753
|
}
|
|
96002
|
-
return /* @__PURE__ */ (0,
|
|
96003
|
-
/* @__PURE__ */ (0,
|
|
96004
|
-
/* @__PURE__ */ (0,
|
|
96005
|
-
/* @__PURE__ */ (0,
|
|
96754
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Box_default, { flexDirection: "column", children: [
|
|
96755
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Box_default, { children: [
|
|
96756
|
+
!plain && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { ...paint(isError ? "delete" : "add"), children: isError ? glyphs.marker.failed : glyphs.marker.ok }),
|
|
96757
|
+
!plain && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { bold: focused, children: focused ? twist : " " }),
|
|
96758
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { inverse: focused, children: [
|
|
96759
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { ...paint("action"), children: line.verb }),
|
|
96006
96760
|
" ",
|
|
96007
|
-
|
|
96761
|
+
line.target
|
|
96008
96762
|
] }),
|
|
96009
|
-
|
|
96763
|
+
line.meta && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { ...paint("secondary"), children: ` ${glyphs.sep} ${line.meta}` })
|
|
96010
96764
|
] }),
|
|
96011
|
-
body
|
|
96765
|
+
open3 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(StepDetail, { state, rows: detailRows, columns }) : body
|
|
96766
|
+
] });
|
|
96767
|
+
}
|
|
96768
|
+
function StepDetail({ state, rows, columns }) {
|
|
96769
|
+
const record = toStepRecord(state);
|
|
96770
|
+
const detail = record.detail ?? (state.status === "running" ? "no output yet" : null);
|
|
96771
|
+
const win = detailWindow(detail, rows, Math.max(20, columns - 8));
|
|
96772
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Box_default, { flexDirection: "column", marginLeft: 3, children: [
|
|
96773
|
+
win.lines.map((l, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { ...paint("secondary"), children: l }, i)),
|
|
96774
|
+
win.below > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { ...paint("secondary"), children: `${activeGlyphs().ellipsis} ${win.below} more line${win.below === 1 ? "" : "s"}` })
|
|
96012
96775
|
] });
|
|
96013
96776
|
}
|
|
96014
|
-
var
|
|
96777
|
+
var import_jsx_runtime5, MAX_ERROR_LINES, MAX_RUNNING_LINE_CHARS, MAX_SUMMARY_CHARS;
|
|
96015
96778
|
var init_ToolCallBlock = __esm({
|
|
96016
96779
|
async "src/components/ToolCallBlock.tsx"() {
|
|
96017
96780
|
"use strict";
|
|
96018
96781
|
await init_build2();
|
|
96019
96782
|
await init_build3();
|
|
96020
96783
|
init_tool_header();
|
|
96784
|
+
init_rail();
|
|
96785
|
+
await init_useTerminalSize();
|
|
96786
|
+
init_step_record();
|
|
96787
|
+
init_describe_error();
|
|
96788
|
+
init_step_view();
|
|
96021
96789
|
init_diff_rows();
|
|
96022
96790
|
init_highlight();
|
|
96023
|
-
|
|
96791
|
+
init_theme();
|
|
96792
|
+
import_jsx_runtime5 = __toESM(require_jsx_runtime(), 1);
|
|
96024
96793
|
MAX_ERROR_LINES = 8;
|
|
96025
96794
|
MAX_RUNNING_LINE_CHARS = 200;
|
|
96026
96795
|
MAX_SUMMARY_CHARS = 100;
|
|
@@ -96028,54 +96797,114 @@ var init_ToolCallBlock = __esm({
|
|
|
96028
96797
|
});
|
|
96029
96798
|
|
|
96030
96799
|
// src/components/EventRenderer.tsx
|
|
96031
|
-
function EventRenderer({ events, liveMaxLines }) {
|
|
96800
|
+
function EventRenderer({ events, liveMaxLines, focusId = null, openId = null, detailRows, waitingId = null, prompt, promptSaved = false, deferErrors = false }) {
|
|
96032
96801
|
const { renderOrder, statusMessage } = useEventAccumulator(events);
|
|
96033
|
-
|
|
96034
|
-
|
|
96802
|
+
const glyphs = activeGlyphs();
|
|
96803
|
+
const railStyle = { topLeft: "", top: "", topRight: "", right: "", bottomRight: "", bottom: "", bottomLeft: "", left: glyphs.rail };
|
|
96804
|
+
const groups = [];
|
|
96805
|
+
for (const item of renderOrder) {
|
|
96806
|
+
const last = groups[groups.length - 1];
|
|
96807
|
+
if (item.type === "tool" && Array.isArray(last)) last.push(item);
|
|
96808
|
+
else groups.push(item.type === "tool" ? [item] : item);
|
|
96809
|
+
}
|
|
96810
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Box_default, { flexDirection: "column", children: [
|
|
96811
|
+
groups.map((group) => {
|
|
96812
|
+
if (Array.isArray(group) && plainOutput()) {
|
|
96813
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Box_default, { flexDirection: "column", children: group.map((item2) => item2.type === "tool" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react27.default.Fragment, { children: [
|
|
96814
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ToolCallBlock, { state: item2.state, focused: item2.id === focusId, open: item2.id === openId, detailRows, waiting: item2.id === waitingId }),
|
|
96815
|
+
item2.id === waitingId && prompt
|
|
96816
|
+
] }, item2.id)) }, group[0].id);
|
|
96817
|
+
}
|
|
96818
|
+
if (Array.isArray(group)) {
|
|
96819
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
96820
|
+
Box_default,
|
|
96821
|
+
{
|
|
96822
|
+
flexDirection: "column",
|
|
96823
|
+
borderStyle: railStyle,
|
|
96824
|
+
borderTop: false,
|
|
96825
|
+
borderBottom: false,
|
|
96826
|
+
borderRight: false,
|
|
96827
|
+
borderColor: paint("secondary").color,
|
|
96828
|
+
paddingLeft: 1,
|
|
96829
|
+
children: group.map((item2) => item2.type === "tool" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react27.default.Fragment, { children: [
|
|
96830
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ToolCallBlock, { state: item2.state, focused: item2.id === focusId, open: item2.id === openId, detailRows, waiting: item2.id === waitingId }),
|
|
96831
|
+
item2.id === waitingId && prompt
|
|
96832
|
+
] }, item2.id))
|
|
96833
|
+
},
|
|
96834
|
+
group[0].id
|
|
96835
|
+
);
|
|
96836
|
+
}
|
|
96837
|
+
const item = group;
|
|
96035
96838
|
switch (item.type) {
|
|
96036
96839
|
case "text":
|
|
96037
|
-
return /* @__PURE__ */ (0,
|
|
96840
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarkdownBlock, { content: item.content, maxLines: liveMaxLines }) }, item.id);
|
|
96038
96841
|
case "tool":
|
|
96039
|
-
return
|
|
96842
|
+
return null;
|
|
96843
|
+
// grouped onto the rail above
|
|
96040
96844
|
case "error":
|
|
96041
|
-
|
|
96042
|
-
|
|
96043
|
-
item.message
|
|
96044
|
-
] }) }, item.id);
|
|
96845
|
+
if (deferErrors) return null;
|
|
96846
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ErrorBlock, { view: describeEventError(item.message, { retained: promptSaved }) }) }, item.id);
|
|
96045
96847
|
default: {
|
|
96046
96848
|
const _exhaustive = item;
|
|
96047
96849
|
return _exhaustive;
|
|
96048
96850
|
}
|
|
96049
96851
|
}
|
|
96050
96852
|
}),
|
|
96051
|
-
statusMessage && /* @__PURE__ */ (0,
|
|
96052
|
-
"\u2022 "
|
|
96853
|
+
statusMessage && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Text, { ...paint("needsYou"), children: [
|
|
96854
|
+
`${glyphs.rail === "\u2502" ? "\u2022" : "*"} `,
|
|
96053
96855
|
statusMessage
|
|
96054
96856
|
] })
|
|
96055
96857
|
] });
|
|
96056
96858
|
}
|
|
96057
|
-
var
|
|
96859
|
+
var import_react27, import_jsx_runtime6;
|
|
96058
96860
|
var init_EventRenderer = __esm({
|
|
96059
96861
|
async "src/components/EventRenderer.tsx"() {
|
|
96060
96862
|
"use strict";
|
|
96863
|
+
import_react27 = __toESM(require_react(), 1);
|
|
96061
96864
|
await init_build2();
|
|
96062
96865
|
init_useEventAccumulator();
|
|
96063
96866
|
await init_MarkdownBlock();
|
|
96064
96867
|
await init_ToolCallBlock();
|
|
96065
|
-
|
|
96868
|
+
init_theme();
|
|
96869
|
+
init_rail();
|
|
96870
|
+
await init_ErrorBlock();
|
|
96871
|
+
init_describe_error();
|
|
96872
|
+
import_jsx_runtime6 = __toESM(require_jsx_runtime(), 1);
|
|
96873
|
+
}
|
|
96874
|
+
});
|
|
96875
|
+
|
|
96876
|
+
// src/render/describe-approval.ts
|
|
96877
|
+
function describeApproval(req) {
|
|
96878
|
+
const reason = req.reason.trim();
|
|
96879
|
+
const sentence = reason === "" ? "" : `${reason[0].toUpperCase()}${reason.slice(1)}${/[.!?]$/.test(reason) ? "" : "."}`;
|
|
96880
|
+
switch (req.kind) {
|
|
96881
|
+
case "exec":
|
|
96882
|
+
return sentence !== "" ? `Runs a shell command. ${sentence}` : "Runs a shell command.";
|
|
96883
|
+
case "edit_file":
|
|
96884
|
+
return sentence !== "" ? `Edits a file in your project. ${sentence}` : "Edits a file in your project.";
|
|
96885
|
+
case "write_file":
|
|
96886
|
+
return sentence !== "" ? `Writes a file in your project. ${sentence}` : "Writes a file in your project.";
|
|
96887
|
+
default:
|
|
96888
|
+
return sentence !== "" ? sentence : "Needs your approval.";
|
|
96889
|
+
}
|
|
96890
|
+
}
|
|
96891
|
+
var init_describe_approval = __esm({
|
|
96892
|
+
"src/render/describe-approval.ts"() {
|
|
96893
|
+
"use strict";
|
|
96066
96894
|
}
|
|
96067
96895
|
});
|
|
96068
96896
|
|
|
96069
96897
|
// src/components/PermissionPrompt.tsx
|
|
96070
96898
|
function permissionOptions(req) {
|
|
96071
|
-
const second = req.kind === "exec" ? `Yes, and don't ask again for this exact command` : "Yes, and allow all edits this session";
|
|
96899
|
+
const second = req.kind === "exec" ? `Yes, and don't ask again for this exact command this session` : "Yes, and allow all edits this session";
|
|
96072
96900
|
return ["Yes", second, "No, tell the agent what to do instead"];
|
|
96073
96901
|
}
|
|
96074
96902
|
function PermissionPrompt({ req, onDecide }) {
|
|
96075
|
-
const [selected, setSelected] = (0,
|
|
96076
|
-
const [typing, setTyping] = (0,
|
|
96077
|
-
const [feedback, setFeedback] = (0,
|
|
96903
|
+
const [selected, setSelected] = (0, import_react28.useState)(0);
|
|
96904
|
+
const [typing, setTyping] = (0, import_react28.useState)(false);
|
|
96905
|
+
const [feedback, setFeedback] = (0, import_react28.useState)("");
|
|
96078
96906
|
const options = permissionOptions(req);
|
|
96907
|
+
const unicode = activeGlyphs().rail === "\u2502";
|
|
96079
96908
|
const { isRawModeSupported } = use_stdin_default();
|
|
96080
96909
|
const choose = (index) => {
|
|
96081
96910
|
if (index === 0) onDecide({ approved: true });
|
|
@@ -96107,39 +96936,42 @@ function PermissionPrompt({ req, onDecide }) {
|
|
|
96107
96936
|
}
|
|
96108
96937
|
}, { isActive: Boolean(isRawModeSupported) });
|
|
96109
96938
|
const title = req.kind === "exec" ? "Run command?" : req.kind === "edit_file" ? "Edit file?" : "Write file?";
|
|
96110
|
-
return /* @__PURE__ */ (0,
|
|
96111
|
-
/* @__PURE__ */ (0,
|
|
96112
|
-
/* @__PURE__ */ (0,
|
|
96113
|
-
req.preview && /* @__PURE__ */ (0,
|
|
96114
|
-
/* @__PURE__ */ (0,
|
|
96115
|
-
req
|
|
96939
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Box_default, { flexDirection: "column", borderStyle: unicode ? "round" : "classic", borderColor: paint("needsYou").color, paddingX: 1, children: [
|
|
96940
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { bold: true, ...paint("needsYou"), children: title }),
|
|
96941
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { children: req.subject }),
|
|
96942
|
+
req.preview && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(DiffBlock, { oldStr: req.preview.oldText, newStr: req.preview.newText, filePath: req.subject }),
|
|
96943
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Text, { ...paint("secondary"), children: [
|
|
96944
|
+
describeApproval(req),
|
|
96116
96945
|
" (",
|
|
96117
96946
|
req.rule,
|
|
96118
96947
|
")"
|
|
96119
96948
|
] }),
|
|
96120
|
-
options.map((label, i) => /* @__PURE__ */ (0,
|
|
96121
|
-
i === selected ? "\u276F " : " ",
|
|
96949
|
+
options.map((label, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Text, { ...i === selected ? paint("action") : {}, children: [
|
|
96950
|
+
i === selected ? unicode ? "\u276F " : "> " : " ",
|
|
96122
96951
|
i + 1,
|
|
96123
96952
|
". ",
|
|
96124
96953
|
label
|
|
96125
96954
|
] }, label)),
|
|
96126
|
-
typing ? /* @__PURE__ */ (0,
|
|
96955
|
+
typing ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Text, { children: [
|
|
96127
96956
|
"Tell the agent what to do instead: ",
|
|
96128
96957
|
feedback,
|
|
96129
|
-
/* @__PURE__ */ (0,
|
|
96958
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { inverse: true, children: " " }),
|
|
96130
96959
|
" ",
|
|
96131
|
-
/* @__PURE__ */ (0,
|
|
96132
|
-
] }) : /* @__PURE__ */ (0,
|
|
96960
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { ...paint("secondary"), children: "(Enter to send, Esc to go back)" })
|
|
96961
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { ...paint("secondary"), children: unicode ? "\u2191/\u2193 Enter or 1-3 to choose \xB7 Esc to refuse" : "up/down Enter or 1-3 to choose - Esc to refuse" })
|
|
96133
96962
|
] });
|
|
96134
96963
|
}
|
|
96135
|
-
var
|
|
96964
|
+
var import_react28, import_jsx_runtime7;
|
|
96136
96965
|
var init_PermissionPrompt = __esm({
|
|
96137
96966
|
async "src/components/PermissionPrompt.tsx"() {
|
|
96138
96967
|
"use strict";
|
|
96139
|
-
|
|
96968
|
+
import_react28 = __toESM(require_react(), 1);
|
|
96140
96969
|
await init_build2();
|
|
96141
96970
|
await init_ToolCallBlock();
|
|
96142
|
-
|
|
96971
|
+
init_describe_approval();
|
|
96972
|
+
init_rail();
|
|
96973
|
+
init_theme();
|
|
96974
|
+
import_jsx_runtime7 = __toESM(require_jsx_runtime(), 1);
|
|
96143
96975
|
}
|
|
96144
96976
|
});
|
|
96145
96977
|
|
|
@@ -96147,10 +96979,10 @@ var init_PermissionPrompt = __esm({
|
|
|
96147
96979
|
function TodoChecklist({ todos }) {
|
|
96148
96980
|
if (todos.length === 0) return null;
|
|
96149
96981
|
const done = todos.filter((t) => t.status === "completed").length;
|
|
96150
|
-
return /* @__PURE__ */ (0,
|
|
96151
|
-
/* @__PURE__ */ (0,
|
|
96982
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Box_default, { flexDirection: "column", marginTop: 1, children: [
|
|
96983
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Text, { bold: true, children: [
|
|
96152
96984
|
"Tasks ",
|
|
96153
|
-
/* @__PURE__ */ (0,
|
|
96985
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Text, { ...paint("secondary"), children: [
|
|
96154
96986
|
"(",
|
|
96155
96987
|
done,
|
|
96156
96988
|
"/",
|
|
@@ -96158,11 +96990,10 @@ function TodoChecklist({ todos }) {
|
|
|
96158
96990
|
")"
|
|
96159
96991
|
] })
|
|
96160
96992
|
] }),
|
|
96161
|
-
todos.map((t, i) => /* @__PURE__ */ (0,
|
|
96993
|
+
todos.map((t, i) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
96162
96994
|
Text,
|
|
96163
96995
|
{
|
|
96164
|
-
|
|
96165
|
-
dimColor: t.status === "completed",
|
|
96996
|
+
...t.status === "in_progress" ? paint("action") : t.status === "completed" ? paint("secondary") : {},
|
|
96166
96997
|
bold: t.status === "in_progress",
|
|
96167
96998
|
strikethrough: t.status === "completed",
|
|
96168
96999
|
children: [
|
|
@@ -96176,35 +97007,17 @@ function TodoChecklist({ todos }) {
|
|
|
96176
97007
|
))
|
|
96177
97008
|
] });
|
|
96178
97009
|
}
|
|
96179
|
-
var
|
|
97010
|
+
var import_jsx_runtime8, TODO_MARKS;
|
|
96180
97011
|
var init_TodoChecklist = __esm({
|
|
96181
97012
|
async "src/components/TodoChecklist.tsx"() {
|
|
96182
97013
|
"use strict";
|
|
96183
97014
|
await init_build2();
|
|
96184
|
-
|
|
97015
|
+
init_theme();
|
|
97016
|
+
import_jsx_runtime8 = __toESM(require_jsx_runtime(), 1);
|
|
96185
97017
|
TODO_MARKS = { pending: "\u2610", in_progress: "\u25D0", completed: "\u2611" };
|
|
96186
97018
|
}
|
|
96187
97019
|
});
|
|
96188
97020
|
|
|
96189
|
-
// src/render/mode-line.ts
|
|
96190
|
-
function modeLine(mode) {
|
|
96191
|
-
switch (mode) {
|
|
96192
|
-
case "default":
|
|
96193
|
-
return { text: "? default mode (shift+tab to cycle)" };
|
|
96194
|
-
case "accept-edits":
|
|
96195
|
-
return { text: "\u23F5\u23F5 accept edits on (shift+tab to cycle)", color: "green" };
|
|
96196
|
-
case "plan":
|
|
96197
|
-
return { text: "\u23F8 plan mode, read-only (shift+tab to cycle)", color: "blue" };
|
|
96198
|
-
case "yolo":
|
|
96199
|
-
return { text: "\u26A0 yolo, no prompts (shift+tab to cycle)", color: "red" };
|
|
96200
|
-
}
|
|
96201
|
-
}
|
|
96202
|
-
var init_mode_line = __esm({
|
|
96203
|
-
"src/render/mode-line.ts"() {
|
|
96204
|
-
"use strict";
|
|
96205
|
-
}
|
|
96206
|
-
});
|
|
96207
|
-
|
|
96208
97021
|
// src/render/footer.ts
|
|
96209
97022
|
function estimateCostUsd(inputTokens, outputTokens) {
|
|
96210
97023
|
return (inputTokens * ESTIMATED_USD_PER_MTOK.input + outputTokens * ESTIMATED_USD_PER_MTOK.output) / 1e6;
|
|
@@ -96222,31 +97035,45 @@ function contextPercent(usedTokens, maxTokens) {
|
|
|
96222
97035
|
function contextLevel(pct) {
|
|
96223
97036
|
return pct >= CONTEXT_CRITICAL_PCT ? "critical" : pct >= CONTEXT_WARN_PCT ? "warn" : "ok";
|
|
96224
97037
|
}
|
|
97038
|
+
function contextBar(pct, unicode = true) {
|
|
97039
|
+
const filled = Math.max(0, Math.min(BAR_CELLS, Math.round(pct / 100 * BAR_CELLS)));
|
|
97040
|
+
return unicode ? "\u25B0".repeat(filled) + "\u25B1".repeat(BAR_CELLS - filled) : `[${"#".repeat(filled)}${"-".repeat(BAR_CELLS - filled)}]`;
|
|
97041
|
+
}
|
|
97042
|
+
function formatModel(model) {
|
|
97043
|
+
return model === void 0 ? MODEL_PLACEHOLDER : `${model} (selected)`;
|
|
97044
|
+
}
|
|
96225
97045
|
function formatFooter({ mode, contextPct, costUsd, model, columns }) {
|
|
96226
97046
|
const level = contextLevel(contextPct);
|
|
96227
|
-
const
|
|
96228
|
-
const
|
|
97047
|
+
const unicode = activeGlyphs().rail === "\u2502";
|
|
97048
|
+
const mark = level === "critical" ? " !!" : level === "warn" ? " !" : "";
|
|
97049
|
+
const ctx = columns >= BAR_MIN_COLUMNS ? `ctx ${contextBar(contextPct, unicode)} ${contextPct}%${mark}` : `ctx ${contextPct}%${mark}`;
|
|
97050
|
+
const fields = [modeLine(mode, unicode).text, ctx, formatCost(costUsd), formatModel(model)];
|
|
96229
97051
|
while (fields.length > 1 && fields.join(" ").length > columns) fields.pop();
|
|
96230
97052
|
return { text: fields.join(" "), level };
|
|
96231
97053
|
}
|
|
96232
|
-
function formatTurnSummary({ toolCount, elapsedMs, inputTokens, outputTokens, interrupted, failed }) {
|
|
97054
|
+
function formatTurnSummary({ toolCount, elapsedMs, inputTokens, outputTokens, interrupted, failed }, unicode = true) {
|
|
96233
97055
|
const tools = `${toolCount} tool${toolCount === 1 ? "" : "s"}`;
|
|
96234
97056
|
const secs = (elapsedMs / 1e3).toFixed(1);
|
|
96235
|
-
const
|
|
96236
|
-
|
|
97057
|
+
const [ok, bad, bar, arrow] = unicode ? ["\u2713", "\u2717", "\u2502", "\u2192"] : ["+", "x", "|", "->"];
|
|
97058
|
+
const head = interrupted ? `${bad} Interrupted (${tools}, ${secs}s)` : failed ? `${bad} Failed (${tools}, ${secs}s)` : `${ok} Done (${tools}, ${secs}s)`;
|
|
97059
|
+
return `${head} ${bar} ${formatTokens(inputTokens)} ${arrow} ${formatTokens(outputTokens)} ${bar} ${formatCost(estimateCostUsd(inputTokens, outputTokens))}`;
|
|
96237
97060
|
}
|
|
96238
|
-
function formatThinking(elapsedMs, approxTokens) {
|
|
96239
|
-
|
|
97061
|
+
function formatThinking(elapsedMs, approxTokens, unicode = true) {
|
|
97062
|
+
const [mark, dots, sep2] = unicode ? ["\u273B", "\u2026", "\xB7"] : ["*", "...", "-"];
|
|
97063
|
+
return `${mark} Thinking${dots} (${Math.floor(elapsedMs / 1e3)}s ${sep2} ~${formatTokens(approxTokens)} tokens ${sep2} esc to interrupt)`;
|
|
96240
97064
|
}
|
|
96241
|
-
var ESTIMATED_USD_PER_MTOK, MODEL_PLACEHOLDER, CONTEXT_WARN_PCT, CONTEXT_CRITICAL_PCT;
|
|
97065
|
+
var ESTIMATED_USD_PER_MTOK, MODEL_PLACEHOLDER, CONTEXT_WARN_PCT, CONTEXT_CRITICAL_PCT, BAR_CELLS, BAR_MIN_COLUMNS;
|
|
96242
97066
|
var init_footer = __esm({
|
|
96243
97067
|
"src/render/footer.ts"() {
|
|
96244
97068
|
"use strict";
|
|
96245
97069
|
init_mode_line();
|
|
97070
|
+
init_rail();
|
|
96246
97071
|
ESTIMATED_USD_PER_MTOK = { input: 0.5, output: 1.5 };
|
|
96247
97072
|
MODEL_PLACEHOLDER = "gateway default";
|
|
96248
97073
|
CONTEXT_WARN_PCT = 80;
|
|
96249
97074
|
CONTEXT_CRITICAL_PCT = 95;
|
|
97075
|
+
BAR_CELLS = 8;
|
|
97076
|
+
BAR_MIN_COLUMNS = 60;
|
|
96250
97077
|
}
|
|
96251
97078
|
});
|
|
96252
97079
|
|
|
@@ -96449,17 +97276,17 @@ function isSubsequence(needle, hay) {
|
|
|
96449
97276
|
for (let j2 = 0; j2 < hay.length && i < needle.length; j2++) if (hay[j2] === needle[i]) i++;
|
|
96450
97277
|
return i === needle.length;
|
|
96451
97278
|
}
|
|
96452
|
-
function tier(
|
|
96453
|
-
const base =
|
|
97279
|
+
function tier(path11, q2) {
|
|
97280
|
+
const base = path11.slice(path11.lastIndexOf("/") + 1);
|
|
96454
97281
|
if (base.startsWith(q2)) return 0;
|
|
96455
97282
|
if (base.includes(q2)) return 1;
|
|
96456
|
-
if (
|
|
97283
|
+
if (path11.includes(q2)) return 2;
|
|
96457
97284
|
return 3;
|
|
96458
97285
|
}
|
|
96459
97286
|
function rankPaths(paths, query, limit = 8) {
|
|
96460
97287
|
if (query === "") return paths.slice(0, limit);
|
|
96461
97288
|
const q2 = query.toLowerCase();
|
|
96462
|
-
return paths.map((
|
|
97289
|
+
return paths.map((path11) => ({ path: path11, lower: path11.toLowerCase() })).filter(({ lower }) => isSubsequence(q2, lower)).map(({ path: path11, lower }) => ({ path: path11, rank: tier(lower, q2) })).sort((a, b2) => a.rank - b2.rank || a.path.length - b2.path.length || (a.path < b2.path ? -1 : a.path > b2.path ? 1 : 0)).slice(0, limit).map((m2) => m2.path);
|
|
96463
97290
|
}
|
|
96464
97291
|
var init_mention_rank = __esm({
|
|
96465
97292
|
"src/editor/mention-rank.ts"() {
|
|
@@ -96476,9 +97303,9 @@ function activeMention(text, cursor) {
|
|
|
96476
97303
|
const explicit = token[1] === "!";
|
|
96477
97304
|
return { start, query: token.slice(explicit ? 2 : 1), explicit };
|
|
96478
97305
|
}
|
|
96479
|
-
function insertMention(text, cursor, mention,
|
|
96480
|
-
if (/\s/.test(
|
|
96481
|
-
const inserted = `@${mention.explicit ? "!" : ""}${
|
|
97306
|
+
function insertMention(text, cursor, mention, path11) {
|
|
97307
|
+
if (/\s/.test(path11)) return { text, cursor };
|
|
97308
|
+
const inserted = `@${mention.explicit ? "!" : ""}${path11}`;
|
|
96482
97309
|
const head = text.slice(0, mention.start) + inserted;
|
|
96483
97310
|
const next = text[cursor];
|
|
96484
97311
|
if (next !== void 0 && /\s/.test(next)) return { text: head + text.slice(cursor), cursor: head.length + 1 };
|
|
@@ -96524,7 +97351,16 @@ function isKnownSlashCommand(name) {
|
|
|
96524
97351
|
function formatHelp() {
|
|
96525
97352
|
const width = Math.max(...SLASH_COMMANDS.map((c) => c.name.length));
|
|
96526
97353
|
const lines2 = SLASH_COMMANDS.map((c) => ` /${c.name.padEnd(width)} ${c.description}`);
|
|
96527
|
-
return [
|
|
97354
|
+
return [
|
|
97355
|
+
"Commands:",
|
|
97356
|
+
...lines2,
|
|
97357
|
+
"",
|
|
97358
|
+
" tab (no menu open) focus a step of the running turn: \u2191\u2193 move, enter opens its output, esc leaves; ctrl+o or /turns browses finished turns",
|
|
97359
|
+
" @path attach a file to your message (@!path overrides the secret/.gitignore filters)",
|
|
97360
|
+
" ! on an empty prompt: shell mode, runs a command on this machine (not sent to the model)",
|
|
97361
|
+
"",
|
|
97362
|
+
"Press Esc while the agent is running to cancel the turn."
|
|
97363
|
+
].join("\n");
|
|
96528
97364
|
}
|
|
96529
97365
|
var SLASH_COMMANDS;
|
|
96530
97366
|
var init_slash_commands = __esm({
|
|
@@ -96539,6 +97375,7 @@ var init_slash_commands = __esm({
|
|
|
96539
97375
|
{ name: "status", description: "Show project, session, mode, context and cost" },
|
|
96540
97376
|
{ name: "compact", description: "Summarize the conversation to free up context" },
|
|
96541
97377
|
{ name: "model", description: "List gateway models; /model <n|id> switches for this session" },
|
|
97378
|
+
{ name: "turns", description: "Browse earlier turns and open their steps (shortcut: ctrl+o)" },
|
|
96542
97379
|
{ name: "tools", description: "List the tools available to the agent" },
|
|
96543
97380
|
{ name: "exit", description: "Quit the REPL (alias: /quit)" },
|
|
96544
97381
|
{ name: "quit", description: "Quit the REPL" }
|
|
@@ -96555,41 +97392,42 @@ function validateSubmission(text) {
|
|
|
96555
97392
|
function withCursor(row, col) {
|
|
96556
97393
|
return `${row.slice(0, col)}\x1B[7m${row[col] ?? " "}\x1B[27m${row.slice(col + 1)}`;
|
|
96557
97394
|
}
|
|
96558
|
-
function PromptEditor({ onSubmit, disabled, history, listFiles: listFiles2 }) {
|
|
97395
|
+
function PromptEditor({ onSubmit, disabled, history, listFiles: listFiles2, onTab, locked = false, promptGlyph: promptGlyph2 = ">", onEmptyEnter, unicode = true }) {
|
|
96559
97396
|
const { exit } = use_app_default();
|
|
97397
|
+
const { columns } = useTerminalSize();
|
|
96560
97398
|
const { isRawModeSupported } = use_stdin_default();
|
|
96561
|
-
const [buf, setBuf] = (0,
|
|
96562
|
-
const [hint, setHint] = (0,
|
|
96563
|
-
const bufRef = (0,
|
|
96564
|
-
const queue = (0,
|
|
96565
|
-
const [files, setFiles] = (0,
|
|
96566
|
-
const filesRef = (0,
|
|
96567
|
-
const loadingFiles = (0,
|
|
96568
|
-
const [sel, setSel] = (0,
|
|
96569
|
-
const selRef = (0,
|
|
96570
|
-
const [shell, setShellState] = (0,
|
|
96571
|
-
const shellRef = (0,
|
|
97399
|
+
const [buf, setBuf] = (0, import_react29.useState)(() => createBuffer());
|
|
97400
|
+
const [hint, setHint] = (0, import_react29.useState)(null);
|
|
97401
|
+
const bufRef = (0, import_react29.useRef)(buf);
|
|
97402
|
+
const queue = (0, import_react29.useRef)([]);
|
|
97403
|
+
const [files, setFiles] = (0, import_react29.useState)(null);
|
|
97404
|
+
const filesRef = (0, import_react29.useRef)(null);
|
|
97405
|
+
const loadingFiles = (0, import_react29.useRef)(false);
|
|
97406
|
+
const [sel, setSel] = (0, import_react29.useState)(0);
|
|
97407
|
+
const selRef = (0, import_react29.useRef)(0);
|
|
97408
|
+
const [shell, setShellState] = (0, import_react29.useState)(false);
|
|
97409
|
+
const shellRef = (0, import_react29.useRef)(false);
|
|
96572
97410
|
const setShell = (on) => {
|
|
96573
97411
|
shellRef.current = on;
|
|
96574
97412
|
setShellState(on);
|
|
96575
97413
|
};
|
|
96576
97414
|
bufRef.current = buf;
|
|
96577
|
-
const fail = (0,
|
|
97415
|
+
const fail = (0, import_react29.useCallback)((what, err) => {
|
|
96578
97416
|
setHint(`${what}: ${err instanceof Error ? err.message : String(err)}`);
|
|
96579
97417
|
}, []);
|
|
96580
|
-
(0,
|
|
97418
|
+
(0, import_react29.useEffect)(() => {
|
|
96581
97419
|
history?.load().catch((err) => fail("Could not load prompt history", err));
|
|
96582
97420
|
}, [history, fail]);
|
|
96583
|
-
const dispatch = (0,
|
|
97421
|
+
const dispatch = (0, import_react29.useCallback)((text) => {
|
|
96584
97422
|
const result = onSubmit(text);
|
|
96585
97423
|
if (result instanceof Promise) result.catch((err) => fail("Submit failed", err));
|
|
96586
97424
|
}, [onSubmit, fail]);
|
|
96587
|
-
(0,
|
|
97425
|
+
(0, import_react29.useEffect)(() => {
|
|
96588
97426
|
if (!disabled && queue.current.length > 0) dispatch(queue.current.shift());
|
|
96589
97427
|
}, [disabled, dispatch]);
|
|
96590
97428
|
const mention = shell ? null : activeMention(buf.text, buf.cursor);
|
|
96591
97429
|
const mentionOpen = mention !== null;
|
|
96592
|
-
(0,
|
|
97430
|
+
(0, import_react29.useEffect)(() => {
|
|
96593
97431
|
if (!mentionOpen || !listFiles2 || filesRef.current !== null || loadingFiles.current) return;
|
|
96594
97432
|
loadingFiles.current = true;
|
|
96595
97433
|
listFiles2().then((f) => {
|
|
@@ -96659,6 +97497,7 @@ function PromptEditor({ onSubmit, disabled, history, listFiles: listFiles2 }) {
|
|
|
96659
97497
|
case "tab": {
|
|
96660
97498
|
const completed = completeSlash(cur.text, slashMatches(cur.text, SLASH_COMMANDS));
|
|
96661
97499
|
if (completed) update(createBuffer(completed));
|
|
97500
|
+
else onTab?.();
|
|
96662
97501
|
return;
|
|
96663
97502
|
}
|
|
96664
97503
|
case "historyPrev":
|
|
@@ -96685,6 +97524,7 @@ function PromptEditor({ onSubmit, disabled, history, listFiles: listFiles2 }) {
|
|
|
96685
97524
|
const typed = cur.text.trim();
|
|
96686
97525
|
if (typed === "") {
|
|
96687
97526
|
if (inShell) setHint("Type a command after ! (Backspace on an empty prompt leaves shell mode)");
|
|
97527
|
+
else onEmptyEnter?.();
|
|
96688
97528
|
return;
|
|
96689
97529
|
}
|
|
96690
97530
|
const text = inShell ? `!${typed}` : typed;
|
|
@@ -96702,36 +97542,39 @@ function PromptEditor({ onSubmit, disabled, history, listFiles: listFiles2 }) {
|
|
|
96702
97542
|
else dispatch(text);
|
|
96703
97543
|
}
|
|
96704
97544
|
}
|
|
96705
|
-
}, { isActive: Boolean(isRawModeSupported) });
|
|
97545
|
+
}, { isActive: Boolean(isRawModeSupported) && !locked });
|
|
96706
97546
|
if (!isRawModeSupported) {
|
|
96707
|
-
return /* @__PURE__ */ (0,
|
|
97547
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { ...paint("delete"), children: 'Interactive input needs a TTY (stdin is not a terminal). Use `spacecode -p "<prompt>"` for headless runs.' });
|
|
96708
97548
|
}
|
|
96709
|
-
if (disabled && buf.text === "") return null;
|
|
96710
97549
|
const rows = lines(buf);
|
|
96711
97550
|
const { line, col } = lineCol(buf);
|
|
96712
97551
|
const matches = shell ? [] : slashMatches(buf.text, SLASH_COMMANDS);
|
|
96713
97552
|
const picks = mention && files ? rankPaths(files, mention.query, PICKER_ROWS) : [];
|
|
96714
97553
|
const pickAt = Math.min(sel, Math.max(picks.length - 1, 0));
|
|
96715
|
-
return /* @__PURE__ */ (0,
|
|
96716
|
-
rows.map((row, i) => /* @__PURE__ */ (0,
|
|
96717
|
-
/* @__PURE__ */ (0,
|
|
96718
|
-
/* @__PURE__ */ (0,
|
|
96719
|
-
] }, i)),
|
|
96720
|
-
matches.length > 0 && /* @__PURE__ */ (0,
|
|
96721
|
-
matches.map((c) => `/${c.name} \u2014 ${c.description}`).join(" "),
|
|
97554
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Box_default, { flexDirection: "column", children: [
|
|
97555
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Box_default, { flexDirection: "column", borderStyle: unicode ? "round" : "classic", borderColor: paint("secondary").color, borderDimColor: disabled, paddingX: 1, width: Math.min(columns, MAX_STATIC_LINE), children: rows.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Box_default, { children: [
|
|
97556
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { ...paint(shell ? "action" : "agent"), bold: true, dimColor: disabled, children: i === 0 ? shell ? "! " : `${promptGlyph2} ` : " " }),
|
|
97557
|
+
disabled && !locked && buf.text === "" ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { ...paint("secondary"), children: "turn running - type to queue your next prompt" }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { dimColor: disabled, children: i === line && !disabled ? withCursor(row, col) : row })
|
|
97558
|
+
] }, i)) }),
|
|
97559
|
+
matches.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { ...paint("secondary"), children: [
|
|
97560
|
+
matches.map((c) => `/${c.name} ${unicode ? "\u2014" : "-"} ${c.description}`).join(" "),
|
|
96722
97561
|
" (Tab to complete)"
|
|
96723
97562
|
] }),
|
|
96724
|
-
shell && buf.text === "" && !disabled && /* @__PURE__ */ (0,
|
|
96725
|
-
picks.map((p, i) => /* @__PURE__ */ (0,
|
|
96726
|
-
picks.length > 0 && /* @__PURE__ */ (0,
|
|
96727
|
-
|
|
97563
|
+
shell && buf.text === "" && !disabled && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { ...paint("secondary"), children: "shell mode: runs on your machine, not sent to the model (Backspace exits)" }),
|
|
97564
|
+
picks.map((p, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { ...i === pickAt ? paint("action") : paint("secondary"), children: `${i === pickAt ? unicode ? "\u203A" : ">" : " "} ${p}` }, p)),
|
|
97565
|
+
picks.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { ...paint("secondary"), children: [
|
|
97566
|
+
"(",
|
|
97567
|
+
unicode ? "\u2191/\u2193" : "up/down",
|
|
97568
|
+
" select, Tab or Enter to insert)"
|
|
97569
|
+
] }),
|
|
97570
|
+
hint && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { ...paint("delete"), children: hint })
|
|
96728
97571
|
] });
|
|
96729
97572
|
}
|
|
96730
|
-
var
|
|
97573
|
+
var import_react29, import_jsx_runtime9, PICKER_ROWS;
|
|
96731
97574
|
var init_PromptEditor = __esm({
|
|
96732
97575
|
async "src/components/PromptEditor.tsx"() {
|
|
96733
97576
|
"use strict";
|
|
96734
|
-
|
|
97577
|
+
import_react29 = __toESM(require_react(), 1);
|
|
96735
97578
|
await init_build2();
|
|
96736
97579
|
init_edit_buffer();
|
|
96737
97580
|
init_editor_keys();
|
|
@@ -96739,33 +97582,14 @@ var init_PromptEditor = __esm({
|
|
|
96739
97582
|
init_mention_token();
|
|
96740
97583
|
init_slash_menu();
|
|
96741
97584
|
init_slash_commands();
|
|
96742
|
-
|
|
97585
|
+
init_theme();
|
|
97586
|
+
init_hints();
|
|
97587
|
+
await init_useTerminalSize();
|
|
97588
|
+
import_jsx_runtime9 = __toESM(require_jsx_runtime(), 1);
|
|
96743
97589
|
PICKER_ROWS = 6;
|
|
96744
97590
|
}
|
|
96745
97591
|
});
|
|
96746
97592
|
|
|
96747
|
-
// src/editor/credential-filter.ts
|
|
96748
|
-
function containsCredential(entry) {
|
|
96749
|
-
return PATTERNS.some((re3) => re3.test(entry));
|
|
96750
|
-
}
|
|
96751
|
-
var PATTERNS;
|
|
96752
|
-
var init_credential_filter = __esm({
|
|
96753
|
-
"src/editor/credential-filter.ts"() {
|
|
96754
|
-
"use strict";
|
|
96755
|
-
PATTERNS = [
|
|
96756
|
-
/\bsk[_-][A-Za-z0-9_-]{16,}/,
|
|
96757
|
-
/\bgh[pos]_[A-Za-z0-9]{20,}/,
|
|
96758
|
-
/\bgithub_pat_[A-Za-z0-9_]{20,}/,
|
|
96759
|
-
/\bAKIA[0-9A-Z]{16}\b/,
|
|
96760
|
-
/\bBearer\s+[A-Za-z0-9._~+/=-]{20,}/,
|
|
96761
|
-
/-----BEGIN [A-Z ]*PRIVATE KEY-----/,
|
|
96762
|
-
/\bxox[abp]-[A-Za-z0-9-]{10,}/,
|
|
96763
|
-
/\bnpm_[A-Za-z0-9]{30,}/,
|
|
96764
|
-
/\b[A-Za-z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD)[A-Za-z0-9_]*=\S{8,}/i
|
|
96765
|
-
];
|
|
96766
|
-
}
|
|
96767
|
-
});
|
|
96768
|
-
|
|
96769
97593
|
// src/editor/history-store.ts
|
|
96770
97594
|
var PromptHistory;
|
|
96771
97595
|
var init_history_store = __esm({
|
|
@@ -96847,24 +97671,24 @@ var init_history_store = __esm({
|
|
|
96847
97671
|
});
|
|
96848
97672
|
|
|
96849
97673
|
// src/editor/history-file-io.ts
|
|
96850
|
-
import { chmod, mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
96851
|
-
import
|
|
97674
|
+
import { chmod as chmod2, mkdir as mkdir2, readFile as readFile2, rename as rename2, writeFile as writeFile2 } from "node:fs/promises";
|
|
97675
|
+
import path2 from "node:path";
|
|
96852
97676
|
function createFileHistoryIO(file) {
|
|
96853
97677
|
return {
|
|
96854
97678
|
async read() {
|
|
96855
97679
|
try {
|
|
96856
|
-
return await
|
|
97680
|
+
return await readFile2(file, "utf8");
|
|
96857
97681
|
} catch (err) {
|
|
96858
97682
|
if (err.code === "ENOENT") return null;
|
|
96859
97683
|
throw err;
|
|
96860
97684
|
}
|
|
96861
97685
|
},
|
|
96862
97686
|
async write(content) {
|
|
96863
|
-
await
|
|
97687
|
+
await mkdir2(path2.dirname(file), { recursive: true, mode: 448 });
|
|
96864
97688
|
const tmp = `${file}.${process.pid}.tmp`;
|
|
96865
|
-
await
|
|
96866
|
-
await
|
|
96867
|
-
await
|
|
97689
|
+
await writeFile2(tmp, content, { mode: 384 });
|
|
97690
|
+
await chmod2(tmp, 384);
|
|
97691
|
+
await rename2(tmp, file);
|
|
96868
97692
|
}
|
|
96869
97693
|
};
|
|
96870
97694
|
}
|
|
@@ -97310,6 +98134,7 @@ async function runAgentLoop(prompt, config, executor, emit, signal, existingMess
|
|
|
97310
98134
|
}
|
|
97311
98135
|
lastToolCount = toolUseBlocks.length;
|
|
97312
98136
|
if (data.stop_reason !== "tool_use") {
|
|
98137
|
+
if (data.content.length > 0) messages.push({ role: "assistant", content: data.content });
|
|
97313
98138
|
emitDone(emit, {
|
|
97314
98139
|
type: "done",
|
|
97315
98140
|
success: true,
|
|
@@ -97336,8 +98161,8 @@ async function runAgentLoop(prompt, config, executor, emit, signal, existingMess
|
|
|
97336
98161
|
const toolInput = block.input ?? {};
|
|
97337
98162
|
const startTime = Date.now();
|
|
97338
98163
|
let result;
|
|
97339
|
-
const onOutput = (
|
|
97340
|
-
emitToolOutput(emit, { type: "tool_output", id, stream, text });
|
|
98164
|
+
const onOutput = (_reportedId, stream, text) => {
|
|
98165
|
+
emitToolOutput(emit, { type: "tool_output", id: toolId, stream, text });
|
|
97341
98166
|
};
|
|
97342
98167
|
try {
|
|
97343
98168
|
result = await executor.execute(toolName, toolInput, onOutput);
|
|
@@ -97539,9 +98364,9 @@ function createFileTools(store) {
|
|
|
97539
98364
|
readCache.clear();
|
|
97540
98365
|
listCache.clear();
|
|
97541
98366
|
}
|
|
97542
|
-
function invalidatePath(
|
|
97543
|
-
readCache.delete(
|
|
97544
|
-
const parent =
|
|
98367
|
+
function invalidatePath(path11) {
|
|
98368
|
+
readCache.delete(path11);
|
|
98369
|
+
const parent = path11.includes("/") ? path11.slice(0, path11.lastIndexOf("/") + 1) : "";
|
|
97545
98370
|
listCache.delete(parent);
|
|
97546
98371
|
listCache.delete("");
|
|
97547
98372
|
listCache.delete(".");
|
|
@@ -97549,36 +98374,36 @@ function createFileTools(store) {
|
|
|
97549
98374
|
}
|
|
97550
98375
|
const handlers = {
|
|
97551
98376
|
read_file: async (input) => {
|
|
97552
|
-
const
|
|
97553
|
-
const cached = readCache.get(
|
|
98377
|
+
const path11 = input.path;
|
|
98378
|
+
const cached = readCache.get(path11);
|
|
97554
98379
|
if (cached !== void 0) {
|
|
97555
98380
|
return { output: cached };
|
|
97556
98381
|
}
|
|
97557
|
-
const content = await store.readFile(
|
|
98382
|
+
const content = await store.readFile(path11);
|
|
97558
98383
|
if (content === null) {
|
|
97559
|
-
return { output: `File not found: ${
|
|
98384
|
+
return { output: `File not found: ${path11}`, is_error: true };
|
|
97560
98385
|
}
|
|
97561
|
-
readCache.set(
|
|
98386
|
+
readCache.set(path11, content);
|
|
97562
98387
|
return { output: content };
|
|
97563
98388
|
},
|
|
97564
98389
|
write_file: async (input) => {
|
|
97565
|
-
const
|
|
98390
|
+
const path11 = input.path;
|
|
97566
98391
|
const content = input.content;
|
|
97567
|
-
await store.writeFile(
|
|
97568
|
-
invalidatePath(
|
|
97569
|
-
readCache.set(
|
|
97570
|
-
return { output: `Written: ${
|
|
98392
|
+
await store.writeFile(path11, content);
|
|
98393
|
+
invalidatePath(path11);
|
|
98394
|
+
readCache.set(path11, content);
|
|
98395
|
+
return { output: `Written: ${path11}` };
|
|
97571
98396
|
},
|
|
97572
98397
|
edit_file: async (input) => {
|
|
97573
|
-
const
|
|
98398
|
+
const path11 = input.path;
|
|
97574
98399
|
const oldStr = input.old_string;
|
|
97575
98400
|
const newStr = input.new_string;
|
|
97576
|
-
let content = readCache.get(
|
|
98401
|
+
let content = readCache.get(path11);
|
|
97577
98402
|
if (content === void 0) {
|
|
97578
|
-
const stored = await store.readFile(
|
|
98403
|
+
const stored = await store.readFile(path11);
|
|
97579
98404
|
if (stored === null) {
|
|
97580
98405
|
return {
|
|
97581
|
-
output: `File not found: ${
|
|
98406
|
+
output: `File not found: ${path11}. Did you read the file first?`,
|
|
97582
98407
|
is_error: true
|
|
97583
98408
|
};
|
|
97584
98409
|
}
|
|
@@ -97588,41 +98413,41 @@ function createFileTools(store) {
|
|
|
97588
98413
|
if (lineNumbers.length === 0) {
|
|
97589
98414
|
const lineCount = content.split("\n").length;
|
|
97590
98415
|
return {
|
|
97591
|
-
output: `old_string not found in ${
|
|
98416
|
+
output: `old_string not found in ${path11}. The file has ${lineCount} lines. Did you read the file first?`,
|
|
97592
98417
|
is_error: true
|
|
97593
98418
|
};
|
|
97594
98419
|
}
|
|
97595
98420
|
if (lineNumbers.length > 1) {
|
|
97596
98421
|
const locations = lineNumbers.map((ln) => ` - line ${ln}`).join("\n");
|
|
97597
98422
|
return {
|
|
97598
|
-
output: `old_string matches ${lineNumbers.length} locations in ${
|
|
98423
|
+
output: `old_string matches ${lineNumbers.length} locations in ${path11}:
|
|
97599
98424
|
${locations}
|
|
97600
98425
|
Include more surrounding context to make old_string unique.`,
|
|
97601
98426
|
is_error: true
|
|
97602
98427
|
};
|
|
97603
98428
|
}
|
|
97604
|
-
const result = await store.editFile(
|
|
98429
|
+
const result = await store.editFile(path11, oldStr, newStr);
|
|
97605
98430
|
if (!result.success) {
|
|
97606
98431
|
return { output: result.error, is_error: true };
|
|
97607
98432
|
}
|
|
97608
|
-
invalidatePath(
|
|
97609
|
-
const updated = await store.readFile(
|
|
97610
|
-
if (updated !== null) readCache.set(
|
|
97611
|
-
return { output: `Edited: ${
|
|
98433
|
+
invalidatePath(path11);
|
|
98434
|
+
const updated = await store.readFile(path11);
|
|
98435
|
+
if (updated !== null) readCache.set(path11, updated);
|
|
98436
|
+
return { output: `Edited: ${path11}` };
|
|
97612
98437
|
},
|
|
97613
98438
|
list_directory: async (input) => {
|
|
97614
|
-
const
|
|
97615
|
-
const cached = listCache.get(
|
|
98439
|
+
const path11 = input.path;
|
|
98440
|
+
const cached = listCache.get(path11);
|
|
97616
98441
|
if (cached !== void 0) {
|
|
97617
98442
|
if (cached.length === 0) {
|
|
97618
|
-
return { output: `Empty or not found: ${
|
|
98443
|
+
return { output: `Empty or not found: ${path11}` };
|
|
97619
98444
|
}
|
|
97620
98445
|
return { output: cached.join("\n") };
|
|
97621
98446
|
}
|
|
97622
|
-
const entries = await store.listDirectory(
|
|
97623
|
-
listCache.set(
|
|
98447
|
+
const entries = await store.listDirectory(path11);
|
|
98448
|
+
listCache.set(path11, entries);
|
|
97624
98449
|
if (entries.length === 0) {
|
|
97625
|
-
return { output: `Empty or not found: ${
|
|
98450
|
+
return { output: `Empty or not found: ${path11}` };
|
|
97626
98451
|
}
|
|
97627
98452
|
return { output: entries.join("\n") };
|
|
97628
98453
|
}
|
|
@@ -97858,8 +98683,8 @@ function parseTodos(input) {
|
|
|
97858
98683
|
if (typeof e.status !== "string" || !STATUSES.includes(e.status)) throw new Error(`Item ${i + 1}: "status" must be one of ${STATUSES.join(", ")}`);
|
|
97859
98684
|
return { content: e.content.trim(), status: e.status };
|
|
97860
98685
|
});
|
|
97861
|
-
const
|
|
97862
|
-
if (
|
|
98686
|
+
const active2 = items.filter((t) => t.status === "in_progress").length;
|
|
98687
|
+
if (active2 > 1) throw new Error(`Only one item may be "in_progress" at a time (found ${active2})`);
|
|
97863
98688
|
return items;
|
|
97864
98689
|
}
|
|
97865
98690
|
function createTodoTool(onUpdate) {
|
|
@@ -97958,9 +98783,9 @@ var init_host = __esm({
|
|
|
97958
98783
|
});
|
|
97959
98784
|
|
|
97960
98785
|
// ../agent/src/host/local-host.ts
|
|
97961
|
-
import { execFile } from "node:child_process";
|
|
98786
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
97962
98787
|
import { promises as fs2 } from "node:fs";
|
|
97963
|
-
import * as
|
|
98788
|
+
import * as path3 from "node:path";
|
|
97964
98789
|
var LocalHost;
|
|
97965
98790
|
var init_local_host = __esm({
|
|
97966
98791
|
"../agent/src/host/local-host.ts"() {
|
|
@@ -97970,7 +98795,7 @@ var init_local_host = __esm({
|
|
|
97970
98795
|
files;
|
|
97971
98796
|
root;
|
|
97972
98797
|
constructor(rootDir) {
|
|
97973
|
-
this.root =
|
|
98798
|
+
this.root = path3.resolve(rootDir);
|
|
97974
98799
|
this.files = {
|
|
97975
98800
|
readFile: (p) => this.readFile(p),
|
|
97976
98801
|
writeFile: (p, c) => this.writeFile(p, c),
|
|
@@ -97979,12 +98804,12 @@ var init_local_host = __esm({
|
|
|
97979
98804
|
};
|
|
97980
98805
|
}
|
|
97981
98806
|
static inside(root, abs) {
|
|
97982
|
-
const rel =
|
|
97983
|
-
return !(rel === ".." || rel.startsWith(".." +
|
|
98807
|
+
const rel = path3.relative(root, abs);
|
|
98808
|
+
return !(rel === ".." || rel.startsWith(".." + path3.sep) || path3.isAbsolute(rel));
|
|
97984
98809
|
}
|
|
97985
98810
|
/** Lexical confinement plus a symlink-aware check of the deepest existing ancestor. */
|
|
97986
98811
|
async resolve(p) {
|
|
97987
|
-
const abs =
|
|
98812
|
+
const abs = path3.resolve(this.root, p);
|
|
97988
98813
|
if (!_LocalHost.inside(this.root, abs)) throw new Error(`Path escapes workspace root: ${p}`);
|
|
97989
98814
|
const realRoot = await fs2.realpath(this.root);
|
|
97990
98815
|
let probe = abs;
|
|
@@ -98002,7 +98827,7 @@ var init_local_host = __esm({
|
|
|
98002
98827
|
throw e;
|
|
98003
98828
|
});
|
|
98004
98829
|
if (st?.isSymbolicLink()) throw new Error(`Path is a dangling symlink: ${p}`);
|
|
98005
|
-
const parent =
|
|
98830
|
+
const parent = path3.dirname(probe);
|
|
98006
98831
|
if (parent === probe) break;
|
|
98007
98832
|
probe = parent;
|
|
98008
98833
|
}
|
|
@@ -98019,7 +98844,7 @@ var init_local_host = __esm({
|
|
|
98019
98844
|
}
|
|
98020
98845
|
async writeFile(p, content) {
|
|
98021
98846
|
const abs = await this.resolve(p);
|
|
98022
|
-
await fs2.mkdir(
|
|
98847
|
+
await fs2.mkdir(path3.dirname(abs), { recursive: true });
|
|
98023
98848
|
await fs2.writeFile(abs, content, "utf8");
|
|
98024
98849
|
}
|
|
98025
98850
|
async editFile(p, oldStr, newStr) {
|
|
@@ -98049,7 +98874,7 @@ var init_local_host = __esm({
|
|
|
98049
98874
|
*/
|
|
98050
98875
|
exec(command, onOutput) {
|
|
98051
98876
|
return new Promise((resolve2, reject) => {
|
|
98052
|
-
const child =
|
|
98877
|
+
const child = execFile2(
|
|
98053
98878
|
"/bin/sh",
|
|
98054
98879
|
["-c", command],
|
|
98055
98880
|
{ cwd: this.root, maxBuffer: 10 * 1024 * 1024 },
|
|
@@ -98311,7 +99136,7 @@ var init_policy = __esm({
|
|
|
98311
99136
|
|
|
98312
99137
|
// ../agent/src/host/audit-log.ts
|
|
98313
99138
|
import { promises as fs3 } from "node:fs";
|
|
98314
|
-
import * as
|
|
99139
|
+
import * as path4 from "node:path";
|
|
98315
99140
|
function truncate(e) {
|
|
98316
99141
|
return e.subject.length > MAX_SUBJECT ? { ...e, subject: e.subject.slice(0, MAX_SUBJECT) + "...[truncated]" } : e;
|
|
98317
99142
|
}
|
|
@@ -98325,7 +99150,7 @@ var init_audit_log = __esm({
|
|
|
98325
99150
|
this.file = file;
|
|
98326
99151
|
}
|
|
98327
99152
|
async record(entry) {
|
|
98328
|
-
await fs3.mkdir(
|
|
99153
|
+
await fs3.mkdir(path4.dirname(this.file), { recursive: true });
|
|
98329
99154
|
await fs3.appendFile(this.file, JSON.stringify(truncate(entry)) + "\n", { mode: 384 });
|
|
98330
99155
|
}
|
|
98331
99156
|
};
|
|
@@ -98570,136 +99395,39 @@ var init_src2 = __esm({
|
|
|
98570
99395
|
}
|
|
98571
99396
|
});
|
|
98572
99397
|
|
|
98573
|
-
// src/session/
|
|
98574
|
-
|
|
98575
|
-
|
|
98576
|
-
|
|
98577
|
-
|
|
98578
|
-
|
|
98579
|
-
|
|
98580
|
-
|
|
98581
|
-
|
|
98582
|
-
|
|
98583
|
-
|
|
98584
|
-
}
|
|
98585
|
-
|
|
98586
|
-
const lines2 = text.split("\n").filter((l) => l.length > 0);
|
|
98587
|
-
if (lines2.length === 0) throw new SessionError(file, "Session file is empty");
|
|
98588
|
-
let parsed;
|
|
98589
|
-
try {
|
|
98590
|
-
parsed = lines2.map((l) => JSON.parse(l));
|
|
98591
|
-
} catch (err) {
|
|
98592
|
-
throw new SessionError(file, `Corrupt session file: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
98593
|
-
}
|
|
98594
|
-
const meta = parsed[0];
|
|
98595
|
-
if (meta.v !== 1 || typeof meta.id !== "string" || typeof meta.projectRoot !== "string" || typeof meta.updatedAt !== "string") {
|
|
98596
|
-
throw new SessionError(file, "Unrecognized session header (expected v1 with id, projectRoot, updatedAt)");
|
|
98597
|
-
}
|
|
98598
|
-
const messages = parsed.slice(1);
|
|
98599
|
-
if (!messages.every((m2) => m2 && (m2.role === "user" || m2.role === "assistant"))) {
|
|
98600
|
-
throw new SessionError(file, "Corrupt session file: a message has no valid role");
|
|
98601
|
-
}
|
|
98602
|
-
return { meta, messages };
|
|
98603
|
-
}
|
|
98604
|
-
function formatAge(fromIso, now) {
|
|
98605
|
-
const secs = Math.max(0, Math.floor((now.getTime() - new Date(fromIso).getTime()) / 1e3));
|
|
98606
|
-
if (secs < 60) return `${secs}s ago`;
|
|
98607
|
-
if (secs < 3600) return `${Math.floor(secs / 60)}m ago`;
|
|
98608
|
-
if (secs < 86400) return `${Math.floor(secs / 3600)}h ago`;
|
|
98609
|
-
return `${Math.floor(secs / 86400)}d ago`;
|
|
98610
|
-
}
|
|
98611
|
-
function titleFromMessages(messages) {
|
|
98612
|
-
const first = messages.find((m2) => m2.role === "user");
|
|
98613
|
-
const text = !first ? "" : typeof first.content === "string" ? first.content : first.content.map((b2) => b2.text ?? "").join(" ");
|
|
98614
|
-
const oneLine = text.replace(/\s+/g, " ").trim();
|
|
98615
|
-
return oneLine.length > 60 ? `${oneLine.slice(0, 59)}\u2026` : oneLine || "(untitled)";
|
|
98616
|
-
}
|
|
98617
|
-
var MAX_SESSION_BYTES, REDACTED, SessionError, ID, SessionStore;
|
|
98618
|
-
var init_session_store = __esm({
|
|
98619
|
-
"src/session/session-store.ts"() {
|
|
98620
|
-
"use strict";
|
|
98621
|
-
init_credential_filter();
|
|
98622
|
-
MAX_SESSION_BYTES = 5 * 1024 * 1024;
|
|
98623
|
-
REDACTED = "[redacted: credential-shaped content]";
|
|
98624
|
-
SessionError = class extends Error {
|
|
98625
|
-
constructor(file, message, options) {
|
|
98626
|
-
super(`${message} (${file})`, options);
|
|
98627
|
-
this.file = file;
|
|
98628
|
-
this.name = "SessionError";
|
|
98629
|
-
}
|
|
98630
|
-
};
|
|
98631
|
-
ID = /^[A-Za-z0-9_-]{8,64}$/;
|
|
98632
|
-
SessionStore = class {
|
|
98633
|
-
constructor(dir, now = () => /* @__PURE__ */ new Date()) {
|
|
98634
|
-
this.dir = dir;
|
|
98635
|
-
this.now = now;
|
|
98636
|
-
}
|
|
98637
|
-
newId() {
|
|
98638
|
-
return `${this.now().toISOString().replace(/[-:.TZ]/g, "").slice(0, 14)}-${randomBytes(4).toString("hex")}`;
|
|
98639
|
-
}
|
|
98640
|
-
fileFor(id) {
|
|
98641
|
-
if (!ID.test(id)) throw new SessionError(this.dir, `Invalid session id ${JSON.stringify(id)}`);
|
|
98642
|
-
return path4.join(this.dir, `${id}.jsonl`);
|
|
98643
|
-
}
|
|
98644
|
-
async save(session) {
|
|
98645
|
-
const file = this.fileFor(session.meta.id);
|
|
98646
|
-
const meta = { ...session.meta, updatedAt: this.now().toISOString() };
|
|
98647
|
-
const body = [meta, ...session.messages].map((entry) => JSON.stringify(redactCredentials(entry))).join("\n") + "\n";
|
|
98648
|
-
const bytes = Buffer.byteLength(body);
|
|
98649
|
-
if (bytes > MAX_SESSION_BYTES) {
|
|
98650
|
-
throw new SessionError(file, `Session is ${bytes} bytes, over the ${MAX_SESSION_BYTES}-byte cap; use /compact or /clear`);
|
|
98651
|
-
}
|
|
98652
|
-
const tmp = `${file}.${process.pid}.tmp`;
|
|
98653
|
-
try {
|
|
98654
|
-
await mkdir2(this.dir, { recursive: true, mode: 448 });
|
|
98655
|
-
await chmod2(this.dir, 448);
|
|
98656
|
-
await writeFile2(tmp, body, { mode: 384 });
|
|
98657
|
-
await chmod2(tmp, 384);
|
|
98658
|
-
await rename2(tmp, file);
|
|
98659
|
-
} catch (err) {
|
|
98660
|
-
throw new SessionError(file, `Could not save session: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
98661
|
-
}
|
|
98662
|
-
}
|
|
98663
|
-
async load(id) {
|
|
98664
|
-
const file = this.fileFor(id);
|
|
98665
|
-
let text;
|
|
98666
|
-
try {
|
|
98667
|
-
text = await readFile2(file, "utf8");
|
|
98668
|
-
} catch (err) {
|
|
98669
|
-
throw new SessionError(file, `Could not read session: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
98670
|
-
}
|
|
98671
|
-
return parseSession(file, text);
|
|
99398
|
+
// src/session/replay.ts
|
|
99399
|
+
function replayTurns(messages) {
|
|
99400
|
+
const turns = [];
|
|
99401
|
+
let current = null;
|
|
99402
|
+
let seq = 0;
|
|
99403
|
+
const push = (event) => current?.events.push({ ...event, seq: ++seq });
|
|
99404
|
+
for (const message of messages) {
|
|
99405
|
+
const blocks = typeof message.content === "string" ? [{ type: "text", text: message.content }] : message.content;
|
|
99406
|
+
if (message.role === "user") {
|
|
99407
|
+
const text = blocks.filter((b2) => b2.type === "text").map((b2) => b2.text ?? "").join("\n");
|
|
99408
|
+
if (text !== "" || !current) {
|
|
99409
|
+
current = { id: -(turns.length + 1), prompt: text || "(no prompt text)", events: [], interrupted: false };
|
|
99410
|
+
turns.push(current);
|
|
98672
99411
|
}
|
|
98673
|
-
|
|
98674
|
-
|
|
98675
|
-
|
|
98676
|
-
try {
|
|
98677
|
-
names = await readdir(this.dir);
|
|
98678
|
-
} catch (err) {
|
|
98679
|
-
if (err.code === "ENOENT") return { sessions: [], errors: [] };
|
|
98680
|
-
throw new SessionError(this.dir, `Could not list sessions: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
99412
|
+
for (const b2 of blocks) {
|
|
99413
|
+
if (b2.type === "tool_result" && b2.tool_use_id) {
|
|
99414
|
+
push({ type: "tool_result", id: b2.tool_use_id, output: contentText(b2.content), is_error: b2.is_error ?? false });
|
|
98681
99415
|
}
|
|
98682
|
-
const sessions = [];
|
|
98683
|
-
const errors = [];
|
|
98684
|
-
for (const name of names.filter((n) => n.endsWith(".jsonl"))) {
|
|
98685
|
-
try {
|
|
98686
|
-
sessions.push((await this.load(name.slice(0, -".jsonl".length))).meta);
|
|
98687
|
-
} catch (err) {
|
|
98688
|
-
errors.push(err instanceof SessionError ? err : new SessionError(path4.join(this.dir, name), String(err), { cause: err }));
|
|
98689
|
-
}
|
|
98690
|
-
}
|
|
98691
|
-
sessions.sort((a, b2) => b2.updatedAt.localeCompare(a.updatedAt));
|
|
98692
|
-
return { sessions, errors };
|
|
98693
99416
|
}
|
|
98694
|
-
|
|
98695
|
-
|
|
98696
|
-
|
|
98697
|
-
|
|
98698
|
-
} catch (err) {
|
|
98699
|
-
throw new SessionError(file, `Could not delete session: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
98700
|
-
}
|
|
99417
|
+
} else if (current) {
|
|
99418
|
+
for (const b2 of blocks) {
|
|
99419
|
+
if (b2.type === "text" && b2.text) push({ type: "text", content: b2.text });
|
|
99420
|
+
else if (b2.type === "tool_use" && b2.id && b2.name) push({ type: "tool_call", id: b2.id, tool: b2.name, input: b2.input ?? {} });
|
|
98701
99421
|
}
|
|
98702
|
-
}
|
|
99422
|
+
}
|
|
99423
|
+
}
|
|
99424
|
+
return turns;
|
|
99425
|
+
}
|
|
99426
|
+
var contentText;
|
|
99427
|
+
var init_replay = __esm({
|
|
99428
|
+
"src/session/replay.ts"() {
|
|
99429
|
+
"use strict";
|
|
99430
|
+
contentText = (content) => typeof content === "string" ? content : Array.isArray(content) ? content.map((b2) => b2 && typeof b2 === "object" && "text" in b2 ? String(b2.text ?? "") : "").join("") : "";
|
|
98703
99431
|
}
|
|
98704
99432
|
});
|
|
98705
99433
|
|
|
@@ -99090,7 +99818,7 @@ var init_mention_resolver = __esm({
|
|
|
99090
99818
|
});
|
|
99091
99819
|
|
|
99092
99820
|
// src/editor/node-mention-fs.ts
|
|
99093
|
-
import { open, realpath, stat } from "node:fs/promises";
|
|
99821
|
+
import { open as open2, realpath, stat as stat2 } from "node:fs/promises";
|
|
99094
99822
|
import path6 from "node:path";
|
|
99095
99823
|
function isInside(root, target) {
|
|
99096
99824
|
return target === root || target.startsWith(root.endsWith(path6.sep) ? root : root + path6.sep);
|
|
@@ -99113,13 +99841,13 @@ function createNodeMentionFS(cwd2) {
|
|
|
99113
99841
|
const real = await confined(abs);
|
|
99114
99842
|
if (real === null) return null;
|
|
99115
99843
|
if (real === "") return { size: 0, isFile: false };
|
|
99116
|
-
const st = await
|
|
99844
|
+
const st = await stat2(real);
|
|
99117
99845
|
return { size: st.size, isFile: st.isFile() };
|
|
99118
99846
|
},
|
|
99119
99847
|
async readFile(abs, maxBytes) {
|
|
99120
99848
|
const real = await confined(abs);
|
|
99121
99849
|
if (!real) throw new Error(`refusing to read outside cwd: ${abs}`);
|
|
99122
|
-
const fh = await
|
|
99850
|
+
const fh = await open2(real, "r");
|
|
99123
99851
|
try {
|
|
99124
99852
|
const buf = Buffer.alloc(maxBytes);
|
|
99125
99853
|
const { bytesRead } = await fh.read(buf, 0, maxBytes, 0);
|
|
@@ -99226,109 +99954,144 @@ var init_api_key = __esm({
|
|
|
99226
99954
|
// src/useReplController.ts
|
|
99227
99955
|
import { readFile as readFile4 } from "node:fs/promises";
|
|
99228
99956
|
import { homedir as homedir2 } from "node:os";
|
|
99229
|
-
function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mentionsOpt, sessions, resumeLatest: resumeLatest2 }) {
|
|
99957
|
+
function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mentionsOpt, sessions, resumeLatest: resumeLatest2, onViewTurns, onViewResume, isEscOwned }) {
|
|
99230
99958
|
const { exit } = use_app_default();
|
|
99231
|
-
const mentions = (0,
|
|
99232
|
-
const [events, setEvents] = (0,
|
|
99233
|
-
const [turns, setTurns] = (0,
|
|
99234
|
-
const turnIdRef = (0,
|
|
99235
|
-
const [isRunning, setIsRunning] = (0,
|
|
99236
|
-
const [
|
|
99237
|
-
const [
|
|
99238
|
-
const [
|
|
99239
|
-
const [
|
|
99240
|
-
const [
|
|
99241
|
-
(0,
|
|
99959
|
+
const mentions = (0, import_react30.useMemo)(() => mentionsOpt ?? createNodeMentions(process.cwd()), [mentionsOpt]);
|
|
99960
|
+
const [events, setEvents] = (0, import_react30.useState)([]);
|
|
99961
|
+
const [turns, setTurns] = (0, import_react30.useState)([]);
|
|
99962
|
+
const turnIdRef = (0, import_react30.useRef)(0);
|
|
99963
|
+
const [isRunning, setIsRunning] = (0, import_react30.useState)(false);
|
|
99964
|
+
const [livePrompt, setLivePrompt] = (0, import_react30.useState)(null);
|
|
99965
|
+
const [history, setHistory] = (0, import_react30.useState)([]);
|
|
99966
|
+
const [turnTokens, setTurnTokens] = (0, import_react30.useState)(null);
|
|
99967
|
+
const [sessionTokens, setSessionTokens] = (0, import_react30.useState)({ input: 0, output: 0 });
|
|
99968
|
+
const [turnStartedAt, setTurnStartedAt] = (0, import_react30.useState)(null);
|
|
99969
|
+
const [now, setNow] = (0, import_react30.useState)(() => Date.now());
|
|
99970
|
+
(0, import_react30.useEffect)(() => {
|
|
99242
99971
|
if (turnStartedAt === null) return;
|
|
99243
99972
|
setNow(Date.now());
|
|
99244
99973
|
const timer = setInterval(() => setNow(Date.now()), 1e3);
|
|
99245
99974
|
return () => clearInterval(timer);
|
|
99246
99975
|
}, [turnStartedAt]);
|
|
99247
|
-
const projectRoot = (0,
|
|
99248
|
-
const sessionIdRef = (0,
|
|
99249
|
-
const sessionCreatedAtRef = (0,
|
|
99250
|
-
const sessionTokensRef = (0,
|
|
99251
|
-
const resumeListRef = (0,
|
|
99252
|
-
const [model, setModel] = (0,
|
|
99253
|
-
const [todos,
|
|
99254
|
-
const
|
|
99255
|
-
const
|
|
99256
|
-
|
|
99257
|
-
|
|
99258
|
-
|
|
99259
|
-
(0,
|
|
99976
|
+
const projectRoot = (0, import_react30.useMemo)(() => process.cwd(), []);
|
|
99977
|
+
const sessionIdRef = (0, import_react30.useRef)(null);
|
|
99978
|
+
const sessionCreatedAtRef = (0, import_react30.useRef)(null);
|
|
99979
|
+
const sessionTokensRef = (0, import_react30.useRef)({ input: 0, output: 0 });
|
|
99980
|
+
const resumeListRef = (0, import_react30.useRef)([]);
|
|
99981
|
+
const [model, setModel] = (0, import_react30.useState)(null);
|
|
99982
|
+
const [todos, setTodosState] = (0, import_react30.useState)([]);
|
|
99983
|
+
const todosRef = (0, import_react30.useRef)([]);
|
|
99984
|
+
const setTodos = (0, import_react30.useCallback)((t) => {
|
|
99985
|
+
todosRef.current = t;
|
|
99986
|
+
setTodosState(t);
|
|
99987
|
+
}, []);
|
|
99988
|
+
const [replayed, setReplayed] = (0, import_react30.useState)([]);
|
|
99989
|
+
const [notices, setNotices] = (0, import_react30.useState)([]);
|
|
99990
|
+
const retryRef = (0, import_react30.useRef)(null);
|
|
99991
|
+
const [pending, setPending] = (0, import_react30.useState)(null);
|
|
99992
|
+
const abortRef = (0, import_react30.useRef)(null);
|
|
99993
|
+
const pendingRef = (0, import_react30.useRef)(null);
|
|
99994
|
+
const toolsetRef = (0, import_react30.useRef)(null);
|
|
99995
|
+
(0, import_react30.useEffect)(() => {
|
|
99260
99996
|
sessionTokensRef.current = sessionTokens;
|
|
99261
99997
|
}, [sessionTokens]);
|
|
99262
|
-
const [permissions] = (0,
|
|
99263
|
-
const [mode, setMode] = (0,
|
|
99264
|
-
(0,
|
|
99265
|
-
const answeredThisKeypressRef = (0,
|
|
99266
|
-
const
|
|
99267
|
-
|
|
99268
|
-
|
|
99998
|
+
const [permissions] = (0, import_react30.useState)(() => new PermissionState(yolo2 ? "yolo" : "default"));
|
|
99999
|
+
const [mode, setMode] = (0, import_react30.useState)(permissions.mode);
|
|
100000
|
+
(0, import_react30.useEffect)(() => permissions.onChange(setMode), [permissions]);
|
|
100001
|
+
const answeredThisKeypressRef = (0, import_react30.useRef)(false);
|
|
100002
|
+
const confirmQueueRef = (0, import_react30.useRef)([]);
|
|
100003
|
+
const confirmIdRef = (0, import_react30.useRef)(0);
|
|
100004
|
+
const showNextConfirm = (0, import_react30.useCallback)(() => {
|
|
100005
|
+
const head = confirmQueueRef.current[0] ?? null;
|
|
100006
|
+
pendingRef.current = head;
|
|
100007
|
+
setPending(head);
|
|
100008
|
+
}, []);
|
|
100009
|
+
const enqueueConfirm = (0, import_react30.useCallback)((p) => {
|
|
100010
|
+
confirmQueueRef.current.push(p);
|
|
100011
|
+
if (confirmQueueRef.current.length === 1) showNextConfirm();
|
|
100012
|
+
}, [showNextConfirm]);
|
|
100013
|
+
const dequeueConfirm = (0, import_react30.useCallback)((p) => {
|
|
100014
|
+
confirmQueueRef.current = confirmQueueRef.current.filter((q2) => q2 !== p);
|
|
100015
|
+
showNextConfirm();
|
|
100016
|
+
}, [showNextConfirm]);
|
|
100017
|
+
const refuseAllConfirms = (0, import_react30.useCallback)(() => {
|
|
100018
|
+
const all = confirmQueueRef.current;
|
|
100019
|
+
confirmQueueRef.current = [];
|
|
100020
|
+
for (const p of all) p.resolve(false);
|
|
100021
|
+
pendingRef.current = null;
|
|
100022
|
+
setPending(null);
|
|
99269
100023
|
}, []);
|
|
99270
|
-
const commitTurn = (0,
|
|
99271
|
-
setTurns((prev) => [...prev, { id: turnIdRef.current++, prompt, events: turnEvents, interrupted, summary }]);
|
|
100024
|
+
const commitTurn = (0, import_react30.useCallback)((prompt, turnEvents, interrupted, summary, retained) => {
|
|
100025
|
+
setTurns((prev) => [...prev, { id: turnIdRef.current++, prompt, events: turnEvents, interrupted, summary, retained }]);
|
|
99272
100026
|
}, []);
|
|
99273
|
-
const notice = (0,
|
|
100027
|
+
const notice = (0, import_react30.useCallback)((message) => {
|
|
99274
100028
|
setNotices((prev) => [...prev, message]);
|
|
99275
100029
|
}, []);
|
|
100030
|
+
const noticeError = (0, import_react30.useCallback)((view) => {
|
|
100031
|
+
setNotices((prev) => [...prev, { error: view }]);
|
|
100032
|
+
}, []);
|
|
99276
100033
|
const { isRawModeSupported } = use_stdin_default();
|
|
99277
100034
|
use_input_default((_input, key) => {
|
|
99278
100035
|
if (key.shift && key.tab) {
|
|
99279
100036
|
permissions.cycle();
|
|
99280
100037
|
return;
|
|
99281
100038
|
}
|
|
99282
|
-
if (pendingRef.current || answeredThisKeypressRef.current) return;
|
|
100039
|
+
if (pendingRef.current || answeredThisKeypressRef.current || isEscOwned?.()) return;
|
|
99283
100040
|
if (key.escape && abortRef.current) abortRef.current.abort();
|
|
99284
100041
|
}, { isActive: Boolean(isRawModeSupported) });
|
|
99285
|
-
const createToolset = (0,
|
|
100042
|
+
const createToolset = (0, import_react30.useCallback)(
|
|
99286
100043
|
() => createLocalToolset({
|
|
99287
100044
|
cwd: process.cwd(),
|
|
99288
100045
|
permissions,
|
|
99289
100046
|
onTodos: setTodos,
|
|
99290
|
-
confirm: (req) => new Promise(
|
|
99291
|
-
|
|
100047
|
+
confirm: (req) => new Promise((resolve2) => {
|
|
100048
|
+
const item = {
|
|
100049
|
+
id: confirmIdRef.current++,
|
|
99292
100050
|
req,
|
|
99293
100051
|
resolve: (decision) => {
|
|
99294
100052
|
answeredThisKeypressRef.current = true;
|
|
99295
100053
|
setTimeout(() => {
|
|
99296
100054
|
answeredThisKeypressRef.current = false;
|
|
99297
100055
|
}, 0);
|
|
99298
|
-
|
|
100056
|
+
dequeueConfirm(item);
|
|
99299
100057
|
resolve2(decision);
|
|
99300
100058
|
}
|
|
99301
|
-
}
|
|
99302
|
-
|
|
100059
|
+
};
|
|
100060
|
+
enqueueConfirm(item);
|
|
100061
|
+
})
|
|
99303
100062
|
}),
|
|
99304
|
-
[permissions,
|
|
100063
|
+
[permissions, enqueueConfirm, dequeueConfirm]
|
|
99305
100064
|
);
|
|
99306
|
-
const persist = (0,
|
|
100065
|
+
const persist = (0, import_react30.useCallback)(
|
|
99307
100066
|
async (messages, tokens) => {
|
|
99308
100067
|
if (!sessions) return;
|
|
99309
100068
|
try {
|
|
99310
100069
|
const id = sessionIdRef.current ??= sessions.newId();
|
|
99311
100070
|
const createdAt = sessionCreatedAtRef.current ??= (/* @__PURE__ */ new Date()).toISOString();
|
|
99312
|
-
await sessions.save({ meta: { v: 1, id, projectRoot, createdAt, updatedAt: createdAt, tokens, title: titleFromMessages(messages) }, messages });
|
|
100071
|
+
await sessions.save({ meta: { v: 1, id, projectRoot, createdAt, updatedAt: createdAt, tokens, title: titleFromMessages(messages), turns: countTurns(messages), todos: todosRef.current }, messages });
|
|
99313
100072
|
} catch (err) {
|
|
99314
|
-
|
|
100073
|
+
noticeError(describeError("session-save", err instanceof Error ? err.message : String(err)));
|
|
99315
100074
|
}
|
|
99316
100075
|
},
|
|
99317
|
-
[sessions, projectRoot,
|
|
100076
|
+
[sessions, projectRoot, noticeError]
|
|
99318
100077
|
);
|
|
99319
|
-
const applySession = (0,
|
|
100078
|
+
const applySession = (0, import_react30.useCallback)(
|
|
99320
100079
|
(loaded) => {
|
|
100080
|
+
retryRef.current = null;
|
|
100081
|
+
for (const w2 of loaded.warnings ?? []) notice(w2);
|
|
99321
100082
|
sessionIdRef.current = loaded.meta.id;
|
|
99322
100083
|
sessionCreatedAtRef.current = loaded.meta.createdAt;
|
|
99323
100084
|
setHistory(loaded.messages);
|
|
99324
100085
|
setSessionTokens(loaded.meta.tokens);
|
|
99325
100086
|
setTurns([]);
|
|
99326
100087
|
setEvents([]);
|
|
99327
|
-
|
|
100088
|
+
setReplayed(replayTurns(loaded.messages));
|
|
100089
|
+
setTodos(loaded.meta.todos ?? []);
|
|
100090
|
+
notice(`Resumed "${loaded.meta.title}" (${loaded.messages.length} messages, saved ${formatAge(loaded.meta.updatedAt, /* @__PURE__ */ new Date())}). Earlier turns are in /turns (ctrl+o).`);
|
|
99328
100091
|
},
|
|
99329
|
-
[notice]
|
|
100092
|
+
[notice, setTodos]
|
|
99330
100093
|
);
|
|
99331
|
-
(0,
|
|
100094
|
+
(0, import_react30.useEffect)(() => {
|
|
99332
100095
|
if (!resumeLatest2) return;
|
|
99333
100096
|
if (!sessions) {
|
|
99334
100097
|
notice("Cannot continue: session storage is not available.");
|
|
@@ -99349,7 +100112,7 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
|
|
|
99349
100112
|
}
|
|
99350
100113
|
})();
|
|
99351
100114
|
}, [resumeLatest2, sessions, projectRoot, notice, applySession]);
|
|
99352
|
-
const submit = (0,
|
|
100115
|
+
const submit = (0, import_react30.useCallback)(async (prompt) => {
|
|
99353
100116
|
const shellCommand = shellCommandOf(prompt);
|
|
99354
100117
|
if (shellCommand !== null) {
|
|
99355
100118
|
if (shellCommand === "") {
|
|
@@ -99379,8 +100142,7 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
|
|
|
99379
100142
|
} finally {
|
|
99380
100143
|
commitTurn(prompt, shellEvents, false);
|
|
99381
100144
|
setEvents([]);
|
|
99382
|
-
|
|
99383
|
-
setPendingConfirm(null);
|
|
100145
|
+
refuseAllConfirms();
|
|
99384
100146
|
setIsRunning(false);
|
|
99385
100147
|
}
|
|
99386
100148
|
return;
|
|
@@ -99400,6 +100162,8 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
|
|
|
99400
100162
|
setSessionTokens({ input: 0, output: 0 });
|
|
99401
100163
|
setTurnTokens(null);
|
|
99402
100164
|
setTodos([]);
|
|
100165
|
+
setReplayed([]);
|
|
100166
|
+
retryRef.current = null;
|
|
99403
100167
|
sessionIdRef.current = null;
|
|
99404
100168
|
sessionCreatedAtRef.current = null;
|
|
99405
100169
|
return;
|
|
@@ -99437,6 +100201,10 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
|
|
|
99437
100201
|
if (parsed.kind === "list") {
|
|
99438
100202
|
const { sessions: all, errors } = await sessions.list();
|
|
99439
100203
|
resumeListRef.current = all;
|
|
100204
|
+
if (onViewResume) {
|
|
100205
|
+
onViewResume({ sessions: all, errors: errors.map((e) => e.message) });
|
|
100206
|
+
return;
|
|
100207
|
+
}
|
|
99440
100208
|
for (const e of errors) notice(e.message);
|
|
99441
100209
|
notice(formatSessionList(all, projectRoot, /* @__PURE__ */ new Date()));
|
|
99442
100210
|
return;
|
|
@@ -99517,6 +100285,10 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
|
|
|
99517
100285
|
}
|
|
99518
100286
|
return;
|
|
99519
100287
|
}
|
|
100288
|
+
case "turns":
|
|
100289
|
+
if (onViewTurns) onViewTurns();
|
|
100290
|
+
else notice("The turn viewer is not available here.");
|
|
100291
|
+
return;
|
|
99520
100292
|
case "tools": {
|
|
99521
100293
|
const tools = toolsetRef.current?.tools ?? createLocalToolset({ cwd: process.cwd(), permissions, onTodos: setTodos }).tools;
|
|
99522
100294
|
notice(`Tools:
|
|
@@ -99529,6 +100301,7 @@ ${tools.map((t) => ` ${t.name}`).join("\n")}`);
|
|
|
99529
100301
|
return;
|
|
99530
100302
|
}
|
|
99531
100303
|
let apiKey;
|
|
100304
|
+
retryRef.current = null;
|
|
99532
100305
|
try {
|
|
99533
100306
|
apiKey = await resolveApiKey({
|
|
99534
100307
|
env: process.env,
|
|
@@ -99544,14 +100317,16 @@ ${tools.map((t) => ` ${t.name}`).join("\n")}`);
|
|
|
99544
100317
|
return;
|
|
99545
100318
|
}
|
|
99546
100319
|
if (!apiKey) {
|
|
100320
|
+
retryRef.current = prompt;
|
|
99547
100321
|
commitTurn(prompt, [{
|
|
99548
100322
|
type: "error",
|
|
99549
100323
|
seq: 1,
|
|
99550
100324
|
message: MISSING_KEY_MESSAGE
|
|
99551
|
-
}], false);
|
|
100325
|
+
}], false, void 0, true);
|
|
99552
100326
|
return;
|
|
99553
100327
|
}
|
|
99554
100328
|
setIsRunning(true);
|
|
100329
|
+
setLivePrompt(prompt);
|
|
99555
100330
|
setNotices([]);
|
|
99556
100331
|
setEvents([]);
|
|
99557
100332
|
setTurnTokens(null);
|
|
@@ -99562,6 +100337,7 @@ ${tools.map((t) => ` ${t.name}`).join("\n")}`);
|
|
|
99562
100337
|
setTurnStartedAt(startedAt);
|
|
99563
100338
|
let usage = { input: 0, output: 0 };
|
|
99564
100339
|
let failed = false;
|
|
100340
|
+
let keepForRetry = false;
|
|
99565
100341
|
try {
|
|
99566
100342
|
const toolset = createToolset();
|
|
99567
100343
|
toolsetRef.current = toolset;
|
|
@@ -99585,13 +100361,17 @@ ${tools.map((t) => ` ${t.name}`).join("\n")}`);
|
|
|
99585
100361
|
const attached = await mentions.attach(prompt);
|
|
99586
100362
|
if (attached.notices.length > 0) setNotices(attached.notices);
|
|
99587
100363
|
const result = await runAgentLoop(attached.prompt, config, toolset.executor, emit, controller.signal, history);
|
|
99588
|
-
|
|
100364
|
+
const failure = [...turnEvents].reverse().find((e) => e.type === "error");
|
|
100365
|
+
const noProgress = result.messages.length === history.length + 1;
|
|
100366
|
+
keepForRetry = !controller.signal.aborted && failure !== void 0 && isRetryable(failure.message) && noProgress;
|
|
100367
|
+
const savedMessages = keepForRetry ? history : result.messages;
|
|
100368
|
+
setHistory(savedMessages);
|
|
99589
100369
|
usage = { input: result.totalInputTokens, output: result.totalOutputTokens };
|
|
99590
100370
|
failed = !result.success;
|
|
99591
100371
|
setTurnTokens(usage);
|
|
99592
100372
|
const totals = { input: sessionTokensRef.current.input + usage.input, output: sessionTokensRef.current.output + usage.output };
|
|
99593
100373
|
setSessionTokens(totals);
|
|
99594
|
-
await persist(
|
|
100374
|
+
if (savedMessages.length > 0) await persist(savedMessages, totals);
|
|
99595
100375
|
} catch (err) {
|
|
99596
100376
|
failed = true;
|
|
99597
100377
|
console.error("[agent-ui] Agent loop failed:", err);
|
|
@@ -99603,21 +100383,24 @@ ${tools.map((t) => ` ${t.name}`).join("\n")}`);
|
|
|
99603
100383
|
} finally {
|
|
99604
100384
|
const toolCount = turnEvents.filter((e) => e.type === "tool_call").length;
|
|
99605
100385
|
const committedEvents = turnEvents.filter((e) => e.type !== "status");
|
|
100386
|
+
const retained = keepForRetry;
|
|
100387
|
+
retryRef.current = retained ? prompt : null;
|
|
99606
100388
|
commitTurn(
|
|
99607
100389
|
prompt,
|
|
99608
100390
|
committedEvents,
|
|
99609
100391
|
controller.signal.aborted,
|
|
99610
|
-
formatTurnSummary({ toolCount, elapsedMs: Date.now() - startedAt, inputTokens: usage.input, outputTokens: usage.output, interrupted: controller.signal.aborted, failed })
|
|
100392
|
+
formatTurnSummary({ toolCount, elapsedMs: Date.now() - startedAt, inputTokens: usage.input, outputTokens: usage.output, interrupted: controller.signal.aborted, failed }, activeGlyphs().rail === "\u2502"),
|
|
100393
|
+
retained
|
|
99611
100394
|
);
|
|
99612
100395
|
setTurnStartedAt(null);
|
|
100396
|
+
setLivePrompt(null);
|
|
99613
100397
|
setEvents([]);
|
|
99614
|
-
|
|
99615
|
-
setPendingConfirm(null);
|
|
100398
|
+
refuseAllConfirms();
|
|
99616
100399
|
abortRef.current = null;
|
|
99617
100400
|
setIsRunning(false);
|
|
99618
100401
|
}
|
|
99619
|
-
}, [repoName2, history, exit, permissions, notice,
|
|
99620
|
-
const approxTokens = (0,
|
|
100402
|
+
}, [repoName2, history, exit, permissions, notice, refuseAllConfirms, commitTurn, mentions, createToolset, sessions, projectRoot, persist, applySession, model, onViewTurns, onViewResume, setTodos]);
|
|
100403
|
+
const approxTokens = (0, import_react30.useMemo)(
|
|
99621
100404
|
() => events.reduce((n, e) => n + (e.type === "text" ? estimateTokens(e.content) : e.type === "text_delta" ? estimateTokens(e.text) : 0), 0),
|
|
99622
100405
|
[events]
|
|
99623
100406
|
);
|
|
@@ -99628,17 +100411,47 @@ ${tools.map((t) => ` ${t.name}`).join("\n")}`);
|
|
|
99628
100411
|
approxTokens,
|
|
99629
100412
|
...model ? { model } : {}
|
|
99630
100413
|
};
|
|
99631
|
-
|
|
100414
|
+
const retry = (0, import_react30.useCallback)(() => {
|
|
100415
|
+
const kept = retryRef.current;
|
|
100416
|
+
if (kept !== null) void submit(kept);
|
|
100417
|
+
}, [submit]);
|
|
100418
|
+
const resume = (0, import_react30.useMemo)(
|
|
100419
|
+
() => ({
|
|
100420
|
+
available: Boolean(sessions),
|
|
100421
|
+
load: async (meta) => {
|
|
100422
|
+
if (!sessions) throw new Error("Session storage is not available.");
|
|
100423
|
+
applySession(await sessions.load(meta.id));
|
|
100424
|
+
},
|
|
100425
|
+
remove: async (meta) => {
|
|
100426
|
+
if (!sessions) throw new Error("Session storage is not available.");
|
|
100427
|
+
await sessions.delete(meta.id);
|
|
100428
|
+
resumeListRef.current = resumeListRef.current.filter((m2) => m2.id !== meta.id);
|
|
100429
|
+
if (sessionIdRef.current === meta.id) {
|
|
100430
|
+
sessionIdRef.current = null;
|
|
100431
|
+
sessionCreatedAtRef.current = null;
|
|
100432
|
+
}
|
|
100433
|
+
},
|
|
100434
|
+
preview: async (meta) => {
|
|
100435
|
+
if (!sessions) return null;
|
|
100436
|
+
return sessions.preview(meta.id);
|
|
100437
|
+
}
|
|
100438
|
+
}),
|
|
100439
|
+
[sessions, applySession]
|
|
100440
|
+
);
|
|
100441
|
+
return { turns, events, isRunning, livePrompt, turnTokens, notices, pending, mode, todos, stats, submit, retry, mentions, replayed, resume };
|
|
99632
100442
|
}
|
|
99633
|
-
var
|
|
100443
|
+
var import_react30, apiUrl;
|
|
99634
100444
|
var init_useReplController = __esm({
|
|
99635
100445
|
async "src/useReplController.ts"() {
|
|
99636
100446
|
"use strict";
|
|
99637
|
-
|
|
100447
|
+
import_react30 = __toESM(require_react(), 1);
|
|
99638
100448
|
await init_build2();
|
|
99639
100449
|
init_src2();
|
|
99640
100450
|
init_footer();
|
|
99641
100451
|
init_session_store();
|
|
100452
|
+
init_replay();
|
|
100453
|
+
init_rail();
|
|
100454
|
+
init_describe_error();
|
|
99642
100455
|
init_compact();
|
|
99643
100456
|
init_models();
|
|
99644
100457
|
init_session_commands();
|
|
@@ -99651,48 +100464,377 @@ var init_useReplController = __esm({
|
|
|
99651
100464
|
}
|
|
99652
100465
|
});
|
|
99653
100466
|
|
|
100467
|
+
// src/components/TurnViewer.tsx
|
|
100468
|
+
function TurnViewer({ turns, steps, rows, columns, onClose, onEscape }) {
|
|
100469
|
+
const { isRawModeSupported } = use_stdin_default();
|
|
100470
|
+
const glyphs = activeGlyphs();
|
|
100471
|
+
const [turnIdx, setTurnIdx] = (0, import_react31.useState)(null);
|
|
100472
|
+
const [cursorRaw, setCursor] = (0, import_react31.useState)(turns.length - 1);
|
|
100473
|
+
const cursor = clamp(cursorRaw, 0, Math.max(0, turns.length - 1));
|
|
100474
|
+
const [stepCursor, setStepCursor] = (0, import_react31.useState)(0);
|
|
100475
|
+
const [openId, setOpenId] = (0, import_react31.useState)(null);
|
|
100476
|
+
const [scroll, setScroll] = (0, import_react31.useState)(0);
|
|
100477
|
+
const turn = turnIdx === null ? null : turns[turnIdx] ?? null;
|
|
100478
|
+
const turnSteps = turn ? steps.get(turn.id) ?? [] : [];
|
|
100479
|
+
const detailRows = Math.max(3, rows - 6 - turnSteps.length);
|
|
100480
|
+
use_input_default((_input, key) => {
|
|
100481
|
+
if (key.escape) onEscape?.();
|
|
100482
|
+
if (turn === null) {
|
|
100483
|
+
if (key.escape) onClose();
|
|
100484
|
+
else if (key.upArrow) setCursor((c) => clamp(clamp(c, 0, turns.length - 1) - 1, 0, turns.length - 1));
|
|
100485
|
+
else if (key.downArrow) setCursor((c) => clamp(clamp(c, 0, turns.length - 1) + 1, 0, turns.length - 1));
|
|
100486
|
+
else if (key.return && turns.length > 0) {
|
|
100487
|
+
setTurnIdx(cursor);
|
|
100488
|
+
setStepCursor(0);
|
|
100489
|
+
setOpenId(null);
|
|
100490
|
+
setScroll(0);
|
|
100491
|
+
}
|
|
100492
|
+
return;
|
|
100493
|
+
}
|
|
100494
|
+
if (key.escape) {
|
|
100495
|
+
if (openId !== null) {
|
|
100496
|
+
setOpenId(null);
|
|
100497
|
+
setScroll(0);
|
|
100498
|
+
} else setTurnIdx(null);
|
|
100499
|
+
} else if (key.upArrow) {
|
|
100500
|
+
setStepCursor((c) => clamp(c - 1, 0, turnSteps.length - 1));
|
|
100501
|
+
setOpenId(null);
|
|
100502
|
+
setScroll(0);
|
|
100503
|
+
} else if (key.downArrow) {
|
|
100504
|
+
setStepCursor((c) => clamp(c + 1, 0, turnSteps.length - 1));
|
|
100505
|
+
setOpenId(null);
|
|
100506
|
+
setScroll(0);
|
|
100507
|
+
} else if (key.return && turnSteps.length > 0) {
|
|
100508
|
+
const id = turnSteps[stepCursor].id;
|
|
100509
|
+
setOpenId((cur) => cur === id ? null : id);
|
|
100510
|
+
setScroll(0);
|
|
100511
|
+
} else if (key.pageDown) setScroll((s) => s + detailRows);
|
|
100512
|
+
else if (key.pageUp) setScroll((s) => Math.max(0, s - detailRows));
|
|
100513
|
+
}, { isActive: Boolean(isRawModeSupported) });
|
|
100514
|
+
const listRows = Math.max(3, rows - 4);
|
|
100515
|
+
const listStart = clamp(cursor - listRows + 1, 0, Math.max(0, turns.length - listRows));
|
|
100516
|
+
if (turn === null) {
|
|
100517
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { flexDirection: "column", children: [
|
|
100518
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { bold: true, children: [
|
|
100519
|
+
"Earlier turns ",
|
|
100520
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { ...paint("secondary"), children: "(\u2191\u2193 choose \xB7 enter open \xB7 esc close)" })
|
|
100521
|
+
] }),
|
|
100522
|
+
turns.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { ...paint("secondary"), children: "No finished turns yet." }),
|
|
100523
|
+
turns.slice(listStart, listStart + listRows).map((t, k2) => {
|
|
100524
|
+
const idx = listStart + k2;
|
|
100525
|
+
const n = steps.get(t.id)?.length ?? 0;
|
|
100526
|
+
const prompt = t.prompt.split("\n")[0].slice(0, Math.max(10, columns - 24));
|
|
100527
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { inverse: idx === cursor, children: [
|
|
100528
|
+
idx === cursor ? "> " : " ",
|
|
100529
|
+
idx + 1,
|
|
100530
|
+
". ",
|
|
100531
|
+
prompt,
|
|
100532
|
+
" ",
|
|
100533
|
+
glyphs.sep,
|
|
100534
|
+
" ",
|
|
100535
|
+
n,
|
|
100536
|
+
" step",
|
|
100537
|
+
n === 1 ? "" : "s"
|
|
100538
|
+
] }, t.id);
|
|
100539
|
+
})
|
|
100540
|
+
] });
|
|
100541
|
+
}
|
|
100542
|
+
const open3 = turnSteps.find((r) => r.id === openId);
|
|
100543
|
+
const win = open3 ? detailWindow(open3.detail, detailRows, Math.max(20, columns - 8), scroll) : null;
|
|
100544
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { flexDirection: "column", children: [
|
|
100545
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { bold: true, children: `${turnIdx + 1}. ${turn.prompt.split("\n")[0].slice(0, Math.max(10, columns - 8))}` }),
|
|
100546
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { ...paint("secondary"), children: turnSteps.length === 0 ? "No steps in this turn. (esc back)" : "\u2191\u2193 choose \xB7 enter open/close \xB7 pgup/pgdn scroll \xB7 esc back" }),
|
|
100547
|
+
turnSteps.map((r, i) => {
|
|
100548
|
+
const line = fitStepLine({ verb: r.verb, target: r.target, meta: r.meta || void 0, columns, indent: 6, ellipsis: glyphs.ellipsis, sep: glyphs.sep });
|
|
100549
|
+
const marker = glyphs.marker[r.state];
|
|
100550
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { flexDirection: "column", children: [
|
|
100551
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { inverse: i === stepCursor, children: [
|
|
100552
|
+
i === stepCursor ? "> " : " ",
|
|
100553
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { ...paint(r.state === "failed" ? "delete" : "add"), children: marker }),
|
|
100554
|
+
" ",
|
|
100555
|
+
line.verb,
|
|
100556
|
+
" ",
|
|
100557
|
+
line.target,
|
|
100558
|
+
line.meta ? ` ${glyphs.sep} ${line.meta}` : ""
|
|
100559
|
+
] }),
|
|
100560
|
+
r.id === openId && win && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { flexDirection: "column", marginLeft: 4, children: [
|
|
100561
|
+
win.above > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { ...paint("secondary"), children: `${glyphs.ellipsis} ${win.above} line${win.above === 1 ? "" : "s"} above` }),
|
|
100562
|
+
win.lines.map((l, k2) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { ...paint("secondary"), children: l }, k2)),
|
|
100563
|
+
win.below > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { ...paint("secondary"), children: `${glyphs.ellipsis} ${win.below} more line${win.below === 1 ? "" : "s"}` })
|
|
100564
|
+
] })
|
|
100565
|
+
] }, r.id);
|
|
100566
|
+
})
|
|
100567
|
+
] });
|
|
100568
|
+
}
|
|
100569
|
+
var import_react31, import_jsx_runtime10, clamp;
|
|
100570
|
+
var init_TurnViewer = __esm({
|
|
100571
|
+
async "src/components/TurnViewer.tsx"() {
|
|
100572
|
+
"use strict";
|
|
100573
|
+
import_react31 = __toESM(require_react(), 1);
|
|
100574
|
+
await init_build2();
|
|
100575
|
+
init_step_view();
|
|
100576
|
+
init_rail();
|
|
100577
|
+
init_theme();
|
|
100578
|
+
import_jsx_runtime10 = __toESM(require_jsx_runtime(), 1);
|
|
100579
|
+
clamp = (n, lo, hi) => Math.max(lo, Math.min(hi, n));
|
|
100580
|
+
}
|
|
100581
|
+
});
|
|
100582
|
+
|
|
100583
|
+
// src/render/resume-rows.ts
|
|
100584
|
+
import path9 from "node:path";
|
|
100585
|
+
function orderSessions(sessions, projectRoot) {
|
|
100586
|
+
return [...sessions.filter((s) => s.projectRoot === projectRoot), ...sessions.filter((s) => s.projectRoot !== projectRoot)];
|
|
100587
|
+
}
|
|
100588
|
+
function formatResumeRow(meta, now, columns, sep2 = "\xB7", ellipsis = "\u2026") {
|
|
100589
|
+
const project = path9.basename(meta.projectRoot) || meta.projectRoot;
|
|
100590
|
+
const tail = [meta.turns !== void 0 ? `${meta.turns} turn${meta.turns === 1 ? "" : "s"}` : null, formatCost(estimateCostUsd(meta.tokens.input, meta.tokens.output))].filter(Boolean).join(` ${sep2} `);
|
|
100591
|
+
const head = `${formatAge(meta.updatedAt, now)} ${sep2} ${project} ${sep2} `;
|
|
100592
|
+
const fixed = head.length + 2 + ` ${sep2} ${tail}`.length;
|
|
100593
|
+
const room = Math.max(8, columns - fixed);
|
|
100594
|
+
const title = meta.title.length > room ? `${meta.title.slice(0, Math.max(1, room - ellipsis.length))}${ellipsis}` : meta.title;
|
|
100595
|
+
return `${head}"${title}" ${sep2} ${tail}`;
|
|
100596
|
+
}
|
|
100597
|
+
var init_resume_rows = __esm({
|
|
100598
|
+
"src/render/resume-rows.ts"() {
|
|
100599
|
+
"use strict";
|
|
100600
|
+
init_footer();
|
|
100601
|
+
init_session_store();
|
|
100602
|
+
}
|
|
100603
|
+
});
|
|
100604
|
+
|
|
100605
|
+
// src/components/ResumePicker.tsx
|
|
100606
|
+
function ResumePicker({ sessions, errors, projectRoot, now, rows, columns, preview, onResume, onDelete, onClose, onEscape }) {
|
|
100607
|
+
const { isRawModeSupported } = use_stdin_default();
|
|
100608
|
+
const glyphs = activeGlyphs();
|
|
100609
|
+
const [items, setItems] = (0, import_react32.useState)(() => orderSessions(sessions, projectRoot));
|
|
100610
|
+
const [cursor, setCursor] = (0, import_react32.useState)(0);
|
|
100611
|
+
const [stage, setStage] = (0, import_react32.useState)("browse");
|
|
100612
|
+
const [message, setMessage] = (0, import_react32.useState)(null);
|
|
100613
|
+
const [previews, setPreviews] = (0, import_react32.useState)({});
|
|
100614
|
+
const at = clamp2(cursor, 0, Math.max(0, items.length - 1));
|
|
100615
|
+
const current = items[at];
|
|
100616
|
+
(0, import_react32.useEffect)(() => {
|
|
100617
|
+
if (!current || current.id in previews) return;
|
|
100618
|
+
let live = true;
|
|
100619
|
+
preview(current).then((p) => live && setPreviews((m2) => ({ ...m2, [current.id]: p }))).catch((err) => live && setPreviews((m2) => ({ ...m2, [current.id]: err instanceof Error ? err : new Error(String(err)) })));
|
|
100620
|
+
return () => {
|
|
100621
|
+
live = false;
|
|
100622
|
+
};
|
|
100623
|
+
}, [current, preview, previews]);
|
|
100624
|
+
use_input_default((input, key) => {
|
|
100625
|
+
if (key.escape) {
|
|
100626
|
+
onEscape?.();
|
|
100627
|
+
if (stage !== "browse") {
|
|
100628
|
+
setStage("browse");
|
|
100629
|
+
setMessage(null);
|
|
100630
|
+
} else onClose();
|
|
100631
|
+
return;
|
|
100632
|
+
}
|
|
100633
|
+
if (stage === "confirm-delete") {
|
|
100634
|
+
if (input === "y" && current) {
|
|
100635
|
+
onDelete(current).then(() => {
|
|
100636
|
+
setItems((list2) => list2.filter((m2) => m2.id !== current.id));
|
|
100637
|
+
setMessage(`Deleted "${current.title}".`);
|
|
100638
|
+
}).catch((err) => setMessage(`Could not delete: ${err instanceof Error ? err.message : String(err)}`));
|
|
100639
|
+
setStage("browse");
|
|
100640
|
+
} else if (input === "n") {
|
|
100641
|
+
setStage("browse");
|
|
100642
|
+
setMessage(null);
|
|
100643
|
+
}
|
|
100644
|
+
return;
|
|
100645
|
+
}
|
|
100646
|
+
if (key.upArrow) {
|
|
100647
|
+
setCursor((c) => clamp2(c - 1, 0, items.length - 1));
|
|
100648
|
+
setStage("browse");
|
|
100649
|
+
setMessage(null);
|
|
100650
|
+
} else if (key.downArrow) {
|
|
100651
|
+
setCursor((c) => clamp2(c + 1, 0, items.length - 1));
|
|
100652
|
+
setStage("browse");
|
|
100653
|
+
setMessage(null);
|
|
100654
|
+
} else if (input === "d" && current) {
|
|
100655
|
+
setStage("confirm-delete");
|
|
100656
|
+
setMessage(`Delete "${current.title}"? y/n`);
|
|
100657
|
+
} else if (key.return && current) {
|
|
100658
|
+
if (current.projectRoot !== projectRoot && stage !== "confirm-foreign") {
|
|
100659
|
+
setStage("confirm-foreign");
|
|
100660
|
+
setMessage(`That session belongs to ${current.projectRoot}, not ${projectRoot}. Enter again to load it anyway, Esc to go back.`);
|
|
100661
|
+
return;
|
|
100662
|
+
}
|
|
100663
|
+
onResume(current).then(onClose).catch((err) => setMessage(`Could not resume: ${err instanceof Error ? err.message : String(err)}`));
|
|
100664
|
+
}
|
|
100665
|
+
}, { isActive: Boolean(isRawModeSupported) });
|
|
100666
|
+
const listRows = Math.max(3, rows - 8);
|
|
100667
|
+
const start = clamp2(at - listRows + 1, 0, Math.max(0, items.length - listRows));
|
|
100668
|
+
const shown = (0, import_react32.useMemo)(() => items.slice(start, start + listRows), [items, start, listRows]);
|
|
100669
|
+
const pv = current ? previews[current.id] : void 0;
|
|
100670
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", children: [
|
|
100671
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { bold: true, children: [
|
|
100672
|
+
"Resume a session ",
|
|
100673
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { ...paint("secondary"), children: `(up/down choose ${glyphs.sep} enter resume ${glyphs.sep} d delete ${glyphs.sep} esc back)` })
|
|
100674
|
+
] }),
|
|
100675
|
+
errors.map((e, i) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { ...paint("needsYou"), children: e }, i)),
|
|
100676
|
+
items.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { ...paint("secondary"), children: "No saved sessions yet." }),
|
|
100677
|
+
shown.map((m2, k2) => {
|
|
100678
|
+
const selected = start + k2 === at;
|
|
100679
|
+
const foreign = m2.projectRoot !== projectRoot;
|
|
100680
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { inverse: selected, ...foreign && !selected ? paint("secondary") : {}, children: [
|
|
100681
|
+
selected ? "> " : " ",
|
|
100682
|
+
formatResumeRow(m2, now, columns - 4, glyphs.sep, glyphs.ellipsis),
|
|
100683
|
+
foreign ? ` [${m2.projectRoot}]` : ""
|
|
100684
|
+
] }, m2.id);
|
|
100685
|
+
}),
|
|
100686
|
+
current && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { ...paint("secondary"), children: pv === void 0 ? "Last message: ..." : pv instanceof Error ? `Preview unavailable: ${pv.message}` : pv === null ? "No assistant reply saved in this session." : `Last message: "${pv}"` }),
|
|
100687
|
+
message && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { ...paint("needsYou"), children: message })
|
|
100688
|
+
] });
|
|
100689
|
+
}
|
|
100690
|
+
var import_react32, import_jsx_runtime11, clamp2;
|
|
100691
|
+
var init_ResumePicker = __esm({
|
|
100692
|
+
async "src/components/ResumePicker.tsx"() {
|
|
100693
|
+
"use strict";
|
|
100694
|
+
import_react32 = __toESM(require_react(), 1);
|
|
100695
|
+
await init_build2();
|
|
100696
|
+
init_resume_rows();
|
|
100697
|
+
init_rail();
|
|
100698
|
+
init_theme();
|
|
100699
|
+
import_jsx_runtime11 = __toESM(require_jsx_runtime(), 1);
|
|
100700
|
+
clamp2 = (n, lo, hi) => Math.max(lo, Math.min(hi, n));
|
|
100701
|
+
}
|
|
100702
|
+
});
|
|
100703
|
+
|
|
99654
100704
|
// src/App.tsx
|
|
99655
100705
|
var App_exports = {};
|
|
99656
100706
|
__export(App_exports, {
|
|
99657
100707
|
App: () => App2
|
|
99658
100708
|
});
|
|
99659
100709
|
import os4 from "node:os";
|
|
99660
|
-
import
|
|
99661
|
-
function App2({ repoName: repoName2, onboardingMode: onboardingMode2, launchCount: launchCount2, yolo: yolo2, history, sessions, resumeLatest: resumeLatest2 }) {
|
|
99662
|
-
const promptHistory = (0,
|
|
99663
|
-
() => history ?? new PromptHistory(createFileHistoryIO(
|
|
100710
|
+
import path10 from "node:path";
|
|
100711
|
+
function App2({ repoName: repoName2, onboardingMode: onboardingMode2, launchCount: launchCount2, yolo: yolo2, history, sessions, resumeLatest: resumeLatest2, git: gitRunner, interactivePicker = true }) {
|
|
100712
|
+
const promptHistory = (0, import_react33.useMemo)(
|
|
100713
|
+
() => history ?? new PromptHistory(createFileHistoryIO(path10.join(os4.homedir(), ".spacecode", "history"))),
|
|
99664
100714
|
[history]
|
|
99665
100715
|
);
|
|
99666
|
-
const sessionStore = (0,
|
|
99667
|
-
const
|
|
100716
|
+
const sessionStore = (0, import_react33.useMemo)(() => sessions ?? new SessionStore(path10.join(os4.homedir(), ".spacecode", "sessions")), [sessions]);
|
|
100717
|
+
const runGit = (0, import_react33.useMemo)(() => gitRunner ?? createNodeGitRunner(process.cwd()), [gitRunner]);
|
|
100718
|
+
const startedAtRef = (0, import_react33.useRef)(Date.now());
|
|
100719
|
+
const [viewingRaw, setViewing] = (0, import_react33.useState)(false);
|
|
100720
|
+
const escViewRef = (0, import_react33.useRef)(false);
|
|
100721
|
+
const escSwallowRef = (0, import_react33.useRef)(false);
|
|
100722
|
+
const swallowEsc = (0, import_react33.useCallback)(() => {
|
|
100723
|
+
escSwallowRef.current = true;
|
|
100724
|
+
setTimeout(() => {
|
|
100725
|
+
escSwallowRef.current = false;
|
|
100726
|
+
}, 0);
|
|
100727
|
+
}, []);
|
|
100728
|
+
const isEscOwned = (0, import_react33.useCallback)(() => escViewRef.current || escSwallowRef.current, []);
|
|
100729
|
+
const openViewer = (0, import_react33.useCallback)(() => setViewing(true), []);
|
|
100730
|
+
const { isRawModeSupported } = use_stdin_default();
|
|
100731
|
+
const [resumeFound, setResumeFound] = (0, import_react33.useState)(null);
|
|
100732
|
+
const openResume = (0, import_react33.useCallback)((found) => setResumeFound(found), []);
|
|
100733
|
+
const { turns, events, isRunning, livePrompt, notices, pending, mode, todos, stats, submit, retry, mentions, replayed, resume } = useReplController({ repoName: repoName2, yolo: yolo2, sessions: sessionStore, resumeLatest: resumeLatest2, onViewTurns: openViewer, onViewResume: isRawModeSupported && interactivePicker ? openResume : void 0, isEscOwned });
|
|
99668
100734
|
const { columns, rows } = useTerminalSize();
|
|
100735
|
+
const viewing = viewingRaw && !pending;
|
|
100736
|
+
const picking = resumeFound !== null && !pending;
|
|
100737
|
+
const liveSteps = (0, import_react33.useMemo)(() => stepsOfEvents(events), [events]);
|
|
100738
|
+
const [focusId, setFocusId] = (0, import_react33.useState)(null);
|
|
100739
|
+
const [openId, setOpenId] = (0, import_react33.useState)(null);
|
|
100740
|
+
const focusIdx = focusId === null ? -1 : liveSteps.findIndex((r) => r.id === focusId);
|
|
100741
|
+
const focusing = focusIdx >= 0 && !viewing && !picking && !pending;
|
|
100742
|
+
escViewRef.current = focusing || viewing || picking;
|
|
100743
|
+
const enterFocus = (0, import_react33.useCallback)(() => {
|
|
100744
|
+
if (liveSteps.length > 0) setFocusId(liveSteps[liveSteps.length - 1].id);
|
|
100745
|
+
}, [liveSteps]);
|
|
100746
|
+
use_input_default((input, key) => {
|
|
100747
|
+
if (key.escape) {
|
|
100748
|
+
swallowEsc();
|
|
100749
|
+
setFocusId(null);
|
|
100750
|
+
setOpenId(null);
|
|
100751
|
+
} else if (key.tab && !key.shift || key.downArrow) {
|
|
100752
|
+
setFocusId(liveSteps[(focusIdx + 1) % liveSteps.length].id);
|
|
100753
|
+
setOpenId(null);
|
|
100754
|
+
} else if (key.upArrow) {
|
|
100755
|
+
setFocusId(liveSteps[(focusIdx + liveSteps.length - 1) % liveSteps.length].id);
|
|
100756
|
+
setOpenId(null);
|
|
100757
|
+
} else if (key.return || input === " ") {
|
|
100758
|
+
setOpenId((cur) => cur === focusId ? null : focusId);
|
|
100759
|
+
}
|
|
100760
|
+
}, { isActive: Boolean(isRawModeSupported) && focusing });
|
|
100761
|
+
use_input_default((input, key) => {
|
|
100762
|
+
if (key.ctrl && input === "o") setViewing(true);
|
|
100763
|
+
}, { isActive: Boolean(isRawModeSupported) && !viewing && !picking && !pending });
|
|
100764
|
+
const waitingStep = pending ? findBlockedStep(liveSteps, pending.req) : void 0;
|
|
100765
|
+
const promptNode = pending ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PermissionPrompt, { req: pending.req, onDecide: pending.resolve }, pending.id) : null;
|
|
100766
|
+
const viewerTurns = (0, import_react33.useMemo)(() => [...replayed, ...turns], [replayed, turns]);
|
|
100767
|
+
const turnSteps = (0, import_react33.useMemo)(() => viewing ? stepsOfTurns(viewerTurns) : /* @__PURE__ */ new Map(), [viewing, viewerTurns]);
|
|
100768
|
+
const [gitState, setGitState] = (0, import_react33.useState)(null);
|
|
100769
|
+
(0, import_react33.useEffect)(() => {
|
|
100770
|
+
let live = true;
|
|
100771
|
+
void readGitState(runGit).then((g2) => {
|
|
100772
|
+
if (live) setGitState(g2);
|
|
100773
|
+
});
|
|
100774
|
+
return () => {
|
|
100775
|
+
live = false;
|
|
100776
|
+
};
|
|
100777
|
+
}, [runGit, turns.length]);
|
|
99669
100778
|
const footer = formatFooter({ mode, contextPct: stats.contextPct, costUsd: stats.costUsd, model: stats.model, columns });
|
|
99670
|
-
return /* @__PURE__ */ (0,
|
|
99671
|
-
|
|
99672
|
-
|
|
99673
|
-
|
|
100779
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Box_default, { flexDirection: "column", children: [
|
|
100780
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Header, { project: repoName2 ?? path10.basename(process.cwd()), git: gitState, turns: turns.length, elapsedMs: Date.now() - startedAtRef.current, columns }),
|
|
100781
|
+
notices.map((n, i) => typeof n === "string" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { ...paint("action"), children: n }, i) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ErrorBlock, { view: n.error }, i)),
|
|
100782
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Static, { items: [BANNER, ...turns], children: (turn) => typeof turn === "symbol" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(WelcomeBanner, { repoName: repoName2, onboardingMode: onboardingMode2, launchCount: launchCount2 }, "banner") : /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Box_default, { flexDirection: "column", children: [
|
|
100783
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Text, { ...paint("secondary"), children: [
|
|
99674
100784
|
"> ",
|
|
99675
100785
|
turn.prompt
|
|
99676
100786
|
] }),
|
|
99677
|
-
/* @__PURE__ */ (0,
|
|
99678
|
-
turn.interrupted && /* @__PURE__ */ (0,
|
|
99679
|
-
turn.summary && /* @__PURE__ */ (0,
|
|
100787
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(EventRenderer, { events: turn.events, promptSaved: turn.retained }),
|
|
100788
|
+
turn.interrupted && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { ...paint("needsYou"), children: "Interrupted" }),
|
|
100789
|
+
turn.summary && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { ...paint("secondary"), children: turn.summary })
|
|
99680
100790
|
] }, turn.id) }),
|
|
99681
|
-
/* @__PURE__ */ (0,
|
|
99682
|
-
/* @__PURE__ */ (0,
|
|
99683
|
-
|
|
99684
|
-
|
|
99685
|
-
|
|
99686
|
-
|
|
100791
|
+
viewing && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(TurnViewer, { turns: viewerTurns, steps: turnSteps, rows, columns, onClose: () => setViewing(false), onEscape: swallowEsc }),
|
|
100792
|
+
picking && resumeFound && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
100793
|
+
ResumePicker,
|
|
100794
|
+
{
|
|
100795
|
+
sessions: resumeFound.sessions,
|
|
100796
|
+
errors: resumeFound.errors,
|
|
100797
|
+
projectRoot: process.cwd(),
|
|
100798
|
+
now: /* @__PURE__ */ new Date(),
|
|
100799
|
+
rows,
|
|
100800
|
+
columns,
|
|
100801
|
+
preview: resume.preview,
|
|
100802
|
+
onResume: resume.load,
|
|
100803
|
+
onDelete: resume.remove,
|
|
100804
|
+
onClose: () => setResumeFound(null),
|
|
100805
|
+
onEscape: swallowEsc
|
|
100806
|
+
}
|
|
100807
|
+
),
|
|
100808
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Box_default, { flexDirection: "column", display: viewing || picking ? "none" : "flex", children: [
|
|
100809
|
+
livePrompt !== null && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Text, { ...paint("secondary"), children: [
|
|
100810
|
+
"> ",
|
|
100811
|
+
livePrompt
|
|
100812
|
+
] }),
|
|
100813
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(EventRenderer, { deferErrors: true, events, liveMaxLines: Math.max(6, rows - 15), focusId: focusing ? focusId : null, openId: focusing ? openId : null, detailRows: Math.max(4, rows - 14), waitingId: waitingStep?.id ?? null, prompt: promptNode }),
|
|
100814
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(TodoChecklist, { todos }),
|
|
100815
|
+
pending && !waitingStep && promptNode,
|
|
100816
|
+
isRunning && !pending && stats.elapsedMs !== null && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { ...paint("secondary"), children: formatThinking(stats.elapsedMs, stats.approxTokens, activeGlyphs().rail === "\u2502") }),
|
|
100817
|
+
focusing && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { ...paint("secondary"), children: activeGlyphs().rail === "\u2502" ? "step focus: \u2191\u2193 or tab move \xB7 enter open/close \xB7 esc back to the prompt" : "step focus: up/down or tab move - enter open/close - esc back to the prompt" }),
|
|
100818
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PromptEditor, { onEmptyEnter: isRunning ? void 0 : retry, promptGlyph: promptGlyph(mode, activeGlyphs().rail === "\u2502"), unicode: activeGlyphs().rail === "\u2502", onSubmit: submit, disabled: isRunning, history: promptHistory, listFiles: mentions.list, onTab: enterFocus, locked: focusing || viewing || picking || Boolean(pending) }),
|
|
100819
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { ...footer.level === "critical" ? paint("delete") : footer.level === "warn" ? paint("needsYou") : paint("secondary"), children: footer.text }),
|
|
100820
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { ...paint("secondary"), children: formatHints({ running: isRunning, asking: Boolean(pending), columns }, activeGlyphs().sep) })
|
|
100821
|
+
] })
|
|
99687
100822
|
] });
|
|
99688
100823
|
}
|
|
99689
|
-
var
|
|
100824
|
+
var import_react33, import_jsx_runtime12, BANNER;
|
|
99690
100825
|
var init_App2 = __esm({
|
|
99691
100826
|
async "src/App.tsx"() {
|
|
99692
100827
|
"use strict";
|
|
99693
|
-
|
|
100828
|
+
import_react33 = __toESM(require_react(), 1);
|
|
99694
100829
|
await init_build2();
|
|
99695
100830
|
await init_WelcomeBanner();
|
|
100831
|
+
await init_Header();
|
|
100832
|
+
await init_ErrorBlock();
|
|
100833
|
+
init_git_status();
|
|
100834
|
+
init_node_git();
|
|
100835
|
+
init_hints();
|
|
100836
|
+
init_mode_line();
|
|
100837
|
+
init_rail();
|
|
99696
100838
|
await init_EventRenderer();
|
|
99697
100839
|
await init_PermissionPrompt();
|
|
99698
100840
|
await init_TodoChecklist();
|
|
@@ -99703,7 +100845,11 @@ var init_App2 = __esm({
|
|
|
99703
100845
|
await init_useReplController();
|
|
99704
100846
|
await init_useTerminalSize();
|
|
99705
100847
|
init_session_store();
|
|
99706
|
-
|
|
100848
|
+
init_theme();
|
|
100849
|
+
await init_TurnViewer();
|
|
100850
|
+
await init_ResumePicker();
|
|
100851
|
+
init_step_view();
|
|
100852
|
+
import_jsx_runtime12 = __toESM(require_jsx_runtime(), 1);
|
|
99707
100853
|
BANNER = /* @__PURE__ */ Symbol("welcome-banner");
|
|
99708
100854
|
}
|
|
99709
100855
|
});
|
|
@@ -99724,7 +100870,7 @@ function hideCiVariables(env3, stdoutIsTTY) {
|
|
|
99724
100870
|
}
|
|
99725
100871
|
|
|
99726
100872
|
// src/index.tsx
|
|
99727
|
-
var
|
|
100873
|
+
var import_jsx_runtime13 = __toESM(require_jsx_runtime(), 1);
|
|
99728
100874
|
var repoName = process.env.REPO_NAME || process.cwd().split("/").pop() || void 0;
|
|
99729
100875
|
var validModes = ["guest", "new_project", "existing_repo"];
|
|
99730
100876
|
var rawMode = process.env.ONBOARDING_MODE;
|
|
@@ -99741,7 +100887,7 @@ try {
|
|
|
99741
100887
|
} finally {
|
|
99742
100888
|
restoreCiVariables();
|
|
99743
100889
|
}
|
|
99744
|
-
render2(/* @__PURE__ */ (0,
|
|
100890
|
+
render2(/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(App3, { repoName, onboardingMode, launchCount, yolo, resumeLatest, interactivePicker: Boolean(process.stdout.isTTY) }));
|
|
99745
100891
|
/*! Bundled license information:
|
|
99746
100892
|
|
|
99747
100893
|
react/cjs/react.production.min.js:
|