ww-agentic-workflows 1.0.0.dev3__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.
- ww/__init__.py +18 -0
- ww/_bundled_extensions/ww/git/extension.py +1728 -0
- ww/action_execution.py +887 -0
- ww/actions/__init__.py +94 -0
- ww/actions/command.py +444 -0
- ww/actions/contracts.py +699 -0
- ww/actions/extension.py +197 -0
- ww/actions/mcp.py +84 -0
- ww/actions/prompt.py +74 -0
- ww/actions/skill.py +62 -0
- ww/actions/slash_command.py +63 -0
- ww/agents.py +151 -0
- ww/amendments.py +54 -0
- ww/artifacts.py +93 -0
- ww/assessments.py +181 -0
- ww/assets/__init__.py +2 -0
- ww/assets/agent_instructions.md +49 -0
- ww/assets/docs/examples.md +879 -0
- ww/assets/docs/features.md +4639 -0
- ww/assets/docs/specification.md +1876 -0
- ww/assets/noww_skill.md +11 -0
- ww/assets/workflows/catchall.yaml +26 -0
- ww/assets/workflows/onboarding.yaml +586 -0
- ww/assets/workflows/scriptize.yaml +130 -0
- ww/assets/ww-automate_skill.md +23 -0
- ww/assets/ww-deduce-feedback_skill.md +38 -0
- ww/assets/ww-feedback-rules_skill.md +48 -0
- ww/assets/ww-learn-project_skill.md +22 -0
- ww/assets/ww-refresh_skill.md +26 -0
- ww/assets/ww-rule_skill.md +83 -0
- ww/assets/ww-rules-from-artifacts_skill.md +22 -0
- ww/assets/ww-scriptize_skill.md +33 -0
- ww/assets/ww-setup_skill.md +94 -0
- ww/assets/ww-solve_skill.md +23 -0
- ww/assets/ww-suggest_skill.md +32 -0
- ww/assets/ww-wizard_skill.md +105 -0
- ww/assets/ww_skill.md +59 -0
- ww/assignments.py +283 -0
- ww/bootstrap.py +405 -0
- ww/builtin_workflows.py +215 -0
- ww/changes.py +225 -0
- ww/child_coordination.py +482 -0
- ww/children.py +106 -0
- ww/claude_permissions.py +115 -0
- ww/cli/__init__.py +7 -0
- ww/cli/__main__.py +6 -0
- ww/cli/audit.py +129 -0
- ww/cli/catalogs.py +131 -0
- ww/cli/discover.py +607 -0
- ww/cli/initialization.py +898 -0
- ww/cli/lookup.py +287 -0
- ww/cli/main.py +1768 -0
- ww/cli/parser.py +1200 -0
- ww/cli/prompts.py +217 -0
- ww/cli/updates.py +117 -0
- ww/completion_artifacts.py +156 -0
- ww/completion_inputs.py +39 -0
- ww/config/__init__.py +582 -0
- ww/config/actions.py +591 -0
- ww/config/composition.py +571 -0
- ww/config/rules.py +511 -0
- ww/config/steps.py +1220 -0
- ww/config/values.py +223 -0
- ww/config_files.py +191 -0
- ww/config_writes.py +264 -0
- ww/contracts.py +155 -0
- ww/control.py +41 -0
- ww/defaults.py +130 -0
- ww/design_docs.py +32 -0
- ww/discovery.py +104 -0
- ww/documents.py +217 -0
- ww/errors.py +18 -0
- ww/executable.py +43 -0
- ww/execution_models/__init__.py +64 -0
- ww/execution_models/construction.py +148 -0
- ww/execution_models/decoding.py +38 -0
- ww/execution_models/plan_codec.py +565 -0
- ww/execution_models/records.py +1206 -0
- ww/execution_models/runs.py +266 -0
- ww/extensions/__init__.py +40 -0
- ww/extensions/api.py +559 -0
- ww/extensions/registry.py +864 -0
- ww/extensions/store.py +78 -0
- ww/feedback.py +342 -0
- ww/handler_repairs.py +57 -0
- ww/hooks/__init__.py +40 -0
- ww/hooks/agents.py +380 -0
- ww/hooks/install.py +168 -0
- ww/hooks/notices.py +206 -0
- ww/hooks/records.py +209 -0
- ww/hooks/runtime.py +266 -0
- ww/hooks/transcripts.py +183 -0
- ww/inspect.py +896 -0
- ww/instructions/__init__.py +17 -0
- ww/instructions/builder.py +1682 -0
- ww/instructions/commands.py +335 -0
- ww/instructions/handoff.py +149 -0
- ww/instructions/models.py +686 -0
- ww/instructions/policy.py +219 -0
- ww/instructions/text.py +168 -0
- ww/interactions.py +187 -0
- ww/interpolation.py +37 -0
- ww/item_passes.py +167 -0
- ww/items.py +99 -0
- ww/locking.py +207 -0
- ww/metadata_publication.py +230 -0
- ww/onboarding.py +229 -0
- ww/open_work.py +236 -0
- ww/operations.py +193 -0
- ww/operator_ui/__init__.py +16 -0
- ww/operator_ui/page.html +351 -0
- ww/operator_ui/server.py +215 -0
- ww/operator_ui/session.py +389 -0
- ww/operator_ui/sheet.py +104 -0
- ww/operator_ui/view.py +109 -0
- ww/output.py +339 -0
- ww/output_adapters/__init__.py +12 -0
- ww/output_adapters/base.py +25 -0
- ww/output_adapters/json_adapter.py +37 -0
- ww/output_adapters/markdown.py +2293 -0
- ww/output_adapters/rule_pages.py +337 -0
- ww/output_adapters/terminal.py +21 -0
- ww/package_updates.py +167 -0
- ww/plan/__init__.py +38 -0
- ww/plan/actions.py +207 -0
- ww/plan/compiler.py +1492 -0
- ww/plan/constructs.py +456 -0
- ww/plan/models.py +665 -0
- ww/project_config.py +752 -0
- ww/recovery.py +401 -0
- ww/replanning.py +367 -0
- ww/results.py +77 -0
- ww/rule_checks.py +230 -0
- ww/rule_conversion.py +331 -0
- ww/rule_disputes.py +148 -0
- ww/rule_store.py +456 -0
- ww/rule_verification.py +714 -0
- ww/rule_views.py +447 -0
- ww/rule_writes.py +920 -0
- ww/run_coordination.py +158 -0
- ww/runtimes.py +105 -0
- ww/service.py +4405 -0
- ww/setup_apply.py +428 -0
- ww/step_values.py +20 -0
- ww/storage.py +447 -0
- ww/storage_adapters/__init__.py +36 -0
- ww/storage_adapters/base.py +540 -0
- ww/storage_adapters/filesystem.py +370 -0
- ww/storage_adapters/memory.py +195 -0
- ww/storage_adapters/project_metadata.py +69 -0
- ww/storage_adapters/task_document.py +484 -0
- ww/task_ids.py +114 -0
- ww/task_references.py +124 -0
- ww/transitions.py +1619 -0
- ww/updates.py +399 -0
- ww/upgrade.py +95 -0
- ww/validation.py +168 -0
- ww/variables.py +275 -0
- ww/workflow_config.py +854 -0
- ww/workflow_update.py +239 -0
- ww/workflow_validation.py +1260 -0
- ww/workspace.py +50 -0
- ww_agentic_workflows-1.0.0.dev3.dist-info/METADATA +690 -0
- ww_agentic_workflows-1.0.0.dev3.dist-info/RECORD +167 -0
- ww_agentic_workflows-1.0.0.dev3.dist-info/WHEEL +4 -0
- ww_agentic_workflows-1.0.0.dev3.dist-info/entry_points.txt +2 -0
- ww_agentic_workflows-1.0.0.dev3.dist-info/licenses/LICENSE +674 -0
ww/errors.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
"""Domain errors exposed as clear CLI failures."""
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class WwError(Exception):
|
|
6
|
+
"""Base error for expected workflow failures."""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ConfigurationError(WwError):
|
|
10
|
+
"""A workflow definition is missing or invalid."""
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class StateError(WwError):
|
|
14
|
+
"""A task state is missing, invalid, or cannot transition."""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class LockError(WwError):
|
|
18
|
+
"""A file lock could not be acquired within the configured wait bound."""
|
ww/executable.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
"""How the commands ww prints invoke ww.
|
|
3
|
+
|
|
4
|
+
A project names its ww binary in ``ww.json`` (``executable``),
|
|
5
|
+
so two installs, such as one for developing ww and one pinned to ``dev``, can
|
|
6
|
+
live side by side under different global names. Every command ww prints
|
|
7
|
+
starts with that binary, or with the project launcher, ``./ww``, when the
|
|
8
|
+
project names none. The CLI sets it once per invocation.
|
|
9
|
+
|
|
10
|
+
This module imports nothing from ww, so any module may use it.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import shlex
|
|
16
|
+
from collections.abc import Iterator
|
|
17
|
+
from contextlib import contextmanager
|
|
18
|
+
from contextvars import ContextVar
|
|
19
|
+
|
|
20
|
+
PROJECT_LAUNCHER_COMMAND = "./ww"
|
|
21
|
+
# The binary a project uses when it names none, and the one the launcher
|
|
22
|
+
# falls back to.
|
|
23
|
+
DEFAULT_EXECUTABLE = "ww-agentic-workflows"
|
|
24
|
+
_EXECUTABLE: ContextVar[str] = ContextVar(
|
|
25
|
+
"ww_executable", default=PROJECT_LAUNCHER_COMMAND
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@contextmanager
|
|
30
|
+
def printed_executable(executable: str | None) -> Iterator[None]:
|
|
31
|
+
"""Print commands with ``executable``; ``None`` keeps the project launcher."""
|
|
32
|
+
token = _EXECUTABLE.set(
|
|
33
|
+
shlex.quote(executable) if executable else PROJECT_LAUNCHER_COMMAND
|
|
34
|
+
)
|
|
35
|
+
try:
|
|
36
|
+
yield
|
|
37
|
+
finally:
|
|
38
|
+
_EXECUTABLE.reset(token)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def ww_command() -> str:
|
|
42
|
+
"""How a printed command invokes ww, as the command's first word."""
|
|
43
|
+
return _EXECUTABLE.get()
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
"""Persisted, plan-driven workflow execution models.
|
|
3
|
+
|
|
4
|
+
This facade preserves the public import surface while the persistence records,
|
|
5
|
+
run aggregates, plan codec, and state construction live in focused modules.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .construction import (
|
|
9
|
+
build_step_projection,
|
|
10
|
+
initial_state,
|
|
11
|
+
new_item_execution,
|
|
12
|
+
operation_scope_for,
|
|
13
|
+
)
|
|
14
|
+
from .records import (
|
|
15
|
+
EXECUTION_SCHEMA_VERSION,
|
|
16
|
+
CheckReport,
|
|
17
|
+
CheckResult,
|
|
18
|
+
CommandExecution,
|
|
19
|
+
Dispute,
|
|
20
|
+
ExecutionState,
|
|
21
|
+
HeldCompletion,
|
|
22
|
+
InputRequest,
|
|
23
|
+
PlanItemExecution,
|
|
24
|
+
ProjectMetadataPublication,
|
|
25
|
+
RuleResolution,
|
|
26
|
+
RuleVerdict,
|
|
27
|
+
StepProgress,
|
|
28
|
+
VerificationRule,
|
|
29
|
+
)
|
|
30
|
+
from .runs import (
|
|
31
|
+
PLAN_COMPILER_VERSION,
|
|
32
|
+
PLAN_SCHEMA_VERSION,
|
|
33
|
+
PlanSnapshot,
|
|
34
|
+
TaskRunAggregate,
|
|
35
|
+
WorkflowRunSummary,
|
|
36
|
+
validate_task_runs,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
__all__ = [
|
|
40
|
+
"CheckReport",
|
|
41
|
+
"CheckResult",
|
|
42
|
+
"EXECUTION_SCHEMA_VERSION",
|
|
43
|
+
"PLAN_COMPILER_VERSION",
|
|
44
|
+
"PLAN_SCHEMA_VERSION",
|
|
45
|
+
"CommandExecution",
|
|
46
|
+
"Dispute",
|
|
47
|
+
"ExecutionState",
|
|
48
|
+
"HeldCompletion",
|
|
49
|
+
"InputRequest",
|
|
50
|
+
"PlanItemExecution",
|
|
51
|
+
"PlanSnapshot",
|
|
52
|
+
"ProjectMetadataPublication",
|
|
53
|
+
"RuleResolution",
|
|
54
|
+
"RuleVerdict",
|
|
55
|
+
"StepProgress",
|
|
56
|
+
"VerificationRule",
|
|
57
|
+
"TaskRunAggregate",
|
|
58
|
+
"WorkflowRunSummary",
|
|
59
|
+
"build_step_projection",
|
|
60
|
+
"initial_state",
|
|
61
|
+
"new_item_execution",
|
|
62
|
+
"operation_scope_for",
|
|
63
|
+
"validate_task_runs",
|
|
64
|
+
]
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
"""Persisted, plan-driven workflow execution models.
|
|
3
|
+
|
|
4
|
+
These models deliberately contain no YAML or handler-resolution concepts. A
|
|
5
|
+
task starts with a compiled :class:`WorkflowPlan` snapshot and progresses only
|
|
6
|
+
through the normalized plan items in that snapshot.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from ww.actions import CommandDefinition, PlannedAction, actions
|
|
14
|
+
from ww.plan import PlanItem, WorkflowPlan
|
|
15
|
+
|
|
16
|
+
from .records import CommandExecution, ExecutionState, PlanItemExecution, StepProgress
|
|
17
|
+
from .runs import PlanSnapshot
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def initial_state(
|
|
21
|
+
snapshot: PlanSnapshot,
|
|
22
|
+
modes: tuple[str, ...],
|
|
23
|
+
now: str,
|
|
24
|
+
run_id: str,
|
|
25
|
+
execution_instance_id: str | None = None,
|
|
26
|
+
parent_task_id: str | None = None,
|
|
27
|
+
start_operation_id: str | None = None,
|
|
28
|
+
workflow_runtime: str = "single",
|
|
29
|
+
model: str = "auto",
|
|
30
|
+
reasoning: str = "auto",
|
|
31
|
+
) -> ExecutionState:
|
|
32
|
+
plan = snapshot.plan
|
|
33
|
+
operation_scope = _operation_scope(run_id, execution_instance_id)
|
|
34
|
+
records = tuple(
|
|
35
|
+
new_item_execution(plan.task_id or "", operation_scope, item)
|
|
36
|
+
for item in plan.items
|
|
37
|
+
)
|
|
38
|
+
return ExecutionState(
|
|
39
|
+
task_id=plan.task_id or "",
|
|
40
|
+
workflow=plan.workflow,
|
|
41
|
+
agent=plan.agent,
|
|
42
|
+
modes=modes,
|
|
43
|
+
status="pending",
|
|
44
|
+
created_at=now,
|
|
45
|
+
updated_at=now,
|
|
46
|
+
snapshot_digest=snapshot.configuration_digest,
|
|
47
|
+
cursor=0,
|
|
48
|
+
active_item_id=None,
|
|
49
|
+
item_executions=records,
|
|
50
|
+
steps=build_step_projection(plan),
|
|
51
|
+
run_id=run_id,
|
|
52
|
+
execution_instance_id=execution_instance_id,
|
|
53
|
+
plan_revision=snapshot.plan_revision,
|
|
54
|
+
plan_digest=snapshot.plan_digest,
|
|
55
|
+
parent_task_id=parent_task_id,
|
|
56
|
+
start_operation_id=start_operation_id,
|
|
57
|
+
workflow_runtime=workflow_runtime,
|
|
58
|
+
model=model,
|
|
59
|
+
reasoning=reasoning,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def new_item_execution(
|
|
64
|
+
task_id: str, operation_scope: str, item: PlanItem
|
|
65
|
+
) -> PlanItemExecution:
|
|
66
|
+
"""Create the complete execution ledger for one concrete plan item."""
|
|
67
|
+
operation_id = f"{task_id}:{operation_scope}:{item.id}"
|
|
68
|
+
return PlanItemExecution(
|
|
69
|
+
plan_item_id=item.id,
|
|
70
|
+
position=item.position,
|
|
71
|
+
operation_id=operation_id,
|
|
72
|
+
commands=tuple(
|
|
73
|
+
CommandExecution(
|
|
74
|
+
index=index,
|
|
75
|
+
operation_id=f"{operation_id}:command:{index}",
|
|
76
|
+
)
|
|
77
|
+
for index, _ in enumerate(_commands_for(item), 1)
|
|
78
|
+
),
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _commands_for(item: PlanItem) -> tuple[CommandDefinition, ...]:
|
|
83
|
+
"""Return tracked command segments when the registered action supports them."""
|
|
84
|
+
if not isinstance(item.operation, PlannedAction):
|
|
85
|
+
return ()
|
|
86
|
+
implementation = actions.get(item.kind)
|
|
87
|
+
traits = implementation.traits(item.payload_as(implementation.planned_type))
|
|
88
|
+
return traits.command_segments or ()
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def operation_scope_for(state: ExecutionState) -> str:
|
|
92
|
+
"""Return the durable idempotency namespace for an execution instance."""
|
|
93
|
+
return _operation_scope(state.run_id or state.workflow, state.execution_instance_id)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _operation_scope(run_id: str, execution_instance_id: str | None) -> str:
|
|
97
|
+
return (
|
|
98
|
+
f"{run_id}:{execution_instance_id}"
|
|
99
|
+
if execution_instance_id is not None
|
|
100
|
+
else run_id
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _persisted_step_chain(item: PlanItem) -> tuple[str, ...]:
|
|
105
|
+
"""Return the hierarchy encoded by a plan item."""
|
|
106
|
+
chain = (*item.ancestors, item.step)
|
|
107
|
+
if item.parent and item.parent not in chain[:-1]:
|
|
108
|
+
return item.parent, item.step
|
|
109
|
+
return chain
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def build_step_projection(
|
|
113
|
+
plan: WorkflowPlan, previous: tuple[StepProgress, ...] = ()
|
|
114
|
+
) -> tuple[StepProgress, ...]:
|
|
115
|
+
"""Build the recursive tree, retaining progress for paths that still exist."""
|
|
116
|
+
prior: dict[str, StepProgress] = {}
|
|
117
|
+
|
|
118
|
+
def remember(entries: tuple[StepProgress, ...]) -> None:
|
|
119
|
+
for entry in entries:
|
|
120
|
+
prior[entry.path] = entry
|
|
121
|
+
remember(entry.children)
|
|
122
|
+
|
|
123
|
+
remember(previous)
|
|
124
|
+
nodes: dict[str, dict[str, Any]] = {}
|
|
125
|
+
order: list[str] = []
|
|
126
|
+
for item in plan.items:
|
|
127
|
+
chain = _persisted_step_chain(item)
|
|
128
|
+
for index, path in enumerate(chain):
|
|
129
|
+
parent = chain[index - 1] if index else None
|
|
130
|
+
if path not in nodes:
|
|
131
|
+
nodes[path] = {"parent": parent, "children": []}
|
|
132
|
+
order.append(path)
|
|
133
|
+
if parent and path not in nodes[parent]["children"]:
|
|
134
|
+
nodes[parent]["children"].append(path)
|
|
135
|
+
|
|
136
|
+
def node(path: str) -> StepProgress:
|
|
137
|
+
info = nodes[path]
|
|
138
|
+
existing = prior.get(path)
|
|
139
|
+
return StepProgress(
|
|
140
|
+
path.rsplit("/", 1)[-1],
|
|
141
|
+
path,
|
|
142
|
+
status=existing.status if existing is not None else "pending",
|
|
143
|
+
started_at=existing.started_at if existing is not None else None,
|
|
144
|
+
completed_at=existing.completed_at if existing is not None else None,
|
|
145
|
+
children=tuple(node(child) for child in info["children"]),
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
return tuple(node(path) for path in order if nodes[path]["parent"] is None)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
"""Decoding helpers specific to persisted execution records.
|
|
3
|
+
|
|
4
|
+
Primitive checks live in :mod:`ww.validation`; this module keeps only the
|
|
5
|
+
shapes that exist solely in saved task state.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from collections.abc import Callable
|
|
11
|
+
from typing import Any, TypeVar
|
|
12
|
+
|
|
13
|
+
from ww.validation import is_positive_int
|
|
14
|
+
|
|
15
|
+
_T = TypeVar("_T")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _from_path(function: Callable[[Any], _T], value: Any, path: str) -> _T:
|
|
19
|
+
try:
|
|
20
|
+
return function(value)
|
|
21
|
+
except (TypeError, ValueError) as error:
|
|
22
|
+
raise ValueError(f"{path}: {error}") from error
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _variables(value: Any, context: str) -> tuple[tuple[str, str], ...]:
|
|
26
|
+
if not isinstance(value, dict) or not all(
|
|
27
|
+
isinstance(name, str) and isinstance(item, str) for name, item in value.items()
|
|
28
|
+
):
|
|
29
|
+
raise ValueError(f"{context} must be a mapping of strings")
|
|
30
|
+
return tuple(value.items())
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _positive_int_mapping(value: Any, context: str) -> tuple[tuple[str, int], ...]:
|
|
34
|
+
if not isinstance(value, dict) or not all(
|
|
35
|
+
isinstance(name, str) and is_positive_int(item) for name, item in value.items()
|
|
36
|
+
):
|
|
37
|
+
raise ValueError(f"{context} must be a mapping of positive integers")
|
|
38
|
+
return tuple(value.items())
|