@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.
- package/README.md +353 -0
- package/npm/bin/kagent-serve.js +6 -0
- package/npm/bin/kagent.js +6 -0
- package/npm/lib/App.js +524 -0
- package/npm/lib/app-state.js +224 -0
- package/npm/lib/approval-choice.js +25 -0
- package/npm/lib/commands.js +59 -0
- package/npm/lib/editor.js +188 -0
- package/npm/lib/ink-runner.js +41 -0
- package/npm/lib/kagent-home.js +39 -0
- package/npm/lib/launcher.js +221 -0
- package/npm/lib/protocol.js +33 -0
- package/npm/lib/provider-setup.js +139 -0
- package/npm/lib/python-runner.js +892 -0
- package/npm/lib/runtime-client.js +390 -0
- package/npm/lib/terminal-input.js +127 -0
- package/npm/lib/terminal-text.js +19 -0
- package/npm/lib/terminal-width.js +14 -0
- package/npm/lib/transcript.js +227 -0
- package/npm/lib/ui-components.js +247 -0
- package/npm/lib/update-manager.js +334 -0
- package/package.json +39 -0
- package/pyproject.toml +55 -0
- package/src/kagent/__init__.py +90 -0
- package/src/kagent/cli/__init__.py +5 -0
- package/src/kagent/cli/__main__.py +6 -0
- package/src/kagent/cli/commands.py +112 -0
- package/src/kagent/cli/conversation.py +127 -0
- package/src/kagent/cli/interactive.py +841 -0
- package/src/kagent/cli/main.py +685 -0
- package/src/kagent/cli/memory.py +460 -0
- package/src/kagent/cli/pending_approval.py +169 -0
- package/src/kagent/cli/provider.py +27 -0
- package/src/kagent/cli/session_commands.py +401 -0
- package/src/kagent/cli/stdio_runtime.py +784 -0
- package/src/kagent/cli/trace.py +67 -0
- package/src/kagent/cli/ui.py +931 -0
- package/src/kagent/core/__init__.py +0 -0
- package/src/kagent/core/agent.py +296 -0
- package/src/kagent/core/faults.py +11 -0
- package/src/kagent/core/invariants.py +47 -0
- package/src/kagent/core/normalization.py +81 -0
- package/src/kagent/core/planning.py +48 -0
- package/src/kagent/core/state.py +73 -0
- package/src/kagent/core/summary.py +64 -0
- package/src/kagent/core/tools.py +196 -0
- package/src/kagent/core/trace.py +57 -0
- package/src/kagent/eval/__init__.py +11 -0
- package/src/kagent/eval/cases.py +229 -0
- package/src/kagent/eval/evaluator.py +216 -0
- package/src/kagent/integrations/__init__.py +3 -0
- package/src/kagent/integrations/audit.py +95 -0
- package/src/kagent/integrations/backends.py +131 -0
- package/src/kagent/integrations/memory.py +301 -0
- package/src/kagent/ops/__init__.py +0 -0
- package/src/kagent/ops/batch.py +156 -0
- package/src/kagent/ops/doctor.py +255 -0
- package/src/kagent/ops/metrics.py +214 -0
- package/src/kagent/ops/release_evidence.py +877 -0
- package/src/kagent/ops/release_manifest.py +142 -0
- package/src/kagent/ops/trace_replay.py +285 -0
- package/src/kagent/providers/__init__.py +7 -0
- package/src/kagent/providers/embeddings.py +187 -0
- package/src/kagent/providers/llm.py +770 -0
- package/src/kagent/py.typed +1 -0
- package/src/kagent/runtime/__init__.py +28 -0
- package/src/kagent/runtime/action_graph.py +543 -0
- package/src/kagent/runtime/agent.py +2089 -0
- package/src/kagent/runtime/approval.py +64 -0
- package/src/kagent/runtime/cancellation.py +64 -0
- package/src/kagent/runtime/checkpoint_state.py +146 -0
- package/src/kagent/runtime/checkpoint_storage.py +270 -0
- package/src/kagent/runtime/context.py +65 -0
- package/src/kagent/runtime/file_transaction.py +195 -0
- package/src/kagent/runtime/hooks.py +74 -0
- package/src/kagent/runtime/metadata.py +116 -0
- package/src/kagent/runtime/patch_checkpoints.py +385 -0
- package/src/kagent/runtime/policy.py +50 -0
- package/src/kagent/runtime/presentation.py +205 -0
- package/src/kagent/runtime/redaction.py +130 -0
- package/src/kagent/runtime/sandbox.py +331 -0
- package/src/kagent/runtime/skills.py +66 -0
- package/src/kagent/runtime/steering.py +56 -0
- package/src/kagent/runtime/steps.py +255 -0
- package/src/kagent/runtime/task_state.py +40 -0
- package/src/kagent/runtime/tools.py +3532 -0
- package/src/kagent/runtime/types.py +240 -0
- package/src/kagent/runtime/workspace.py +597 -0
- package/src/kagent/service/__init__.py +26 -0
- package/src/kagent/service/__main__.py +6 -0
- package/src/kagent/service/active_runs.py +178 -0
- package/src/kagent/service/cli.py +737 -0
- package/src/kagent/service/contract.py +2571 -0
- package/src/kagent/service/errors.py +71 -0
- package/src/kagent/service/idempotency.py +584 -0
- package/src/kagent/service/router.py +884 -0
- package/src/kagent/service/run.py +150 -0
- package/src/kagent/service/runtime.py +1915 -0
- package/src/kagent/service/runtime_approval.py +20 -0
- package/src/kagent/service/runtime_cancel.py +192 -0
- package/src/kagent/service/runtime_lifecycle.py +229 -0
- package/src/kagent/service/runtime_metadata.py +21 -0
- package/src/kagent/service/runtime_policy.py +115 -0
- package/src/kagent/service/runtime_recovery.py +573 -0
- package/src/kagent/service/runtime_resume.py +382 -0
- package/src/kagent/service/runtime_resume_claim.py +116 -0
- package/src/kagent/service/runtime_run.py +350 -0
- package/src/kagent/service/runtime_status.py +2007 -0
- package/src/kagent/service/safety.py +139 -0
- package/src/kagent/service/server.py +114 -0
- package/src/kagent/service/status.py +233 -0
- package/src/kagent/service/trace_store.py +322 -0
- package/src/kagent/service/transport.py +24 -0
- package/src/kagent/utils/__init__.py +0 -0
- package/src/kagent/utils/config_validation.py +21 -0
- package/src/kagent/utils/json_output.py +23 -0
- package/src/kagent/utils/paths.py +473 -0
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UPDATE_CHECK_TIMEOUT_MS = exports.UPDATE_CHECK_TTL_MS = exports.NPM_REGISTRY_URL = void 0;
|
|
4
|
+
exports.resolveUpdateChannel = resolveUpdateChannel;
|
|
5
|
+
exports.compareSemVer = compareSemVer;
|
|
6
|
+
exports.checkForUpdate = checkForUpdate;
|
|
7
|
+
exports.runUpgrade = runUpgrade;
|
|
8
|
+
exports.NPM_REGISTRY_URL = "https://registry.npmjs.org/%40openlucaskaka%2Fkagent";
|
|
9
|
+
exports.UPDATE_CHECK_TTL_MS = 24 * 60 * 60 * 1_000;
|
|
10
|
+
exports.UPDATE_CHECK_TIMEOUT_MS = 3_000;
|
|
11
|
+
const INSTALL_ARGS = {
|
|
12
|
+
latest: ["install", "--global", "@openlucaskaka/kagent@latest"],
|
|
13
|
+
next: ["install", "--global", "@openlucaskaka/kagent@next"],
|
|
14
|
+
};
|
|
15
|
+
class UpdateNetworkError extends Error {
|
|
16
|
+
}
|
|
17
|
+
class UpdateMetadataError extends Error {
|
|
18
|
+
}
|
|
19
|
+
function resolveUpdateChannel(env = process.env) {
|
|
20
|
+
const configured = env.KAGENT_UPDATE_CHANNEL?.trim().toLowerCase();
|
|
21
|
+
if (!configured || configured === "stable" || configured === "latest") {
|
|
22
|
+
return "latest";
|
|
23
|
+
}
|
|
24
|
+
if (configured === "beta" || configured === "next") {
|
|
25
|
+
return "next";
|
|
26
|
+
}
|
|
27
|
+
throw new Error(`KAGENT_UPDATE_CHANNEL ${JSON.stringify(configured)} is invalid; expected stable/latest or beta/next`);
|
|
28
|
+
}
|
|
29
|
+
function compareSemVer(left, right) {
|
|
30
|
+
const parsedLeft = parseSemVer(left);
|
|
31
|
+
const parsedRight = parseSemVer(right);
|
|
32
|
+
for (let index = 0; index < parsedLeft.core.length; index += 1) {
|
|
33
|
+
const compared = compareNumericIdentifier(parsedLeft.core[index], parsedRight.core[index]);
|
|
34
|
+
if (compared !== 0) {
|
|
35
|
+
return compared;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (parsedLeft.prerelease === null && parsedRight.prerelease === null) {
|
|
39
|
+
return 0;
|
|
40
|
+
}
|
|
41
|
+
if (parsedLeft.prerelease === null) {
|
|
42
|
+
return 1;
|
|
43
|
+
}
|
|
44
|
+
if (parsedRight.prerelease === null) {
|
|
45
|
+
return -1;
|
|
46
|
+
}
|
|
47
|
+
const identifiers = Math.max(parsedLeft.prerelease.length, parsedRight.prerelease.length);
|
|
48
|
+
for (let index = 0; index < identifiers; index += 1) {
|
|
49
|
+
const leftIdentifier = parsedLeft.prerelease[index];
|
|
50
|
+
const rightIdentifier = parsedRight.prerelease[index];
|
|
51
|
+
if (leftIdentifier === undefined) {
|
|
52
|
+
return -1;
|
|
53
|
+
}
|
|
54
|
+
if (rightIdentifier === undefined) {
|
|
55
|
+
return 1;
|
|
56
|
+
}
|
|
57
|
+
const compared = comparePrereleaseIdentifier(leftIdentifier, rightIdentifier);
|
|
58
|
+
if (compared !== 0) {
|
|
59
|
+
return compared;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return 0;
|
|
63
|
+
}
|
|
64
|
+
async function checkForUpdate(options) {
|
|
65
|
+
parseSemVer(options.currentVersion);
|
|
66
|
+
const deps = options.deps ?? {};
|
|
67
|
+
const channel = resolveCheckChannel(options.channel, options.env);
|
|
68
|
+
const now = deps.now?.() ?? new Date();
|
|
69
|
+
const checkedAt = now.toISOString();
|
|
70
|
+
let cacheWarning;
|
|
71
|
+
if (!options.force && deps.readState) {
|
|
72
|
+
let cached;
|
|
73
|
+
try {
|
|
74
|
+
cached = await deps.readState();
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
cacheWarning = `Unable to read update cache: ${errorMessage(error)}`;
|
|
78
|
+
}
|
|
79
|
+
if (isFreshState(cached, channel, now)) {
|
|
80
|
+
return {
|
|
81
|
+
current: options.currentVersion,
|
|
82
|
+
latest: cached.latest,
|
|
83
|
+
channel,
|
|
84
|
+
updateAvailable: compareSemVer(cached.latest, options.currentVersion) > 0,
|
|
85
|
+
checkedAt: cached.checkedAt,
|
|
86
|
+
skipped: true,
|
|
87
|
+
reason: "ttl",
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
let latest;
|
|
92
|
+
try {
|
|
93
|
+
latest = await fetchLatestVersion(channel, deps);
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
const message = errorMessage(error);
|
|
97
|
+
if (options.force) {
|
|
98
|
+
if (error instanceof UpdateNetworkError) {
|
|
99
|
+
throw new Error(`Unable to check for kagent updates: ${message}`);
|
|
100
|
+
}
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
return skippedCheck(options.currentVersion, channel, checkedAt, error instanceof UpdateMetadataError
|
|
104
|
+
? "metadata-error"
|
|
105
|
+
: "network-error", error);
|
|
106
|
+
}
|
|
107
|
+
const state = { channel, latest, checkedAt };
|
|
108
|
+
try {
|
|
109
|
+
await deps.writeState?.(state);
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
if (options.force) {
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
const warning = `Unable to write update cache: ${errorMessage(error)}`;
|
|
116
|
+
cacheWarning = cacheWarning ? `${cacheWarning}; ${warning}` : warning;
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
current: options.currentVersion,
|
|
120
|
+
latest,
|
|
121
|
+
channel,
|
|
122
|
+
updateAvailable: compareSemVer(latest, options.currentVersion) > 0,
|
|
123
|
+
checkedAt,
|
|
124
|
+
...(cacheWarning ? { cacheWarning } : {}),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function resolveCheckChannel(channel, env) {
|
|
128
|
+
if (channel === undefined) {
|
|
129
|
+
return resolveUpdateChannel(env);
|
|
130
|
+
}
|
|
131
|
+
if (channel === "latest" || channel === "next") {
|
|
132
|
+
return channel;
|
|
133
|
+
}
|
|
134
|
+
throw new Error(`Update channel ${JSON.stringify(channel)} is invalid; expected latest or next`);
|
|
135
|
+
}
|
|
136
|
+
function skippedCheck(current, channel, checkedAt, reason, error) {
|
|
137
|
+
return {
|
|
138
|
+
current,
|
|
139
|
+
latest: null,
|
|
140
|
+
channel,
|
|
141
|
+
updateAvailable: false,
|
|
142
|
+
checkedAt,
|
|
143
|
+
skipped: true,
|
|
144
|
+
reason,
|
|
145
|
+
error: errorMessage(error),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
async function runUpgrade(options) {
|
|
149
|
+
const deps = options.deps ?? {};
|
|
150
|
+
const checked = await checkForUpdate({
|
|
151
|
+
currentVersion: options.currentVersion,
|
|
152
|
+
channel: options.channel,
|
|
153
|
+
force: true,
|
|
154
|
+
env: options.env,
|
|
155
|
+
deps,
|
|
156
|
+
});
|
|
157
|
+
if (checked.latest === null) {
|
|
158
|
+
throw new Error("Forced update checks cannot be skipped");
|
|
159
|
+
}
|
|
160
|
+
if (!checked.updateAvailable) {
|
|
161
|
+
return {
|
|
162
|
+
...checked,
|
|
163
|
+
upgraded: false,
|
|
164
|
+
installedVersion: options.currentVersion,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
if (!deps.runInstall || !deps.readInstalledVersion) {
|
|
168
|
+
throw new Error("runUpgrade requires runInstall and readInstalledVersion dependencies");
|
|
169
|
+
}
|
|
170
|
+
const installArgs = INSTALL_ARGS[checked.channel];
|
|
171
|
+
const installSpec = installArgs[2];
|
|
172
|
+
try {
|
|
173
|
+
await deps.runInstall([...installArgs]);
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
throw new Error(`Failed to install ${installSpec}: ${errorMessage(error)}`);
|
|
177
|
+
}
|
|
178
|
+
let installedVersion;
|
|
179
|
+
try {
|
|
180
|
+
installedVersion = await deps.readInstalledVersion();
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
throw new Error(`Unable to verify the installed kagent version: ${errorMessage(error)}`);
|
|
184
|
+
}
|
|
185
|
+
try {
|
|
186
|
+
if (compareSemVer(installedVersion, checked.latest) < 0) {
|
|
187
|
+
throw new Error(`Installed version ${installedVersion} is below expected version ${checked.latest}`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
if (error instanceof Error && /below expected version/.test(error.message)) {
|
|
192
|
+
throw error;
|
|
193
|
+
}
|
|
194
|
+
throw new Error(`Unable to verify installed version ${JSON.stringify(installedVersion)}: ${errorMessage(error)}`);
|
|
195
|
+
}
|
|
196
|
+
return { ...checked, upgraded: true, installedVersion };
|
|
197
|
+
}
|
|
198
|
+
function parseSemVer(version) {
|
|
199
|
+
const match = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/.exec(version);
|
|
200
|
+
if (!match) {
|
|
201
|
+
throw new Error(`Invalid SemVer: ${JSON.stringify(version)}`);
|
|
202
|
+
}
|
|
203
|
+
const prerelease = match[4]?.split(".") ?? null;
|
|
204
|
+
if (prerelease?.some((identifier) => /^\d+$/.test(identifier) && /^0\d/.test(identifier))) {
|
|
205
|
+
throw new Error(`Invalid SemVer: ${JSON.stringify(version)}`);
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
core: [match[1], match[2], match[3]],
|
|
209
|
+
prerelease,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
function compareNumericIdentifier(left, right) {
|
|
213
|
+
if (left.length !== right.length) {
|
|
214
|
+
return left.length < right.length ? -1 : 1;
|
|
215
|
+
}
|
|
216
|
+
return left === right ? 0 : left < right ? -1 : 1;
|
|
217
|
+
}
|
|
218
|
+
function comparePrereleaseIdentifier(left, right) {
|
|
219
|
+
const leftNumeric = /^\d+$/.test(left);
|
|
220
|
+
const rightNumeric = /^\d+$/.test(right);
|
|
221
|
+
if (leftNumeric && rightNumeric) {
|
|
222
|
+
return compareNumericIdentifier(left, right);
|
|
223
|
+
}
|
|
224
|
+
if (leftNumeric !== rightNumeric) {
|
|
225
|
+
return leftNumeric ? -1 : 1;
|
|
226
|
+
}
|
|
227
|
+
return left === right ? 0 : left < right ? -1 : 1;
|
|
228
|
+
}
|
|
229
|
+
function isFreshState(state, channel, now) {
|
|
230
|
+
if (!state || state.channel !== channel) {
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
const checkedAt = Date.parse(state.checkedAt);
|
|
234
|
+
const age = now.getTime() - checkedAt;
|
|
235
|
+
if (!Number.isFinite(checkedAt) || age < 0 || age >= exports.UPDATE_CHECK_TTL_MS) {
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
try {
|
|
239
|
+
parseSemVer(state.latest);
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
async function fetchLatestVersion(channel, deps) {
|
|
247
|
+
const fetchFn = deps.fetch ?? defaultFetch;
|
|
248
|
+
const timeoutMs = deps.timeoutMs ?? exports.UPDATE_CHECK_TIMEOUT_MS;
|
|
249
|
+
const controller = new AbortController();
|
|
250
|
+
let timedOut = false;
|
|
251
|
+
let timer;
|
|
252
|
+
const timeout = new Promise((_resolve, reject) => {
|
|
253
|
+
timer = setTimeout(() => {
|
|
254
|
+
timedOut = true;
|
|
255
|
+
controller.abort();
|
|
256
|
+
reject(new Error(`Registry request timed out after ${timeoutMs}ms`));
|
|
257
|
+
}, timeoutMs);
|
|
258
|
+
});
|
|
259
|
+
try {
|
|
260
|
+
let response;
|
|
261
|
+
try {
|
|
262
|
+
response = await Promise.race([
|
|
263
|
+
fetchFn(exports.NPM_REGISTRY_URL, { signal: controller.signal }),
|
|
264
|
+
timeout,
|
|
265
|
+
]);
|
|
266
|
+
}
|
|
267
|
+
catch (error) {
|
|
268
|
+
const message = timedOut
|
|
269
|
+
? `Registry request timed out after ${timeoutMs}ms`
|
|
270
|
+
: errorMessage(error);
|
|
271
|
+
throw new UpdateNetworkError(message);
|
|
272
|
+
}
|
|
273
|
+
if (!response.ok) {
|
|
274
|
+
releaseFailedResponse(response, controller);
|
|
275
|
+
throw new UpdateNetworkError(`npm registry returned HTTP ${response.status}`);
|
|
276
|
+
}
|
|
277
|
+
let metadata;
|
|
278
|
+
try {
|
|
279
|
+
metadata = await Promise.race([response.json(), timeout]);
|
|
280
|
+
}
|
|
281
|
+
catch (error) {
|
|
282
|
+
if (timedOut) {
|
|
283
|
+
throw new UpdateNetworkError(`Registry request timed out after ${timeoutMs}ms`);
|
|
284
|
+
}
|
|
285
|
+
if (error instanceof TypeError) {
|
|
286
|
+
throw new UpdateNetworkError(errorMessage(error));
|
|
287
|
+
}
|
|
288
|
+
throw new UpdateMetadataError(`npm registry metadata is not valid JSON: ${errorMessage(error)}`);
|
|
289
|
+
}
|
|
290
|
+
if (!isRecord(metadata)) {
|
|
291
|
+
throw new UpdateMetadataError("npm registry metadata must be a JSON object");
|
|
292
|
+
}
|
|
293
|
+
const distTags = metadata["dist-tags"];
|
|
294
|
+
if (!isRecord(distTags)) {
|
|
295
|
+
throw new UpdateMetadataError("npm registry metadata must contain dist-tags");
|
|
296
|
+
}
|
|
297
|
+
const version = distTags[channel];
|
|
298
|
+
if (typeof version !== "string" || !version.trim()) {
|
|
299
|
+
throw new UpdateMetadataError(`npm registry dist-tags.${channel} must be non-empty`);
|
|
300
|
+
}
|
|
301
|
+
try {
|
|
302
|
+
parseSemVer(version);
|
|
303
|
+
}
|
|
304
|
+
catch (error) {
|
|
305
|
+
throw new UpdateMetadataError(`npm registry dist-tags.${channel} is not valid SemVer: ${errorMessage(error)}`);
|
|
306
|
+
}
|
|
307
|
+
return version;
|
|
308
|
+
}
|
|
309
|
+
finally {
|
|
310
|
+
if (timer !== undefined) {
|
|
311
|
+
clearTimeout(timer);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
function releaseFailedResponse(response, controller) {
|
|
316
|
+
try {
|
|
317
|
+
void response.body?.cancel().catch(() => undefined);
|
|
318
|
+
}
|
|
319
|
+
finally {
|
|
320
|
+
controller.abort();
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
function defaultFetch(input, init) {
|
|
324
|
+
if (typeof globalThis.fetch !== "function") {
|
|
325
|
+
return Promise.reject(new Error("global fetch is unavailable"));
|
|
326
|
+
}
|
|
327
|
+
return globalThis.fetch(input, init);
|
|
328
|
+
}
|
|
329
|
+
function isRecord(value) {
|
|
330
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
331
|
+
}
|
|
332
|
+
function errorMessage(error) {
|
|
333
|
+
return error instanceof Error ? error.message : String(error);
|
|
334
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@openlucaskaka/kagent",
|
|
3
|
+
"version": "0.1.7",
|
|
4
|
+
"description": "Codex-style LangGraph agent runtime for internal non-coding workflows.",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"bin": {
|
|
7
|
+
"kagent": "npm/bin/kagent.js",
|
|
8
|
+
"kagent-serve": "npm/bin/kagent-serve.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"README.md",
|
|
12
|
+
"pyproject.toml",
|
|
13
|
+
"src",
|
|
14
|
+
"npm/bin",
|
|
15
|
+
"npm/lib"
|
|
16
|
+
],
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=18"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"ink": "^5.0.1",
|
|
22
|
+
"react": "^18.3.1",
|
|
23
|
+
"string-width": "^4.2.3"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^20.14.15",
|
|
27
|
+
"@types/react": "^18.3.3",
|
|
28
|
+
"typescript": "^5.5.4"
|
|
29
|
+
},
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "npm run build:cli",
|
|
35
|
+
"build:cli": "tsc -p tsconfig.json",
|
|
36
|
+
"test:cli": "npm run build:cli && node --test npm/lib/*.test.js",
|
|
37
|
+
"check": "npm run build:cli && node --test npm/lib/*.test.js && node --check npm/bin/kagent.js && node --check npm/bin/kagent-serve.js && node --check npm/lib/ink-runner.js && node --check npm/lib/python-runner.js"
|
|
38
|
+
}
|
|
39
|
+
}
|
package/pyproject.toml
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61", "wheel>=0.45,<1"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "kagent"
|
|
7
|
+
version = "0.1.7"
|
|
8
|
+
description = "A bounded kagent built with LangGraph."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"langgraph>=0.6.11,<0.7",
|
|
13
|
+
"prompt_toolkit>=3.0,<4",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
[project.optional-dependencies]
|
|
17
|
+
dev = [
|
|
18
|
+
"pytest>=8,<9",
|
|
19
|
+
"ruff>=0.8,<1",
|
|
20
|
+
"setuptools>=61",
|
|
21
|
+
"wheel>=0.45,<1",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[project.scripts]
|
|
25
|
+
kagent = "kagent.cli:main"
|
|
26
|
+
kagent-batch = "kagent.ops.batch:main"
|
|
27
|
+
kagent-doctor = "kagent.ops.doctor:main"
|
|
28
|
+
kagent-eval = "kagent.eval.evaluator:main"
|
|
29
|
+
kagent-metrics = "kagent.ops.metrics:main"
|
|
30
|
+
kagent-release-evidence = "kagent.ops.release_evidence:main"
|
|
31
|
+
kagent-release-manifest = "kagent.ops.release_manifest:main"
|
|
32
|
+
kagent-serve = "kagent.service:main"
|
|
33
|
+
kagent-trace-prune = "kagent.service.trace_store:main"
|
|
34
|
+
kagent-trace-replay = "kagent.ops.trace_replay:main"
|
|
35
|
+
|
|
36
|
+
[tool.setuptools.packages.find]
|
|
37
|
+
where = ["src"]
|
|
38
|
+
|
|
39
|
+
[tool.setuptools.package-data]
|
|
40
|
+
kagent = ["py.typed"]
|
|
41
|
+
|
|
42
|
+
[tool.pytest.ini_options]
|
|
43
|
+
addopts = "-q"
|
|
44
|
+
testpaths = ["tests"]
|
|
45
|
+
filterwarnings = [
|
|
46
|
+
"ignore:urllib3 v2 only supports OpenSSL 1.1.1+:urllib3.exceptions.NotOpenSSLWarning",
|
|
47
|
+
"ignore:The default value of `allowed_objects` will change in a future version:langchain_core._api.deprecation.LangChainPendingDeprecationWarning",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
[tool.ruff]
|
|
51
|
+
line-length = 100
|
|
52
|
+
target-version = "py39"
|
|
53
|
+
|
|
54
|
+
[tool.ruff.lint]
|
|
55
|
+
select = ["E", "F", "I"]
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""kagent LangGraph agent package."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _source_tree_version() -> str:
|
|
9
|
+
for parent in Path(__file__).resolve().parents:
|
|
10
|
+
pyproject_path = parent / "pyproject.toml"
|
|
11
|
+
if not pyproject_path.exists():
|
|
12
|
+
continue
|
|
13
|
+
pyproject = pyproject_path.read_text(encoding="utf-8")
|
|
14
|
+
if 'name = "kagent"' not in pyproject:
|
|
15
|
+
continue
|
|
16
|
+
match = re.search(r'(?m)^version = "([^"]+)"$', pyproject)
|
|
17
|
+
if match:
|
|
18
|
+
return match.group(1)
|
|
19
|
+
return ""
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
__version__ = _source_tree_version() or version("kagent")
|
|
24
|
+
except PackageNotFoundError:
|
|
25
|
+
__version__ = "0.1.7"
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
"AgentConfig",
|
|
29
|
+
"AgentStatus",
|
|
30
|
+
"FakeLLMProvider",
|
|
31
|
+
"LLMProviderConfig",
|
|
32
|
+
"ProviderKind",
|
|
33
|
+
"__version__",
|
|
34
|
+
"agent_topology",
|
|
35
|
+
"build_agent_graph",
|
|
36
|
+
"detect_provider_kind",
|
|
37
|
+
"evaluate_agent",
|
|
38
|
+
"preview_plan",
|
|
39
|
+
"registered_evaluation_cases",
|
|
40
|
+
"registered_tool_metadata",
|
|
41
|
+
"registered_tool_names",
|
|
42
|
+
"runtime_topology",
|
|
43
|
+
"run_agent",
|
|
44
|
+
"run_runtime_agent",
|
|
45
|
+
"summarize_run",
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def __getattr__(name):
|
|
50
|
+
if name in {
|
|
51
|
+
"AgentConfig",
|
|
52
|
+
"AgentStatus",
|
|
53
|
+
"agent_topology",
|
|
54
|
+
"build_agent_graph",
|
|
55
|
+
"preview_plan",
|
|
56
|
+
"run_agent",
|
|
57
|
+
}:
|
|
58
|
+
from kagent.core import agent
|
|
59
|
+
|
|
60
|
+
return getattr(agent, name)
|
|
61
|
+
if name in {"evaluate_agent", "registered_evaluation_cases"}:
|
|
62
|
+
from kagent.eval import evaluator
|
|
63
|
+
|
|
64
|
+
return getattr(evaluator, name)
|
|
65
|
+
if name in {
|
|
66
|
+
"FakeLLMProvider",
|
|
67
|
+
"LLMProviderConfig",
|
|
68
|
+
"ProviderKind",
|
|
69
|
+
"detect_provider_kind",
|
|
70
|
+
}:
|
|
71
|
+
from kagent.providers import llm_provider
|
|
72
|
+
|
|
73
|
+
return getattr(llm_provider, name)
|
|
74
|
+
if name == "run_runtime_agent":
|
|
75
|
+
from kagent.runtime import run_runtime_agent
|
|
76
|
+
|
|
77
|
+
return run_runtime_agent
|
|
78
|
+
if name == "runtime_topology":
|
|
79
|
+
from kagent.runtime import runtime_topology
|
|
80
|
+
|
|
81
|
+
return runtime_topology
|
|
82
|
+
if name == "summarize_run":
|
|
83
|
+
from kagent.core.summary import summarize_run
|
|
84
|
+
|
|
85
|
+
return summarize_run
|
|
86
|
+
if name in {"registered_tool_metadata", "registered_tool_names"}:
|
|
87
|
+
from kagent.core import tools
|
|
88
|
+
|
|
89
|
+
return getattr(tools, name)
|
|
90
|
+
raise AttributeError(name)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from difflib import get_close_matches
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass(frozen=True)
|
|
8
|
+
class RuntimeInteractiveCommand:
|
|
9
|
+
primary: str
|
|
10
|
+
description: str
|
|
11
|
+
section: str
|
|
12
|
+
aliases: tuple[str, ...] = ()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
_RUNTIME_INTERACTIVE_COMMANDS: tuple[RuntimeInteractiveCommand, ...] = (
|
|
16
|
+
RuntimeInteractiveCommand("/pwd", "show working directory", "Session", ("/cwd",)),
|
|
17
|
+
RuntimeInteractiveCommand("/cd PATH", "change working directory", "Session", ("/cd",)),
|
|
18
|
+
RuntimeInteractiveCommand("/status", "show shell state", "Session", ("/stat",)),
|
|
19
|
+
RuntimeInteractiveCommand("/memory", "review compact memory", "Session", ("/mem",)),
|
|
20
|
+
RuntimeInteractiveCommand(
|
|
21
|
+
"/compact-memory",
|
|
22
|
+
"compact remembered context now",
|
|
23
|
+
"Session",
|
|
24
|
+
("/compress-memory",),
|
|
25
|
+
),
|
|
26
|
+
RuntimeInteractiveCommand("/clear", "clear remembered turns", "Session", ("/clear-memory",)),
|
|
27
|
+
RuntimeInteractiveCommand(
|
|
28
|
+
"/reset",
|
|
29
|
+
"clear memory and prompt history",
|
|
30
|
+
"Session",
|
|
31
|
+
("/reset-session",),
|
|
32
|
+
),
|
|
33
|
+
RuntimeInteractiveCommand("/last", "replay last answer", "Session", ("/last-run",)),
|
|
34
|
+
RuntimeInteractiveCommand(
|
|
35
|
+
"/config",
|
|
36
|
+
"show redacted provider config",
|
|
37
|
+
"Provider",
|
|
38
|
+
("/provider",),
|
|
39
|
+
),
|
|
40
|
+
RuntimeInteractiveCommand("/tools", "show available actions", "Provider", ("/actions",)),
|
|
41
|
+
RuntimeInteractiveCommand(
|
|
42
|
+
"/compact",
|
|
43
|
+
"clean transcript",
|
|
44
|
+
"Output",
|
|
45
|
+
("/summary",),
|
|
46
|
+
),
|
|
47
|
+
RuntimeInteractiveCommand(
|
|
48
|
+
"/json",
|
|
49
|
+
"full JSON traces",
|
|
50
|
+
"Output",
|
|
51
|
+
("/full", "/debug"),
|
|
52
|
+
),
|
|
53
|
+
RuntimeInteractiveCommand("/trace", "last JSON trace once", "Output", ("/last-json",)),
|
|
54
|
+
RuntimeInteractiveCommand(
|
|
55
|
+
"/save-trace PATH",
|
|
56
|
+
"save last JSON trace",
|
|
57
|
+
"Output",
|
|
58
|
+
("/export-trace",),
|
|
59
|
+
),
|
|
60
|
+
RuntimeInteractiveCommand(
|
|
61
|
+
"/doctor",
|
|
62
|
+
"show local diagnostics",
|
|
63
|
+
"Debug",
|
|
64
|
+
("/diagnostics",),
|
|
65
|
+
),
|
|
66
|
+
RuntimeInteractiveCommand("/help", "command palette", "Debug", ("/?",)),
|
|
67
|
+
RuntimeInteractiveCommand("exit", "quit", "Debug", ("quit", ":q")),
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def runtime_interactive_commands() -> tuple[RuntimeInteractiveCommand, ...]:
|
|
72
|
+
return _RUNTIME_INTERACTIVE_COMMANDS
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def runtime_interactive_completion_words() -> list[str]:
|
|
76
|
+
words: list[str] = []
|
|
77
|
+
for command in _RUNTIME_INTERACTIVE_COMMANDS:
|
|
78
|
+
for word in (command.primary.split()[0], *command.aliases):
|
|
79
|
+
if word not in words:
|
|
80
|
+
words.append(word)
|
|
81
|
+
return words
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def is_runtime_interactive_command(text: str) -> bool:
|
|
85
|
+
command_name = _runtime_command_name(text)
|
|
86
|
+
if not command_name:
|
|
87
|
+
return False
|
|
88
|
+
return command_name in runtime_interactive_completion_words()
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def runtime_interactive_command_suggestions(text: str) -> list[str]:
|
|
92
|
+
command_name = _runtime_command_name(text)
|
|
93
|
+
if not command_name:
|
|
94
|
+
return []
|
|
95
|
+
return get_close_matches(
|
|
96
|
+
command_name,
|
|
97
|
+
runtime_interactive_completion_words(),
|
|
98
|
+
n=3,
|
|
99
|
+
cutoff=0.55,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def runtime_interactive_command_usage(text: str) -> str:
|
|
104
|
+
command_name = _runtime_command_name(text)
|
|
105
|
+
for command in _RUNTIME_INTERACTIVE_COMMANDS:
|
|
106
|
+
if command_name == command.primary.split()[0] or command_name in command.aliases:
|
|
107
|
+
return command.primary
|
|
108
|
+
return ""
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _runtime_command_name(text: str) -> str:
|
|
112
|
+
return str(text).strip().split(maxsplit=1)[0].lower()
|