@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,142 @@
1
+ /** In-process durable workspace store for local development, examples, and tests. */
2
+ export class InMemoryDurableWorkspaceStore {
3
+ info = {
4
+ id: 'in_memory_workspace_store',
5
+ packageName: '@purista/harness',
6
+ capabilities: [
7
+ 'workspace_store.durable',
8
+ 'workspace_store.checkpoint',
9
+ 'workspace_store.resume',
10
+ 'workspace_store.abort',
11
+ 'workspace_store.cleanup',
12
+ 'workspace_store.inspect',
13
+ 'workspace_store.retention',
14
+ 'workspace_store.quota'
15
+ ],
16
+ policy: {
17
+ retention: {
18
+ pausedTtlMs: 86_400_000,
19
+ terminalFailureTtlMs: 86_400_000,
20
+ terminalSuccessTtlMs: 0,
21
+ cleanupMode: 'manual_only'
22
+ },
23
+ quota: { maxActiveWorkspaces: 100, maxWorkspaceBytes: 10_000_000 }
24
+ }
25
+ };
26
+ capabilities = this.info.capabilities;
27
+ workspaces = new Map();
28
+ nextId = 1;
29
+ configureHarnessContext() { }
30
+ async startWorkspace(opts) {
31
+ opts.signal?.throwIfAborted();
32
+ const workspaceRef = `workspace_${this.nextId++}`;
33
+ const now = new Date().toISOString();
34
+ const metadata = { ...(opts.metadata ?? {}) };
35
+ const workspace = {
36
+ workspaceRef,
37
+ state: 'active',
38
+ runId: opts.runId,
39
+ sessionId: opts.sessionId,
40
+ attempt: opts.attempt,
41
+ createdAt: now,
42
+ updatedAt: now,
43
+ metadata,
44
+ checkpoints: []
45
+ };
46
+ this.workspaces.set(workspaceRef, workspace);
47
+ return { workspaceRef, runId: opts.runId, sessionId: opts.sessionId, state: 'active', startedAt: now, attempt: opts.attempt, metadata };
48
+ }
49
+ async pauseWorkspace(opts) {
50
+ opts.signal?.throwIfAborted();
51
+ const workspace = this.requireWorkspace(opts.handle.workspaceRef);
52
+ workspace.state = 'paused';
53
+ workspace.updatedAt = new Date().toISOString();
54
+ const checkpoint = {
55
+ workspaceRef: workspace.workspaceRef,
56
+ checkpointRef: `${workspace.workspaceRef}:checkpoint:${opts.sequence}`,
57
+ runId: workspace.runId,
58
+ sessionId: workspace.sessionId,
59
+ stepId: opts.stepId,
60
+ sequence: opts.sequence,
61
+ attempt: opts.attempt,
62
+ committedAt: workspace.updatedAt,
63
+ metadata: {
64
+ reason: opts.reason,
65
+ ...(opts.checkpointPayload !== undefined ? { checkpointPayload: opts.checkpointPayload } : {})
66
+ }
67
+ };
68
+ workspace.checkpoints.push(checkpoint);
69
+ return checkpoint;
70
+ }
71
+ async resumeWorkspace(opts) {
72
+ opts.signal?.throwIfAborted();
73
+ const workspace = this.requireWorkspace(opts.workspaceRef);
74
+ if (opts.checkpointRef && !workspace.checkpoints.some((checkpoint) => checkpoint.checkpointRef === opts.checkpointRef)) {
75
+ throw new Error(`Unknown workspace checkpoint: ${opts.checkpointRef}`);
76
+ }
77
+ workspace.state = 'active';
78
+ workspace.runId = opts.runId;
79
+ workspace.sessionId = opts.sessionId;
80
+ workspace.attempt = opts.attempt;
81
+ workspace.updatedAt = new Date().toISOString();
82
+ return {
83
+ workspaceRef: workspace.workspaceRef,
84
+ runId: workspace.runId,
85
+ sessionId: workspace.sessionId,
86
+ state: 'active',
87
+ startedAt: workspace.updatedAt,
88
+ attempt: workspace.attempt,
89
+ metadata: workspace.metadata
90
+ };
91
+ }
92
+ async abortWorkspace(opts) {
93
+ opts.signal?.throwIfAborted();
94
+ const workspace = this.requireWorkspace(opts.workspaceRef);
95
+ workspace.state = 'aborted';
96
+ workspace.updatedAt = new Date().toISOString();
97
+ return { workspaceRef: opts.workspaceRef, state: 'aborted', abortedAt: workspace.updatedAt };
98
+ }
99
+ async cleanupWorkspace(opts) {
100
+ opts.signal?.throwIfAborted();
101
+ const workspace = this.requireWorkspace(opts.workspaceRef);
102
+ workspace.state = 'cleaned';
103
+ workspace.updatedAt = new Date().toISOString();
104
+ this.workspaces.delete(opts.workspaceRef);
105
+ return { workspaceRef: opts.workspaceRef, state: 'cleaned', completedAt: workspace.updatedAt };
106
+ }
107
+ async inspectWorkspace(opts) {
108
+ opts.signal?.throwIfAborted();
109
+ const workspaceRef = opts.workspaceRef ?? this.findWorkspaceByCheckpoint(opts.checkpointRef);
110
+ const workspace = this.requireWorkspace(workspaceRef);
111
+ const latest = workspace.checkpoints.at(-1);
112
+ return {
113
+ workspaceRef: workspace.workspaceRef,
114
+ state: workspace.state,
115
+ checkpoints: workspace.checkpoints,
116
+ ...(latest ? { currentCheckpointRef: latest.checkpointRef } : {}),
117
+ retention: this.info.policy.retention,
118
+ quota: this.info.policy.quota,
119
+ createdAt: workspace.createdAt,
120
+ updatedAt: workspace.updatedAt,
121
+ metadata: workspace.metadata
122
+ };
123
+ }
124
+ findWorkspaceByCheckpoint(checkpointRef) {
125
+ if (!checkpointRef)
126
+ throw new Error('workspaceRef or checkpointRef is required.');
127
+ const found = [...this.workspaces.values()].find((workspace) => workspace.checkpoints.some((checkpoint) => checkpoint.checkpointRef === checkpointRef));
128
+ if (!found)
129
+ throw new Error(`Unknown workspace checkpoint: ${checkpointRef}`);
130
+ return found.workspaceRef;
131
+ }
132
+ requireWorkspace(workspaceRef) {
133
+ const workspace = this.workspaces.get(workspaceRef);
134
+ if (!workspace)
135
+ throw new Error(`Unknown workspace: ${workspaceRef}`);
136
+ return workspace;
137
+ }
138
+ }
139
+ /** Creates a fresh in-process durable workspace store. */
140
+ export function inMemoryDurableWorkspaceStore() {
141
+ return new InMemoryDurableWorkspaceStore();
142
+ }
@@ -0,0 +1 @@
1
+ export { InMemoryDurableWorkspaceStore, inMemoryDurableWorkspaceStore } from './in-memory.js';
@@ -0,0 +1 @@
1
+ export { InMemoryDurableWorkspaceStore, inMemoryDurableWorkspaceStore } from './in-memory.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@purista/harness",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "Self-hosted enterprise agent harness for typed tools, agents, workflows, state, sandboxing, and telemetry.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -39,22 +39,25 @@
39
39
  "scripts": {
40
40
  "clean": "rm -rf dist",
41
41
  "build": "npm run clean && tsc -p tsconfig.json",
42
+ "lint": "npm run typecheck",
42
43
  "typecheck": "tsc -p tsconfig.json --noEmit",
43
44
  "test:types": "tsc -p tsconfig.type-tests.json",
44
45
  "test": "vitest run",
45
46
  "test:unit": "vitest run src",
47
+ "test:coverage": "vitest run --coverage",
46
48
  "test:contracts": "vitest run src/ports test/sandbox.test.ts",
47
49
  "test:integration": "vitest run test/harness.test.ts test/tools.test.ts test/skills.test.ts test/telemetry-flow.test.ts",
48
50
  "test:failure": "vitest run test/failure"
49
51
  },
