@openlucaskaka/kagent 0.1.7 → 0.1.9

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/npm/lib/App.js CHANGED
@@ -144,6 +144,7 @@ function KagentInkApp({ React, Ink, runtimeSessionFactory = runtime_client_1.cre
144
144
  const pagingLayout = (0, ui_components_1.createTerminalLayout)(terminalSize.columns, terminalSize.rows, {
145
145
  approval: approval !== null,
146
146
  commandMenu: status === "idle" && commandMenu ? commandMenu : false,
147
+ introVisible: transcript.entries.length === 0,
147
148
  prompt: editor.value,
148
149
  promptCursor: editor.cursor,
149
150
  });
@@ -255,6 +256,7 @@ function KagentInkApp({ React, Ink, runtimeSessionFactory = runtime_client_1.cre
255
256
  const promptLayout = (0, ui_components_1.createTerminalLayout)(terminalSize.columns, terminalSize.rows, {
256
257
  approval: approval !== null,
257
258
  commandMenu: false,
259
+ introVisible: transcript.entries.length === 0,
258
260
  prompt: editor.value,
259
261
  promptCursor: editor.cursor,
260
262
  });
@@ -448,6 +450,7 @@ function KagentInkApp({ React, Ink, runtimeSessionFactory = runtime_client_1.cre
448
450
  ? { ...approval, showDetails: showApprovalDetails }
449
451
  : false,
450
452
  commandMenu: status === "idle" && commandMenu ? commandMenu : false,
453
+ introVisible: transcript.entries.length === 0,
451
454
  prompt: editor.value,
452
455
  promptCursor: editor.cursor,
453
456
  });
@@ -459,15 +462,25 @@ function KagentInkApp({ React, Ink, runtimeSessionFactory = runtime_client_1.cre
459
462
  rows: layout.rows,
460
463
  reservedRows: layout.reservedRows + (transcriptOffset > 0 ? 1 : 0),
461
464
  }, transcriptOffset);
462
- return React.createElement(Box, { flexDirection: "column", paddingX: layout.horizontalPadding }, React.createElement(ui_components_1.Header, {
463
- React,
464
- Box,
465
- Text,
466
- compact: layout.compact,
467
- provider,
468
- setup: false,
469
- workspace: process.cwd(),
470
- }), React.createElement(ui_components_1.TranscriptPosition, {
465
+ const promptDisabled = status === "cancelling" || status === "approval";
466
+ const promptCursorControl = (0, ui_components_1.createPromptTerminalCursorControl)({
467
+ input: editor.value,
468
+ cursor: editor.cursor,
469
+ columns: layout.promptColumns,
470
+ maxRows: layout.promptRowLimit,
471
+ horizontalPadding: layout.horizontalPadding,
472
+ });
473
+ return React.createElement(Box, { flexDirection: "column", paddingX: layout.horizontalPadding }, transcript.entries.length === 0
474
+ ? React.createElement(ui_components_1.Header, {
475
+ React,
476
+ Box,
477
+ Text,
478
+ compact: layout.compact,
479
+ provider,
480
+ setup: false,
481
+ workspace: process.cwd(),
482
+ })
483
+ : null, React.createElement(ui_components_1.TranscriptPosition, {
471
484
  React,
472
485
  Text,
473
486
  newerCount: transcriptOffset,
@@ -499,16 +512,31 @@ function KagentInkApp({ React, Ink, runtimeSessionFactory = runtime_client_1.cre
499
512
  })
500
513
  : null, status === "starting"
501
514
  ? null
502
- : React.createElement(ui_components_1.PromptLine, {
515
+ : React.createElement(React.Fragment, null, React.createElement(ui_components_1.PromptLine, {
503
516
  React,
504
517
  Box,
505
518
  Text,
506
519
  cursor: editor.cursor,
507
520
  input: editor.value,
508
- disabled: status === "cancelling" || status === "approval",
521
+ disabled: promptDisabled,
509
522
  columns: layout.promptColumns,
510
523
  maxRows: layout.promptRowLimit,
511
- }));
524
+ }), React.createElement(TerminalCursorSync, {
525
+ React,
526
+ control: promptDisabled ? null : promptCursorControl,
527
+ })));
528
+ }
529
+ function TerminalCursorSync({ React, control, }) {
530
+ React.useLayoutEffect(() => {
531
+ if (!control || !process.stdout.isTTY) {
532
+ return undefined;
533
+ }
534
+ process.stdout.write(control.position);
535
+ return () => {
536
+ process.stdout.write(control.restore);
537
+ };
538
+ });
539
+ return null;
512
540
  }
513
541
  function currentTerminalSize() {
514
542
  return {
@@ -144,6 +144,7 @@ function selectTranscriptViewport(entries, viewport, offset = 0) {
144
144
  usedRows += rows;
145
145
  start = index;
146
146
  }
147
+ start = avoidOrphanedLeadingAssistant(entries, start, end, usedRows, availableRows, viewport.columns);
147
148
  return entries.slice(start, end);
148
149
  }
149
150
  function moveTranscriptViewport(entries, viewport, offset, direction) {
@@ -216,6 +217,22 @@ function retain(state) {
216
217
  : null;
217
218
  return { ...state, entries, activeAssistantId };
218
219
  }
220
+ function avoidOrphanedLeadingAssistant(entries, start, end, usedRows, availableRows, columns) {
221
+ let nextStart = start;
222
+ let nextUsedRows = usedRows;
223
+ while (nextStart > 0 &&
224
+ nextStart < end - 1 &&
225
+ entries[nextStart].role === "assistant" &&
226
+ entries[nextStart - 1].role === "user") {
227
+ const userRows = estimateEntryRows(entries[nextStart - 1], columns);
228
+ if (nextUsedRows + userRows <= availableRows) {
229
+ return nextStart - 1;
230
+ }
231
+ nextUsedRows -= estimateEntryRows(entries[nextStart], columns);
232
+ nextStart += 1;
233
+ }
234
+ return nextStart;
235
+ }
219
236
  function estimateEntryRows(entry, columns) {
220
237
  const contentColumns = Math.max(4, columns - 4);
221
238
  const titleRows = entry.title ? (0, terminal_width_1.estimateTextRows)(entry.title, contentColumns) : 0;
@@ -4,6 +4,7 @@ exports.TERMINAL_SPINNER_FRAMES = void 0;
4
4
  exports.createTerminalLayout = createTerminalLayout;
5
5
  exports.NarrowTerminal = NarrowTerminal;
6
6
  exports.createPromptViewport = createPromptViewport;
7
+ exports.createPromptTerminalCursorControl = createPromptTerminalCursorControl;
7
8
  exports.Header = Header;
8
9
  exports.ProviderSetupPanel = ProviderSetupPanel;
9
10
  exports.MessageList = MessageList;
@@ -24,7 +25,7 @@ function createTerminalLayout(columns, rows, overlays) {
24
25
  const compact = safeColumns < 56;
25
26
  const horizontalPadding = compact ? 0 : 1;
26
27
  const defaultCommandLimit = compact ? 4 : 6;
27
- const headerRows = compact ? 2 : 3;
28
+ const headerRows = overlays.introVisible === false ? 0 : compact ? 2 : 3;
28
29
  const promptChromeRows = 1;
29
30
  const fixedBaseRows = headerRows + promptChromeRows;
30
31
  const approvalColumns = Math.max(4, safeColumns - horizontalPadding * 2 - (compact ? 0 : 2));
@@ -115,6 +116,18 @@ function createPromptViewport(input, cursor, columns, maxRows) {
115
116
  }
116
117
  return promptViewportParts(characters, safeCursor, start, end, preserveActiveNewline);
117
118
  }
119
+ function createPromptTerminalCursorControl({ input, cursor, columns, maxRows, horizontalPadding, }) {
120
+ const viewport = createPromptViewport(input, cursor, columns, maxRows);
121
+ const safeColumns = Math.max(4, columns);
122
+ const cursorPosition = textEndPosition(viewport.before, safeColumns);
123
+ const promptRows = (0, terminal_width_1.estimateTextRows)(viewport.rendered, safeColumns);
124
+ const up = Math.max(1, promptRows - cursorPosition.row);
125
+ const right = Math.max(0, horizontalPadding + 2 + cursorPosition.column);
126
+ return {
127
+ position: `${showTerminalCursor()}${moveCursorUp(up)}${moveCursorRight(right)}`,
128
+ restore: `\r${moveCursorDown(up)}`,
129
+ };
130
+ }
118
131
  function promptViewportRows(characters, cursor, start, end, columns, preserveActiveNewline) {
119
132
  return (0, terminal_width_1.estimateTextRows)(promptViewportParts(characters, cursor, start, end, preserveActiveNewline).rendered, columns);
120
133
  }
@@ -136,6 +149,38 @@ function promptViewportParts(characters, cursor, start, end, preserveActiveNewli
136
149
  suffixClipped,
137
150
  };
138
151
  }
152
+ function textEndPosition(text, columns) {
153
+ let row = 0;
154
+ let column = 0;
155
+ for (const grapheme of (0, editor_1.splitGraphemes)(text)) {
156
+ if (grapheme === "\n") {
157
+ row += 1;
158
+ column = 0;
159
+ continue;
160
+ }
161
+ const width = Math.max(0, (0, terminal_text_1.terminalGraphemeWidth)(grapheme));
162
+ if (column + width >= columns) {
163
+ row += 1;
164
+ column = 0;
165
+ }
166
+ else {
167
+ column += width;
168
+ }
169
+ }
170
+ return { row, column };
171
+ }
172
+ function showTerminalCursor() {
173
+ return "\u001b[?25h";
174
+ }
175
+ function moveCursorUp(rows) {
176
+ return rows > 0 ? `\u001b[${rows}A` : "";
177
+ }
178
+ function moveCursorDown(rows) {
179
+ return rows > 0 ? `\u001b[${rows}B` : "";
180
+ }
181
+ function moveCursorRight(columns) {
182
+ return columns > 0 ? `\u001b[${columns}C` : "";
183
+ }
139
184
  function Header({ React, Box, Text, compact, provider, setup, workspace, }) {
140
185
  const providerLabel = provider?.configured
141
186
  ? `${(0, terminal_text_1.terminalSafeText)(provider.display_name)}${provider.model ? ` · ${(0, terminal_text_1.terminalSafeText)(provider.model)}` : ""}`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openlucaskaka/kagent",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "Codex-style LangGraph agent runtime for internal non-coding workflows.",
5
5
  "license": "UNLICENSED",
6
6
  "bin": {
package/pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "kagent"
7
- version = "0.1.7"
7
+ version = "0.1.9"
8
8
  description = "A bounded kagent built with LangGraph."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -22,7 +22,7 @@ def _source_tree_version() -> str:
22
22
  try:
23
23
  __version__ = _source_tree_version() or version("kagent")
24
24
  except PackageNotFoundError:
25
- __version__ = "0.1.7"
25
+ __version__ = "0.1.9"
26
26
 
27
27
  __all__ = [
28
28
  "AgentConfig",
@@ -128,6 +128,10 @@ _EMBEDDING_RETRY_BACKOFF_ENV_VAR = "KAGENT_EMBEDDING_RETRY_BACKOFF_SECONDS"
128
128
  _EXTERNAL_BACKEND_TIMEOUT_ENV_VAR = "KAGENT_EXTERNAL_BACKEND_TIMEOUT_SECONDS"
129
129
  _APP_NAME_MAX_LENGTH = 120
130
130
  _APP_NAME_ALLOWED_PATTERN = re.compile(r"^[\w .+()#&-]+$", re.UNICODE)
131
+ _APP_NAME_ALIASES = {
132
+ "飞书": "Feishu",
133
+ "feishu": "Feishu",
134
+ }
131
135
  _OPEN_COMMAND_TIMEOUT_SECONDS = 10.0
132
136
 
133
137
 
@@ -3005,24 +3009,47 @@ def _open_app(input_payload: Dict[str, Any]) -> Dict[str, Any]:
3005
3009
  if not isinstance(application, str) or not application.strip():
3006
3010
  raise ValueError("application must be a non-empty string")
3007
3011
  normalized_application = " ".join(application.strip().split())
3012
+ normalized_application = _APP_NAME_ALIASES.get(
3013
+ normalized_application.casefold(),
3014
+ normalized_application,
3015
+ )
3008
3016
  _validate_open_app_name(normalized_application)
3009
- try:
3010
- subprocess.run(
3011
- ["open", "-a", normalized_application],
3012
- check=True,
3013
- capture_output=True,
3014
- text=True,
3015
- timeout=_OPEN_COMMAND_TIMEOUT_SECONDS,
3016
- )
3017
- except subprocess.TimeoutExpired as exc:
3018
- raise TimeoutError("open app command timed out") from exc
3019
- except (OSError, subprocess.CalledProcessError) as exc:
3020
- raise ValueError("open app failed") from exc
3021
- return {
3022
- "application": normalized_application,
3023
- "opened": True,
3024
- "command": "open -a",
3025
- }
3017
+ attempts = [
3018
+ (
3019
+ ["osascript", "-e", _app_frontmost_script(normalized_application)],
3020
+ "osascript frontmost",
3021
+ ),
3022
+ (["open", "-a", normalized_application], "open -a"),
3023
+ ]
3024
+ last_error: BaseException | None = None
3025
+ for command_args, command_label in attempts:
3026
+ try:
3027
+ subprocess.run(
3028
+ command_args,
3029
+ check=True,
3030
+ capture_output=True,
3031
+ text=True,
3032
+ timeout=_OPEN_COMMAND_TIMEOUT_SECONDS,
3033
+ )
3034
+ return {
3035
+ "application": normalized_application,
3036
+ "opened": True,
3037
+ "command": command_label,
3038
+ }
3039
+ except subprocess.TimeoutExpired as exc:
3040
+ raise TimeoutError("open app command timed out") from exc
3041
+ except (OSError, subprocess.CalledProcessError) as exc:
3042
+ last_error = exc
3043
+ continue
3044
+ raise ValueError("open app failed") from last_error
3045
+
3046
+
3047
+ def _app_frontmost_script(application: str) -> str:
3048
+ escaped = application.replace("\\", "\\\\").replace('"', '\\"')
3049
+ return (
3050
+ 'tell application "System Events" to set frontmost of '
3051
+ f'first application process whose name is "{escaped}" to true'
3052
+ )
3026
3053
 
3027
3054
 
3028
3055
  def _validate_open_app_name(application: str) -> None:
@@ -42,7 +42,7 @@ def service_openapi() -> Dict[str, Any]:
42
42
  "openapi": "3.1.0",
43
43
  "info": {
44
44
  "title": "kagent API",
45
- "version": "0.1.7",
45
+ "version": "0.1.9",
46
46
  },
47
47
  "components": {
48
48
  "securitySchemes": {