@hunsu/protocol 0.1.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/LICENSE +157 -0
- package/dist/command-decoder.d.ts +5 -0
- package/dist/command-decoder.js +329 -0
- package/dist/constants.d.ts +15 -0
- package/dist/constants.js +15 -0
- package/dist/decoder-helpers.d.ts +58 -0
- package/dist/decoder-helpers.js +1757 -0
- package/dist/errors.d.ts +14 -0
- package/dist/errors.js +28 -0
- package/dist/event-decoder.d.ts +5 -0
- package/dist/event-decoder.js +232 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +10 -0
- package/dist/lifecycle.d.ts +67 -0
- package/dist/lifecycle.js +271 -0
- package/dist/model.d.ts +1058 -0
- package/dist/model.js +1 -0
- package/dist/primitives.d.ts +59 -0
- package/dist/primitives.js +135 -0
- package/dist/projection.d.ts +8 -0
- package/dist/projection.js +875 -0
- package/dist/prompt-template.d.ts +12 -0
- package/dist/prompt-template.js +83 -0
- package/dist/protocol-validation.d.ts +36 -0
- package/dist/protocol-validation.js +1139 -0
- package/dist/result.d.ts +20 -0
- package/dist/result.js +21 -0
- package/dist/workflow-helpers.d.ts +31 -0
- package/dist/workflow-helpers.js +193 -0
- package/dist/workflow.d.ts +10 -0
- package/dist/workflow.js +594 -0
- package/package.json +30 -0
- package/src/command-decoder.ts +292 -0
- package/src/constants.ts +27 -0
- package/src/decoder-helpers.ts +1672 -0
- package/src/errors.ts +38 -0
- package/src/event-decoder.ts +225 -0
- package/src/index.ts +43 -0
- package/src/lifecycle.ts +403 -0
- package/src/model.ts +1233 -0
- package/src/primitives.ts +196 -0
- package/src/projection.ts +1081 -0
- package/src/prompt-template.ts +97 -0
- package/src/protocol-validation.ts +1252 -0
- package/src/result.ts +35 -0
- package/src/workflow-helpers.ts +239 -0
- package/src/workflow.ts +753 -0
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import {
|
|
2
|
+
workflowError,
|
|
3
|
+
type DomainWorkflowError
|
|
4
|
+
} from "./errors.ts";
|
|
5
|
+
import type { ValidatedCommand } from "./model.ts";
|
|
6
|
+
import {
|
|
7
|
+
makeLineId,
|
|
8
|
+
makeMoveCommit,
|
|
9
|
+
makeMoveId,
|
|
10
|
+
makeNonEmptyText,
|
|
11
|
+
makeTeamName,
|
|
12
|
+
makeRequestGoal,
|
|
13
|
+
makeRequestId,
|
|
14
|
+
makeRequestTitle,
|
|
15
|
+
makeSingleItemArray,
|
|
16
|
+
makeSkillDraftId,
|
|
17
|
+
makeSummary,
|
|
18
|
+
makeDestinationId,
|
|
19
|
+
makeFailureReason
|
|
20
|
+
} from "./primitives.ts";
|
|
21
|
+
import { ok, type Result } from "./result.ts";
|
|
22
|
+
import {
|
|
23
|
+
decodeAgentConversationRef,
|
|
24
|
+
decodeArtifactRecord,
|
|
25
|
+
decodeDestinationIdArray,
|
|
26
|
+
decodeDestinationSeedArray,
|
|
27
|
+
decodeHubPackageLock,
|
|
28
|
+
decodeHunsuOrigin,
|
|
29
|
+
decodeEvidenceArray,
|
|
30
|
+
decodeHunsuDraftRecord,
|
|
31
|
+
decodeOptionalAt,
|
|
32
|
+
decodeOptionalExecuteId,
|
|
33
|
+
decodeOptionalNonEmptyText,
|
|
34
|
+
decodeRecord,
|
|
35
|
+
decodeRiskArray,
|
|
36
|
+
decodeWorktreeRef,
|
|
37
|
+
type HarnessDecoder
|
|
38
|
+
} from "./decoder-helpers.ts";
|
|
39
|
+
|
|
40
|
+
export function decodeCommand(input: unknown, decodeHarness: HarnessDecoder): Result<ValidatedCommand, DomainWorkflowError> {
|
|
41
|
+
const command = decodeRecord(input, "Command");
|
|
42
|
+
if (!command.ok) {
|
|
43
|
+
return command;
|
|
44
|
+
}
|
|
45
|
+
switch (command.value.type) {
|
|
46
|
+
case "RegisterHunsuOrigin": {
|
|
47
|
+
const origin = decodeHunsuOrigin(command.value.origin, "origin");
|
|
48
|
+
if (!origin.ok) return origin;
|
|
49
|
+
const actor = decodeOptionalNonEmptyText(command.value.actor, "actor");
|
|
50
|
+
if (!actor.ok) return actor;
|
|
51
|
+
const at = decodeOptionalAt(command.value.at);
|
|
52
|
+
if (!at.ok) return at;
|
|
53
|
+
return ok({ type: "RegisterHunsuOrigin", origin: origin.value, actor: actor.value, at: at.value });
|
|
54
|
+
}
|
|
55
|
+
case "CreateInitialTeam": {
|
|
56
|
+
const requestId = primitive(makeRequestId(command.value.requestId));
|
|
57
|
+
if (!requestId.ok) return requestId;
|
|
58
|
+
const lineId = primitive(makeLineId(command.value.lineId));
|
|
59
|
+
if (!lineId.ok) return lineId;
|
|
60
|
+
const title = primitive(makeRequestTitle(command.value.title));
|
|
61
|
+
if (!title.ok) return title;
|
|
62
|
+
const goal = primitive(makeRequestGoal(command.value.goal));
|
|
63
|
+
if (!goal.ok) return goal;
|
|
64
|
+
const destinations = decodeDestinationSeedArray(command.value.destinations, "destinations");
|
|
65
|
+
if (!destinations.ok) return destinations;
|
|
66
|
+
const harness = command.value.harness === undefined ? ok(undefined) : decodeHarness(command.value.harness);
|
|
67
|
+
if (!harness.ok) return harness;
|
|
68
|
+
const harnessLock = decodeHubPackageLock(command.value.harnessLock, "harnessLock", ["team"]);
|
|
69
|
+
if (!harnessLock.ok) return harnessLock;
|
|
70
|
+
const teamName = command.value.teamName === undefined ? ok(undefined) : primitive(makeTeamName(command.value.teamName));
|
|
71
|
+
if (!teamName.ok) return teamName;
|
|
72
|
+
const actor = decodeOptionalNonEmptyText(command.value.actor, "actor");
|
|
73
|
+
if (!actor.ok) return actor;
|
|
74
|
+
const at = decodeOptionalAt(command.value.at);
|
|
75
|
+
if (!at.ok) return at;
|
|
76
|
+
return ok({
|
|
77
|
+
type: "CreateInitialTeam",
|
|
78
|
+
requestId: requestId.value,
|
|
79
|
+
lineId: lineId.value,
|
|
80
|
+
title: title.value,
|
|
81
|
+
goal: goal.value,
|
|
82
|
+
destinations: destinations.value,
|
|
83
|
+
harness: harness.value,
|
|
84
|
+
harnessLock: harnessLock.value,
|
|
85
|
+
teamName: teamName.value,
|
|
86
|
+
actor: actor.value,
|
|
87
|
+
at: at.value
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
case "StartLine": {
|
|
91
|
+
const requestId = primitive(makeRequestId(command.value.requestId));
|
|
92
|
+
if (!requestId.ok) return requestId;
|
|
93
|
+
const lineId = primitive(makeLineId(command.value.lineId));
|
|
94
|
+
if (!lineId.ok) return lineId;
|
|
95
|
+
const teamName = command.value.teamName === undefined ? ok(undefined) : primitive(makeTeamName(command.value.teamName));
|
|
96
|
+
if (!teamName.ok) return teamName;
|
|
97
|
+
const at = decodeOptionalAt(command.value.at);
|
|
98
|
+
if (!at.ok) return at;
|
|
99
|
+
return ok({ type: "StartLine", lineId: lineId.value, requestId: requestId.value, teamName: teamName.value, at: at.value });
|
|
100
|
+
}
|
|
101
|
+
case "PauseLine":
|
|
102
|
+
case "ResumeLine": {
|
|
103
|
+
const lineId = primitive(makeLineId(command.value.lineId));
|
|
104
|
+
if (!lineId.ok) return lineId;
|
|
105
|
+
const at = decodeOptionalAt(command.value.at);
|
|
106
|
+
if (!at.ok) return at;
|
|
107
|
+
return ok({ type: command.value.type, lineId: lineId.value, at: at.value });
|
|
108
|
+
}
|
|
109
|
+
case "AcceptLine":
|
|
110
|
+
case "RejectLine": {
|
|
111
|
+
const lineId = primitive(makeLineId(command.value.lineId));
|
|
112
|
+
if (!lineId.ok) return lineId;
|
|
113
|
+
const reason = decodeOptionalNonEmptyText(command.value.reason, "reason");
|
|
114
|
+
if (!reason.ok) return reason;
|
|
115
|
+
const at = decodeOptionalAt(command.value.at);
|
|
116
|
+
if (!at.ok) return at;
|
|
117
|
+
return ok({ type: command.value.type, lineId: lineId.value, reason: reason.value, at: at.value });
|
|
118
|
+
}
|
|
119
|
+
case "ClaimDestination":
|
|
120
|
+
case "StartDestinationWork": {
|
|
121
|
+
const destinationId = primitive(makeDestinationId(command.value.destinationId));
|
|
122
|
+
if (!destinationId.ok) return destinationId;
|
|
123
|
+
const actor = primitive(makeNonEmptyText(command.value.actor, "actor"));
|
|
124
|
+
if (!actor.ok) return actor;
|
|
125
|
+
const at = decodeOptionalAt(command.value.at);
|
|
126
|
+
if (!at.ok) return at;
|
|
127
|
+
return ok({ type: command.value.type, destinationId: destinationId.value, actor: actor.value, at: at.value });
|
|
128
|
+
}
|
|
129
|
+
case "ReportDestinationBlocked": {
|
|
130
|
+
const destinationId = primitive(makeDestinationId(command.value.destinationId));
|
|
131
|
+
if (!destinationId.ok) return destinationId;
|
|
132
|
+
const reason = primitive(makeFailureReason(command.value.reason, "reason"));
|
|
133
|
+
if (!reason.ok) return reason;
|
|
134
|
+
const actor = primitive(makeNonEmptyText(command.value.actor, "actor"));
|
|
135
|
+
if (!actor.ok) return actor;
|
|
136
|
+
const at = decodeOptionalAt(command.value.at);
|
|
137
|
+
if (!at.ok) return at;
|
|
138
|
+
return ok({ type: "ReportDestinationBlocked", destinationId: destinationId.value, reason: reason.value, actor: actor.value, at: at.value });
|
|
139
|
+
}
|
|
140
|
+
case "RecordMove": {
|
|
141
|
+
const lineId = primitive(makeLineId(command.value.lineId));
|
|
142
|
+
if (!lineId.ok) return lineId;
|
|
143
|
+
const moveId = primitive(makeMoveId(command.value.moveId));
|
|
144
|
+
if (!moveId.ok) return moveId;
|
|
145
|
+
const summary = primitive(makeSummary(command.value.summary));
|
|
146
|
+
if (!summary.ok) return summary;
|
|
147
|
+
const commit = primitive(makeMoveCommit(command.value.commit, "commit"));
|
|
148
|
+
if (!commit.ok) return commit;
|
|
149
|
+
const reachedDestinationIds = decodeDestinationIdArray(command.value.reachedDestinationIds, "reachedDestinationIds", true);
|
|
150
|
+
if (!reachedDestinationIds.ok) return reachedDestinationIds;
|
|
151
|
+
const singleReachedDestinationIds = primitive(makeSingleItemArray(reachedDestinationIds.value, "reachedDestinationIds"));
|
|
152
|
+
if (!singleReachedDestinationIds.ok) return singleReachedDestinationIds;
|
|
153
|
+
const evidence = decodeEvidenceArray(command.value.evidence, "evidence", true);
|
|
154
|
+
if (!evidence.ok) return evidence;
|
|
155
|
+
const risks = command.value.risks === undefined ? ok(undefined) : decodeRiskArray(command.value.risks, "risks");
|
|
156
|
+
if (!risks.ok) return risks;
|
|
157
|
+
const executeId = decodeOptionalExecuteId(command.value.executeId);
|
|
158
|
+
if (!executeId.ok) return executeId;
|
|
159
|
+
const conversationRef = decodeAgentConversationRef(command.value.conversationRef, "conversationRef");
|
|
160
|
+
if (!conversationRef.ok) return conversationRef;
|
|
161
|
+
const worktree = decodeWorktreeRef(command.value.worktree, "worktree");
|
|
162
|
+
if (!worktree.ok) return worktree;
|
|
163
|
+
const actor = primitive(makeNonEmptyText(command.value.actor, "actor"));
|
|
164
|
+
if (!actor.ok) return actor;
|
|
165
|
+
const at = decodeOptionalAt(command.value.at);
|
|
166
|
+
if (!at.ok) return at;
|
|
167
|
+
return ok({
|
|
168
|
+
type: "RecordMove",
|
|
169
|
+
lineId: lineId.value,
|
|
170
|
+
moveId: moveId.value,
|
|
171
|
+
summary: summary.value,
|
|
172
|
+
commit: commit.value,
|
|
173
|
+
reachedDestinationIds: singleReachedDestinationIds.value,
|
|
174
|
+
evidence: evidence.value,
|
|
175
|
+
risks: risks.value,
|
|
176
|
+
executeId: executeId.value,
|
|
177
|
+
conversationRef: conversationRef.value,
|
|
178
|
+
worktree: worktree.value,
|
|
179
|
+
actor: actor.value,
|
|
180
|
+
at: at.value
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
case "RecordAccident": {
|
|
184
|
+
const lineId = primitive(makeLineId(command.value.lineId));
|
|
185
|
+
if (!lineId.ok) return lineId;
|
|
186
|
+
const moveId = primitive(makeMoveId(command.value.moveId));
|
|
187
|
+
if (!moveId.ok) return moveId;
|
|
188
|
+
const summary = primitive(makeSummary(command.value.summary));
|
|
189
|
+
if (!summary.ok) return summary;
|
|
190
|
+
const commit = primitive(makeMoveCommit(command.value.commit, "commit"));
|
|
191
|
+
if (!commit.ok) return commit;
|
|
192
|
+
const evidence = decodeEvidenceArray(command.value.evidence, "evidence", true);
|
|
193
|
+
if (!evidence.ok) return evidence;
|
|
194
|
+
const failureReason = primitive(makeFailureReason(command.value.failureReason));
|
|
195
|
+
if (!failureReason.ok) return failureReason;
|
|
196
|
+
const risks = command.value.risks === undefined ? ok(undefined) : decodeRiskArray(command.value.risks, "risks");
|
|
197
|
+
if (!risks.ok) return risks;
|
|
198
|
+
const executeId = decodeOptionalExecuteId(command.value.executeId);
|
|
199
|
+
if (!executeId.ok) return executeId;
|
|
200
|
+
const conversationRef = decodeAgentConversationRef(command.value.conversationRef, "conversationRef");
|
|
201
|
+
if (!conversationRef.ok) return conversationRef;
|
|
202
|
+
const worktree = decodeWorktreeRef(command.value.worktree, "worktree");
|
|
203
|
+
if (!worktree.ok) return worktree;
|
|
204
|
+
const actor = primitive(makeNonEmptyText(command.value.actor, "actor"));
|
|
205
|
+
if (!actor.ok) return actor;
|
|
206
|
+
const at = decodeOptionalAt(command.value.at);
|
|
207
|
+
if (!at.ok) return at;
|
|
208
|
+
return ok({
|
|
209
|
+
type: "RecordAccident",
|
|
210
|
+
lineId: lineId.value,
|
|
211
|
+
moveId: moveId.value,
|
|
212
|
+
summary: summary.value,
|
|
213
|
+
commit: commit.value,
|
|
214
|
+
evidence: evidence.value,
|
|
215
|
+
failureReason: failureReason.value,
|
|
216
|
+
risks: risks.value,
|
|
217
|
+
executeId: executeId.value,
|
|
218
|
+
conversationRef: conversationRef.value,
|
|
219
|
+
worktree: worktree.value,
|
|
220
|
+
actor: actor.value,
|
|
221
|
+
at: at.value
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
case "AttachMoveEvidence": {
|
|
225
|
+
const moveId = primitive(makeMoveId(command.value.moveId));
|
|
226
|
+
if (!moveId.ok) return moveId;
|
|
227
|
+
const artifact = decodeArtifactRecord(command.value.artifact, "artifact");
|
|
228
|
+
if (!artifact.ok) return artifact;
|
|
229
|
+
const actor = primitive(makeNonEmptyText(command.value.actor, "actor"));
|
|
230
|
+
if (!actor.ok) return actor;
|
|
231
|
+
const at = decodeOptionalAt(command.value.at);
|
|
232
|
+
if (!at.ok) return at;
|
|
233
|
+
return ok({ type: "AttachMoveEvidence", moveId: moveId.value, artifact: artifact.value, actor: actor.value, at: at.value });
|
|
234
|
+
}
|
|
235
|
+
case "ConfirmHunsuDraft": {
|
|
236
|
+
const draft = decodeHunsuDraftRecord(command.value.draft, "draft", decodeHarness);
|
|
237
|
+
if (!draft.ok) return draft;
|
|
238
|
+
if (draft.value.status !== "ready") {
|
|
239
|
+
return workflowError(`HUNSU Draft ${draft.value.id} cannot be confirmed from status ${draft.value.status}`);
|
|
240
|
+
}
|
|
241
|
+
const actor = primitive(makeNonEmptyText(command.value.actor, "actor"));
|
|
242
|
+
if (!actor.ok) return actor;
|
|
243
|
+
const at = decodeOptionalAt(command.value.at);
|
|
244
|
+
if (!at.ok) return at;
|
|
245
|
+
return ok({
|
|
246
|
+
type: "ConfirmHunsuDraft",
|
|
247
|
+
draft: draft.value,
|
|
248
|
+
actor: actor.value,
|
|
249
|
+
at: at.value
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
case "CreateSkillDraft": {
|
|
253
|
+
const draftId = primitive(makeSkillDraftId(command.value.draftId));
|
|
254
|
+
if (!draftId.ok) return draftId;
|
|
255
|
+
const lineId = primitive(makeLineId(command.value.lineId));
|
|
256
|
+
if (!lineId.ok) return lineId;
|
|
257
|
+
const name = primitive(makeNonEmptyText(command.value.name, "name"));
|
|
258
|
+
if (!name.ok) return name;
|
|
259
|
+
const draftPath = primitive(makeNonEmptyText(command.value.draftPath, "draftPath"));
|
|
260
|
+
if (!draftPath.ok) return draftPath;
|
|
261
|
+
const sourcePath = decodeOptionalNonEmptyText(command.value.sourcePath, "sourcePath");
|
|
262
|
+
if (!sourcePath.ok) return sourcePath;
|
|
263
|
+
const actor = primitive(makeNonEmptyText(command.value.actor, "actor"));
|
|
264
|
+
if (!actor.ok) return actor;
|
|
265
|
+
const at = decodeOptionalAt(command.value.at);
|
|
266
|
+
if (!at.ok) return at;
|
|
267
|
+
return ok({ type: "CreateSkillDraft", draftId: draftId.value, lineId: lineId.value, name: name.value, draftPath: draftPath.value, sourcePath: sourcePath.value, actor: actor.value, at: at.value });
|
|
268
|
+
}
|
|
269
|
+
case "DiscardSkillDraft": {
|
|
270
|
+
const draftId = primitive(makeSkillDraftId(command.value.draftId));
|
|
271
|
+
if (!draftId.ok) return draftId;
|
|
272
|
+
const actor = primitive(makeNonEmptyText(command.value.actor, "actor"));
|
|
273
|
+
if (!actor.ok) return actor;
|
|
274
|
+
const at = decodeOptionalAt(command.value.at);
|
|
275
|
+
if (!at.ok) return at;
|
|
276
|
+
return ok({ type: "DiscardSkillDraft", draftId: draftId.value, actor: actor.value, at: at.value });
|
|
277
|
+
}
|
|
278
|
+
case "RecordArtifact": {
|
|
279
|
+
const artifact = decodeArtifactRecord(command.value.artifact, "artifact");
|
|
280
|
+
if (!artifact.ok) return artifact;
|
|
281
|
+
const at = decodeOptionalAt(command.value.at);
|
|
282
|
+
if (!at.ok) return at;
|
|
283
|
+
return ok({ type: "RecordArtifact", artifact: artifact.value, at: at.value });
|
|
284
|
+
}
|
|
285
|
+
default:
|
|
286
|
+
return workflowError(`Unsupported command type: ${String(command.value.type)}`);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function primitive<T>(result: Result<T, { message: string }>): Result<T, DomainWorkflowError> {
|
|
291
|
+
return result.ok ? ok(result.value) : workflowError(result.error.message);
|
|
292
|
+
}
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
DestinationSource,
|
|
3
|
+
DestinationStatus,
|
|
4
|
+
ArtifactActionKind,
|
|
5
|
+
ArtifactActionSourceScope,
|
|
6
|
+
HunsuDraftStatus,
|
|
7
|
+
LineStatus,
|
|
8
|
+
MoveOutcome,
|
|
9
|
+
ReasoningEffort,
|
|
10
|
+
ServiceTier
|
|
11
|
+
} from "./model.ts";
|
|
12
|
+
|
|
13
|
+
export const REASONING_EFFORTS = ["default", "minimal", "low", "medium", "high", "xhigh"] as const satisfies readonly ReasoningEffort[];
|
|
14
|
+
export const SERVICE_TIERS = ["default", "fast"] as const satisfies readonly ServiceTier[];
|
|
15
|
+
export const DOMAIN_ROLES = ["TEAM", "DIRECTOR", "SYSTEM"] as const;
|
|
16
|
+
export const GUARDRAIL_SCOPES = ["input", "output", "context", "artifact", "final_evidence"] as const;
|
|
17
|
+
export const GUARDRAIL_SEVERITIES = ["warn", "block"] as const;
|
|
18
|
+
export const VOTE_RULES = ["majority", "unanimous", "weighted", "coordinator_decides"] as const;
|
|
19
|
+
export const DESTINATION_STATUSES = ["pending", "claimed", "in_progress", "reached", "blocked", "superseded", "canceled"] as const satisfies readonly DestinationStatus[];
|
|
20
|
+
export const DESTINATION_SOURCES = ["initial-execute-team", "initial-request", "hunsu"] as const satisfies readonly DestinationSource[];
|
|
21
|
+
export const LINE_STATUSES = ["active", "paused", "complete", "failed", "abandoned"] as const satisfies readonly LineStatus[];
|
|
22
|
+
export const MOVE_OUTCOMES = ["arrived", "accident"] as const satisfies readonly MoveOutcome[];
|
|
23
|
+
export const HUNSU_DRAFT_STATUSES = ["draft", "ready", "confirmed", "discarded"] as const satisfies readonly HunsuDraftStatus[];
|
|
24
|
+
export const SKILL_DRAFT_STATUSES = ["draft", "accepted", "discarded"] as const;
|
|
25
|
+
export const ARTIFACT_KINDS = ["transcript", "command-output", "test-log", "screenshot", "diff-summary", "note"] as const;
|
|
26
|
+
export const ARTIFACT_ACTION_KINDS = ["host", "check"] as const satisfies readonly ArtifactActionKind[];
|
|
27
|
+
export const ARTIFACT_ACTION_SOURCE_SCOPES = ["move", "commit", "move-or-commit"] as const satisfies readonly ArtifactActionSourceScope[];
|