@purista/harness 1.0.0 → 1.2.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 (50) hide show
  1. package/README.md +15 -0
  2. package/dist/agents/index.d.ts +5 -3
  3. package/dist/agents/index.js +84 -8
  4. package/dist/errors/catalog.d.ts +45 -5
  5. package/dist/errors/catalog.js +19 -0
  6. package/dist/errors/harness-error.d.ts +2 -0
  7. package/dist/eval/index.d.ts +57 -0
  8. package/dist/eval/index.js +181 -0
  9. package/dist/harness/defineHarness.d.ts +96 -20
  10. package/dist/harness/defineHarness.js +59 -2
  11. package/dist/index.d.ts +4 -0
  12. package/dist/index.js +4 -0
  13. package/dist/memory/sandbox/index.d.ts +17 -0
  14. package/dist/memory/sandbox/index.js +122 -0
  15. package/dist/models/registry.js +32 -7
  16. package/dist/ports/capabilities.d.ts +46 -2
  17. package/dist/ports/harness-context.d.ts +4 -1
  18. package/dist/ports/index.d.ts +2 -0
  19. package/dist/ports/index.js +2 -0
  20. package/dist/ports/memory/facade.d.ts +5 -0
  21. package/dist/ports/memory/facade.js +123 -0
  22. package/dist/ports/memory/telemetry.d.ts +16 -0
  23. package/dist/ports/memory/telemetry.js +77 -0
  24. package/dist/ports/memory/types.d.ts +204 -0
  25. package/dist/ports/memory/types.js +1 -0
  26. package/dist/ports/memory/validation.d.ts +19 -0
  27. package/dist/ports/memory/validation.js +160 -0
  28. package/dist/ports/memory.d.ts +3 -0
  29. package/dist/ports/memory.js +3 -0
  30. package/dist/ports/workspace.d.ts +177 -0
  31. package/dist/ports/workspace.js +32 -0
  32. package/dist/runtime/durable.d.ts +3 -0
  33. package/dist/runtime/durable.js +2 -1
  34. package/dist/sessions/index.d.ts +2 -0
  35. package/dist/sessions/index.js +275 -68
  36. package/dist/skills/index.d.ts +2 -1
  37. package/dist/skills/index.js +263 -35
  38. package/dist/telemetry/shim.d.ts +20 -0
  39. package/dist/telemetry/shim.js +28 -0
  40. package/dist/testing/durableWorkspaceStoreContract.d.ts +3 -0
  41. package/dist/testing/durableWorkspaceStoreContract.js +41 -0
  42. package/dist/testing/fakeMemoryAdapter.d.ts +16 -0
  43. package/dist/testing/fakeMemoryAdapter.js +110 -0
  44. package/dist/testing/index.d.ts +5 -0
  45. package/dist/testing/index.js +4 -0
  46. package/dist/workspace/in-memory.d.ts +35 -0
  47. package/dist/workspace/in-memory.js +142 -0
  48. package/dist/workspace/index.d.ts +1 -0
  49. package/dist/workspace/index.js +1 -0
  50. package/package.json +12 -6
