@cat-factory/orchestration 0.302.1 → 0.303.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 (31) hide show
  1. package/dist/container/dependencies.d.ts +8 -1
  2. package/dist/container/dependencies.d.ts.map +1 -1
  3. package/dist/container/environmentsModule.factory.d.ts +2 -0
  4. package/dist/container/environmentsModule.factory.d.ts.map +1 -1
  5. package/dist/container/environmentsModule.factory.js +17 -0
  6. package/dist/container/environmentsModule.factory.js.map +1 -1
  7. package/dist/container/modules.d.ts +23 -3
  8. package/dist/container/modules.d.ts.map +1 -1
  9. package/dist/container/modules.js +61 -1
  10. package/dist/container/modules.js.map +1 -1
  11. package/dist/container/platform-modules.d.ts +6 -0
  12. package/dist/container/platform-modules.d.ts.map +1 -1
  13. package/dist/container/platform-modules.js +2 -2
  14. package/dist/container/platform-modules.js.map +1 -1
  15. package/dist/container.d.ts +1 -1
  16. package/dist/container.d.ts.map +1 -1
  17. package/dist/container.js +5 -37
  18. package/dist/container.js.map +1 -1
  19. package/dist/index.d.ts +1 -0
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +1 -0
  22. package/dist/index.js.map +1 -1
  23. package/dist/modules/environments/EnvironmentTestService.d.ts +95 -2
  24. package/dist/modules/environments/EnvironmentTestService.d.ts.map +1 -1
  25. package/dist/modules/environments/EnvironmentTestService.js +219 -7
  26. package/dist/modules/environments/EnvironmentTestService.js.map +1 -1
  27. package/dist/modules/environments/environmentProbeStage.d.ts +85 -0
  28. package/dist/modules/environments/environmentProbeStage.d.ts.map +1 -0
  29. package/dist/modules/environments/environmentProbeStage.js +181 -0
  30. package/dist/modules/environments/environmentProbeStage.js.map +1 -0
  31. package/package.json +12 -12
