@adula/kit 0.2.0-alpha.4 → 1.0.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.
Files changed (63) hide show
  1. package/README.md +12 -2
  2. package/build/agent/capabilities.md +49 -7
  3. package/build/agent/skills/idea-review/SKILL.md +26 -1
  4. package/build/agent/skills/module-review/SKILL.md +22 -0
  5. package/build/agent/skills/perf-review/SKILL.md +24 -0
  6. package/build/agent/skills/schema-review/SKILL.md +22 -0
  7. package/build/agent/skills/security-review/SKILL.md +24 -0
  8. package/build/agent/skills/ui-review/SKILL.md +22 -0
  9. package/build/commands/capabilities.d.ts +4 -0
  10. package/build/commands/capabilities.js +35 -4
  11. package/build/commands/main.d.ts +4 -2
  12. package/build/database/migrations/1770000000004_kit_collaboration.d.ts +5 -0
  13. package/build/database/migrations/1770000000004_kit_collaboration.js +10 -0
  14. package/build/database/migrations/1770000000005_kit_assignments.d.ts +5 -0
  15. package/build/database/migrations/1770000000005_kit_assignments.js +10 -0
  16. package/build/database/migrations/1770000000006_kit_messaging.d.ts +5 -0
  17. package/build/database/migrations/1770000000006_kit_messaging.js +10 -0
  18. package/build/database/migrations/1770000000007_kit_webhooks.d.ts +5 -0
  19. package/build/database/migrations/1770000000007_kit_webhooks.js +10 -0
  20. package/build/database/migrations/1770000000008_kit_imports.d.ts +5 -0
  21. package/build/database/migrations/1770000000008_kit_imports.js +10 -0
  22. package/build/database/migrations/1770000000010_kit_workflows.d.ts +5 -0
  23. package/build/database/migrations/1770000000010_kit_workflows.js +10 -0
  24. package/build/index.d.ts +21 -1
  25. package/build/index.js +12 -1
  26. package/build/src/admin/contracts.js +19 -10
  27. package/build/src/admin/controller.d.ts +1 -0
  28. package/build/src/admin/controller.js +6 -0
  29. package/build/src/admin/resource_service.d.ts +21 -0
  30. package/build/src/admin/resource_service.js +163 -7
  31. package/build/src/collaboration/assignments.d.ts +78 -0
  32. package/build/src/collaboration/assignments.js +219 -0
  33. package/build/src/collaboration/record_collaboration.d.ts +86 -0
  34. package/build/src/collaboration/record_collaboration.js +360 -0
  35. package/build/src/commands/agent_assets.js +5 -0
  36. package/build/src/commands/capabilities.d.ts +19 -0
  37. package/build/src/commands/capabilities.js +160 -0
  38. package/build/src/core/message_templates.d.ts +80 -0
  39. package/build/src/core/message_templates.js +288 -0
  40. package/build/src/database/schema.d.ts +18 -0
  41. package/build/src/database/schema.js +192 -0
  42. package/build/src/events/outbox.d.ts +1 -0
  43. package/build/src/events/outbox.js +1 -1
  44. package/build/src/events/record_mutation.d.ts +7 -0
  45. package/build/src/events/record_mutation.js +13 -2
  46. package/build/src/integrations/imports.d.ts +74 -0
  47. package/build/src/integrations/imports.js +331 -0
  48. package/build/src/integrations/openapi.d.ts +39 -0
  49. package/build/src/integrations/openapi.js +256 -0
  50. package/build/src/integrations/print.d.ts +37 -0
  51. package/build/src/integrations/print.js +123 -0
  52. package/build/src/integrations/webhooks.d.ts +98 -0
  53. package/build/src/integrations/webhooks.js +298 -0
  54. package/build/src/resource/define_resource.d.ts +1 -0
  55. package/build/src/resource/define_resource.js +9 -1
  56. package/build/src/resource/registry.d.ts +2 -0
  57. package/build/src/resource/registry.js +9 -0
  58. package/build/src/resource/types.d.ts +3 -0
  59. package/build/src/workflows/define_workflow.d.ts +95 -0
  60. package/build/src/workflows/define_workflow.js +83 -0
  61. package/build/src/workflows/engine.d.ts +119 -0
  62. package/build/src/workflows/engine.js +687 -0
  63. package/package.json +7 -3
