@kitman-sh/cli 0.0.0-stage → 0.7.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.
@@ -0,0 +1,962 @@
1
+ // @bun
2
+ import {
3
+ dual,
4
+ hasProperty,
5
+ Prototype,
6
+ isNone,
7
+ Service,
8
+ empty,
9
+ add,
10
+ getOrUndefined2,
11
+ get,
12
+ merge,
13
+ Reference,
14
+ fromInputUnsafe,
15
+ toMillis,
16
+ TaggedClass,
17
+ hasInterruptsOnly,
18
+ combine,
19
+ squash,
20
+ hasInterrupts,
21
+ isSuccess2,
22
+ addFinalizerExit,
23
+ effectDiscard,
24
+ min,
25
+ exponential,
26
+ spaced,
27
+ while_,
28
+ setInputType,
29
+ promise,
30
+ succeed5,
31
+ suspend,
32
+ sync,
33
+ void_2,
34
+ callback,
35
+ gen,
36
+ fail4,
37
+ failCause2,
38
+ die3,
39
+ try_,
40
+ yieldNow,
41
+ flatMap,
42
+ andThen,
43
+ tap,
44
+ map3,
45
+ catch_,
46
+ mapError,
47
+ orDie,
48
+ retry,
49
+ sandbox,
50
+ matchEffect,
51
+ context,
52
+ contextWith,
53
+ provideContext,
54
+ provideService,
55
+ acquireUseRelease,
56
+ onExit,
57
+ interruptible,
58
+ uninterruptibleMask,
59
+ annotateCurrentSpan,
60
+ withSpan,
61
+ fnUntraced,
62
+ getCurrent,
63
+ encode2,
64
+ transform,
65
+ encodeBase64Url,
66
+ decodeBase64UrlString,
67
+ decodeUnknownEffect,
68
+ decodeEffect,
69
+ decodeSync,
70
+ encodeUnknownEffect,
71
+ encodeEffect,
72
+ encodeSync,
73
+ isSchema,
74
+ optionalKey,
75
+ optional,
76
+ Literal,
77
+ Never,
78
+ Unknown,
79
+ String2,
80
+ Boolean2,
81
+ Void,
82
+ Struct,
83
+ Record,
84
+ Tuple,
85
+ ArraySchema,
86
+ Union,
87
+ Literals,
88
+ decodeTo,
89
+ tag,
90
+ makeFilter,
91
+ Finite,
92
+ Int,
93
+ Defect,
94
+ fromJsonString,
95
+ Cause,
96
+ Exit,
97
+ Class,
98
+ TaggedError2,
99
+ toJsonSchemaDocument,
100
+ toCodecJson,
101
+ Json,
102
+ LoopCapabilities
103
+ } from "./chunk-704ws2q7.js";
104
+
105
+ // packages/sdk/src/loop/define.ts
106
+ var LoopOutcome = Literals(["feature", "bug", "refactor", "investigation", "docs", "migration"]);
107
+ var defineLoop = (definition) => definition;
108
+
109
+ class ManifestInvalid extends TaggedError2()("ManifestInvalid", {
110
+ loop: String2,
111
+ message: String2
112
+ }) {
113
+ }
114
+ var jsonSchemaOf = (loop, schema) => try_({
115
+ try: () => toJsonSchemaDocument(schema),
116
+ catch: (error) => new ManifestInvalid({ loop, message: `Schema has no JSON Schema: ${String(error)}` })
117
+ }).pipe(flatMap((document) => decodeUnknownEffect(JsonObjectSchema)({ ...document.schema, $defs: document.definitions }).pipe(mapError((error) => new ManifestInvalid({ loop, message: error.message })))));
118
+ var JsonObjectSchema = Record(String2, Json);
119
+ // node_modules/.bun/effect@4.0.4/node_modules/effect/dist/workflow/internal/crypto.js
120
+ var makeHashDigest = (original) => map3(promise(() => crypto.subtle.digest("SHA-256", new TextEncoder().encode(original))), (buffer) => {
121
+ const data = new Uint8Array(buffer);
122
+ let hexString = "";
123
+ for (let i = 0;i < 16; i++) {
124
+ hexString += data[i].toString(16).padStart(2, "0");
125
+ }
126
+ return hexString;
127
+ });
128
+
129
+ // node_modules/.bun/effect@4.0.4/node_modules/effect/dist/workflow/Workflow.js
130
+ var TypeId = "~effect/workflow/Workflow";
131
+ var EngineTag = /* @__PURE__ */ Service("effect/workflow/WorkflowEngine");
132
+ var InstanceTag = /* @__PURE__ */ Service("effect/workflow/WorkflowEngine/WorkflowInstance");
133
+ var makeExecutionIdFromPayload = (self, payload) => makeHashDigest(`${self._tag.length}:${self._tag}:${self.idempotencyKey(payload)}`);
134
+ var Proto = {
135
+ [TypeId]: TypeId,
136
+ annotate(tag, value) {
137
+ return makeProto({
138
+ _tag: this._tag,
139
+ payloadSchema: this.payloadSchema,
140
+ successSchema: this.successSchema,
141
+ errorSchema: this.errorSchema,
142
+ annotations: add(this.annotations, tag, value),
143
+ idempotencyKey: this.idempotencyKey,
144
+ suspendedRetrySchedule: this.suspendedRetrySchedule
145
+ });
146
+ },
147
+ annotateMerge(context) {
148
+ return makeProto({
149
+ _tag: this._tag,
150
+ payloadSchema: this.payloadSchema,
151
+ successSchema: this.successSchema,
152
+ errorSchema: this.errorSchema,
153
+ annotations: merge(this.annotations, context),
154
+ idempotencyKey: this.idempotencyKey,
155
+ suspendedRetrySchedule: this.suspendedRetrySchedule
156
+ });
157
+ },
158
+ execute(fields, opts) {
159
+ return suspend(() => {
160
+ const payload = this.payloadSchema.make(fields);
161
+ const run = flatMap(EngineTag, (engine) => flatMap(makeExecutionIdFromPayload(this, payload), (executionId) => andThen(annotateCurrentSpan({
162
+ executionId
163
+ }), engine.execute(this, {
164
+ executionId,
165
+ payload,
166
+ discard: opts?.discard,
167
+ suspendedRetrySchedule: this.suspendedRetrySchedule
168
+ }))));
169
+ return opts?.discard ? run : withPendingActivity(run);
170
+ }).pipe(withSpan(`${this._tag}.execute`, {}, {
171
+ captureStackTrace: false
172
+ }));
173
+ },
174
+ poll(executionId) {
175
+ return flatMap(EngineTag, (engine) => engine.poll(this, executionId)).pipe(withSpan(`${this._tag}.poll`, {
176
+ attributes: {
177
+ executionId
178
+ }
179
+ }, {
180
+ captureStackTrace: false
181
+ }));
182
+ },
183
+ interrupt(executionId) {
184
+ return flatMap(EngineTag, (engine) => engine.interrupt(this, executionId)).pipe(withSpan(`${this._tag}.interrupt`, {
185
+ attributes: {
186
+ executionId
187
+ }
188
+ }, {
189
+ captureStackTrace: false
190
+ }));
191
+ },
192
+ resume(executionId) {
193
+ return flatMap(EngineTag, (engine) => engine.resume(this, executionId)).pipe(withSpan(`${this._tag}.resume`, {
194
+ attributes: {
195
+ executionId
196
+ }
197
+ }, {
198
+ captureStackTrace: false
199
+ }));
200
+ },
201
+ toLayer(execute) {
202
+ return effectDiscard(flatMap(EngineTag, (engine) => engine.register(this, execute)));
203
+ },
204
+ executionId(payload) {
205
+ return flatMap(orDie(this.payloadSchema.makeEffect(payload)), (payload) => makeExecutionIdFromPayload(this, payload));
206
+ },
207
+ withCompensation: (...args) => withCompensation(...args)
208
+ };
209
+ var makeProto = (options) => {
210
+ function Workflow() {}
211
+ Object.setPrototypeOf(Workflow, Proto);
212
+ Object.assign(Workflow, options);
213
+ return Workflow;
214
+ };
215
+ var make = (tag, options) => makeProto({
216
+ _tag: tag,
217
+ payloadSchema: isSchema(options.payload) ? options.payload : Struct(options.payload),
218
+ successSchema: options.success ?? Void,
219
+ errorSchema: options.error ?? Never,
220
+ annotations: options.annotations ?? empty(),
221
+ idempotencyKey: options.idempotencyKey,
222
+ suspendedRetrySchedule: options.suspendedRetrySchedule
223
+ });
224
+ var ResultTypeId = "~effect/workflow/Workflow/Result";
225
+ var isResult = (u) => hasProperty(u, ResultTypeId);
226
+ class Suspended extends (/* @__PURE__ */ Class("effect/workflow/Workflow/Suspended")({
227
+ _tag: /* @__PURE__ */ tag("Suspended"),
228
+ cause: /* @__PURE__ */ optional(/* @__PURE__ */ Cause(Never, /* @__PURE__ */ Defect()))
229
+ })) {
230
+ [ResultTypeId] = ResultTypeId;
231
+ }
232
+ var wrapActivityResult = (effect, isSuspend) => contextWith((context) => {
233
+ const instance = get(context, InstanceTag);
234
+ return acquireUseRelease(sync(() => adoptActivityUnsafe(context, instance) ?? registerActivityUnsafe(instance)), () => effect, (registration, exit) => {
235
+ releaseActivityUnsafe(registration);
236
+ const isSuspended = isSuccess2(exit) && isSuspend(exit.value);
237
+ if (isSuccess2(exit) && isResult(exit.value) && exit.value._tag === "Suspended" && exit.value.cause) {
238
+ instance.cause = instance.cause ? combine(instance.cause, exit.value.cause) : exit.value.cause;
239
+ }
240
+ return isSuspended && instance.activityState.count > 0 ? waitForZero(instance) : void_2;
241
+ });
242
+ });
243
+ var PendingActivityRegistration = /* @__PURE__ */ Service("effect/workflow/Workflow/PendingActivityRegistration");
244
+ var registerActivityUnsafe = (instance) => {
245
+ const state = instance.activityState;
246
+ if (state.count === 0)
247
+ state.latch.closeUnsafe();
248
+ state.count++;
249
+ return {
250
+ instance,
251
+ state: "pending"
252
+ };
253
+ };
254
+ var adoptActivityUnsafe = (context, instance) => {
255
+ const pending = getOrUndefined2(context, PendingActivityRegistration);
256
+ if (!pending || pending.instance !== instance) {
257
+ return;
258
+ }
259
+ if (pending.state !== "pending") {
260
+ throw new Error("Workflow.wrapActivityResult: pending child registration has already been consumed");
261
+ }
262
+ pending.state = "adopted";
263
+ return pending;
264
+ };
265
+ var releaseActivityUnsafe = (registration) => {
266
+ if (registration.state === "released")
267
+ return;
268
+ registration.state = "released";
269
+ const state = registration.instance.activityState;
270
+ state.count--;
271
+ if (state.count === 0)
272
+ state.latch.openUnsafe();
273
+ };
274
+ var withPendingActivity = (effect) => contextWith((context) => {
275
+ const instance = getOrUndefined2(context, InstanceTag);
276
+ if (!instance)
277
+ return effect;
278
+ return acquireUseRelease(sync(() => registerActivityUnsafe(instance)), (registration) => provideService(effect, PendingActivityRegistration, registration), (registration) => sync(() => releaseActivityUnsafe(registration)));
279
+ });
280
+ var waitForZero = /* @__PURE__ */ fnUntraced(function* (instance) {
281
+ const state = instance.activityState;
282
+ while (true) {
283
+ if (state.count > 0) {
284
+ yield* state.latch.await;
285
+ yield* yieldNow;
286
+ continue;
287
+ }
288
+ yield* yieldNow;
289
+ if (state.count === 0)
290
+ return;
291
+ }
292
+ });
293
+ var addFinalizer = /* @__PURE__ */ fnUntraced(function* (f) {
294
+ const instance = yield* InstanceTag;
295
+ const services = yield* context();
296
+ yield* addFinalizerExit(instance.scope, (exit) => instance.abandoned ? void_2 : provideContext(f(exit), services));
297
+ });
298
+ var withCompensation = /* @__PURE__ */ dual(2, (effect, compensation) => uninterruptibleMask((restore) => tap(restore(effect), (value) => addFinalizer((exit) => isSuccess2(exit) ? void_2 : compensation(value, exit.cause)))));
299
+ var suspend2 = (instance) => interruptible(callback(() => {
300
+ instance.suspended = true;
301
+ const fiber = getCurrent();
302
+ fiber.interruptUnsafe(fiber.id);
303
+ }));
304
+
305
+ // node_modules/.bun/effect@4.0.4/node_modules/effect/dist/workflow/DurableDeferred.js
306
+ var TypeId2 = "~effect/workflow/DurableDeferred";
307
+ var make2 = (name, options) => {
308
+ const successSchema = options?.success ?? Void;
309
+ const errorSchema = options?.error ?? Never;
310
+ return {
311
+ [TypeId2]: TypeId2,
312
+ name,
313
+ successSchema,
314
+ errorSchema,
315
+ exitSchema: Exit(toCodecJson(successSchema), toCodecJson(errorSchema), toCodecJson(Defect())),
316
+ withActivityAttempt: gen(function* () {
317
+ const attempt = yield* CurrentAttempt;
318
+ return make2(`${name}/${attempt}`, {
319
+ success: successSchema,
320
+ error: errorSchema
321
+ });
322
+ })
323
+ };
324
+ };
325
+ var EngineTag2 = /* @__PURE__ */ Service("effect/workflow/WorkflowEngine");
326
+ var InstanceTag2 = /* @__PURE__ */ Service("effect/workflow/WorkflowEngine/WorkflowInstance");
327
+ var CurrentAttempt = /* @__PURE__ */ Reference("effect/workflow/Activity/CurrentAttempt", {
328
+ defaultValue: () => 1
329
+ });
330
+ var awaitOwners = /* @__PURE__ */ new WeakMap;
331
+ var registerAwait = (instance, name) => {
332
+ instance.awaitedDeferreds.set(name, (instance.awaitedDeferreds.get(name) ?? 0) + 1);
333
+ awaitOwners.get(instance)?.push(name);
334
+ };
335
+ var await_ = /* @__PURE__ */ fnUntraced(function* (self) {
336
+ const engine = yield* EngineTag2;
337
+ const instance = yield* InstanceTag2;
338
+ registerAwait(instance, self.name);
339
+ const exit = yield* wrapActivityResult(engine.deferredResult(self), isNone);
340
+ if (isNone(exit)) {
341
+ return yield* suspend2(instance);
342
+ }
343
+ return yield* exit.value;
344
+ });
345
+ class TokenParsed extends (/* @__PURE__ */ Class("effect/workflow/DurableDeferred/TokenParsed")({
346
+ workflowName: String2,
347
+ executionId: String2,
348
+ deferredName: String2
349
+ })) {
350
+ get asToken() {
351
+ return encode2(JSON.stringify([this.workflowName, this.executionId, this.deferredName]));
352
+ }
353
+ static FromString = /* @__PURE__ */ String2.pipe(/* @__PURE__ */ decodeTo(/* @__PURE__ */ fromJsonString(/* @__PURE__ */ Tuple([String2, String2, String2])), {
354
+ decode: /* @__PURE__ */ decodeBase64UrlString(),
355
+ encode: /* @__PURE__ */ encodeBase64Url()
356
+ }), /* @__PURE__ */ decodeTo(TokenParsed, {
357
+ decode: /* @__PURE__ */ transform(([workflowName, executionId, deferredName]) => new TokenParsed({
358
+ workflowName,
359
+ executionId,
360
+ deferredName
361
+ })),
362
+ encode: /* @__PURE__ */ transform((parsed) => [parsed.workflowName, parsed.executionId, parsed.deferredName])
363
+ }));
364
+ static fromString = /* @__PURE__ */ decodeSync(TokenParsed.FromString);
365
+ static encode = /* @__PURE__ */ encodeSync(TokenParsed.FromString);
366
+ }
367
+ var token = /* @__PURE__ */ fnUntraced(function* (self) {
368
+ const instance = yield* InstanceTag2;
369
+ return tokenFromExecutionId(self, instance);
370
+ });
371
+ var tokenFromExecutionId = /* @__PURE__ */ dual(2, (self, options) => new TokenParsed({
372
+ workflowName: options.workflow._tag,
373
+ executionId: options.executionId,
374
+ deferredName: self.name
375
+ }).asToken);
376
+
377
+ // node_modules/.bun/effect@4.0.4/node_modules/effect/dist/workflow/Activity.js
378
+ var TypeId3 = "~effect/workflow/Activity";
379
+ var make3 = (options) => {
380
+ const successSchema = options.success ?? Void;
381
+ const errorSchema = options.error ?? Never;
382
+ const successSchemaJson = toCodecJson(successSchema);
383
+ const errorSchemaJson = toCodecJson(errorSchema);
384
+ let execute;
385
+ const executeWithoutInterrupt = retryOnInterrupt(options.name, options.interruptRetryPolicy)(options.execute);
386
+ const self = {
387
+ ...Prototype({
388
+ label: "Activity",
389
+ evaluate(_) {
390
+ return execute;
391
+ }
392
+ }),
393
+ [TypeId3]: TypeId3,
394
+ name: options.name,
395
+ successSchema,
396
+ errorSchema,
397
+ exitSchema: Exit(successSchemaJson, errorSchemaJson, Defect()),
398
+ exitSchemaPartial: Exit(successSchemaJson, errorSchemaJson, Unknown),
399
+ annotations: options.annotations ?? empty(),
400
+ annotate(tag, value) {
401
+ return make3({
402
+ ...options,
403
+ annotations: add(self.annotations, tag, value)
404
+ });
405
+ },
406
+ annotateMerge(context) {
407
+ return make3({
408
+ ...options,
409
+ annotations: merge(self.annotations, context)
410
+ });
411
+ },
412
+ execute: executeWithoutInterrupt,
413
+ executeEncoded: matchEffect(executeWithoutInterrupt, {
414
+ onFailure: (error) => flatMap(orDie(encodeEffect(errorSchemaJson)(error)), fail4),
415
+ onSuccess: (value) => orDie(encodeEffect(successSchemaJson)(value))
416
+ })
417
+ };
418
+ execute = makeExecute(self);
419
+ return self;
420
+ };
421
+ var interruptRetryPolicy = /* @__PURE__ */ min([exponential(400, 1.5), spaced("10 seconds")]).pipe(/* @__PURE__ */ setInputType(), /* @__PURE__ */ while_((meta) => meta.attempt <= 10 && hasInterrupts(meta.input)));
422
+ var retryOnInterrupt = (name, policy = interruptRetryPolicy) => (effect) => flatMap(InstanceTag3, (instance) => effect.pipe(sandbox, retry({
423
+ schedule: policy,
424
+ while: () => !instance.suspended
425
+ }), catch_((cause) => hasInterrupts(cause) && !instance.suspended ? die3(`Activity "${name}" interrupted and retry attempts exhausted`) : failCause2(cause))));
426
+ var CurrentAttempt2 = /* @__PURE__ */ Reference("effect/workflow/Activity/CurrentAttempt", {
427
+ defaultValue: () => 1
428
+ });
429
+ var EngineTag3 = /* @__PURE__ */ Service("effect/workflow/WorkflowEngine");
430
+ var InstanceTag3 = /* @__PURE__ */ Service("effect/workflow/WorkflowEngine/WorkflowInstance");
431
+ var makeExecute = /* @__PURE__ */ fnUntraced(function* (activity) {
432
+ const engine = yield* EngineTag3;
433
+ const instance = yield* InstanceTag3;
434
+ const attempt = yield* CurrentAttempt2;
435
+ yield* annotateCurrentSpan({
436
+ executionId: instance.executionId
437
+ });
438
+ const result = yield* wrapActivityResult(engine.activityExecute(activity, attempt), (_) => _._tag === "Suspended");
439
+ if (result._tag === "Suspended") {
440
+ return yield* suspend2(instance);
441
+ }
442
+ return yield* result.exit;
443
+ }, (effect, activity) => withSpan(effect, activity.name, {
444
+ captureStackTrace: false
445
+ }));
446
+ // packages/sdk/src/loop/evidence.ts
447
+ var EvidenceCheck = Struct({
448
+ name: String2,
449
+ command: String2,
450
+ status: Literals(["passed", "failed"]),
451
+ preExisting: Boolean2,
452
+ durationMs: Finite,
453
+ evidenceId: String2
454
+ });
455
+ var VerifyResult = Struct({
456
+ revision: String2,
457
+ tree: String2,
458
+ passed: Boolean2,
459
+ checks: ArraySchema(EvidenceCheck)
460
+ });
461
+ var verdict = (reasons) => reasons.length === 0 ? { settled: true } : { settled: false, reasons };
462
+ var missing = (side) => `No verification evidence for the ${side}`;
463
+ var allPass = (side, result) => {
464
+ if (result === undefined)
465
+ return [missing(side)];
466
+ if (result.checks.length === 0)
467
+ return [`No approved verification ran on the ${side} tree ${result.tree}`];
468
+ return result.checks.filter((check) => check.status === "failed" && !check.preExisting).map((check) => `${check.name} fails on the ${side} tree ${result.tree}`);
469
+ };
470
+ var checkOn = (side, result, name) => {
471
+ if (result === undefined)
472
+ return { reason: missing(side) };
473
+ if (name === undefined)
474
+ return { reason: `No check was named for the ${side}` };
475
+ const found = result.checks.find((check) => check.name === name);
476
+ return found === undefined ? { reason: `No evidence for ${name} on the ${side} tree ${result.tree}` } : { found };
477
+ };
478
+ var passesOnHead = (head, name) => {
479
+ const { found, reason } = checkOn("head", head, name);
480
+ if (found === undefined)
481
+ return [reason];
482
+ return found.status === "passed" ? [] : [`${found.name} fails on the head tree ${head?.tree}`];
483
+ };
484
+ var bugReasons = (input) => {
485
+ const base = checkOn("base", input.base, input.reproducing);
486
+ const head = checkOn("head", input.head, input.reproducing);
487
+ return [
488
+ ...base.found === undefined ? [base.reason] : base.found.status === "failed" ? [] : [`${base.found.name} does not fail on the base tree ${input.base?.tree}, so the bug is not reproduced`],
489
+ ...head.found === undefined ? [head.reason] : head.found.status === "passed" ? [] : [`${head.found.name} fails on the head tree ${input.head?.tree}`]
490
+ ];
491
+ };
492
+ var conclusionReasons = (conclusion) => {
493
+ if (conclusion === undefined || conclusion.text.trim() === "")
494
+ return ["No conclusion was given"];
495
+ return conclusion.sources.length === 0 ? ["The conclusion names no sources"] : [];
496
+ };
497
+ var scopeReasons = (scope) => {
498
+ if (scope === undefined)
499
+ return ["Review has not judged the scope"];
500
+ if (scope.met)
501
+ return [];
502
+ return scope.unmet.length === 0 ? ["Review found the scope unmet"] : scope.unmet.map((item) => `Review found the scope unmet: ${item}`);
503
+ };
504
+ var evidenceGate = (input) => {
505
+ switch (input.outcome) {
506
+ case "feature":
507
+ return verdict([...allPass("head", input.head), ...scopeReasons(input.review)]);
508
+ case "bug":
509
+ return verdict(bugReasons(input));
510
+ case "refactor":
511
+ return verdict([...allPass("base", input.base), ...allPass("head", input.head)]);
512
+ case "investigation":
513
+ return verdict(conclusionReasons(input.conclusion));
514
+ case "docs":
515
+ return verdict((input.documented ?? []).length === 0 ? ["No documented command was named"] : (input.documented ?? []).flatMap((name) => passesOnHead(input.head, name)));
516
+ case "migration":
517
+ return verdict(passesOnHead(input.head, input.rollback));
518
+ default:
519
+ return verdict(allPass("head", input.head));
520
+ }
521
+ };
522
+
523
+ // packages/sdk/src/loop/steps.ts
524
+ class StepFailed extends TaggedError2()("StepFailed", {
525
+ step: String2,
526
+ reason: String2
527
+ }) {
528
+ get message() {
529
+ return `Step "${this.step}" failed: ${this.reason}`;
530
+ }
531
+ }
532
+
533
+ class OutputInvalid extends TaggedError2()("OutputInvalid", {
534
+ step: String2,
535
+ message: String2
536
+ }) {
537
+ }
538
+
539
+ class QuestionRefused extends TaggedError2()("QuestionRefused", {
540
+ step: String2,
541
+ answer: String2,
542
+ options: ArraySchema(String2)
543
+ }) {
544
+ }
545
+
546
+ class WaitTimedOut extends TaggedError2()("WaitTimedOut", {
547
+ step: String2,
548
+ event: String2
549
+ }) {
550
+ }
551
+
552
+ class ChildFailed extends TaggedError2()("ChildFailed", {
553
+ step: String2,
554
+ loop: String2,
555
+ reason: String2
556
+ }) {
557
+ get message() {
558
+ return `Child Run of "${this.loop}" for Step "${this.step}" failed: ${this.reason}`;
559
+ }
560
+ }
561
+
562
+ class CapabilityDenied extends TaggedError2()("CapabilityDenied", {
563
+ step: String2,
564
+ capability: Literals(LoopCapabilities),
565
+ secret: optionalKey(String2)
566
+ }) {
567
+ get message() {
568
+ return this.secret === undefined ? `Step "${this.step}" needs the "${this.capability}" capability, which this Run was not granted` : `Step "${this.step}" names the Secret "${this.secret}", which its Loop does not declare`;
569
+ }
570
+ }
571
+
572
+ class DuplicateStep extends TaggedError2()("DuplicateStep", {
573
+ step: String2
574
+ }) {
575
+ }
576
+
577
+ class InputInvalid extends TaggedError2()("InputInvalid", {
578
+ loop: String2,
579
+ message: String2
580
+ }) {
581
+ }
582
+ var LoopEvent = Struct({ type: String2, payload: Record(String2, Json) });
583
+
584
+ class LoopRun extends Service()("@kitman-sh/sdk/loop/LoopRun") {
585
+ }
586
+
587
+ class LoopHost extends Service()("@kitman-sh/sdk/loop/LoopHost") {
588
+ }
589
+ var agentWorkDeferred = (step) => make2(`agent_work/${step}`, { success: Json, error: StepFailed });
590
+ var answerDeferred = (step) => make2(`ask/${step}`, { success: String2, error: StepFailed });
591
+ var childDeferred = (step) => make2(`call/${step}`, { success: Json, error: ChildFailed });
592
+ var eventDeferred = (step) => make2(`wait_for/${step}`, { success: LoopEvent, error: WaitTimedOut });
593
+ var verifyDeferred = (step) => make2(`verify/${step}`, { success: VerifyResult, error: StepFailed });
594
+ var recordStep = (name, kind, exit) => {
595
+ if (isSuccess2(exit))
596
+ return { name, kind, status: "succeeded", result: exit.value };
597
+ if (hasInterruptsOnly(exit.cause))
598
+ return;
599
+ return { name, kind, status: "failed", error: squash(exit.cause) };
600
+ };
601
+ var takeStep = (name, kind, capability, body) => gen(function* () {
602
+ const run = yield* LoopRun;
603
+ if (run.names.has(name))
604
+ return yield* new DuplicateStep({ step: name });
605
+ if (capability !== undefined && !run.granted.has(capability))
606
+ return yield* new CapabilityDenied({ step: name, capability });
607
+ run.names.add(name);
608
+ return yield* body.pipe(onExit((exit) => sync(() => {
609
+ const record = recordStep(name, kind, exit);
610
+ if (record !== undefined)
611
+ run.steps.push(record);
612
+ })));
613
+ });
614
+ var launch = (deferred, start) => make3({
615
+ name: deferred.name,
616
+ error: StepFailed,
617
+ execute: gen(function* () {
618
+ return yield* start(yield* LoopHost, yield* token(deferred));
619
+ })
620
+ });
621
+ var toJson = (step, schema, value) => encodeUnknownEffect(toCodecJson(schema))(value).pipe(flatMap(decodeUnknownEffect(Json)), mapError((error) => new StepFailed({ step, reason: error.message })));
622
+ var agentWork = (name, options) => takeStep(name, "agent_work", "agent_work", gen(function* () {
623
+ const run = yield* LoopRun;
624
+ const agent = run.agents[options.agent];
625
+ const undeclared = options.secrets?.find((secret) => !run.secrets.has(secret));
626
+ if (undeclared !== undefined)
627
+ return yield* new CapabilityDenied({ step: name, capability: "agent_work", secret: undeclared });
628
+ if (agent === undefined)
629
+ return yield* new StepFailed({ step: name, reason: `Agent role "${options.agent}" is not declared` });
630
+ const deferred = agentWorkDeferred(name);
631
+ const output = yield* jsonSchemaOf(run.loop, options.output).pipe(mapError((error) => new StepFailed({ step: name, reason: error.message })));
632
+ yield* launch(deferred, (host, token2) => host.startAgentWork({
633
+ step: name,
634
+ role: options.agent,
635
+ agent,
636
+ prompt: options.prompt,
637
+ output,
638
+ ...options.secrets === undefined ? undefined : { secrets: options.secrets },
639
+ token: token2
640
+ }));
641
+ const submitted = yield* await_(deferred);
642
+ return yield* decodeEffect(toCodecJson(options.output))(submitted).pipe(mapError((error) => new OutputInvalid({ step: name, message: error.message })));
643
+ }));
644
+ var ask = (name, question) => takeStep(name, "ask", "ask", gen(function* () {
645
+ const deferred = answerDeferred(name);
646
+ const options = "options" in question ? question.options : undefined;
647
+ yield* launch(deferred, (host, token2) => host.putQuestion({
648
+ step: name,
649
+ prompt: question.prompt,
650
+ ...options === undefined ? undefined : { options },
651
+ token: token2
652
+ }));
653
+ const answer = yield* await_(deferred);
654
+ if (options === undefined)
655
+ return answer;
656
+ const offered = options.find((option) => option === answer);
657
+ if (offered === undefined)
658
+ return yield* new QuestionRefused({ step: name, answer, options });
659
+ return offered;
660
+ }));
661
+ var call = (name, loop, input) => takeStep(name, "call", "call", gen(function* () {
662
+ const deferred = childDeferred(name);
663
+ const encoded = yield* toJson(name, loop.input, input);
664
+ yield* launch(deferred, (host, token2) => host.admitChild({ step: name, loop: loop.id, input: encoded, token: token2 }));
665
+ const result = yield* await_(deferred);
666
+ return yield* decodeEffect(toCodecJson(loop.output))(result).pipe(mapError((error) => new ChildFailed({ step: name, loop: loop.id, reason: error.message })));
667
+ }));
668
+ var waitFor = (name, wait) => takeStep(name, "wait_for", undefined, gen(function* () {
669
+ const deferred = eventDeferred(name);
670
+ const timeoutMillis = toMillis(fromInputUnsafe(wait.timeout));
671
+ yield* launch(deferred, (host, token2) => host.awaitEvent({
672
+ step: name,
673
+ event: wait.event,
674
+ ...wait.filter === undefined ? undefined : { filter: wait.filter },
675
+ timeoutMillis,
676
+ token: token2
677
+ }));
678
+ return yield* await_(deferred);
679
+ }));
680
+ var verify = (name, request) => takeStep(name, "verify", "verify", gen(function* () {
681
+ const deferred = verifyDeferred(name);
682
+ yield* launch(deferred, (host, token2) => host.startVerification({
683
+ step: name,
684
+ ...request.revision === undefined ? undefined : { revision: request.revision },
685
+ ...request.checks === undefined ? undefined : { checks: request.checks },
686
+ token: token2
687
+ }));
688
+ return yield* await_(deferred);
689
+ }));
690
+ // packages/sdk/src/loop/plan.ts
691
+ var PlanTicket = Struct({
692
+ key: String2,
693
+ title: String2,
694
+ body: String2,
695
+ acceptanceCriteria: ArraySchema(String2),
696
+ repoBindingId: optional(String2),
697
+ blockedBy: ArraySchema(String2),
698
+ outcome: optional(LoopOutcome)
699
+ });
700
+ var cycleMessage = (keys) => `These tickets block each other in a cycle, so none can start: ${keys.join(", ")}`;
701
+ var ticketsProblem = (tickets) => {
702
+ const keys = new Set;
703
+ for (const ticket of tickets) {
704
+ if (keys.has(ticket.key))
705
+ return `Two tickets have the key "${ticket.key}"`;
706
+ keys.add(ticket.key);
707
+ }
708
+ for (const ticket of tickets)
709
+ for (const blocker of ticket.blockedBy)
710
+ if (!keys.has(blocker))
711
+ return `Ticket "${ticket.key}" is blocked by "${blocker}", which is not a ticket`;
712
+ const { blocked } = wavesOf(tickets);
713
+ return blocked.length === 0 ? undefined : cycleMessage(blocked);
714
+ };
715
+ var PlanTickets = ArraySchema(PlanTicket).check(makeFilter((tickets) => ticketsProblem(tickets) ?? true));
716
+
717
+ class BlockingCycle extends TaggedError2()("BlockingCycle", {
718
+ tickets: ArraySchema(String2)
719
+ }) {
720
+ get message() {
721
+ return cycleMessage(this.tickets);
722
+ }
723
+ }
724
+ var byRepoBinding = (left, right) => (left.repoBindingId ?? "").localeCompare(right.repoBindingId ?? "");
725
+ function wavesOf(tickets) {
726
+ const placed = new Set;
727
+ const waves = [];
728
+ let left = tickets;
729
+ while (left.length > 0) {
730
+ const ready = left.filter((ticket) => ticket.blockedBy.every((blocker) => placed.has(blocker)));
731
+ if (ready.length === 0)
732
+ break;
733
+ for (const ticket of ready)
734
+ placed.add(ticket.key);
735
+ waves.push(ready.toSorted(byRepoBinding));
736
+ left = left.filter((ticket) => !placed.has(ticket.key));
737
+ }
738
+ return { waves, blocked: left.map((ticket) => ticket.key) };
739
+ }
740
+ var planWaves = (tickets) => suspend(() => {
741
+ const { waves, blocked } = wavesOf(tickets);
742
+ return blocked.length === 0 ? succeed5(waves) : fail4(new BlockingCycle({ tickets: blocked }));
743
+ });
744
+
745
+ // packages/sdk/src/loop/activities.ts
746
+ var ChangeRequestRequest = Struct({
747
+ title: String2,
748
+ body: String2,
749
+ integration: optional(String2)
750
+ });
751
+ var ChangeRequestResult = Struct({ id: String2, number: String2, url: String2 });
752
+ var WriteBackMilestones = ["started", "change_request_opened", "merged", "failed"];
753
+ var WriteBackRequest = Struct({
754
+ milestone: optional(Literals(WriteBackMilestones)),
755
+ changeRequestUrl: optional(String2)
756
+ });
757
+ var ArtifactRequest = Struct({
758
+ kind: String2,
759
+ title: String2,
760
+ text: String2,
761
+ data: optional(Json)
762
+ });
763
+ var ArtifactResult = Struct({ artifactId: String2 });
764
+ var NoteRequest = Struct({ text: String2, artifactId: optional(String2) });
765
+ var CreateTasksRequest = Struct({ tickets: ArraySchema(PlanTicket) });
766
+ var CreateTasksResult = Struct({
767
+ tasks: ArraySchema(Struct({ key: String2, taskId: String2 }))
768
+ });
769
+ var IntegrationBranchRequest = Struct({ repoBindingId: optional(String2) });
770
+ var IntegrationBranch = Struct({
771
+ repoBindingId: String2,
772
+ branch: String2,
773
+ revision: String2
774
+ });
775
+ var MergeChangeRequestRequest = Struct({ changeRequestId: String2 });
776
+ var MergeResult = Union([
777
+ Struct({ status: Literal("merged"), revision: String2 }),
778
+ Struct({ status: Literal("conflict") })
779
+ ]);
780
+ var CommentRequest = Struct({ changeRequestId: String2, body: String2 });
781
+ var ReleaseRequest = Struct({
782
+ tag: String2,
783
+ revision: optional(String2),
784
+ title: String2,
785
+ notes: String2
786
+ });
787
+ var ReleaseResult = Struct({ tag: String2, revision: String2, url: String2 });
788
+
789
+ class ChangeRequestActivity extends Service()("@kitman-sh/sdk/loop/ChangeRequestActivity") {
790
+ }
791
+
792
+ class WriteBackActivity extends Service()("@kitman-sh/sdk/loop/WriteBackActivity") {
793
+ }
794
+
795
+ class ArtifactActivity extends Service()("@kitman-sh/sdk/loop/ArtifactActivity") {
796
+ }
797
+
798
+ class NoteActivity extends Service()("@kitman-sh/sdk/loop/NoteActivity") {
799
+ }
800
+
801
+ class CommentActivity extends Service()("@kitman-sh/sdk/loop/CommentActivity") {
802
+ }
803
+
804
+ class CreateTasksActivity extends Service()("@kitman-sh/sdk/loop/CreateTasksActivity") {
805
+ }
806
+
807
+ class IntegrationBranchActivity extends Service()("@kitman-sh/sdk/loop/IntegrationBranchActivity") {
808
+ }
809
+
810
+ class MergeChangeRequestActivity extends Service()("@kitman-sh/sdk/loop/MergeChangeRequestActivity") {
811
+ }
812
+
813
+ class ReleaseActivity extends Service()("@kitman-sh/sdk/loop/ReleaseActivity") {
814
+ }
815
+ var hostActivity = (name, kind, capability, success, execute) => takeStep(name, kind, capability, make3({ name: `${kind}/${name}`, success, error: StepFailed, execute }));
816
+ var changeRequest = (name, request) => hostActivity(name, "change_request", "change_request", ChangeRequestResult, gen(function* () {
817
+ return yield* (yield* ChangeRequestActivity).run(request, name);
818
+ }));
819
+ var writeBack = (name, request) => hostActivity(name, "write_back", "write_back", Void, gen(function* () {
820
+ return yield* (yield* WriteBackActivity).run(request, name);
821
+ }));
822
+ var artifact = (name, request) => hostActivity(name, "artifact", undefined, ArtifactResult, gen(function* () {
823
+ return yield* (yield* ArtifactActivity).run(request, name);
824
+ }));
825
+ var note = (name, request) => hostActivity(name, "note", undefined, Void, gen(function* () {
826
+ return yield* (yield* NoteActivity).run(request, name);
827
+ }));
828
+ var comment = (name, request) => hostActivity(name, "comment", "post_comment", Void, gen(function* () {
829
+ return yield* (yield* CommentActivity).run(request, name);
830
+ }));
831
+ var createTasks = (name, request) => hostActivity(name, "create_tasks", "create_tasks", CreateTasksResult, gen(function* () {
832
+ return yield* (yield* CreateTasksActivity).run(request, name);
833
+ }));
834
+ var integrationBranch = (name, request) => hostActivity(name, "integration_branch", "merge_change_request", IntegrationBranch, gen(function* () {
835
+ return yield* (yield* IntegrationBranchActivity).run(request, name);
836
+ }));
837
+ var mergeChangeRequest = (name, request) => hostActivity(name, "merge_change_request", "merge_change_request", MergeResult, gen(function* () {
838
+ return yield* (yield* MergeChangeRequestActivity).run(request, name);
839
+ }));
840
+ var release2 = (name, request) => hostActivity(name, "release", "publish_release", ReleaseResult, gen(function* () {
841
+ return yield* (yield* ReleaseActivity).run(request, name);
842
+ }));
843
+ // packages/sdk/src/loop/test-loop.ts
844
+ var LoopJournal = Struct({
845
+ activities: Record(String2, Json),
846
+ deferreds: Record(String2, Json)
847
+ });
848
+
849
+ class ScriptedFailure extends TaggedClass("ScriptedFailure") {
850
+ }
851
+ // packages/sdk/src/loop/review.ts
852
+ var Severity = Literals(["blocker", "major", "minor"]);
853
+ var Finding = Struct({
854
+ severity: Severity,
855
+ file: String2,
856
+ line: Int,
857
+ detail: String2
858
+ });
859
+ var MergedFinding = Struct({
860
+ id: String2,
861
+ ...Finding.fields,
862
+ seats: ArraySchema(String2)
863
+ });
864
+ var Rebuttal = Struct({ findingId: String2, reason: String2 });
865
+ var RebuttalVerdict = Struct({ findingId: String2, accepted: Boolean2 });
866
+ var ScopeJudgement = Struct({ met: Boolean2, unmet: ArraySchema(String2) });
867
+ var rank = { blocker: 0, major: 1, minor: 2 };
868
+ var isBlocking = (finding) => finding.severity !== "minor";
869
+ var mergeFindings = (bySeat) => {
870
+ const merged = new Map;
871
+ for (const [seat, findings] of Object.entries(bySeat))
872
+ for (const finding of findings) {
873
+ const id = `${finding.file}:${finding.line}`;
874
+ const known = merged.get(id);
875
+ const seats = [...new Set([...known?.seats ?? [], seat])];
876
+ const worst = known === undefined || rank[finding.severity] < rank[known.severity] ? finding : known;
877
+ merged.set(id, { id, severity: worst.severity, file: worst.file, line: worst.line, detail: worst.detail, seats });
878
+ }
879
+ return [...merged.values()].toSorted((left, right) => rank[left.severity] - rank[right.severity]);
880
+ };
881
+ var splitDisputed = (findings, rebuttals) => {
882
+ const disputed = [];
883
+ const undisputed = [];
884
+ for (const finding of findings) {
885
+ const rebuttal = rebuttals.find((entry) => entry.findingId === finding.id);
886
+ if (rebuttal === undefined)
887
+ undisputed.push(finding);
888
+ else
889
+ disputed.push({ finding, rebuttal });
890
+ }
891
+ return { disputed, undisputed };
892
+ };
893
+ var settleRound = (round) => {
894
+ const { disputed, undisputed } = splitDisputed(round.findings, round.rebuttals);
895
+ const accepted = (entry) => {
896
+ const verdicts = round.verdicts.filter((verdict) => verdict.findingId === entry.finding.id);
897
+ return verdicts.length > 0 && verdicts.every((verdict) => verdict.accepted);
898
+ };
899
+ return {
900
+ open: undisputed,
901
+ withdrawn: disputed.filter(accepted),
902
+ standing: disputed.filter((entry) => !accepted(entry))
903
+ };
904
+ };
905
+ var rallyStop = (input) => {
906
+ const open = input.settled.open.filter(isBlocking).map((finding) => finding.id);
907
+ const standing = input.settled.standing.flatMap((entry) => isBlocking(entry.finding) ? [entry.finding.id] : []);
908
+ const unsettled = [...open, ...standing];
909
+ const stop = (reason) => ({ stop: true, reason, unsettled });
910
+ if (unsettled.length === 0)
911
+ return stop("settled");
912
+ if (standing.length > 0)
913
+ return stop("dispute_stands");
914
+ if (input.previous !== undefined && sameIds(input.previous, unsettled))
915
+ return stop("findings_returned");
916
+ return input.round >= input.maxRounds ? stop("round_cap") : { stop: false };
917
+ };
918
+ var sameIds = (left, right) => left.length === right.length && left.every((id) => right.includes(id));
919
+ // packages/sdk/src/loop/check.ts
920
+ class CheckFailed extends TaggedError2()("CheckFailed", {
921
+ loop: String2,
922
+ check: String2,
923
+ reason: String2
924
+ }) {
925
+ get detail() {
926
+ return `Check "${this.check}": ${this.reason}`;
927
+ }
928
+ get message() {
929
+ return `Loop "${this.loop}": ${this.detail}`;
930
+ }
931
+ }
932
+
933
+ class StepNamedTwice extends TaggedError2()("StepNamedTwice", {
934
+ loop: String2,
935
+ check: String2,
936
+ step: String2
937
+ }) {
938
+ get detail() {
939
+ return `Check "${this.check}": Step "${this.step}" is named twice`;
940
+ }
941
+ get message() {
942
+ return `Loop "${this.loop}": ${this.detail}`;
943
+ }
944
+ }
945
+
946
+ class ReplayDiverged extends TaggedError2()("ReplayDiverged", {
947
+ loop: String2,
948
+ check: String2,
949
+ first: ArraySchema(String2),
950
+ replay: ArraySchema(String2),
951
+ rerun: ArraySchema(String2)
952
+ }) {
953
+ get detail() {
954
+ const steps = (names) => names.join(", ") || "no Steps";
955
+ return `Check "${this.check}": its replay from the journal diverged; it took ${steps(this.first)} and then ${steps(this.replay)}${this.rerun.length > 0 ? `, running ${this.rerun.join(", ")} again` : ""}. Keep effects and randomness inside Activities.`;
956
+ }
957
+ get message() {
958
+ return `Loop "${this.loop}": ${this.detail}`;
959
+ }
960
+ }
961
+ var LoopCheckError = Union([CheckFailed, StepNamedTwice, ReplayDiverged]);
962
+ export { LoopOutcome, defineLoop, ManifestInvalid, make, Suspended, make2, await_, make3, evidenceGate, ChildFailed, agentWork, ask, call, waitFor, verify, PlanTickets, planWaves, ChangeRequestResult, CreateTasksResult, changeRequest, writeBack, artifact, note, comment, createTasks, integrationBranch, mergeChangeRequest, release2, Finding, MergedFinding, Rebuttal, RebuttalVerdict, ScopeJudgement, isBlocking, mergeFindings, settleRound, rallyStop, LoopCheckError };