@moretea-labs/forge 1.7.0 → 1.7.1
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/AGENTS.md +2 -0
- package/CHANGELOG.md +13 -0
- package/CLAUDE.md +2 -0
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/agents/openai.yaml +1 -1
- package/assets/partials-agents/02-operating-mode.partial.md +2 -0
- package/assets/plugin-registry.v1.json +3 -3
- package/assets/skill-version.json +2 -2
- package/assets/templates/helpers/check-architecture-sync.sh +17 -0
- package/assets/templates/helpers/inspect-project-state.ts +8 -6
- package/assets/templates/helpers/migrate-workflow-docs.ts +9 -7
- package/assets/templates/helpers/workflow-contract.ts +5 -1
- package/docs/architecture/CURRENT.md +12 -4
- package/docs/forge-plugin-management.md +1 -1
- package/docs/operations/controller-performance-and-502.md +19 -0
- package/docs/operations/homebrew.md +2 -2
- package/package.json +5 -2
- package/scripts/architecture-event.ts +2 -820
- package/scripts/architecture-queue.sh +2 -645
- package/scripts/archive-architecture-request.sh +2 -242
- package/scripts/archive-workflow.sh +1 -291
- package/scripts/benchmark-work-efficiency.ts +0 -0
- package/scripts/capability-config.ts +2 -392
- package/scripts/capability-resolver.ts +2 -503
- package/scripts/capture-plan.sh +1 -332
- package/scripts/check-agent-tooling.sh +2 -1531
- package/scripts/check-architecture-sync.sh +2 -302
- package/scripts/check-brain-manifest.sh +2 -253
- package/scripts/check-context-files.sh +2 -47
- package/scripts/check-deploy-sql-order.sh +2 -115
- package/scripts/check-runtime-architecture.mjs +243 -16
- package/scripts/check-skill-version.ts +3 -191
- package/scripts/check-source-duplication.mjs +61 -0
- package/scripts/check-stable-baseline.ts +0 -0
- package/scripts/check-task-sync.sh +2 -63
- package/scripts/check-task-workflow.sh +2 -1000
- package/scripts/codex-handoff-resume.sh +2 -267
- package/scripts/context-contract-sync.sh +2 -514
- package/scripts/contract-run.ts +2 -463
- package/scripts/contract-worktree.sh +1 -702
- package/scripts/ensure-task-workflow.sh +1 -1192
- package/scripts/inspect-project-state.ts +4 -279
- package/scripts/lib/project-init-lib.sh +7 -2
- package/scripts/lib/run-forge-helper.sh +19 -0
- package/scripts/lib/run-forge-helper.ts +26 -0
- package/scripts/migrate-project-template.sh +1 -33
- package/scripts/migrate-workflow-docs.ts +4 -412
- package/scripts/new-plan.sh +1 -180
- package/scripts/new-spec.sh +1 -47
- package/scripts/new-sprint.sh +1 -46
- package/scripts/plan-to-todo.sh +1 -771
- package/scripts/prepare-codex-handoff.sh +2 -201
- package/scripts/prepare-handoff.sh +1 -78
- package/scripts/refresh-current-status.sh +2 -467
- package/scripts/run-governed-gate.ts +3 -0
- package/scripts/select-agent-context-blocks.sh +2 -64
- package/scripts/ship-worktrees.sh +1 -531
- package/scripts/sprint-backlog.sh +1 -834
- package/scripts/stage-runtime-release.ts +2 -1
- package/scripts/summarize-failures.sh +2 -123
- package/scripts/switch-plan.sh +1 -150
- package/scripts/sync-brain-docs.sh +2 -379
- package/scripts/sync-generated-authority.mjs +175 -0
- package/scripts/verify-contract.sh +2 -593
- package/scripts/verify-sprint.sh +1 -500
- package/scripts/workflow-contract.ts +4 -201
- package/scripts/workstream-sync.sh +2 -261
- package/src/cli/agent-jobs/executor-health.ts +1 -1
- package/src/cli/commands/plugin.ts +12 -3
- package/src/cli/controller/check-runner.ts +7 -2
- package/src/cli/controller/context-pack.ts +46 -21
- package/src/cli/github/contracts.ts +16 -0
- package/src/cli/github/github.ts +2 -16
- package/src/cli/github/session.ts +2 -1
- package/src/cli/local-bridge/facade-api.ts +37 -47
- package/src/cli/local-bridge/server.ts +2 -2
- package/src/cli/mcp/legacy-tool-service.ts +10 -33
- package/src/cli/mcp/post-finalize-work-attribution.ts +150 -0
- package/src/cli/mcp/repository-tools.ts +85 -89
- package/src/cli/mcp/server.ts +57 -13
- package/src/cli/mcp/tool-contract.ts +14 -0
- package/src/cli/mcp/toolset.ts +2 -2
- package/src/cli/mcp/transports/http.ts +23 -0
- package/src/cli/repositories/command-classifier.ts +2 -2
- package/src/cli/repositories/command-executor.ts +24 -6
- package/src/cli/repositories/repository-snapshot.ts +5 -2
- package/src/cli/repositories/structured-git.ts +57 -3
- package/src/cli/repository/inspector.ts +11 -3
- package/src/runtime/context/generic-lsp-provider.ts +1 -1
- package/src/runtime/context/semantic-navigation-contract.ts +61 -0
- package/src/runtime/context/semantic-navigation.ts +9 -61
- package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +18 -25
- package/src/runtime/control-plane/execution/repository-work-attribution.ts +106 -0
- package/src/runtime/control-plane/execution/retained-work-resume.ts +203 -0
- package/src/runtime/control-plane/execution/work-completion-authority.ts +58 -0
- package/src/runtime/control-plane/execution/work-evidence-policy.ts +200 -0
- package/src/runtime/control-plane/execution/work-execution-support.ts +386 -0
- package/src/runtime/control-plane/execution/work-finalization-service.ts +1913 -0
- package/src/runtime/control-plane/execution/work-handle-authority.ts +96 -0
- package/src/runtime/control-plane/execution/work-handle-store.ts +1 -1
- package/src/runtime/control-plane/execution/work-operation-service.ts +468 -0
- package/src/runtime/control-plane/execution/work-path-scope.ts +37 -0
- package/src/runtime/control-plane/execution/work-preparation-service.ts +479 -0
- package/src/runtime/control-plane/execution/work-process-evidence.ts +129 -0
- package/src/runtime/control-plane/execution/work-task-receipt.ts +4 -43
- package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +13 -3
- package/src/runtime/control-plane/execution/work-verification-context.ts +56 -0
- package/src/runtime/control-plane/execution/work-verification-service.ts +388 -0
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +5 -1
- package/src/runtime/control-plane/facade/controller-round-relay.ts +164 -37
- package/src/runtime/control-plane/facade/controller-session-store.ts +183 -24
- package/src/runtime/control-plane/facade/goal-workloop-access.ts +15 -1
- package/src/runtime/control-plane/facade/goal-workloop.ts +329 -148
- package/src/runtime/control-plane/facade/index.ts +2 -0
- package/src/runtime/control-plane/facade/plan-contract-store.ts +90 -22
- package/src/runtime/control-plane/facade/repository-work-admission.ts +147 -0
- package/src/runtime/control-plane/facade/requirement-authority.ts +83 -0
- package/src/runtime/control-plane/facade/types.ts +68 -5
- package/src/runtime/control-plane/facade/work-contract-store.ts +3 -209
- package/src/runtime/control-plane/facade/work-state-machine.ts +250 -0
- package/src/runtime/control-plane/global-scheduler/maintenance.ts +14 -7
- package/src/runtime/control-plane/global-scheduler/scheduler.ts +5 -1
- package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +13 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +15 -6
- package/src/runtime/control-plane/launcher/launch-reservation-store.ts +54 -5
- package/src/runtime/control-plane/launcher/thin-launcher.ts +56 -7
- package/src/runtime/control-plane/persistence/requirement-store.ts +0 -12
- package/src/runtime/control-plane/repo-actor/registry.ts +4 -2
- package/src/runtime/control-plane/routing/route-policy.ts +18 -6
- package/src/runtime/control-plane/routing/workspace-admission.ts +70 -0
- package/src/runtime/evidence/process-check-execution.ts +18 -0
- package/src/runtime/execution/jobs/child-reference.ts +6 -2
- package/src/runtime/execution/process-runtime/check-result.ts +53 -18
- package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +7 -1
- package/src/runtime/execution/process-runtime/command-facade.ts +39 -17
- package/src/runtime/execution/process-runtime/lightweight-managed.ts +20 -3
- package/src/runtime/execution/process-runtime/persisted-check.ts +4 -1
- package/src/runtime/execution/process-runtime/runtime.ts +10 -0
- package/src/runtime/execution/process-runtime/types.ts +2 -12
- package/src/runtime/execution/work-activity.ts +21 -0
- package/src/runtime/execution/workers/executor.ts +18 -13
- package/src/runtime/gateway/mcp/execution-tools.ts +35 -2930
- package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +61 -0
- package/src/runtime/gateway/mcp/router.ts +2 -2
- package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +21 -2
- package/src/runtime/gateway/mcp/runtime-tools.ts +805 -1009
- package/src/runtime/plugins/app-store-connect-adapter.ts +93 -28
- package/src/runtime/plugins/browser-adapter.ts +256 -1083
- package/src/runtime/plugins/browser-handoff-host.ts +2 -25
- package/src/runtime/plugins/browser-handoff.ts +18 -3
- package/src/runtime/plugins/browser-macos-bridge.ts +121 -16
- package/src/runtime/plugins/browser-manifest-surface.ts +603 -0
- package/src/runtime/plugins/browser-native-session-service.ts +222 -0
- package/src/runtime/plugins/browser-runtime-contract.ts +2 -0
- package/src/runtime/plugins/browser-runtime.ts +10 -0
- package/src/runtime/plugins/browser-session-store.ts +121 -0
- package/src/runtime/plugins/browser-session-types.ts +98 -0
- package/src/runtime/plugins/desktop-operator-registration.ts +4 -4
- package/src/runtime/plugins/external-adapter.ts +92 -58
- package/src/runtime/plugins/macos-capability-broker.ts +0 -8
- package/src/runtime/plugins/store.ts +66 -9
- package/src/runtime/plugins/types.ts +3 -1
- package/src/runtime/recovery/maintenance-executor.ts +61 -25
- package/src/runtime/root/package-connector-service.ts +46 -4
- package/src/runtime/root/package-runtime-release.ts +1 -1
- package/src/runtime/root/package-runtime-service.ts +33 -2
- package/src/runtime/root/release-manifest.ts +137 -16
- package/src/runtime/root/release-materialize.ts +109 -35
- package/src/runtime/root/types.ts +10 -0
- package/src/runtime/standalone-recovery/core.ts +425 -194
- package/src/runtime/standalone-recovery/entry.ts +1 -1
- package/src/runtime/standalone-recovery/installer.ts +1 -1
- package/src/runtime/workflow/schedules/engine.ts +113 -87
- package/src/runtime/workflow/schedules/settlement.ts +26 -23
- package/src/runtime/workflow/schedules/store.ts +79 -10
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createRequire } from 'module';
|
|
2
|
-
import {
|
|
2
|
+
import { rmSync } from 'fs';
|
|
3
3
|
import { basename } from 'path';
|
|
4
4
|
import { spawnSync } from 'child_process';
|
|
5
|
-
import { readJsonFile
|
|
5
|
+
import { readJsonFile } from '../shared/json-files';
|
|
6
6
|
import {
|
|
7
7
|
interactionCommandPath,
|
|
8
8
|
patchInteractionSession,
|
|
@@ -100,19 +100,7 @@ async function runNativeHandoff(specPath: string, spec: BrowserHandoffLaunchSpec
|
|
|
100
100
|
removeInteractionCommand(spec.repoRoot, 'browser', spec.interactionId, 'resume');
|
|
101
101
|
metadata = await readMacOsBrowserOwnedTabMetadata(native.product, native.ref, spec.defaultTimeoutMs);
|
|
102
102
|
assertHttpUrl(metadata.url);
|
|
103
|
-
const existing = existsSync(spec.sessionPath)
|
|
104
|
-
? readJsonFile<Record<string, unknown>>(spec.sessionPath, {})
|
|
105
|
-
: {};
|
|
106
|
-
const timestamp = new Date().toISOString();
|
|
107
103
|
const result = { url: metadata.url, title: metadata.title };
|
|
108
|
-
writeJsonAtomic(spec.sessionPath, {
|
|
109
|
-
...existing,
|
|
110
|
-
schemaVersion: 1,
|
|
111
|
-
sessionId: spec.sessionId,
|
|
112
|
-
...result,
|
|
113
|
-
createdAt: typeof existing.createdAt === 'string' ? existing.createdAt : timestamp,
|
|
114
|
-
updatedAt: timestamp,
|
|
115
|
-
});
|
|
116
104
|
terminalOutcome = { status: 'completed', result };
|
|
117
105
|
patchInteractionSession(spec.repoRoot, 'browser', spec.interactionId, { status: 'closing', result });
|
|
118
106
|
break;
|
|
@@ -239,18 +227,7 @@ async function main(): Promise<void> {
|
|
|
239
227
|
if (resume) {
|
|
240
228
|
removeInteractionCommand(spec.repoRoot, 'browser', spec.interactionId, 'resume');
|
|
241
229
|
assertHttpUrl(page.url());
|
|
242
|
-
const existing = existsSync(spec.sessionPath)
|
|
243
|
-
? readJsonFile<Record<string, unknown>>(spec.sessionPath, {})
|
|
244
|
-
: {};
|
|
245
|
-
const timestamp = new Date().toISOString();
|
|
246
230
|
const result = { url: page.url(), title: await page.title() };
|
|
247
|
-
writeJsonAtomic(spec.sessionPath, {
|
|
248
|
-
schemaVersion: 1,
|
|
249
|
-
sessionId: spec.sessionId,
|
|
250
|
-
...result,
|
|
251
|
-
createdAt: typeof existing.createdAt === 'string' ? existing.createdAt : timestamp,
|
|
252
|
-
updatedAt: timestamp,
|
|
253
|
-
});
|
|
254
231
|
terminalOutcome = { status: 'completed', result };
|
|
255
232
|
patchInteractionSession(spec.repoRoot, 'browser', spec.interactionId, { status: 'closing', result });
|
|
256
233
|
break;
|
|
@@ -32,7 +32,6 @@ export interface BrowserHandoffLaunchSpec {
|
|
|
32
32
|
repoRoot: string;
|
|
33
33
|
interactionId: string;
|
|
34
34
|
sessionId: string;
|
|
35
|
-
sessionPath: string;
|
|
36
35
|
url: string;
|
|
37
36
|
profileDir: string;
|
|
38
37
|
selectedProfilePath: string;
|
|
@@ -50,7 +49,6 @@ export interface BrowserHandoffStartInput {
|
|
|
50
49
|
requestId: string;
|
|
51
50
|
jobId?: string;
|
|
52
51
|
sessionId: string;
|
|
53
|
-
sessionPath: string;
|
|
54
52
|
url: string;
|
|
55
53
|
profileDir: string;
|
|
56
54
|
selectedProfilePath: string;
|
|
@@ -407,7 +405,6 @@ export async function startBrowserHandoff(input: BrowserHandoffStartInput): Prom
|
|
|
407
405
|
repoRoot: input.repoRoot,
|
|
408
406
|
interactionId,
|
|
409
407
|
sessionId: input.sessionId,
|
|
410
|
-
sessionPath: input.sessionPath,
|
|
411
408
|
url: input.url,
|
|
412
409
|
profileDir: input.profileDir,
|
|
413
410
|
selectedProfilePath: input.selectedProfilePath,
|
|
@@ -443,6 +440,24 @@ export function resumeBrowserHandoff(repoRoot: string, interactionId: string, re
|
|
|
443
440
|
return current;
|
|
444
441
|
}
|
|
445
442
|
|
|
443
|
+
export async function resumeBrowserHandoffAndWait(
|
|
444
|
+
repoRoot: string,
|
|
445
|
+
interactionId: string,
|
|
446
|
+
requestedBy: string,
|
|
447
|
+
timeoutMs = 5_000,
|
|
448
|
+
): Promise<InteractionSessionRecord> {
|
|
449
|
+
const initial = resumeBrowserHandoff(repoRoot, interactionId, requestedBy);
|
|
450
|
+
if (!isInteractionSessionActive(initial.status)) return initial;
|
|
451
|
+
const deadline = Date.now() + Math.max(0, Math.trunc(timeoutMs));
|
|
452
|
+
let latest = initial;
|
|
453
|
+
while (Date.now() < deadline) {
|
|
454
|
+
await hooks.wait(STARTUP_POLL_MS);
|
|
455
|
+
latest = getBrowserHandoff(repoRoot, interactionId);
|
|
456
|
+
if (!isInteractionSessionActive(latest.status)) return latest;
|
|
457
|
+
}
|
|
458
|
+
return latest;
|
|
459
|
+
}
|
|
460
|
+
|
|
446
461
|
export function cancelBrowserHandoff(repoRoot: string, interactionId: string, requestedBy: string): InteractionSessionRecord {
|
|
447
462
|
const current = getBrowserHandoff(repoRoot, interactionId);
|
|
448
463
|
if (!isInteractionSessionActive(current.status) || current.status === 'closing') return current;
|
|
@@ -9,12 +9,13 @@ import {
|
|
|
9
9
|
type BrowserAutomationTrustedInput,
|
|
10
10
|
} from './browser-automation-service';
|
|
11
11
|
import { AssistantPluginError } from './errors';
|
|
12
|
+
import type { BrowserNativeAttachAttempt, BrowserNativeProduct } from './browser-session-types';
|
|
12
13
|
|
|
13
14
|
const RECORD_SEPARATOR = String.fromCharCode(30);
|
|
14
15
|
const DEFAULT_NATIVE_TIMEOUT_MS = 5_000;
|
|
15
16
|
const MAX_NATIVE_OUTPUT_BYTES = 1_048_576;
|
|
16
17
|
|
|
17
|
-
export type MacOsBrowserProduct =
|
|
18
|
+
export type MacOsBrowserProduct = BrowserNativeProduct;
|
|
18
19
|
|
|
19
20
|
interface MacOsBrowserDefinition {
|
|
20
21
|
product: MacOsBrowserProduct;
|
|
@@ -29,6 +30,20 @@ export interface MacOsBrowserTabRef {
|
|
|
29
30
|
tabId: string;
|
|
30
31
|
}
|
|
31
32
|
|
|
33
|
+
export interface MacOsBrowserCreateTabNavigationProvenance {
|
|
34
|
+
provenanceVersion: 1;
|
|
35
|
+
requestedUrl: string;
|
|
36
|
+
assignmentAccepted: true;
|
|
37
|
+
acceptedBy?: string;
|
|
38
|
+
observedUrlAfterAssignment?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface MacOsBrowserCreateTabEvidence {
|
|
42
|
+
ref: MacOsBrowserTabRef;
|
|
43
|
+
refSource: 'structured' | 'legacy_value';
|
|
44
|
+
navigation?: MacOsBrowserCreateTabNavigationProvenance;
|
|
45
|
+
}
|
|
46
|
+
|
|
32
47
|
export interface MacOsBrowserMetadata {
|
|
33
48
|
product: MacOsBrowserProduct;
|
|
34
49
|
appName: string;
|
|
@@ -57,14 +72,7 @@ export interface MacOsBrowserTabInventory {
|
|
|
57
72
|
truncated: boolean;
|
|
58
73
|
}
|
|
59
74
|
|
|
60
|
-
export
|
|
61
|
-
product: MacOsBrowserProduct;
|
|
62
|
-
appName: string;
|
|
63
|
-
bundleId: string;
|
|
64
|
-
status: 'selected' | 'available' | 'not_installed' | 'not_running' | 'unavailable';
|
|
65
|
-
frontmost?: boolean;
|
|
66
|
-
error?: string;
|
|
67
|
-
}
|
|
75
|
+
export type MacOsBrowserAttachAttempt = BrowserNativeAttachAttempt;
|
|
68
76
|
|
|
69
77
|
export interface MacOsBrowserAttachment {
|
|
70
78
|
metadata: MacOsBrowserMetadata;
|
|
@@ -132,6 +140,87 @@ const defaultRuntimeHooks: MacOsBrowserRuntimeHooks = {
|
|
|
132
140
|
},
|
|
133
141
|
};
|
|
134
142
|
|
|
143
|
+
export function parseMacOsBrowserCreateTabBrokerResult(result: Record<string, unknown>): MacOsBrowserCreateTabEvidence {
|
|
144
|
+
const structuredRef = result.ref;
|
|
145
|
+
let ref: MacOsBrowserTabRef;
|
|
146
|
+
let refSource: MacOsBrowserCreateTabEvidence['refSource'];
|
|
147
|
+
if (structuredRef !== undefined) {
|
|
148
|
+
if (!structuredRef || typeof structuredRef !== 'object' || Array.isArray(structuredRef)) {
|
|
149
|
+
throw new AssistantPluginError('PLUGIN_MACOS_CAPABILITY_BROKER_PROTOCOL_ERROR', 'Stable Forge macOS capability broker returned an invalid create-tab ref.', { retryable: true });
|
|
150
|
+
}
|
|
151
|
+
const record = structuredRef as Record<string, unknown>;
|
|
152
|
+
if (typeof record.windowId !== 'string' || !record.windowId.trim() || typeof record.tabId !== 'string' || !record.tabId.trim()) {
|
|
153
|
+
throw new AssistantPluginError('PLUGIN_MACOS_CAPABILITY_BROKER_PROTOCOL_ERROR', 'Stable Forge macOS capability broker returned an incomplete create-tab ref.', { retryable: true });
|
|
154
|
+
}
|
|
155
|
+
ref = { windowId: record.windowId, tabId: record.tabId };
|
|
156
|
+
refSource = 'structured';
|
|
157
|
+
if (typeof result.value === 'string') {
|
|
158
|
+
const legacyParts = result.value.split(RECORD_SEPARATOR);
|
|
159
|
+
if (legacyParts.length >= 2) {
|
|
160
|
+
const legacyRef = {
|
|
161
|
+
windowId: parseBrowserId(legacyParts[0] ?? '', 'window id'),
|
|
162
|
+
tabId: parseBrowserId(legacyParts[1] ?? '', 'tab id'),
|
|
163
|
+
};
|
|
164
|
+
if (legacyRef.windowId !== ref.windowId || legacyRef.tabId !== ref.tabId) {
|
|
165
|
+
throw new AssistantPluginError('PLUGIN_MACOS_CAPABILITY_BROKER_PROTOCOL_ERROR', 'Stable Forge macOS capability broker returned conflicting structured and legacy create-tab refs.', { retryable: true });
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
} else {
|
|
170
|
+
if (typeof result.value !== 'string') {
|
|
171
|
+
throw new AssistantPluginError('PLUGIN_MACOS_CAPABILITY_BROKER_PROTOCOL_ERROR', 'Stable Forge macOS capability broker returned no create-tab ref.', { retryable: true });
|
|
172
|
+
}
|
|
173
|
+
const parts = result.value.split(RECORD_SEPARATOR);
|
|
174
|
+
if (parts.length < 2) throw new AssistantPluginError('PLUGIN_MACOS_CAPABILITY_BROKER_PROTOCOL_ERROR', 'Stable Forge macOS capability broker returned incomplete legacy create-tab metadata.', { retryable: true });
|
|
175
|
+
ref = { windowId: parseBrowserId(parts[0] ?? '', 'window id'), tabId: parseBrowserId(parts[1] ?? '', 'tab id') };
|
|
176
|
+
refSource = 'legacy_value';
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const navigationValue = result.navigation;
|
|
180
|
+
if (navigationValue === undefined) return { ref, refSource };
|
|
181
|
+
if (!navigationValue || typeof navigationValue !== 'object' || Array.isArray(navigationValue)) {
|
|
182
|
+
throw new AssistantPluginError('PLUGIN_MACOS_CAPABILITY_BROKER_PROTOCOL_ERROR', 'Stable Forge macOS capability broker returned invalid create-tab navigation provenance.', { retryable: true });
|
|
183
|
+
}
|
|
184
|
+
const navigation = navigationValue as Record<string, unknown>;
|
|
185
|
+
if (navigation.provenanceVersion !== 1
|
|
186
|
+
|| typeof navigation.requestedUrl !== 'string'
|
|
187
|
+
|| navigation.assignmentAccepted !== true) {
|
|
188
|
+
throw new AssistantPluginError('PLUGIN_MACOS_CAPABILITY_BROKER_PROTOCOL_ERROR', 'Stable Forge macOS capability broker returned incomplete create-tab navigation provenance.', { retryable: true });
|
|
189
|
+
}
|
|
190
|
+
if (navigation.acceptedBy !== undefined && typeof navigation.acceptedBy !== 'string') {
|
|
191
|
+
throw new AssistantPluginError('PLUGIN_MACOS_CAPABILITY_BROKER_PROTOCOL_ERROR', 'Stable Forge macOS capability broker returned invalid create-tab acceptedBy provenance.', { retryable: true });
|
|
192
|
+
}
|
|
193
|
+
if (navigation.observedUrlAfterAssignment !== undefined && typeof navigation.observedUrlAfterAssignment !== 'string') {
|
|
194
|
+
throw new AssistantPluginError('PLUGIN_MACOS_CAPABILITY_BROKER_PROTOCOL_ERROR', 'Stable Forge macOS capability broker returned invalid create-tab observed URL provenance.', { retryable: true });
|
|
195
|
+
}
|
|
196
|
+
const acceptedBy = typeof navigation.acceptedBy === 'string' ? navigation.acceptedBy : undefined;
|
|
197
|
+
const observedUrlAfterAssignment = typeof navigation.observedUrlAfterAssignment === 'string' ? navigation.observedUrlAfterAssignment : undefined;
|
|
198
|
+
return {
|
|
199
|
+
ref,
|
|
200
|
+
refSource,
|
|
201
|
+
navigation: {
|
|
202
|
+
provenanceVersion: 1,
|
|
203
|
+
requestedUrl: navigation.requestedUrl,
|
|
204
|
+
assignmentAccepted: true,
|
|
205
|
+
...(acceptedBy ? { acceptedBy } : {}),
|
|
206
|
+
...(observedUrlAfterAssignment ? { observedUrlAfterAssignment } : {}),
|
|
207
|
+
},
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async function runCreateTabAutomation(
|
|
212
|
+
request: Extract<BrowserAutomationBrokerAction, { action: 'create_tab' }>,
|
|
213
|
+
testScript: string,
|
|
214
|
+
testArgs: string[],
|
|
215
|
+
timeoutMs: number,
|
|
216
|
+
): Promise<MacOsBrowserCreateTabEvidence> {
|
|
217
|
+
if (runtimeHooks.runAppleScript) {
|
|
218
|
+
const value = await runtimeHooks.runAppleScript(testScript, testArgs, timeoutMs);
|
|
219
|
+
return parseMacOsBrowserCreateTabBrokerResult({ value });
|
|
220
|
+
}
|
|
221
|
+
return parseMacOsBrowserCreateTabBrokerResult(await callBrowserAutomationBroker(request, timeoutMs));
|
|
222
|
+
}
|
|
223
|
+
|
|
135
224
|
async function runBrowserAutomationText(
|
|
136
225
|
request: BrowserAutomationBrokerAction,
|
|
137
226
|
testScript: string,
|
|
@@ -673,12 +762,18 @@ export class MacOsAppleEventsPage {
|
|
|
673
762
|
private readonly browser: MacOsBrowserDefinition;
|
|
674
763
|
private timeoutMs: number;
|
|
675
764
|
private targetRef?: MacOsBrowserTabRef;
|
|
765
|
+
private readonly createTabEvidence?: MacOsBrowserCreateTabEvidence;
|
|
676
766
|
|
|
677
|
-
constructor(attachment: MacOsBrowserAttachment, timeoutMs = DEFAULT_NATIVE_TIMEOUT_MS, targetRef?: MacOsBrowserTabRef) {
|
|
767
|
+
constructor(attachment: MacOsBrowserAttachment, timeoutMs = DEFAULT_NATIVE_TIMEOUT_MS, targetRef?: MacOsBrowserTabRef, createTabEvidence?: MacOsBrowserCreateTabEvidence) {
|
|
678
768
|
this.metadata = { ...attachment.metadata, ...(targetRef ? { windowId: targetRef.windowId, tabId: targetRef.tabId } : {}) };
|
|
679
769
|
this.browser = browserDefinition(attachment.metadata.product);
|
|
680
770
|
this.timeoutMs = timeoutMs;
|
|
681
771
|
this.targetRef = targetRef;
|
|
772
|
+
this.createTabEvidence = createTabEvidence ? {
|
|
773
|
+
...createTabEvidence,
|
|
774
|
+
ref: { ...createTabEvidence.ref },
|
|
775
|
+
...(createTabEvidence.navigation ? { navigation: { ...createTabEvidence.navigation } } : {}),
|
|
776
|
+
} : undefined;
|
|
682
777
|
}
|
|
683
778
|
|
|
684
779
|
private async runAutomation(
|
|
@@ -741,6 +836,14 @@ export class MacOsAppleEventsPage {
|
|
|
741
836
|
return this.targetRef ? { ...this.targetRef } : undefined;
|
|
742
837
|
}
|
|
743
838
|
|
|
839
|
+
creationEvidence(): MacOsBrowserCreateTabEvidence | undefined {
|
|
840
|
+
return this.createTabEvidence ? {
|
|
841
|
+
...this.createTabEvidence,
|
|
842
|
+
ref: { ...this.createTabEvidence.ref },
|
|
843
|
+
...(this.createTabEvidence.navigation ? { navigation: { ...this.createTabEvidence.navigation } } : {}),
|
|
844
|
+
} : undefined;
|
|
845
|
+
}
|
|
846
|
+
|
|
744
847
|
updateTimeout(timeoutMs: number): void {
|
|
745
848
|
if (Number.isFinite(timeoutMs) && timeoutMs > 0) this.timeoutMs = Math.trunc(timeoutMs);
|
|
746
849
|
}
|
|
@@ -1195,7 +1298,11 @@ export class MacOsAppleEventsPage {
|
|
|
1195
1298
|
}
|
|
1196
1299
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
1197
1300
|
}
|
|
1198
|
-
throw new
|
|
1301
|
+
throw new AssistantPluginError(
|
|
1302
|
+
'PLUGIN_BROWSER_LOAD_STATE_TIMEOUT',
|
|
1303
|
+
`Timed out waiting for native browser load state ${state}.`,
|
|
1304
|
+
{ retryable: true, details: { state, timeoutMs: timeout, requireHttpUrl } },
|
|
1305
|
+
);
|
|
1199
1306
|
}
|
|
1200
1307
|
|
|
1201
1308
|
locator(_selector: string): { screenshot: (options?: Record<string, unknown>) => Promise<Buffer> } {
|
|
@@ -1342,19 +1449,17 @@ export async function createMacOsBrowserOwnedPage(
|
|
|
1342
1449
|
timeoutMs = DEFAULT_NATIVE_TIMEOUT_MS,
|
|
1343
1450
|
): Promise<MacOsAppleEventsPage> {
|
|
1344
1451
|
const browser = browserDefinition(attachment.metadata.product);
|
|
1345
|
-
const
|
|
1452
|
+
const creationEvidence = await runCreateTabAutomation(
|
|
1346
1453
|
{ action: 'create_tab', product: attachment.metadata.product, url },
|
|
1347
1454
|
createOwnedTabScript(browser),
|
|
1348
1455
|
[url],
|
|
1349
1456
|
timeoutMs,
|
|
1350
1457
|
);
|
|
1351
|
-
const
|
|
1352
|
-
if (parts.length < 2) throw new Error(`Browser scripting returned incomplete owned-tab metadata for ${browser.appName}.`);
|
|
1353
|
-
const ref = { windowId: parseBrowserId(parts[0] ?? '', 'window id'), tabId: parseBrowserId(parts[1] ?? '', 'tab id') };
|
|
1458
|
+
const ref = creationEvidence.ref;
|
|
1354
1459
|
const page = new MacOsAppleEventsPage({
|
|
1355
1460
|
...attachment,
|
|
1356
1461
|
metadata: { ...attachment.metadata, url, title: '', windowId: ref.windowId, tabId: ref.tabId, active: false },
|
|
1357
|
-
}, timeoutMs, ref);
|
|
1462
|
+
}, timeoutMs, ref, creationEvidence);
|
|
1358
1463
|
rememberMacOsBrowserPageHandle(attachment.metadata.product, ref, page);
|
|
1359
1464
|
return page;
|
|
1360
1465
|
}
|