gobby 0.2.6__py3-none-any.whl → 0.2.8__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.
- gobby/__init__.py +1 -1
- gobby/adapters/__init__.py +2 -1
- gobby/adapters/claude_code.py +96 -35
- gobby/adapters/codex_impl/__init__.py +28 -0
- gobby/adapters/codex_impl/adapter.py +722 -0
- gobby/adapters/codex_impl/client.py +679 -0
- gobby/adapters/codex_impl/protocol.py +20 -0
- gobby/adapters/codex_impl/types.py +68 -0
- gobby/adapters/gemini.py +140 -38
- gobby/agents/definitions.py +11 -1
- gobby/agents/isolation.py +525 -0
- gobby/agents/registry.py +11 -0
- gobby/agents/sandbox.py +261 -0
- gobby/agents/session.py +1 -0
- gobby/agents/spawn.py +42 -287
- gobby/agents/spawn_executor.py +415 -0
- gobby/agents/spawners/__init__.py +24 -0
- gobby/agents/spawners/command_builder.py +189 -0
- gobby/agents/spawners/embedded.py +21 -2
- gobby/agents/spawners/headless.py +21 -2
- gobby/agents/spawners/macos.py +26 -1
- gobby/agents/spawners/prompt_manager.py +125 -0
- gobby/cli/__init__.py +0 -2
- gobby/cli/install.py +4 -4
- gobby/cli/installers/claude.py +6 -0
- gobby/cli/installers/gemini.py +6 -0
- gobby/cli/installers/shared.py +103 -4
- gobby/cli/memory.py +185 -0
- gobby/cli/sessions.py +1 -1
- gobby/cli/utils.py +9 -2
- gobby/clones/git.py +177 -0
- gobby/config/__init__.py +12 -97
- gobby/config/app.py +10 -94
- gobby/config/extensions.py +2 -2
- gobby/config/features.py +7 -130
- gobby/config/skills.py +31 -0
- gobby/config/tasks.py +4 -28
- gobby/hooks/__init__.py +0 -13
- gobby/hooks/event_handlers.py +150 -8
- gobby/hooks/hook_manager.py +21 -3
- gobby/hooks/plugins.py +1 -1
- gobby/hooks/webhooks.py +1 -1
- gobby/install/gemini/hooks/hook_dispatcher.py +74 -15
- gobby/llm/resolver.py +3 -2
- gobby/mcp_proxy/importer.py +62 -4
- gobby/mcp_proxy/instructions.py +4 -2
- gobby/mcp_proxy/registries.py +22 -8
- gobby/mcp_proxy/services/recommendation.py +43 -11
- gobby/mcp_proxy/tools/agent_messaging.py +93 -44
- gobby/mcp_proxy/tools/agents.py +76 -740
- gobby/mcp_proxy/tools/artifacts.py +43 -9
- gobby/mcp_proxy/tools/clones.py +0 -385
- gobby/mcp_proxy/tools/memory.py +2 -2
- gobby/mcp_proxy/tools/sessions/__init__.py +14 -0
- gobby/mcp_proxy/tools/sessions/_commits.py +239 -0
- gobby/mcp_proxy/tools/sessions/_crud.py +253 -0
- gobby/mcp_proxy/tools/sessions/_factory.py +63 -0
- gobby/mcp_proxy/tools/sessions/_handoff.py +503 -0
- gobby/mcp_proxy/tools/sessions/_messages.py +166 -0
- gobby/mcp_proxy/tools/skills/__init__.py +14 -29
- gobby/mcp_proxy/tools/spawn_agent.py +455 -0
- gobby/mcp_proxy/tools/tasks/_context.py +18 -0
- gobby/mcp_proxy/tools/tasks/_crud.py +13 -6
- gobby/mcp_proxy/tools/tasks/_lifecycle.py +79 -30
- gobby/mcp_proxy/tools/tasks/_lifecycle_validation.py +1 -1
- gobby/mcp_proxy/tools/tasks/_session.py +22 -7
- gobby/mcp_proxy/tools/workflows.py +84 -34
- gobby/mcp_proxy/tools/worktrees.py +32 -350
- gobby/memory/extractor.py +15 -1
- gobby/memory/ingestion/__init__.py +5 -0
- gobby/memory/ingestion/multimodal.py +221 -0
- gobby/memory/manager.py +62 -283
- gobby/memory/search/__init__.py +10 -0
- gobby/memory/search/coordinator.py +248 -0
- gobby/memory/services/__init__.py +5 -0
- gobby/memory/services/crossref.py +142 -0
- gobby/prompts/loader.py +5 -2
- gobby/runner.py +13 -0
- gobby/servers/http.py +1 -4
- gobby/servers/routes/admin.py +14 -0
- gobby/servers/routes/mcp/endpoints/__init__.py +61 -0
- gobby/servers/routes/mcp/endpoints/discovery.py +405 -0
- gobby/servers/routes/mcp/endpoints/execution.py +568 -0
- gobby/servers/routes/mcp/endpoints/registry.py +378 -0
- gobby/servers/routes/mcp/endpoints/server.py +304 -0
- gobby/servers/routes/mcp/hooks.py +51 -4
- gobby/servers/routes/mcp/tools.py +48 -1506
- gobby/servers/websocket.py +57 -1
- gobby/sessions/analyzer.py +2 -2
- gobby/sessions/lifecycle.py +1 -1
- gobby/sessions/manager.py +9 -0
- gobby/sessions/processor.py +10 -0
- gobby/sessions/transcripts/base.py +1 -0
- gobby/sessions/transcripts/claude.py +15 -5
- gobby/sessions/transcripts/gemini.py +100 -34
- gobby/skills/parser.py +30 -2
- gobby/storage/database.py +9 -2
- gobby/storage/memories.py +32 -21
- gobby/storage/migrations.py +174 -368
- gobby/storage/sessions.py +45 -7
- gobby/storage/skills.py +80 -7
- gobby/storage/tasks/_lifecycle.py +18 -3
- gobby/sync/memories.py +1 -1
- gobby/tasks/external_validator.py +1 -1
- gobby/tasks/validation.py +22 -20
- gobby/tools/summarizer.py +91 -10
- gobby/utils/project_context.py +2 -3
- gobby/utils/status.py +13 -0
- gobby/workflows/actions.py +221 -1217
- gobby/workflows/artifact_actions.py +31 -0
- gobby/workflows/autonomous_actions.py +11 -0
- gobby/workflows/context_actions.py +50 -1
- gobby/workflows/detection_helpers.py +38 -24
- gobby/workflows/enforcement/__init__.py +47 -0
- gobby/workflows/enforcement/blocking.py +281 -0
- gobby/workflows/enforcement/commit_policy.py +283 -0
- gobby/workflows/enforcement/handlers.py +269 -0
- gobby/workflows/enforcement/task_policy.py +542 -0
- gobby/workflows/engine.py +93 -0
- gobby/workflows/evaluator.py +110 -0
- gobby/workflows/git_utils.py +106 -0
- gobby/workflows/hooks.py +41 -0
- gobby/workflows/llm_actions.py +30 -0
- gobby/workflows/mcp_actions.py +20 -1
- gobby/workflows/memory_actions.py +91 -0
- gobby/workflows/safe_evaluator.py +191 -0
- gobby/workflows/session_actions.py +44 -0
- gobby/workflows/state_actions.py +60 -1
- gobby/workflows/stop_signal_actions.py +55 -0
- gobby/workflows/summary_actions.py +217 -51
- gobby/workflows/task_sync_actions.py +347 -0
- gobby/workflows/todo_actions.py +34 -1
- gobby/workflows/webhook_actions.py +185 -0
- {gobby-0.2.6.dist-info → gobby-0.2.8.dist-info}/METADATA +6 -1
- {gobby-0.2.6.dist-info → gobby-0.2.8.dist-info}/RECORD +139 -163
- {gobby-0.2.6.dist-info → gobby-0.2.8.dist-info}/WHEEL +1 -1
- gobby/adapters/codex.py +0 -1332
- gobby/cli/tui.py +0 -34
- gobby/install/claude/commands/gobby/bug.md +0 -51
- gobby/install/claude/commands/gobby/chore.md +0 -51
- gobby/install/claude/commands/gobby/epic.md +0 -52
- gobby/install/claude/commands/gobby/eval.md +0 -235
- gobby/install/claude/commands/gobby/feat.md +0 -49
- gobby/install/claude/commands/gobby/nit.md +0 -52
- gobby/install/claude/commands/gobby/ref.md +0 -52
- gobby/mcp_proxy/tools/session_messages.py +0 -1055
- gobby/prompts/defaults/expansion/system.md +0 -119
- gobby/prompts/defaults/expansion/user.md +0 -48
- gobby/prompts/defaults/external_validation/agent.md +0 -72
- gobby/prompts/defaults/external_validation/external.md +0 -63
- gobby/prompts/defaults/external_validation/spawn.md +0 -83
- gobby/prompts/defaults/external_validation/system.md +0 -6
- gobby/prompts/defaults/features/import_mcp.md +0 -22
- gobby/prompts/defaults/features/import_mcp_github.md +0 -17
- gobby/prompts/defaults/features/import_mcp_search.md +0 -16
- gobby/prompts/defaults/features/recommend_tools.md +0 -32
- gobby/prompts/defaults/features/recommend_tools_hybrid.md +0 -35
- gobby/prompts/defaults/features/recommend_tools_llm.md +0 -30
- gobby/prompts/defaults/features/server_description.md +0 -20
- gobby/prompts/defaults/features/server_description_system.md +0 -6
- gobby/prompts/defaults/features/task_description.md +0 -31
- gobby/prompts/defaults/features/task_description_system.md +0 -6
- gobby/prompts/defaults/features/tool_summary.md +0 -17
- gobby/prompts/defaults/features/tool_summary_system.md +0 -6
- gobby/prompts/defaults/handoff/compact.md +0 -63
- gobby/prompts/defaults/handoff/session_end.md +0 -57
- gobby/prompts/defaults/memory/extract.md +0 -61
- gobby/prompts/defaults/research/step.md +0 -58
- gobby/prompts/defaults/validation/criteria.md +0 -47
- gobby/prompts/defaults/validation/validate.md +0 -38
- gobby/storage/migrations_legacy.py +0 -1359
- gobby/tui/__init__.py +0 -5
- gobby/tui/api_client.py +0 -278
- gobby/tui/app.py +0 -329
- gobby/tui/screens/__init__.py +0 -25
- gobby/tui/screens/agents.py +0 -333
- gobby/tui/screens/chat.py +0 -450
- gobby/tui/screens/dashboard.py +0 -377
- gobby/tui/screens/memory.py +0 -305
- gobby/tui/screens/metrics.py +0 -231
- gobby/tui/screens/orchestrator.py +0 -903
- gobby/tui/screens/sessions.py +0 -412
- gobby/tui/screens/tasks.py +0 -440
- gobby/tui/screens/workflows.py +0 -289
- gobby/tui/screens/worktrees.py +0 -174
- gobby/tui/widgets/__init__.py +0 -21
- gobby/tui/widgets/chat.py +0 -210
- gobby/tui/widgets/conductor.py +0 -104
- gobby/tui/widgets/menu.py +0 -132
- gobby/tui/widgets/message_panel.py +0 -160
- gobby/tui/widgets/review_gate.py +0 -224
- gobby/tui/widgets/task_tree.py +0 -99
- gobby/tui/widgets/token_budget.py +0 -166
- gobby/tui/ws_client.py +0 -258
- gobby/workflows/task_enforcement_actions.py +0 -1343
- {gobby-0.2.6.dist-info → gobby-0.2.8.dist-info}/entry_points.txt +0 -0
- {gobby-0.2.6.dist-info → gobby-0.2.8.dist-info}/licenses/LICENSE.md +0 -0
- {gobby-0.2.6.dist-info → gobby-0.2.8.dist-info}/top_level.txt +0 -0
|
@@ -113,12 +113,20 @@ def create_workflows_registry(
|
|
|
113
113
|
Returns:
|
|
114
114
|
InternalToolRegistry with workflow tools registered
|
|
115
115
|
"""
|
|
116
|
+
from gobby.utils.project_context import get_project_context
|
|
117
|
+
|
|
116
118
|
# Create defaults if not provided
|
|
117
119
|
_db = db or LocalDatabase()
|
|
118
120
|
_loader = loader or WorkflowLoader()
|
|
119
121
|
_state_manager = state_manager or WorkflowStateManager(_db)
|
|
120
122
|
_session_manager = session_manager or LocalSessionManager(_db)
|
|
121
123
|
|
|
124
|
+
def _resolve_session_id(ref: str) -> str:
|
|
125
|
+
"""Resolve session reference (#N, N, UUID, or prefix) to UUID."""
|
|
126
|
+
project_ctx = get_project_context()
|
|
127
|
+
project_id = project_ctx.get("id") if project_ctx else None
|
|
128
|
+
return _session_manager.resolve_session_reference(ref, project_id)
|
|
129
|
+
|
|
122
130
|
registry = InternalToolRegistry(
|
|
123
131
|
name="gobby-workflows",
|
|
124
132
|
description="Workflow management - list, activate, status, transition, end",
|
|
@@ -264,7 +272,7 @@ def create_workflows_registry(
|
|
|
264
272
|
|
|
265
273
|
@registry.tool(
|
|
266
274
|
name="activate_workflow",
|
|
267
|
-
description="Activate a step-based workflow for the current session.",
|
|
275
|
+
description="Activate a step-based workflow for the current session. Accepts #N, N, UUID, or prefix for session_id.",
|
|
268
276
|
)
|
|
269
277
|
def activate_workflow(
|
|
270
278
|
name: str,
|
|
@@ -278,7 +286,7 @@ def create_workflows_registry(
|
|
|
278
286
|
|
|
279
287
|
Args:
|
|
280
288
|
name: Workflow name (e.g., "plan-act-reflect", "auto-task")
|
|
281
|
-
session_id:
|
|
289
|
+
session_id: Session reference (accepts #N, N, UUID, or prefix) - required to prevent cross-session bleed
|
|
282
290
|
initial_step: Optional starting step (defaults to first step)
|
|
283
291
|
variables: Optional initial variables to set (merged with workflow defaults)
|
|
284
292
|
project_path: Project directory path. Auto-discovered from cwd if not provided.
|
|
@@ -290,7 +298,7 @@ def create_workflows_registry(
|
|
|
290
298
|
activate_workflow(
|
|
291
299
|
name="auto-task",
|
|
292
300
|
variables={"session_task": "#47"},
|
|
293
|
-
session_id="
|
|
301
|
+
session_id="#5"
|
|
294
302
|
)
|
|
295
303
|
|
|
296
304
|
Errors if:
|
|
@@ -325,12 +333,18 @@ def create_workflows_registry(
|
|
|
325
333
|
"error": "session_id is required. Pass the session ID explicitly to prevent cross-session variable bleed.",
|
|
326
334
|
}
|
|
327
335
|
|
|
336
|
+
# Resolve session_id to UUID (accepts #N, N, UUID, or prefix)
|
|
337
|
+
try:
|
|
338
|
+
resolved_session_id = _resolve_session_id(session_id)
|
|
339
|
+
except ValueError as e:
|
|
340
|
+
return {"success": False, "error": str(e)}
|
|
341
|
+
|
|
328
342
|
# Check for existing workflow
|
|
329
343
|
# Allow if:
|
|
330
344
|
# - No existing state
|
|
331
345
|
# - Existing is __lifecycle__ placeholder
|
|
332
346
|
# - Existing is a lifecycle-type workflow (they run concurrently with step workflows)
|
|
333
|
-
existing = _state_manager.get_state(
|
|
347
|
+
existing = _state_manager.get_state(resolved_session_id)
|
|
334
348
|
if existing and existing.workflow_name != "__lifecycle__":
|
|
335
349
|
# Check if existing workflow is a lifecycle type
|
|
336
350
|
existing_def = _loader.load_workflow(existing.workflow_name, proj)
|
|
@@ -366,12 +380,12 @@ def create_workflows_registry(
|
|
|
366
380
|
session_task_val = merged_variables["session_task"]
|
|
367
381
|
if isinstance(session_task_val, str):
|
|
368
382
|
merged_variables["session_task"] = _resolve_session_task_value(
|
|
369
|
-
session_task_val,
|
|
383
|
+
session_task_val, resolved_session_id, _session_manager, _db
|
|
370
384
|
)
|
|
371
385
|
|
|
372
386
|
# Create state
|
|
373
387
|
state = WorkflowState(
|
|
374
|
-
session_id=
|
|
388
|
+
session_id=resolved_session_id,
|
|
375
389
|
workflow_name=name,
|
|
376
390
|
step=step,
|
|
377
391
|
step_entered_at=datetime.now(UTC),
|
|
@@ -391,7 +405,7 @@ def create_workflows_registry(
|
|
|
391
405
|
|
|
392
406
|
return {
|
|
393
407
|
"success": True,
|
|
394
|
-
"session_id":
|
|
408
|
+
"session_id": resolved_session_id,
|
|
395
409
|
"workflow": name,
|
|
396
410
|
"step": step,
|
|
397
411
|
"steps": [s.name for s in definition.steps],
|
|
@@ -400,7 +414,7 @@ def create_workflows_registry(
|
|
|
400
414
|
|
|
401
415
|
@registry.tool(
|
|
402
416
|
name="end_workflow",
|
|
403
|
-
description="End the currently active step-based workflow.",
|
|
417
|
+
description="End the currently active step-based workflow. Accepts #N, N, UUID, or prefix for session_id.",
|
|
404
418
|
)
|
|
405
419
|
def end_workflow(
|
|
406
420
|
session_id: str | None = None,
|
|
@@ -413,7 +427,7 @@ def create_workflows_registry(
|
|
|
413
427
|
Does not affect lifecycle workflows (they continue running).
|
|
414
428
|
|
|
415
429
|
Args:
|
|
416
|
-
session_id:
|
|
430
|
+
session_id: Session reference (accepts #N, N, UUID, or prefix) - required to prevent cross-session bleed
|
|
417
431
|
reason: Optional reason for ending
|
|
418
432
|
|
|
419
433
|
Returns:
|
|
@@ -426,24 +440,30 @@ def create_workflows_registry(
|
|
|
426
440
|
"error": "session_id is required. Pass the session ID explicitly to prevent cross-session variable bleed.",
|
|
427
441
|
}
|
|
428
442
|
|
|
429
|
-
|
|
443
|
+
# Resolve session_id to UUID (accepts #N, N, UUID, or prefix)
|
|
444
|
+
try:
|
|
445
|
+
resolved_session_id = _resolve_session_id(session_id)
|
|
446
|
+
except ValueError as e:
|
|
447
|
+
return {"success": False, "error": str(e)}
|
|
448
|
+
|
|
449
|
+
state = _state_manager.get_state(resolved_session_id)
|
|
430
450
|
if not state:
|
|
431
451
|
return {"error": "No workflow active for session"}
|
|
432
452
|
|
|
433
|
-
_state_manager.delete_state(
|
|
453
|
+
_state_manager.delete_state(resolved_session_id)
|
|
434
454
|
|
|
435
455
|
return {}
|
|
436
456
|
|
|
437
457
|
@registry.tool(
|
|
438
458
|
name="get_workflow_status",
|
|
439
|
-
description="Get current workflow step and state.",
|
|
459
|
+
description="Get current workflow step and state. Accepts #N, N, UUID, or prefix for session_id.",
|
|
440
460
|
)
|
|
441
461
|
def get_workflow_status(session_id: str | None = None) -> dict[str, Any]:
|
|
442
462
|
"""
|
|
443
463
|
Get current workflow step and state.
|
|
444
464
|
|
|
445
465
|
Args:
|
|
446
|
-
session_id:
|
|
466
|
+
session_id: Session reference (accepts #N, N, UUID, or prefix) - required to prevent cross-session bleed
|
|
447
467
|
|
|
448
468
|
Returns:
|
|
449
469
|
Workflow state including step, action counts, artifacts
|
|
@@ -455,13 +475,19 @@ def create_workflows_registry(
|
|
|
455
475
|
"error": "session_id is required. Pass the session ID explicitly to prevent cross-session variable bleed.",
|
|
456
476
|
}
|
|
457
477
|
|
|
458
|
-
|
|
478
|
+
# Resolve session_id to UUID (accepts #N, N, UUID, or prefix)
|
|
479
|
+
try:
|
|
480
|
+
resolved_session_id = _resolve_session_id(session_id)
|
|
481
|
+
except ValueError as e:
|
|
482
|
+
return {"has_workflow": False, "error": str(e)}
|
|
483
|
+
|
|
484
|
+
state = _state_manager.get_state(resolved_session_id)
|
|
459
485
|
if not state:
|
|
460
|
-
return {"has_workflow": False, "session_id":
|
|
486
|
+
return {"has_workflow": False, "session_id": resolved_session_id}
|
|
461
487
|
|
|
462
488
|
return {
|
|
463
489
|
"has_workflow": True,
|
|
464
|
-
"session_id":
|
|
490
|
+
"session_id": resolved_session_id,
|
|
465
491
|
"workflow_name": state.workflow_name,
|
|
466
492
|
"step": state.step,
|
|
467
493
|
"step_action_count": state.step_action_count,
|
|
@@ -479,7 +505,7 @@ def create_workflows_registry(
|
|
|
479
505
|
|
|
480
506
|
@registry.tool(
|
|
481
507
|
name="request_step_transition",
|
|
482
|
-
description="Request transition to a different step.",
|
|
508
|
+
description="Request transition to a different step. Accepts #N, N, UUID, or prefix for session_id.",
|
|
483
509
|
)
|
|
484
510
|
def request_step_transition(
|
|
485
511
|
to_step: str,
|
|
@@ -494,7 +520,7 @@ def create_workflows_registry(
|
|
|
494
520
|
Args:
|
|
495
521
|
to_step: Target step name
|
|
496
522
|
reason: Reason for transition
|
|
497
|
-
session_id:
|
|
523
|
+
session_id: Session reference (accepts #N, N, UUID, or prefix) - required to prevent cross-session bleed
|
|
498
524
|
force: Skip exit condition checks
|
|
499
525
|
project_path: Project directory path. Auto-discovered from cwd if not provided.
|
|
500
526
|
|
|
@@ -516,7 +542,13 @@ def create_workflows_registry(
|
|
|
516
542
|
"error": "session_id is required. Pass the session ID explicitly to prevent cross-session variable bleed.",
|
|
517
543
|
}
|
|
518
544
|
|
|
519
|
-
|
|
545
|
+
# Resolve session_id to UUID (accepts #N, N, UUID, or prefix)
|
|
546
|
+
try:
|
|
547
|
+
resolved_session_id = _resolve_session_id(session_id)
|
|
548
|
+
except ValueError as e:
|
|
549
|
+
return {"success": False, "error": str(e)}
|
|
550
|
+
|
|
551
|
+
state = _state_manager.get_state(resolved_session_id)
|
|
520
552
|
if not state:
|
|
521
553
|
return {"success": False, "error": "No workflow active for session"}
|
|
522
554
|
|
|
@@ -565,7 +597,7 @@ def create_workflows_registry(
|
|
|
565
597
|
|
|
566
598
|
@registry.tool(
|
|
567
599
|
name="mark_artifact_complete",
|
|
568
|
-
description="Register an artifact as complete (plan, spec, etc.).",
|
|
600
|
+
description="Register an artifact as complete (plan, spec, etc.). Accepts #N, N, UUID, or prefix for session_id.",
|
|
569
601
|
)
|
|
570
602
|
def mark_artifact_complete(
|
|
571
603
|
artifact_type: str,
|
|
@@ -578,7 +610,7 @@ def create_workflows_registry(
|
|
|
578
610
|
Args:
|
|
579
611
|
artifact_type: Type of artifact (e.g., "plan", "spec", "test")
|
|
580
612
|
file_path: Path to the artifact file
|
|
581
|
-
session_id:
|
|
613
|
+
session_id: Session reference (accepts #N, N, UUID, or prefix) - required to prevent cross-session bleed
|
|
582
614
|
|
|
583
615
|
Returns:
|
|
584
616
|
Success status
|
|
@@ -590,7 +622,13 @@ def create_workflows_registry(
|
|
|
590
622
|
"error": "session_id is required. Pass the session ID explicitly to prevent cross-session variable bleed.",
|
|
591
623
|
}
|
|
592
624
|
|
|
593
|
-
|
|
625
|
+
# Resolve session_id to UUID (accepts #N, N, UUID, or prefix)
|
|
626
|
+
try:
|
|
627
|
+
resolved_session_id = _resolve_session_id(session_id)
|
|
628
|
+
except ValueError as e:
|
|
629
|
+
return {"success": False, "error": str(e)}
|
|
630
|
+
|
|
631
|
+
state = _state_manager.get_state(resolved_session_id)
|
|
594
632
|
if not state:
|
|
595
633
|
return {"error": "No workflow active for session"}
|
|
596
634
|
|
|
@@ -602,7 +640,7 @@ def create_workflows_registry(
|
|
|
602
640
|
|
|
603
641
|
@registry.tool(
|
|
604
642
|
name="set_variable",
|
|
605
|
-
description="Set a workflow variable for the current session (session-scoped, not persisted to YAML).",
|
|
643
|
+
description="Set a workflow variable for the current session (session-scoped, not persisted to YAML). Accepts #N, N, UUID, or prefix for session_id.",
|
|
606
644
|
)
|
|
607
645
|
def set_variable(
|
|
608
646
|
name: str,
|
|
@@ -623,7 +661,7 @@ def create_workflows_registry(
|
|
|
623
661
|
Args:
|
|
624
662
|
name: Variable name (e.g., "session_epic", "is_worktree")
|
|
625
663
|
value: Variable value (string, number, boolean, or null)
|
|
626
|
-
session_id:
|
|
664
|
+
session_id: Session reference (accepts #N, N, UUID, or prefix) - required to prevent cross-session bleed
|
|
627
665
|
|
|
628
666
|
Returns:
|
|
629
667
|
Success status and updated variables
|
|
@@ -635,12 +673,18 @@ def create_workflows_registry(
|
|
|
635
673
|
"error": "session_id is required. Pass the session ID explicitly to prevent cross-session variable bleed.",
|
|
636
674
|
}
|
|
637
675
|
|
|
676
|
+
# Resolve session_id to UUID (accepts #N, N, UUID, or prefix)
|
|
677
|
+
try:
|
|
678
|
+
resolved_session_id = _resolve_session_id(session_id)
|
|
679
|
+
except ValueError as e:
|
|
680
|
+
return {"success": False, "error": str(e)}
|
|
681
|
+
|
|
638
682
|
# Get or create state
|
|
639
|
-
state = _state_manager.get_state(
|
|
683
|
+
state = _state_manager.get_state(resolved_session_id)
|
|
640
684
|
if not state:
|
|
641
685
|
# Create a minimal lifecycle state for variable storage
|
|
642
686
|
state = WorkflowState(
|
|
643
|
-
session_id=
|
|
687
|
+
session_id=resolved_session_id,
|
|
644
688
|
workflow_name="__lifecycle__",
|
|
645
689
|
step="",
|
|
646
690
|
step_entered_at=datetime.now(UTC),
|
|
@@ -664,7 +708,7 @@ def create_workflows_registry(
|
|
|
664
708
|
# Resolve session_task references (#N or N) to UUIDs upfront
|
|
665
709
|
# This prevents repeated resolution failures in condition evaluation
|
|
666
710
|
if name == "session_task" and isinstance(value, str):
|
|
667
|
-
value = _resolve_session_task_value(value,
|
|
711
|
+
value = _resolve_session_task_value(value, resolved_session_id, _session_manager, _db)
|
|
668
712
|
|
|
669
713
|
# Set the variable
|
|
670
714
|
state.variables[name] = value
|
|
@@ -684,7 +728,7 @@ def create_workflows_registry(
|
|
|
684
728
|
|
|
685
729
|
@registry.tool(
|
|
686
730
|
name="get_variable",
|
|
687
|
-
description="Get workflow variable(s) for the current session.",
|
|
731
|
+
description="Get workflow variable(s) for the current session. Accepts #N, N, UUID, or prefix for session_id.",
|
|
688
732
|
)
|
|
689
733
|
def get_variable(
|
|
690
734
|
name: str | None = None,
|
|
@@ -695,7 +739,7 @@ def create_workflows_registry(
|
|
|
695
739
|
|
|
696
740
|
Args:
|
|
697
741
|
name: Variable name to get (if None, returns all variables)
|
|
698
|
-
session_id:
|
|
742
|
+
session_id: Session reference (accepts #N, N, UUID, or prefix) - required to prevent cross-session bleed
|
|
699
743
|
|
|
700
744
|
Returns:
|
|
701
745
|
Variable value(s) and session info
|
|
@@ -707,19 +751,25 @@ def create_workflows_registry(
|
|
|
707
751
|
"error": "session_id is required. Pass the session ID explicitly to prevent cross-session variable bleed.",
|
|
708
752
|
}
|
|
709
753
|
|
|
710
|
-
|
|
754
|
+
# Resolve session_id to UUID (accepts #N, N, UUID, or prefix)
|
|
755
|
+
try:
|
|
756
|
+
resolved_session_id = _resolve_session_id(session_id)
|
|
757
|
+
except ValueError as e:
|
|
758
|
+
return {"success": False, "error": str(e)}
|
|
759
|
+
|
|
760
|
+
state = _state_manager.get_state(resolved_session_id)
|
|
711
761
|
if not state:
|
|
712
762
|
if name:
|
|
713
763
|
return {
|
|
714
764
|
"success": True,
|
|
715
|
-
"session_id":
|
|
765
|
+
"session_id": resolved_session_id,
|
|
716
766
|
"variable": name,
|
|
717
767
|
"value": None,
|
|
718
768
|
"exists": False,
|
|
719
769
|
}
|
|
720
770
|
return {
|
|
721
771
|
"success": True,
|
|
722
|
-
"session_id":
|
|
772
|
+
"session_id": resolved_session_id,
|
|
723
773
|
"variables": {},
|
|
724
774
|
}
|
|
725
775
|
|
|
@@ -727,7 +777,7 @@ def create_workflows_registry(
|
|
|
727
777
|
value = state.variables.get(name)
|
|
728
778
|
return {
|
|
729
779
|
"success": True,
|
|
730
|
-
"session_id":
|
|
780
|
+
"session_id": resolved_session_id,
|
|
731
781
|
"variable": name,
|
|
732
782
|
"value": value,
|
|
733
783
|
"exists": name in state.variables,
|
|
@@ -735,7 +785,7 @@ def create_workflows_registry(
|
|
|
735
785
|
|
|
736
786
|
return {
|
|
737
787
|
"success": True,
|
|
738
|
-
"session_id":
|
|
788
|
+
"session_id": resolved_session_id,
|
|
739
789
|
"variables": state.variables,
|
|
740
790
|
}
|
|
741
791
|
|