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
|
@@ -0,0 +1,686 @@
|
|
|
1
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
"""Instruction contract and serialization."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from typing import TYPE_CHECKING
|
|
8
|
+
|
|
9
|
+
from ww.amendments import Amendment
|
|
10
|
+
from ww.assessments import AssessmentOutcome
|
|
11
|
+
from ww.children import ChildTask
|
|
12
|
+
from ww.contracts import (
|
|
13
|
+
CallerRole,
|
|
14
|
+
Control,
|
|
15
|
+
InstructionStatus,
|
|
16
|
+
ItemStatus,
|
|
17
|
+
NextRole,
|
|
18
|
+
OperatorReason,
|
|
19
|
+
PlanItemKind,
|
|
20
|
+
RecoveryAction,
|
|
21
|
+
StepRole,
|
|
22
|
+
)
|
|
23
|
+
from ww.execution_models import WorkflowRunSummary
|
|
24
|
+
from ww.items import WorkItem
|
|
25
|
+
from ww.plan import PlannedMode
|
|
26
|
+
|
|
27
|
+
if TYPE_CHECKING:
|
|
28
|
+
from ww.replanning import PlanChange
|
|
29
|
+
from ww.workflow_config import (
|
|
30
|
+
ChoiceDefinition,
|
|
31
|
+
ItemFieldUpdate,
|
|
32
|
+
ProvidedVariable,
|
|
33
|
+
SavedMetadata,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass(frozen=True)
|
|
38
|
+
class RecoveryCommand:
|
|
39
|
+
"""One operator choice for an interrupted automatic item."""
|
|
40
|
+
|
|
41
|
+
action: RecoveryAction
|
|
42
|
+
command: str
|
|
43
|
+
|
|
44
|
+
def to_dict(self) -> dict[str, str]:
|
|
45
|
+
return {"action": self.action, "command": self.command}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass(frozen=True)
|
|
49
|
+
class StepHandover:
|
|
50
|
+
"""One completed step's handover, an input to the workflow summary."""
|
|
51
|
+
|
|
52
|
+
step: str
|
|
53
|
+
artifact: str
|
|
54
|
+
summary: str | None
|
|
55
|
+
|
|
56
|
+
def to_dict(self) -> dict[str, str | None]:
|
|
57
|
+
return {"step": self.step, "artifact": self.artifact, "summary": self.summary}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass(frozen=True)
|
|
61
|
+
class DocumentTask:
|
|
62
|
+
"""A document the active step must create or update, with its file."""
|
|
63
|
+
|
|
64
|
+
name: str
|
|
65
|
+
instruction: str
|
|
66
|
+
path: str
|
|
67
|
+
exists: bool
|
|
68
|
+
|
|
69
|
+
def to_dict(self) -> dict[str, object]:
|
|
70
|
+
return {
|
|
71
|
+
"name": self.name,
|
|
72
|
+
"instruction": self.instruction,
|
|
73
|
+
"path": self.path,
|
|
74
|
+
"exists": self.exists,
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@dataclass(frozen=True)
|
|
79
|
+
class InteractCommands:
|
|
80
|
+
"""The commands of an interactive step, spelled for the role that holds it."""
|
|
81
|
+
|
|
82
|
+
# Record the whole conversation and end it: a heredoc transcript, with
|
|
83
|
+
# the pick in the same call on a step with choices.
|
|
84
|
+
transcript: str
|
|
85
|
+
operator: str
|
|
86
|
+
agent: str
|
|
87
|
+
choice: str
|
|
88
|
+
end: str
|
|
89
|
+
# Serve the operator page and wait on it; a ``ui`` stage only.
|
|
90
|
+
wait: str
|
|
91
|
+
# Show the step again after the operator paused and returned.
|
|
92
|
+
resume: str
|
|
93
|
+
|
|
94
|
+
def to_dict(self) -> dict[str, str]:
|
|
95
|
+
return {
|
|
96
|
+
"transcript": self.transcript,
|
|
97
|
+
"operator": self.operator,
|
|
98
|
+
"agent": self.agent,
|
|
99
|
+
"choice": self.choice,
|
|
100
|
+
"end": self.end,
|
|
101
|
+
"wait": self.wait,
|
|
102
|
+
"resume": self.resume,
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@dataclass(frozen=True)
|
|
107
|
+
class ConversationEntry:
|
|
108
|
+
"""One recorded entry of the current step's conversation."""
|
|
109
|
+
|
|
110
|
+
at: str
|
|
111
|
+
speaker: str
|
|
112
|
+
text: str
|
|
113
|
+
|
|
114
|
+
def to_dict(self) -> dict[str, str]:
|
|
115
|
+
return {"at": self.at, "speaker": self.speaker, "text": self.text}
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@dataclass(frozen=True)
|
|
119
|
+
class RuleLine:
|
|
120
|
+
"""One rule the active step is given, as its page lists it.
|
|
121
|
+
|
|
122
|
+
``has_command`` rules are checked by ww when the step completes; the
|
|
123
|
+
others the worker follows and reports on in its artifact.
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
id: str
|
|
127
|
+
summary: str
|
|
128
|
+
paths: tuple[str, ...] = ()
|
|
129
|
+
has_command: bool = False
|
|
130
|
+
hook: bool = False
|
|
131
|
+
interpretation: str | None = None
|
|
132
|
+
# The converted derived check that checks a rule without a command, or,
|
|
133
|
+
# with ``missing``, the converted check that does not apply here.
|
|
134
|
+
check: str | None = None
|
|
135
|
+
# A verifier judges the rule because its converted ``check`` does not
|
|
136
|
+
# apply here: this configuration file is missing in the step's directory.
|
|
137
|
+
missing: str | None = None
|
|
138
|
+
|
|
139
|
+
def to_dict(self) -> dict[str, object]:
|
|
140
|
+
return {
|
|
141
|
+
"id": self.id,
|
|
142
|
+
"summary": self.summary,
|
|
143
|
+
"paths": list(self.paths),
|
|
144
|
+
"has_command": self.has_command,
|
|
145
|
+
"hook": self.hook,
|
|
146
|
+
"interpretation": self.interpretation,
|
|
147
|
+
"check": self.check,
|
|
148
|
+
"missing": self.missing,
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@dataclass(frozen=True)
|
|
153
|
+
class FixFailure:
|
|
154
|
+
"""One check that failed when the worker last completed the step."""
|
|
155
|
+
|
|
156
|
+
id: str
|
|
157
|
+
hook: bool
|
|
158
|
+
text: str | None
|
|
159
|
+
command: str
|
|
160
|
+
output: str
|
|
161
|
+
# A verifier's verdict rather than a command: ``output`` is its evidence.
|
|
162
|
+
judged: bool = False
|
|
163
|
+
# For a derived check: the rules it covers, whose texts are ``text``.
|
|
164
|
+
covers: tuple[str, ...] = ()
|
|
165
|
+
|
|
166
|
+
def to_dict(self) -> dict[str, object]:
|
|
167
|
+
return {
|
|
168
|
+
"id": self.id,
|
|
169
|
+
"hook": self.hook,
|
|
170
|
+
"text": self.text,
|
|
171
|
+
"command": self.command,
|
|
172
|
+
"output": self.output,
|
|
173
|
+
"judged": self.judged,
|
|
174
|
+
"covers": list(self.covers),
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
@dataclass(frozen=True)
|
|
179
|
+
class DisputeView:
|
|
180
|
+
"""A worker's dispute of a check, as the operator decides it.
|
|
181
|
+
|
|
182
|
+
``failure`` is the disputed check as it last failed, ``reason`` the
|
|
183
|
+
worker's argument, ``attempt`` the rejected completion it answers.
|
|
184
|
+
"""
|
|
185
|
+
|
|
186
|
+
failure: FixFailure
|
|
187
|
+
reason: str
|
|
188
|
+
attempt: int
|
|
189
|
+
|
|
190
|
+
def to_dict(self) -> dict[str, object]:
|
|
191
|
+
return {
|
|
192
|
+
"check": self.failure.to_dict(),
|
|
193
|
+
"reason": self.reason,
|
|
194
|
+
"attempt": self.attempt,
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
@dataclass(frozen=True)
|
|
199
|
+
class CheckPreview:
|
|
200
|
+
"""What ``ww check`` found: the step's checks run now, nothing recorded.
|
|
201
|
+
|
|
202
|
+
``failures`` are shown like a fix page; ``passed`` and ``not_applicable``
|
|
203
|
+
name the other checks; ``judged`` are the rules a verifier judges only
|
|
204
|
+
when the step completes; ``waived`` the checks the operator waived.
|
|
205
|
+
"""
|
|
206
|
+
|
|
207
|
+
task_id: str
|
|
208
|
+
step: str
|
|
209
|
+
checks: int
|
|
210
|
+
failures: tuple[FixFailure, ...] = ()
|
|
211
|
+
passed: tuple[str, ...] = ()
|
|
212
|
+
not_applicable: tuple[str, ...] = ()
|
|
213
|
+
judged: tuple[str, ...] = ()
|
|
214
|
+
waived: tuple[tuple[str, str], ...] = ()
|
|
215
|
+
all_files: bool = False
|
|
216
|
+
|
|
217
|
+
def to_dict(self) -> dict[str, object]:
|
|
218
|
+
return {
|
|
219
|
+
"task_id": self.task_id,
|
|
220
|
+
"step": self.step,
|
|
221
|
+
"checks": self.checks,
|
|
222
|
+
"passed": not self.failures,
|
|
223
|
+
"failures": [failure.to_dict() for failure in self.failures],
|
|
224
|
+
"passed_checks": list(self.passed),
|
|
225
|
+
"not_applicable": list(self.not_applicable),
|
|
226
|
+
"judged_at_completion": list(self.judged),
|
|
227
|
+
"waived": dict(self.waived),
|
|
228
|
+
"all_files": self.all_files,
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
@dataclass(frozen=True)
|
|
233
|
+
class VerificationRuleLine:
|
|
234
|
+
"""One rule on a verification page, which the verifier judges."""
|
|
235
|
+
|
|
236
|
+
id: str
|
|
237
|
+
text: str
|
|
238
|
+
interpretation: str | None = None
|
|
239
|
+
# Its converted ``check`` does not apply here: this configuration file is
|
|
240
|
+
# missing in the step's directory.
|
|
241
|
+
check: str | None = None
|
|
242
|
+
missing: str | None = None
|
|
243
|
+
|
|
244
|
+
def to_dict(self) -> dict[str, object]:
|
|
245
|
+
return {
|
|
246
|
+
"id": self.id,
|
|
247
|
+
"text": self.text,
|
|
248
|
+
"interpretation": self.interpretation,
|
|
249
|
+
"check": self.check,
|
|
250
|
+
"missing": self.missing,
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
@dataclass(frozen=True)
|
|
255
|
+
class VerificationPage:
|
|
256
|
+
"""What a verification item's worker needs: the rules and the evidence.
|
|
257
|
+
|
|
258
|
+
``files`` is the verified step's change set, or every file when
|
|
259
|
+
``all_files`` (no git); ``diff_command`` shows the change itself;
|
|
260
|
+
``draft_artifact`` is the path of the step worker's held artifact.
|
|
261
|
+
"""
|
|
262
|
+
|
|
263
|
+
step: str
|
|
264
|
+
rules: tuple[VerificationRuleLine, ...]
|
|
265
|
+
files: tuple[str, ...] = ()
|
|
266
|
+
all_files: bool = False
|
|
267
|
+
diff_command: str | None = None
|
|
268
|
+
draft_artifact: str | None = None
|
|
269
|
+
|
|
270
|
+
def to_dict(self) -> dict[str, object]:
|
|
271
|
+
return {
|
|
272
|
+
"step": self.step,
|
|
273
|
+
"rules": [rule.to_dict() for rule in self.rules],
|
|
274
|
+
"files": list(self.files),
|
|
275
|
+
"all_files": self.all_files,
|
|
276
|
+
"diff_command": self.diff_command,
|
|
277
|
+
"draft_artifact": self.draft_artifact,
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
@dataclass(frozen=True)
|
|
282
|
+
class FixRequired:
|
|
283
|
+
"""ww refused the step's completion: the failed checks and the count.
|
|
284
|
+
|
|
285
|
+
``attempt`` is the number of rejected completions so far and
|
|
286
|
+
``max_fixes`` the most any of the step's checks allows.
|
|
287
|
+
"""
|
|
288
|
+
|
|
289
|
+
attempt: int
|
|
290
|
+
max_fixes: int
|
|
291
|
+
checks: int
|
|
292
|
+
failures: tuple[FixFailure, ...]
|
|
293
|
+
draft_artifact: str | None = None
|
|
294
|
+
|
|
295
|
+
def to_dict(self) -> dict[str, object]:
|
|
296
|
+
return {
|
|
297
|
+
"attempt": self.attempt,
|
|
298
|
+
"max_fixes": self.max_fixes,
|
|
299
|
+
"checks": self.checks,
|
|
300
|
+
"failures": [failure.to_dict() for failure in self.failures],
|
|
301
|
+
"draft_artifact": self.draft_artifact,
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
@dataclass(frozen=True)
|
|
306
|
+
class HandoffStep:
|
|
307
|
+
"""One item a worker performed in an assignment, as the handoff reports it.
|
|
308
|
+
|
|
309
|
+
``outcome`` is ``completed``, ``loop break``, ``loop continue``,
|
|
310
|
+
``held for verification``, ``failed``, or ``not completed``; ``checks``
|
|
311
|
+
pairs each check of the last attempt with its status, and ``fix_rounds``
|
|
312
|
+
counts the completions ww rejected.
|
|
313
|
+
"""
|
|
314
|
+
|
|
315
|
+
name: str
|
|
316
|
+
outcome: str
|
|
317
|
+
artifact: str | None = None
|
|
318
|
+
checks: tuple[tuple[str, str], ...] = ()
|
|
319
|
+
checks_waived: tuple[str, ...] = ()
|
|
320
|
+
fix_rounds: int = 0
|
|
321
|
+
error: str | None = None
|
|
322
|
+
|
|
323
|
+
def to_dict(self) -> dict[str, object]:
|
|
324
|
+
return {
|
|
325
|
+
"name": self.name,
|
|
326
|
+
"outcome": self.outcome,
|
|
327
|
+
"artifact": self.artifact,
|
|
328
|
+
"checks": dict(self.checks),
|
|
329
|
+
"checks_waived": list(self.checks_waived),
|
|
330
|
+
"fix_rounds": self.fix_rounds,
|
|
331
|
+
"error": self.error,
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
@dataclass(frozen=True)
|
|
336
|
+
class HandoffBlock:
|
|
337
|
+
"""What ww tells the manager when a worker's assignment ends.
|
|
338
|
+
|
|
339
|
+
Built from the saved state, so the manager never depends on free text
|
|
340
|
+
from the worker; the worker's own words are only its short ``summary``.
|
|
341
|
+
``files`` is ``None`` when no change set was taken: no step of the
|
|
342
|
+
assignment has rules or checks, or the directory has no git.
|
|
343
|
+
"""
|
|
344
|
+
|
|
345
|
+
task_id: str
|
|
346
|
+
token: str
|
|
347
|
+
steps: tuple[HandoffStep, ...] = ()
|
|
348
|
+
files: tuple[str, ...] | None = None
|
|
349
|
+
summary: str | None = None
|
|
350
|
+
error: str | None = None
|
|
351
|
+
|
|
352
|
+
def to_dict(self) -> dict[str, object]:
|
|
353
|
+
return {
|
|
354
|
+
"task_id": self.task_id,
|
|
355
|
+
"assignment": self.token,
|
|
356
|
+
"steps": [step.to_dict() for step in self.steps],
|
|
357
|
+
"files": None if self.files is None else list(self.files),
|
|
358
|
+
"summary": self.summary,
|
|
359
|
+
"error": self.error,
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
@dataclass(frozen=True)
|
|
364
|
+
class Instruction:
|
|
365
|
+
"""A presentation-ready view derived only from persisted execution state."""
|
|
366
|
+
|
|
367
|
+
task_id: str
|
|
368
|
+
workflow: str
|
|
369
|
+
status: InstructionStatus
|
|
370
|
+
item_id: str | None
|
|
371
|
+
item_name: str | None
|
|
372
|
+
stage: str | None
|
|
373
|
+
step: str | None
|
|
374
|
+
parent: str | None
|
|
375
|
+
item_status: ItemStatus | None
|
|
376
|
+
action_kind: PlanItemKind | None
|
|
377
|
+
action_text: str | None
|
|
378
|
+
loop_break_prompt: str | None = None
|
|
379
|
+
loop_break_command: str | None = None
|
|
380
|
+
# The break ends the per-child stages, skipping the remaining children.
|
|
381
|
+
breaks_children: bool = False
|
|
382
|
+
loop_continue_prompt: str | None = None
|
|
383
|
+
loop_continue_command: str | None = None
|
|
384
|
+
loop_iteration: int | None = None
|
|
385
|
+
max_rounds: int | None = None
|
|
386
|
+
loop_limit_reached: bool = False
|
|
387
|
+
# The enclosing loop of a body step, so the round can be described.
|
|
388
|
+
loop_name: str | None = None
|
|
389
|
+
# The task requirements saved by ``init``, repeated to every worker.
|
|
390
|
+
task_requirements: str | None = None
|
|
391
|
+
# The full requirements are printed on the task's first work page; later
|
|
392
|
+
# pages point at ``requirements_command`` instead.
|
|
393
|
+
requirements_in_full: bool = True
|
|
394
|
+
requirements_command: str | None = None
|
|
395
|
+
# Amendments appended to the requirements, oldest first; every page lists
|
|
396
|
+
# them in full.
|
|
397
|
+
task_amendments: tuple[Amendment, ...] = ()
|
|
398
|
+
# The most recently completed step's handover and artifact reference, so a
|
|
399
|
+
# worker knows what was just done. Hooks and the summary never qualify.
|
|
400
|
+
previous_step: str | None = None
|
|
401
|
+
previous_step_artifact: str | None = None
|
|
402
|
+
previous_step_summary: str | None = None
|
|
403
|
+
# The completion of this step must include a summary for the next step.
|
|
404
|
+
summary_required: bool = False
|
|
405
|
+
# For the built-in workflow summary: every step's handover in this run.
|
|
406
|
+
run_handovers: tuple[StepHandover, ...] = ()
|
|
407
|
+
# For pending input: the steps completed since the waiting handler last
|
|
408
|
+
# ran in this run, so the values describe current work.
|
|
409
|
+
input_context: tuple[StepHandover, ...] = ()
|
|
410
|
+
# Documents the active step promised to create or update in place.
|
|
411
|
+
documents: tuple[DocumentTask, ...] = ()
|
|
412
|
+
# An interactive step: its conversation state and the recording commands.
|
|
413
|
+
interactive: bool = False
|
|
414
|
+
# This agent-owned step requests concrete pre/post operation visibility.
|
|
415
|
+
explicit: bool = False
|
|
416
|
+
# Optional suggestion after completion; never a plan item or assignment.
|
|
417
|
+
feedback_deduction_command: str | None = None
|
|
418
|
+
interaction_entries: int = 0
|
|
419
|
+
interaction_ended: bool = False
|
|
420
|
+
interact_commands: InteractCommands | None = None
|
|
421
|
+
# Operator choices of an interactive step, the agent's way to offer them,
|
|
422
|
+
# and what was chosen so far.
|
|
423
|
+
choices: tuple[ChoiceDefinition, ...] = ()
|
|
424
|
+
choice_mechanism: str | None = None
|
|
425
|
+
chosen: str | None = None
|
|
426
|
+
# The operator said they are done for now; the agent stops until they
|
|
427
|
+
# return.
|
|
428
|
+
operator_paused: bool = False
|
|
429
|
+
# The conversation of this step so far, as recorded.
|
|
430
|
+
conversation: tuple[ConversationEntry, ...] = ()
|
|
431
|
+
# A per-item stage answered on the operator page rather than in a
|
|
432
|
+
# conversation; the page is an extra the core knows only by this flag.
|
|
433
|
+
ui: bool = False
|
|
434
|
+
# A collection step of a shared item flow: the items it reconciles.
|
|
435
|
+
shared_items: bool = False
|
|
436
|
+
stored_items: tuple[WorkItem, ...] = ()
|
|
437
|
+
# Custom item fields this step must set, and the flow's identity and
|
|
438
|
+
# unique fields when this step is the collection.
|
|
439
|
+
required_item_fields: tuple[ItemFieldUpdate, ...] = ()
|
|
440
|
+
collects_items: bool = False
|
|
441
|
+
item_identity: str | None = None
|
|
442
|
+
item_unique: tuple[str, ...] = ()
|
|
443
|
+
# Pending input on an input-only assignment: the manager supplies it.
|
|
444
|
+
manager_input: bool = False
|
|
445
|
+
required_values: tuple[ProvidedVariable, ...] = ()
|
|
446
|
+
# What a retried handler was given last time, per requested value.
|
|
447
|
+
previous_values: tuple[tuple[str, str], ...] = ()
|
|
448
|
+
required_metadata: tuple[SavedMetadata, ...] = ()
|
|
449
|
+
automatic_context: tuple[str, ...] = ()
|
|
450
|
+
has_previous_artifacts: bool = False
|
|
451
|
+
next_steps: tuple[str, ...] = ()
|
|
452
|
+
artifact: str | None = None
|
|
453
|
+
continuation_command: str | None = None
|
|
454
|
+
error: str | None = None
|
|
455
|
+
handoff: str | None = None
|
|
456
|
+
# The workflow a completed run offers the operator next.
|
|
457
|
+
recommended_workflow: str | None = None
|
|
458
|
+
# A completed child task's parent, which the manager continues next.
|
|
459
|
+
parent_task_id: str | None = None
|
|
460
|
+
# An assessment's answers and what each does: on the assessment's own
|
|
461
|
+
# page, and on the page that asks ``next`` for the chosen one.
|
|
462
|
+
assessment_outcomes: tuple[AssessmentOutcome, ...] = ()
|
|
463
|
+
# This page asks for the outcome of the named, completed assessment.
|
|
464
|
+
choosing_outcome_of: str | None = None
|
|
465
|
+
profile_instruction: str | None = None
|
|
466
|
+
run_id: str | None = None
|
|
467
|
+
task_runs: tuple[WorkflowRunSummary, ...] = ()
|
|
468
|
+
working_directory: str | None = None
|
|
469
|
+
child_tasks: tuple[ChildTask, ...] = ()
|
|
470
|
+
operation_id: str | None = None
|
|
471
|
+
recovery_commands: tuple[RecoveryCommand, ...] = ()
|
|
472
|
+
workflow_runtime: str = "single"
|
|
473
|
+
# The agent the task was started for, as ``start --agent`` named it.
|
|
474
|
+
agent: str = ""
|
|
475
|
+
workflow_runtime_instruction: tuple[str, ...] = ()
|
|
476
|
+
model: str = "auto"
|
|
477
|
+
reasoning: str = "auto"
|
|
478
|
+
requested_agent: str | None = None
|
|
479
|
+
requested_model: str | None = None
|
|
480
|
+
requested_reasoning: str | None = None
|
|
481
|
+
requested_profile: str | None = None
|
|
482
|
+
role: StepRole = "worker"
|
|
483
|
+
# ``False``: whoever performs the item spawns no subagents.
|
|
484
|
+
subagents: bool = True
|
|
485
|
+
selected_agent: str | None = None
|
|
486
|
+
selected_model: str | None = None
|
|
487
|
+
selected_reasoning: str | None = None
|
|
488
|
+
assignment_preview: dict[str, object] | None = None
|
|
489
|
+
# The first stage of a multi-stage item assignment describes its scope;
|
|
490
|
+
# later stages of the same assignment are rendered compactly.
|
|
491
|
+
assignment_scope: dict[str, object] | None = None
|
|
492
|
+
# Agent-owned explicit steps covered by this worker assignment.
|
|
493
|
+
assignment_explicit_steps: tuple[str, ...] = ()
|
|
494
|
+
continues_assignment: bool = False
|
|
495
|
+
# The assignment the current item belongs to, in the ``auto`` runtime: the
|
|
496
|
+
# step that drives worker selection, every agent or input item it covers,
|
|
497
|
+
# and whether the current item is a later one the worker already holds.
|
|
498
|
+
assignment_step: str | None = None
|
|
499
|
+
assignment_items: tuple[str, ...] = ()
|
|
500
|
+
assignment_continues: bool = False
|
|
501
|
+
manager_intro: bool = False
|
|
502
|
+
# On the first page of ``start``: the declared rules no check covers yet.
|
|
503
|
+
rules_notice: str | None = None
|
|
504
|
+
# One-line notes ww shows under the page header: what it did on the
|
|
505
|
+
# caller's behalf (started a child, dispatched the next step).
|
|
506
|
+
notices: tuple[str, ...] = ()
|
|
507
|
+
completion_registered: bool = False
|
|
508
|
+
# The completion was accepted but held: verifiers judge its rules first.
|
|
509
|
+
completion_held: bool = False
|
|
510
|
+
caller_role: CallerRole | None = None
|
|
511
|
+
next_role: NextRole | None = None
|
|
512
|
+
control: Control | None = None
|
|
513
|
+
# Set exactly when ``control`` is ``awaiting_operator``.
|
|
514
|
+
operator_reason: OperatorReason | None = None
|
|
515
|
+
result_saved: bool | None = None
|
|
516
|
+
# The rules the active step is given, and, after a rejected completion,
|
|
517
|
+
# what failed; ``checks_waived`` are the checks the operator waived for
|
|
518
|
+
# the step, each with the reason; ``dispute`` is the worker's objection
|
|
519
|
+
# to one at a ``check_disputed`` stop.
|
|
520
|
+
rules: tuple[RuleLine, ...] = ()
|
|
521
|
+
# The modes the active step works in: the run's selected modes, then the
|
|
522
|
+
# automatic ones whose filters admit the step.
|
|
523
|
+
modes: tuple[PlannedMode, ...] = ()
|
|
524
|
+
fix_required: FixRequired | None = None
|
|
525
|
+
handler_repair: dict[str, object] | None = None
|
|
526
|
+
checks_waived: tuple[tuple[str, str], ...] = ()
|
|
527
|
+
dispute: DisputeView | None = None
|
|
528
|
+
# A worker asked for an item the manager performs itself (``role:
|
|
529
|
+
# manager`` or ``interactive`` in the ``auto`` runtime): the page names it
|
|
530
|
+
# and offers no completion command.
|
|
531
|
+
manager_only: bool = False
|
|
532
|
+
# The open assignment's token in the ``auto`` runtime, carried by every
|
|
533
|
+
# worker command the page prints.
|
|
534
|
+
assignment_token: str | None = None
|
|
535
|
+
# The worker's assignment ended with this command: ww's report of it,
|
|
536
|
+
# which the worker returns to the manager verbatim.
|
|
537
|
+
handoff_block: HandoffBlock | None = None
|
|
538
|
+
# A verification item's rules and evidence.
|
|
539
|
+
verification: VerificationPage | None = None
|
|
540
|
+
# At a ``plan_changed`` stop: how the workflow's current definition
|
|
541
|
+
# differs from the run's saved plan, which the operator takes or declines.
|
|
542
|
+
plan_change: PlanChange | None = None
|
|
543
|
+
# Internal capability markers let presentation and service refresh paths
|
|
544
|
+
# avoid rediscovering a saved plan or matching built-in action names.
|
|
545
|
+
is_child_workflow_control: bool = False
|
|
546
|
+
is_loop_control: bool = False
|
|
547
|
+
|
|
548
|
+
def to_dict(self) -> dict[str, object]:
|
|
549
|
+
return {
|
|
550
|
+
"task_id": self.task_id,
|
|
551
|
+
"workflow": self.workflow,
|
|
552
|
+
"status": self.status,
|
|
553
|
+
"item_id": self.item_id,
|
|
554
|
+
"item_name": self.item_name,
|
|
555
|
+
"stage": self.stage,
|
|
556
|
+
"step": self.step,
|
|
557
|
+
"parent": self.parent,
|
|
558
|
+
"item_status": self.item_status,
|
|
559
|
+
"action_kind": self.action_kind,
|
|
560
|
+
"action_text": self.action_text,
|
|
561
|
+
"loop_break_prompt": self.loop_break_prompt,
|
|
562
|
+
"loop_break_command": self.loop_break_command,
|
|
563
|
+
"breaks_children": self.breaks_children,
|
|
564
|
+
"loop_continue_prompt": self.loop_continue_prompt,
|
|
565
|
+
"loop_continue_command": self.loop_continue_command,
|
|
566
|
+
"loop_iteration": self.loop_iteration,
|
|
567
|
+
"max_rounds": self.max_rounds,
|
|
568
|
+
"loop_limit_reached": self.loop_limit_reached,
|
|
569
|
+
"loop_name": self.loop_name,
|
|
570
|
+
"task_requirements": self.task_requirements,
|
|
571
|
+
"requirements_in_full": self.requirements_in_full,
|
|
572
|
+
"requirements_command": self.requirements_command,
|
|
573
|
+
"task_amendments": [entry.to_dict() for entry in self.task_amendments],
|
|
574
|
+
"previous_step": self.previous_step,
|
|
575
|
+
"previous_step_artifact": self.previous_step_artifact,
|
|
576
|
+
"previous_step_summary": self.previous_step_summary,
|
|
577
|
+
"summary_required": self.summary_required,
|
|
578
|
+
"run_handovers": [item.to_dict() for item in self.run_handovers],
|
|
579
|
+
"input_context": [item.to_dict() for item in self.input_context],
|
|
580
|
+
"documents": [item.to_dict() for item in self.documents],
|
|
581
|
+
"interactive": self.interactive,
|
|
582
|
+
"explicit": self.explicit,
|
|
583
|
+
"feedback_deduction_command": self.feedback_deduction_command,
|
|
584
|
+
"interaction_entries": self.interaction_entries,
|
|
585
|
+
"interaction_ended": self.interaction_ended,
|
|
586
|
+
"interact_commands": (
|
|
587
|
+
self.interact_commands.to_dict() if self.interact_commands else None
|
|
588
|
+
),
|
|
589
|
+
"choices": [choice.to_dict() for choice in self.choices],
|
|
590
|
+
"choice_mechanism": self.choice_mechanism,
|
|
591
|
+
"chosen": self.chosen,
|
|
592
|
+
"operator_paused": self.operator_paused,
|
|
593
|
+
"conversation": [entry.to_dict() for entry in self.conversation],
|
|
594
|
+
"ui": self.ui,
|
|
595
|
+
"shared_items": self.shared_items,
|
|
596
|
+
"stored_items": [item.to_dict() for item in self.stored_items],
|
|
597
|
+
"required_item_fields": [
|
|
598
|
+
field.to_dict() for field in self.required_item_fields
|
|
599
|
+
],
|
|
600
|
+
"collects_items": self.collects_items,
|
|
601
|
+
"item_identity": self.item_identity,
|
|
602
|
+
"item_unique": list(self.item_unique),
|
|
603
|
+
"manager_input": self.manager_input,
|
|
604
|
+
"required_values": [value.to_dict() for value in self.required_values],
|
|
605
|
+
"previous_values": dict(self.previous_values),
|
|
606
|
+
"required_metadata": [value.to_dict() for value in self.required_metadata],
|
|
607
|
+
"automatic_context": list(self.automatic_context),
|
|
608
|
+
"has_previous_artifacts": self.has_previous_artifacts,
|
|
609
|
+
"next_steps": list(self.next_steps),
|
|
610
|
+
"artifact": self.artifact,
|
|
611
|
+
"continuation_command": self.continuation_command,
|
|
612
|
+
"error": self.error,
|
|
613
|
+
"handoff": self.handoff,
|
|
614
|
+
"recommended_workflow": self.recommended_workflow,
|
|
615
|
+
"parent_task_id": self.parent_task_id,
|
|
616
|
+
"assessment_outcomes": [
|
|
617
|
+
{
|
|
618
|
+
"label": outcome.label,
|
|
619
|
+
"stops": outcome.stops,
|
|
620
|
+
"first_step": outcome.first_step,
|
|
621
|
+
}
|
|
622
|
+
for outcome in self.assessment_outcomes
|
|
623
|
+
],
|
|
624
|
+
"choosing_outcome_of": self.choosing_outcome_of,
|
|
625
|
+
"profile_instruction": self.profile_instruction,
|
|
626
|
+
"run_id": self.run_id,
|
|
627
|
+
"task_runs": [item.to_dict() for item in self.task_runs],
|
|
628
|
+
"working_directory": self.working_directory,
|
|
629
|
+
"child_tasks": [child.to_dict() for child in self.child_tasks],
|
|
630
|
+
"operation_id": self.operation_id,
|
|
631
|
+
"recovery_commands": [item.to_dict() for item in self.recovery_commands],
|
|
632
|
+
"workflow_runtime": self.workflow_runtime,
|
|
633
|
+
"agent": self.agent,
|
|
634
|
+
"workflow_runtime_instruction": list(self.workflow_runtime_instruction),
|
|
635
|
+
"model": self.model,
|
|
636
|
+
"reasoning": self.reasoning,
|
|
637
|
+
"requested_agent": self.requested_agent,
|
|
638
|
+
"requested_model": self.requested_model,
|
|
639
|
+
"requested_reasoning": self.requested_reasoning,
|
|
640
|
+
"requested_profile": self.requested_profile,
|
|
641
|
+
"role": self.role,
|
|
642
|
+
"subagents": self.subagents,
|
|
643
|
+
"selected_agent": self.selected_agent,
|
|
644
|
+
"selected_model": self.selected_model,
|
|
645
|
+
"selected_reasoning": self.selected_reasoning,
|
|
646
|
+
"assignment_preview": self.assignment_preview,
|
|
647
|
+
"assignment_scope": self.assignment_scope,
|
|
648
|
+
"continues_assignment": self.continues_assignment,
|
|
649
|
+
"assignment_step": self.assignment_step,
|
|
650
|
+
"assignment_items": list(self.assignment_items),
|
|
651
|
+
"assignment_explicit_steps": list(self.assignment_explicit_steps),
|
|
652
|
+
"assignment_continues": self.assignment_continues,
|
|
653
|
+
"manager_intro": self.manager_intro,
|
|
654
|
+
"rules_notice": self.rules_notice,
|
|
655
|
+
"notices": list(self.notices),
|
|
656
|
+
"completion_registered": self.completion_registered,
|
|
657
|
+
"completion_held": self.completion_held,
|
|
658
|
+
"caller_role": self.caller_role,
|
|
659
|
+
"next_role": self.next_role,
|
|
660
|
+
"control": self.control,
|
|
661
|
+
"operator_reason": self.operator_reason,
|
|
662
|
+
"result_saved": self.result_saved,
|
|
663
|
+
"rules": [rule.to_dict() for rule in self.rules],
|
|
664
|
+
"modes": [mode.to_dict() for mode in self.modes],
|
|
665
|
+
**(
|
|
666
|
+
{"handler_repair": self.handler_repair}
|
|
667
|
+
if self.handler_repair is not None
|
|
668
|
+
else {}
|
|
669
|
+
),
|
|
670
|
+
"fix_required": (
|
|
671
|
+
self.fix_required.to_dict() if self.fix_required else None
|
|
672
|
+
),
|
|
673
|
+
"checks_waived": dict(self.checks_waived),
|
|
674
|
+
"dispute": self.dispute.to_dict() if self.dispute else None,
|
|
675
|
+
"manager_only": self.manager_only,
|
|
676
|
+
"assignment_token": self.assignment_token,
|
|
677
|
+
"handoff_block": (
|
|
678
|
+
self.handoff_block.to_dict() if self.handoff_block else None
|
|
679
|
+
),
|
|
680
|
+
"verification": (
|
|
681
|
+
self.verification.to_dict() if self.verification else None
|
|
682
|
+
),
|
|
683
|
+
"plan_change": (
|
|
684
|
+
self.plan_change.to_dict() if self.plan_change is not None else None
|
|
685
|
+
),
|
|
686
|
+
}
|