zjcode 0.0.1__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.
Files changed (163) hide show
  1. deepagents_code/__init__.py +42 -0
  2. deepagents_code/__main__.py +6 -0
  3. deepagents_code/_ask_user_types.py +90 -0
  4. deepagents_code/_cli_context.py +96 -0
  5. deepagents_code/_constants.py +41 -0
  6. deepagents_code/_debug.py +148 -0
  7. deepagents_code/_debug_buffer.py +204 -0
  8. deepagents_code/_env_vars.py +411 -0
  9. deepagents_code/_fake_models.py +66 -0
  10. deepagents_code/_git.py +521 -0
  11. deepagents_code/_paths.py +69 -0
  12. deepagents_code/_server_config.py +576 -0
  13. deepagents_code/_session_stats.py +235 -0
  14. deepagents_code/_startup_error.py +45 -0
  15. deepagents_code/_testing_models.py +305 -0
  16. deepagents_code/_textual_patches.py +420 -0
  17. deepagents_code/_tool_stream.py +694 -0
  18. deepagents_code/_version.py +46 -0
  19. deepagents_code/agent.py +1976 -0
  20. deepagents_code/app.py +19239 -0
  21. deepagents_code/app.tcss +438 -0
  22. deepagents_code/approval_mode.py +131 -0
  23. deepagents_code/ask_user.py +306 -0
  24. deepagents_code/auth_display.py +185 -0
  25. deepagents_code/auth_store.py +545 -0
  26. deepagents_code/built_in_skills/__init__.py +5 -0
  27. deepagents_code/built_in_skills/remember/SKILL.md +118 -0
  28. deepagents_code/built_in_skills/skill-creator/SKILL.md +383 -0
  29. deepagents_code/built_in_skills/skill-creator/scripts/init_skill.py +366 -0
  30. deepagents_code/built_in_skills/skill-creator/scripts/quick_validate.py +158 -0
  31. deepagents_code/client/__init__.py +1 -0
  32. deepagents_code/client/commands/__init__.py +1 -0
  33. deepagents_code/client/commands/auth.py +541 -0
  34. deepagents_code/client/commands/config.py +714 -0
  35. deepagents_code/client/commands/mcp.py +250 -0
  36. deepagents_code/client/commands/tools.py +416 -0
  37. deepagents_code/client/launch/__init__.py +1 -0
  38. deepagents_code/client/launch/server.py +978 -0
  39. deepagents_code/client/launch/server_manager.py +540 -0
  40. deepagents_code/client/non_interactive.py +1758 -0
  41. deepagents_code/client/remote_client.py +794 -0
  42. deepagents_code/clipboard.py +217 -0
  43. deepagents_code/command_registry.py +450 -0
  44. deepagents_code/config.py +4829 -0
  45. deepagents_code/config_manifest.py +1451 -0
  46. deepagents_code/configurable_model.py +577 -0
  47. deepagents_code/default_agent_prompt.md +12 -0
  48. deepagents_code/doctor.py +559 -0
  49. deepagents_code/editor.py +142 -0
  50. deepagents_code/event_bus.py +411 -0
  51. deepagents_code/extras_info.py +661 -0
  52. deepagents_code/file_ops.py +576 -0
  53. deepagents_code/formatting.py +135 -0
  54. deepagents_code/goal_rubric.py +497 -0
  55. deepagents_code/goal_tools.py +459 -0
  56. deepagents_code/hooks.py +348 -0
  57. deepagents_code/input.py +1041 -0
  58. deepagents_code/integrations/__init__.py +1 -0
  59. deepagents_code/integrations/openai_codex.py +551 -0
  60. deepagents_code/integrations/sandbox_config.py +198 -0
  61. deepagents_code/integrations/sandbox_factory.py +1124 -0
  62. deepagents_code/integrations/sandbox_provider.py +137 -0
  63. deepagents_code/integrations/sandbox_registry.py +350 -0
  64. deepagents_code/iterm_cursor_guide.py +176 -0
  65. deepagents_code/local_context.py +926 -0
  66. deepagents_code/main.py +3985 -0
  67. deepagents_code/managed_tools.py +642 -0
  68. deepagents_code/mcp_auth.py +1950 -0
  69. deepagents_code/mcp_config.py +176 -0
  70. deepagents_code/mcp_disabled.py +212 -0
  71. deepagents_code/mcp_login_service.py +281 -0
  72. deepagents_code/mcp_oauth_ui.py +199 -0
  73. deepagents_code/mcp_providers/__init__.py +23 -0
  74. deepagents_code/mcp_providers/_registry.py +39 -0
  75. deepagents_code/mcp_providers/base.py +133 -0
  76. deepagents_code/mcp_providers/github.py +102 -0
  77. deepagents_code/mcp_providers/slack.py +175 -0
  78. deepagents_code/mcp_tools.py +2427 -0
  79. deepagents_code/mcp_trust.py +207 -0
  80. deepagents_code/media_utils.py +826 -0
  81. deepagents_code/memory_guard.py +474 -0
  82. deepagents_code/model_config.py +4156 -0
  83. deepagents_code/notifications.py +247 -0
  84. deepagents_code/offload.py +402 -0
  85. deepagents_code/onboarding.py +223 -0
  86. deepagents_code/output.py +69 -0
  87. deepagents_code/paste_collapse.py +103 -0
  88. deepagents_code/project_utils.py +231 -0
  89. deepagents_code/py.typed +0 -0
  90. deepagents_code/reasoning_effort.py +641 -0
  91. deepagents_code/reliable_rubric.py +97 -0
  92. deepagents_code/resume_state.py +237 -0
  93. deepagents_code/server_graph.py +310 -0
  94. deepagents_code/session_end_summary.py +329 -0
  95. deepagents_code/sessions.py +1716 -0
  96. deepagents_code/skills/__init__.py +18 -0
  97. deepagents_code/skills/commands.py +1252 -0
  98. deepagents_code/skills/invocation.py +112 -0
  99. deepagents_code/skills/load.py +196 -0
  100. deepagents_code/skills/trust.py +547 -0
  101. deepagents_code/state_migration.py +136 -0
  102. deepagents_code/subagents.py +278 -0
  103. deepagents_code/system_prompt.md +204 -0
  104. deepagents_code/terminal_capabilities.py +115 -0
  105. deepagents_code/terminal_escape.py +287 -0
  106. deepagents_code/theme.py +891 -0
  107. deepagents_code/todo_list_prompt.md +12 -0
  108. deepagents_code/tool_catalog.py +509 -0
  109. deepagents_code/tool_display.py +367 -0
  110. deepagents_code/tools.py +516 -0
  111. deepagents_code/tui/__init__.py +1 -0
  112. deepagents_code/tui/textual_adapter.py +2553 -0
  113. deepagents_code/tui/widgets/__init__.py +9 -0
  114. deepagents_code/tui/widgets/_js_eval_display.py +139 -0
  115. deepagents_code/tui/widgets/_links.py +261 -0
  116. deepagents_code/tui/widgets/agent_selector.py +420 -0
  117. deepagents_code/tui/widgets/approval.py +602 -0
  118. deepagents_code/tui/widgets/ask_user.py +515 -0
  119. deepagents_code/tui/widgets/auth.py +1997 -0
  120. deepagents_code/tui/widgets/autocomplete.py +890 -0
  121. deepagents_code/tui/widgets/chat_input.py +3181 -0
  122. deepagents_code/tui/widgets/codex_auth.py +452 -0
  123. deepagents_code/tui/widgets/cwd_switch.py +242 -0
  124. deepagents_code/tui/widgets/debug_console.py +868 -0
  125. deepagents_code/tui/widgets/diff.py +252 -0
  126. deepagents_code/tui/widgets/effort_selector.py +189 -0
  127. deepagents_code/tui/widgets/goal_review.py +390 -0
  128. deepagents_code/tui/widgets/goal_status.py +50 -0
  129. deepagents_code/tui/widgets/history.py +194 -0
  130. deepagents_code/tui/widgets/install_confirm.py +248 -0
  131. deepagents_code/tui/widgets/launch_init.py +482 -0
  132. deepagents_code/tui/widgets/loading.py +227 -0
  133. deepagents_code/tui/widgets/mcp_login.py +539 -0
  134. deepagents_code/tui/widgets/mcp_reconnect.py +212 -0
  135. deepagents_code/tui/widgets/mcp_viewer.py +1634 -0
  136. deepagents_code/tui/widgets/message_store.py +999 -0
  137. deepagents_code/tui/widgets/messages.py +3846 -0
  138. deepagents_code/tui/widgets/model_selector.py +2343 -0
  139. deepagents_code/tui/widgets/notification_center.py +456 -0
  140. deepagents_code/tui/widgets/notification_detail.py +257 -0
  141. deepagents_code/tui/widgets/notification_settings.py +170 -0
  142. deepagents_code/tui/widgets/restart_prompt.py +158 -0
  143. deepagents_code/tui/widgets/skill_trust.py +131 -0
  144. deepagents_code/tui/widgets/startup_tip.py +91 -0
  145. deepagents_code/tui/widgets/status.py +781 -0
  146. deepagents_code/tui/widgets/subagent_panel.py +969 -0
  147. deepagents_code/tui/widgets/theme_selector.py +401 -0
  148. deepagents_code/tui/widgets/thread_selector.py +2564 -0
  149. deepagents_code/tui/widgets/tool_renderers.py +190 -0
  150. deepagents_code/tui/widgets/tool_widgets.py +304 -0
  151. deepagents_code/tui/widgets/update_available.py +311 -0
  152. deepagents_code/tui/widgets/update_confirm.py +184 -0
  153. deepagents_code/tui/widgets/update_progress.py +327 -0
  154. deepagents_code/tui/widgets/welcome.py +508 -0
  155. deepagents_code/turn_end_summary.py +522 -0
  156. deepagents_code/ui.py +858 -0
  157. deepagents_code/unicode_security.py +563 -0
  158. deepagents_code/update_check.py +3124 -0
  159. zjcode-0.0.1.data/data/deepagents_code/default_agent_prompt.md +12 -0
  160. zjcode-0.0.1.dist-info/METADATA +220 -0
  161. zjcode-0.0.1.dist-info/RECORD +163 -0
  162. zjcode-0.0.1.dist-info/WHEEL +4 -0
  163. zjcode-0.0.1.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,438 @@
