@kontextmind/kxm 0.6.0
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/.claude-plugin/marketplace.json +19 -0
- package/.kxm/README.md +14 -0
- package/.kxm/assets/README.md +5 -0
- package/.kxm/assets/retrospectives/README.md +5 -0
- package/.kxm/config/README.md +5 -0
- package/.kxm/config/agents.json +43 -0
- package/.kxm/config/env.example +56 -0
- package/.kxm/config/update.example.yaml +9 -0
- package/.kxm/config/workflows/fix.json +160 -0
- package/.kxm/config/workflows/jira-development.json +116 -0
- package/.kxm/config/workflows/provenance-quorum.json +150 -0
- package/.kxm/config/workflows/v04-dogfood.json +72 -0
- package/CHANGELOG.md +465 -0
- package/LICENSE +21 -0
- package/README.md +306 -0
- package/SECURITY.md +72 -0
- package/docs/README.md +48 -0
- package/docs/agent-communication-envelopes-and-gates.md +553 -0
- package/docs/architecture.md +242 -0
- package/docs/assignment-runner.md +241 -0
- package/docs/configuration.md +361 -0
- package/docs/continuous-improvement.md +114 -0
- package/docs/getting-started.md +253 -0
- package/docs/kxm-handbook.md +1090 -0
- package/docs/operations.md +205 -0
- package/docs/provenance-gates.md +291 -0
- package/docs/skills.md +45 -0
- package/docs/templates/README.md +95 -0
- package/docs/templates/adr.md +88 -0
- package/docs/templates/architecture.md +120 -0
- package/docs/templates/bug-fix.md +109 -0
- package/docs/templates/feature.md +108 -0
- package/docs/templates/handoff.md +72 -0
- package/docs/templates/postmortem.md +77 -0
- package/docs/templates/research.md +100 -0
- package/docs/templates/review.md +85 -0
- package/docs/templates/runbook.md +73 -0
- package/docs/templates/test-plan.md +87 -0
- package/docs/templates/test-report.md +72 -0
- package/docs/test-matrix.md +121 -0
- package/docs/troubleshooting.md +249 -0
- package/docs/vnext/README.md +62 -0
- package/docs/vnext/architecture.md +185 -0
- package/docs/vnext/effects-and-recovery.md +172 -0
- package/docs/vnext/lifecycles.md +235 -0
- package/docs/vnext/migration.md +220 -0
- package/docs/vnext/routing.md +184 -0
- package/docs/vnext/synchronization.md +172 -0
- package/docs/vnext/terminology.md +240 -0
- package/docs/vnext/validation.md +335 -0
- package/docs/webhook-workflows.md +240 -0
- package/docs/workflow-guide.md +1150 -0
- package/examples/README.md +102 -0
- package/examples/provenance-workflow.json +40 -0
- package/examples/requester.ts +30 -0
- package/examples/reviewer-agent.ts +29 -0
- package/examples/roundtrip.ts +46 -0
- package/examples/vnext/.kxm/agents/coordinator.yaml +16 -0
- package/examples/vnext/.kxm/agents/critic-1.yaml +16 -0
- package/examples/vnext/.kxm/agents/critic-2.yaml +15 -0
- package/examples/vnext/.kxm/agents/critic-3.yaml +15 -0
- package/examples/vnext/.kxm/agents/implementer.yaml +15 -0
- package/examples/vnext/.kxm/agents/planner.yaml +13 -0
- package/examples/vnext/.kxm/agents/reproducer.yaml +15 -0
- package/examples/vnext/.kxm/agents/reviewer.yaml +15 -0
- package/examples/vnext/.kxm/gates.yaml +8 -0
- package/examples/vnext/.kxm/models/critic-claude.yaml +11 -0
- package/examples/vnext/.kxm/models/critic-gemini.yaml +11 -0
- package/examples/vnext/.kxm/models/critic-grok.yaml +12 -0
- package/examples/vnext/.kxm/models/implementation.yaml +14 -0
- package/examples/vnext/.kxm/models/primary.yaml +17 -0
- package/examples/vnext/.kxm/prices.yaml +111 -0
- package/examples/vnext/.kxm/project/env.yaml +7 -0
- package/examples/vnext/.kxm/project.yaml +32 -0
- package/examples/vnext/.kxm/repo/repo.yaml +8 -0
- package/examples/vnext/.kxm/workflows/default.yaml +92 -0
- package/examples/vnext/.kxm/workflows/fix.yaml +376 -0
- package/examples/vnext/.kxm/workflows/improve.yaml +57 -0
- package/examples/vnext/README.md +53 -0
- package/examples/vnext/records/assignment-result-recorded.json +63 -0
- package/examples/vnext/records/assignment-result.json +46 -0
- package/examples/vnext/records/context-candidate.json +42 -0
- package/examples/vnext/records/delivery-manifest.json +66 -0
- package/examples/vnext/records/effect-uncertainty-resolved-sync.json +67 -0
- package/examples/vnext/records/effect-uncertainty-resolved.json +62 -0
- package/examples/vnext/records/run-created.json +54 -0
- package/examples/vnext/records/sync-event.json +65 -0
- package/examples/vnext/repositories/api/.kxm/repo/env.yaml +7 -0
- package/examples/vnext/repositories/api/.kxm/repo/repo.yaml +8 -0
- package/examples/vnext/repositories/web/.kxm/repo/repo.yaml +8 -0
- package/examples/workflow-signal.ts +63 -0
- package/package.json +129 -0
- package/plugins/kxm/.claude-plugin/plugin.json +73 -0
- package/plugins/kxm/.mcp.json +19 -0
- package/plugins/kxm/README.md +93 -0
- package/plugins/kxm/dist/cli.js +42853 -0
- package/plugins/kxm/dist/client.js +416 -0
- package/plugins/kxm/dist/core.js +1823 -0
- package/plugins/kxm/dist/extension.js +3797 -0
- package/plugins/kxm/dist/mcp-server.js +17104 -0
- package/plugins/kxm/dist/runtime.js +23361 -0
- package/plugins/kxm/dist/server.js +13640 -0
- package/plugins/kxm/dist/vnext-runtime-supervisor.js +21109 -0
- package/plugins/kxm/package.json +12 -0
- package/plugins/kxm/skills/kxm/SKILL.md +97 -0
- package/plugins/kxm/skills/kxm/references/protocol.md +103 -0
- package/plugins/kxm/skills/kxm-session/SKILL.md +53 -0
- package/plugins/kxm/src/arbiter.ts +355 -0
- package/plugins/kxm/src/artifacts-exist.ts +62 -0
- package/plugins/kxm/src/autocomplete.ts +236 -0
- package/plugins/kxm/src/cli.ts +3707 -0
- package/plugins/kxm/src/client.ts +614 -0
- package/plugins/kxm/src/commands.ts +1063 -0
- package/plugins/kxm/src/config.ts +290 -0
- package/plugins/kxm/src/context/providers.ts +101 -0
- package/plugins/kxm/src/context-packet.ts +332 -0
- package/plugins/kxm/src/context.ts +499 -0
- package/plugins/kxm/src/core.ts +6 -0
- package/plugins/kxm/src/database.ts +563 -0
- package/plugins/kxm/src/diagnostics.ts +184 -0
- package/plugins/kxm/src/envelope.ts +118 -0
- package/plugins/kxm/src/extension.ts +895 -0
- package/plugins/kxm/src/external-effects.ts +299 -0
- package/plugins/kxm/src/github-watch.ts +255 -0
- package/plugins/kxm/src/hub-binding.ts +160 -0
- package/plugins/kxm/src/hub.ts +2502 -0
- package/plugins/kxm/src/improve.ts +383 -0
- package/plugins/kxm/src/inbox.ts +10 -0
- package/plugins/kxm/src/kxm-install-kind.ts +113 -0
- package/plugins/kxm/src/kxm-update-config.ts +39 -0
- package/plugins/kxm/src/kxm-update.ts +238 -0
- package/plugins/kxm/src/local-snapshot.ts +406 -0
- package/plugins/kxm/src/logger.ts +198 -0
- package/plugins/kxm/src/mcp-server.ts +143 -0
- package/plugins/kxm/src/memory.ts +385 -0
- package/plugins/kxm/src/nous-pi.ts +287 -0
- package/plugins/kxm/src/nous-provider.ts +729 -0
- package/plugins/kxm/src/price-calc.ts +87 -0
- package/plugins/kxm/src/prices.ts +121 -0
- package/plugins/kxm/src/protocol.ts +172 -0
- package/plugins/kxm/src/recovery.ts +211 -0
- package/plugins/kxm/src/redact.ts +26 -0
- package/plugins/kxm/src/retrospective.ts +400 -0
- package/plugins/kxm/src/routing.ts +830 -0
- package/plugins/kxm/src/runtime.ts +9 -0
- package/plugins/kxm/src/server.ts +117 -0
- package/plugins/kxm/src/session-work.ts +571 -0
- package/plugins/kxm/src/session.ts +184 -0
- package/plugins/kxm/src/skills.ts +535 -0
- package/plugins/kxm/src/state.ts +326 -0
- package/plugins/kxm/src/store.ts +637 -0
- package/plugins/kxm/src/studio-layout.ts +268 -0
- package/plugins/kxm/src/suggest.ts +162 -0
- package/plugins/kxm/src/task-manager.ts +244 -0
- package/plugins/kxm/src/telemetry.ts +116 -0
- package/plugins/kxm/src/tui.ts +1046 -0
- package/plugins/kxm/src/vnext-bindings.ts +403 -0
- package/plugins/kxm/src/vnext-config.ts +1646 -0
- package/plugins/kxm/src/vnext-engine-artifacts.ts +86 -0
- package/plugins/kxm/src/vnext-engine-command.ts +533 -0
- package/plugins/kxm/src/vnext-engine-compile.ts +722 -0
- package/plugins/kxm/src/vnext-engine-evidence.ts +273 -0
- package/plugins/kxm/src/vnext-engine-fold.ts +1400 -0
- package/plugins/kxm/src/vnext-engine-gate-records.ts +583 -0
- package/plugins/kxm/src/vnext-engine-plan.ts +717 -0
- package/plugins/kxm/src/vnext-engine.ts +2458 -0
- package/plugins/kxm/src/vnext-gate-hash.ts +10 -0
- package/plugins/kxm/src/vnext-harness.ts +1142 -0
- package/plugins/kxm/src/vnext-init.ts +430 -0
- package/plugins/kxm/src/vnext-migrate.ts +1848 -0
- package/plugins/kxm/src/vnext-oneshot-producer.ts +424 -0
- package/plugins/kxm/src/vnext-permission.ts +936 -0
- package/plugins/kxm/src/vnext-pi-producer.ts +628 -0
- package/plugins/kxm/src/vnext-repair.ts +1094 -0
- package/plugins/kxm/src/vnext-runtime-owner.ts +320 -0
- package/plugins/kxm/src/vnext-runtime-store.ts +1560 -0
- package/plugins/kxm/src/vnext-runtime-supervisor.ts +586 -0
- package/plugins/kxm/src/vnext-runtime.ts +663 -0
- package/plugins/kxm/src/vnext-template.ts +247 -0
- package/plugins/kxm/src/wiki.ts +313 -0
- package/plugins/kxm/src/workflow.ts +1548 -0
- package/schemas/vnext/README.md +46 -0
- package/schemas/vnext/agent.schema.json +40 -0
- package/schemas/vnext/assignment-result.schema.json +66 -0
- package/schemas/vnext/backup-manifest.schema.json +89 -0
- package/schemas/vnext/candidate.schema.json +109 -0
- package/schemas/vnext/common.schema.json +422 -0
- package/schemas/vnext/context-candidate.schema.json +76 -0
- package/schemas/vnext/context-packet.schema.json +192 -0
- package/schemas/vnext/delivery-manifest.schema.json +159 -0
- package/schemas/vnext/environment.schema.json +66 -0
- package/schemas/vnext/gate-registry.schema.json +109 -0
- package/schemas/vnext/handoff-manifest.schema.json +146 -0
- package/schemas/vnext/init-operation.schema.json +61 -0
- package/schemas/vnext/local-repository-bindings.schema.json +30 -0
- package/schemas/vnext/memory-record.schema.json +45 -0
- package/schemas/vnext/migration-decision.schema.json +26 -0
- package/schemas/vnext/migration-plan.schema.json +123 -0
- package/schemas/vnext/migration-receipt.schema.json +52 -0
- package/schemas/vnext/model.schema.json +42 -0
- package/schemas/vnext/permission-diff.schema.json +57 -0
- package/schemas/vnext/prices.schema.json +115 -0
- package/schemas/vnext/project.schema.json +85 -0
- package/schemas/vnext/repository.schema.json +24 -0
- package/schemas/vnext/run-event.schema.json +460 -0
- package/schemas/vnext/session-brief.schema.json +153 -0
- package/schemas/vnext/sync-event.schema.json +234 -0
- package/schemas/vnext/template-provenance.schema.json +38 -0
- package/schemas/vnext/workflow.schema.json +248 -0
- package/scripts/assignment-run.d.mts +354 -0
- package/scripts/assignment-run.mjs +4451 -0
- package/scripts/build-runtime.mjs +56 -0
- package/scripts/check-generated.mjs +77 -0
- package/scripts/check-versions.mjs +34 -0
- package/scripts/emit-codex-artifacts.d.mts +9 -0
- package/scripts/emit-codex-artifacts.mjs +91 -0
- package/scripts/harness-run.d.mts +83 -0
- package/scripts/harness-run.mjs +2095 -0
- package/scripts/kxm-hub.mjs +105 -0
- package/scripts/kxm-publish-npm.mjs +327 -0
- package/scripts/kxm-release-github.mjs +472 -0
- package/scripts/kxm-runtime-supervisor.mjs +7 -0
- package/scripts/kxm-worker.mjs +1127 -0
- package/scripts/kxm.mjs +27 -0
- package/scripts/roster-policy.d.mts +20 -0
- package/scripts/roster-policy.mjs +161 -0
- package/scripts/smoke-multi-pi.mjs +479 -0
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
// plugins/kxm/src/client.ts
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
|
|
4
|
+
// plugins/kxm/src/protocol.ts
|
|
5
|
+
var DEFAULT_MESSAGE_TTL_MS = 24 * 60 * 6e4;
|
|
6
|
+
var MAX_MESSAGE_TTL_MS = 7 * 24 * 60 * 6e4;
|
|
7
|
+
var DEFAULT_MESSAGE_RETENTION_MS = 7 * 24 * 60 * 6e4;
|
|
8
|
+
var MAX_BODY_BYTES = 256 * 1024;
|
|
9
|
+
|
|
10
|
+
// plugins/kxm/src/workflow.ts
|
|
11
|
+
function canonicalWorkflowEvidenceKey(value) {
|
|
12
|
+
return value.trim().replace(/\s+/gu, " ").toLowerCase();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// plugins/kxm/src/client.ts
|
|
16
|
+
var MeshWaitError = class extends Error {
|
|
17
|
+
waitStatus;
|
|
18
|
+
constructor(waitStatus, messageId) {
|
|
19
|
+
super(waitStatus === "aborted" ? "await cancelled" : `timed out waiting for ${messageId}`);
|
|
20
|
+
this.name = "MeshWaitError";
|
|
21
|
+
this.waitStatus = waitStatus;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
function completedFanoutResult(target, message) {
|
|
25
|
+
if (message.status === "queued" || message.status === "delivered") {
|
|
26
|
+
throw new Error(`message ${message.id} is not complete`);
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
target,
|
|
30
|
+
messageId: message.id,
|
|
31
|
+
status: message.status,
|
|
32
|
+
...message.reply ? { reply: message.reply.content } : {},
|
|
33
|
+
...message.error ? { error: message.error } : {}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function fanoutIdempotencyKey(prefix, target, correlationId, workflowContext) {
|
|
37
|
+
const scope = JSON.stringify(workflowContext ? {
|
|
38
|
+
prefix,
|
|
39
|
+
correlationId: correlationId ?? null,
|
|
40
|
+
target: target.toLowerCase(),
|
|
41
|
+
workflowContext: {
|
|
42
|
+
runId: workflowContext.runId,
|
|
43
|
+
stageId: workflowContext.stageId,
|
|
44
|
+
requirementKey: canonicalWorkflowEvidenceKey(workflowContext.requirementKey),
|
|
45
|
+
attempt: workflowContext.attempt
|
|
46
|
+
}
|
|
47
|
+
} : {
|
|
48
|
+
prefix,
|
|
49
|
+
correlationId: correlationId ?? null,
|
|
50
|
+
target: target.toLowerCase()
|
|
51
|
+
});
|
|
52
|
+
return `fanout:${createHash("sha256").update(scope).digest("hex")}`;
|
|
53
|
+
}
|
|
54
|
+
var HubHttpError = class extends Error {
|
|
55
|
+
statusCode;
|
|
56
|
+
code;
|
|
57
|
+
requestId;
|
|
58
|
+
extras;
|
|
59
|
+
constructor(statusCode, message, code, requestId, extras) {
|
|
60
|
+
super(message);
|
|
61
|
+
this.name = "HubHttpError";
|
|
62
|
+
this.statusCode = statusCode;
|
|
63
|
+
if (code) this.code = code;
|
|
64
|
+
if (requestId) this.requestId = requestId;
|
|
65
|
+
if (extras) this.extras = extras;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
var HubClient = class {
|
|
69
|
+
options;
|
|
70
|
+
agent;
|
|
71
|
+
agentKey;
|
|
72
|
+
heartbeatTimer;
|
|
73
|
+
eventsAbort;
|
|
74
|
+
stopped = true;
|
|
75
|
+
onEvent;
|
|
76
|
+
registration;
|
|
77
|
+
eventLoop;
|
|
78
|
+
constructor(options) {
|
|
79
|
+
this.options = { heartbeatMs: 1e4, reconnectMs: 1e3, requestTimeoutMs: 15e3, ...options };
|
|
80
|
+
}
|
|
81
|
+
async start(onEvent) {
|
|
82
|
+
if (!this.stopped) throw new Error("hub client is already started");
|
|
83
|
+
this.stopped = false;
|
|
84
|
+
this.onEvent = onEvent;
|
|
85
|
+
try {
|
|
86
|
+
await this.register();
|
|
87
|
+
} catch (error) {
|
|
88
|
+
this.stopped = true;
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
this.heartbeatTimer = setInterval(() => void this.heartbeat(), this.options.heartbeatMs);
|
|
92
|
+
this.heartbeatTimer.unref();
|
|
93
|
+
this.eventLoop = this.runEventLoop();
|
|
94
|
+
return this.agent;
|
|
95
|
+
}
|
|
96
|
+
async stop() {
|
|
97
|
+
if (this.stopped) return;
|
|
98
|
+
this.stopped = true;
|
|
99
|
+
this.eventsAbort?.abort();
|
|
100
|
+
if (this.heartbeatTimer) clearInterval(this.heartbeatTimer);
|
|
101
|
+
await this.eventLoop;
|
|
102
|
+
if (this.agent) {
|
|
103
|
+
try {
|
|
104
|
+
await this.request(`/v1/agents/${encodeURIComponent(this.agent.id)}`, { method: "DELETE" });
|
|
105
|
+
} catch {
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
this.agent = void 0;
|
|
109
|
+
this.agentKey = void 0;
|
|
110
|
+
this.onEvent = void 0;
|
|
111
|
+
this.eventLoop = void 0;
|
|
112
|
+
}
|
|
113
|
+
async listAgents() {
|
|
114
|
+
const result = await this.request("/v1/agents");
|
|
115
|
+
return result.agents;
|
|
116
|
+
}
|
|
117
|
+
async send(options) {
|
|
118
|
+
const result = await this.request("/v1/messages", {
|
|
119
|
+
method: "POST",
|
|
120
|
+
body: JSON.stringify(options)
|
|
121
|
+
});
|
|
122
|
+
return result.message;
|
|
123
|
+
}
|
|
124
|
+
async fanout(options) {
|
|
125
|
+
const targets = [...new Set(options.targets.map((target) => target.trim().toLowerCase()).filter(Boolean))];
|
|
126
|
+
if (targets.length < 1 || targets.length > 3) throw new Error("fanout requires between one and three unique targets");
|
|
127
|
+
return await Promise.all(targets.map(async (target) => {
|
|
128
|
+
let message;
|
|
129
|
+
try {
|
|
130
|
+
message = await this.send({
|
|
131
|
+
target,
|
|
132
|
+
content: options.content,
|
|
133
|
+
delivery: "followUp",
|
|
134
|
+
...options.correlationId ? { correlationId: options.correlationId } : {},
|
|
135
|
+
...options.workflowContext ? { workflowContext: options.workflowContext } : {},
|
|
136
|
+
...options.idempotencyKeyPrefix ? {
|
|
137
|
+
idempotencyKey: fanoutIdempotencyKey(
|
|
138
|
+
options.idempotencyKeyPrefix,
|
|
139
|
+
target,
|
|
140
|
+
options.correlationId,
|
|
141
|
+
options.workflowContext
|
|
142
|
+
)
|
|
143
|
+
} : {},
|
|
144
|
+
...options.ttlMs ? { ttlMs: options.ttlMs } : {}
|
|
145
|
+
});
|
|
146
|
+
const completed = await this.awaitResponse(
|
|
147
|
+
message.id,
|
|
148
|
+
options.timeoutMs ?? 30 * 6e4,
|
|
149
|
+
options.signal
|
|
150
|
+
);
|
|
151
|
+
return completedFanoutResult(target, completed);
|
|
152
|
+
} catch (error) {
|
|
153
|
+
if (message && error instanceof MeshWaitError) {
|
|
154
|
+
try {
|
|
155
|
+
const current = await this.getMessage(message.id);
|
|
156
|
+
if (current.status === "queued" || current.status === "delivered") {
|
|
157
|
+
return {
|
|
158
|
+
target,
|
|
159
|
+
messageId: current.id,
|
|
160
|
+
status: "pending",
|
|
161
|
+
messageStatus: current.status,
|
|
162
|
+
expiresAt: current.expiresAt,
|
|
163
|
+
waitStatus: error.waitStatus
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
return completedFanoutResult(target, current);
|
|
167
|
+
} catch (finalError) {
|
|
168
|
+
return {
|
|
169
|
+
target,
|
|
170
|
+
messageId: message.id,
|
|
171
|
+
status: "error",
|
|
172
|
+
error: finalError instanceof Error ? finalError.message : String(finalError)
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
target,
|
|
178
|
+
...message ? { messageId: message.id } : {},
|
|
179
|
+
status: "error",
|
|
180
|
+
error: error instanceof Error ? error.message : String(error)
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
}));
|
|
184
|
+
}
|
|
185
|
+
async getMessage(messageId) {
|
|
186
|
+
const result = await this.request(`/v1/messages/${encodeURIComponent(messageId)}`);
|
|
187
|
+
return result.message;
|
|
188
|
+
}
|
|
189
|
+
async acknowledge(messageId) {
|
|
190
|
+
const result = await this.request(
|
|
191
|
+
`/v1/messages/${encodeURIComponent(messageId)}/ack`,
|
|
192
|
+
{ method: "POST", body: "{}" }
|
|
193
|
+
);
|
|
194
|
+
return result.message;
|
|
195
|
+
}
|
|
196
|
+
async reply(messageId, content) {
|
|
197
|
+
const result = await this.request(
|
|
198
|
+
`/v1/messages/${encodeURIComponent(messageId)}/reply`,
|
|
199
|
+
{ method: "POST", body: JSON.stringify({ content }) }
|
|
200
|
+
);
|
|
201
|
+
return result.message;
|
|
202
|
+
}
|
|
203
|
+
async cancel(messageId) {
|
|
204
|
+
const result = await this.request(
|
|
205
|
+
`/v1/messages/${encodeURIComponent(messageId)}`,
|
|
206
|
+
{ method: "DELETE" }
|
|
207
|
+
);
|
|
208
|
+
return result.message;
|
|
209
|
+
}
|
|
210
|
+
async listWorkflows() {
|
|
211
|
+
const result = await this.request("/v1/workflows");
|
|
212
|
+
return result.runs;
|
|
213
|
+
}
|
|
214
|
+
async getWorkflow(runId) {
|
|
215
|
+
return await this.request(`/v1/workflows/${encodeURIComponent(runId)}`);
|
|
216
|
+
}
|
|
217
|
+
async checkpointWorkflow(runId, input) {
|
|
218
|
+
return await this.request(`/v1/workflows/${encodeURIComponent(runId)}/checkpoints`, {
|
|
219
|
+
method: "POST",
|
|
220
|
+
body: JSON.stringify(input)
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
async waitForWorkflowSignal(runId, input) {
|
|
224
|
+
return await this.request(`/v1/workflows/${encodeURIComponent(runId)}/waits`, {
|
|
225
|
+
method: "POST",
|
|
226
|
+
body: JSON.stringify(input)
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
async recordWorkflowEntry(runId, input) {
|
|
230
|
+
const result = await this.request(
|
|
231
|
+
`/v1/workflows/${encodeURIComponent(runId)}/journal`,
|
|
232
|
+
{ method: "POST", body: JSON.stringify(input) }
|
|
233
|
+
);
|
|
234
|
+
return result.entry;
|
|
235
|
+
}
|
|
236
|
+
async improvementReport() {
|
|
237
|
+
return await this.request("/v1/improvements");
|
|
238
|
+
}
|
|
239
|
+
// ----- Context operating-system API (v0.5, issue #34) -----
|
|
240
|
+
async contextGet(input) {
|
|
241
|
+
return await this.request("/v1/context/get", { method: "POST", body: JSON.stringify(input) });
|
|
242
|
+
}
|
|
243
|
+
async contextRecall(input) {
|
|
244
|
+
return await this.request("/v1/context/recall", { method: "POST", body: JSON.stringify(input) });
|
|
245
|
+
}
|
|
246
|
+
async contextState(input) {
|
|
247
|
+
return await this.request("/v1/context/state", { method: "POST", body: JSON.stringify(input) });
|
|
248
|
+
}
|
|
249
|
+
async contextStatePropose(input) {
|
|
250
|
+
return await this.request("/v1/context/state/propose", { method: "POST", body: JSON.stringify(input) });
|
|
251
|
+
}
|
|
252
|
+
async contextStatePromote(input) {
|
|
253
|
+
return await this.request("/v1/context/state/promote", { method: "POST", body: JSON.stringify(input) });
|
|
254
|
+
}
|
|
255
|
+
async contextEpisode(input) {
|
|
256
|
+
return await this.request("/v1/context/episode", { method: "POST", body: JSON.stringify(input) });
|
|
257
|
+
}
|
|
258
|
+
async contextExplain(input) {
|
|
259
|
+
return await this.request("/v1/context/explain", { method: "POST", body: JSON.stringify(input) });
|
|
260
|
+
}
|
|
261
|
+
async contextWikiCompile(input) {
|
|
262
|
+
return await this.request("/v1/context/wiki/compile", { method: "POST", body: JSON.stringify(input) });
|
|
263
|
+
}
|
|
264
|
+
async awaitResponse(messageId, timeoutMs = 30 * 6e4, signal) {
|
|
265
|
+
const deadline = Date.now() + timeoutMs;
|
|
266
|
+
while (Date.now() < deadline) {
|
|
267
|
+
if (signal?.aborted) throw new MeshWaitError("aborted", messageId);
|
|
268
|
+
const message = await this.getMessage(messageId);
|
|
269
|
+
if (["replied", "cancelled", "expired", "error"].includes(message.status)) return message;
|
|
270
|
+
await new Promise((resolve, reject) => {
|
|
271
|
+
const onAbort = () => {
|
|
272
|
+
signal?.removeEventListener("abort", onAbort);
|
|
273
|
+
clearTimeout(timer);
|
|
274
|
+
reject(new MeshWaitError("aborted", messageId));
|
|
275
|
+
};
|
|
276
|
+
const timer = setTimeout(() => {
|
|
277
|
+
signal?.removeEventListener("abort", onAbort);
|
|
278
|
+
resolve();
|
|
279
|
+
}, Math.min(500, Math.max(1, deadline - Date.now())));
|
|
280
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
281
|
+
if (signal?.aborted) onAbort();
|
|
282
|
+
timer.unref();
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
throw new MeshWaitError("timed_out", messageId);
|
|
286
|
+
}
|
|
287
|
+
async heartbeat() {
|
|
288
|
+
if (this.stopped || !this.agent) return;
|
|
289
|
+
try {
|
|
290
|
+
await this.request(`/v1/agents/${encodeURIComponent(this.agent.id)}/heartbeat`, {
|
|
291
|
+
method: "POST",
|
|
292
|
+
body: "{}"
|
|
293
|
+
});
|
|
294
|
+
} catch (error) {
|
|
295
|
+
if (error instanceof HubHttpError && error.statusCode === 401) void this.recoverRegistration();
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
async runEventLoop() {
|
|
299
|
+
while (!this.stopped && this.agent) {
|
|
300
|
+
this.eventsAbort = new AbortController();
|
|
301
|
+
try {
|
|
302
|
+
const response = await fetch(
|
|
303
|
+
`${this.options.serverUrl.replace(/\/$/, "")}/v1/events?agentId=${encodeURIComponent(this.agent.id)}`,
|
|
304
|
+
{
|
|
305
|
+
headers: this.headers(),
|
|
306
|
+
signal: this.eventsAbort.signal
|
|
307
|
+
}
|
|
308
|
+
);
|
|
309
|
+
if (response.status === 401) {
|
|
310
|
+
await response.body?.cancel();
|
|
311
|
+
await this.recoverRegistration();
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
if (!response.ok || !response.body) throw new Error(`event stream failed with HTTP ${response.status}`);
|
|
315
|
+
const decoder = new TextDecoder();
|
|
316
|
+
let buffer = "";
|
|
317
|
+
for await (const chunk of response.body) {
|
|
318
|
+
if (this.stopped) break;
|
|
319
|
+
buffer += decoder.decode(chunk, { stream: true }).replaceAll("\r\n", "\n");
|
|
320
|
+
let boundary;
|
|
321
|
+
while ((boundary = buffer.indexOf("\n\n")) >= 0) {
|
|
322
|
+
const frame = buffer.slice(0, boundary);
|
|
323
|
+
buffer = buffer.slice(boundary + 2);
|
|
324
|
+
const data = frame.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n");
|
|
325
|
+
if (!data) continue;
|
|
326
|
+
const parsed = JSON.parse(data);
|
|
327
|
+
if ("type" in parsed) await this.onEvent?.(parsed);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
} catch (error) {
|
|
331
|
+
if (this.stopped || error instanceof Error && error.name === "AbortError") return;
|
|
332
|
+
}
|
|
333
|
+
if (!this.stopped) await new Promise((resolve) => setTimeout(resolve, this.options.reconnectMs));
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
headers(includeIdentity = true) {
|
|
337
|
+
const headers = { "content-type": "application/json" };
|
|
338
|
+
if (this.options.authToken) headers.authorization = `Bearer ${this.options.authToken}`;
|
|
339
|
+
if (includeIdentity && this.agent && this.agentKey) {
|
|
340
|
+
headers["x-kxm-agent-id"] = this.agent.id;
|
|
341
|
+
headers["x-kxm-agent-key"] = this.agentKey;
|
|
342
|
+
}
|
|
343
|
+
return headers;
|
|
344
|
+
}
|
|
345
|
+
async register() {
|
|
346
|
+
if (this.registration) return this.registration;
|
|
347
|
+
this.registration = (async () => {
|
|
348
|
+
const registration = await this.request("/v1/agents/register", {
|
|
349
|
+
method: "POST",
|
|
350
|
+
body: JSON.stringify({
|
|
351
|
+
name: this.options.name,
|
|
352
|
+
purpose: this.options.purpose,
|
|
353
|
+
project: this.options.project,
|
|
354
|
+
model: this.options.model
|
|
355
|
+
})
|
|
356
|
+
}, false);
|
|
357
|
+
this.agent = registration.agent;
|
|
358
|
+
this.agentKey = registration.agentKey;
|
|
359
|
+
return registration.agent;
|
|
360
|
+
})();
|
|
361
|
+
try {
|
|
362
|
+
return await this.registration;
|
|
363
|
+
} finally {
|
|
364
|
+
this.registration = void 0;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
async recoverRegistration() {
|
|
368
|
+
if (this.stopped) return;
|
|
369
|
+
this.agent = void 0;
|
|
370
|
+
this.agentKey = void 0;
|
|
371
|
+
await this.register();
|
|
372
|
+
}
|
|
373
|
+
async request(path, init = {}, includeIdentity = true) {
|
|
374
|
+
const requestTimeoutMs = this.options.requestTimeoutMs ?? 15e3;
|
|
375
|
+
const timeoutSignal = AbortSignal.timeout(requestTimeoutMs);
|
|
376
|
+
const signal = init.signal ? AbortSignal.any([init.signal, timeoutSignal]) : timeoutSignal;
|
|
377
|
+
let response;
|
|
378
|
+
try {
|
|
379
|
+
response = await fetch(`${this.options.serverUrl.replace(/\/$/, "")}${path}`, {
|
|
380
|
+
...init,
|
|
381
|
+
signal,
|
|
382
|
+
headers: { ...this.headers(includeIdentity), ...init.headers ?? {} }
|
|
383
|
+
});
|
|
384
|
+
} catch (error) {
|
|
385
|
+
if (timeoutSignal.aborted) throw new Error(`request timed out after ${requestTimeoutMs}ms`);
|
|
386
|
+
throw error;
|
|
387
|
+
}
|
|
388
|
+
const text = await response.text();
|
|
389
|
+
let body = {};
|
|
390
|
+
if (text) {
|
|
391
|
+
try {
|
|
392
|
+
body = JSON.parse(text);
|
|
393
|
+
} catch {
|
|
394
|
+
throw new Error(`hub returned invalid JSON with HTTP ${response.status}`);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
if (!response.ok) {
|
|
398
|
+
const extras = {};
|
|
399
|
+
for (const key of ["operation", "nextAction", "assignedCoordinatorName"]) {
|
|
400
|
+
if (typeof body[key] === "string") extras[key] = body[key];
|
|
401
|
+
}
|
|
402
|
+
throw new HubHttpError(
|
|
403
|
+
response.status,
|
|
404
|
+
String(body.error ?? `HTTP ${response.status}`),
|
|
405
|
+
typeof body.code === "string" ? body.code : void 0,
|
|
406
|
+
response.headers.get("x-request-id") ?? void 0,
|
|
407
|
+
Object.keys(extras).length > 0 ? extras : void 0
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
return body;
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
export {
|
|
414
|
+
HubClient,
|
|
415
|
+
HubHttpError
|
|
416
|
+
};
|