Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 9 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@
"benchmark": "uv run python benchmarks/scripts/run_eval.py",
"dev": "tsx src/main.ts",
"dev:bun": "bun src/main.ts",
"daemon": "tsx src/daemon.ts",
"daemon:start": "tsx src/main.ts daemon start",
"daemon:status": "tsx src/main.ts daemon status",
"daemon:stop": "tsx src/main.ts daemon stop",
"build": "npm run clean-dist && npm run copy-yaml && npm run compile && npm run build-manifest",
"compile": "tsc --build && node -e \"require('fs').chmodSync('dist/src/main.js', 0o755)\"",
"build-manifest": "tsx src/build-manifest.ts",
Expand All @@ -77,7 +81,11 @@
"test:e2e": "vitest run --project e2e-fixed-port --project e2e",
"gate:cloak-sessions": "WEBCMD_LIVE_CLOAK=1 vitest run --project e2e tests/e2e/cloak-session-concurrency.test.ts",
"advise:listing-id-pairing": "node scripts/check-listing-id-pairing.mjs",
"check:package-bin": "node scripts/check-package-bin.mjs"
"check:package-bin": "node scripts/check-package-bin.mjs",
"pc2a:raw-smoke": "tsx src/adapters/webcmd/raw-smoke.ts",
"pc2a:core-smoke": "tsx src/adapters/webcmd/core-smoke.ts",
"pc2a:test": "vitest run src/adapters/core src/adapters/webcmd",
"pc2a:typecheck": "tsc --noEmit"
},
"keywords": [
"cli",
Expand Down
99 changes: 99 additions & 0 deletions src/CONTRACT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# PilgrimOS PC 2A Core Contract

This contract is the seam between PC 2A (engine) and PC 2B (Temple / Travel / Hotel adapters).
Do not put domain logic in this package.

## BrowserManager

```ts
BrowserManager.startSession(name?) -> Promise<BrowserSession>
BrowserManager.navigate(session, url) -> Promise<PageState>
BrowserManager.readPage(session) -> Promise<PageState>
BrowserManager.closeSession(session) -> Promise<void>
BrowserManager.pauseSession(session) -> void
```

`pauseSession()` is an in-memory safety boundary. While paused, navigation/read calls throw. It does not close or destroy the Webcmd session, allowing an upstream HITL flow to resume or clean up it safely.

`PageState`:

```ts
{
url: string;
title: string;
content: string;
content_found: boolean;
}
```

## AdapterBase

```ts
abstract class AdapterBase<TInput extends AdapterInput, TData> {
readonly adapter: string;
abstract run(input: TInput): Promise<StandardResult<TData>>;
}
```

A domain adapter should return `StandardResult` for every completed/failed/partial path and never leak raw Webcmd errors to callers.

## StandardResult

```json
{
"success": true,
"status": "completed",
"adapter": "temple",
"action": "check_availability",
"data": {},
"metadata": {
"source": "",
"timestamp": ""
},
"error": null
}
```

Valid statuses are exactly:

`completed | searching | partial | failed | retrying | blocked | approval_required`

`error` is `null` on success and follows:

```ts
{
code: ErrorCode;
message: string;
retryable: boolean;
details?: unknown;
}
```

## Error codes

- `NAVIGATION_FAILED`
- `TIMEOUT`
- `ELEMENT_NOT_FOUND`
- `PAGE_CHANGED`
- `WEBSITE_UNAVAILABLE`
- `RATE_LIMITED`
- `LOGIN_REQUIRED`
- `CAPTCHA_DETECTED`
- `NO_AVAILABILITY`
- `INVALID_INPUT`
- `UNKNOWN_ERROR`

## Recovery

```ts
RecoveryManager.retry(fn, policy) -> Promise<result>
```

Default policy: 3 attempts, exponential backoff, bounded jitter.

Dangerous actions are never meant to enter recovery. The manager also rejects a policy marked `dangerousAction: true` as a defense in depth check. Payment, OTP, and final submit must be short-circuited upstream to `approval_required`.

## Webcmd boundary

PC 2B should not call the Webcmd CLI directly. Use `BrowserManager` and/or `WebcmdSkills`.
The client follows the current Webcmd CLI lifecycle: create a session, run browser programs against that explicit session, then close it.
12 changes: 12 additions & 0 deletions src/adapter_base.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { StandardResult } from './result_schema.js';

export interface AdapterInput {
action: string;
[key: string]: unknown;
}

export abstract class AdapterBase<TInput extends AdapterInput = AdapterInput, TData = unknown> {
public abstract readonly adapter: string;

public abstract run(input: TInput): Promise<StandardResult<TData>>;
}
Loading