@@ -0,0 +1,83 @@
1
+ import { createMachine, getNextSnapshot } from 'xstate';
2
+ const IDENTIFIER = /^[a-z][a-z0-9_]*$/;
3
+ function targets(step) {
4
+ switch (step.type) {
5
+ case 'condition':
6
+ return [step.then, step.else];
7
+ case 'approval':
8
+ return [step.approve, step.reject];
9
+ case 'end':
10
+ return [];
11
+ default:
12
+ return [step.next];
13
+ }
14
+ }
15
+ /**
16
+ * Declares a versioned workflow. Steps compile to an XState machine; the engine
17
+ * performs each step's effect and asks the machine for the next step. Runs keep
18
+ * the version they started with, so publishing a new version never changes a
19
+ * running workflow (migrate explicitly with WorkflowEngine.migrateRuns).
20
+ */
21
+ export function defineWorkflow(input) {
22
+ if (!IDENTIFIER.test(input.name))
23
+ throw new Error(`Invalid workflow name: ${input.name}`);
24
+ if (!Number.isInteger(input.version) || input.version < 1)
25
+ throw new Error('Workflow version must be a positive integer');
26
+ if (!(input.start in input.steps))
27
+ throw new Error(`Unknown start step: ${input.start}`);
28
+ const keys = Object.keys(input.steps);
29
+ for (const key of keys) {
30
+ if (!IDENTIFIER.test(key))
31
+ throw new Error(`Invalid step name: ${key}`);
32
+ for (const target of targets(input.steps[key]))
33
+ if (!(target in input.steps))
34
+ throw new Error(`Step ${key} points to unknown step ${target}`);
35
+ const step = input.steps[key];
36
+ if (step.type === 'delay' && (!Number.isInteger(step.ms) || step.ms < 0))
37
+ throw new Error(`Step ${key} needs a non-negative integer delay`);
38
+ }
39
+ if (!keys.some((key) => input.steps[key].type === 'end'))
40
+ throw new Error('A workflow needs at least one end step');
41
+ const machine = createMachine({
42
+ id: `${input.name}@${input.version}`,
43
+ initial: input.start,
44
+ states: Object.fromEntries(keys.map((key) => {
45
+ const step = input.steps[key];
46
+ switch (step.type) {
47
+ case 'condition':
48
+ return [
49
+ key,
50
+ {
51
+ on: {
52
+ EVALUATE: [
53
+ {
54
+ target: step.then,
55
+ guard: ({ event }) => event.type === 'EVALUATE' && step.when(event.record),
56
+ },
57
+ { target: step.else },
58
+ ],
59
+ },
60
+ },
61
+ ];
62
+ case 'approval':
63
+ return [key, { on: { APPROVE: step.approve, REJECT: step.reject } }];
64
+ case 'end':
65
+ return [key, { type: 'final' }];
66
+ default:
67
+ return [key, { on: { DONE: step.next } }];
68
+ }
69
+ })),
70
+ });
71
+ return { ...input, machine };
72
+ }
73
+ /** The step reached from `current` by `event`, or an error if the step does not accept it. */
74
+ export function nextStep(definition, current, event) {
75
+ if (!(current in definition.steps))
76
+ throw new Error(`Step ${current} is not part of ${definition.name}@${definition.version}`);
77
+ const snapshot = definition.machine.resolveState({ value: current, context: {} });
78
+ const next = getNextSnapshot(definition.machine, snapshot, event);
79
+ const value = String(next.value);
80
+ if (value === current)
81
+ throw new Error(`Step ${current} does not accept ${event.type}`);
82
+ return value;
83
+ }
@@ -0,0 +1,119 @@
1
+ import type { Knex } from 'knex';
2
+ import type { Actor } from '../auth/ability.js';
3
+ import type { ResourceService } from '../admin/resource_service.js';
4
+ import type { ResourceRegistry } from '../resource/registry.js';
5
+ import type { JsonValue } from '../resource/types.js';
6
+ import type { DomainEvent, Listener } from '../events/outbox.js';
7
+ import type { HttpPoster } from '../integrations/webhooks.js';
8
+ import type { Assignments } from '../collaboration/assignments.js';
9
+ import { type WorkflowDefinition } from './define_workflow.js';
10
+ export type WorkflowRunStatus = 'pending_definition' | 'running' | 'waiting' | 'completed' | 'failed' | 'cancelled';
11
+ export type WorkflowRun = {
12
+ id: string;
13
+ resource: string;
14
+ resourceLabel: string;
15
+ recordId: number;
16
+ definition: string;
17
+ label: string;
18
+ version: number;
19
+ status: WorkflowRunStatus;
20
+ step: string | null;
21
+ stepLabel: string | null;
22
+ outcome: string | null;
23
+ attempts: number;
24
+ lastError: string | null;
25
+ wakeAt: string | null;
26
+ createdAt: string;
27
+ completedAt: string | null;
28
+ history: {
29
+ step: string | null;
30
+ event: string;
31
+ actorName: string | null;
32
+ detail: {
33
+ [key: string]: JsonValue;
34
+ };
35
+ at: string;
36
+ }[];
37
+ /** Open approval assigned to the viewer for this run, if any. */
38
+ myApproval: {
39
+ assignmentId: number;
40
+ title: string;
41
+ } | null;
42
+ };
43
+ export type WorkflowOptions = {
44
+ post?: HttpPoster;
45
+ /** Test hook simulating a crash after a step's effect and before its commit. */
46
+ beforeCommit?: (run: {
47
+ id: string;
48
+ step: string;
49
+ }) => void | Promise<void>;
50
+ now?: () => Date;
51
+ };
52
+ /**
53
+ * Durable workflow engine over workflow_runs. Every step runs inside the run's
54
+ * row lock (FOR UPDATE); its database effects and the transition commit together,
55
+ * so a crash repeats at most the step's external calls (HTTP carries a stable
56
+ * Idempotency-Key). Failed steps retry with growing delays up to maxAttempts.
57
+ */
58
+ export declare class WorkflowEngine {
59
+ #private;
60
+ private db;
61
+ private registry;
62
+ private resources;
63
+ private actors;
64
+ private assignments;
65
+ private options;
66
+ constructor(db: Knex, registry: ResourceRegistry, resources: ResourceService, actors: {
67
+ load(id: number): Promise<Actor>;
68
+ }, assignments: Assignments, definitions: readonly WorkflowDefinition[], options?: WorkflowOptions);
69
+ register(definition: WorkflowDefinition): this;
70
+ /** The newest version of each workflow attached to a resource. */
71
+ private latestFor;
72
+ private definition;
73
+ /** Listeners: submitted documents start their workflow; cancelled ones stop it. */
74
+ listeners(): Listener[];
75
+ /** Claims the submission envelope written in the submit transaction. */
76
+ start(trx: Knex.Transaction, event: DomainEvent): Promise<void>;
77
+ /** Worker step: advances due runs, each under its own row lock. */
78
+ tick(limit?: number): Promise<number>;
79
+ /**
80
+ * Runs steps until the run waits, ends, fails or is scheduled later. The caller
81
+ * holds the row lock. Each step executes in a savepoint so a failing step leaves
82
+ * no partial effects behind.
83
+ */
84
+ private advance;
85
+ private execute;
86
+ /**
87
+ * Records an approver's decision. Only an open approval assigned to the actor
88
+ * counts, and the run is locked so concurrent decisions cannot both apply.
89
+ */
90
+ decide(runId: string, actor: Actor, decision: 'approve' | 'reject', comment?: unknown): Promise<WorkflowRun>;
91
+ /** Administrators put a failed run back to its failed step. */
92
+ retry(runId: string, actorId: number): Promise<void>;
93
+ /**
94
+ * Explicit version migration for runs that have not finished: `mapStep` returns
95
+ * the equivalent step name in the new version.
96
+ */
97
+ migrateRuns(name: string, from: number, to: number, mapStep: (step: string) => string): Promise<number>;
98
+ runsFor(name: string, id: number, actor: Actor): Promise<WorkflowRun[]>;
99
+ run(runId: string, actor: Actor): Promise<WorkflowRun>;
100
+ /** Runs waiting for the actor's decision, newest first, on records they can still read. */
101
+ inbox(actor: Actor): Promise<WorkflowRun[]>;
102
+ failed(limit?: number): Promise<WorkflowRun[]>;
103
+ private present;
104
+ private fail;
105
+ private cancelRuns;
106
+ private context;
107
+ /** Workflow writes are system writes attributed to the submitter, with history. */
108
+ private updateRecord;
109
+ private cancelDocument;
110
+ private recipients;
111
+ private canView;
112
+ private log;
113
+ private now;
114
+ }
115
+ /**
116
+ * Listeners resolved per call, so hosts can build the engine lazily (for example
117
+ * per request) while the listener list is fixed at boot from the registry.
118
+ */
119
+ export declare function workflowListeners(registry: ResourceRegistry, engine: () => WorkflowEngine): Listener[];