glaip-sdk 0.6.12__py3-none-any.whl → 0.6.14__py3-none-any.whl
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.
- glaip_sdk/__init__.py +42 -5
- {glaip_sdk-0.6.12.dist-info → glaip_sdk-0.6.14.dist-info}/METADATA +31 -37
- glaip_sdk-0.6.14.dist-info/RECORD +12 -0
- {glaip_sdk-0.6.12.dist-info → glaip_sdk-0.6.14.dist-info}/WHEEL +2 -1
- glaip_sdk-0.6.14.dist-info/entry_points.txt +2 -0
- glaip_sdk-0.6.14.dist-info/top_level.txt +1 -0
- glaip_sdk/agents/__init__.py +0 -27
- glaip_sdk/agents/base.py +0 -1191
- glaip_sdk/cli/__init__.py +0 -9
- glaip_sdk/cli/account_store.py +0 -540
- glaip_sdk/cli/agent_config.py +0 -78
- glaip_sdk/cli/auth.py +0 -699
- glaip_sdk/cli/commands/__init__.py +0 -5
- glaip_sdk/cli/commands/accounts.py +0 -746
- glaip_sdk/cli/commands/agents.py +0 -1509
- glaip_sdk/cli/commands/common_config.py +0 -101
- glaip_sdk/cli/commands/configure.py +0 -896
- glaip_sdk/cli/commands/mcps.py +0 -1356
- glaip_sdk/cli/commands/models.py +0 -69
- glaip_sdk/cli/commands/tools.py +0 -576
- glaip_sdk/cli/commands/transcripts.py +0 -755
- glaip_sdk/cli/commands/update.py +0 -61
- glaip_sdk/cli/config.py +0 -95
- glaip_sdk/cli/constants.py +0 -38
- glaip_sdk/cli/context.py +0 -150
- glaip_sdk/cli/core/__init__.py +0 -79
- glaip_sdk/cli/core/context.py +0 -124
- glaip_sdk/cli/core/output.py +0 -846
- glaip_sdk/cli/core/prompting.py +0 -649
- glaip_sdk/cli/core/rendering.py +0 -187
- glaip_sdk/cli/display.py +0 -355
- glaip_sdk/cli/hints.py +0 -57
- glaip_sdk/cli/io.py +0 -112
- glaip_sdk/cli/main.py +0 -604
- glaip_sdk/cli/masking.py +0 -136
- glaip_sdk/cli/mcp_validators.py +0 -287
- glaip_sdk/cli/pager.py +0 -266
- glaip_sdk/cli/parsers/__init__.py +0 -7
- glaip_sdk/cli/parsers/json_input.py +0 -177
- glaip_sdk/cli/resolution.py +0 -67
- glaip_sdk/cli/rich_helpers.py +0 -27
- glaip_sdk/cli/slash/__init__.py +0 -15
- glaip_sdk/cli/slash/accounts_controller.py +0 -578
- glaip_sdk/cli/slash/accounts_shared.py +0 -75
- glaip_sdk/cli/slash/agent_session.py +0 -285
- glaip_sdk/cli/slash/prompt.py +0 -256
- glaip_sdk/cli/slash/remote_runs_controller.py +0 -566
- glaip_sdk/cli/slash/session.py +0 -1708
- glaip_sdk/cli/slash/tui/__init__.py +0 -9
- glaip_sdk/cli/slash/tui/accounts_app.py +0 -876
- glaip_sdk/cli/slash/tui/background_tasks.py +0 -72
- glaip_sdk/cli/slash/tui/loading.py +0 -58
- glaip_sdk/cli/slash/tui/remote_runs_app.py +0 -628
- glaip_sdk/cli/transcript/__init__.py +0 -31
- glaip_sdk/cli/transcript/cache.py +0 -536
- glaip_sdk/cli/transcript/capture.py +0 -329
- glaip_sdk/cli/transcript/export.py +0 -38
- glaip_sdk/cli/transcript/history.py +0 -815
- glaip_sdk/cli/transcript/launcher.py +0 -77
- glaip_sdk/cli/transcript/viewer.py +0 -374
- glaip_sdk/cli/update_notifier.py +0 -290
- glaip_sdk/cli/utils.py +0 -263
- glaip_sdk/cli/validators.py +0 -238
- glaip_sdk/client/__init__.py +0 -11
- glaip_sdk/client/_agent_payloads.py +0 -520
- glaip_sdk/client/agent_runs.py +0 -147
- glaip_sdk/client/agents.py +0 -1335
- glaip_sdk/client/base.py +0 -502
- glaip_sdk/client/main.py +0 -249
- glaip_sdk/client/mcps.py +0 -370
- glaip_sdk/client/run_rendering.py +0 -700
- glaip_sdk/client/shared.py +0 -21
- glaip_sdk/client/tools.py +0 -661
- glaip_sdk/client/validators.py +0 -198
- glaip_sdk/config/constants.py +0 -52
- glaip_sdk/mcps/__init__.py +0 -21
- glaip_sdk/mcps/base.py +0 -345
- glaip_sdk/models/__init__.py +0 -90
- glaip_sdk/models/agent.py +0 -47
- glaip_sdk/models/agent_runs.py +0 -116
- glaip_sdk/models/common.py +0 -42
- glaip_sdk/models/mcp.py +0 -33
- glaip_sdk/models/tool.py +0 -33
- glaip_sdk/payload_schemas/__init__.py +0 -7
- glaip_sdk/payload_schemas/agent.py +0 -85
- glaip_sdk/registry/__init__.py +0 -55
- glaip_sdk/registry/agent.py +0 -164
- glaip_sdk/registry/base.py +0 -139
- glaip_sdk/registry/mcp.py +0 -253
- glaip_sdk/registry/tool.py +0 -232
- glaip_sdk/runner/__init__.py +0 -59
- glaip_sdk/runner/base.py +0 -84
- glaip_sdk/runner/deps.py +0 -115
- glaip_sdk/runner/langgraph.py +0 -782
- glaip_sdk/runner/mcp_adapter/__init__.py +0 -13
- glaip_sdk/runner/mcp_adapter/base_mcp_adapter.py +0 -43
- glaip_sdk/runner/mcp_adapter/langchain_mcp_adapter.py +0 -257
- glaip_sdk/runner/mcp_adapter/mcp_config_builder.py +0 -95
- glaip_sdk/runner/tool_adapter/__init__.py +0 -18
- glaip_sdk/runner/tool_adapter/base_tool_adapter.py +0 -44
- glaip_sdk/runner/tool_adapter/langchain_tool_adapter.py +0 -219
- glaip_sdk/tools/__init__.py +0 -22
- glaip_sdk/tools/base.py +0 -435
- glaip_sdk/utils/__init__.py +0 -86
- glaip_sdk/utils/a2a/__init__.py +0 -34
- glaip_sdk/utils/a2a/event_processor.py +0 -188
- glaip_sdk/utils/agent_config.py +0 -194
- glaip_sdk/utils/bundler.py +0 -267
- glaip_sdk/utils/client.py +0 -111
- glaip_sdk/utils/client_utils.py +0 -486
- glaip_sdk/utils/datetime_helpers.py +0 -58
- glaip_sdk/utils/discovery.py +0 -78
- glaip_sdk/utils/display.py +0 -135
- glaip_sdk/utils/export.py +0 -143
- glaip_sdk/utils/general.py +0 -61
- glaip_sdk/utils/import_export.py +0 -168
- glaip_sdk/utils/import_resolver.py +0 -492
- glaip_sdk/utils/instructions.py +0 -101
- glaip_sdk/utils/rendering/__init__.py +0 -115
- glaip_sdk/utils/rendering/formatting.py +0 -264
- glaip_sdk/utils/rendering/layout/__init__.py +0 -64
- glaip_sdk/utils/rendering/layout/panels.py +0 -156
- glaip_sdk/utils/rendering/layout/progress.py +0 -202
- glaip_sdk/utils/rendering/layout/summary.py +0 -74
- glaip_sdk/utils/rendering/layout/transcript.py +0 -606
- glaip_sdk/utils/rendering/models.py +0 -85
- glaip_sdk/utils/rendering/renderer/__init__.py +0 -55
- glaip_sdk/utils/rendering/renderer/base.py +0 -1024
- glaip_sdk/utils/rendering/renderer/config.py +0 -27
- glaip_sdk/utils/rendering/renderer/console.py +0 -55
- glaip_sdk/utils/rendering/renderer/debug.py +0 -178
- glaip_sdk/utils/rendering/renderer/factory.py +0 -138
- glaip_sdk/utils/rendering/renderer/stream.py +0 -202
- glaip_sdk/utils/rendering/renderer/summary_window.py +0 -79
- glaip_sdk/utils/rendering/renderer/thinking.py +0 -273
- glaip_sdk/utils/rendering/renderer/toggle.py +0 -182
- glaip_sdk/utils/rendering/renderer/tool_panels.py +0 -442
- glaip_sdk/utils/rendering/renderer/transcript_mode.py +0 -162
- glaip_sdk/utils/rendering/state.py +0 -204
- glaip_sdk/utils/rendering/step_tree_state.py +0 -100
- glaip_sdk/utils/rendering/steps/__init__.py +0 -34
- glaip_sdk/utils/rendering/steps/event_processor.py +0 -778
- glaip_sdk/utils/rendering/steps/format.py +0 -176
- glaip_sdk/utils/rendering/steps/manager.py +0 -387
- glaip_sdk/utils/rendering/timing.py +0 -36
- glaip_sdk/utils/rendering/viewer/__init__.py +0 -21
- glaip_sdk/utils/rendering/viewer/presenter.py +0 -184
- glaip_sdk/utils/resource_refs.py +0 -195
- glaip_sdk/utils/run_renderer.py +0 -41
- glaip_sdk/utils/runtime_config.py +0 -425
- glaip_sdk/utils/serialization.py +0 -424
- glaip_sdk/utils/sync.py +0 -142
- glaip_sdk/utils/tool_detection.py +0 -33
- glaip_sdk/utils/validation.py +0 -264
- glaip_sdk-0.6.12.dist-info/RECORD +0 -159
- glaip_sdk-0.6.12.dist-info/entry_points.txt +0 -3
|
@@ -1,285 +0,0 @@
|
|
|
1
|
-
"""Agent-specific interaction loop for the command palette.
|
|
2
|
-
|
|
3
|
-
Authors:
|
|
4
|
-
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
from contextlib import contextmanager
|
|
10
|
-
from typing import TYPE_CHECKING, Any
|
|
11
|
-
|
|
12
|
-
import click
|
|
13
|
-
|
|
14
|
-
from glaip_sdk.branding import ERROR_STYLE, HINT_PREFIX_STYLE
|
|
15
|
-
from glaip_sdk.cli.commands.agents import get as agents_get_command
|
|
16
|
-
from glaip_sdk.cli.commands.agents import run as agents_run_command
|
|
17
|
-
from glaip_sdk.cli.constants import DEFAULT_AGENT_INSTRUCTION_PREVIEW_LIMIT
|
|
18
|
-
from glaip_sdk.cli.hints import format_command_hint
|
|
19
|
-
from glaip_sdk.cli.slash.prompt import _HAS_PROMPT_TOOLKIT, FormattedText
|
|
20
|
-
from glaip_sdk.cli.utils import bind_slash_session_context
|
|
21
|
-
|
|
22
|
-
if TYPE_CHECKING: # pragma: no cover - type checking only
|
|
23
|
-
from glaip_sdk.cli.slash.session import SlashSession
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
class AgentRunSession:
|
|
27
|
-
"""Per-agent execution context for the command palette."""
|
|
28
|
-
|
|
29
|
-
def __init__(self, session: SlashSession, agent: Any) -> None:
|
|
30
|
-
"""Initialize the agent run session.
|
|
31
|
-
|
|
32
|
-
Args:
|
|
33
|
-
session: The slash session context
|
|
34
|
-
agent: The agent to interact with
|
|
35
|
-
"""
|
|
36
|
-
self.session = session
|
|
37
|
-
self.agent = agent
|
|
38
|
-
self.console = session.console
|
|
39
|
-
self._agent_id = str(getattr(agent, "id", ""))
|
|
40
|
-
self._agent_name = getattr(agent, "name", "") or self._agent_id
|
|
41
|
-
self._prompt_placeholder: str = (
|
|
42
|
-
"Chat with this agent here; use / for shortcuts. "
|
|
43
|
-
"Alt+Enter inserts a newline. Ctrl+T opens the last transcript."
|
|
44
|
-
)
|
|
45
|
-
self._contextual_completion_help: dict[str, str] = {
|
|
46
|
-
"details": "Show this agent's configuration (+ expands prompt).",
|
|
47
|
-
"help": "Display this context-aware menu.",
|
|
48
|
-
"runs": "✨ NEW · Browse remote run history for this agent.",
|
|
49
|
-
"exit": "Return to the command palette.",
|
|
50
|
-
"q": "Return to the command palette.",
|
|
51
|
-
}
|
|
52
|
-
self._instruction_preview_limit = DEFAULT_AGENT_INSTRUCTION_PREVIEW_LIMIT
|
|
53
|
-
|
|
54
|
-
def run(self) -> None:
|
|
55
|
-
"""Run the interactive agent session loop."""
|
|
56
|
-
self.session.set_contextual_commands(self._contextual_completion_help, include_global=False)
|
|
57
|
-
previous_agent = getattr(self.session, "_current_agent", None)
|
|
58
|
-
self.session._current_agent = self.agent
|
|
59
|
-
clear_ready = getattr(self.session, "clear_agent_transcript_ready", None)
|
|
60
|
-
if callable(clear_ready):
|
|
61
|
-
clear_ready(self._agent_id)
|
|
62
|
-
try:
|
|
63
|
-
self._display_agent_info()
|
|
64
|
-
self._run_agent_loop()
|
|
65
|
-
finally:
|
|
66
|
-
self.session.set_contextual_commands(None)
|
|
67
|
-
self.session._current_agent = previous_agent
|
|
68
|
-
|
|
69
|
-
def _display_agent_info(self) -> None:
|
|
70
|
-
"""Display agent information and summary."""
|
|
71
|
-
self.session._render_header(self.agent, focus_agent=True)
|
|
72
|
-
|
|
73
|
-
def _run_agent_loop(self) -> None:
|
|
74
|
-
"""Run the main agent interaction loop."""
|
|
75
|
-
while True:
|
|
76
|
-
raw = self._get_user_input()
|
|
77
|
-
if raw is None:
|
|
78
|
-
return
|
|
79
|
-
|
|
80
|
-
raw = raw.strip()
|
|
81
|
-
if not raw:
|
|
82
|
-
continue
|
|
83
|
-
|
|
84
|
-
if raw.startswith("/"):
|
|
85
|
-
if not self._handle_slash_command(raw, self._agent_id):
|
|
86
|
-
return
|
|
87
|
-
continue
|
|
88
|
-
|
|
89
|
-
self._run_agent(self._agent_id, raw)
|
|
90
|
-
|
|
91
|
-
def _get_user_input(self) -> str | None:
|
|
92
|
-
"""Get user input with proper error handling."""
|
|
93
|
-
try:
|
|
94
|
-
|
|
95
|
-
def _prompt_message() -> Any:
|
|
96
|
-
"""Get formatted prompt message for agent session."""
|
|
97
|
-
prompt_prefix = f"{self._agent_name} ({self._agent_id}) "
|
|
98
|
-
|
|
99
|
-
# Use FormattedText if prompt_toolkit is available, otherwise use simple string
|
|
100
|
-
if _HAS_PROMPT_TOOLKIT and FormattedText is not None:
|
|
101
|
-
segments = [
|
|
102
|
-
("class:prompt", prompt_prefix),
|
|
103
|
-
("class:prompt", "\n› "),
|
|
104
|
-
]
|
|
105
|
-
return FormattedText(segments)
|
|
106
|
-
|
|
107
|
-
return f"{prompt_prefix}\n› "
|
|
108
|
-
|
|
109
|
-
raw = self.session._prompt(
|
|
110
|
-
_prompt_message,
|
|
111
|
-
placeholder=self._prompt_placeholder,
|
|
112
|
-
)
|
|
113
|
-
if self._prompt_placeholder:
|
|
114
|
-
# Show the guidance once, then fall back to a clean prompt.
|
|
115
|
-
self._prompt_placeholder = ""
|
|
116
|
-
return raw
|
|
117
|
-
except EOFError:
|
|
118
|
-
self.console.print("\nExiting agent context.")
|
|
119
|
-
return None
|
|
120
|
-
except KeyboardInterrupt:
|
|
121
|
-
self.console.print("")
|
|
122
|
-
return ""
|
|
123
|
-
|
|
124
|
-
def _handle_slash_command(self, raw: str, agent_id: str) -> bool:
|
|
125
|
-
"""Handle slash commands in agent context. Returns False if should exit."""
|
|
126
|
-
# Handle simple commands first
|
|
127
|
-
if raw == "/":
|
|
128
|
-
return self._handle_help_command()
|
|
129
|
-
|
|
130
|
-
if raw in {"/exit", "/back", "/q"}:
|
|
131
|
-
return self._handle_exit_command()
|
|
132
|
-
|
|
133
|
-
if raw == "/details":
|
|
134
|
-
return self._handle_details_command(agent_id)
|
|
135
|
-
|
|
136
|
-
if raw in {"/help", "/?"}:
|
|
137
|
-
return self._handle_help_command()
|
|
138
|
-
|
|
139
|
-
# Handle other commands through the main session
|
|
140
|
-
return self._handle_other_command(raw)
|
|
141
|
-
|
|
142
|
-
def _handle_help_command(self) -> bool:
|
|
143
|
-
"""Handle help command."""
|
|
144
|
-
self.session._cmd_help([], True)
|
|
145
|
-
return True
|
|
146
|
-
|
|
147
|
-
def _handle_exit_command(self) -> bool:
|
|
148
|
-
"""Handle exit command."""
|
|
149
|
-
self.console.print("[dim]Returning to the main prompt.[/dim]")
|
|
150
|
-
return False
|
|
151
|
-
|
|
152
|
-
def _handle_details_command(self, agent_id: str) -> bool:
|
|
153
|
-
"""Handle details command."""
|
|
154
|
-
self._show_details(agent_id)
|
|
155
|
-
return True
|
|
156
|
-
|
|
157
|
-
def _handle_other_command(self, raw: str) -> bool:
|
|
158
|
-
"""Handle other commands through the main session."""
|
|
159
|
-
self.session.handle_command(raw, invoked_from_agent=True)
|
|
160
|
-
return not self.session._should_exit
|
|
161
|
-
|
|
162
|
-
def _show_details(self, agent_id: str, *, enable_prompt: bool = True) -> None:
|
|
163
|
-
"""Render the agent's configuration export inside the command palette."""
|
|
164
|
-
try:
|
|
165
|
-
self.session.ctx.invoke(
|
|
166
|
-
agents_get_command,
|
|
167
|
-
agent_ref=agent_id,
|
|
168
|
-
instruction_preview=self._instruction_preview_limit,
|
|
169
|
-
)
|
|
170
|
-
if enable_prompt:
|
|
171
|
-
self._prompt_instruction_view_toggle(agent_id)
|
|
172
|
-
self.console.print(
|
|
173
|
-
f"[{HINT_PREFIX_STYLE}]Tip:[/] Continue the conversation in this prompt, or use "
|
|
174
|
-
f"{format_command_hint('/help') or '/help'} for shortcuts."
|
|
175
|
-
)
|
|
176
|
-
except click.ClickException as exc:
|
|
177
|
-
self.console.print(f"[{ERROR_STYLE}]{exc}[/]")
|
|
178
|
-
|
|
179
|
-
def _prompt_instruction_view_toggle(self, agent_id: str) -> None:
|
|
180
|
-
"""Offer a prompt to expand or collapse the instruction preview after details."""
|
|
181
|
-
if not getattr(self.console, "is_terminal", False):
|
|
182
|
-
return
|
|
183
|
-
|
|
184
|
-
while True:
|
|
185
|
-
mode = "expanded" if self._instruction_preview_limit == 0 else "trimmed"
|
|
186
|
-
self.console.print(f"[dim]Instruction view is {mode}. Press Ctrl+T to toggle, Enter to continue.[/dim]")
|
|
187
|
-
try:
|
|
188
|
-
ch = click.getchar()
|
|
189
|
-
except (EOFError, KeyboardInterrupt): # pragma: no cover - defensive guard
|
|
190
|
-
return
|
|
191
|
-
|
|
192
|
-
if not self._handle_instruction_toggle_input(agent_id, ch):
|
|
193
|
-
break
|
|
194
|
-
|
|
195
|
-
if self._instruction_preview_limit == 0:
|
|
196
|
-
self._instruction_preview_limit = DEFAULT_AGENT_INSTRUCTION_PREVIEW_LIMIT
|
|
197
|
-
self.console.print("")
|
|
198
|
-
|
|
199
|
-
def _handle_instruction_toggle_input(self, agent_id: str, ch: str) -> bool:
|
|
200
|
-
"""Process a single toggle keypress; return False when the loop should exit."""
|
|
201
|
-
if ch in {"\r", "\n"}:
|
|
202
|
-
return False
|
|
203
|
-
|
|
204
|
-
lowered = ch.lower()
|
|
205
|
-
if lowered == "t" or ch == "\x14": # support literal 't' or Ctrl+T
|
|
206
|
-
self._instruction_preview_limit = (
|
|
207
|
-
DEFAULT_AGENT_INSTRUCTION_PREVIEW_LIMIT if self._instruction_preview_limit == 0 else 0
|
|
208
|
-
)
|
|
209
|
-
self._show_details(agent_id, enable_prompt=False)
|
|
210
|
-
return True
|
|
211
|
-
|
|
212
|
-
# Ignore other keys and continue prompting.
|
|
213
|
-
return True
|
|
214
|
-
|
|
215
|
-
def _after_agent_run(self) -> None:
|
|
216
|
-
"""Handle transcript viewer behaviour after a successful run."""
|
|
217
|
-
payload, manifest = self.session._get_last_transcript()
|
|
218
|
-
if not self._transcript_matches(payload, manifest):
|
|
219
|
-
return
|
|
220
|
-
run_id = str(manifest.get("run_id") or "")
|
|
221
|
-
mark_ready = getattr(self.session, "mark_agent_transcript_ready", None)
|
|
222
|
-
if callable(mark_ready):
|
|
223
|
-
mark_ready(self._agent_id, run_id)
|
|
224
|
-
if self._open_transcript_viewer():
|
|
225
|
-
return
|
|
226
|
-
self.console.print("[dim]Transcript viewer is unavailable in this environment.[/dim]")
|
|
227
|
-
|
|
228
|
-
def _transcript_matches(self, payload: Any, manifest: Any) -> bool:
|
|
229
|
-
"""Return True when the latest transcript belongs to this agent."""
|
|
230
|
-
if not payload or not isinstance(manifest, dict):
|
|
231
|
-
return False
|
|
232
|
-
if not manifest.get("run_id"):
|
|
233
|
-
return False
|
|
234
|
-
return manifest.get("agent_id") == self._agent_id
|
|
235
|
-
|
|
236
|
-
def _open_transcript_viewer(self) -> bool:
|
|
237
|
-
"""Launch the transcript viewer when terminal support is available."""
|
|
238
|
-
if not getattr(self.console, "is_terminal", False):
|
|
239
|
-
return False
|
|
240
|
-
try:
|
|
241
|
-
current_agent = getattr(self.session, "_current_agent", None)
|
|
242
|
-
self.session.open_transcript_viewer(announce=True)
|
|
243
|
-
if getattr(self.session.console, "is_terminal", False):
|
|
244
|
-
try:
|
|
245
|
-
self.session.console.clear()
|
|
246
|
-
except Exception: # pragma: no cover - defensive cleanup
|
|
247
|
-
pass
|
|
248
|
-
if current_agent is not None: # pragma: no cover - UI refresh best effort
|
|
249
|
-
try:
|
|
250
|
-
self.session._render_header(current_agent, focus_agent=True)
|
|
251
|
-
except Exception: # pragma: no cover - defensive cleanup
|
|
252
|
-
pass
|
|
253
|
-
return True
|
|
254
|
-
except Exception: # pragma: no cover - defensive cleanup
|
|
255
|
-
return False
|
|
256
|
-
|
|
257
|
-
@contextmanager
|
|
258
|
-
def _bind_session_context(self) -> Any:
|
|
259
|
-
"""Temporarily attach this slash session to the Click context."""
|
|
260
|
-
with bind_slash_session_context(self.session.ctx, self.session):
|
|
261
|
-
yield
|
|
262
|
-
|
|
263
|
-
def _run_agent(self, agent_id: str, message: str) -> None:
|
|
264
|
-
"""Execute the agents run command for the active agent."""
|
|
265
|
-
if not message:
|
|
266
|
-
return
|
|
267
|
-
|
|
268
|
-
try:
|
|
269
|
-
self.session.notify_agent_run_started()
|
|
270
|
-
with self._bind_session_context():
|
|
271
|
-
self.session.ctx.invoke(
|
|
272
|
-
agents_run_command,
|
|
273
|
-
agent_ref=agent_id,
|
|
274
|
-
input_text=message,
|
|
275
|
-
verbose=False,
|
|
276
|
-
)
|
|
277
|
-
self.session.last_run_input = message
|
|
278
|
-
self._after_agent_run()
|
|
279
|
-
except click.ClickException as exc:
|
|
280
|
-
self.console.print(f"[{ERROR_STYLE}]{exc}[/]")
|
|
281
|
-
finally:
|
|
282
|
-
try:
|
|
283
|
-
self.session.notify_agent_run_finished()
|
|
284
|
-
except Exception:
|
|
285
|
-
pass
|
glaip_sdk/cli/slash/prompt.py
DELETED
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
"""prompt_toolkit integration helpers for the slash session.
|
|
2
|
-
|
|
3
|
-
Authors:
|
|
4
|
-
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
from collections.abc import Iterable
|
|
10
|
-
from typing import TYPE_CHECKING, Any
|
|
11
|
-
|
|
12
|
-
_HAS_PROMPT_TOOLKIT = False
|
|
13
|
-
|
|
14
|
-
try: # pragma: no cover - optional dependency
|
|
15
|
-
from prompt_toolkit import PromptSession
|
|
16
|
-
from prompt_toolkit.completion import Completer, Completion
|
|
17
|
-
from prompt_toolkit.formatted_text import FormattedText, to_formatted_text
|
|
18
|
-
from prompt_toolkit.key_binding import KeyBindings
|
|
19
|
-
from prompt_toolkit.patch_stdout import patch_stdout
|
|
20
|
-
from prompt_toolkit.styles import Style
|
|
21
|
-
|
|
22
|
-
_HAS_PROMPT_TOOLKIT = True
|
|
23
|
-
except Exception: # pragma: no cover - optional dependency
|
|
24
|
-
PromptSession = None # type: ignore[assignment]
|
|
25
|
-
Completer = None # type: ignore[assignment]
|
|
26
|
-
Completion = None # type: ignore[assignment]
|
|
27
|
-
FormattedText = None # type: ignore[assignment]
|
|
28
|
-
to_formatted_text = None # type: ignore[assignment]
|
|
29
|
-
KeyBindings = None # type: ignore[assignment]
|
|
30
|
-
Style = None # type: ignore[assignment]
|
|
31
|
-
patch_stdout = None # type: ignore[assignment]
|
|
32
|
-
|
|
33
|
-
if TYPE_CHECKING: # pragma: no cover - typing only
|
|
34
|
-
from glaip_sdk.cli.slash.session import SlashSession
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if _HAS_PROMPT_TOOLKIT:
|
|
38
|
-
|
|
39
|
-
class SlashCompleter(Completer):
|
|
40
|
-
"""Provide slash command completions inside the prompt."""
|
|
41
|
-
|
|
42
|
-
def __init__(self, session: SlashSession) -> None:
|
|
43
|
-
"""Initialize the slash completer.
|
|
44
|
-
|
|
45
|
-
Args:
|
|
46
|
-
session: The slash session context
|
|
47
|
-
"""
|
|
48
|
-
self._session = session
|
|
49
|
-
|
|
50
|
-
def get_completions(
|
|
51
|
-
self,
|
|
52
|
-
document: Any,
|
|
53
|
-
_complete_event: Any, # type: ignore[no-any-return]
|
|
54
|
-
) -> Iterable[Completion]:
|
|
55
|
-
"""Get completions for slash commands.
|
|
56
|
-
|
|
57
|
-
Args:
|
|
58
|
-
document: The document being edited
|
|
59
|
-
_complete_event: The completion event
|
|
60
|
-
|
|
61
|
-
Yields:
|
|
62
|
-
Completion objects for matching commands
|
|
63
|
-
"""
|
|
64
|
-
if Completion is None:
|
|
65
|
-
return
|
|
66
|
-
|
|
67
|
-
text = document.text_before_cursor or ""
|
|
68
|
-
if not text.startswith("/") or " " in text:
|
|
69
|
-
return
|
|
70
|
-
|
|
71
|
-
yield from _iter_command_completions(self._session, text)
|
|
72
|
-
yield from _iter_contextual_completions(self._session, text)
|
|
73
|
-
|
|
74
|
-
else: # pragma: no cover - fallback when prompt_toolkit is missing
|
|
75
|
-
|
|
76
|
-
class SlashCompleter: # type: ignore[too-many-ancestors]
|
|
77
|
-
"""Fallback slash completer when prompt_toolkit is not available."""
|
|
78
|
-
|
|
79
|
-
def __init__(self, session: SlashSession) -> None:
|
|
80
|
-
"""Initialize the fallback slash completer.
|
|
81
|
-
|
|
82
|
-
Args:
|
|
83
|
-
session: The slash session context
|
|
84
|
-
"""
|
|
85
|
-
self._session = session
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
def setup_prompt_toolkit(
|
|
89
|
-
session: SlashSession,
|
|
90
|
-
*,
|
|
91
|
-
interactive: bool,
|
|
92
|
-
) -> tuple[Any | None, Any | None]:
|
|
93
|
-
"""Configure prompt_toolkit session and style for interactive mode."""
|
|
94
|
-
if not (interactive and _HAS_PROMPT_TOOLKIT):
|
|
95
|
-
return None, None
|
|
96
|
-
|
|
97
|
-
if PromptSession is None or Style is None:
|
|
98
|
-
return None, None
|
|
99
|
-
|
|
100
|
-
bindings = _create_key_bindings(session)
|
|
101
|
-
|
|
102
|
-
prompt_session = PromptSession(
|
|
103
|
-
completer=SlashCompleter(session),
|
|
104
|
-
complete_while_typing=True,
|
|
105
|
-
key_bindings=bindings,
|
|
106
|
-
)
|
|
107
|
-
prompt_style = Style.from_dict(
|
|
108
|
-
{
|
|
109
|
-
"prompt": "bg:#0f172a #facc15 bold",
|
|
110
|
-
"": "bg:#0f172a #e2e8f0",
|
|
111
|
-
"placeholder": "bg:#0f172a #94a3b8 italic",
|
|
112
|
-
}
|
|
113
|
-
)
|
|
114
|
-
|
|
115
|
-
return prompt_session, prompt_style
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
def _create_key_bindings(_session: SlashSession) -> Any:
|
|
119
|
-
"""Create prompt_toolkit key bindings for the command palette."""
|
|
120
|
-
if KeyBindings is None:
|
|
121
|
-
return None
|
|
122
|
-
|
|
123
|
-
bindings = KeyBindings()
|
|
124
|
-
|
|
125
|
-
def _refresh_completions(buffer: Any) -> None: # type: ignore[no-any-return]
|
|
126
|
-
"""Refresh completions when slash command is typed.
|
|
127
|
-
|
|
128
|
-
Args:
|
|
129
|
-
buffer: Prompt buffer instance.
|
|
130
|
-
"""
|
|
131
|
-
text = buffer.document.text_before_cursor or ""
|
|
132
|
-
if text.startswith("/") and " " not in text:
|
|
133
|
-
buffer.start_completion(select_first=False)
|
|
134
|
-
elif buffer.complete_state is not None:
|
|
135
|
-
buffer.cancel_completion()
|
|
136
|
-
|
|
137
|
-
@bindings.add("/") # type: ignore[misc]
|
|
138
|
-
def _handle_slash_key(event: Any) -> None: # vulture: ignore
|
|
139
|
-
"""Handle '/' key press - insert slash and trigger completion."""
|
|
140
|
-
buffer = event.app.current_buffer
|
|
141
|
-
buffer.insert_text("/")
|
|
142
|
-
_refresh_completions(buffer)
|
|
143
|
-
|
|
144
|
-
@bindings.add("backspace") # type: ignore[misc]
|
|
145
|
-
def _handle_backspace_key(event: Any) -> None: # vulture: ignore
|
|
146
|
-
"""Handle backspace key - delete character and refresh completions."""
|
|
147
|
-
buffer = event.app.current_buffer
|
|
148
|
-
if buffer.document.cursor_position > 0:
|
|
149
|
-
buffer.delete_before_cursor()
|
|
150
|
-
_refresh_completions(buffer)
|
|
151
|
-
|
|
152
|
-
@bindings.add("c-h") # type: ignore[misc]
|
|
153
|
-
def _handle_ctrl_h_key(event: Any) -> None: # vulture: ignore
|
|
154
|
-
"""Handle Ctrl+H key - same as backspace."""
|
|
155
|
-
_handle_backspace_key(event) # Reuse backspace handler
|
|
156
|
-
|
|
157
|
-
@bindings.add("escape", "enter") # type: ignore[misc]
|
|
158
|
-
def _handle_alt_enter_key(event: Any) -> None: # vulture: ignore
|
|
159
|
-
"""Handle Alt+Enter key - insert line break and cancel completion."""
|
|
160
|
-
buffer = event.app.current_buffer
|
|
161
|
-
buffer.insert_text("\n")
|
|
162
|
-
if buffer.complete_state is not None:
|
|
163
|
-
buffer.cancel_completion()
|
|
164
|
-
|
|
165
|
-
@bindings.add("c-t") # type: ignore[misc]
|
|
166
|
-
def _handle_ctrl_t_key(event: Any) -> None: # vulture: ignore
|
|
167
|
-
"""Handle Ctrl+T key - open the transcript viewer (when available)."""
|
|
168
|
-
buffer = event.app.current_buffer
|
|
169
|
-
if buffer.complete_state is not None:
|
|
170
|
-
buffer.cancel_completion()
|
|
171
|
-
|
|
172
|
-
open_viewer = getattr(_session, "open_transcript_viewer", None)
|
|
173
|
-
if callable(open_viewer):
|
|
174
|
-
open_viewer(announce=True)
|
|
175
|
-
|
|
176
|
-
return bindings
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
def _iter_command_completions(
|
|
180
|
-
session: SlashSession, text: str
|
|
181
|
-
) -> Iterable[Completion]: # pragma: no cover - thin wrapper
|
|
182
|
-
"""Yield completions for global slash commands."""
|
|
183
|
-
prefix = text[1:]
|
|
184
|
-
seen: set[str] = set()
|
|
185
|
-
|
|
186
|
-
# Early return for contextual commands scenario
|
|
187
|
-
if not _should_include_commands(session):
|
|
188
|
-
return []
|
|
189
|
-
|
|
190
|
-
commands = sorted(session._unique_commands.values(), key=lambda c: c.name)
|
|
191
|
-
agent_context = bool(getattr(session, "_current_agent", None))
|
|
192
|
-
|
|
193
|
-
for cmd in commands:
|
|
194
|
-
if getattr(cmd, "agent_only", False) and not agent_context:
|
|
195
|
-
continue
|
|
196
|
-
yield from _generate_command_completions(cmd, prefix, text, seen)
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
def _should_include_commands(session: SlashSession) -> bool:
|
|
200
|
-
"""Check if commands should be included in completions."""
|
|
201
|
-
return not (session.get_contextual_commands() and not session.should_include_global_commands())
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
def _generate_command_completions(cmd: Any, prefix: str, text: str, seen: set[str]) -> Iterable[Completion]:
|
|
205
|
-
"""Generate completion items for a single command."""
|
|
206
|
-
for alias in (cmd.name, *cmd.aliases):
|
|
207
|
-
if alias in seen or alias.startswith("?"):
|
|
208
|
-
continue
|
|
209
|
-
|
|
210
|
-
if prefix and not alias.startswith(prefix):
|
|
211
|
-
continue
|
|
212
|
-
|
|
213
|
-
seen.add(alias)
|
|
214
|
-
label = f"/{alias}"
|
|
215
|
-
yield Completion(
|
|
216
|
-
text=label,
|
|
217
|
-
start_position=-len(text),
|
|
218
|
-
display=label,
|
|
219
|
-
display_meta=cmd.help,
|
|
220
|
-
)
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
def _iter_contextual_completions(
|
|
224
|
-
session: SlashSession, text: str
|
|
225
|
-
) -> Iterable[Completion]: # pragma: no cover - thin wrapper
|
|
226
|
-
"""Yield completions for context-specific slash commands."""
|
|
227
|
-
prefix = text[1:]
|
|
228
|
-
seen: set[str] = set()
|
|
229
|
-
|
|
230
|
-
contextual_commands = sorted(session.get_contextual_commands().items(), key=lambda item: item[0])
|
|
231
|
-
|
|
232
|
-
for alias, help_text in contextual_commands:
|
|
233
|
-
if alias in seen:
|
|
234
|
-
continue
|
|
235
|
-
if prefix and not alias.startswith(prefix):
|
|
236
|
-
continue
|
|
237
|
-
seen.add(alias)
|
|
238
|
-
label = f"/{alias}"
|
|
239
|
-
yield Completion(
|
|
240
|
-
text=label,
|
|
241
|
-
start_position=-len(text),
|
|
242
|
-
display=label,
|
|
243
|
-
display_meta=help_text,
|
|
244
|
-
)
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
__all__ = [
|
|
248
|
-
"SlashCompleter",
|
|
249
|
-
"setup_prompt_toolkit",
|
|
250
|
-
"FormattedText",
|
|
251
|
-
"to_formatted_text",
|
|
252
|
-
"patch_stdout",
|
|
253
|
-
"PromptSession",
|
|
254
|
-
"Style",
|
|
255
|
-
"_HAS_PROMPT_TOOLKIT",
|
|
256
|
-
]
|