@@ -0,0 +1,177 @@
1
+ import type { JsonValue } from '../models/json.js';
2
+ import type { AdapterCapabilities, AdapterCapability } from './capabilities.js';
3
+ import type { HarnessAdapterContext } from './harness-context.js';
4
+ export type WorkspaceLifecycleState = 'active' | 'paused' | 'aborted' | 'cleanup_pending' | 'cleaned';
5
+ export interface WorkspaceRetentionPolicy {
6
+ activeTtlMs?: number;
7
+ pausedTtlMs?: number;
8
+ terminalSuccessTtlMs?: number;
9
+ terminalFailureTtlMs?: number;
10
+ abortedTtlMs?: number;
11
+ orphanTtlMs?: number;
12
+ maxTtlMs?: number;
13
+ cleanupMode: 'adapter_automatic' | 'application_scheduled' | 'manual_only';
14
+ }
15
+ export interface WorkspaceEncryptionInfo {
16
+ encryptedAtRest: boolean;
17
+ keyScope: 'adapter' | 'tenant' | 'project' | 'application';
18
+ rotationSupported: boolean;
19
+ metadataEncrypted: boolean;
20
+ }
21
+ export interface WorkspaceQuotaPolicy {
22
+ maxWorkspaceBytes?: number;
23
+ maxWorkspaceFiles?: number;
24
+ maxSingleFileBytes?: number;
25
+ maxCheckpointPayloadBytes?: number;
26
+ maxSnapshotBytes?: number;
27
+ maxActiveWorkspaces?: number;
28
+ maxPausedWorkspaces?: number;
29
+ maxConcurrentResumes?: number;
30
+ maxWorkspaceAgeMs?: number;
31
+ }
32
+ export interface DurableWorkspacePolicy {
33
+ retention?: WorkspaceRetentionPolicy;
34
+ encryption?: WorkspaceEncryptionInfo;
35
+ quota?: WorkspaceQuotaPolicy;
36
+ }
37
+ export interface DurableWorkspaceStoreInfo {
38
+ id: string;
39
+ packageName: string;
40
+ capabilities: readonly AdapterCapability[];
41
+ policy: DurableWorkspacePolicy;
42
+ }
43
+ export interface WorkspaceStartOptions {
44
+ runId: string;
45
+ sessionId: string;
46
+ workflowId?: string;
47
+ agentId?: string;
48
+ workerId?: string;
49
+ attempt: number;
50
+ idempotencyKey: string;
51
+ metadata?: Record<string, JsonValue>;
52
+ policy?: Partial<DurableWorkspacePolicy>;
53
+ signal?: AbortSignal;
54
+ }
55
+ export interface WorkspaceHandle {
56
+ workspaceRef: string;
57
+ runId: string;
58
+ sessionId: string;
59
+ state: 'active';
60
+ startedAt: string;
61
+ attempt: number;
62
+ metadata?: Record<string, JsonValue>;
63
+ }
64
+ export interface WorkspacePauseOptions {
65
+ handle: WorkspaceHandle;
66
+ stepId: string;
67
+ sequence: number;
68
+ attempt: number;
69
+ checkpointPayload?: JsonValue;
70
+ reason: 'step_completed' | 'manual_pause' | 'timeout' | 'shutdown' | 'retry_boundary';
71
+ idempotencyKey: string;
72
+ signal?: AbortSignal;
73
+ }
74
+ export interface WorkspaceCheckpoint {
75
+ workspaceRef: string;
76
+ checkpointRef: string;
77
+ snapshotRef?: string;
78
+ runId: string;
79
+ sessionId: string;
80
+ stepId: string;
81
+ sequence: number;
82
+ attempt: number;
83
+ committedAt: string;
84
+ expiresAt?: string;
85
+ sizeBytes?: number;
86
+ metadata?: Record<string, JsonValue>;
87
+ }
88
+ export interface WorkspaceResumeOptions {
89
+ workspaceRef: string;
90
+ checkpointRef?: string;
91
+ snapshotRef?: string;
92
+ runId: string;
93
+ sessionId: string;
94
+ attempt: number;
95
+ idempotencyKey: string;
96
+ signal?: AbortSignal;
97
+ }
98
+ export interface WorkspaceAbortOptions {
99
+ workspaceRef: string;
100
+ runId: string;
101
+ sessionId: string;
102
+ reason: 'cancelled' | 'failed' | 'superseded' | 'manual_abort';
103
+ idempotencyKey: string;
104
+ signal?: AbortSignal;
105
+ }
106
+ export interface WorkspaceAbortResult {
107
+ workspaceRef: string;
108
+ state: 'aborted';
109
+ abortedAt: string;
110
+ cleanupEligibleAt?: string;
111
+ }
112
+ export interface WorkspaceCleanupOptions {
113
+ workspaceRef: string;
114
+ reason: 'terminal_success' | 'terminal_failure' | 'aborted' | 'expired' | 'orphan' | 'manual';
115
+ idempotencyKey: string;
116
+ signal?: AbortSignal;
117
+ }
118
+ export interface WorkspaceCleanupResult {
119
+ workspaceRef: string;
120
+ state: 'cleaned' | 'cleanup_pending';
121
+ deletedBytes?: number;
122
+ deletedFiles?: number;
123
+ completedAt?: string;
124
+ retryAfterMs?: number;
125
+ partial?: boolean;
126
+ remainingRefs?: readonly string[];
127
+ }
128
+ export interface WorkspaceInspectionOptions {
129
+ workspaceRef?: string;
130
+ checkpointRef?: string;
131
+ snapshotRef?: string;
132
+ signal?: AbortSignal;
133
+ }
134
+ export interface WorkspaceInspection {
135
+ workspaceRef: string;
136
+ state: WorkspaceLifecycleState;
137
+ checkpoints: readonly WorkspaceCheckpoint[];
138
+ currentCheckpointRef?: string;
139
+ retention?: WorkspaceRetentionPolicy;
140
+ quota?: WorkspaceQuotaPolicy;
141
+ encryption?: WorkspaceEncryptionInfo;
142
+ createdAt: string;
143
+ updatedAt: string;
144
+ expiresAt?: string;
145
+ cleanupEligibleAt?: string;
146
+ metadata?: Record<string, JsonValue>;
147
+ }
148
+ export interface DurableReplayCheckpoint {
149
+ runId: string;
150
+ sessionId: string;
151
+ workerId?: string;
152
+ leaseId?: string;
153
+ stepId: string;
154
+ sequence: number;
155
+ attempt: number;
156
+ checkpointRef: string;
157
+ workspaceRef?: string;
158
+ snapshotRef?: string;
159
+ runtimeCheckpointRef?: string;
160
+ schemaVersion: 1;
161
+ payload?: JsonValue;
162
+ payloadSizeBytes?: number;
163
+ committedAt: string;
164
+ expiresAt?: string;
165
+ metadata?: Record<string, JsonValue>;
166
+ }
167
+ export interface DurableWorkspaceStore extends AdapterCapabilities {
168
+ readonly info: DurableWorkspaceStoreInfo;
169
+ configureHarnessContext?(context: HarnessAdapterContext): void;
170
+ startWorkspace(opts: WorkspaceStartOptions): Promise<WorkspaceHandle>;
171
+ pauseWorkspace(opts: WorkspacePauseOptions): Promise<WorkspaceCheckpoint>;
172
+ resumeWorkspace(opts: WorkspaceResumeOptions): Promise<WorkspaceHandle>;
173
+ abortWorkspace(opts: WorkspaceAbortOptions): Promise<WorkspaceAbortResult>;
174
+ cleanupWorkspace(opts: WorkspaceCleanupOptions): Promise<WorkspaceCleanupResult>;
175
+ inspectWorkspace?(opts: WorkspaceInspectionOptions): Promise<WorkspaceInspection>;
176
+ }
177
+ export declare function validateDurableWorkspaceStore(adapter: DurableWorkspaceStore): void;
@@ -0,0 +1,32 @@
1
+ import { HarnessConfigError } from '../errors/catalog.js';
2
+ const adapterIdPattern = /^[a-z][a-z0-9_.-]{1,63}$/;
3
+ export function validateDurableWorkspaceStore(adapter) {
4
+ if (!adapterIdPattern.test(adapter.info.id)) {
5
+ throw new HarnessConfigError('Workspace store id is invalid.', {
6
+ reason: 'invalid_workspace_store',
7
+ path: 'workspaceStore.info.id',
8
+ id: adapter.info.id
9
+ });
10
+ }
11
+ if (!adapter.info.packageName.trim()) {
12
+ throw new HarnessConfigError('Workspace store packageName is required.', {
13
+ reason: 'invalid_workspace_store',
14
+ path: 'workspaceStore.info.packageName',
15
+ id: adapter.info.id
16
+ });
17
+ }
18
+ if (!adapter.info.capabilities.includes('workspace_store.durable')) {
19
+ throw new HarnessConfigError('Workspace store must support workspace_store.durable.', {
20
+ reason: 'invalid_workspace_store',
21
+ path: 'workspaceStore.info.capabilities',
22
+ id: adapter.info.id
23
+ });
24
+ }
25
+ if (adapter.capabilities.length !== adapter.info.capabilities.length || adapter.capabilities.some((capability) => !adapter.info.capabilities.includes(capability))) {
26
+ throw new HarnessConfigError('Workspace store capabilities must match info.capabilities.', {
27
+ reason: 'invalid_workspace_store',
28
+ path: 'workspaceStore.capabilities',
29
+ id: adapter.info.id
30
+ });
31
+ }
32
+ }
@@ -1,6 +1,7 @@
1
1
  import type { AdapterCapability } from '../ports/capabilities.js';
