@stigmer/ink 0.5.1 → 1.0.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/app/SessionApp.d.ts +11 -1
- package/app/SessionApp.d.ts.map +1 -1
- package/app/SessionApp.js +2 -2
- package/app/SessionApp.js.map +1 -1
- package/app/SessionView.d.ts +14 -1
- package/app/SessionView.d.ts.map +1 -1
- package/app/SessionView.js +14 -5
- package/app/SessionView.js.map +1 -1
- package/cli/stigmer-ink.js +12 -1
- package/cli/stigmer-ink.js.map +1 -1
- package/components/ContextGauge.d.ts +26 -0
- package/components/ContextGauge.d.ts.map +1 -0
- package/components/ContextGauge.js +59 -0
- package/components/ContextGauge.js.map +1 -0
- package/components/FollowUpInput.d.ts +3 -1
- package/components/FollowUpInput.d.ts.map +1 -1
- package/components/FollowUpInput.js +2 -2
- package/components/FollowUpInput.js.map +1 -1
- package/index.d.ts +2 -1
- package/index.d.ts.map +1 -1
- package/index.js +1 -0
- package/index.js.map +1 -1
- package/package.json +4 -4
- package/src/__tests__/context-gauge.test.tsx +176 -0
- package/src/app/SessionApp.tsx +12 -1
- package/src/app/SessionView.tsx +41 -13
- package/src/cli/stigmer-ink.tsx +13 -0
- package/src/components/ContextGauge.tsx +120 -0
- package/src/components/FollowUpInput.tsx +4 -1
- package/src/index.ts +2 -1
package/app/SessionApp.d.ts
CHANGED
|
@@ -11,6 +11,16 @@ export interface SessionAppProps {
|
|
|
11
11
|
readonly apiKey?: string;
|
|
12
12
|
/** Dynamic token provider for authentication. */
|
|
13
13
|
readonly getAccessToken?: TokenProvider;
|
|
14
|
+
/**
|
|
15
|
+
* Default interaction mode for follow-up executions.
|
|
16
|
+
*
|
|
17
|
+
* - `"agent"` (default): full tool access.
|
|
18
|
+
* - `"plan"`: read-only analysis, no file mutations.
|
|
19
|
+
*
|
|
20
|
+
* When set, all follow-up executions in this session use this mode
|
|
21
|
+
* unless overridden by the user.
|
|
22
|
+
*/
|
|
23
|
+
readonly mode?: "agent" | "plan";
|
|
14
24
|
}
|
|
15
25
|
/**
|
|
16
26
|
* Self-contained top-level Ink application for viewing and
|
|
@@ -38,5 +48,5 @@ export interface SessionAppProps {
|
|
|
38
48
|
* );
|
|
39
49
|
* ```
|
|
40
50
|
*/
|
|
41
|
-
export declare function SessionApp({ sessionId, org, baseUrl, apiKey, getAccessToken, }: SessionAppProps): import("react/jsx-runtime").JSX.Element;
|
|
51
|
+
export declare function SessionApp({ sessionId, org, baseUrl, apiKey, getAccessToken, mode, }: SessionAppProps): import("react/jsx-runtime").JSX.Element;
|
|
42
52
|
//# sourceMappingURL=SessionApp.d.ts.map
|
package/app/SessionApp.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionApp.d.ts","sourceRoot":"","sources":["../../src/app/SessionApp.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAKlD,oCAAoC;AACpC,MAAM,WAAW,eAAe;IAC9B,6CAA6C;IAC7C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,2DAA2D;IAC3D,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,8BAA8B;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,yCAAyC;IACzC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,iDAAiD;IACjD,QAAQ,CAAC,cAAc,CAAC,EAAE,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"SessionApp.d.ts","sourceRoot":"","sources":["../../src/app/SessionApp.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAKlD,oCAAoC;AACpC,MAAM,WAAW,eAAe;IAC9B,6CAA6C;IAC7C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,2DAA2D;IAC3D,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,8BAA8B;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,yCAAyC;IACzC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,iDAAiD;IACjD,QAAQ,CAAC,cAAc,CAAC,EAAE,aAAa,CAAC;IACxC;;;;;;;;OAQG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;CAClC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,UAAU,CAAC,EACzB,SAAS,EACT,GAAG,EACH,OAAO,EACP,MAAM,EACN,cAAc,EACd,IAAI,GACL,EAAE,eAAe,2CAoBjB"}
|
package/app/SessionApp.js
CHANGED
|
@@ -30,9 +30,9 @@ import { SessionView } from "./SessionView.js";
|
|
|
30
30
|
* );
|
|
31
31
|
* ```
|
|
32
32
|
*/
|
|
33
|
-
export function SessionApp({ sessionId, org, baseUrl, apiKey, getAccessToken, }) {
|
|
33
|
+
export function SessionApp({ sessionId, org, baseUrl, apiKey, getAccessToken, mode, }) {
|
|
34
34
|
const clientConfig = { baseUrl, apiKey, getAccessToken };
|
|
35
35
|
const client = React.useMemo(() => createNodeClient(clientConfig), [baseUrl, apiKey, getAccessToken]);
|
|
36
|
-
return (_jsx(InkStigmerProvider, { client: client, children: _jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { paddingLeft: 1, paddingBottom: 1, children: _jsxs(Text, { dimColor: true, children: ["Session ", sessionId, " \u00B7 ", org] }) }), _jsx(SessionView, { sessionId: sessionId, org: org })] }) }));
|
|
36
|
+
return (_jsx(InkStigmerProvider, { client: client, children: _jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { paddingLeft: 1, paddingBottom: 1, children: _jsxs(Text, { dimColor: true, children: ["Session ", sessionId, " \u00B7 ", org] }) }), _jsx(SessionView, { sessionId: sessionId, org: org, mode: mode })] }) }));
|
|
37
37
|
}
|
|
38
38
|
//# sourceMappingURL=SessionApp.js.map
|
package/app/SessionApp.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionApp.js","sourceRoot":"","sources":["../../src/app/SessionApp.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAEhC,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAyB,MAAM,mBAAmB,CAAC;AAC5E,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"SessionApp.js","sourceRoot":"","sources":["../../src/app/SessionApp.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAEhC,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAyB,MAAM,mBAAmB,CAAC;AAC5E,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AA0B/C;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,UAAU,UAAU,CAAC,EACzB,SAAS,EACT,GAAG,EACH,OAAO,EACP,MAAM,EACN,cAAc,EACd,IAAI,GACY;IAChB,MAAM,YAAY,GAAqB,EAAE,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC;IAE3E,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAC1B,GAAG,EAAE,CAAC,gBAAgB,CAAC,YAAY,CAAC,EACpC,CAAC,OAAO,EAAE,MAAM,EAAE,cAAc,CAAC,CAClC,CAAC;IAEF,OAAO,CACL,KAAC,kBAAkB,IAAC,MAAM,EAAE,MAAM,YAChC,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aACzB,KAAC,GAAG,IAAC,WAAW,EAAE,CAAC,EAAE,aAAa,EAAE,CAAC,YACnC,MAAC,IAAI,IAAC,QAAQ,+BACH,SAAS,cAAK,GAAG,IACrB,GACH,EACN,KAAC,WAAW,IAAC,SAAS,EAAE,SAAS,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,GAAI,IACvD,GACa,CACtB,CAAC;AACJ,CAAC"}
|
package/app/SessionView.d.ts
CHANGED
|
@@ -1,9 +1,22 @@
|
|
|
1
|
+
/** Interaction mode type used for follow-up executions. */
|
|
2
|
+
export type InteractionMode = "agent" | "plan";
|
|
1
3
|
/** Props for {@link SessionView}. */
|
|
2
4
|
export interface SessionViewProps {
|
|
3
5
|
/** Session ID to display and converse in. */
|
|
4
6
|
readonly sessionId: string;
|
|
5
7
|
/** Organization slug for creating follow-up executions. */
|
|
6
8
|
readonly org: string;
|
|
9
|
+
/**
|
|
10
|
+
* Initial interaction mode for follow-up executions.
|
|
11
|
+
*
|
|
12
|
+
* - `"agent"` (default): full tool access.
|
|
13
|
+
* - `"plan"`: read-only analysis, no file mutations.
|
|
14
|
+
*
|
|
15
|
+
* The user can toggle between modes with Ctrl+T during the session.
|
|
16
|
+
* This prop sets the initial value; subsequent toggles are managed
|
|
17
|
+
* as local state.
|
|
18
|
+
*/
|
|
19
|
+
readonly mode?: InteractionMode;
|
|
7
20
|
}
|
|
8
21
|
/**
|
|
9
22
|
* Full-featured session conversation view for the terminal.
|
|
@@ -28,5 +41,5 @@ export interface SessionViewProps {
|
|
|
28
41
|
* );
|
|
29
42
|
* ```
|
|
30
43
|
*/
|
|
31
|
-
export declare function SessionView({ sessionId, org }: SessionViewProps): import("react/jsx-runtime").JSX.Element;
|
|
44
|
+
export declare function SessionView({ sessionId, org, mode }: SessionViewProps): import("react/jsx-runtime").JSX.Element;
|
|
32
45
|
//# sourceMappingURL=SessionView.d.ts.map
|
package/app/SessionView.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionView.d.ts","sourceRoot":"","sources":["../../src/app/SessionView.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"SessionView.d.ts","sourceRoot":"","sources":["../../src/app/SessionView.tsx"],"names":[],"mappings":"AAWA,2DAA2D;AAC3D,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,MAAM,CAAC;AAE/C,qCAAqC;AACrC,MAAM,WAAW,gBAAgB;IAC/B,6CAA6C;IAC7C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,2DAA2D;IAC3D,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB;;;;;;;;;OASG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,eAAe,CAAC;CACjC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,WAAW,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,gBAAgB,2CAuIrE"}
|
package/app/SessionView.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from "react";
|
|
2
|
+
import { useState, useEffect } from "react";
|
|
3
3
|
import { Box, Text, useInput } from "ink";
|
|
4
4
|
import Spinner from "ink-spinner";
|
|
5
5
|
import { useSessionConversation, resolvedSubject, PENDING_SUBJECT } from "@stigmer/react";
|
|
@@ -7,6 +7,7 @@ import { MessageThread } from "../components/MessageThread.js";
|
|
|
7
7
|
import { TodoList } from "../components/TodoList.js";
|
|
8
8
|
import { FollowUpInput } from "../components/FollowUpInput.js";
|
|
9
9
|
import { UsageWidget } from "../components/UsageWidget.js";
|
|
10
|
+
import { ContextGauge } from "../components/ContextGauge.js";
|
|
10
11
|
import { ExecutionProgress } from "../components/ExecutionProgress.js";
|
|
11
12
|
/**
|
|
12
13
|
* Full-featured session conversation view for the terminal.
|
|
@@ -31,13 +32,21 @@ import { ExecutionProgress } from "../components/ExecutionProgress.js";
|
|
|
31
32
|
* );
|
|
32
33
|
* ```
|
|
33
34
|
*/
|
|
34
|
-
export function SessionView({ sessionId, org }) {
|
|
35
|
+
export function SessionView({ sessionId, org, mode }) {
|
|
35
36
|
const conv = useSessionConversation(sessionId, org);
|
|
36
37
|
const [expandTools, setExpandTools] = useState(false);
|
|
38
|
+
const [activeMode, setActiveMode] = useState(mode ?? "agent");
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
if (mode)
|
|
41
|
+
setActiveMode(mode);
|
|
42
|
+
}, [mode]);
|
|
37
43
|
useInput((input, key) => {
|
|
38
44
|
if (key.ctrl && input === "o") {
|
|
39
45
|
setExpandTools((e) => !e);
|
|
40
46
|
}
|
|
47
|
+
if (key.ctrl && input === "t") {
|
|
48
|
+
setActiveMode((m) => (m === "plan" ? "agent" : "plan"));
|
|
49
|
+
}
|
|
41
50
|
});
|
|
42
51
|
if (conv.isLoading) {
|
|
43
52
|
return (_jsxs(Box, { gap: 1, paddingLeft: 1, children: [_jsx(Text, { color: "cyan", children: _jsx(Spinner, { type: "dots" }) }), _jsx(Text, { children: "Loading session..." })] }));
|
|
@@ -50,9 +59,9 @@ export function SessionView({ sessionId, org }) {
|
|
|
50
59
|
...(conv.activeStreamExecution ? [conv.activeStreamExecution] : []),
|
|
51
60
|
];
|
|
52
61
|
const activeTodos = conv.activeStreamExecution?.status?.todos;
|
|
53
|
-
const contextInfo = conv.activeStreamExecution?.status?.contextInfo;
|
|
54
|
-
const summarizationCount = contextInfo?.summarizationEvents?.length ?? 0;
|
|
55
62
|
const subject = resolvedSubject(conv.session?.spec?.subject);
|
|
56
|
-
return (_jsxs(Box, { flexDirection: "column", children: [subject && subject !== PENDING_SUBJECT && (_jsx(Box, { paddingLeft: 1, marginBottom: 1, children: _jsx(Text, { dimColor: true, bold: true, children: subject }) })), conv.isConnecting && (_jsxs(Box, { gap: 1, paddingLeft: 1, children: [_jsx(Text, { color: "cyan", children: _jsx(Spinner, { type: "dots" }) }), _jsx(Text, { dimColor: true, children: "Connecting to stream..." })] })), conv.streamError && (_jsxs(Box, { flexDirection: "column", paddingLeft: 1, marginBottom: 1, children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: "yellow", bold: true, children: "Stream disconnected" }), _jsx(Text, { dimColor: true, children: "\u2014 reconnecting..." })] }), _jsx(Text, { color: "red", dimColor: true, children: conv.streamError.message })] })), conv.activePhase != null && conv.activePhase !== 0 && (_jsx(ExecutionProgress, { phase: conv.activePhase })), _jsx(MessageThread, { executions: conv.completedExecutions, activeStreamExecution: conv.activeStreamExecution, pendingUserMessage: conv.pendingUserMessage, onApprovalSubmit: conv.submitApproval, submittingApprovalIds: conv.submittingApprovalIds, expandToolCalls: expandTools }),
|
|
63
|
+
return (_jsxs(Box, { flexDirection: "column", children: [subject && subject !== PENDING_SUBJECT && (_jsx(Box, { paddingLeft: 1, marginBottom: 1, children: _jsx(Text, { dimColor: true, bold: true, children: subject }) })), conv.isConnecting && (_jsxs(Box, { gap: 1, paddingLeft: 1, children: [_jsx(Text, { color: "cyan", children: _jsx(Spinner, { type: "dots" }) }), _jsx(Text, { dimColor: true, children: "Connecting to stream..." })] })), conv.streamError && (_jsxs(Box, { flexDirection: "column", paddingLeft: 1, marginBottom: 1, children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: "yellow", bold: true, children: "Stream disconnected" }), _jsx(Text, { dimColor: true, children: "\u2014 reconnecting..." })] }), _jsx(Text, { color: "red", dimColor: true, children: conv.streamError.message })] })), conv.activePhase != null && conv.activePhase !== 0 && (_jsx(ExecutionProgress, { phase: conv.activePhase })), _jsx(MessageThread, { executions: conv.completedExecutions, activeStreamExecution: conv.activeStreamExecution, pendingUserMessage: conv.pendingUserMessage, onApprovalSubmit: conv.submitApproval, submittingApprovalIds: conv.submittingApprovalIds, expandToolCalls: expandTools }), activeTodos && Object.keys(activeTodos).length > 0 && (_jsx(Box, { marginTop: 1, paddingLeft: 1, children: _jsx(TodoList, { todos: activeTodos }) })), conv.approvalError && (_jsx(Box, { paddingLeft: 1, children: _jsxs(Text, { color: "red", children: ["Approval error: ", conv.approvalError.message] }) })), conv.sendError && (_jsx(Box, { paddingLeft: 1, children: _jsxs(Text, { color: "red", children: ["Send error: ", conv.sendError.message] }) })), _jsx(UsageWidget, { executions: allExecutions }), _jsx(ContextGauge, { execution: conv.activeStreamExecution ?? null }), conv.canSendFollowUp && (_jsx(Box, { paddingLeft: 1, children: _jsx(Text, { color: activeMode === "plan" ? "yellow" : "cyan", dimColor: true, children: activeMode === "plan"
|
|
64
|
+
? "Plan mode — read-only analysis, no file mutations"
|
|
65
|
+
: "Agent mode — full tool access" }) })), _jsx(FollowUpInput, { onSubmit: (message) => conv.sendFollowUp(message, { interactionMode: activeMode }), isSubmitting: conv.isSending, disabled: !conv.canSendFollowUp, mode: activeMode })] }));
|
|
57
66
|
}
|
|
58
67
|
//# sourceMappingURL=SessionView.js.map
|
package/app/SessionView.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionView.js","sourceRoot":"","sources":["../../src/app/SessionView.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"SessionView.js","sourceRoot":"","sources":["../../src/app/SessionView.tsx"],"names":[],"mappings":"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"}
|
package/cli/stigmer-ink.js
CHANGED
|
@@ -24,6 +24,16 @@ function parseArgs(argv) {
|
|
|
24
24
|
case "-k":
|
|
25
25
|
config.apiKey = args[++i];
|
|
26
26
|
break;
|
|
27
|
+
case "--mode":
|
|
28
|
+
case "-M": {
|
|
29
|
+
const value = args[++i];
|
|
30
|
+
if (value !== "agent" && value !== "plan") {
|
|
31
|
+
console.error(`Invalid --mode value "${value}": must be "agent" or "plan"`);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
config.mode = value;
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
27
37
|
case "--help":
|
|
28
38
|
case "-h":
|
|
29
39
|
printUsage();
|
|
@@ -80,6 +90,7 @@ Options:
|
|
|
80
90
|
-o, --org <slug> Organization slug (required)
|
|
81
91
|
-u, --base-url <url> Stigmer API URL (or STIGMER_BASE_URL env)
|
|
82
92
|
-k, --api-key <key> API key (or STIGMER_API_KEY env)
|
|
93
|
+
-M, --mode <mode> Interaction mode for follow-ups: "agent" or "plan"
|
|
83
94
|
-h, --help Show this help message
|
|
84
95
|
|
|
85
96
|
Environment:
|
|
@@ -101,7 +112,7 @@ async function main() {
|
|
|
101
112
|
const stdin = isTTY
|
|
102
113
|
? process.stdin
|
|
103
114
|
: new Readable({ read() { } });
|
|
104
|
-
const instance = render(_jsx(SessionApp, { sessionId: config.sessionId, org: config.org, baseUrl: config.baseUrl, apiKey: config.apiKey }), { stdin, exitOnCtrlC: isTTY, debug: !isTTY });
|
|
115
|
+
const instance = render(_jsx(SessionApp, { sessionId: config.sessionId, org: config.org, baseUrl: config.baseUrl, apiKey: config.apiKey, mode: config.mode }), { stdin, exitOnCtrlC: isTTY, debug: !isTTY });
|
|
105
116
|
const cleanup = () => {
|
|
106
117
|
instance.unmount();
|
|
107
118
|
process.exit(0);
|
package/cli/stigmer-ink.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stigmer-ink.js","sourceRoot":"","sources":["../../src/cli/stigmer-ink.tsx"],"names":[],"mappings":";;AAGA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,MAAM,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"stigmer-ink.js","sourceRoot":"","sources":["../../src/cli/stigmer-ink.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { AgentExecution } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
2
|
+
/** Props for {@link ContextGauge}. */
|
|
3
|
+
export interface ContextGaugeProps {
|
|
4
|
+
/**
|
|
5
|
+
* The execution snapshot from the active stream, or `null`.
|
|
6
|
+
* The gauge extracts `context_info` from the execution status.
|
|
7
|
+
*/
|
|
8
|
+
readonly execution: AgentExecution | null;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Terminal-native context window utilization gauge.
|
|
12
|
+
*
|
|
13
|
+
* Renders an ASCII progress bar with health-based coloring, token counts,
|
|
14
|
+
* and summarization event details. Uses the headless
|
|
15
|
+
* {@link useContextWindow} hook from `@stigmer/react`.
|
|
16
|
+
*
|
|
17
|
+
* Returns `null` when `ContextInfo` is absent (e.g., Cursor harness
|
|
18
|
+
* where context is managed externally).
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```tsx
|
|
22
|
+
* <ContextGauge execution={activeStreamExecution} />
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export declare function ContextGauge({ execution }: ContextGaugeProps): import("react/jsx-runtime").JSX.Element | null;
|
|
26
|
+
//# sourceMappingURL=ContextGauge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ContextGauge.d.ts","sourceRoot":"","sources":["../../src/components/ContextGauge.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6DAA6D,CAAC;AAOlG,sCAAsC;AACtC,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,QAAQ,CAAC,SAAS,EAAE,cAAc,GAAG,IAAI,CAAC;CAC3C;AA0BD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,CAAC,EAAE,SAAS,EAAE,EAAE,iBAAiB,kDA4C5D"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import { useContextWindow, } from "@stigmer/react";
|
|
4
|
+
const BAR_WIDTH = 20;
|
|
5
|
+
const HEALTH_COLORS = {
|
|
6
|
+
healthy: "green",
|
|
7
|
+
warning: "yellow",
|
|
8
|
+
critical: "red",
|
|
9
|
+
};
|
|
10
|
+
const HEALTH_LABELS = {
|
|
11
|
+
warning: "Approaching limit",
|
|
12
|
+
critical: "Near limit",
|
|
13
|
+
};
|
|
14
|
+
function formatCompactTokens(count) {
|
|
15
|
+
if (count >= 1_000_000)
|
|
16
|
+
return `${(count / 1_000_000).toFixed(1)}M`;
|
|
17
|
+
if (count >= 1_000)
|
|
18
|
+
return `${Math.round(count / 1_000)}K`;
|
|
19
|
+
return String(count);
|
|
20
|
+
}
|
|
21
|
+
function formatDuration(ms) {
|
|
22
|
+
if (ms < 1000)
|
|
23
|
+
return `${ms}ms`;
|
|
24
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Terminal-native context window utilization gauge.
|
|
28
|
+
*
|
|
29
|
+
* Renders an ASCII progress bar with health-based coloring, token counts,
|
|
30
|
+
* and summarization event details. Uses the headless
|
|
31
|
+
* {@link useContextWindow} hook from `@stigmer/react`.
|
|
32
|
+
*
|
|
33
|
+
* Returns `null` when `ContextInfo` is absent (e.g., Cursor harness
|
|
34
|
+
* where context is managed externally).
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```tsx
|
|
38
|
+
* <ContextGauge execution={activeStreamExecution} />
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export function ContextGauge({ execution }) {
|
|
42
|
+
const ctx = useContextWindow(execution);
|
|
43
|
+
if (!ctx.hasContextInfo)
|
|
44
|
+
return null;
|
|
45
|
+
const filled = Math.round((Math.min(ctx.utilizationPercent, 100) / 100) * BAR_WIDTH);
|
|
46
|
+
const empty = BAR_WIDTH - filled;
|
|
47
|
+
const bar = "█".repeat(filled) + "░".repeat(empty);
|
|
48
|
+
const color = HEALTH_COLORS[ctx.health];
|
|
49
|
+
const healthLabel = HEALTH_LABELS[ctx.health];
|
|
50
|
+
const eventCount = ctx.summarizationEvents.length;
|
|
51
|
+
return (_jsxs(Box, { flexDirection: "column", paddingLeft: 1, children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: "Context" }), _jsx(Text, { color: color, children: bar }), _jsxs(Text, { color: color, children: [Math.round(ctx.utilizationPercent), "%"] }), _jsx(Text, { dimColor: true, children: "\u00B7" }), _jsxs(Text, { dimColor: true, children: [formatCompactTokens(ctx.currentTokenCount), " /", " ", formatCompactTokens(ctx.contextWindowLimit), " tokens"] }), healthLabel && (_jsxs(_Fragment, { children: [_jsx(Text, { dimColor: true, children: "\u00B7" }), _jsx(Text, { color: color, children: healthLabel })] })), eventCount > 0 && (_jsxs(_Fragment, { children: [_jsx(Text, { dimColor: true, children: "\u00B7" }), _jsxs(Text, { dimColor: true, children: [eventCount, " ", eventCount === 1 ? "compaction" : "compactions"] })] }))] }), eventCount > 0 && (_jsx(LatestEvent, { event: ctx.summarizationEvents[eventCount - 1] }))] }));
|
|
52
|
+
}
|
|
53
|
+
function LatestEvent({ event }) {
|
|
54
|
+
const reduction = Math.round(event.compressionRatio * 100);
|
|
55
|
+
return (_jsx(Box, { paddingLeft: 2, children: _jsxs(Text, { dimColor: true, children: [formatCompactTokens(event.tokensBefore), " \u2192", " ", formatCompactTokens(event.tokensAfter), " tokens (", reduction, "% reduction)", event.model ? ` · ${event.model}` : "", event.durationMs > 0 ? ` · ${formatDuration(event.durationMs)}` : "", event.costUsd > 0
|
|
56
|
+
? ` · $${event.costUsd < 0.01 ? event.costUsd.toFixed(4) : event.costUsd.toFixed(2)}`
|
|
57
|
+
: ""] }) }));
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=ContextGauge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ContextGauge.js","sourceRoot":"","sources":["../../src/components/ContextGauge.tsx"],"names":[],"mappings":"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"}
|
|
@@ -8,6 +8,8 @@ export interface FollowUpInputProps {
|
|
|
8
8
|
readonly disabled?: boolean;
|
|
9
9
|
/** Placeholder text. Default: "Reply..." */
|
|
10
10
|
readonly placeholder?: string;
|
|
11
|
+
/** Current interaction mode. Shown in the shortcut hint line. */
|
|
12
|
+
readonly mode?: "agent" | "plan";
|
|
11
13
|
}
|
|
12
14
|
/**
|
|
13
15
|
* Terminal text input for sending follow-up messages in a session.
|
|
@@ -15,5 +17,5 @@ export interface FollowUpInputProps {
|
|
|
15
17
|
* Renders a single-line text input with a submit hint. Press Enter
|
|
16
18
|
* to submit, Ctrl+C to exit the application.
|
|
17
19
|
*/
|
|
18
|
-
export declare function FollowUpInput({ onSubmit, isSubmitting, disabled, placeholder, }: FollowUpInputProps): import("react/jsx-runtime").JSX.Element;
|
|
20
|
+
export declare function FollowUpInput({ onSubmit, isSubmitting, disabled, placeholder, mode, }: FollowUpInputProps): import("react/jsx-runtime").JSX.Element;
|
|
19
21
|
//# sourceMappingURL=FollowUpInput.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FollowUpInput.d.ts","sourceRoot":"","sources":["../../src/components/FollowUpInput.tsx"],"names":[],"mappings":"AAIA,uCAAuC;AACvC,MAAM,WAAW,kBAAkB;IACjC,0DAA0D;IAC1D,QAAQ,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,sDAAsD;IACtD,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;IAChC,mCAAmC;IACnC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,4CAA4C;IAC5C,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"FollowUpInput.d.ts","sourceRoot":"","sources":["../../src/components/FollowUpInput.tsx"],"names":[],"mappings":"AAIA,uCAAuC;AACvC,MAAM,WAAW,kBAAkB;IACjC,0DAA0D;IAC1D,QAAQ,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,sDAAsD;IACtD,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;IAChC,mCAAmC;IACnC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,4CAA4C;IAC5C,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,iEAAiE;IACjE,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;CAClC;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,EAC5B,QAAQ,EACR,YAAoB,EACpB,QAAgB,EAChB,WAAwB,EACxB,IAAI,GACL,EAAE,kBAAkB,2CA0CpB"}
|
|
@@ -8,7 +8,7 @@ import TextInput from "ink-text-input";
|
|
|
8
8
|
* Renders a single-line text input with a submit hint. Press Enter
|
|
9
9
|
* to submit, Ctrl+C to exit the application.
|
|
10
10
|
*/
|
|
11
|
-
export function FollowUpInput({ onSubmit, isSubmitting = false, disabled = false, placeholder = "Reply...", }) {
|
|
11
|
+
export function FollowUpInput({ onSubmit, isSubmitting = false, disabled = false, placeholder = "Reply...", mode, }) {
|
|
12
12
|
const [value, setValue] = useState("");
|
|
13
13
|
const { isRawModeSupported } = useStdin();
|
|
14
14
|
const isDisabled = disabled || isSubmitting || !isRawModeSupported;
|
|
@@ -22,6 +22,6 @@ export function FollowUpInput({ onSubmit, isSubmitting = false, disabled = false
|
|
|
22
22
|
if (isDisabled) {
|
|
23
23
|
return (_jsx(Box, { paddingLeft: 1, paddingTop: 1, children: _jsx(Text, { dimColor: true, children: isSubmitting ? "Sending..." : placeholder }) }));
|
|
24
24
|
}
|
|
25
|
-
return (_jsxs(Box, { paddingLeft: 1, paddingTop: 1, flexDirection: "column", children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: "cyan", bold: true, children: "\u276F" }), _jsx(TextInput, { value: value, onChange: setValue, onSubmit: handleSubmit, placeholder: placeholder })] }), _jsx(Box, { paddingLeft: 2, children:
|
|
25
|
+
return (_jsxs(Box, { paddingLeft: 1, paddingTop: 1, flexDirection: "column", children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: "cyan", bold: true, children: "\u276F" }), _jsx(TextInput, { value: value, onChange: setValue, onSubmit: handleSubmit, placeholder: placeholder })] }), _jsx(Box, { paddingLeft: 2, children: _jsxs(Text, { dimColor: true, children: ["Enter to send \u00B7 Ctrl+T ", mode === "plan" ? "agent" : "plan", " mode \u00B7 Ctrl+C exit"] }) })] }));
|
|
26
26
|
}
|
|
27
27
|
//# sourceMappingURL=FollowUpInput.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FollowUpInput.js","sourceRoot":"","sources":["../../src/components/FollowUpInput.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACxC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAC1C,OAAO,SAAS,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"FollowUpInput.js","sourceRoot":"","sources":["../../src/components/FollowUpInput.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACxC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAC1C,OAAO,SAAS,MAAM,gBAAgB,CAAC;AAgBvC;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,EAC5B,QAAQ,EACR,YAAY,GAAG,KAAK,EACpB,QAAQ,GAAG,KAAK,EAChB,WAAW,GAAG,UAAU,EACxB,IAAI,GACe;IACnB,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;IACvC,MAAM,EAAE,kBAAkB,EAAE,GAAG,QAAQ,EAAE,CAAC;IAC1C,MAAM,UAAU,GAAG,QAAQ,IAAI,YAAY,IAAI,CAAC,kBAAkB,CAAC;IAEnE,MAAM,YAAY,GAAG,CAAC,KAAa,EAAE,EAAE;QACrC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;QAC7B,IAAI,CAAC,OAAO;YAAE,OAAO;QACrB,QAAQ,CAAC,OAAO,CAAC,CAAC;QAClB,QAAQ,CAAC,EAAE,CAAC,CAAC;IACf,CAAC,CAAC;IAEF,IAAI,UAAU,EAAE,CAAC;QACf,OAAO,CACL,KAAC,GAAG,IAAC,WAAW,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,YAChC,KAAC,IAAI,IAAC,QAAQ,kBACX,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,WAAW,GACrC,GACH,CACP,CAAC;IACJ,CAAC;IAED,OAAO,CACL,MAAC,GAAG,IAAC,WAAW,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,aAAa,EAAC,QAAQ,aACxD,MAAC,GAAG,IAAC,GAAG,EAAE,CAAC,aACT,KAAC,IAAI,IAAC,KAAK,EAAC,MAAM,EAAC,IAAI,6BAEhB,EACP,KAAC,SAAS,IACR,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,YAAY,EACtB,WAAW,EAAE,WAAW,GACxB,IACE,EACN,KAAC,GAAG,IAAC,WAAW,EAAE,CAAC,YACjB,MAAC,IAAI,IAAC,QAAQ,mDACY,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,gCACrD,GACH,IACF,CACP,CAAC;AACJ,CAAC"}
|
package/index.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export { ApprovalPrompt, type ApprovalPromptProps } from "./components/ApprovalP
|
|
|
11
11
|
export { ExecutionProgress, type ExecutionProgressProps } from "./components/ExecutionProgress.js";
|
|
12
12
|
export { FollowUpInput, type FollowUpInputProps } from "./components/FollowUpInput.js";
|
|
13
13
|
export { UsageWidget, type UsageWidgetProps } from "./components/UsageWidget.js";
|
|
14
|
-
export {
|
|
14
|
+
export { ContextGauge, type ContextGaugeProps } from "./components/ContextGauge.js";
|
|
15
|
+
export { SessionView, type SessionViewProps, type InteractionMode } from "./app/SessionView.js";
|
|
15
16
|
export { SessionApp, type SessionAppProps } from "./app/SessionApp.js";
|
|
16
17
|
//# sourceMappingURL=index.d.ts.map
|
package/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,KAAK,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAGjF,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,KAAK,gBAAgB,GACtB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAG/C,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACpF,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACpF,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAC1F,OAAO,EAAE,iBAAiB,EAAE,KAAK,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AACnG,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,KAAK,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAGjF,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,KAAK,gBAAgB,GACtB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAG/C,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACpF,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACpF,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAC1F,OAAO,EAAE,iBAAiB,EAAE,KAAK,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AACnG,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAGpF,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAChG,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC"}
|
package/index.js
CHANGED
|
@@ -15,6 +15,7 @@ export { ApprovalPrompt } from "./components/ApprovalPrompt.js";
|
|
|
15
15
|
export { ExecutionProgress } from "./components/ExecutionProgress.js";
|
|
16
16
|
export { FollowUpInput } from "./components/FollowUpInput.js";
|
|
17
17
|
export { UsageWidget } from "./components/UsageWidget.js";
|
|
18
|
+
export { ContextGauge } from "./components/ContextGauge.js";
|
|
18
19
|
// Composed views
|
|
19
20
|
export { SessionView } from "./app/SessionView.js";
|
|
20
21
|
export { SessionApp } from "./app/SessionApp.js";
|
package/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,WAAW;AACX,OAAO,EAAE,kBAAkB,EAAgC,MAAM,eAAe,CAAC;AAEjF,iDAAiD;AACjD,OAAO,EACL,gBAAgB,EAChB,mBAAmB,GAEpB,MAAM,mBAAmB,CAAC;AAE3B,WAAW;AACX,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,aAAa;AACb,OAAO,EAAE,YAAY,EAA0B,MAAM,8BAA8B,CAAC;AACpF,OAAO,EAAE,aAAa,EAA2B,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,YAAY,EAA0B,MAAM,8BAA8B,CAAC;AACpF,OAAO,EAAE,aAAa,EAA2B,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,aAAa,EAA2B,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,QAAQ,EAAsB,MAAM,0BAA0B,CAAC;AACxE,OAAO,EAAE,cAAc,EAA4B,MAAM,gCAAgC,CAAC;AAC1F,OAAO,EAAE,iBAAiB,EAA+B,MAAM,mCAAmC,CAAC;AACnG,OAAO,EAAE,aAAa,EAA2B,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,WAAW,EAAyB,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,WAAW;AACX,OAAO,EAAE,kBAAkB,EAAgC,MAAM,eAAe,CAAC;AAEjF,iDAAiD;AACjD,OAAO,EACL,gBAAgB,EAChB,mBAAmB,GAEpB,MAAM,mBAAmB,CAAC;AAE3B,WAAW;AACX,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,aAAa;AACb,OAAO,EAAE,YAAY,EAA0B,MAAM,8BAA8B,CAAC;AACpF,OAAO,EAAE,aAAa,EAA2B,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,YAAY,EAA0B,MAAM,8BAA8B,CAAC;AACpF,OAAO,EAAE,aAAa,EAA2B,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,aAAa,EAA2B,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,QAAQ,EAAsB,MAAM,0BAA0B,CAAC;AACxE,OAAO,EAAE,cAAc,EAA4B,MAAM,gCAAgC,CAAC;AAC1F,OAAO,EAAE,iBAAiB,EAA+B,MAAM,mCAAmC,CAAC;AACnG,OAAO,EAAE,aAAa,EAA2B,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,WAAW,EAAyB,MAAM,6BAA6B,CAAC;AACjF,OAAO,EAAE,YAAY,EAA0B,MAAM,8BAA8B,CAAC;AAEpF,iBAAiB;AACjB,OAAO,EAAE,WAAW,EAA+C,MAAM,sBAAsB,CAAC;AAChG,OAAO,EAAE,UAAU,EAAwB,MAAM,qBAAqB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stigmer/ink",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Ink (React for terminals) components for rendering Stigmer agent sessions in the terminal",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@connectrpc/connect-node": "^2.1.1",
|
|
35
|
-
"@stigmer/react": "0.
|
|
35
|
+
"@stigmer/react": "1.0.1",
|
|
36
36
|
"ink-spinner": "^5.0.0",
|
|
37
37
|
"ink-text-input": "^6.0.0",
|
|
38
38
|
"marked": "^15.0.7",
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"@bufbuild/protobuf": "^2.0.0",
|
|
43
|
-
"@stigmer/protos": "0.
|
|
44
|
-
"@stigmer/sdk": "0.
|
|
43
|
+
"@stigmer/protos": "1.0.1",
|
|
44
|
+
"@stigmer/sdk": "1.0.1",
|
|
45
45
|
"ink": "^7.0.0",
|
|
46
46
|
"react": ">=19.0.0"
|
|
47
47
|
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { describe, it, expect } from "vitest";
|
|
3
|
+
import { render } from "ink-testing-library";
|
|
4
|
+
import { create } from "@bufbuild/protobuf";
|
|
5
|
+
import {
|
|
6
|
+
AgentExecutionSchema,
|
|
7
|
+
AgentExecutionStatusSchema,
|
|
8
|
+
type AgentExecution,
|
|
9
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
10
|
+
import {
|
|
11
|
+
ContextInfoSchema,
|
|
12
|
+
SummarizationEventSchema,
|
|
13
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/context_pb";
|
|
14
|
+
import { ContextGauge } from "../components/ContextGauge.js";
|
|
15
|
+
|
|
16
|
+
function makeExecution(overrides?: {
|
|
17
|
+
currentTokenCount?: number;
|
|
18
|
+
contextWindowLimit?: number;
|
|
19
|
+
utilizationPercent?: number;
|
|
20
|
+
}): AgentExecution {
|
|
21
|
+
const exec = create(AgentExecutionSchema);
|
|
22
|
+
const status = create(AgentExecutionStatusSchema);
|
|
23
|
+
const contextInfo = create(ContextInfoSchema);
|
|
24
|
+
|
|
25
|
+
contextInfo.currentTokenCount = overrides?.currentTokenCount ?? 10_000;
|
|
26
|
+
contextInfo.contextWindowLimit = overrides?.contextWindowLimit ?? 128_000;
|
|
27
|
+
contextInfo.summarizationTriggerThreshold = 115_200;
|
|
28
|
+
contextInfo.summarizationTargetTokens = 102_400;
|
|
29
|
+
contextInfo.summarizationEnabled = true;
|
|
30
|
+
contextInfo.utilizationPercent = overrides?.utilizationPercent ?? 5;
|
|
31
|
+
|
|
32
|
+
status.contextInfo = contextInfo;
|
|
33
|
+
exec.status = status;
|
|
34
|
+
return exec;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function addSummarizationEvent(
|
|
38
|
+
exec: AgentExecution,
|
|
39
|
+
overrides?: {
|
|
40
|
+
tokensBefore?: number;
|
|
41
|
+
tokensAfter?: number;
|
|
42
|
+
compressionRatio?: number;
|
|
43
|
+
durationMs?: number;
|
|
44
|
+
model?: string;
|
|
45
|
+
costUsd?: number;
|
|
46
|
+
},
|
|
47
|
+
): void {
|
|
48
|
+
const event = create(SummarizationEventSchema);
|
|
49
|
+
event.timestamp = "2026-05-17T10:30:00.000Z";
|
|
50
|
+
event.tokensBefore = overrides?.tokensBefore ?? 120_000;
|
|
51
|
+
event.tokensAfter = overrides?.tokensAfter ?? 82_000;
|
|
52
|
+
event.compressionRatio = overrides?.compressionRatio ?? 0.32;
|
|
53
|
+
event.durationMs = overrides?.durationMs ?? 2100;
|
|
54
|
+
event.summarizationModel = overrides?.model ?? "claude-haiku-4";
|
|
55
|
+
event.messagesBefore = 30;
|
|
56
|
+
event.messagesAfter = 10;
|
|
57
|
+
event.summarizationCostUsd = overrides?.costUsd ?? 0.02;
|
|
58
|
+
|
|
59
|
+
exec.status!.contextInfo!.summarizationEvents.push(event);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
describe("ContextGauge", () => {
|
|
63
|
+
it("renders nothing when execution is null", () => {
|
|
64
|
+
const { lastFrame } = render(<ContextGauge execution={null} />);
|
|
65
|
+
expect(lastFrame()).toBe("");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("renders nothing when contextInfo is absent", () => {
|
|
69
|
+
const exec = create(AgentExecutionSchema);
|
|
70
|
+
exec.status = create(AgentExecutionStatusSchema);
|
|
71
|
+
|
|
72
|
+
const { lastFrame } = render(<ContextGauge execution={exec} />);
|
|
73
|
+
expect(lastFrame()).toBe("");
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("renders healthy gauge with green bar and no health label", () => {
|
|
77
|
+
const exec = makeExecution({
|
|
78
|
+
currentTokenCount: 51_200,
|
|
79
|
+
contextWindowLimit: 128_000,
|
|
80
|
+
utilizationPercent: 40,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const { lastFrame } = render(<ContextGauge execution={exec} />);
|
|
84
|
+
const output = lastFrame() ?? "";
|
|
85
|
+
|
|
86
|
+
expect(output).toContain("Context");
|
|
87
|
+
expect(output).toContain("40%");
|
|
88
|
+
expect(output).toContain("51K / 128K tokens");
|
|
89
|
+
expect(output).not.toContain("Approaching limit");
|
|
90
|
+
expect(output).not.toContain("Near limit");
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("renders warning gauge with health label", () => {
|
|
94
|
+
const exec = makeExecution({
|
|
95
|
+
currentTokenCount: 96_000,
|
|
96
|
+
contextWindowLimit: 128_000,
|
|
97
|
+
utilizationPercent: 75,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const { lastFrame } = render(<ContextGauge execution={exec} />);
|
|
101
|
+
const output = lastFrame() ?? "";
|
|
102
|
+
|
|
103
|
+
expect(output).toContain("75%");
|
|
104
|
+
expect(output).toContain("96K / 128K tokens");
|
|
105
|
+
expect(output).toContain("Approaching limit");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("renders critical gauge with health label", () => {
|
|
109
|
+
const exec = makeExecution({
|
|
110
|
+
currentTokenCount: 118_000,
|
|
111
|
+
contextWindowLimit: 128_000,
|
|
112
|
+
utilizationPercent: 92,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const { lastFrame } = render(<ContextGauge execution={exec} />);
|
|
116
|
+
const output = lastFrame() ?? "";
|
|
117
|
+
|
|
118
|
+
expect(output).toContain("92%");
|
|
119
|
+
expect(output).toContain("118K / 128K tokens");
|
|
120
|
+
expect(output).toContain("Near limit");
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("shows correct token formatting for large counts", () => {
|
|
124
|
+
const exec = makeExecution({
|
|
125
|
+
currentTokenCount: 1_500_000,
|
|
126
|
+
contextWindowLimit: 2_000_000,
|
|
127
|
+
utilizationPercent: 75,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const { lastFrame } = render(<ContextGauge execution={exec} />);
|
|
131
|
+
const output = lastFrame() ?? "";
|
|
132
|
+
|
|
133
|
+
expect(output).toContain("1.5M / 2.0M tokens");
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("shows summarization count and latest event details", () => {
|
|
137
|
+
const exec = makeExecution({
|
|
138
|
+
currentTokenCount: 82_000,
|
|
139
|
+
contextWindowLimit: 128_000,
|
|
140
|
+
utilizationPercent: 64,
|
|
141
|
+
});
|
|
142
|
+
addSummarizationEvent(exec, {
|
|
143
|
+
tokensBefore: 120_000,
|
|
144
|
+
tokensAfter: 82_000,
|
|
145
|
+
compressionRatio: 0.32,
|
|
146
|
+
durationMs: 2100,
|
|
147
|
+
model: "claude-haiku-4",
|
|
148
|
+
costUsd: 0.02,
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
const { lastFrame } = render(<ContextGauge execution={exec} />);
|
|
152
|
+
const output = lastFrame() ?? "";
|
|
153
|
+
|
|
154
|
+
expect(output).toContain("1 compaction");
|
|
155
|
+
expect(output).toContain("120K → 82K tokens (32% reduction)");
|
|
156
|
+
expect(output).toContain("claude-haiku-4");
|
|
157
|
+
expect(output).toContain("2.1s");
|
|
158
|
+
expect(output).toContain("$0.02");
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it("shows plural compaction count for multiple events", () => {
|
|
162
|
+
const exec = makeExecution({
|
|
163
|
+
currentTokenCount: 60_000,
|
|
164
|
+
contextWindowLimit: 128_000,
|
|
165
|
+
utilizationPercent: 47,
|
|
166
|
+
});
|
|
167
|
+
addSummarizationEvent(exec, { tokensBefore: 120_000, tokensAfter: 90_000 });
|
|
168
|
+
addSummarizationEvent(exec, { tokensBefore: 110_000, tokensAfter: 70_000 });
|
|
169
|
+
addSummarizationEvent(exec, { tokensBefore: 100_000, tokensAfter: 60_000 });
|
|
170
|
+
|
|
171
|
+
const { lastFrame } = render(<ContextGauge execution={exec} />);
|
|
172
|
+
const output = lastFrame() ?? "";
|
|
173
|
+
|
|
174
|
+
expect(output).toContain("3 compactions");
|
|
175
|
+
});
|
|
176
|
+
});
|
package/src/app/SessionApp.tsx
CHANGED
|
@@ -17,6 +17,16 @@ export interface SessionAppProps {
|
|
|
17
17
|
readonly apiKey?: string;
|
|
18
18
|
/** Dynamic token provider for authentication. */
|
|
19
19
|
readonly getAccessToken?: TokenProvider;
|
|
20
|
+
/**
|
|
21
|
+
* Default interaction mode for follow-up executions.
|
|
22
|
+
*
|
|
23
|
+
* - `"agent"` (default): full tool access.
|
|
24
|
+
* - `"plan"`: read-only analysis, no file mutations.
|
|
25
|
+
*
|
|
26
|
+
* When set, all follow-up executions in this session use this mode
|
|
27
|
+
* unless overridden by the user.
|
|
28
|
+
*/
|
|
29
|
+
readonly mode?: "agent" | "plan";
|
|
20
30
|
}
|
|
21
31
|
|
|
22
32
|
/**
|
|
@@ -51,6 +61,7 @@ export function SessionApp({
|
|
|
51
61
|
baseUrl,
|
|
52
62
|
apiKey,
|
|
53
63
|
getAccessToken,
|
|
64
|
+
mode,
|
|
54
65
|
}: SessionAppProps) {
|
|
55
66
|
const clientConfig: NodeClientConfig = { baseUrl, apiKey, getAccessToken };
|
|
56
67
|
|
|
@@ -67,7 +78,7 @@ export function SessionApp({
|
|
|
67
78
|
Session {sessionId} · {org}
|
|
68
79
|
</Text>
|
|
69
80
|
</Box>
|
|
70
|
-
<SessionView sessionId={sessionId} org={org} />
|
|
81
|
+
<SessionView sessionId={sessionId} org={org} mode={mode} />
|
|
71
82
|
</Box>
|
|
72
83
|
</InkStigmerProvider>
|
|
73
84
|
);
|
package/src/app/SessionView.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useState } from "react";
|
|
1
|
+
import React, { useState, useEffect } from "react";
|
|
2
2
|
import { Box, Text, useInput } from "ink";
|
|
3
3
|
import Spinner from "ink-spinner";
|
|
4
4
|
import { useSessionConversation, resolvedSubject, PENDING_SUBJECT } from "@stigmer/react";
|
|
@@ -6,14 +6,29 @@ import { MessageThread } from "../components/MessageThread.js";
|
|
|
6
6
|
import { TodoList } from "../components/TodoList.js";
|
|
7
7
|
import { FollowUpInput } from "../components/FollowUpInput.js";
|
|
8
8
|
import { UsageWidget } from "../components/UsageWidget.js";
|
|
9
|
+
import { ContextGauge } from "../components/ContextGauge.js";
|
|
9
10
|
import { ExecutionProgress } from "../components/ExecutionProgress.js";
|
|
10
11
|
|
|
12
|
+
/** Interaction mode type used for follow-up executions. */
|
|
13
|
+
export type InteractionMode = "agent" | "plan";
|
|
14
|
+
|
|
11
15
|
/** Props for {@link SessionView}. */
|
|
12
16
|
export interface SessionViewProps {
|
|
13
17
|
/** Session ID to display and converse in. */
|
|
14
18
|
readonly sessionId: string;
|
|
15
19
|
/** Organization slug for creating follow-up executions. */
|
|
16
20
|
readonly org: string;
|
|
21
|
+
/**
|
|
22
|
+
* Initial interaction mode for follow-up executions.
|
|
23
|
+
*
|
|
24
|
+
* - `"agent"` (default): full tool access.
|
|
25
|
+
* - `"plan"`: read-only analysis, no file mutations.
|
|
26
|
+
*
|
|
27
|
+
* The user can toggle between modes with Ctrl+T during the session.
|
|
28
|
+
* This prop sets the initial value; subsequent toggles are managed
|
|
29
|
+
* as local state.
|
|
30
|
+
*/
|
|
31
|
+
readonly mode?: InteractionMode;
|
|
17
32
|
}
|
|
18
33
|
|
|
19
34
|
/**
|
|
@@ -39,14 +54,22 @@ export interface SessionViewProps {
|
|
|
39
54
|
* );
|
|
40
55
|
* ```
|
|
41
56
|
*/
|
|
42
|
-
export function SessionView({ sessionId, org }: SessionViewProps) {
|
|
57
|
+
export function SessionView({ sessionId, org, mode }: SessionViewProps) {
|
|
43
58
|
const conv = useSessionConversation(sessionId, org);
|
|
44
59
|
const [expandTools, setExpandTools] = useState(false);
|
|
60
|
+
const [activeMode, setActiveMode] = useState<InteractionMode>(mode ?? "agent");
|
|
61
|
+
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
if (mode) setActiveMode(mode);
|
|
64
|
+
}, [mode]);
|
|
45
65
|
|
|
46
66
|
useInput((input, key) => {
|
|
47
67
|
if (key.ctrl && input === "o") {
|
|
48
68
|
setExpandTools((e) => !e);
|
|
49
69
|
}
|
|
70
|
+
if (key.ctrl && input === "t") {
|
|
71
|
+
setActiveMode((m) => (m === "plan" ? "agent" : "plan"));
|
|
72
|
+
}
|
|
50
73
|
});
|
|
51
74
|
|
|
52
75
|
if (conv.isLoading) {
|
|
@@ -77,8 +100,6 @@ export function SessionView({ sessionId, org }: SessionViewProps) {
|
|
|
77
100
|
];
|
|
78
101
|
|
|
79
102
|
const activeTodos = conv.activeStreamExecution?.status?.todos;
|
|
80
|
-
const contextInfo = conv.activeStreamExecution?.status?.contextInfo;
|
|
81
|
-
const summarizationCount = contextInfo?.summarizationEvents?.length ?? 0;
|
|
82
103
|
|
|
83
104
|
const subject = resolvedSubject(conv.session?.spec?.subject);
|
|
84
105
|
|
|
@@ -122,14 +143,6 @@ export function SessionView({ sessionId, org }: SessionViewProps) {
|
|
|
122
143
|
expandToolCalls={expandTools}
|
|
123
144
|
/>
|
|
124
145
|
|
|
125
|
-
{summarizationCount > 0 && (
|
|
126
|
-
<Box paddingLeft={1} marginTop={1}>
|
|
127
|
-
<Text dimColor>
|
|
128
|
-
Context compacted ({summarizationCount} {summarizationCount === 1 ? "event" : "events"}, {Math.round(contextInfo!.utilizationPercent)}% utilization)
|
|
129
|
-
</Text>
|
|
130
|
-
</Box>
|
|
131
|
-
)}
|
|
132
|
-
|
|
133
146
|
{activeTodos && Object.keys(activeTodos).length > 0 && (
|
|
134
147
|
<Box marginTop={1} paddingLeft={1}>
|
|
135
148
|
<TodoList todos={activeTodos} />
|
|
@@ -154,10 +167,25 @@ export function SessionView({ sessionId, org }: SessionViewProps) {
|
|
|
154
167
|
|
|
155
168
|
<UsageWidget executions={allExecutions} />
|
|
156
169
|
|
|
170
|
+
<ContextGauge execution={conv.activeStreamExecution ?? null} />
|
|
171
|
+
|
|
172
|
+
{conv.canSendFollowUp && (
|
|
173
|
+
<Box paddingLeft={1}>
|
|
174
|
+
<Text color={activeMode === "plan" ? "yellow" : "cyan"} dimColor>
|
|
175
|
+
{activeMode === "plan"
|
|
176
|
+
? "Plan mode — read-only analysis, no file mutations"
|
|
177
|
+
: "Agent mode — full tool access"}
|
|
178
|
+
</Text>
|
|
179
|
+
</Box>
|
|
180
|
+
)}
|
|
181
|
+
|
|
157
182
|
<FollowUpInput
|
|
158
|
-
onSubmit={(message) =>
|
|
183
|
+
onSubmit={(message) =>
|
|
184
|
+
conv.sendFollowUp(message, { interactionMode: activeMode })
|
|
185
|
+
}
|
|
159
186
|
isSubmitting={conv.isSending}
|
|
160
187
|
disabled={!conv.canSendFollowUp}
|
|
188
|
+
mode={activeMode}
|
|
161
189
|
/>
|
|
162
190
|
</Box>
|
|
163
191
|
);
|
package/src/cli/stigmer-ink.tsx
CHANGED
|
@@ -10,6 +10,7 @@ interface CliConfig {
|
|
|
10
10
|
org: string;
|
|
11
11
|
baseUrl: string;
|
|
12
12
|
apiKey?: string;
|
|
13
|
+
mode?: "agent" | "plan";
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
function parseArgs(argv: string[]): CliConfig | null {
|
|
@@ -34,6 +35,16 @@ function parseArgs(argv: string[]): CliConfig | null {
|
|
|
34
35
|
case "-k":
|
|
35
36
|
config.apiKey = args[++i];
|
|
36
37
|
break;
|
|
38
|
+
case "--mode":
|
|
39
|
+
case "-M": {
|
|
40
|
+
const value = args[++i];
|
|
41
|
+
if (value !== "agent" && value !== "plan") {
|
|
42
|
+
console.error(`Invalid --mode value "${value}": must be "agent" or "plan"`);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
config.mode = value;
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
37
48
|
case "--help":
|
|
38
49
|
case "-h":
|
|
39
50
|
printUsage();
|
|
@@ -93,6 +104,7 @@ Options:
|
|
|
93
104
|
-o, --org <slug> Organization slug (required)
|
|
94
105
|
-u, --base-url <url> Stigmer API URL (or STIGMER_BASE_URL env)
|
|
95
106
|
-k, --api-key <key> API key (or STIGMER_API_KEY env)
|
|
107
|
+
-M, --mode <mode> Interaction mode for follow-ups: "agent" or "plan"
|
|
96
108
|
-h, --help Show this help message
|
|
97
109
|
|
|
98
110
|
Environment:
|
|
@@ -127,6 +139,7 @@ async function main() {
|
|
|
127
139
|
org={config.org}
|
|
128
140
|
baseUrl={config.baseUrl}
|
|
129
141
|
apiKey={config.apiKey}
|
|
142
|
+
mode={config.mode}
|
|
130
143
|
/>,
|
|
131
144
|
{ stdin, exitOnCtrlC: isTTY, debug: !isTTY },
|
|
132
145
|
);
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import type { AgentExecution } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
4
|
+
import {
|
|
5
|
+
useContextWindow,
|
|
6
|
+
type ContextHealth,
|
|
7
|
+
type SummarizationEventView,
|
|
8
|
+
} from "@stigmer/react";
|
|
9
|
+
|
|
10
|
+
/** Props for {@link ContextGauge}. */
|
|
11
|
+
export interface ContextGaugeProps {
|
|
12
|
+
/**
|
|
13
|
+
* The execution snapshot from the active stream, or `null`.
|
|
14
|
+
* The gauge extracts `context_info` from the execution status.
|
|
15
|
+
*/
|
|
16
|
+
readonly execution: AgentExecution | null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const BAR_WIDTH = 20;
|
|
20
|
+
|
|
21
|
+
const HEALTH_COLORS: Record<ContextHealth, string> = {
|
|
22
|
+
healthy: "green",
|
|
23
|
+
warning: "yellow",
|
|
24
|
+
critical: "red",
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const HEALTH_LABELS: Partial<Record<ContextHealth, string>> = {
|
|
28
|
+
warning: "Approaching limit",
|
|
29
|
+
critical: "Near limit",
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
function formatCompactTokens(count: number): string {
|
|
33
|
+
if (count >= 1_000_000) return `${(count / 1_000_000).toFixed(1)}M`;
|
|
34
|
+
if (count >= 1_000) return `${Math.round(count / 1_000)}K`;
|
|
35
|
+
return String(count);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function formatDuration(ms: number): string {
|
|
39
|
+
if (ms < 1000) return `${ms}ms`;
|
|
40
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Terminal-native context window utilization gauge.
|
|
45
|
+
*
|
|
46
|
+
* Renders an ASCII progress bar with health-based coloring, token counts,
|
|
47
|
+
* and summarization event details. Uses the headless
|
|
48
|
+
* {@link useContextWindow} hook from `@stigmer/react`.
|
|
49
|
+
*
|
|
50
|
+
* Returns `null` when `ContextInfo` is absent (e.g., Cursor harness
|
|
51
|
+
* where context is managed externally).
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```tsx
|
|
55
|
+
* <ContextGauge execution={activeStreamExecution} />
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export function ContextGauge({ execution }: ContextGaugeProps) {
|
|
59
|
+
const ctx = useContextWindow(execution);
|
|
60
|
+
|
|
61
|
+
if (!ctx.hasContextInfo) return null;
|
|
62
|
+
|
|
63
|
+
const filled = Math.round((Math.min(ctx.utilizationPercent, 100) / 100) * BAR_WIDTH);
|
|
64
|
+
const empty = BAR_WIDTH - filled;
|
|
65
|
+
const bar = "█".repeat(filled) + "░".repeat(empty);
|
|
66
|
+
|
|
67
|
+
const color = HEALTH_COLORS[ctx.health];
|
|
68
|
+
const healthLabel = HEALTH_LABELS[ctx.health];
|
|
69
|
+
const eventCount = ctx.summarizationEvents.length;
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<Box flexDirection="column" paddingLeft={1}>
|
|
73
|
+
<Box gap={1}>
|
|
74
|
+
<Text dimColor>Context</Text>
|
|
75
|
+
<Text color={color}>{bar}</Text>
|
|
76
|
+
<Text color={color}>{Math.round(ctx.utilizationPercent)}%</Text>
|
|
77
|
+
<Text dimColor>·</Text>
|
|
78
|
+
<Text dimColor>
|
|
79
|
+
{formatCompactTokens(ctx.currentTokenCount)} /{" "}
|
|
80
|
+
{formatCompactTokens(ctx.contextWindowLimit)} tokens
|
|
81
|
+
</Text>
|
|
82
|
+
{healthLabel && (
|
|
83
|
+
<>
|
|
84
|
+
<Text dimColor>·</Text>
|
|
85
|
+
<Text color={color}>{healthLabel}</Text>
|
|
86
|
+
</>
|
|
87
|
+
)}
|
|
88
|
+
{eventCount > 0 && (
|
|
89
|
+
<>
|
|
90
|
+
<Text dimColor>·</Text>
|
|
91
|
+
<Text dimColor>
|
|
92
|
+
{eventCount} {eventCount === 1 ? "compaction" : "compactions"}
|
|
93
|
+
</Text>
|
|
94
|
+
</>
|
|
95
|
+
)}
|
|
96
|
+
</Box>
|
|
97
|
+
{eventCount > 0 && (
|
|
98
|
+
<LatestEvent event={ctx.summarizationEvents[eventCount - 1]} />
|
|
99
|
+
)}
|
|
100
|
+
</Box>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function LatestEvent({ event }: { readonly event: SummarizationEventView }) {
|
|
105
|
+
const reduction = Math.round(event.compressionRatio * 100);
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<Box paddingLeft={2}>
|
|
109
|
+
<Text dimColor>
|
|
110
|
+
{formatCompactTokens(event.tokensBefore)} →{" "}
|
|
111
|
+
{formatCompactTokens(event.tokensAfter)} tokens ({reduction}% reduction)
|
|
112
|
+
{event.model ? ` · ${event.model}` : ""}
|
|
113
|
+
{event.durationMs > 0 ? ` · ${formatDuration(event.durationMs)}` : ""}
|
|
114
|
+
{event.costUsd > 0
|
|
115
|
+
? ` · $${event.costUsd < 0.01 ? event.costUsd.toFixed(4) : event.costUsd.toFixed(2)}`
|
|
116
|
+
: ""}
|
|
117
|
+
</Text>
|
|
118
|
+
</Box>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
@@ -12,6 +12,8 @@ export interface FollowUpInputProps {
|
|
|
12
12
|
readonly disabled?: boolean;
|
|
13
13
|
/** Placeholder text. Default: "Reply..." */
|
|
14
14
|
readonly placeholder?: string;
|
|
15
|
+
/** Current interaction mode. Shown in the shortcut hint line. */
|
|
16
|
+
readonly mode?: "agent" | "plan";
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
/**
|
|
@@ -25,6 +27,7 @@ export function FollowUpInput({
|
|
|
25
27
|
isSubmitting = false,
|
|
26
28
|
disabled = false,
|
|
27
29
|
placeholder = "Reply...",
|
|
30
|
+
mode,
|
|
28
31
|
}: FollowUpInputProps) {
|
|
29
32
|
const [value, setValue] = useState("");
|
|
30
33
|
const { isRawModeSupported } = useStdin();
|
|
@@ -62,7 +65,7 @@ export function FollowUpInput({
|
|
|
62
65
|
</Box>
|
|
63
66
|
<Box paddingLeft={2}>
|
|
64
67
|
<Text dimColor>
|
|
65
|
-
Enter to send · Ctrl+C
|
|
68
|
+
Enter to send · Ctrl+T {mode === "plan" ? "agent" : "plan"} mode · Ctrl+C exit
|
|
66
69
|
</Text>
|
|
67
70
|
</Box>
|
|
68
71
|
</Box>
|
package/src/index.ts
CHANGED
|
@@ -22,7 +22,8 @@ export { ApprovalPrompt, type ApprovalPromptProps } from "./components/ApprovalP
|
|
|
22
22
|
export { ExecutionProgress, type ExecutionProgressProps } from "./components/ExecutionProgress.js";
|
|
23
23
|
export { FollowUpInput, type FollowUpInputProps } from "./components/FollowUpInput.js";
|
|
24
24
|
export { UsageWidget, type UsageWidgetProps } from "./components/UsageWidget.js";
|
|
25
|
+
export { ContextGauge, type ContextGaugeProps } from "./components/ContextGauge.js";
|
|
25
26
|
|
|
26
27
|
// Composed views
|
|
27
|
-
export { SessionView, type SessionViewProps } from "./app/SessionView.js";
|
|
28
|
+
export { SessionView, type SessionViewProps, type InteractionMode } from "./app/SessionView.js";
|
|
28
29
|
export { SessionApp, type SessionAppProps } from "./app/SessionApp.js";
|