@openlucaskaka/kagent 0.1.7

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.
Files changed (117) hide show
  1. package/README.md +353 -0
  2. package/npm/bin/kagent-serve.js +6 -0
  3. package/npm/bin/kagent.js +6 -0
  4. package/npm/lib/App.js +524 -0
  5. package/npm/lib/app-state.js +224 -0
  6. package/npm/lib/approval-choice.js +25 -0
  7. package/npm/lib/commands.js +59 -0
  8. package/npm/lib/editor.js +188 -0
  9. package/npm/lib/ink-runner.js +41 -0
  10. package/npm/lib/kagent-home.js +39 -0
  11. package/npm/lib/launcher.js +221 -0
  12. package/npm/lib/protocol.js +33 -0
  13. package/npm/lib/provider-setup.js +139 -0
  14. package/npm/lib/python-runner.js +892 -0
  15. package/npm/lib/runtime-client.js +390 -0
  16. package/npm/lib/terminal-input.js +127 -0
  17. package/npm/lib/terminal-text.js +19 -0
  18. package/npm/lib/terminal-width.js +14 -0
  19. package/npm/lib/transcript.js +227 -0
  20. package/npm/lib/ui-components.js +247 -0
  21. package/npm/lib/update-manager.js +334 -0
  22. package/package.json +39 -0
  23. package/pyproject.toml +55 -0
  24. package/src/kagent/__init__.py +90 -0
  25. package/src/kagent/cli/__init__.py +5 -0
  26. package/src/kagent/cli/__main__.py +6 -0
  27. package/src/kagent/cli/commands.py +112 -0
  28. package/src/kagent/cli/conversation.py +127 -0
  29. package/src/kagent/cli/interactive.py +841 -0
  30. package/src/kagent/cli/main.py +685 -0
  31. package/src/kagent/cli/memory.py +460 -0
  32. package/src/kagent/cli/pending_approval.py +169 -0
  33. package/src/kagent/cli/provider.py +27 -0
  34. package/src/kagent/cli/session_commands.py +401 -0
  35. package/src/kagent/cli/stdio_runtime.py +784 -0
  36. package/src/kagent/cli/trace.py +67 -0
  37. package/src/kagent/cli/ui.py +931 -0
  38. package/src/kagent/core/__init__.py +0 -0
  39. package/src/kagent/core/agent.py +296 -0
  40. package/src/kagent/core/faults.py +11 -0
  41. package/src/kagent/core/invariants.py +47 -0
  42. package/src/kagent/core/normalization.py +81 -0
  43. package/src/kagent/core/planning.py +48 -0
  44. package/src/kagent/core/state.py +73 -0
  45. package/src/kagent/core/summary.py +64 -0
  46. package/src/kagent/core/tools.py +196 -0
  47. package/src/kagent/core/trace.py +57 -0
  48. package/src/kagent/eval/__init__.py +11 -0
  49. package/src/kagent/eval/cases.py +229 -0
  50. package/src/kagent/eval/evaluator.py +216 -0
  51. package/src/kagent/integrations/__init__.py +3 -0
  52. package/src/kagent/integrations/audit.py +95 -0
  53. package/src/kagent/integrations/backends.py +131 -0
  54. package/src/kagent/integrations/memory.py +301 -0
  55. package/src/kagent/ops/__init__.py +0 -0
  56. package/src/kagent/ops/batch.py +156 -0
  57. package/src/kagent/ops/doctor.py +255 -0
  58. package/src/kagent/ops/metrics.py +214 -0
  59. package/src/kagent/ops/release_evidence.py +877 -0
  60. package/src/kagent/ops/release_manifest.py +142 -0
  61. package/src/kagent/ops/trace_replay.py +285 -0
  62. package/src/kagent/providers/__init__.py +7 -0
  63. package/src/kagent/providers/embeddings.py +187 -0
  64. package/src/kagent/providers/llm.py +770 -0
  65. package/src/kagent/py.typed +1 -0
  66. package/src/kagent/runtime/__init__.py +28 -0
  67. package/src/kagent/runtime/action_graph.py +543 -0
  68. package/src/kagent/runtime/agent.py +2089 -0
  69. package/src/kagent/runtime/approval.py +64 -0
  70. package/src/kagent/runtime/cancellation.py +64 -0
  71. package/src/kagent/runtime/checkpoint_state.py +146 -0
  72. package/src/kagent/runtime/checkpoint_storage.py +270 -0
  73. package/src/kagent/runtime/context.py +65 -0
  74. package/src/kagent/runtime/file_transaction.py +195 -0
  75. package/src/kagent/runtime/hooks.py +74 -0
  76. package/src/kagent/runtime/metadata.py +116 -0
  77. package/src/kagent/runtime/patch_checkpoints.py +385 -0
  78. package/src/kagent/runtime/policy.py +50 -0
  79. package/src/kagent/runtime/presentation.py +205 -0
  80. package/src/kagent/runtime/redaction.py +130 -0
  81. package/src/kagent/runtime/sandbox.py +331 -0
  82. package/src/kagent/runtime/skills.py +66 -0
  83. package/src/kagent/runtime/steering.py +56 -0
  84. package/src/kagent/runtime/steps.py +255 -0
  85. package/src/kagent/runtime/task_state.py +40 -0
  86. package/src/kagent/runtime/tools.py +3532 -0
  87. package/src/kagent/runtime/types.py +240 -0
  88. package/src/kagent/runtime/workspace.py +597 -0
  89. package/src/kagent/service/__init__.py +26 -0
  90. package/src/kagent/service/__main__.py +6 -0
  91. package/src/kagent/service/active_runs.py +178 -0
  92. package/src/kagent/service/cli.py +737 -0
  93. package/src/kagent/service/contract.py +2571 -0
  94. package/src/kagent/service/errors.py +71 -0
  95. package/src/kagent/service/idempotency.py +584 -0
  96. package/src/kagent/service/router.py +884 -0
  97. package/src/kagent/service/run.py +150 -0
  98. package/src/kagent/service/runtime.py +1915 -0
  99. package/src/kagent/service/runtime_approval.py +20 -0
  100. package/src/kagent/service/runtime_cancel.py +192 -0
  101. package/src/kagent/service/runtime_lifecycle.py +229 -0
  102. package/src/kagent/service/runtime_metadata.py +21 -0
  103. package/src/kagent/service/runtime_policy.py +115 -0
  104. package/src/kagent/service/runtime_recovery.py +573 -0
  105. package/src/kagent/service/runtime_resume.py +382 -0
  106. package/src/kagent/service/runtime_resume_claim.py +116 -0
  107. package/src/kagent/service/runtime_run.py +350 -0
  108. package/src/kagent/service/runtime_status.py +2007 -0
  109. package/src/kagent/service/safety.py +139 -0
  110. package/src/kagent/service/server.py +114 -0
  111. package/src/kagent/service/status.py +233 -0
  112. package/src/kagent/service/trace_store.py +322 -0
  113. package/src/kagent/service/transport.py +24 -0
  114. package/src/kagent/utils/__init__.py +0 -0
  115. package/src/kagent/utils/config_validation.py +21 -0
  116. package/src/kagent/utils/json_output.py +23 -0
  117. package/src/kagent/utils/paths.py +473 -0