2
2
  import type { JsonValue } from '../models/json.js';
3
3
  import type { RunStatus, SerializedError } from '../models/state.js';
4
+ import type { DurableReplayCheckpoint } from '../ports/workspace.js';
4
5
  /** Non-terminal run status used while durable work can still be resumed. */
5
6
  export type DurableActiveRunStatus = 'running';
6
7
  /** Terminal run statuses that must never be resumed by a durable runtime. */
@@ -67,6 +68,8 @@ export interface RunCheckpoint {
67
68
  readonly sequence: number;
68
69
  /** JSON-serializable checkpoint payload. */
69
70
  readonly output?: JsonValue;
71
+ /** Optional durable workspace replay checkpoint linked to this runtime checkpoint. */
72
+ readonly replay?: DurableReplayCheckpoint;
70
73
  /** Adapter-neutral checkpoint metadata. */
71
74
  readonly metadata?: Record<string, JsonValue>;
72
75
  /** ISO timestamp for the commit. */
@@ -39,7 +39,8 @@ class InMemoryDurableRuntime {
39
39
  'runtime.checkpoint',
40
40
  'runtime.retry',
41
41
  'runtime.distributed_lock',
42
- 'runtime.resume_from_checkpoint'
42
+ 'runtime.resume_from_checkpoint',
43
+ 'runtime.workspace_checkpoint'
43
44
  ];
44
45
  runs = new Map();
45
46
  runLeases = new Map();
@@ -1,5 +1,6 @@
1
1
  import type { Logger } from '../logger/index.js';
2
2
  import type { Harness, HarnessDefaults, BuilderState, TelemetryOptions } from '../harness/defineHarness.js';
3
+ import type { MemoryAdapter } from '../ports/memory.js';
3
4
  import type { HarnessInspection } from '../ports/capabilities.js';
4
5
  import type { Sandbox } from '../sandbox/index.js';
5
6
  import type { StateStore } from '../ports/state.js';
@@ -11,6 +12,7 @@ type HarnessDefinition<S extends BuilderState> = {
11
12
  telemetryShim?: TelemetryShim;
12
13
  state: StateStore;
13
14
  sandbox: Sandbox;
15
+ memory: MemoryAdapter;
14
16
  defaults: HarnessDefaults;
15
17
  models: NonNullable<S['models']>;
16
18
  tools: NonNullable<S['tools']>;