50
52
  "dependencies": {
51
- "@opentelemetry/semantic-conventions": "^1.40.0",
53
+ "@opentelemetry/semantic-conventions": "^1.41.1",
54
+ "yaml": "^2.9.0",
52
55
  "zod": "^4.4.3"
53
56
  },
54
57
  "peerDependencies": {
55
58
  "@modelcontextprotocol/sdk": "^1.29.0",
56
59
  "@opentelemetry/api": "^1.9.1",
57
- "just-bash": "^2.14.4"
60
+ "just-bash": "^3.0.1"
58
61
  },
59
62
  "peerDependenciesMeta": {
60
63
  "@modelcontextprotocol/sdk": {
@@ -67,9 +70,12 @@
67
70
  "devDependencies": {
68
71
  "@modelcontextprotocol/sdk": "^1.29.0",
69
72
  "@opentelemetry/context-async-hooks": "^2.7.1",
70
- "@types/node": "^25.6.0",
71
- "just-bash": "^2.14.4",
73
+ "@types/node": "^25.9.1",
74
+ "just-bash": "^3.0.1",
72
75
  "typescript": "^6.0.3",
73
- "vitest": "^4.1.5"
76
+ "vitest": "^4.1.8"
77
+ },
78
+ "engines": {
79
+ "node": ">=24.15.0"
74
80
  }
75
81
  }