1
+ /* Deep Agents Code Textual Stylesheet */
2
+
3
+ /* Define layers for z-ordering */
4
+ Screen {
5
+ layout: vertical;
6
+ layers: base autocomplete;
7
+ }
8
+
9
+ /* Thin scrollbars app-wide */
10
+ * {
11
+ scrollbar-size-vertical: 1;
12
+ }
13
+
14
+ /* Hide the Header's clickable ⭘ icon. It opens the command palette by default,
15
+ but ENABLE_COMMAND_PALETTE = False on the app, so it's already inert. We use
16
+ `visibility: hidden` (not `display: none`) so the 8-cell gutter is preserved
17
+ and balances the clock-space gutter on the right, keeping the title centered. */
18
+ #app-header HeaderIcon {
19
+ visibility: hidden;
20
+ }
21
+
22
+ /* Main content goes on base layer by default */
23
+
24
+ .message-timestamp-footer {
25
+ display: none;
26
+ height: auto;
27
+ color: $text-muted;
28
+ text-style: dim;
29
+ margin: -1 0 1 0;
30
+ padding: 0;
31
+ }
32
+
33
+ /*
34
+ * Visibility is toggled per-footer (not via a class on #messages) so a
35
+ * `/timestamps` toggle restyles only the footer leaves instead of forcing a
36
+ * re-cascade across every message subtree.
37
+ */
38
+ .message-timestamp-footer.message-timestamp-footer-visible {
39
+ display: block;
40
+ }
41
+
42
+ /* Chat area - main scrollable messages area.
43
+ No horizontal padding so message blocks span the same full width as the
44
+ input box below (whose border is flush to both edges). The scrollbar is
45
+ toggled at runtime via the /scrollbar command (off by default). */
46
+ #chat {
47
+ height: 1fr;
48
+ padding: 1 0 0 0;
49
+ background: $background;
50
+ }
51
+
52
+ /* Welcome banner */
53
+ #welcome-banner {
54
+ height: auto;
55
+ margin-bottom: 1;
56
+ }
57
+
58
+ /* Messages area — uses undocumented "stream" layout (Textual ≥5.2.0) for
59
+ O(1) append performance via incremental placement caching. */
60
+ #messages {
61
+ layout: stream;
62
+ height: auto;
63
+ }
64
+
65
+ /* Bottom app container - holds ChatInput (now inside scroll) */
66
+ #bottom-app-container {
67
+ height: auto;
68
+ margin-top: 0;
69
+ }
70
+
71
+ /* Input area */
72
+ #input-area {
73
+ height: auto;
74
+ min-height: 3;
75
+ max-height: 25;
76
+ }
77
+
78
+ /* Approval Menu - inline in messages area */
79
+ .approval-menu {
80
+ height: auto;
81
+ margin: 1 0;
82
+ padding: 0 1;
83
+ background: $surface;
84
+ border: solid $warning;
85
+ }
86
+
87
+ /* Placeholder shown while the user is actively typing (approval deferred) */
88
+ .approval-placeholder {
89
+ height: auto;
90
+ margin: 1 0;
91
+ padding: 0 1;
92
+ border: solid $panel;
93
+ color: $text-muted;
94
+ text-style: italic;
95
+ }
96
+
97
+ .approval-menu .approval-title {
98
+ text-style: bold;
99
+ color: $warning;
100
+ margin-bottom: 0;
101
+ }
102
+
103
+ .approval-menu .approval-info {
104
+ height: auto;
105
+ color: $text-muted;
106
+ margin-bottom: 1;
107
+ }
108
+
109
+ .approval-menu .approval-option {
110
+ height: 1;
111
+ padding: 0 1;
112
+ }
113
+
114
+ .approval-menu .approval-option-selected {
115
+ background: $primary;
116
+ text-style: bold;
117
+ }
118
+
119
+ .approval-menu .approval-help {
120
+ color: $text-muted;
121
+ text-style: italic;
122
+ margin-top: 0;
123
+ margin-bottom: 0;
124
+ }
125
+
126
+ /* Status bar */
127
+ #status-bar {
128
+ height: 1;
129
+ dock: bottom;
130
+ }
131
+
132
+ /* Tool approval widgets */
133
+ .tool-approval-widget {
134
+ height: auto;
135
+ }
136
+
137
+ .approval-description {
138
+ color: $text-muted;
139
+ }
140
+
141
+ /* Diff styling — used by EditFileApprovalWidget (fg + bg + padding) */
142
+ .diff-removed {
143
+ height: auto;
144
+ color: $text-error;
145
+ background: $error-muted;
146
+ padding: 0 1;
147
+ }
148
+
149
+ .diff-added {
150
+ height: auto;
151
+ color: $text-success;
152
+ background: $success-muted;
153
+ padding: 0 1;
154
+ }
155
+
156
+ .diff-context {
157
+ height: auto;
158
+ color: $text-muted;
159
+ padding: 0 1;
160
+ }
161
+
162
+ /* Diff line backgrounds — used by compose_diff_lines (bg only, fg via Content) */
163
+ .diff-line-removed {
164
+ background: $error-muted;
165
+ }
166
+
167
+ .diff-line-added {
168
+ background: $success-muted;
169
+ }
170
+
171
+ /* Mode-specific borders for UserMessage */
172
+ UserMessage.-mode-shell {
173
+ border-left: wide $mode-bash;
174
+ }
175
+
176
+ UserMessage.-mode-shell-incognito {
177
+ border-left: wide $mode-incognito;
178
+ }
179
+
180
+ UserMessage.-mode-command {
181
+ border-left: wide $mode-command;
182
+ }
183
+
184
+ /* ASCII border overrides */
185
+ UserMessage.-ascii {
186
+ border-left: ascii $primary;
187
+ }
188
+
189
+ UserMessage.-ascii.-mode-shell {
190
+ border-left: ascii $mode-bash;
191
+ }
192
+
193
+ UserMessage.-ascii.-mode-shell-incognito {
194
+ border-left: ascii $mode-incognito;
195
+ }
196
+
197
+ UserMessage.-ascii.-mode-command {
198
+ border-left: ascii $mode-command;
199
+ }
200
+
201
+ QueuedUserMessage.-ascii {
202
+ border-left: ascii $panel;
203
+ }
204
+
205
+ ToolCallMessage.-ascii {
206
+ border-left: ascii $panel;
207
+ }
208
+
209
+ ToolCallMessage.-ascii:hover {
210
+ border-left: ascii $secondary;
211
+ }
212
+
213
+ /* Approval command — warning color for shell commands */
214
+ .approval-menu .approval-command {
215
+ height: auto;
216
+ margin: 0 0 1 0;
217
+ padding: 0 1;
218
+ color: $warning;
219
+ }
220
+
221
+ /* Separator line between tool details and options */
222
+ .approval-menu .approval-separator {
223
+ height: 1;
224
+ color: $warning;
225
+ margin: 0;
226
+ }
227
+
228
+ /* Scrollable tool info area in approval menu */
229
+ .approval-menu .tool-info-scroll {
230
+ height: auto;
231
+ max-height: 10;
232
+ margin-top: 0;
233
+ }
234
+
235
+ /* Inner container for tool info - allows content to expand for scrolling */
236
+ .approval-menu .tool-info-container {
237
+ height: auto;
238
+ }
239
+
240
+ /* Options container with background */
241
+ .approval-menu .approval-options-container {
242
+ height: auto;
243
+ background: $surface-darken-1;
244
+ padding: 0 1;
245
+ margin-top: 0;
246
+ }
247
+
248
+ /* Free-text reject reason input (Tab on Reject) */
249
+ .approval-menu .approval-reason-input {
250
+ margin: 1 0 0 0;
251
+ border: solid $warning;
252
+ background: $surface;
253
+ }
254
+
255
+ /* Persistent goal status */
256
+ .goal-status-panel {
257
+ height: auto;
258
+ max-height: 5;
259
+ margin: 0 1;
260
+ padding: 0 1;
261
+ color: $text;
262
+ background: $surface-darken-1;
263
+ border-left: thick $primary;
264
+ }
265
+
266
+ /* Goal review widget */
267
+ .goal-review-menu {
268
+ height: auto;
269
+ margin: 1 0;
270
+ padding: 0 1;
271
+ background: $surface;
272
+ border: solid $success;
273
+ }
274
+
275
+ .goal-review-menu .goal-review-title {
276
+ text-style: bold;
277
+ color: $success;
278
+ margin-bottom: 0;
279
+ }
280
+
281
+ .goal-review-menu .goal-review-content {
282
+ height: auto;
283
+ max-height: 12;
284
+ padding: 0 1;
285
+ }
286
+
287
+ .goal-review-menu .goal-review-body {
288
+ height: auto;
289
+ }
290
+
291
+ .goal-review-menu .goal-review-markdown {
292
+ margin: 0 0 1 0;
293
+ padding: 0;
294
+ }
295
+
296
+ .goal-review-menu .goal-review-options-container {
297
+ height: auto;
298
+ background: $surface-darken-1;
299
+ padding: 0 1;
300
+ margin-top: 0;
301
+ }
302
+
303
+ .goal-review-menu .goal-review-option {
304
+ height: 1;
305
+ padding: 0 1;
306
+ }
307
+
308
+ .goal-review-menu .goal-review-option-selected {
309
+ background: $primary;
310
+ text-style: bold;
311
+ }
312
+
313
+ .goal-review-menu .goal-review-edit-input {
314
+ margin: 1 1 0 1;
315
+ height: auto;
316
+ min-height: 4;
317
+ max-height: 12;
318
+ background: transparent;
319
+ }
320
+
321
+ .goal-review-menu .goal-review-help {
322
+ color: $text-muted;
323
+ text-style: italic;
324
+ margin-top: 0;
325
+ margin-bottom: 0;
326
+ }
327
+
328
+ .goal-review-menu AskUserTextArea {
329
+ & .text-area--cursor-line {
330
+ background: transparent;
331
+ }
332
+
333
+ & .text-area--cursor-gutter {
334
+ background: transparent;
335
+ }
336
+ }
337
+
338
+ /* Ask user widget */
339
+ .ask-user-menu {
340
+ height: auto;
341
+ margin: 1 0;
342
+ padding: 0 1;
343
+ background: $surface;
344
+ border: solid $success;
345
+ }
346
+
347
+ .ask-user-menu .ask-user-title {
348
+ text-style: bold;
349
+ color: $success;
350
+ margin-bottom: 0;
351
+ }
352
+
353
+ .ask-user-menu .ask-user-help {
354
+ color: $text-muted;
355
+ text-style: italic;
356
+ margin-top: 0;
357
+ margin-bottom: 0;
358
+ }
359
+
360
+ .ask-user-menu .ask-user-questions {
361
+ height: auto;
362
+ padding: 0 1;
363
+ }
364
+
365
+ .ask-user-menu .ask-user-question {
366
+ height: auto;
367
+ margin-bottom: 1;
368
+ }
369
+
370
+ .ask-user-menu .ask-user-question-active {
371
+ border-left: thick $success;
372
+ padding-left: 1;
373
+ }
374
+
375
+ .ask-user-menu .ask-user-question-inactive {
376
+ opacity: 0.5;
377
+ padding-left: 2;
378
+ }
379
+
380
+ .ask-user-menu .ask-user-question-text {
381
+ margin: 0 0 0 2;
382
+ padding: 0;
383
+ }
384
+
385
+ .ask-user-menu .ask-user-choice {
386
+ height: auto;
387
+ padding: 0 1;
388
+ }
389
+
390
+ .ask-user-menu .ask-user-text-input {
391
+ margin: 1 1 0 1;
392
+ height: auto;
393
+ min-height: 3;
394
+ max-height: 10;
395
+ background: transparent;
396
+ }
397
+
398
+ .ask-user-menu .ask-user-other-input {
399
+ margin: 1 1 0 1;
400
+ height: auto;
401
+ min-height: 3;
402
+ max-height: 10;
403
+ background: transparent;
404
+ }
405
+
406
+ .ask-user-menu AskUserTextArea {
407
+ & .text-area--cursor-line {
408
+ background: transparent;
409
+ }
410
+
411
+ & .text-area--cursor-gutter {
412
+ background: transparent;
413
+ }
414
+ }
415
+
416
+ /* ChatTextArea: override TextArea's built-in $surface background */
417
+ ChatTextArea {
418
+ background: transparent;
419
+
420
+ & .text-area--cursor-line {
421
+ background: transparent;
422
+ }
423
+
424
+ & .text-area--cursor-gutter {
425
+ background: transparent;
426
+ }
427
+ }
428
+
429
+ /* Completion popup styling (used by ChatInput) - appears BELOW input */
430
+ #completion-popup {
431
+ height: auto;
432
+ max-height: 12;
433
+ width: 100%;
434
+ margin-left: 3;
435
+ margin-top: 0;
436
+ padding: 0;
437
+ color: $text;
438
+ }
@@ -0,0 +1,131 @@
1
+ """Live approval-mode state shared through the LangGraph Store."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from collections.abc import Mapping
7
+ from hashlib import sha256
8
+ from typing import TypedDict
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+ APPROVAL_MODE_NAMESPACE: tuple[str, str] = ("deepagents_code", "approval_mode")
13
+ """Store namespace for per-thread approval-mode control records."""
14
+
15
+
16
+ class ApprovalModePayload(TypedDict):
17
+ """Stored approval-mode control payload."""
18
+
19
+ auto_approve: bool
20
+
21
+
22
+ def approval_mode_key(thread_id: str) -> str:
23
+ """Return the store key for a thread's live approval mode.
24
+
25
+ Args:
26
+ thread_id: LangGraph thread id for the active session.
27
+
28
+ Returns:
29
+ Deterministic store key that does not expose the raw thread id.
30
+ """
31
+ return sha256(thread_id.encode("utf-8")).hexdigest()
32
+
33
+
34
+ def approval_mode_payload(*, auto_approve: bool) -> ApprovalModePayload:
35
+ """Return the stored approval-mode payload.
36
+
37
+ Args:
38
+ auto_approve: Whether gated tool calls should skip HITL approval.
39
+
40
+ Returns:
41
+ JSON-serializable store value.
42
+ """
43
+ return {"auto_approve": auto_approve}
44
+
45
+
46
+ def _item_value(item: object) -> object:
47
+ """Extract a store item's value from SDK and runtime item shapes.
48
+
49
+ Returns:
50
+ The item's stored value, or `None` when the shape is unrecognized.
51
+ """
52
+ if isinstance(item, Mapping):
53
+ return item.get("value")
54
+ return getattr(item, "value", None)
55
+
56
+
57
+ def read_approval_mode_from_store(store: object, key: str | None) -> bool | None:
58
+ """Read a live approval mode from the server-side LangGraph Store.
59
+
60
+ Args:
61
+ store: `request.runtime.store` from the graph server.
62
+ key: Store key produced by `approval_mode_key`. The `isinstance` guard
63
+ below still rejects non-string keys as defense-in-depth, since the
64
+ value crosses the JSON/RemoteGraph boundary before reaching here.
65
+
66
+ Returns:
67
+ `True` or `False` when the store contains a valid mode, otherwise
68
+ `None`. Callers should treat `None` as fail-closed.
69
+ """
70
+ if store is None:
71
+ logger.debug("Approval-mode store is unavailable")
72
+ return None
73
+ if not isinstance(key, str) or not key:
74
+ logger.debug("Approval-mode store key is missing or invalid")
75
+ return None
76
+
77
+ get = getattr(store, "get", None)
78
+ if get is None:
79
+ logger.debug("Approval-mode store does not expose get()")
80
+ return None
81
+
82
+ try:
83
+ item = get(APPROVAL_MODE_NAMESPACE, key)
84
+ except Exception:
85
+ logger.warning("Could not read approval-mode store item", exc_info=True)
86
+ return None
87
+ if item is None:
88
+ logger.debug("Approval-mode store item is missing")
89
+ return None
90
+
91
+ value = _item_value(item)
92
+ auto_approve = value.get("auto_approve") if isinstance(value, Mapping) else None
93
+ if isinstance(auto_approve, bool):
94
+ return auto_approve
95
+
96
+ logger.debug("Approval-mode store item has invalid contents")
97
+ return None
98
+
99
+
100
+ async def awrite_approval_mode(
101
+ agent: object,
102
+ thread_id: str,
103
+ *,
104
+ auto_approve: bool,
105
+ ) -> str | None:
106
+ """Persist approval mode through an agent's remote store client.
107
+
108
+ Args:
109
+ agent: Agent object. Remote agents expose `aput_store_item`; agents
110
+ without a writer use run context only.
111
+ thread_id: LangGraph thread id for the active session.
112
+ auto_approve: Whether gated tool calls should skip HITL approval.
113
+
114
+ Returns:
115
+ Store key written, or `None` when the agent has no store writer.
116
+
117
+ Notes:
118
+ Remote agents rely on the server-side store being visible to the
119
+ running graph before the next gated tool predicate executes.
120
+ """
121
+ put = getattr(agent, "aput_store_item", None)
122
+ if put is None:
123
+ return None
124
+
125
+ key = approval_mode_key(thread_id)
126
+ await put(
127
+ APPROVAL_MODE_NAMESPACE,
128
+ key,
129
+ approval_mode_payload(auto_approve=auto_approve),
130
+ )
131
+ return key