@agent-relay/factory 0.1.49 → 0.1.51
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.
- package/README.md +93 -11
- package/dist/cli/fleet.d.ts +4 -1
- package/dist/cli/fleet.d.ts.map +1 -1
- package/dist/cli/fleet.js +87 -42
- package/dist/cli/fleet.js.map +1 -1
- package/dist/config/schema.d.ts +1671 -10
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +83 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/dispatch/templates.d.ts +6 -0
- package/dist/dispatch/templates.d.ts.map +1 -1
- package/dist/dispatch/templates.js +16 -0
- package/dist/dispatch/templates.js.map +1 -1
- package/dist/environments/connection-registry.d.ts +617 -0
- package/dist/environments/connection-registry.d.ts.map +1 -0
- package/dist/environments/connection-registry.js +174 -0
- package/dist/environments/connection-registry.js.map +1 -0
- package/dist/environments/index.d.ts +21 -0
- package/dist/environments/index.d.ts.map +1 -0
- package/dist/environments/index.js +11 -0
- package/dist/environments/index.js.map +1 -0
- package/dist/environments/k6-job.d.ts +64 -0
- package/dist/environments/k6-job.d.ts.map +1 -0
- package/dist/environments/k6-job.js +376 -0
- package/dist/environments/k6-job.js.map +1 -0
- package/dist/environments/kubernetes-client.d.ts +72 -0
- package/dist/environments/kubernetes-client.d.ts.map +1 -0
- package/dist/environments/kubernetes-client.js +337 -0
- package/dist/environments/kubernetes-client.js.map +1 -0
- package/dist/environments/kubernetes-command.d.ts +30 -0
- package/dist/environments/kubernetes-command.d.ts.map +1 -0
- package/dist/environments/kubernetes-command.js +108 -0
- package/dist/environments/kubernetes-command.js.map +1 -0
- package/dist/environments/kubernetes-environment.d.ts +38 -0
- package/dist/environments/kubernetes-environment.d.ts.map +1 -0
- package/dist/environments/kubernetes-environment.js +333 -0
- package/dist/environments/kubernetes-environment.js.map +1 -0
- package/dist/environments/kubernetes-provider.d.ts +83 -0
- package/dist/environments/kubernetes-provider.d.ts.map +1 -0
- package/dist/environments/kubernetes-provider.js +956 -0
- package/dist/environments/kubernetes-provider.js.map +1 -0
- package/dist/environments/load-harness.d.ts +199 -0
- package/dist/environments/load-harness.d.ts.map +1 -0
- package/dist/environments/load-harness.js +259 -0
- package/dist/environments/load-harness.js.map +1 -0
- package/dist/environments/load-profile.d.ts +294 -0
- package/dist/environments/load-profile.d.ts.map +1 -0
- package/dist/environments/load-profile.js +125 -0
- package/dist/environments/load-profile.js.map +1 -0
- package/dist/environments/stack-deployer.d.ts +22 -0
- package/dist/environments/stack-deployer.d.ts.map +1 -0
- package/dist/environments/stack-deployer.js +27 -0
- package/dist/environments/stack-deployer.js.map +1 -0
- package/dist/environments/stack-descriptor.d.ts +243 -0
- package/dist/environments/stack-descriptor.d.ts.map +1 -0
- package/dist/environments/stack-descriptor.js +88 -0
- package/dist/environments/stack-descriptor.js.map +1 -0
- package/dist/environments/verification-pipeline.d.ts +117 -0
- package/dist/environments/verification-pipeline.d.ts.map +1 -0
- package/dist/environments/verification-pipeline.js +643 -0
- package/dist/environments/verification-pipeline.js.map +1 -0
- package/dist/environments/verification-stack-deployer.d.ts +73 -0
- package/dist/environments/verification-stack-deployer.d.ts.map +1 -0
- package/dist/environments/verification-stack-deployer.js +484 -0
- package/dist/environments/verification-stack-deployer.js.map +1 -0
- package/dist/environments/verification-stack-descriptor.d.ts +1134 -0
- package/dist/environments/verification-stack-descriptor.d.ts.map +1 -0
- package/dist/environments/verification-stack-descriptor.js +275 -0
- package/dist/environments/verification-stack-descriptor.js.map +1 -0
- package/dist/environments/verification-stack.d.ts +29 -0
- package/dist/environments/verification-stack.d.ts.map +1 -0
- package/dist/environments/verification-stack.js +43 -0
- package/dist/environments/verification-stack.js.map +1 -0
- package/dist/featuremap/generate.d.ts +72 -0
- package/dist/featuremap/generate.d.ts.map +1 -0
- package/dist/featuremap/generate.js +854 -0
- package/dist/featuremap/generate.js.map +1 -0
- package/dist/featuremap/index.d.ts +2 -0
- package/dist/featuremap/index.d.ts.map +1 -1
- package/dist/featuremap/index.js +1 -0
- package/dist/featuremap/index.js.map +1 -1
- package/dist/featuremap/validate.d.ts +6 -0
- package/dist/featuremap/validate.d.ts.map +1 -1
- package/dist/featuremap/validate.js +95 -13
- package/dist/featuremap/validate.js.map +1 -1
- package/dist/fleet/create-fleet.d.ts +2 -0
- package/dist/fleet/create-fleet.d.ts.map +1 -1
- package/dist/fleet/create-fleet.js +1 -0
- package/dist/fleet/create-fleet.js.map +1 -1
- package/dist/fleet/internal-fleet-client.d.ts +8 -1
- package/dist/fleet/internal-fleet-client.d.ts.map +1 -1
- package/dist/fleet/internal-fleet-client.js +31 -6
- package/dist/fleet/internal-fleet-client.js.map +1 -1
- package/dist/fleet/relay-fleet-client.d.ts +4 -1
- package/dist/fleet/relay-fleet-client.d.ts.map +1 -1
- package/dist/fleet/relay-fleet-client.js +142 -1
- package/dist/fleet/relay-fleet-client.js.map +1 -1
- package/dist/git/agent-worktree.d.ts +4 -1
- package/dist/git/agent-worktree.d.ts.map +1 -1
- package/dist/git/agent-worktree.js +151 -8
- package/dist/git/agent-worktree.js.map +1 -1
- package/dist/index.d.ts +32 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -3
- package/dist/index.js.map +1 -1
- package/dist/mount/local-mount-preflight.d.ts.map +1 -1
- package/dist/mount/local-mount-preflight.js +6 -2
- package/dist/mount/local-mount-preflight.js.map +1 -1
- package/dist/mount/relayfile-cloud-mount-client.d.ts +8 -2
- package/dist/mount/relayfile-cloud-mount-client.d.ts.map +1 -1
- package/dist/mount/relayfile-cloud-mount-client.js +11 -1
- package/dist/mount/relayfile-cloud-mount-client.js.map +1 -1
- package/dist/node/factory-node.d.ts +2 -0
- package/dist/node/factory-node.d.ts.map +1 -1
- package/dist/node/factory-node.js +107 -2
- package/dist/node/factory-node.js.map +1 -1
- package/dist/node/preview-process.d.ts +65 -0
- package/dist/node/preview-process.d.ts.map +1 -0
- package/dist/node/preview-process.js +431 -0
- package/dist/node/preview-process.js.map +1 -0
- package/dist/node/tailscale-preview.d.ts +45 -0
- package/dist/node/tailscale-preview.d.ts.map +1 -0
- package/dist/node/tailscale-preview.js +748 -0
- package/dist/node/tailscale-preview.js.map +1 -0
- package/dist/observability/events.d.ts +1838 -28
- package/dist/observability/events.d.ts.map +1 -1
- package/dist/observability/events.js +52 -0
- package/dist/observability/events.js.map +1 -1
- package/dist/orchestrator/factory.d.ts.map +1 -1
- package/dist/orchestrator/factory.js +1416 -78
- package/dist/orchestrator/factory.js.map +1 -1
- package/dist/orchestrator/index.d.ts +2 -1
- package/dist/orchestrator/index.d.ts.map +1 -1
- package/dist/orchestrator/index.js +1 -1
- package/dist/orchestrator/index.js.map +1 -1
- package/dist/orchestrator/reaper.d.ts +24 -0
- package/dist/orchestrator/reaper.d.ts.map +1 -1
- package/dist/orchestrator/reaper.js +117 -0
- package/dist/orchestrator/reaper.js.map +1 -1
- package/dist/ports/environment.d.ts +83 -0
- package/dist/ports/environment.d.ts.map +1 -0
- package/dist/ports/environment.js +2 -0
- package/dist/ports/environment.js.map +1 -0
- package/dist/ports/fleet.d.ts +68 -1
- package/dist/ports/fleet.d.ts.map +1 -1
- package/dist/ports/index.d.ts +3 -2
- package/dist/ports/index.d.ts.map +1 -1
- package/dist/ports/mount.d.ts +9 -0
- package/dist/ports/mount.d.ts.map +1 -1
- package/dist/ports/state.d.ts +17 -1
- package/dist/ports/state.d.ts.map +1 -1
- package/dist/ports/worktree.d.ts +14 -0
- package/dist/ports/worktree.d.ts.map +1 -1
- package/dist/state/file-state-store.js +28 -1
- package/dist/state/file-state-store.js.map +1 -1
- package/dist/subscriptions/index.d.ts +2 -0
- package/dist/subscriptions/index.d.ts.map +1 -1
- package/dist/subscriptions/index.js +1 -0
- package/dist/subscriptions/index.js.map +1 -1
- package/dist/subscriptions/resource-subscriptions.d.ts +77 -0
- package/dist/subscriptions/resource-subscriptions.d.ts.map +1 -0
- package/dist/subscriptions/resource-subscriptions.js +91 -0
- package/dist/subscriptions/resource-subscriptions.js.map +1 -0
- package/dist/testing/fakes.d.ts +9 -1
- package/dist/testing/fakes.d.ts.map +1 -1
- package/dist/testing/fakes.js +40 -0
- package/dist/testing/fakes.js.map +1 -1
- package/dist/triage/schema.d.ts +32 -32
- package/dist/types.d.ts +4 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +18 -4
- package/schemas/kubernetes-environment-stack.schema.json +86 -0
- package/schemas/verification-stack.schema.json +292 -0
package/README.md
CHANGED
|
@@ -124,13 +124,15 @@ From a source checkout instead of an npm install, run
|
|
|
124
124
|
| `factory dispatch <KEY\|path>` | Triage + dispatch one issue. Honors `--dry-run`. |
|
|
125
125
|
| `factory babysit <PR\|PR-URL>` | Spawn a one-shot babysitter for an existing open PR, even when it was not created by Factory. |
|
|
126
126
|
| `factory canary <KEY\|path>` | Assert a known "Ready for Agent" issue is dispatch-ready by the real dry-run triage path. Prints `{ok,issue,status,reason}`; exits non-zero (with the skip reason) if it isn't. |
|
|
127
|
-
| `factory featuremap check [--base <ref>]` | Validate the repository feature/test manifest and optionally report advisory drift for unchanged entries whose locations changed. |
|
|
127
|
+
| `factory featuremap check [--manifest <path>] [--base <ref>]` | Validate the repository feature/test manifest and optionally report advisory drift for unchanged entries whose locations changed. |
|
|
128
128
|
|
|
129
129
|
Global options work anywhere in the args: `--config <path>`, `--dry-run`,
|
|
130
130
|
`--backend <internal|relay>`, and `--agent-exit-timeout <ms>`. The internal
|
|
131
131
|
backend reuses a relay broker that's already running for your workspace, and
|
|
132
132
|
starts one if none is. For self-started brokers, the agent-exit timeout defaults
|
|
133
|
-
to 30 minutes and can also be set with `FACTORY_AGENT_EXIT_TIMEOUT_MS`.
|
|
133
|
+
to 30 minutes and can also be set with `FACTORY_AGENT_EXIT_TIMEOUT_MS`. Set
|
|
134
|
+
`FACTORY_LOG_LEVEL=debug` to include per-checkout details for summarized local
|
|
135
|
+
mount refreshes.
|
|
134
136
|
|
|
135
137
|
Integration connection prompts only run for commands that need provider data or
|
|
136
138
|
GitHub write access. Maintenance commands such as `status`, `loop-status`,
|
|
@@ -146,9 +148,9 @@ instruction to rerun the Factory command in an interactive terminal instead.
|
|
|
146
148
|
|
|
147
149
|
Repositories with `.agentworkforce/features/manifest.yaml` can run
|
|
148
150
|
`factory featuremap check` in CI. The command rejects malformed or duplicate
|
|
149
|
-
entries, invalid verification tiers, catalog-summary drift,
|
|
150
|
-
|
|
151
|
-
for programmatic use.
|
|
151
|
+
entries, invalid verification tiers, catalog-summary drift, locations that do
|
|
152
|
+
not exist, and incomplete v1.1 category-to-procedure routing. The same checker
|
|
153
|
+
is published as `@agent-relay/factory/featuremap` for programmatic use.
|
|
152
154
|
|
|
153
155
|
During review, pass the PR base ref with `--base <ref>`. A changed file named by
|
|
154
156
|
an existing manifest entry produces an advisory when that entry's description,
|
|
@@ -156,12 +158,20 @@ verification tier, and locations are unchanged. The reviewer must re-confirm
|
|
|
156
158
|
that metadata; the advisory does not itself fail the command because a covered
|
|
157
159
|
file can change without changing the feature contract.
|
|
158
160
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
161
|
+
Factory's agent-facing runbook is `.claude/skills/verify-features.md`. It tells
|
|
162
|
+
an agent how to resolve a manifest category to its named end-to-end procedure,
|
|
163
|
+
run the applicable tier with safe fixtures and cleanup, assert provider/fleet
|
|
164
|
+
state, and report unexercised live tiers explicitly. The deterministic full gate
|
|
165
|
+
is `npm run build && npm run featuremap:check && npm test && npm run verify:e2e`;
|
|
166
|
+
`workflows/verify-features.ts` adds opt-in provider, fleet, and cloud checks.
|
|
167
|
+
|
|
168
|
+
The checked-in `factory-feature-guardian` proactive persona mirrors Relay's
|
|
169
|
+
guardian operating model. Hourly, it reads the catalog from a scoped Factory
|
|
170
|
+
repository mount, selects one unchecked feature from exact revisioned cycle
|
|
171
|
+
state, posts an idempotent Slack question, requires a provider timestamp, and
|
|
172
|
+
only then checkpoints progress. Manifest/state/delivery ambiguity fails closed
|
|
173
|
+
instead of silently skipping a feature. Configure its Slack channel and deploy
|
|
174
|
+
the persona through the normal Agent Workforce proactive-agent path.
|
|
165
175
|
|
|
166
176
|
### Cloud progress and trace correlation
|
|
167
177
|
|
|
@@ -279,6 +289,78 @@ The definition reads its node config from `./factory.node.json` (or
|
|
|
279
289
|
mapped repo is advertised as a `repo:<label>` tag so placement can route
|
|
280
290
|
repo-scoped spawns to it. Spawns for unadvertised paths are refused on the node.
|
|
281
291
|
|
|
292
|
+
### Tailnet live previews
|
|
293
|
+
|
|
294
|
+
Factory can attach an issue-lifetime [Tailscale Serve](https://tailscale.com/docs/features/tailscale-serve)
|
|
295
|
+
route to a repository's local development port. Configure the same service on
|
|
296
|
+
the control plane and execution node (a combined local config needs it only
|
|
297
|
+
once):
|
|
298
|
+
|
|
299
|
+
```json
|
|
300
|
+
{
|
|
301
|
+
"preview": {
|
|
302
|
+
"provider": "tailscale-serve",
|
|
303
|
+
"access": "tailnet",
|
|
304
|
+
"services": {
|
|
305
|
+
"AgentWorkforce/pear": {
|
|
306
|
+
"port": 3000,
|
|
307
|
+
"portSpan": 25,
|
|
308
|
+
"startCommand": "npm ci && exec npm run dev"
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
The node then advertises `preview:tailscale-serve`; Factory places the preview
|
|
316
|
+
first and pins the issue's agents to that node. The URL is included in agent
|
|
317
|
+
tasks, the Slack dispatch root, and the pull-request description. Factory uses
|
|
318
|
+
Serve—not Funnel—so the URL remains inside the configured tailnet and normal
|
|
319
|
+
tailnet grants/ACLs apply. Factory checks the live Serve status and refuses to
|
|
320
|
+
surface a route marked as Funnel.
|
|
321
|
+
|
|
322
|
+
`port` is the preferred node-local app port. Factory reserves the first free
|
|
323
|
+
port in `port..port + portSpan - 1` (the span defaults to 100), places that
|
|
324
|
+
allocated port in every agent task, and reserves a separate HTTPS port from
|
|
325
|
+
`preview.httpsPortRange`. Keep that HTTPS range dedicated to Factory previews.
|
|
326
|
+
Preview provisioning happens immediately after Factory creates the isolated
|
|
327
|
+
issue worktree, before an agent has had a chance to install ignored dependencies
|
|
328
|
+
such as `node_modules`. `startCommand` must therefore include any bounded,
|
|
329
|
+
non-interactive bootstrap the fresh checkout needs, followed by a foreground
|
|
330
|
+
development command that honors `PORT` (for example,
|
|
331
|
+
`npm ci && exec npm run dev`).
|
|
332
|
+
The node starts it in the issue checkout, waits for the allocated local HTTP
|
|
333
|
+
port to respond, and verifies on Linux or macOS that the listener belongs to the
|
|
334
|
+
supervised process tree before returning the URL. It persists an exact process
|
|
335
|
+
identity so the command survives agent handoffs and can be safely recovered or
|
|
336
|
+
stopped. The command must not daemonize or bind a different port. Active sweeps
|
|
337
|
+
repeat listener ownership verification and disable the exact Factory route if
|
|
338
|
+
the port is taken over by an unrelated process.
|
|
339
|
+
For safety, preview commands receive only `PORT` plus basic shell, locale, home,
|
|
340
|
+
and temporary-directory variables; they do not inherit arbitrary Factory,
|
|
341
|
+
Relay, or provider credentials from the node process. Load intentional
|
|
342
|
+
application settings through a checkout-local environment mechanism. Do not put
|
|
343
|
+
secrets directly in `startCommand`, because lifecycle recovery persists the
|
|
344
|
+
command as metadata.
|
|
345
|
+
|
|
346
|
+
Terminal lifecycle completion is withheld until routes and their supervised
|
|
347
|
+
commands have been removed at Human Review or Done. If the source-state
|
|
348
|
+
writeback wins a crash race, startup recovery observes that terminal source
|
|
349
|
+
state and finishes preview teardown before terminalizing the durable lifecycle.
|
|
350
|
+
A startup and periodic sweep reaps only orphaned resources in the current
|
|
351
|
+
Factory workspace whose persisted route and process identities still match.
|
|
352
|
+
See the [provider evaluation](planning/preview-provider-evaluation.md)
|
|
353
|
+
for the decision and lifecycle contract.
|
|
354
|
+
|
|
355
|
+
To exercise the real provider lifecycle on a signed-in node, build first and
|
|
356
|
+
run `TAILSCALE_BIN=/path/to/tailscale node scripts/verify-tailscale-preview-e2e.mjs`.
|
|
357
|
+
The check drives the node's advertised `preview:tailscale-serve` action to start
|
|
358
|
+
a detached HTTP service, reaches it through Serve, recovers it through a fresh
|
|
359
|
+
node-action instance, tears it down, then proves a startup orphan sweep reaps a
|
|
360
|
+
second abandoned route and process while preserving unrelated Serve
|
|
361
|
+
configuration. Override its dedicated ports with
|
|
362
|
+
`FACTORY_PREVIEW_E2E_HTTPS_PORT` and `FACTORY_PREVIEW_E2E_TARGET_PORT`.
|
|
363
|
+
|
|
282
364
|
### Dispatching to nodes (`--backend relay`)
|
|
283
365
|
|
|
284
366
|
With `--backend relay`, the factory orchestrator dispatches work through the
|
package/dist/cli/fleet.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ensureCloudSession, type CloudSession } from '@agent-relay/cloud';
|
|
2
2
|
import { type LocalClonePathOptions } from '../config/local-clone-paths.js';
|
|
3
|
-
import { createFactory, createFleet, ensureRelayBroker, type Capability, type Factory, type FactoryEventReporter, type FactoryConfig, type FleetBackend, type FleetClient, type GhRunner, type FactoryStateResolution, type LocalMountOptions, type MountClient, type ProbeCloser, type RelayfileCloudMountClientConfig, type ResolvedFactoryWorkspace } from '../index.js';
|
|
3
|
+
import { createFactory, createFleet, ensureRelayBroker, reapFactoryEnvironmentsOnce, type Capability, type Factory, type FactoryEventReporter, type FactoryConfig, type FleetBackend, type FleetClient, type GhRunner, type FactoryStateResolution, type LocalMountOptions, type MountClient, type ProbeCloser, type RelayfileCloudMountClientConfig, type ResolvedFactoryWorkspace } from '../index.js';
|
|
4
4
|
import { type CheckFeatureMapOptions, type FeatureMapCheckReport } from '../featuremap/index.js';
|
|
5
5
|
import type { FactoryIntegrationProvider } from '../ports/index.js';
|
|
6
6
|
interface FleetCliDeps {
|
|
@@ -33,6 +33,8 @@ interface FleetCliDeps {
|
|
|
33
33
|
confirmIntegrationConnect?: (provider: FactoryIntegrationProvider) => Promise<boolean>;
|
|
34
34
|
openIntegrationUrl?: (url: string) => void | Promise<void>;
|
|
35
35
|
featureMapCheck?: (options?: CheckFeatureMapOptions) => Promise<FeatureMapCheckReport>;
|
|
36
|
+
/** Hermetic verification-environment sweep for CLI tests and alternate runtimes. */
|
|
37
|
+
reapEnvironments?: typeof reapFactoryEnvironmentsOnce;
|
|
36
38
|
}
|
|
37
39
|
interface GlobalOptions {
|
|
38
40
|
backend: FleetBackend;
|
|
@@ -47,6 +49,7 @@ type ParsedCommand = {
|
|
|
47
49
|
name?: string;
|
|
48
50
|
node?: 'self' | string;
|
|
49
51
|
task?: string;
|
|
52
|
+
workflow?: string;
|
|
50
53
|
model?: string;
|
|
51
54
|
sessionRef?: string;
|
|
52
55
|
cwd?: string;
|
package/dist/cli/fleet.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fleet.d.ts","sourceRoot":"","sources":["../../src/cli/fleet.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,kBAAkB,EAAE,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAG1E,OAAO,EAA6B,KAAK,qBAAqB,EAAE,MAAM,6BAA6B,CAAA;AACnG,OAAO,EAKL,aAAa,EACb,WAAW,EAEX,iBAAiB,
|
|
1
|
+
{"version":3,"file":"fleet.d.ts","sourceRoot":"","sources":["../../src/cli/fleet.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/fleet.js
CHANGED
|
@@ -6,13 +6,14 @@ import readline from 'node:readline/promises';
|
|
|
6
6
|
import { ensureCloudSession } from '@agent-relay/cloud';
|
|
7
7
|
import { stringifyLogValue } from '../logging.js';
|
|
8
8
|
import { resolveLocalFactoryConfig } from '../config/local-clone-paths.js';
|
|
9
|
-
import { FileStateStore, RelayfileCloudMountClient, checkFactoryLoopLiveness, closeProbePr, createFactory, createFleet, createFactoryCloudEventV1, ensureRelayBroker, FactoryCloudReporter, FileFactoryCloudEventOutbox, defaultGhRunner, explicitLinkedIssueKey, githubIssuePathParts, githubWatchStatePath, isInFactoryScope, parseGithubFactoryIssue, publishFactoryMountHealth, parseOwnedBrokerAgentExitTimeoutMs, parseStandaloneBabysitTarget, readStandalonePullRequest, readLinearIssueWithCanonicalFallback, reapFactoryOrphansOnce, readFactoryLoopHeartbeat, resolveFactoryStates, stateResolutionFromIds, standaloneBabysitterAgentName, renderAgentTask, resolveFactoryWorkspace, } from '../index.js';
|
|
9
|
+
import { FileStateStore, RelayfileCloudMountClient, checkFactoryLoopLiveness, closeProbePr, createFactory, createFleet, createFactoryCloudEventV1, ensureRelayBroker, FactoryCloudReporter, FileFactoryCloudEventOutbox, defaultGhRunner, explicitLinkedIssueKey, githubIssuePathParts, githubWatchStatePath, isInFactoryScope, parseGithubFactoryIssue, publishFactoryMountHealth, parseOwnedBrokerAgentExitTimeoutMs, parseStandaloneBabysitTarget, readStandalonePullRequest, readLinearIssueWithCanonicalFallback, reapFactoryOrphansOnce, reapFactoryEnvironmentsOnce, readFactoryLoopHeartbeat, resolveFactoryStates, stateResolutionFromIds, standaloneBabysitterAgentName, renderAgentTask, resolveFactoryWorkspace, } from '../index.js';
|
|
10
10
|
import { resolveTestGuidance } from '../dispatch/test-guidance.js';
|
|
11
11
|
import { FakeFleetClient, FakeMountClient } from '../testing/index.js';
|
|
12
12
|
import { GitAgentWorktreeManager } from '../git/agent-worktree.js';
|
|
13
13
|
import { checkFeatureMap } from '../featuremap/index.js';
|
|
14
14
|
import { loadOrCreateFactoryInstanceId, resolveFactoryInstanceName } from '../observability/instance-identity.js';
|
|
15
15
|
import { ensureFactoryIntegrations, inspectFactoryIntegration, openIntegrationUrl, } from '../mount/relayfile-integration-preflight.js';
|
|
16
|
+
import { checkMountStaleness } from '../mount/relayfile-binary.js';
|
|
16
17
|
const autoDetectedIssueSources = new WeakSet();
|
|
17
18
|
const CLONE_MOUNT_PREFLIGHT_CONCURRENCY = 4;
|
|
18
19
|
export async function runFleetCli(argv, deps = {}) {
|
|
@@ -89,6 +90,7 @@ export async function runFleetCli(argv, deps = {}) {
|
|
|
89
90
|
capability: command.input.capability,
|
|
90
91
|
node: command.input.node ?? 'self',
|
|
91
92
|
task: command.input.task,
|
|
93
|
+
workflow: command.input.workflow,
|
|
92
94
|
model: command.input.model,
|
|
93
95
|
cwd: command.input.cwd,
|
|
94
96
|
}));
|
|
@@ -109,12 +111,13 @@ export async function runFleetCli(argv, deps = {}) {
|
|
|
109
111
|
if (!loaded)
|
|
110
112
|
throw new Error('factory command requires config');
|
|
111
113
|
if (command.kind === 'factory' && command.action === 'reap-orphans') {
|
|
112
|
-
|
|
114
|
+
const processes = await reapFactoryOrphansOnce({
|
|
113
115
|
heartbeatPath: loaded.config.loop.heartbeatPath,
|
|
114
116
|
registryPath: loaded.config.loop.registryPath,
|
|
115
117
|
staleMs: loaded.config.loop.heartbeatStaleMs,
|
|
116
118
|
fleet,
|
|
117
|
-
})
|
|
119
|
+
});
|
|
120
|
+
writeJson(out, { ...processes, environments: await safeEnvironmentReap(deps.reapEnvironments) });
|
|
118
121
|
return 0;
|
|
119
122
|
}
|
|
120
123
|
// Derive the workspace from the cloud session when it isn't pinned in
|
|
@@ -299,6 +302,9 @@ function parseFleetSubcommand(args) {
|
|
|
299
302
|
throw new Error('factory fleet spawn requires capability spawn:codex, spawn:claude, or workflow:run');
|
|
300
303
|
}
|
|
301
304
|
const parsed = parseFlags(flags);
|
|
305
|
+
if (capability === 'workflow:run' && !parsed.workflow) {
|
|
306
|
+
throw new Error('factory fleet spawn workflow:run requires --workflow <path>');
|
|
307
|
+
}
|
|
302
308
|
return {
|
|
303
309
|
kind: 'spawn',
|
|
304
310
|
input: {
|
|
@@ -306,6 +312,7 @@ function parseFleetSubcommand(args) {
|
|
|
306
312
|
name: parsed.name,
|
|
307
313
|
node: parsed.node,
|
|
308
314
|
task: parsed.task,
|
|
315
|
+
...(parsed.workflow ? { workflow: parsed.workflow } : {}),
|
|
309
316
|
model: parsed.model,
|
|
310
317
|
sessionRef: parsed.resume,
|
|
311
318
|
cwd: parsed.cwd,
|
|
@@ -361,16 +368,18 @@ export function parseGlobalOptions(argv) {
|
|
|
361
368
|
}
|
|
362
369
|
async function runFactoryCommand(command, factory, mount, fleet, config, globals, out, deps = {}, workspaceId = config.workspaceId ?? '', acceptableMountIds) {
|
|
363
370
|
const mountFn = resolveLocalMountFn(deps, mount);
|
|
371
|
+
const mountStderr = deps.stderr ?? process.stderr;
|
|
372
|
+
const debugMountRefreshes = (deps.env ?? process.env).FACTORY_LOG_LEVEL?.toLowerCase() === 'debug';
|
|
364
373
|
if (command.kind === 'factory') {
|
|
365
374
|
if (command.action === 'start') {
|
|
366
375
|
// Local mirrors are a writeback aid, not the source of truth for remote
|
|
367
376
|
// issue discovery. Start their SDK-backed supervisors immediately, but
|
|
368
377
|
// do not serialize durable recovery behind a stale checkout's readiness
|
|
369
378
|
// timeout. The mount client reports degradation and keeps retrying.
|
|
370
|
-
void warmStartPathMounts(mountFn, workspaceId, config, acceptableMountIds)
|
|
379
|
+
void warmStartPathMounts(mountFn, workspaceId, config, acceptableMountIds, mountStderr, debugMountRefreshes)
|
|
371
380
|
.catch((error) => {
|
|
372
381
|
const message = error instanceof Error ? error.message : String(error);
|
|
373
|
-
|
|
382
|
+
mountStderr.write(`[factory] warning: background relayfile mount warmup failed: ${message}\n`);
|
|
374
383
|
});
|
|
375
384
|
const waiter = createStopSignalWaiter();
|
|
376
385
|
let stoppedBySignal = false;
|
|
@@ -405,7 +414,7 @@ async function runFactoryCommand(command, factory, mount, fleet, config, globals
|
|
|
405
414
|
}
|
|
406
415
|
}
|
|
407
416
|
if (command.action === 'run-once') {
|
|
408
|
-
await ensureClonePathMounts(mountFn, workspaceId, config, acceptableMountIds);
|
|
417
|
+
await ensureClonePathMounts(mountFn, workspaceId, config, acceptableMountIds, mountStderr, debugMountRefreshes);
|
|
409
418
|
writeJson(out, await factory.runOnce({ dryRun: globals.dryRun }));
|
|
410
419
|
return 0;
|
|
411
420
|
}
|
|
@@ -427,16 +436,7 @@ async function runFactoryCommand(command, factory, mount, fleet, config, globals
|
|
|
427
436
|
writeJson(out, { killed: heartbeat.pid, signal: 'SIGTERM' });
|
|
428
437
|
return 0;
|
|
429
438
|
}
|
|
430
|
-
|
|
431
|
-
writeJson(out, await reapFactoryOrphansOnce({
|
|
432
|
-
heartbeatPath: config.loop.heartbeatPath,
|
|
433
|
-
registryPath: config.loop.registryPath,
|
|
434
|
-
staleMs: config.loop.heartbeatStaleMs,
|
|
435
|
-
fleet,
|
|
436
|
-
}));
|
|
437
|
-
return 0;
|
|
438
|
-
}
|
|
439
|
-
await ensureClonePathMounts(mountFn, workspaceId, config, acceptableMountIds);
|
|
439
|
+
await ensureClonePathMounts(mountFn, workspaceId, config, acceptableMountIds, mountStderr, debugMountRefreshes);
|
|
440
440
|
const removeSignalHandlers = installFactoryStopSignalHandlers(factory, {
|
|
441
441
|
processLike: deps.stopSignalProcessLike,
|
|
442
442
|
});
|
|
@@ -480,22 +480,13 @@ async function runFactoryCommand(command, factory, mount, fleet, config, globals
|
|
|
480
480
|
writeJson(out, await factory.dispatch(decision, { dryRun: globals.dryRun }));
|
|
481
481
|
return 0;
|
|
482
482
|
}
|
|
483
|
-
async function warmStartPathMounts(mountFn, workspaceId, config, acceptableMountIds) {
|
|
484
|
-
const
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
});
|
|
489
|
-
}
|
|
490
|
-
catch (error) {
|
|
491
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
492
|
-
process.stderr.write(`[factory] warning: could not start relayfile mount at ${process.cwd()}: ${message}\n`);
|
|
493
|
-
}
|
|
494
|
-
})();
|
|
495
|
-
await Promise.all([
|
|
496
|
-
daemonMount,
|
|
497
|
-
ensureClonePathMounts(mountFn, workspaceId, config, acceptableMountIds),
|
|
483
|
+
async function warmStartPathMounts(mountFn, workspaceId, config, acceptableMountIds, stderr = process.stderr, debug = process.env.FACTORY_LOG_LEVEL?.toLowerCase() === 'debug') {
|
|
484
|
+
const mountOpts = { acceptableWorkspaceIds: acceptableMountIds };
|
|
485
|
+
const [daemonRefresh, cloneRefreshes] = await Promise.all([
|
|
486
|
+
ensureMountPath(mountFn, workspaceId, process.cwd(), mountOpts, stderr),
|
|
487
|
+
ensureClonePathMounts(mountFn, workspaceId, config, acceptableMountIds, stderr, debug, false),
|
|
498
488
|
]);
|
|
489
|
+
writeMountRefreshSummary([...(daemonRefresh ? [daemonRefresh] : []), ...cloneRefreshes], stderr, debug);
|
|
499
490
|
}
|
|
500
491
|
async function runStandaloneBabysitCommand(command, mount, fleet, config, globals, out, deps, workspaceId, acceptableMountIds) {
|
|
501
492
|
const repo = resolveStandaloneBabysitRepo(command.repo, config);
|
|
@@ -646,25 +637,60 @@ function standaloneBabysitClonePath(repo, config) {
|
|
|
646
637
|
* is not automatically accessible from a different directory, and agents need
|
|
647
638
|
* these paths for integration writebacks (Slack, GitHub, etc.).
|
|
648
639
|
*/
|
|
649
|
-
async function ensureClonePathMounts(mountFn, workspaceId, config, acceptableMountIds) {
|
|
640
|
+
async function ensureClonePathMounts(mountFn, workspaceId, config, acceptableMountIds, stderr = process.stderr, debug = process.env.FACTORY_LOG_LEVEL?.toLowerCase() === 'debug', reportSummary = true) {
|
|
650
641
|
const mountOpts = { acceptableWorkspaceIds: acceptableMountIds };
|
|
651
642
|
const daemonCwd = resolve(process.cwd());
|
|
652
643
|
const clonePaths = [...new Set(Object.values(config.clonePaths ?? {}).map((clonePath) => resolve(clonePath)))]
|
|
653
644
|
.filter((clonePath) => clonePath !== daemonCwd);
|
|
645
|
+
const refreshedStaleMounts = [];
|
|
654
646
|
let nextIndex = 0;
|
|
655
647
|
const mountNext = async () => {
|
|
656
648
|
while (nextIndex < clonePaths.length) {
|
|
657
649
|
const resolved = clonePaths[nextIndex++];
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
catch (error) {
|
|
662
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
663
|
-
process.stderr.write(`[factory] warning: could not start relayfile mount at ${resolved}: ${message}\n`);
|
|
664
|
-
}
|
|
650
|
+
const refreshed = await ensureMountPath(mountFn, workspaceId, resolved, mountOpts, stderr);
|
|
651
|
+
if (refreshed)
|
|
652
|
+
refreshedStaleMounts.push(refreshed);
|
|
665
653
|
}
|
|
666
654
|
};
|
|
667
655
|
await Promise.all(Array.from({ length: Math.min(CLONE_MOUNT_PREFLIGHT_CONCURRENCY, clonePaths.length) }, mountNext));
|
|
656
|
+
if (reportSummary)
|
|
657
|
+
writeMountRefreshSummary(refreshedStaleMounts, stderr, debug);
|
|
658
|
+
return refreshedStaleMounts;
|
|
659
|
+
}
|
|
660
|
+
async function ensureMountPath(mountFn, workspaceId, path, mountOpts, stderr) {
|
|
661
|
+
const resolved = resolve(path);
|
|
662
|
+
const statePath = join(resolved, '.integrations', '.relay', 'state.json');
|
|
663
|
+
const staleBefore = checkMountStaleness(statePath, workspaceId, mountOpts.acceptableWorkspaceIds);
|
|
664
|
+
try {
|
|
665
|
+
await mountFn(workspaceId, resolved, {
|
|
666
|
+
...mountOpts,
|
|
667
|
+
...(staleBefore.stale ? { suppressStaleRefreshLogs: true } : {}),
|
|
668
|
+
});
|
|
669
|
+
if (staleBefore.stale && !checkMountStaleness(statePath, workspaceId, mountOpts.acceptableWorkspaceIds).stale) {
|
|
670
|
+
return { path: resolved, reason: staleBefore.reason };
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
catch (error) {
|
|
674
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
675
|
+
stderr.write(`[factory] warning: could not start relayfile mount at ${resolved}: ${message}\n`);
|
|
676
|
+
}
|
|
677
|
+
return undefined;
|
|
678
|
+
}
|
|
679
|
+
function writeMountRefreshSummary(refreshedStaleMounts, stderr, debug) {
|
|
680
|
+
if (debug) {
|
|
681
|
+
for (const mount of [...refreshedStaleMounts].sort((left, right) => left.path.localeCompare(right.path))) {
|
|
682
|
+
const suffix = mount.reason ? ` (${mount.reason})` : '';
|
|
683
|
+
stderr.write(`[factory] debug: refreshed stale local mount at ${mount.path}${suffix}\n`);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
if (refreshedStaleMounts.length > 0) {
|
|
687
|
+
const ages = refreshedStaleMounts
|
|
688
|
+
.map(({ reason }) => reason?.match(/^last reconcile (\d+)m ago$/u)?.[1])
|
|
689
|
+
.filter((age) => age !== undefined)
|
|
690
|
+
.map(Number);
|
|
691
|
+
const ageSuffix = ages.length > 0 ? ` (last reconcile ~${Math.max(...ages)}m ago)` : '';
|
|
692
|
+
stderr.write(`[factory] refreshed ${refreshedStaleMounts.length} stale local mount(s)${ageSuffix}\n`);
|
|
693
|
+
}
|
|
668
694
|
}
|
|
669
695
|
function resolveLocalMountFn(deps, mount) {
|
|
670
696
|
if (deps.ensureLocalMount)
|
|
@@ -860,7 +886,7 @@ async function buildFleet(globals, loaded, deps) {
|
|
|
860
886
|
// An injected createFleet owns fleet construction entirely (tests), so skip the
|
|
861
887
|
// real broker bootstrap.
|
|
862
888
|
if (deps.createFleet) {
|
|
863
|
-
return deps.createFleet({ backend: globals.backend, cwd, connectionPath }, { ownedBrokerAgentExitTimeoutMs: globals.agentExitTimeoutMs });
|
|
889
|
+
return deps.createFleet({ backend: globals.backend, cwd, connectionPath, previewConfig: loaded?.config.preview }, { ownedBrokerAgentExitTimeoutMs: globals.agentExitTimeoutMs });
|
|
864
890
|
}
|
|
865
891
|
// The internal backend talks to a relay broker. Reuse the one already running
|
|
866
892
|
// for this workspace, or start one if none is up, so `factory <run-once|loop>`
|
|
@@ -875,7 +901,12 @@ async function buildFleet(globals, loaded, deps) {
|
|
|
875
901
|
logger,
|
|
876
902
|
env,
|
|
877
903
|
});
|
|
878
|
-
|
|
904
|
+
if (started && connectionPath === undefined) {
|
|
905
|
+
stderr.write(`[factory] no existing relay broker connection found under ${cwd} (.agentworkforce/relay/connection.json);\n` +
|
|
906
|
+
'starting a NEW broker. If you expected to attach to an already-running Factory, you are likely running\n' +
|
|
907
|
+
'from the wrong directory — cd to the checkout that owns .agentworkforce/relay/ and retry.\n');
|
|
908
|
+
}
|
|
909
|
+
return createFleet({ backend: 'internal', cwd, connectionPath, previewConfig: loaded?.config.preview }, {
|
|
879
910
|
harnessClient: client,
|
|
880
911
|
ownsBroker: started,
|
|
881
912
|
ownedBrokerAgentExitTimeoutMs: globals.agentExitTimeoutMs,
|
|
@@ -883,7 +914,7 @@ async function buildFleet(globals, loaded, deps) {
|
|
|
883
914
|
logger,
|
|
884
915
|
});
|
|
885
916
|
}
|
|
886
|
-
return createFleet({ backend: globals.backend, cwd, connectionPath }, { env: deps.env });
|
|
917
|
+
return createFleet({ backend: globals.backend, cwd, connectionPath, previewConfig: loaded?.config.preview }, { env: deps.env });
|
|
887
918
|
}
|
|
888
919
|
function streamLogger(stream) {
|
|
889
920
|
const write = (message, args) => {
|
|
@@ -1274,6 +1305,20 @@ function requireValue(args, index, flag) {
|
|
|
1274
1305
|
function writeJson(out, value) {
|
|
1275
1306
|
out.write(`${JSON.stringify(value, null, 2)}\n`);
|
|
1276
1307
|
}
|
|
1308
|
+
async function safeEnvironmentReap(reap = reapFactoryEnvironmentsOnce) {
|
|
1309
|
+
try {
|
|
1310
|
+
return await reap();
|
|
1311
|
+
}
|
|
1312
|
+
catch (error) {
|
|
1313
|
+
// Process cleanup remains useful on hosts without kubectl or an active
|
|
1314
|
+
// cluster. Surface the skipped backstop without failing the whole command.
|
|
1315
|
+
return {
|
|
1316
|
+
reaped: [],
|
|
1317
|
+
retained: [],
|
|
1318
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1319
|
+
};
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1277
1322
|
async function flushProcessOutput() {
|
|
1278
1323
|
await Promise.all([
|
|
1279
1324
|
flushWritable(process.stdout),
|