Files
pikasTech-HWLAB/internal/agent/index.mjs
T
2026-05-21 14:57:00 +00:00

353 lines
8.2 KiB
JavaScript

import crypto from "node:crypto";
import path from "node:path";
import { ENVIRONMENT_DEV } from "../protocol/index.mjs";
export const AGENT_MGR_SERVICE_ID = "hwlab-agent-mgr";
export const AGENT_WORKER_SERVICE_ID = "hwlab-agent-worker";
export const AGENT_SKILLS_SERVICE_ID = "hwlab-agent-skills";
export const AGENT_SESSION_STATUSES = Object.freeze([
"create",
"start",
"trace",
"finish",
"cleanup"
]);
export const WORKER_SESSION_STATUSES = Object.freeze([
"create",
"start",
"trace",
"finish",
"cleanup"
]);
export function createAgentSession({
agentSessionId,
projectId,
requestedBy = "usr_local",
goal = "",
environment = ENVIRONMENT_DEV,
now = () => new Date().toISOString(),
metadata = {}
}) {
const timestamp = now();
return {
agentSessionId,
projectId,
serviceId: AGENT_MGR_SERVICE_ID,
status: "create",
environment,
requestedBy,
goal,
startedAt: timestamp,
updatedAt: timestamp,
metadata: {
...metadata,
lifecycle: AGENT_SESSION_STATUSES.slice(),
currentState: "create"
}
};
}
export function advanceAgentSession(session, nextStatus, { now = () => new Date().toISOString(), extra = {} } = {}) {
assertAgentStatus(nextStatus);
const timestamp = now();
const completedAt = nextStatus === "finish" ? timestamp : session.completedAt;
return {
...session,
status: nextStatus,
completedAt,
updatedAt: timestamp,
metadata: {
...session.metadata,
...extra,
currentState: nextStatus
}
};
}
export function createWorkspaceVolume({ agentSessionId, projectId, now = () => new Date().toISOString() }) {
return {
workspaceVolumeId: `vol_${agentSessionId}`,
agentSessionId,
projectId,
serviceId: AGENT_WORKER_SERVICE_ID,
mountPath: path.posix.join("/workspace", projectId, agentSessionId),
lifecycle: "create",
createdAt: now(),
updatedAt: now()
};
}
export function createWorkerSession({
workerSessionId,
agentSessionId,
projectId,
gatewaySessionId = null,
now = () => new Date().toISOString(),
metadata = {}
}) {
const timestamp = now();
return {
workerSessionId,
agentSessionId,
projectId,
serviceId: AGENT_WORKER_SERVICE_ID,
gatewaySessionId,
status: "create",
environment: ENVIRONMENT_DEV,
startedAt: timestamp,
updatedAt: timestamp,
metadata: {
...metadata,
lifecycle: WORKER_SESSION_STATUSES.slice(),
workspaceVolumeId: `vol_${agentSessionId}`,
currentState: "create"
}
};
}
export function advanceWorkerSession(session, nextStatus, { now = () => new Date().toISOString(), extra = {} } = {}) {
assertWorkerStatus(nextStatus);
const timestamp = now();
const completedAt = nextStatus === "finish" ? timestamp : session.completedAt;
return {
...session,
status: nextStatus,
completedAt,
updatedAt: timestamp,
metadata: {
...session.metadata,
...extra,
currentState: nextStatus
}
};
}
export function createTraceEvent({
traceEventId,
traceId,
projectId,
serviceId = AGENT_MGR_SERVICE_ID,
level = "info",
message,
environment = ENVIRONMENT_DEV,
now = () => new Date().toISOString(),
agentSessionId = null,
workerSessionId = null,
operationId = null,
metadata = {}
}) {
return {
traceEventId,
traceId,
projectId,
serviceId,
level,
message,
environment,
occurredAt: now(),
agentSessionId,
workerSessionId,
operationId,
metadata: {
...metadata
}
};
}
export function createEvidenceRecord({
evidenceId,
projectId,
operationId,
uri,
sha256 = digestHex(uri),
serviceId = AGENT_WORKER_SERVICE_ID,
environment = ENVIRONMENT_DEV,
kind = "artifact",
now = () => new Date().toISOString(),
agentSessionId = null,
workerSessionId = null,
mimeType = "text/plain",
sizeBytes = 0,
metadata = {}
}) {
return {
evidenceId,
projectId,
operationId,
agentSessionId,
workerSessionId,
kind,
uri,
mimeType,
sha256,
sizeBytes,
serviceId,
environment,
metadata: {
...metadata
},
createdAt: now()
};
}
export function createAgentRunPlan({
agentSessionId,
projectId,
goal = "",
prompt = "",
skillCommitId = null,
now = () => new Date().toISOString()
}) {
const traceId = `trc_${agentSessionId}`;
const workerSessionId = `wkr_${agentSessionId}`;
const operationId = `op_${agentSessionId}`;
const evidenceId = `evi_${agentSessionId}`;
const traceEventId = `tev_${agentSessionId}`;
const workspaceVolume = createWorkspaceVolume({ agentSessionId, projectId, now });
const agentSession = createAgentSession({
agentSessionId,
projectId,
goal,
now
});
const workerSession = createWorkerSession({
workerSessionId,
agentSessionId,
projectId,
now
});
const traceEvent = createTraceEvent({
traceEventId,
traceId,
projectId,
message: prompt || goal || "agent runtime dry run",
agentSessionId,
workerSessionId,
operationId,
now
});
const evidenceRecord = createEvidenceRecord({
evidenceId,
projectId,
operationId,
uri: `file://${path.posix.join(workspaceVolume.mountPath, "evidence.txt")}`,
agentSessionId,
workerSessionId,
metadata: {
skillCommitId,
prompt,
goal,
workspaceVolumeId: workspaceVolume.workspaceVolumeId
},
now
});
return {
agentSession: advanceAgentSession(agentSession, "start", {
now,
extra: {
workerSessionId,
workspaceVolumeId: workspaceVolume.workspaceVolumeId
}
}),
workerSession: advanceWorkerSession(workerSession, "start", {
now,
extra: {
workspaceVolumeId: workspaceVolume.workspaceVolumeId
}
}),
workspaceVolume,
events: [
{ state: "create", actor: AGENT_MGR_SERVICE_ID, agentSessionId },
{ state: "start", actor: AGENT_WORKER_SERVICE_ID, workerSessionId },
{ state: "trace", actor: AGENT_MGR_SERVICE_ID, traceEvent },
{ state: "finish", actor: AGENT_WORKER_SERVICE_ID, evidenceRecord },
{ state: "cleanup", actor: AGENT_WORKER_SERVICE_ID, workspaceVolumeId: workspaceVolume.workspaceVolumeId }
],
traceEvent,
evidenceRecord,
skillCommitId
};
}
export function finalizeAgentRun(plan, { now = () => new Date().toISOString() } = {}) {
const finishedAgentSession = advanceAgentSession(plan.agentSession, "finish", { now });
const finishedWorkerSession = advanceWorkerSession(plan.workerSession, "finish", { now });
return {
...plan,
agentSession: advanceAgentSession(finishedAgentSession, "cleanup", {
now,
extra: { cleanedUp: true }
}),
workerSession: advanceWorkerSession(finishedWorkerSession, "cleanup", {
now,
extra: { cleanedUp: true }
}),
cleanup: {
workspaceVolumeId: plan.workspaceVolume.workspaceVolumeId,
removed: true
}
};
}
export function createSkillsManifest({
commitId,
skillName = "hwlab-agent-runtime",
serviceId = AGENT_SKILLS_SERVICE_ID,
environment = ENVIRONMENT_DEV,
skills = []
}) {
if (!commitId) {
throw new Error("skills manifest requires explicit commitId");
}
return {
manifestVersion: "v1",
environment,
commitId,
namespace: "hwlab",
endpoint: "http://74.48.78.17:6667",
profiles: {
dev: {
name: "dev",
enabled: true,
namespace: "hwlab-dev",
endpoint: "http://74.48.78.17:6667",
notes: "explicit repo-local skills artifact"
}
},
services: [
{
serviceId,
image: `${skillName}:${commitId.slice(0, 7)}`,
namespace: "hwlab-dev",
healthPath: "/health",
profile: "dev",
env: {
HWLAB_SKILLS_COMMIT_ID: commitId
}
}
],
skills
};
}
function assertAgentStatus(status) {
if (!AGENT_SESSION_STATUSES.includes(status)) {
throw new Error(`unknown agent session status ${JSON.stringify(status)}`);
}
}
function assertWorkerStatus(status) {
if (!WORKER_SESSION_STATUSES.includes(status)) {
throw new Error(`unknown worker session status ${JSON.stringify(status)}`);
}
}
function digestHex(value) {
return crypto.createHash("sha256").update(String(value)).digest("hex");
}