minder-cli 0.6.2__tar.gz → 0.6.3__tar.gz
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.
- {minder_cli-0.6.2 → minder_cli-0.6.3}/PKG-INFO +1 -1
- {minder_cli-0.6.2 → minder_cli-0.6.3}/pyproject.toml +1 -1
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/bootstrap/transport.py +32 -12
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/models/repository.py +2 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/models/skill.py +9 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/store/interfaces.py +2 -1
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/store/qdrant/operational_store.py +16 -3
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/store/relational.py +30 -2
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/store/repo_state.py +23 -12
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/tools/memory.py +39 -4
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/tools/workflow.py +16 -9
- {minder_cli-0.6.2 → minder_cli-0.6.3}/.gitignore +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/LICENSE +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/README-pypi.md +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/README.md +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/api/routers/prompts.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/application/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/application/admin/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/application/admin/dto.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/application/admin/jobs.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/application/admin/use_cases.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/auth/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/auth/context.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/auth/middleware.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/auth/principal.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/auth/rate_limiter.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/auth/rbac.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/auth/service.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/bootstrap/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/bootstrap/agent_seeder.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/bootstrap/providers.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/bootstrap/workflow_seeder.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/cache/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/cache/providers.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/chunking/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/chunking/code_splitter.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/chunking/splitter.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/cli.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/config.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/context_compactor.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/continuity.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/dev.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/embedding/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/embedding/base.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/embedding/local.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/embedding/openai.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/checkpoint.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/edges.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/executor.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/graph.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/memory_graph.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/nodes/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/nodes/clarification.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/nodes/evaluator.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/nodes/guard.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/nodes/llm.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/nodes/parallel_retriever.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/nodes/planning.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/nodes/reasoning.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/nodes/reflection.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/nodes/reranker.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/nodes/retriever.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/nodes/verification.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/nodes/workflow_planner.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/runtime.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/session_graph.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/state.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/graph/supervisor.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/learning/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/learning/error_learner.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/learning/pattern_extractor.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/learning/quality_optimizer.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/learning/skill_synthesizer.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/llm/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/llm/base.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/llm/factory.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/llm/llama_cpp_llm.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/llm/openai.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/model_bootstrap.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/models/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/models/agent.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/models/base.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/models/checkpoint.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/models/client.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/models/document.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/models/error.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/models/graph.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/models/history.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/models/job.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/models/prompt.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/models/rule.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/models/session.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/models/user.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/models/workflow.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/observability/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/observability/audit.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/observability/logging.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/observability/metrics.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/observability/tracing.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/cli/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/cli/commands/agent.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/cli/commands/auth.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/cli/commands/mcp.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/cli/commands/sync.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/cli/commands/update.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/cli/main.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/cli/utils/common.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/cli/utils/config.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/cli/utils/git.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/cli/utils/version.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/http/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/http/admin/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/http/admin/agents.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/http/admin/api.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/http/admin/context.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/http/admin/dashboard.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/http/admin/jobs.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/http/admin/memories.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/http/admin/prompts.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/http/admin/routes.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/http/admin/runtime.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/http/admin/search.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/presentation/http/admin/skills.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/prompts/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/prompts/formatter.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/resources/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/retrieval/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/retrieval/hybrid.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/retrieval/mmr.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/retrieval/multi_hop.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/runtime.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/server.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/store/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/store/document.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/store/error.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/store/feedback.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/store/graph.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/store/history.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/store/qdrant/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/store/qdrant/client.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/store/qdrant/crud.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/store/qdrant/graph_store.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/store/qdrant/vector_store.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/store/rule.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/store/vector.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/tools/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/tools/agents.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/tools/auth.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/tools/graph.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/tools/ingest.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/tools/query.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/tools/registry.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/tools/repo_scanner.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/tools/seeds/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/tools/seeds/default_agents.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/tools/session.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/tools/skills.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/transport/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/transport/base.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/transport/sse.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/transport/stdio.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.3}/src/minder/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: minder-cli
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.3
|
|
4
4
|
Summary: Minder CLI is the command-line interface for the Minder self-hosted MCP platform.
|
|
5
5
|
Project-URL: Homepage, https://github.com/hiimtrung/minder
|
|
6
6
|
Project-URL: Repository, https://github.com/hiimtrung/minder
|
|
@@ -315,12 +315,13 @@ def build_transport(
|
|
|
315
315
|
return await session_tools.minder_session_cleanup(user_id=authenticated_user.id)
|
|
316
316
|
|
|
317
317
|
async def minder_workflow_get(
|
|
318
|
-
*, user=None, repo_id: str, repo_path: str
|
|
318
|
+
*, user=None, repo_id: str, repo_path: str, branch: str = "main"
|
|
319
319
|
) -> dict[str, Any]: # noqa: ANN001
|
|
320
320
|
del user
|
|
321
321
|
return await workflow_tools.minder_workflow_get(
|
|
322
322
|
repo_id=await _resolve_repo_uuid(repo_id),
|
|
323
323
|
repo_path=repo_path,
|
|
324
|
+
branch=branch,
|
|
324
325
|
)
|
|
325
326
|
|
|
326
327
|
async def minder_workflow_step(
|
|
@@ -330,6 +331,7 @@ def build_transport(
|
|
|
330
331
|
repo_path: str | None = None,
|
|
331
332
|
session_id: str | None = None,
|
|
332
333
|
decision: dict[str, Any] | None = None,
|
|
334
|
+
branch: str = "main",
|
|
333
335
|
) -> dict[str, Any]: # noqa: ANN001
|
|
334
336
|
del user
|
|
335
337
|
return await workflow_tools.minder_workflow_step(
|
|
@@ -337,6 +339,7 @@ def build_transport(
|
|
|
337
339
|
repo_path=repo_path,
|
|
338
340
|
session_id=uuid.UUID(session_id) if session_id else None,
|
|
339
341
|
decision=decision,
|
|
342
|
+
branch=branch,
|
|
340
343
|
)
|
|
341
344
|
|
|
342
345
|
async def minder_workflow_update(
|
|
@@ -347,6 +350,7 @@ def build_transport(
|
|
|
347
350
|
completed_step: str,
|
|
348
351
|
artifact_name: str | None = None,
|
|
349
352
|
artifact_content: str | None = None,
|
|
353
|
+
branch: str = "main",
|
|
350
354
|
) -> dict[str, Any]: # noqa: ANN001
|
|
351
355
|
del user
|
|
352
356
|
return await workflow_tools.minder_workflow_update(
|
|
@@ -355,88 +359,104 @@ def build_transport(
|
|
|
355
359
|
completed_step=completed_step,
|
|
356
360
|
artifact_name=artifact_name,
|
|
357
361
|
artifact_content=artifact_content,
|
|
362
|
+
branch=branch,
|
|
358
363
|
)
|
|
359
364
|
|
|
360
365
|
async def minder_workflow_guard(
|
|
361
|
-
*, user=None, repo_id: str, requested_step: str, action: str | None = None
|
|
366
|
+
*, user=None, repo_id: str, requested_step: str, action: str | None = None, branch: str = "main"
|
|
362
367
|
) -> dict[str, Any]: # noqa: ANN001
|
|
363
368
|
del user
|
|
364
369
|
return await workflow_tools.minder_workflow_guard(
|
|
365
370
|
repo_id=await _resolve_repo_uuid(repo_id),
|
|
366
371
|
requested_step=requested_step,
|
|
367
372
|
action=action,
|
|
373
|
+
branch=branch,
|
|
368
374
|
)
|
|
369
375
|
|
|
370
376
|
async def minder_memory_store(
|
|
371
377
|
*,
|
|
372
378
|
user=None,
|
|
379
|
+
principal: Principal | None = None,
|
|
373
380
|
title: str,
|
|
374
381
|
content: str,
|
|
375
382
|
tags: list[str],
|
|
376
383
|
language: str, # noqa: ANN001
|
|
384
|
+
scope: str = "private",
|
|
377
385
|
) -> dict[str, Any]:
|
|
378
|
-
|
|
386
|
+
owner_id = principal.principal_id if principal else (user.id if user else None)
|
|
379
387
|
return await memory_tools.minder_memory_store(
|
|
380
388
|
title=title,
|
|
381
389
|
content=content,
|
|
382
390
|
tags=tags,
|
|
383
391
|
language=language,
|
|
392
|
+
owner_id=owner_id,
|
|
393
|
+
scope=scope,
|
|
384
394
|
)
|
|
385
395
|
|
|
386
396
|
async def minder_memory_recall(
|
|
387
397
|
*,
|
|
388
398
|
user=None,
|
|
399
|
+
principal: Principal | None = None,
|
|
389
400
|
query: str,
|
|
390
401
|
limit: int = 5,
|
|
391
402
|
current_step: str | None = None,
|
|
392
403
|
artifact_type: str | None = None,
|
|
393
404
|
) -> list[dict[str, Any]]: # noqa: ANN001
|
|
394
|
-
|
|
405
|
+
owner_id = principal.principal_id if principal else (user.id if user else None)
|
|
395
406
|
return await memory_tools.minder_memory_recall(
|
|
396
407
|
query,
|
|
397
408
|
limit=limit,
|
|
398
409
|
current_step=current_step,
|
|
399
410
|
artifact_type=artifact_type,
|
|
411
|
+
owner_id=owner_id,
|
|
400
412
|
)
|
|
401
413
|
|
|
402
|
-
async def minder_memory_list(
|
|
403
|
-
|
|
404
|
-
|
|
414
|
+
async def minder_memory_list(
|
|
415
|
+
*,
|
|
416
|
+
user=None,
|
|
417
|
+
principal: Principal | None = None,
|
|
418
|
+
) -> list[dict[str, Any]]: # noqa: ANN001
|
|
419
|
+
owner_id = principal.principal_id if principal else (user.id if user else None)
|
|
420
|
+
return await memory_tools.minder_memory_list(owner_id=owner_id)
|
|
405
421
|
|
|
406
422
|
async def minder_memory_update(
|
|
407
423
|
*,
|
|
408
424
|
user=None,
|
|
425
|
+
principal: Principal | None = None,
|
|
409
426
|
memory_id: str,
|
|
410
427
|
title: str | None = None,
|
|
411
428
|
content: str | None = None,
|
|
412
429
|
tags: list[str] | None = None,
|
|
413
430
|
) -> dict[str, Any]: # noqa: ANN001
|
|
414
|
-
|
|
431
|
+
owner_id = principal.principal_id if principal else (user.id if user else None)
|
|
415
432
|
return await memory_tools.minder_memory_update(
|
|
416
433
|
memory_id,
|
|
417
434
|
title=title,
|
|
418
435
|
content=content,
|
|
419
436
|
tags=tags,
|
|
437
|
+
owner_id=owner_id,
|
|
420
438
|
)
|
|
421
439
|
|
|
422
440
|
async def minder_memory_delete(
|
|
423
|
-
*, user=None, skill_id: str
|
|
441
|
+
*, user=None, principal: Principal | None = None, skill_id: str
|
|
424
442
|
) -> dict[str, bool]: # noqa: ANN001
|
|
425
|
-
|
|
426
|
-
return await memory_tools.minder_memory_delete(skill_id)
|
|
443
|
+
owner_id = principal.principal_id if principal else (user.id if user else None)
|
|
444
|
+
return await memory_tools.minder_memory_delete(skill_id, owner_id=owner_id)
|
|
427
445
|
|
|
428
446
|
async def minder_memory_compact(
|
|
429
447
|
*,
|
|
430
448
|
user=None,
|
|
449
|
+
principal: Principal | None = None,
|
|
431
450
|
memory_ids: list[str],
|
|
432
451
|
similarity_threshold: float = 0.92,
|
|
433
452
|
dry_run: bool = True,
|
|
434
453
|
) -> dict[str, Any]: # noqa: ANN001
|
|
435
|
-
|
|
454
|
+
owner_id = principal.principal_id if principal else (user.id if user else None)
|
|
436
455
|
return await memory_tools.minder_memory_compact(
|
|
437
456
|
memory_ids=memory_ids,
|
|
438
457
|
similarity_threshold=similarity_threshold,
|
|
439
458
|
dry_run=dry_run,
|
|
459
|
+
owner_id=owner_id,
|
|
440
460
|
)
|
|
441
461
|
|
|
442
462
|
async def minder_skill_store(
|
|
@@ -40,6 +40,7 @@ class Repository(Base):
|
|
|
40
40
|
class RepositoryWorkflowStateSchema(BaseModelMeta):
|
|
41
41
|
id: uuid.UUID = Field(default_factory=uuid.uuid4)
|
|
42
42
|
repo_id: uuid.UUID
|
|
43
|
+
branch: str = "main"
|
|
43
44
|
session_id: Optional[uuid.UUID] = None
|
|
44
45
|
current_step: str
|
|
45
46
|
completed_steps: List[str] = Field(default_factory=list)
|
|
@@ -53,6 +54,7 @@ class RepositoryWorkflowState(Base):
|
|
|
53
54
|
|
|
54
55
|
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
55
56
|
repo_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), index=True)
|
|
57
|
+
branch: Mapped[str] = mapped_column(String, index=True, default="main", server_default="main")
|
|
56
58
|
session_id: Mapped[Optional[uuid.UUID]] = mapped_column(UUID(as_uuid=True), nullable=True, index=True)
|
|
57
59
|
current_step: Mapped[str] = mapped_column(String)
|
|
58
60
|
completed_steps: Mapped[Dict[str, Any]] = mapped_column(JSON, default=list) # stored as JSON list
|
|
@@ -21,6 +21,8 @@ class SkillSchema(BaseModelMeta):
|
|
|
21
21
|
deprecated: bool = False
|
|
22
22
|
source_metadata: Optional[Dict[str, Any]] = None
|
|
23
23
|
excerpt_kind: str = "none"
|
|
24
|
+
owner_id: Optional[uuid.UUID] = None # principal who created this entry (None = team/legacy)
|
|
25
|
+
scope: str = "private" # 'private' = owner-only, 'team' = visible to all principals
|
|
24
26
|
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
25
27
|
updated_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
26
28
|
|
|
@@ -46,6 +48,13 @@ class Skill(Base):
|
|
|
46
48
|
JSON, nullable=True
|
|
47
49
|
)
|
|
48
50
|
excerpt_kind: Mapped[str] = mapped_column(String, default="none")
|
|
51
|
+
# Multi-developer isolation: owner_id is the principal who created this entry.
|
|
52
|
+
# None means team/legacy (visible to all). Indexed for efficient filtering.
|
|
53
|
+
owner_id: Mapped[Optional[uuid.UUID]] = mapped_column(
|
|
54
|
+
UUID(as_uuid=True), nullable=True, index=True
|
|
55
|
+
)
|
|
56
|
+
# 'private' = only visible to owner, 'team' = visible to all principals
|
|
57
|
+
scope: Mapped[str] = mapped_column(String, default="private", server_default="private")
|
|
49
58
|
created_at: Mapped[datetime] = mapped_column(
|
|
50
59
|
DateTime(timezone=True), server_default=func.now()
|
|
51
60
|
)
|
|
@@ -64,6 +64,7 @@ class ISkillRepository(Protocol):
|
|
|
64
64
|
*,
|
|
65
65
|
is_memory: bool,
|
|
66
66
|
exclude_deprecated: bool = True,
|
|
67
|
+
owner_id: uuid.UUID | None = None,
|
|
67
68
|
) -> list[Any]: ...
|
|
68
69
|
async def update_skill(self, skill_id: uuid.UUID, **kwargs: Any) -> Any | None: ...
|
|
69
70
|
async def delete_skill(self, skill_id: uuid.UUID) -> None: ...
|
|
@@ -144,7 +145,7 @@ class IRepositoryRepo(Protocol):
|
|
|
144
145
|
class IWorkflowStateRepository(Protocol):
|
|
145
146
|
async def create_workflow_state(self, **kwargs: Any) -> Any: ...
|
|
146
147
|
async def get_workflow_state_by_id(self, state_id: uuid.UUID) -> Any | None: ...
|
|
147
|
-
async def get_workflow_state_by_repo(self, repo_id: uuid.UUID) -> Any | None: ...
|
|
148
|
+
async def get_workflow_state_by_repo(self, repo_id: uuid.UUID, *, branch: str = "main") -> Any | None: ...
|
|
148
149
|
async def update_workflow_state(
|
|
149
150
|
self, state_id: uuid.UUID, **kwargs: Any
|
|
150
151
|
) -> Any | None: ...
|
|
@@ -131,7 +131,7 @@ class QdrantOperationalStore:
|
|
|
131
131
|
return await self._col("skills").find_many()
|
|
132
132
|
|
|
133
133
|
async def list_skills_by_kind(
|
|
134
|
-
self, *, is_memory: bool, exclude_deprecated: bool = True
|
|
134
|
+
self, *, is_memory: bool, exclude_deprecated: bool = True, owner_id: uuid.UUID | None = None
|
|
135
135
|
) -> list[Any]:
|
|
136
136
|
all_skills = await self.list_skills()
|
|
137
137
|
_mem_langs = {"markdown", "text", "en", "vi", "", None}
|
|
@@ -140,6 +140,14 @@ class QdrantOperationalStore:
|
|
|
140
140
|
sm = s._data.get("source_metadata")
|
|
141
141
|
lang = s._data.get("language")
|
|
142
142
|
is_mem = sm is None and lang in _mem_langs
|
|
143
|
+
|
|
144
|
+
# Apply owner filtering
|
|
145
|
+
if owner_id is not None:
|
|
146
|
+
s_owner = s._data.get("owner_id")
|
|
147
|
+
s_scope = s._data.get("scope", "team")
|
|
148
|
+
if s_owner is not None and s_owner != str(owner_id) and s_scope != "team":
|
|
149
|
+
continue
|
|
150
|
+
|
|
143
151
|
if is_memory and is_mem:
|
|
144
152
|
result.append(s)
|
|
145
153
|
elif not is_memory and not is_mem:
|
|
@@ -326,6 +334,7 @@ class QdrantOperationalStore:
|
|
|
326
334
|
for uf in ("repo_id", "session_id"):
|
|
327
335
|
if uf in kw and isinstance(kw[uf], uuid.UUID):
|
|
328
336
|
kw[uf] = _uid(kw[uf])
|
|
337
|
+
kw.setdefault("branch", "main")
|
|
329
338
|
kw.setdefault("completed_steps", [])
|
|
330
339
|
kw.setdefault("blocked_by", [])
|
|
331
340
|
kw.setdefault("artifacts", {})
|
|
@@ -335,8 +344,12 @@ class QdrantOperationalStore:
|
|
|
335
344
|
async def get_workflow_state_by_id(self, state_id: uuid.UUID) -> Any:
|
|
336
345
|
return await self._col("workflow_states").get(_uid(state_id))
|
|
337
346
|
|
|
338
|
-
async def get_workflow_state_by_repo(self, repo_id: uuid.UUID) -> Any:
|
|
339
|
-
|
|
347
|
+
async def get_workflow_state_by_repo(self, repo_id: uuid.UUID, *, branch: str = "main") -> Any:
|
|
348
|
+
states = await self._col("workflow_states").find_many({"repo_id": _uid(repo_id)})
|
|
349
|
+
for state in states:
|
|
350
|
+
if state._data.get("branch", "main") == branch:
|
|
351
|
+
return state
|
|
352
|
+
return None
|
|
340
353
|
|
|
341
354
|
async def update_workflow_state(self, state_id: uuid.UUID, **kw: Any) -> Any:
|
|
342
355
|
if not kw:
|
|
@@ -125,6 +125,22 @@ class RelationalStore:
|
|
|
125
125
|
sync_conn.execute(
|
|
126
126
|
text("ALTER TABLE skills ADD COLUMN deprecated BOOLEAN NOT NULL DEFAULT 0")
|
|
127
127
|
)
|
|
128
|
+
# skills.owner_id + skills.scope (multi-developer isolation)
|
|
129
|
+
if "owner_id" not in existing:
|
|
130
|
+
sync_conn.execute(
|
|
131
|
+
text("ALTER TABLE skills ADD COLUMN owner_id VARCHAR(36) DEFAULT NULL")
|
|
132
|
+
)
|
|
133
|
+
if "scope" not in existing:
|
|
134
|
+
sync_conn.execute(
|
|
135
|
+
text("ALTER TABLE skills ADD COLUMN scope VARCHAR(10) NOT NULL DEFAULT 'team'")
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
if "repository_workflow_states" in inspector.get_table_names():
|
|
139
|
+
existing = {col["name"] for col in inspector.get_columns("repository_workflow_states")}
|
|
140
|
+
if "branch" not in existing:
|
|
141
|
+
sync_conn.execute(
|
|
142
|
+
text("ALTER TABLE repository_workflow_states ADD COLUMN branch VARCHAR(255) NOT NULL DEFAULT 'main'")
|
|
143
|
+
)
|
|
128
144
|
|
|
129
145
|
async def dispose(self) -> None:
|
|
130
146
|
"""Dispose the engine connection pool."""
|
|
@@ -266,6 +282,7 @@ class RelationalStore:
|
|
|
266
282
|
*,
|
|
267
283
|
is_memory: bool,
|
|
268
284
|
exclude_deprecated: bool = True,
|
|
285
|
+
owner_id: uuid.UUID | None = None,
|
|
269
286
|
) -> List[Skill]:
|
|
270
287
|
_memory_langs = ["markdown", "text", "en", "vi", ""]
|
|
271
288
|
_is_memory_cond = Skill.source_metadata.is_(None) & (
|
|
@@ -278,6 +295,16 @@ class RelationalStore:
|
|
|
278
295
|
stmt = select(Skill).where(~_is_memory_cond)
|
|
279
296
|
if exclude_deprecated:
|
|
280
297
|
stmt = stmt.where(Skill.deprecated.isnot(True))
|
|
298
|
+
|
|
299
|
+
if owner_id is not None:
|
|
300
|
+
# Can see their own private memories + team scope memories + legacy memories
|
|
301
|
+
stmt = stmt.where(
|
|
302
|
+
or_(
|
|
303
|
+
Skill.owner_id == owner_id,
|
|
304
|
+
Skill.scope == "team",
|
|
305
|
+
Skill.owner_id.is_(None)
|
|
306
|
+
)
|
|
307
|
+
)
|
|
281
308
|
result = await sess.execute(stmt)
|
|
282
309
|
return list(result.scalars().all())
|
|
283
310
|
|
|
@@ -775,12 +802,13 @@ class RelationalStore:
|
|
|
775
802
|
return result.scalar_one_or_none()
|
|
776
803
|
|
|
777
804
|
async def get_workflow_state_by_repo(
|
|
778
|
-
self, repo_id: uuid.UUID
|
|
805
|
+
self, repo_id: uuid.UUID, *, branch: str = "main"
|
|
779
806
|
) -> Optional[RepositoryWorkflowState]:
|
|
780
807
|
async with self._session() as sess:
|
|
781
808
|
result = await sess.execute(
|
|
782
809
|
select(RepositoryWorkflowState).where(
|
|
783
|
-
RepositoryWorkflowState.repo_id == repo_id
|
|
810
|
+
RepositoryWorkflowState.repo_id == repo_id,
|
|
811
|
+
RepositoryWorkflowState.branch == branch
|
|
784
812
|
)
|
|
785
813
|
)
|
|
786
814
|
return result.scalar_one_or_none()
|
|
@@ -9,8 +9,8 @@ class RepoStateStore:
|
|
|
9
9
|
def __init__(self, state_dir_name: str = ".minder") -> None:
|
|
10
10
|
self._state_dir_name = state_dir_name
|
|
11
11
|
|
|
12
|
-
async def read_all(self, repo_path: str) -> dict[str, Any]:
|
|
13
|
-
state_dir = self._ensure_state_dir(repo_path)
|
|
12
|
+
async def read_all(self, repo_path: str, branch: str = "main") -> dict[str, Any]:
|
|
13
|
+
state_dir = self._ensure_state_dir(repo_path, branch)
|
|
14
14
|
return {
|
|
15
15
|
"workflow": self._read_json(state_dir / "workflow.json", default={}),
|
|
16
16
|
"context": self._read_json(state_dir / "context.json", default={}),
|
|
@@ -18,22 +18,33 @@ class RepoStateStore:
|
|
|
18
18
|
"artifacts": self._read_artifacts(state_dir / "artifacts"),
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
async def write_workflow_state(self, repo_path: str, payload: dict[str, Any]) -> None:
|
|
22
|
-
self._write_json(self._ensure_state_dir(repo_path) / "workflow.json", payload)
|
|
21
|
+
async def write_workflow_state(self, repo_path: str, payload: dict[str, Any], branch: str = "main") -> None:
|
|
22
|
+
self._write_json(self._ensure_state_dir(repo_path, branch) / "workflow.json", payload)
|
|
23
23
|
|
|
24
|
-
async def write_context(self, repo_path: str, payload: dict[str, Any]) -> None:
|
|
25
|
-
self._write_json(self._ensure_state_dir(repo_path) / "context.json", payload)
|
|
24
|
+
async def write_context(self, repo_path: str, payload: dict[str, Any], branch: str = "main") -> None:
|
|
25
|
+
self._write_json(self._ensure_state_dir(repo_path, branch) / "context.json", payload)
|
|
26
26
|
|
|
27
|
-
async def write_relationships(self, repo_path: str, payload: dict[str, Any]) -> None:
|
|
28
|
-
self._write_json(self._ensure_state_dir(repo_path) / "relationships.json", payload)
|
|
27
|
+
async def write_relationships(self, repo_path: str, payload: dict[str, Any], branch: str = "main") -> None:
|
|
28
|
+
self._write_json(self._ensure_state_dir(repo_path, branch) / "relationships.json", payload)
|
|
29
29
|
|
|
30
|
-
async def write_artifact(self, repo_path: str, name: str, content: str) -> None:
|
|
31
|
-
artifacts_dir = self._ensure_state_dir(repo_path) / "artifacts"
|
|
30
|
+
async def write_artifact(self, repo_path: str, name: str, content: str, branch: str = "main") -> None:
|
|
31
|
+
artifacts_dir = self._ensure_state_dir(repo_path, branch) / "artifacts"
|
|
32
32
|
artifacts_dir.mkdir(parents=True, exist_ok=True)
|
|
33
33
|
(artifacts_dir / name).write_text(content, encoding="utf-8")
|
|
34
34
|
|
|
35
|
-
def _ensure_state_dir(self, repo_path: str) -> Path:
|
|
36
|
-
|
|
35
|
+
def _ensure_state_dir(self, repo_path: str, branch: str = "main") -> Path:
|
|
36
|
+
base_dir = Path(repo_path) / self._state_dir_name
|
|
37
|
+
|
|
38
|
+
# Phase 3: Auto-generate .gitignore in the base directory to prevent merge conflicts
|
|
39
|
+
if not base_dir.exists():
|
|
40
|
+
base_dir.mkdir(parents=True, exist_ok=True)
|
|
41
|
+
gitignore = base_dir / ".gitignore"
|
|
42
|
+
if not gitignore.exists():
|
|
43
|
+
gitignore.write_text("*\n", encoding="utf-8")
|
|
44
|
+
|
|
45
|
+
# Branch isolation: normalize branch name to be safe for file system
|
|
46
|
+
safe_branch = "".join(c if c.isalnum() or c in "-_" else "_" for c in branch)
|
|
47
|
+
state_dir = base_dir / safe_branch
|
|
37
48
|
state_dir.mkdir(parents=True, exist_ok=True)
|
|
38
49
|
return state_dir
|
|
39
50
|
|
|
@@ -65,6 +65,7 @@ class MemoryTools:
|
|
|
65
65
|
limit: int,
|
|
66
66
|
current_step: str | None,
|
|
67
67
|
artifact_type: str | None,
|
|
68
|
+
owner_id: uuid.UUID | None = None,
|
|
68
69
|
) -> list[dict[str, Any]]:
|
|
69
70
|
graph = self._get_agentic_graph()
|
|
70
71
|
result = await graph.run(
|
|
@@ -72,6 +73,7 @@ class MemoryTools:
|
|
|
72
73
|
"original_query": query,
|
|
73
74
|
"current_step": current_step,
|
|
74
75
|
"artifact_type": artifact_type,
|
|
76
|
+
"owner_id": owner_id,
|
|
75
77
|
"target_count": limit,
|
|
76
78
|
"min_score": float(self._config.memory.recall_min_score),
|
|
77
79
|
"all_memories": [],
|
|
@@ -95,9 +97,10 @@ class MemoryTools:
|
|
|
95
97
|
current_step: str | None,
|
|
96
98
|
artifact_type: str | None,
|
|
97
99
|
include_raw_scores: bool = False,
|
|
100
|
+
owner_id: uuid.UUID | None = None,
|
|
98
101
|
) -> list[dict[str, Any]]:
|
|
99
102
|
query_embedding = self._embedder.embed(query)
|
|
100
|
-
skills = await self._store.list_skills_by_kind(is_memory=True)
|
|
103
|
+
skills = await self._store.list_skills_by_kind(is_memory=True, owner_id=owner_id)
|
|
101
104
|
vector_results: list[dict[str, Any]] = []
|
|
102
105
|
corpus: list[dict[str, Any]] = []
|
|
103
106
|
for skill in skills:
|
|
@@ -162,6 +165,8 @@ class MemoryTools:
|
|
|
162
165
|
content: str,
|
|
163
166
|
tags: list[str],
|
|
164
167
|
language: str,
|
|
168
|
+
owner_id: uuid.UUID | None = None,
|
|
169
|
+
scope: str = "private",
|
|
165
170
|
) -> dict[str, Any]:
|
|
166
171
|
# Normalize the language to a memory-eligible value so that
|
|
167
172
|
# is_memory_record() always identifies this entry as a memory.
|
|
@@ -183,6 +188,8 @@ class MemoryTools:
|
|
|
183
188
|
embedding=self._embedder.embed(f"{title}\n{content}"),
|
|
184
189
|
usage_count=0,
|
|
185
190
|
quality_score=0.0,
|
|
191
|
+
owner_id=owner_id,
|
|
192
|
+
scope=scope,
|
|
186
193
|
)
|
|
187
194
|
|
|
188
195
|
# Record persistent audit event
|
|
@@ -209,6 +216,7 @@ class MemoryTools:
|
|
|
209
216
|
current_step: str | None = None,
|
|
210
217
|
artifact_type: str | None = None,
|
|
211
218
|
skip_synthesis: bool = False,
|
|
219
|
+
owner_id: uuid.UUID | None = None,
|
|
212
220
|
) -> list[dict[str, Any]]:
|
|
213
221
|
if skip_synthesis:
|
|
214
222
|
limited = await self._recall_candidates(
|
|
@@ -216,6 +224,7 @@ class MemoryTools:
|
|
|
216
224
|
limit=limit,
|
|
217
225
|
current_step=current_step,
|
|
218
226
|
artifact_type=artifact_type,
|
|
227
|
+
owner_id=owner_id,
|
|
219
228
|
)
|
|
220
229
|
for item in limited:
|
|
221
230
|
item.pop("_step_compat", None)
|
|
@@ -229,6 +238,7 @@ class MemoryTools:
|
|
|
229
238
|
limit=limit,
|
|
230
239
|
current_step=current_step,
|
|
231
240
|
artifact_type=artifact_type,
|
|
241
|
+
owner_id=owner_id,
|
|
232
242
|
)
|
|
233
243
|
except Exception:
|
|
234
244
|
pass
|
|
@@ -238,6 +248,7 @@ class MemoryTools:
|
|
|
238
248
|
limit=limit,
|
|
239
249
|
current_step=current_step,
|
|
240
250
|
artifact_type=artifact_type,
|
|
251
|
+
owner_id=owner_id,
|
|
241
252
|
)
|
|
242
253
|
|
|
243
254
|
try:
|
|
@@ -283,8 +294,12 @@ class MemoryTools:
|
|
|
283
294
|
item.pop("continuity_reasons", None)
|
|
284
295
|
return limited
|
|
285
296
|
|
|
286
|
-
async def minder_memory_list(
|
|
287
|
-
|
|
297
|
+
async def minder_memory_list(
|
|
298
|
+
self,
|
|
299
|
+
*,
|
|
300
|
+
owner_id: uuid.UUID | None = None,
|
|
301
|
+
) -> list[dict[str, Any]]:
|
|
302
|
+
skills = await self._store.list_skills_by_kind(is_memory=True, owner_id=owner_id)
|
|
288
303
|
return [
|
|
289
304
|
{
|
|
290
305
|
"id": str(skill.id),
|
|
@@ -302,10 +317,16 @@ class MemoryTools:
|
|
|
302
317
|
title: str | None = None,
|
|
303
318
|
content: str | None = None,
|
|
304
319
|
tags: list[str] | None = None,
|
|
320
|
+
owner_id: uuid.UUID | None = None,
|
|
305
321
|
) -> dict[str, Any]:
|
|
306
322
|
existing = await self._store.get_skill_by_id(uuid.UUID(memory_id))
|
|
307
323
|
if existing is None or not is_memory_record(existing):
|
|
308
324
|
raise ValueError(f"Memory not found: {memory_id}")
|
|
325
|
+
|
|
326
|
+
if owner_id is not None:
|
|
327
|
+
existing_owner = getattr(existing, "owner_id", None)
|
|
328
|
+
if existing_owner is not None and str(existing_owner) != str(owner_id):
|
|
329
|
+
raise ValueError(f"Access denied: you do not own memory {memory_id}")
|
|
309
330
|
|
|
310
331
|
update_data: dict[str, Any] = {}
|
|
311
332
|
next_title = title if title is not None else str(existing.title)
|
|
@@ -343,7 +364,15 @@ class MemoryTools:
|
|
|
343
364
|
"updated": True,
|
|
344
365
|
}
|
|
345
366
|
|
|
346
|
-
async def minder_memory_delete(self, skill_id: str) -> dict[str, bool]:
|
|
367
|
+
async def minder_memory_delete(self, skill_id: str, *, owner_id: uuid.UUID | None = None) -> dict[str, bool]:
|
|
368
|
+
if owner_id is not None:
|
|
369
|
+
existing = await self._store.get_skill_by_id(uuid.UUID(skill_id))
|
|
370
|
+
if existing is None or not is_memory_record(existing):
|
|
371
|
+
raise ValueError(f"Memory not found: {skill_id}")
|
|
372
|
+
existing_owner = getattr(existing, "owner_id", None)
|
|
373
|
+
if existing_owner is not None and str(existing_owner) != str(owner_id):
|
|
374
|
+
raise ValueError(f"Access denied: you do not own memory {skill_id}")
|
|
375
|
+
|
|
347
376
|
await self._store.delete_skill(uuid.UUID(skill_id))
|
|
348
377
|
|
|
349
378
|
# Record persistent audit event
|
|
@@ -367,6 +396,7 @@ class MemoryTools:
|
|
|
367
396
|
memory_ids: list[str],
|
|
368
397
|
similarity_threshold: float = 0.92,
|
|
369
398
|
dry_run: bool = True,
|
|
399
|
+
owner_id: uuid.UUID | None = None,
|
|
370
400
|
) -> dict[str, Any]:
|
|
371
401
|
normalized_ids = self._normalize_memory_ids(memory_ids)
|
|
372
402
|
if len(normalized_ids) < 2:
|
|
@@ -377,6 +407,11 @@ class MemoryTools:
|
|
|
377
407
|
skill = await self._store.get_skill_by_id(uuid.UUID(memory_id))
|
|
378
408
|
if skill is None:
|
|
379
409
|
raise ValueError(f"Memory not found: {memory_id}")
|
|
410
|
+
|
|
411
|
+
if owner_id is not None:
|
|
412
|
+
existing_owner = getattr(skill, "owner_id", None)
|
|
413
|
+
if existing_owner is not None and str(existing_owner) != str(owner_id):
|
|
414
|
+
raise ValueError(f"Access denied: you do not own memory {memory_id}")
|
|
380
415
|
embedding = self._embedder.embed(
|
|
381
416
|
self._compaction_text(
|
|
382
417
|
title=str(skill.title),
|
|
@@ -35,15 +35,15 @@ class WorkflowTools:
|
|
|
35
35
|
return self._graph
|
|
36
36
|
|
|
37
37
|
async def minder_workflow_get(
|
|
38
|
-
self, *, repo_id: uuid.UUID, repo_path: str
|
|
38
|
+
self, *, repo_id: uuid.UUID, repo_path: str, branch: str = "main"
|
|
39
39
|
) -> dict[str, Any]:
|
|
40
40
|
repo = await self._require_repo(repo_id)
|
|
41
41
|
workflow = await self._require_workflow(repo.workflow_id)
|
|
42
|
-
state = await self.
|
|
42
|
+
state = await self._require_workflow_state(repo_id, branch=branch)
|
|
43
43
|
relationships = (
|
|
44
44
|
dict(repo.relationships) if isinstance(repo.relationships, dict) else {}
|
|
45
45
|
)
|
|
46
|
-
await self._repo_state.write_relationships(repo_path, relationships)
|
|
46
|
+
await self._repo_state.write_relationships(repo_path, relationships, branch=branch)
|
|
47
47
|
if state is not None:
|
|
48
48
|
await self._repo_state.write_workflow_state(
|
|
49
49
|
repo_path,
|
|
@@ -53,6 +53,7 @@ class WorkflowTools:
|
|
|
53
53
|
"blocked_by": list(state.blocked_by),
|
|
54
54
|
"next_step": state.next_step,
|
|
55
55
|
},
|
|
56
|
+
branch=branch,
|
|
56
57
|
)
|
|
57
58
|
return {
|
|
58
59
|
"workflow": {
|
|
@@ -70,6 +71,7 @@ class WorkflowTools:
|
|
|
70
71
|
repo_path: str | None = None,
|
|
71
72
|
session_id: uuid.UUID | None = None,
|
|
72
73
|
decision: dict[str, Any] | None = None,
|
|
74
|
+
branch: str = "main",
|
|
73
75
|
) -> dict[str, Any]:
|
|
74
76
|
if session_id is not None or decision is not None:
|
|
75
77
|
if session_id is None or decision is None:
|
|
@@ -100,7 +102,7 @@ class WorkflowTools:
|
|
|
100
102
|
raise ValueError("repo_id and repo_path are required when not resuming")
|
|
101
103
|
repo = await self._require_repo(repo_id)
|
|
102
104
|
workflow = await self._require_workflow(repo.workflow_id)
|
|
103
|
-
state = await self._require_workflow_state(repo_id)
|
|
105
|
+
state = await self._require_workflow_state(repo_id, branch=branch)
|
|
104
106
|
envelope = build_instruction_envelope(workflow=workflow, workflow_state=state)
|
|
105
107
|
await self._repo_state.write_workflow_state(
|
|
106
108
|
repo_path,
|
|
@@ -110,6 +112,7 @@ class WorkflowTools:
|
|
|
110
112
|
"blocked_by": list(state.blocked_by),
|
|
111
113
|
"next_step": state.next_step,
|
|
112
114
|
},
|
|
115
|
+
branch=branch,
|
|
113
116
|
)
|
|
114
117
|
for _k in ("workflow_id", "workflow_version", "policies"):
|
|
115
118
|
envelope.pop(_k, None)
|
|
@@ -127,10 +130,11 @@ class WorkflowTools:
|
|
|
127
130
|
completed_step: str,
|
|
128
131
|
artifact_name: str | None = None,
|
|
129
132
|
artifact_content: str | None = None,
|
|
133
|
+
branch: str = "main",
|
|
130
134
|
) -> dict[str, Any]:
|
|
131
135
|
repo = await self._require_repo(repo_id)
|
|
132
136
|
workflow = await self._require_workflow(repo.workflow_id)
|
|
133
|
-
state = await self._require_workflow_state(repo_id)
|
|
137
|
+
state = await self._require_workflow_state(repo_id, branch=branch)
|
|
134
138
|
step_names = [
|
|
135
139
|
step["name"]
|
|
136
140
|
for step in workflow.steps
|
|
@@ -144,7 +148,7 @@ class WorkflowTools:
|
|
|
144
148
|
if artifact_name and artifact_content is not None:
|
|
145
149
|
artifacts[artifact_name] = artifact_content
|
|
146
150
|
await self._repo_state.write_artifact(
|
|
147
|
-
repo_path, artifact_name, artifact_content
|
|
151
|
+
repo_path, artifact_name, artifact_content, branch=branch
|
|
148
152
|
)
|
|
149
153
|
updated = await self._store.update_workflow_state(
|
|
150
154
|
state.id,
|
|
@@ -163,10 +167,12 @@ class WorkflowTools:
|
|
|
163
167
|
"blocked_by": list(updated.blocked_by),
|
|
164
168
|
"next_step": updated.next_step,
|
|
165
169
|
},
|
|
170
|
+
branch=branch,
|
|
166
171
|
)
|
|
167
172
|
await self._repo_state.write_relationships(
|
|
168
173
|
repo_path,
|
|
169
174
|
dict(repo.relationships) if isinstance(repo.relationships, dict) else {},
|
|
175
|
+
branch=branch,
|
|
170
176
|
)
|
|
171
177
|
return {
|
|
172
178
|
"current_step": updated.current_step,
|
|
@@ -180,10 +186,11 @@ class WorkflowTools:
|
|
|
180
186
|
repo_id: uuid.UUID,
|
|
181
187
|
requested_step: str,
|
|
182
188
|
action: str | None = None,
|
|
189
|
+
branch: str = "main",
|
|
183
190
|
) -> dict[str, Any]:
|
|
184
191
|
repo = await self._require_repo(repo_id)
|
|
185
192
|
workflow = await self._require_workflow(repo.workflow_id)
|
|
186
|
-
state = await self._require_workflow_state(repo_id)
|
|
193
|
+
state = await self._require_workflow_state(repo_id, branch=branch)
|
|
187
194
|
step_names = [
|
|
188
195
|
step["name"]
|
|
189
196
|
for step in workflow.steps
|
|
@@ -234,8 +241,8 @@ class WorkflowTools:
|
|
|
234
241
|
raise ValueError(f"Workflow not found: {workflow_id}")
|
|
235
242
|
return workflow
|
|
236
243
|
|
|
237
|
-
async def _require_workflow_state(self, repo_id: uuid.UUID): # noqa: ANN202
|
|
238
|
-
state = await self._store.get_workflow_state_by_repo(repo_id)
|
|
244
|
+
async def _require_workflow_state(self, repo_id: uuid.UUID, *, branch: str = "main"): # noqa: ANN202
|
|
245
|
+
state = await self._store.get_workflow_state_by_repo(repo_id, branch=branch)
|
|
239
246
|
if state is None:
|
|
240
247
|
raise ValueError(f"Workflow state not found for repo: {repo_id}")
|
|
241
248
|
return state
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|