@kody-ade/kody-engine 0.4.430 → 0.4.432
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.
- package/dist/bin/kody.js +518 -131
- package/dist/capabilities/run/definition.json +42 -0
- package/dist/implementations/run/definition.json +13 -0
- package/dist/implementations/run/{profile.json → runtime.json} +1 -2
- package/dist/implementations/types.ts +8 -0
- package/dist/runtime-services/capability-scheduler/profile.json +61 -0
- package/dist/runtime-services/capability-tick/profile.json +86 -0
- package/dist/runtime-services/capability-tick/prompt.md +77 -0
- package/dist/runtime-services/capability-tick/prompts/locked.md +50 -0
- package/dist/runtime-services/capability-tick-scripted/profile.json +76 -0
- package/dist/runtime-services/dispatch-due-loops/profile.json +54 -0
- package/dist/runtime-services/goal-manager/profile.json +56 -0
- package/dist/runtime-services/goal-scheduler/__pycache__/managed_todo_state.cpython-314.pyc +0 -0
- package/dist/runtime-services/goal-scheduler/managed_todo_state.py +160 -0
- package/dist/runtime-services/goal-scheduler/profile.json +51 -0
- package/dist/runtime-services/goal-scheduler/scheduler.sh +872 -0
- package/dist/runtime-services/task-jobs/profile.json +51 -0
- package/package.json +3 -1
- package/dist/capabilities/run/profile.json +0 -6
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"""Managed-goal todo state contract for goal-scheduler.
|
|
2
|
+
|
|
3
|
+
The scheduler owns timing and dispatch. This module owns the todo JSON shape
|
|
4
|
+
used for managed goal state.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def is_managed_todo_text(text: str) -> bool:
|
|
11
|
+
data = parse_json_object(text)
|
|
12
|
+
return is_managed_todo_data(data) if data is not None else False
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def is_managed_todo_data(data: dict) -> bool:
|
|
16
|
+
return (
|
|
17
|
+
data.get("managed") is True
|
|
18
|
+
or data.get("managed") == "true"
|
|
19
|
+
or data.get("managedModel") in ("agentGoal", "agentLoop")
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def parse_json_object(text: str) -> dict | None:
|
|
24
|
+
try:
|
|
25
|
+
data = json.loads(text)
|
|
26
|
+
except Exception:
|
|
27
|
+
return None
|
|
28
|
+
return data if isinstance(data, dict) else None
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def string_list(value: object) -> list[str]:
|
|
32
|
+
return [item for item in value if isinstance(item, str)] if isinstance(value, list) else []
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def route_from_items(items: list[dict]) -> list[dict]:
|
|
36
|
+
route: list[dict] = []
|
|
37
|
+
for item in items:
|
|
38
|
+
meta = item.get("meta") if isinstance(item.get("meta"), dict) else {}
|
|
39
|
+
stage = meta.get("stage")
|
|
40
|
+
evidence = meta.get("evidence") or item.get("id")
|
|
41
|
+
capability = meta.get("capability")
|
|
42
|
+
if isinstance(stage, str) and isinstance(evidence, str) and isinstance(capability, str):
|
|
43
|
+
step = {"stage": stage, "evidence": evidence, "capability": capability}
|
|
44
|
+
if isinstance(meta.get("args"), dict):
|
|
45
|
+
step["args"] = meta["args"]
|
|
46
|
+
if meta.get("saveReport") is True:
|
|
47
|
+
step["saveReport"] = True
|
|
48
|
+
route.append(step)
|
|
49
|
+
return route
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def parse_json_todo_goal_state(goal_id: str, data: dict) -> dict:
|
|
53
|
+
items = [item for item in data.get("items", []) if isinstance(item, dict)] if isinstance(data.get("items"), list) else []
|
|
54
|
+
raw_destination = data.get("destination") if isinstance(data.get("destination"), dict) else {}
|
|
55
|
+
route = data.get("route") if isinstance(data.get("route"), list) else route_from_items(items)
|
|
56
|
+
evidence = string_list(raw_destination.get("evidence")) or string_list(data.get("evidence"))
|
|
57
|
+
if not evidence:
|
|
58
|
+
evidence = [
|
|
59
|
+
str((item.get("meta") if isinstance(item.get("meta"), dict) else {}).get("evidence") or item.get("id"))
|
|
60
|
+
for item in items
|
|
61
|
+
if item.get("id")
|
|
62
|
+
]
|
|
63
|
+
facts = data.get("facts") if isinstance(data.get("facts"), dict) else {}
|
|
64
|
+
facts = dict(facts)
|
|
65
|
+
for item in items:
|
|
66
|
+
meta = item.get("meta") if isinstance(item.get("meta"), dict) else {}
|
|
67
|
+
key = meta.get("evidence") or item.get("id")
|
|
68
|
+
if isinstance(key, str):
|
|
69
|
+
facts[key] = item.get("completed") is True
|
|
70
|
+
capabilities = string_list(data.get("capabilities"))
|
|
71
|
+
if not capabilities:
|
|
72
|
+
capabilities = [step["capability"] for step in route if isinstance(step.get("capability"), str)]
|
|
73
|
+
outcome = data.get("description") if isinstance(data.get("description"), str) else raw_destination.get("outcome")
|
|
74
|
+
parsed = dict(data)
|
|
75
|
+
parsed.update(
|
|
76
|
+
{
|
|
77
|
+
"id": goal_id,
|
|
78
|
+
"version": data.get("version", 1),
|
|
79
|
+
"state": data.get("state", "active"),
|
|
80
|
+
"type": data.get("type", "general"),
|
|
81
|
+
"destination": {**raw_destination, "outcome": outcome if isinstance(outcome, str) else "", "evidence": evidence},
|
|
82
|
+
"capabilities": capabilities,
|
|
83
|
+
"route": route,
|
|
84
|
+
"facts": facts,
|
|
85
|
+
"blockers": string_list(data.get("blockers")),
|
|
86
|
+
}
|
|
87
|
+
)
|
|
88
|
+
return parsed
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def parse_todo_goal_state(goal_id: str, text: str) -> dict:
|
|
92
|
+
data = parse_json_object(text)
|
|
93
|
+
if data is None:
|
|
94
|
+
raise ValueError(f"goal {goal_id} todo state must be JSON")
|
|
95
|
+
return parse_json_todo_goal_state(goal_id, data)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def todo_items_from_state(data: dict, now: str) -> list[dict]:
|
|
99
|
+
destination = data.get("destination") if isinstance(data.get("destination"), dict) else {}
|
|
100
|
+
evidence = string_list(destination.get("evidence"))
|
|
101
|
+
route = data.get("route") if isinstance(data.get("route"), list) else []
|
|
102
|
+
facts = data.get("facts") if isinstance(data.get("facts"), dict) else {}
|
|
103
|
+
route_by_evidence = {
|
|
104
|
+
step.get("evidence"): step
|
|
105
|
+
for step in route
|
|
106
|
+
if isinstance(step, dict) and isinstance(step.get("evidence"), str)
|
|
107
|
+
}
|
|
108
|
+
if evidence:
|
|
109
|
+
items = []
|
|
110
|
+
for key in evidence:
|
|
111
|
+
step = route_by_evidence.get(key) if isinstance(route_by_evidence.get(key), dict) else {}
|
|
112
|
+
completed = facts.get(key) is True
|
|
113
|
+
items.append(
|
|
114
|
+
{
|
|
115
|
+
"id": key,
|
|
116
|
+
"title": step.get("stage") if isinstance(step.get("stage"), str) else key,
|
|
117
|
+
"body": "",
|
|
118
|
+
"assignee": None,
|
|
119
|
+
"completed": completed,
|
|
120
|
+
"createdAt": data.get("createdAt") if isinstance(data.get("createdAt"), str) else now,
|
|
121
|
+
"completedAt": data.get("updatedAt") if completed and isinstance(data.get("updatedAt"), str) else None,
|
|
122
|
+
"meta": {
|
|
123
|
+
"evidence": key,
|
|
124
|
+
**({"stage": step["stage"]} if isinstance(step.get("stage"), str) else {}),
|
|
125
|
+
**({"capability": step["capability"]} if isinstance(step.get("capability"), str) else {}),
|
|
126
|
+
**({"args": step["args"]} if isinstance(step.get("args"), dict) else {}),
|
|
127
|
+
**({"saveReport": True} if step.get("saveReport") is True else {}),
|
|
128
|
+
},
|
|
129
|
+
}
|
|
130
|
+
)
|
|
131
|
+
return items
|
|
132
|
+
return [
|
|
133
|
+
{
|
|
134
|
+
"id": capability,
|
|
135
|
+
"title": capability,
|
|
136
|
+
"body": "",
|
|
137
|
+
"assignee": None,
|
|
138
|
+
"completed": False,
|
|
139
|
+
"createdAt": data.get("createdAt") if isinstance(data.get("createdAt"), str) else now,
|
|
140
|
+
"completedAt": None,
|
|
141
|
+
"meta": {"capability": capability},
|
|
142
|
+
}
|
|
143
|
+
for capability in string_list(data.get("capabilities"))
|
|
144
|
+
]
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def serialize_todo_goal_state(goal_id: str, data: dict, now: str) -> str:
|
|
148
|
+
destination = data.get("destination") if isinstance(data.get("destination"), dict) else {}
|
|
149
|
+
outcome = destination.get("outcome") if isinstance(destination.get("outcome"), str) else ""
|
|
150
|
+
record = dict(data)
|
|
151
|
+
record["version"] = 1
|
|
152
|
+
record["id"] = goal_id
|
|
153
|
+
record["title"] = goal_id
|
|
154
|
+
record["description"] = outcome
|
|
155
|
+
record["createdAt"] = data.get("createdAt") if isinstance(data.get("createdAt"), str) else now
|
|
156
|
+
record["managed"] = True
|
|
157
|
+
record["managedModel"] = "agentLoop" if record.get("scheduleMode") == "agentLoop" or record.get("type") == "agentLoop" else "agentGoal"
|
|
158
|
+
record["evidence"] = string_list(destination.get("evidence"))
|
|
159
|
+
record["items"] = todo_items_from_state(data, now)
|
|
160
|
+
return json.dumps(record, indent=2) + "\n"
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"internal": true,
|
|
3
|
+
"role": "watch",
|
|
4
|
+
"kind": "scheduled",
|
|
5
|
+
"schedule": "*/5 * * * *",
|
|
6
|
+
"claudeCode": {
|
|
7
|
+
"model": "inherit",
|
|
8
|
+
"permissionMode": "default",
|
|
9
|
+
"maxTurns": 0,
|
|
10
|
+
"maxThinkingTokens": null,
|
|
11
|
+
"systemPromptAppend": null,
|
|
12
|
+
"tools": [],
|
|
13
|
+
"hooks": [],
|
|
14
|
+
"skills": [],
|
|
15
|
+
"commands": [],
|
|
16
|
+
"subagents": [],
|
|
17
|
+
"plugins": [],
|
|
18
|
+
"mcpServers": []
|
|
19
|
+
},
|
|
20
|
+
"cliTools": [
|
|
21
|
+
{
|
|
22
|
+
"name": "python3",
|
|
23
|
+
"install": {
|
|
24
|
+
"required": true,
|
|
25
|
+
"checkCommand": "command -v python3"
|
|
26
|
+
},
|
|
27
|
+
"verify": "python3 --version",
|
|
28
|
+
"usage": "scheduler.sh parses each managed goal todo file",
|
|
29
|
+
"allowedUses": []
|
|
30
|
+
}
|
|
31
|
+
],
|
|
32
|
+
"inputArtifacts": [],
|
|
33
|
+
"outputArtifacts": [],
|
|
34
|
+
"scripts": {
|
|
35
|
+
"preflight": [
|
|
36
|
+
{
|
|
37
|
+
"script": "dispatchAgencyLoops"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"shell": "scheduler.sh",
|
|
41
|
+
"timeoutSec": 1800
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"script": "skipAgent"
|
|
45
|
+
}
|
|
46
|
+
],
|
|
47
|
+
"postflight": []
|
|
48
|
+
},
|
|
49
|
+
"inputs": [],
|
|
50
|
+
"name": "goal-scheduler"
|
|
51
|
+
}
|