@@ -0,0 +1,181 @@
1
+ import { noopLogger, redactSecrets } from '@cat-factory/kernel';
2
+ import { coerceEnvironmentProbeReport, environmentProbeSurfaceFor, reachabilityNote, } from '@cat-factory/contracts';
3
+ /**
4
+ * What a BROKEN dry run says, ahead of the container's own message.
5
+ *
6
+ * The run record has ONE error field, so without this a container that vanished and a model that
7
+ * errored read identically to the developer who has to decide whether pressing the button again
8
+ * is worth anything. Not an exhaustive `Record` over the failure vocabulary: only these arrive on
9
+ * this path (the dispatcher maps everything else through `failureKindFromHarnessCause`), and the
10
+ * default is a true statement about every one of the rest rather than a hole.
11
+ */
12
+ function probeFailureCause(kind) {
13
+ switch (kind) {
14
+ case 'evicted':
15
+ return ('The dry run container vanished before it reported (an eviction or a crash), so nothing ' +
16
+ 'was established about the environment. Re-running gets a fresh container.');
17
+ case 'harness_shutdown':
18
+ return ('The dry run container was shut down while the agent was still working, so nothing was ' +
19
+ 'established about the environment.');
20
+ case 'timeout':
21
+ return ('The dry run passed its watchdog before reporting, so nothing was established about the ' +
22
+ 'environment.');
23
+ default:
24
+ return 'The dry-run agent failed before it produced a report.';
25
+ }
26
+ }
27
+ export class EnvironmentProbeStage {
28
+ deps;
29
+ /** The injected logger, normalised once so every site can log unconditionally (CLAUDE.md). */
30
+ log;
31
+ constructor(deps) {
32
+ this.deps = deps;
33
+ this.log = deps.logger ?? noopLogger;
34
+ }
35
+ /**
36
+ * Which prober a frame's dry run would run, through the shared rule the SPA's button label
37
+ * reads. Pure, so ADMISSION can ask it with the frame it has already loaded and no record yet.
38
+ */
39
+ surfaceFor(frame) {
40
+ return environmentProbeSurfaceFor(frame.type);
41
+ }
42
+ /**
43
+ * Whether this deployment can actually run `surface`'s prober for this workspace, asked at
44
+ * admission. Delegated to the agent, which is where the runner backend is known; see the port
45
+ * for why an unknown answers yes.
46
+ */
47
+ async supports(workspaceId, surface) {
48
+ return this.deps.agent.supports(workspaceId, surface);
49
+ }
50
+ /**
51
+ * The frame this run is probing, read ONCE per tick, with the prober its type selects.
52
+ *
53
+ * One read rather than one per collaborator: the surface decides which container the job runs
54
+ * in and the title and description go into the prompt, and two reads a moment apart can observe
55
+ * two different frames, so a rename between them puts a service name in the prompt that does not
56
+ * match the surface the claim pinned. The caller PINS the surface on the run at the claim
57
+ * (`probeSurface`) and passes the pinned value back to {@link dispatch}: every later poll and
58
+ * reclaim must address the container that actually started, not one re-derived from a frame that
59
+ * may since have been retyped or deleted.
60
+ */
61
+ async resolveTarget(record) {
62
+ const frame = await this.deps.blockRepository.get(record.workspaceId, record.blockId);
63
+ if (!frame) {
64
+ throw new Error('The service frame was deleted while the agent dry run was starting.');
65
+ }
66
+ return { frame, surface: this.surfaceFor(frame) };
67
+ }
68
+ /**
69
+ * Dispatch the dry run for `record`, whose environment is `ready`.
70
+ *
71
+ * Throws when anything it needs is missing, and each throw is the run's terminal error, so the
72
+ * messages name what a human has to fix rather than what this function could not find. The
73
+ * caller has already CLAIMED the probe (see `EnvironmentTestRunRecord.probeSurface`), so a throw
74
+ * here fails the run at the `probing` stage with the environment still up for `fail()` to
75
+ * reclaim.
76
+ */
77
+ async dispatch(record, surface, frame) {
78
+ if (!record.environmentId) {
79
+ throw new Error('The agent dry run has no provisioned environment to probe.');
80
+ }
81
+ const handle = await this.deps.environments.getHandleWithAccess(record.workspaceId, record.environmentId);
82
+ if (!handle) {
83
+ throw new Error('The provisioned environment could not be read back for the agent dry run.');
84
+ }
85
+ if (!handle.url) {
86
+ throw new Error('The environment provider exposed no URL for this environment, so there is nothing for ' +
87
+ 'an agent to drive. Configure the handler to report the environment URL.');
88
+ }
89
+ const bound = await this.deps.resolveRunRepoContext(record.workspaceId, record.blockId);
90
+ if (!bound?.owner || !bound.name) {
91
+ throw new Error('The agent dry run needs the service repository to work out what the service exposes, ' +
92
+ 'and no linked repository resolved for this frame.');
93
+ }
94
+ if (!record.branch) {
95
+ throw new Error('The agent dry run has no test branch to check out.');
96
+ }
97
+ // The same projection the engine hands a tester, so the prober and the tester cannot disagree
98
+ // about what the platform proved. Undefined when nothing has dialled it, which the prompt
99
+ // states rather than omits.
100
+ const reachability = reachabilityNote(handle.reachability ?? null);
101
+ return this.deps.agent.start({
102
+ workspaceId: record.workspaceId,
103
+ jobId: record.id,
104
+ blockId: record.blockId,
105
+ surface,
106
+ repo: {
107
+ owner: bound.owner,
108
+ name: bound.name,
109
+ // The THROWAWAY branch, not the default one: it is the tree this environment was built
110
+ // from, so a service whose routes or auth changed on it is described to the prober as it
111
+ // actually runs rather than as `main` describes it.
112
+ branch: record.branch,
113
+ ...(bound.provider ? { provider: bound.provider } : {}),
114
+ ...(bound.serviceDirectory ? { serviceDirectory: bound.serviceDirectory } : {}),
115
+ },
116
+ environment: {
117
+ url: handle.url,
118
+ status: handle.status,
119
+ ...(handle.access ? { access: handle.access } : {}),
120
+ ...(reachability ? { reachability } : {}),
121
+ },
122
+ service: {
123
+ title: frame.title,
124
+ ...(frame.description?.trim() ? { description: frame.description.trim() } : {}),
125
+ },
126
+ initiatedBy: record.initiatedBy,
127
+ });
128
+ }
129
+ /**
130
+ * Poll the dispatched dry run.
131
+ *
132
+ * A `failed` update THROWS rather than being reported as a verdict: an evicted container or a
133
+ * reply carrying no JSON is a diagnostic that never happened, and returning it as a finding
134
+ * would tell an operator their service is inoperable when it is the platform's own step that
135
+ * broke. The run then fails at the `probing` stage with the container's own error, which is the
136
+ * honest reading and the one the sweeper's cleanup already handles.
137
+ */
138
+ async poll(record, surface) {
139
+ const update = await this.deps.agent.poll({
140
+ workspaceId: record.workspaceId,
141
+ jobId: record.id,
142
+ surface,
143
+ });
144
+ if (update.state === 'running') {
145
+ return update.subtasks
146
+ ? { state: 'running', subtasks: update.subtasks }
147
+ : { state: 'running' };
148
+ }
149
+ if (update.state === 'failed') {
150
+ throw new Error(`${probeFailureCause(update.failureKind)} ${update.error}`);
151
+ }
152
+ const report = coerceEnvironmentProbeReport(update.report, {
153
+ surface,
154
+ ...(update.model ? { model: update.model } : {}),
155
+ // The report is model-authored text on its way to a persisted row and a rendered panel, and
156
+ // the prompt hands the agent the environment's own credential verbatim plus every sealed
157
+ // test secret in its shell. Asking it not to echo them is guidance; this is the boundary.
158
+ // Applied at the COMPOSE site so it runs before the caps, per the untrusted-text rule.
159
+ scrub: (value) => redactSecrets(value) ?? value,
160
+ });
161
+ this.log.info('environment dry run reported', {
162
+ workspaceId: record.workspaceId,
163
+ runId: record.id,
164
+ surface,
165
+ verdict: report.verdict,
166
+ attempted: report.attempted,
167
+ succeeded: report.succeeded,
168
+ authenticatedSucceeded: report.authenticatedSucceeded,
169
+ });
170
+ return { state: 'reported', report };
171
+ }
172
+ /** Reclaim the probe's container. Best-effort by contract; never throws at the caller. */
173
+ async release(record, surface) {
174
+ await this.deps.agent.stop({
175
+ workspaceId: record.workspaceId,
176
+ jobId: record.id,
177
+ surface,
178
+ });
179
+ }
180
+ }
181
+ //# sourceMappingURL=environmentProbeStage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"environmentProbeStage.js","sourceRoot":"","sources":["../../../src/modules/environments/environmentProbeStage.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cat-factory/orchestration",
3
- "version": "0.302.1",
3
+ "version": "0.303.0",
4
4
  "description": "Delivery-workflow engine for the Agent Architecture Board (execution, bootstrap, pipelines, board, boardScan, requirements, and composition root).",