@@ -0,0 +1,240 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from dataclasses import dataclass, field
5
+ from typing import Any, Dict, List
6
+
7
+ MAX_PLAN_ACTIONS = 50
8
+ MAX_ACTION_REASON_CHARS = 2000
9
+ MAX_PLAN_FINAL_ANSWER_CHARS = 20000
10
+ _PLAN_FIELDS = {"actions", "final_answer"}
11
+ _ACTION_FIELDS = {"id", "tool", "input", "reason", "depends_on"}
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class AgentAction:
16
+ id: str
17
+ tool: str
18
+ input: Dict[str, Any]
19
+ reason: str = ""
20
+ depends_on: List[str] = field(default_factory=list)
21
+
22
+ def to_dict(self) -> Dict[str, Any]:
23
+ payload = {
24
+ "id": self.id,
25
+ "tool": self.tool,
26
+ "input": self.input,
27
+ "reason": self.reason,
28
+ }
29
+ if self.depends_on:
30
+ payload["depends_on"] = self.depends_on
31
+ return payload
32
+
33
+
34
+ @dataclass(frozen=True)
35
+ class AgentPlan:
36
+ actions: List[AgentAction]
37
+ final_answer: str = ""
38
+
39
+ def to_dict(self) -> Dict[str, Any]:
40
+ payload: Dict[str, Any] = {
41
+ "actions": [action.to_dict() for action in self.actions]
42
+ }
43
+ if self.final_answer:
44
+ payload["final_answer"] = self.final_answer
45
+ return payload
46
+
47
+
48
+ @dataclass(frozen=True)
49
+ class AgentObservation:
50
+ action_id: str
51
+ tool: str
52
+ status: str
53
+ output: Dict[str, Any]
54
+ error_code: str = ""
55
+ error: str = ""
56
+ started_at: str = ""
57
+ completed_at: str = ""
58
+ duration_seconds: str = ""
59
+
60
+ def to_dict(self) -> Dict[str, Any]:
61
+ payload = {
62
+ "action_id": self.action_id,
63
+ "tool": self.tool,
64
+ "status": self.status,
65
+ "output": self.output,
66
+ }
67
+ if self.error_code:
68
+ payload["error_code"] = self.error_code
69
+ if self.error:
70
+ payload["error"] = self.error
71
+ if self.started_at:
72
+ payload["started_at"] = self.started_at
73
+ if self.completed_at:
74
+ payload["completed_at"] = self.completed_at
75
+ if self.duration_seconds:
76
+ payload["duration_seconds"] = self.duration_seconds
77
+ return payload
78
+
79
+
80
+ def parse_agent_plan(text: str) -> AgentPlan:
81
+ payload = _parse_plan_json_object(text)
82
+ if not isinstance(payload, dict):
83
+ raise ValueError("plan must be a JSON object")
84
+ _reject_unknown_fields(payload, _PLAN_FIELDS, "plan")
85
+ actions_payload = payload.get("actions")
86
+ if not isinstance(actions_payload, list):
87
+ raise ValueError("plan actions must be a list")
88
+ if len(actions_payload) > MAX_PLAN_ACTIONS:
89
+ raise ValueError(f"plan actions must contain at most {MAX_PLAN_ACTIONS} item(s)")
90
+ final_answer = payload.get("final_answer", "")
91
+ if not isinstance(final_answer, str):
92
+ raise ValueError("final_answer must be a string")
93
+ if len(final_answer) > MAX_PLAN_FINAL_ANSWER_CHARS:
94
+ raise ValueError(
95
+ f"final_answer must contain at most {MAX_PLAN_FINAL_ANSWER_CHARS} character(s)"
96
+ )
97
+
98
+ actions = []
99
+ seen_action_ids = set()
100
+ for index, action_payload in enumerate(actions_payload):
101
+ if not isinstance(action_payload, dict):
102
+ raise ValueError(f"action {index} must be an object")
103
+ _reject_unknown_fields(action_payload, _ACTION_FIELDS, "action")
104
+ action_id = action_payload.get("id")
105
+ if not isinstance(action_id, str) or not action_id.strip():
106
+ raise ValueError("action id is required")
107
+ if action_id != action_id.strip():
108
+ raise ValueError("action id must not contain surrounding whitespace")
109
+ if action_id in seen_action_ids:
110
+ raise ValueError(f"duplicate action id: {action_id}")
111
+ tool = action_payload.get("tool")
112
+ if not isinstance(tool, str) or not tool.strip():
113
+ raise ValueError("action tool is required")
114
+ if tool != tool.strip():
115
+ raise ValueError("action tool must not contain surrounding whitespace")
116
+ action_input = action_payload.get("input", {})
117
+ if not isinstance(action_input, dict):
118
+ raise ValueError("action input must be an object")
119
+ reason = action_payload.get("reason", "")
120
+ if not isinstance(reason, str):
121
+ raise ValueError("action reason must be a string")
122
+ if len(reason) > MAX_ACTION_REASON_CHARS:
123
+ raise ValueError(
124
+ f"action reason must contain at most {MAX_ACTION_REASON_CHARS} character(s)"
125
+ )
126
+ depends_on = _parse_action_dependencies(
127
+ action_payload.get("depends_on", []),
128
+ seen_action_ids,
129
+ )
130
+ _validate_dependency_references(action_input, set(depends_on))
131
+ seen_action_ids.add(action_id)
132
+ actions.append(
133
+ AgentAction(
134
+ id=action_id,
135
+ tool=tool,
136
+ input=action_input,
137
+ reason=reason,
138
+ depends_on=depends_on,
139
+ )
140
+ )
141
+ return AgentPlan(actions=actions, final_answer=final_answer)
142
+
143
+
144
+ def _parse_plan_json_object(text: str) -> Any:
145
+ try:
146
+ return json.loads(text)
147
+ except json.JSONDecodeError as exc:
148
+ strict_error = exc
149
+ decoder = json.JSONDecoder()
150
+ candidates = []
151
+ for index, character in enumerate(text):
152
+ if character != "{":
153
+ continue
154
+ try:
155
+ payload, _end = decoder.raw_decode(text, index)
156
+ except json.JSONDecodeError:
157
+ continue
158
+ if isinstance(payload, dict) and "actions" in payload:
159
+ candidates.append(payload)
160
+ if candidates:
161
+ return candidates[-1]
162
+ raise ValueError(f"plan JSON is invalid: {strict_error}") from strict_error
163
+
164
+
165
+ def _reject_unknown_fields(
166
+ payload: Dict[str, Any],
167
+ allowed_fields: set,
168
+ label: str,
169
+ ) -> None:
170
+ for field_name in payload:
171
+ if field_name not in allowed_fields:
172
+ raise ValueError(f"{label} field is not allowed: {field_name}")
173
+
174
+
175
+ def _parse_action_dependencies(value: Any, seen_action_ids: set) -> List[str]:
176
+ if not isinstance(value, list):
177
+ raise ValueError("action depends_on must be a list")
178
+ dependencies = []
179
+ seen_dependencies = set()
180
+ for dependency in value:
181
+ if not isinstance(dependency, str):
182
+ raise ValueError("action dependency must be a string")
183
+ if not dependency.strip():
184
+ raise ValueError("action dependency is required")
185
+ if dependency != dependency.strip():
186
+ raise ValueError("action dependency must not contain surrounding whitespace")
187
+ if dependency not in seen_action_ids:
188
+ raise ValueError(f"unknown or later action dependency: {dependency}")
189
+ if dependency in seen_dependencies:
190
+ raise ValueError(f"duplicate action dependency: {dependency}")
191
+ seen_dependencies.add(dependency)
192
+ dependencies.append(dependency)
193
+ return dependencies
194
+
195
+
196
+ def _validate_dependency_references(
197
+ value: Any,
198
+ declared_dependencies: set[str],
199
+ *,
200
+ depth: int = 0,
201
+ reference_count: List[int] | None = None,
202
+ ) -> None:
203
+ if depth > 20:
204
+ raise ValueError("dependency input nesting exceeds 20 levels")
205
+ active_reference_count = reference_count or [0]
206
+ if isinstance(value, list):
207
+ for item in value:
208
+ _validate_dependency_references(
209
+ item,
210
+ declared_dependencies,
211
+ depth=depth + 1,
212
+ reference_count=active_reference_count,
213
+ )
214
+ return
215
+ if not isinstance(value, dict):
216
+ return
217
+ if "$from_action" in value:
218
+ if set(value) != {"$from_action", "pointer"}:
219
+ raise ValueError(
220
+ "dependency reference must contain only $from_action and pointer"
221
+ )
222
+ active_reference_count[0] += 1
223
+ if active_reference_count[0] > 50:
224
+ raise ValueError("action input contains more than 50 dependency references")
225
+ action_id = value.get("$from_action")
226
+ pointer = value.get("pointer")
227
+ if not isinstance(action_id, str) or action_id not in declared_dependencies:
228
+ raise ValueError("dependency reference must name a declared dependency")
229
+ if not isinstance(pointer, str) or (pointer and not pointer.startswith("/")):
230
+ raise ValueError(
231
+ "dependency reference pointer must be empty or start with /"
232
+ )
233
+ return
234
+ for item in value.values():
235
+ _validate_dependency_references(
236
+ item,
237
+ declared_dependencies,
238
+ depth=depth + 1,
239
+ reference_count=active_reference_count,
240
+ )