5
5
  "repository": {
6
6
  "type": "git",
@@ -24,19 +24,19 @@
24
24
  "access": "public"
25
25
  },
26
26
  "dependencies": {
27
- "@cat-factory/agents": "0.157.1",
28
- "@cat-factory/caching": "0.20.72",
29
- "@cat-factory/contracts": "0.347.0",
30
- "@cat-factory/integrations": "0.172.7",
31
- "@cat-factory/kernel": "0.338.0",
32
- "@cat-factory/prompt-fragments": "1.1.34",
33
- "@cat-factory/sandbox": "0.12.46",
34
- "@cat-factory/spend": "0.19.0",
35
- "@cat-factory/workspaces": "0.28.60",
36
- "ai": "^7.0.92"
27
+ "@cat-factory/agents": "0.158.0",
28
+ "@cat-factory/caching": "0.20.74",
29
+ "@cat-factory/contracts": "0.348.0",
30
+ "@cat-factory/integrations": "0.172.9",
31
+ "@cat-factory/kernel": "0.340.0",
32
+ "@cat-factory/prompt-fragments": "1.1.36",
33
+ "@cat-factory/sandbox": "0.12.48",
34
+ "@cat-factory/spend": "0.20.1",
35
+ "@cat-factory/workspaces": "0.28.62",
36
+ "ai": "^7.0.93"
37
37
  },
38
38
  "devDependencies": {
39
- "@cat-factory/sandbox-fixtures": "0.8.30",
39
+ "@cat-factory/sandbox-fixtures": "0.8.31",
40
40
  "typescript": "7.0.2",
41
41
  "vitest": "^4.1.11"
42
42
  },