pomerado 0.1.1 → 0.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 (36) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/LICENSE +21 -661
  3. package/README.md +15 -10
  4. package/dist/typescript/authoring/auth/SKILL.md +21 -159
  5. package/dist/typescript/authoring/caller-input/SKILL.md +7 -68
  6. package/dist/typescript/authoring/core/SKILL.md +40 -330
  7. package/dist/typescript/authoring/forms/SKILL.md +7 -78
  8. package/dist/typescript/authoring/pagination/SKILL.md +5 -22
  9. package/dist/typescript/authoring/workspace/AGENTS.md +53 -293
  10. package/dist/typescript/authoring/workspace/README.md +2 -10
  11. package/dist/typescript/authoring/writes/SKILL.md +12 -140
  12. package/dist/typescript/src/execution/sign-in-diagnostics.d.ts +19 -20
  13. package/dist/typescript/src/guardian/openai.js +3 -1
  14. package/dist/typescript/src/guardian/upstream-policy.d.ts +2 -1
  15. package/dist/typescript/src/guardian/upstream-policy.js +10 -3
  16. package/dist/typescript/src/guardian/upstream-policy.md +9 -0
  17. package/dist/typescript/src/mint/contracts.d.ts +36 -9
  18. package/dist/typescript/src/mint/harness.js +80 -8
  19. package/dist/typescript/src/mint/incident-contracts.d.ts +6 -6
  20. package/dist/typescript/src/mint/openai.js +4 -2
  21. package/dist/typescript/src/mint/sign-in-failure.d.ts +2 -2
  22. package/dist/typescript/src/mint/skills.d.ts +4 -0
  23. package/dist/typescript/src/mint/skills.js +60 -61
  24. package/dist/typescript/src/runtime/failure-detail.d.ts +10 -11
  25. package/dist/typescript/src/runtime/failure-detail.js +41 -45
  26. package/dist/typescript/src/runtime/input-request.d.ts +1 -0
  27. package/dist/typescript/src/runtime/input-request.js +2 -1
  28. package/dist/typescript/src/runtime/provider-metadata.d.ts +7 -6
  29. package/dist/typescript/src/runtime/script-input.d.ts +2 -2
  30. package/dist/typescript/src/runtime/script-input.js +13 -3
  31. package/dist/typescript/src/standalone/mcp-cli.js +13 -2
  32. package/dist/typescript/src/standalone/mcp-package.js +73 -23
  33. package/dist/typescript/tests/support/credential-masking-corpus.js +2 -2
  34. package/package.json +6 -3
  35. package/third-party/codex/LICENSE +201 -0
  36. package/third-party/codex/NOTICE +6 -0
@@ -3,15 +3,14 @@ import { authorizationCredential, awsAccessKeyIdPattern, bracketedValueSource, c
3
3
  import { isSecretKey } from "../privacy/secret-keys.js";
4
4
  import { urlSpans } from "../privacy/url-spans.js";
5
5
  /**
6
- * Detailed failure record shared by every host failure class. See
7
- * typescript/src/runtime/ERROR-LOGGING-STANDARD.md. The finite fields (`subCause`, `operation`,
8
- * `phase`) may reach operational logs; everything else reaches the screened diagnostic archive,
9
- * where the privacy broker screens it again under the diagnostics area policy, and, for a relayed
10
- * HTTP failure only, the sandbox's answer through `agentDetail` in `browser/http-relay.ts`.
6
+ * Detailed failure record shared by every host failure class. The finite fields (`subCause`,
7
+ * `operation`, `phase`) may reach operational logs; everything else reaches the screened
8
+ * diagnostic archive, where the privacy broker screens it again under the diagnostics area policy,
9
+ * and, for a relayed HTTP failure only, the sandbox's answer through a host's HTTP relay.
11
10
  */
12
11
  /** Stable, finite sub-causes. Add a new value rather than reusing one for a different check. */
13
12
  const failureSubCauses = [
14
- // CDP transport (destinations/cdp-transport.ts)
13
+ // A host's CDP transport to its browser
15
14
  "cdp_endpoint_invalid",
16
15
  "cdp_connect_failed",
17
16
  "cdp_command_rejected",
@@ -24,12 +23,12 @@ const failureSubCauses = [
24
23
  "cdp_envelope_invalid",
25
24
  "cdp_transport_closed",
26
25
  "cdp_command_not_allowed",
27
- // Host HTTP relay and direct sign-in (destinations/http-relay.ts, destinations/direct-login.ts)
26
+ // A host's HTTP relay and direct sign-in
28
27
  "http_relay_transport_failed",
29
28
  "direct_login_transport_failed",
30
29
  // Host autofill sign-in (destinations/autofill-step.ts); a failed fill keeps no error text
31
30
  "autofill_step_failed",
32
- // Browser recorder (destinations/browser-recorder.ts)
31
+ // A host's browser recorder
33
32
  "recorder_start_incomplete",
34
33
  "recorder_context_ambiguous",
35
34
  "recorder_primary_missing",
@@ -38,26 +37,28 @@ const failureSubCauses = [
38
37
  "recorder_foreign_target_unclosed",
39
38
  "recorder_target_undetached",
40
39
  "recorder_request_unseen",
41
- // Origin policy reads (worker/origin-boundary.ts, worker/browser.ts)
40
+ // Origin policy reads a hosted service may make before and while it browses
42
41
  "origin_route_blocked",
43
42
  "origin_policy_read_failed",
44
43
  "origin_policy_timeout",
45
44
  "origin_policy_snapshot_stale",
46
45
  "origin_url_invalid",
47
- // Worker browser (worker/browser.ts)
46
+ // A hosted service's job browser
48
47
  "browser_origin_missing",
49
48
  "browser_initial_policy_failed",
50
49
  "browser_initial_origin_blocked",
51
- // The primary origin is on the site blocklist (origin-policy/site-blocklist.ts)
50
+ // The primary origin is on a site blocklist a hosted service may keep
52
51
  "browser_site_not_supported",
53
52
  "browser_entry_url_invalid",
54
53
  "browser_primary_page_crashed",
55
54
  "browser_primary_page_closed",
56
55
  "browser_gone",
57
56
  "browser_unresponsive",
58
- // A mint browser lost a third time with no agent command since the first (mint/host-browser-recovery.ts)
57
+ // The browser responded with a page-command failure, such as a script error or timeout.
58
+ "browser_page_call_failed",
59
+ // A mint browser lost a third time with no agent command since the first
59
60
  "browser_loss_repeated",
60
- // Startup and job authority (worker/startup-authority.ts, worker/mint.ts, worker/run.ts)
61
+ // Startup and job authority a hosted service may check before and during a job
61
62
  "startup_job_read_failed",
62
63
  "startup_attempt_superseded",
63
64
  "startup_cancel_requested",
@@ -66,10 +67,10 @@ const failureSubCauses = [
66
67
  "startup_maintenance_work_failed",
67
68
  "startup_original_read_failed",
68
69
  "startup_original_cancel_requested",
69
- // An attempt's unreadable authority check and the grace that bounds it (worker/authority-grace.ts)
70
+ // An attempt's unreadable authority check and the grace that bounds it
70
71
  "authority_check_unreadable",
71
72
  "authority_unavailable_past_grace",
72
- // The attempt lease heartbeat and its thread (jobs/lease-heartbeat.ts, jobs/lease-thread.ts)
73
+ // The heartbeat, and its thread, that a hosted service may use to hold an attempt's lease
73
74
  "lease_attempt_inactive",
74
75
  "lease_watchdog_expired",
75
76
  "lease_thread_failed",
@@ -79,54 +80,50 @@ const failureSubCauses = [
79
80
  // A run's in-place login question went unanswered, was refused, or could not be asked.
80
81
  "run_login_request_failed",
81
82
  "run_expired_auth_site_mismatch",
82
- // A run's lost page or browser and its retry (worker/run.ts, decided 2026-09-27)
83
+ // A run's lost page or browser and its retry
83
84
  "run_browser_loss_retried",
84
85
  "run_signed_in_session_cleared",
85
- // A session a mode or proxy change wiped again past the attempt's relogin cap (worker/browser.ts)
86
+ // A session a mode or proxy change wiped again past the attempt's relogin cap
86
87
  "browser_relogin_spent",
87
- // A wiped session whose latest sign-in was never verified (worker/browser.ts)
88
+ // A wiped session whose latest sign-in was never verified
88
89
  "browser_relogin_unverified",
89
90
  // A mint's authenticate after its attempt's sign-ins were spent: sign-in is unavailable in the
90
- // build (mint/host-execution.ts)
91
+ // build
91
92
  "mint_sign_in_spent",
92
93
  "run_browser_loss_retry_declined",
93
- // A write mint's accepted confirm it could not screen for secrets, so never kept (mint/host-evidence.ts)
94
+ // A write mint's accepted confirm it could not screen for secrets, so never kept
94
95
  "mint_expected_confirm_unscreened",
95
- // Mint host destination authority (mint/host.ts)
96
+ // A mint host's destination authority
96
97
  "mint_admission_generation_changed",
97
98
  // A publication whose attempt no longer holds its job, or whose job was told to stop
98
- // (registry/postgres.ts)
99
99
  "publication_attempt_stopped",
100
- // Mint host managed-login identity check (mint/host.ts)
100
+ // A mint host's managed-login identity check
101
101
  "identity_receipt_invalid",
102
102
  "identity_check_failed",
103
103
  // The site rejected the supplied credentials during managed login; not a dependency failure.
104
104
  "managed_login_credentials_rejected",
105
105
  // A run's autofill replay: the site showed the password screen again after it took the
106
- // password, or the replay did not sign in for another reason, which maintenance repairs
107
- // (worker/run.ts).
106
+ // password, or the replay did not sign in for another reason, which maintenance repairs.
108
107
  "autofill_credentials_rejected",
109
108
  "operation_credentials_rejected",
110
109
  "run_autofill_sign_in_failed",
111
110
  // A run's sign-in found an execution VM that could still reach its browser, so nothing was
112
- // filled (worker/run.ts).
111
+ // filled.
113
112
  "run_sign_in_executor_attached",
114
- // A host resource that did not settle within its bound: the attempt's host close
115
- // (mint/host-settle.ts) and a cancel or lost lease's browser stop (worker/mint.ts)
113
+ // A host resource that did not settle within its bound: the attempt's host close, and the
114
+ // browser stop after a cancel or a lost lease
116
115
  "mint_host_settle_timeout",
117
116
  "mint_outside_stop_timeout",
118
117
  // A takeover's resource that stayed unavailable for the whole recovery wait
119
- // (mint/host-restore-sandbox.ts)
120
118
  "mint_recovery_wait_timeout",
121
- // A takeover's step whose outcome no later try can confirm (mint/host-restore-execution.ts,
122
- // mint/host-restore-browser.ts)
119
+ // A takeover's step whose outcome no later try can confirm
123
120
  "mint_recovery_unconfirmed",
124
121
  // The model provider refused a mint's call because the account's quota is spent (mint/openai.ts)
125
122
  "model_quota_exhausted",
126
- // Input request answers and views (application/input-requests.ts): the request's protected
127
- // handoff was deleted or had expired, so the request closed while it was read.
123
+ // Input request answers and views: the request's protected handoff was deleted or had
124
+ // expired, so the request closed while it was read.
128
125
  "input_request_handoff_closed",
129
- // Application host (application/host.ts)
126
+ // A host's authority reference and its recheck
130
127
  "host_authority_reference_invalid",
131
128
  "host_authority_recheck_failed",
132
129
  // A queued attempt's server renewal and a job whose
@@ -136,9 +133,9 @@ const failureSubCauses = [
136
133
  "authority_server_renewal_stopped",
137
134
  "authority_server_renewal_unavailable",
138
135
  "authority_session_signed_out",
139
- // A worker takeover that never settled ended its job as a lost lease (application/recovery-claim.ts)
136
+ // A takeover that never settled ended its job as a lost lease
140
137
  "recovery_unsettled",
141
- // A browser create Kernel kept answering with 429 past its wait budget (providers/kernel/controller.ts)
138
+ // A browser create the provider kept answering with 429 past its wait budget
142
139
  "kernel_rate_limited",
143
140
  // Dependency failures wrapped by a host failure, by area. `site` names the mapping.
144
141
  "mint_execution_failed",
@@ -150,14 +147,14 @@ const failureSubCauses = [
150
147
  "executor_boundary_failed",
151
148
  "sandbox_workspace_failed",
152
149
  "credential_connector_failed",
153
- // A credential launcher's refused worker start (credentials/workload/launcher.ts): the
154
- // Kubernetes API refused the worker Job, by admission policy or webhook, the namespace quota,
155
- // a webhook that can't judge a dry run, or otherwise (authorization, an invalid template)
150
+ // A hosted service may refuse to start a credential worker: the Kubernetes API refused the
151
+ // worker, by admission policy or webhook, a quota, a webhook that can't judge a dry run, or
152
+ // otherwise (authorization, an invalid template)
156
153
  "admission_rejected",
157
154
  "quota_exceeded",
158
155
  "dry_run_unsupported",
159
156
  "kubernetes_create_rejected",
160
- // ...or the launcher refused before creating one
157
+ // ...or the service refused before creating one
161
158
  "launcher_draining",
162
159
  "launcher_at_capacity",
163
160
  "launcher_tenant_at_capacity",
@@ -165,11 +162,10 @@ const failureSubCauses = [
165
162
  "controller_operation_forbidden",
166
163
  "workload_identity_mismatch",
167
164
  "launch_superseded",
168
- // ...or the start's gateway-signed capability did not cover it (credentials/capability.ts)
165
+ // ...or the start's signed capability did not cover it
169
166
  "capability_refused",
170
- // A credential worker's launcher call that never reached the launcher: DNS, connection or TLS
171
- // failed, as when the namespace's default deny ships without the worker egress allow
172
- // (credentials/workload/worker.ts)
167
+ // A credential worker's call to start work that never arrived: DNS, connection or TLS failed,
168
+ // as when network policy blocks the worker's egress
173
169
  "worker_launcher_unreachable",
174
170
  "job_storage_failed",
175
171
  "private_input_storage_failed",
@@ -852,7 +848,7 @@ export const withCauseEntry = (detail, error) => {
852
848
  * A detail nested in a failure object is invisible to JSON serialization: RPC encoders,
853
849
  * public responses and `JSON.stringify(failure)` omit it. Only `failureDetailMetadata` and
854
850
  * `failureDetailOf` project it, into the archive and the maintenance evidence files, and
855
- * `agentDetail` in `browser/http-relay.ts`, into a relayed HTTP failure's answer to the sandbox,
851
+ * a host's HTTP relay, into a relayed HTTP failure's answer to the sandbox,
856
852
  * which the agent's execution result carries. The detail itself, serialized at the top level,
857
853
  * keeps every field.
858
854
  */
@@ -6,6 +6,7 @@ import { Either, Schema, type Effect } from "effect";
6
6
  * surface (Dashboard, MCP, REST) answers through `validateAnswer`.
7
7
  */
8
8
  /** A question's key in the request and in the answer. */
9
+ export declare const questionIdPattern: RegExp;
9
10
  export declare const QuestionId: Schema.filter<typeof Schema.String>;
10
11
  /** The longest free-text or secret answer any question accepts. */
11
12
  export declare const maximumAnswerLength = 16384;
@@ -6,7 +6,8 @@ import { Data, Either, Schema } from "effect";
6
6
  * surface (Dashboard, MCP, REST) answers through `validateAnswer`.
7
7
  */
8
8
  /** A question's key in the request and in the answer. */
9
- export const QuestionId = Schema.String.pipe(Schema.pattern(/^[a-z][a-z0-9_]{0,63}$/));
9
+ export const questionIdPattern = /^[a-z][a-z0-9_]{0,63}$/;
10
+ export const QuestionId = Schema.String.pipe(Schema.pattern(questionIdPattern));
10
11
  /** The host's opaque name for an offered option; a script's or provider's own value stays private. */
11
12
  const OptionId = Schema.String.pipe(Schema.pattern(/^[a-z0-9_]{1,64}$/));
12
13
  const Label = Schema.String.pipe(Schema.minLength(1), Schema.maxLength(256));
@@ -1,14 +1,15 @@
1
1
  export type BrowserMode = "headless" | "headful" | "headful-gpu";
2
2
  export type RetainedProviderStage = "profile_decode" | "connection_decode" | "connection_duplicate_fields" | "timeline_decode" | "browser_session_mismatch" | "login_browser_decode" | "login_already_running" | "login_response_decode" | "submit_state"
3
- /** Kernel answered the submission `accepted: false`. */
3
+ /** The provider answered the submission `accepted: false`. */
4
4
  | "submit_rejected" | "submit_response_decode" | "cleanup_identity_decode";
5
5
  export type RetainedProviderSchemaField = "profile" | "profile.id" | "profile_save_changes" | "stealth" | "browser.stealth" | "other";
6
6
  export type RetainedProviderCode = "Unavailable" | "InvalidConfiguration" | "ProxyUnavailable" | "AllocationUncertain"
7
- /** Kernel answered the create with a definite refusal, so no browser exists. */
7
+ /** The provider answered the create with a definite refusal, so no browser exists. */
8
8
  | "AllocationRejected" | "StopUnconfirmed" | "UnexpectedBrowserState" | "StorageUnavailable" | "BindingNotFound";
9
- /** Proxy failure codes retained in sign-in diagnostics. */
10
- export type RetainedProxyError = "upstream_timeout" | "provider_unreachable" | "upstream_connect_failed" | "upstream_dns_failure" | "origin_tls_timeout" | "restricted_route_unavailable" | "destination_route_unavailable" | "proxy_unavailable" | "origin_response_incomplete" | "provider_rejected" | "provider_blacklisted" | "destination_blocked" | "mitm_certificate" | "mitm_tls" | "mitm_tls_rejected" | "mitm_connect" | "mitm_stream" | "mitm_io" | "mitm_timeout" | "mitm_canceled" | "mitm_upstream_proxy" | "mitm_h1" | "mitm_request_invalid" | "mitm_other" | "proxy_forbidden" | "proxy_auth_required" | "proxy_rate_limited" | "other";
11
- export type ProxySwitchSummary = {
9
+ /** Network failure codes a host retains in sign-in diagnostics, as its provider reported them. */
10
+ export type RetainedNetworkError = "upstream_timeout" | "provider_unreachable" | "upstream_connect_failed" | "upstream_dns_failure" | "origin_tls_timeout" | "restricted_route_unavailable" | "destination_route_unavailable" | "proxy_unavailable" | "origin_response_incomplete" | "provider_rejected" | "provider_blacklisted" | "destination_blocked" | "mitm_certificate" | "mitm_tls" | "mitm_tls_rejected" | "mitm_connect" | "mitm_stream" | "mitm_io" | "mitm_timeout" | "mitm_canceled" | "mitm_upstream_proxy" | "mitm_h1" | "mitm_request_invalid" | "mitm_other" | "proxy_forbidden" | "proxy_auth_required" | "proxy_rate_limited" | "other";
11
+ /** How the host replaced a browser after a failure, as the agent sees it. */
12
+ export type BrowserRecoverySummary = {
12
13
  readonly kind: "switched";
13
14
  readonly egress: "proxy" | "direct";
14
15
  readonly cause: string;
@@ -16,7 +17,7 @@ export type ProxySwitchSummary = {
16
17
  readonly possiblySent: boolean;
17
18
  readonly inFlightPossiblySent: true;
18
19
  readonly repeated: false;
19
- /** The browser's cookies, cache and site storage were cleared for the new proxy. */
20
+ /** The browser's cookies, cache and site storage were cleared for the replacement. */
20
21
  readonly stateCleared: true;
21
22
  /** Set when the clear ended the site's signed-in session. */
22
23
  readonly signIn?: "again_once" | "unavailable";
@@ -32,7 +32,7 @@ export declare const ScriptQuestionDeclaration: Schema.Union<[Schema.Struct<{
32
32
  prompt: Schema.filter<Schema.filter<typeof Schema.String>>;
33
33
  }>]>;
34
34
  export type ScriptQuestionDeclaration = typeof ScriptQuestionDeclaration.Type;
35
- export declare const ScriptQuestionDeclarations: Schema.Record$<Schema.filter<typeof Schema.String>, Schema.Union<[Schema.Struct<{
35
+ export declare const ScriptQuestionDeclarations: Schema.transform<Schema.filter<typeof Schema.Unknown>, Schema.Record$<Schema.filter<typeof Schema.String>, Schema.Union<[Schema.Struct<{
36
36
  type: Schema.Literal<["choice"]>;
37
37
  prompt: Schema.filter<Schema.filter<typeof Schema.String>>;
38
38
  allowOther: Schema.optional<typeof Schema.Boolean>;
@@ -61,7 +61,7 @@ export declare const ScriptQuestionDeclarations: Schema.Record$<Schema.filter<ty
61
61
  maxLength: Schema.optional<Schema.filter<typeof Schema.Int>>;
62
62
  }>, Schema.Struct<{
63
63
  prompt: Schema.filter<Schema.filter<typeof Schema.String>>;
64
- }>]>>;
64
+ }>]>>>;
65
65
  export type ScriptQuestionDeclarations = Readonly<Record<string, ScriptQuestionDeclaration>>;
66
66
  /** One option the page offers now. */
67
67
  export declare const ScriptOption: Schema.Struct<{
@@ -1,6 +1,6 @@
1
1
  import { randomUUID } from "node:crypto";
2
- import { Context, Data, Effect, Either, Schema } from "effect";
3
- import { ConfirmQuestion, InputRequest, QuestionId, SecretQuestion, TextQuestion, validateAnswer, } from "./input-request.js";
2
+ import { Context, Data, Effect, Either, Schema, SchemaAST } from "effect";
3
+ import { ConfirmQuestion, InputRequest, QuestionId, questionIdPattern, SecretQuestion, TextQuestion, validateAnswer, } from "./input-request.js";
4
4
  /**
5
5
  * A script asks its caller through the one input request (source `script`). It declares each
6
6
  * question once in its contract, so the publication review reads every prompt, and passes the
@@ -35,10 +35,20 @@ export const ScriptQuestionDeclaration = Schema.Union(Schema.Struct({
35
35
  }),
36
36
  // The pre-unification shape, `{ prompt }`, kept for published revisions: a single choice.
37
37
  Schema.Struct({ prompt: Schema.String.pipe(Schema.minLength(1), Schema.maxLength(500)) }));
38
- export const ScriptQuestionDeclarations = Schema.Record({
38
+ const QuestionDeclarations = Schema.Record({
39
39
  key: QuestionId,
40
40
  value: ScriptQuestionDeclaration,
41
41
  });
42
+ export const ScriptQuestionDeclarations = Schema.Unknown.pipe(
43
+ // Check original keys before record construction can discard __proto__ or another invalid id.
44
+ Schema.filter((questions) => typeof questions !== "object" ||
45
+ questions === null ||
46
+ Object.keys(questions).every((id) => questionIdPattern.test(id)), {
47
+ message: () => "question ids must start with a lowercase letter and contain only lowercase letters, digits, or underscores (up to 64 characters)",
48
+ }), Schema.compose(QuestionDeclarations)).annotations({
49
+ // Schema generation still describes the declarations rather than the unvalidated input.
50
+ [SchemaAST.SurrogateAnnotationId]: QuestionDeclarations.ast,
51
+ });
42
52
  /** One option the page offers now. */
43
53
  export const ScriptOption = Schema.Struct({
44
54
  /** What `ask` returns when the caller picks it. It never leaves the sandbox. */
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import { realpathSync } from "node:fs";
2
3
  import { parseArgs } from "node:util";
3
4
  import { pathToFileURL } from "node:url";
4
5
  import { resolve } from "node:path";
@@ -98,6 +99,16 @@ export const startMcpCli = (args = process.argv.slice(2), options = {}) => {
98
99
  },
99
100
  });
100
101
  };
101
- if (process.argv[1] !== undefined &&
102
- pathToFileURL(resolve(process.argv[1])).href === import.meta.url)
102
+ /** npm links a bin to this file, so the entry path counts once its links are resolved. */
103
+ const isEntrypoint = (path) => {
104
+ if (path === undefined)
105
+ return false;
106
+ try {
107
+ return pathToFileURL(realpathSync(path)).href === import.meta.url;
108
+ }
109
+ catch {
110
+ return false;
111
+ }
112
+ };
113
+ if (isEntrypoint(process.argv[1]))
103
114
  startMcpCli();
@@ -6,12 +6,29 @@ import { localFilePath, localPromise, localError, localRelativePath, } from "../
6
6
  import { Deployment, writeArtifact } from "./artifact-files.js";
7
7
  const launcher = `import { fileURLToPath } from 'node:url';
8
8
  const runtime = process.argv[2];
9
- if (!runtime) throw new Error('Start this integration using its generated Codex configuration.');
9
+ if (!runtime) throw new Error('Start this integration with the command and arguments in its mcp.json.');
10
10
  const { startMcpCli } = await import(runtime);
11
11
  startMcpCli(['serve', '--artifact', fileURLToPath(new URL('.', import.meta.url))]);
12
12
  `;
13
13
  const shellQuote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
14
- const reserved = new Set(["deployment.json", "mcp.mjs", "codex-mcp.toml", "readme.md"]);
14
+ /**
15
+ * Top-level names authored source may not use, compared in lower case. They cover the packaging
16
+ * files, the TOML that 0.1.2 wrote and its docs told users to copy into Codex, and the project
17
+ * config an MCP client might load from this folder.
18
+ */
19
+ const reserved = new Set([
20
+ "deployment.json",
21
+ "mcp.mjs",
22
+ "mcp.json",
23
+ "readme.md",
24
+ "codex-mcp.toml",
25
+ ".mcp.json",
26
+ ".vscode",
27
+ ".cursor",
28
+ ".codex",
29
+ ".gemini",
30
+ ".claude",
31
+ ]);
15
32
  /** Only the local operator's configuration chooses the root; tool arguments choose one slug. */
16
33
  export const prepareIntegration = (options) => Effect.gen(function* () {
17
34
  const deployment = yield* Schema.decodeUnknown(Deployment)({
@@ -46,37 +63,70 @@ export const prepareIntegration = (options) => Effect.gen(function* () {
46
63
  yield* writeArtifact(directory, artifact);
47
64
  const workspace = yield* createLocalWorkspace({ root: directory });
48
65
  const launcherPath = join(directory, "mcp.mjs");
49
- const configPath = join(directory, "codex-mcp.toml");
66
+ const configPath = join(directory, "mcp.json");
50
67
  const runtime = new URL("./mcp-cli.js", import.meta.url).href;
51
- const configuration = `[mcp_servers.${deployment.name}]
52
- command = ${JSON.stringify(process.execPath)}
53
- args = ${JSON.stringify([launcherPath, runtime])}
54
- env_vars = ["OPENAI_API_KEY"]
55
- `;
68
+ const args = [launcherPath, runtime];
69
+ const configuration = {
70
+ mcpServers: { [deployment.name]: { command: process.execPath, args } },
71
+ };
72
+ const command = [process.execPath, ...args].map(shellQuote).join(" ");
56
73
  yield* workspace.write("deployment.json", `${JSON.stringify(deployment, null, 2)}\n`);
57
74
  yield* workspace.write("mcp.mjs", launcher);
58
- yield* workspace.write("codex-mcp.toml", configuration);
75
+ yield* workspace.write("mcp.json", `${JSON.stringify(configuration, null, 2)}\n`);
59
76
  yield* workspace.write("README.md", `# ${deployment.name}
60
77
 
61
- This integration is hosted locally over MCP stdio. Add the command and arguments from
62
- codex-mcp.toml to your Codex configuration (~/.codex/config.toml). Alternatively, register it with:
78
+ This integration runs on your computer as a local MCP stdio server. mcp.json holds its server
79
+ entry in the standard mcpServers format. The entry starts your Node with this directory's
80
+ launcher and your installed Pomerado runtime.
81
+
82
+ ## Add it to your MCP client
83
+
84
+ - Claude Code passes its own environment to the server.
85
+
86
+ \`\`\`sh
87
+ claude mcp add ${deployment.name} -- ${command}
88
+ \`\`\`
89
+
90
+ - Codex passes servers only a short list of environment variables. After adding the server, put
91
+ \`env_vars = ["OPENAI_API_KEY"]\` under \`[mcp_servers.${deployment.name}]\` in
92
+ ~/.codex/config.toml, or $CODEX_HOME/config.toml when CODEX_HOME is set.
93
+
94
+ \`\`\`sh
95
+ codex mcp add ${deployment.name} -- ${command}
96
+ \`\`\`
63
97
 
64
- \`\`\`sh
65
- codex mcp add ${deployment.name} -- ${shellQuote(process.execPath)} ${shellQuote(launcherPath)} ${shellQuote(runtime)}
66
- \`\`\`
98
+ - Gemini CLI hides variables named like keys from servers. The -e flag below passes
99
+ OPENAI_API_KEY by reference, so the settings file holds no key.
100
+
101
+ \`\`\`sh
102
+ gemini mcp add -e 'OPENAI_API_KEY=$OPENAI_API_KEY' ${deployment.name} ${command}
103
+ \`\`\`
104
+
105
+ - Cursor, VS Code, Claude Desktop and other clients that read an mcpServers JSON file take the
106
+ entry from mcp.json. In Cursor, add \`"env": { "OPENAI_API_KEY": "\${env:OPENAI_API_KEY}" }\`
107
+ to it.
108
+
109
+ ## Model key
110
+
111
+ The server needs OPENAI_API_KEY in its environment, because Guardian reviews every run. Model
112
+ requests go to the configured provider. This directory and mcp.json hold no key. Give the key to
113
+ the server through your client's environment settings, never through chat.
114
+
115
+ ## Call it
67
116
 
68
117
  Call ${deployment.name} with its discovered input schema. Its URL, intent, authority and
69
- authentication origins are pinned in deployment.json. Jobs and questions continue through
70
- get_job, provide_input and cancel_job; polling never resubmits an operation.
118
+ authentication origins are pinned in deployment.json. A call that needs an answer or more time
119
+ returns a job ID. Continue that job with get_job, provide_input and cancel_job. Polling never
120
+ resubmits an operation.
121
+
122
+ Answers sent through provide_input are visible to your MCP client and its model provider.
123
+ Restarting the server loses live jobs.
71
124
 
72
- The launcher uses the shared installed Pomerado runtime, minter/Guardian dependencies and
73
- local Chromium. Configured model providers receive model requests. The TOML forwards
74
- OPENAI_API_KEY from Codex's environment. Supply provider keys in
75
- the server's environment; this directory and its configuration contain no keys. Answers sent
76
- through provide_input are visible to the MCP client and its model. Restarting loses live jobs.
125
+ ## Paths
77
126
 
78
- The launcher source is portable. The local configuration references your current Node and
79
- runtime installation; update those paths if you move either installation or this directory.
127
+ The launcher uses your installed Pomerado runtime, its minter and Guardian dependencies, and
128
+ local Chromium. The launcher source is portable. mcp.json names your current Node and Pomerado
129
+ installation. Update those paths if you move either installation or this directory.
80
130
  `);
81
131
  completed = true;
82
132
  return { directory, configPath, launcherPath };
@@ -56,7 +56,7 @@ export const credentials = [
56
56
  "MIIfakeKeyMaterial",
57
57
  both,
58
58
  ],
59
- // Review round 1: cookie shapes, Azure account keys, Digest params and bracketed values.
59
+ // Cookie shapes, Azure account keys, Digest params and bracketed values.
60
60
  ['Cookie: sid="FAKEq0t3dCookie123"; theme=dark', "FAKEq0t3dCookie123", both],
61
61
  [
62
62
  "upstream set-cookie: theme=dark; Path=/, session=FAKEsess0123abc; HttpOnly",
@@ -69,7 +69,7 @@ export const credentials = [
69
69
  both,
70
70
  ],
71
71
  ["Cookie: FAKEopaquecookie0123456789", "FAKEopaquecookie0123456789", both],
72
- // Review round 2: a comma inside a cookie value continues it (consent-manager cookies).
72
+ // A comma inside a cookie value continues it (consent-manager cookies).
73
73
  ["Cookie: consent=analytics,ads; sid=SEKRETcomma02; theme=dark", "SEKRETcomma02", both],
74
74
  [
75
75
  "Cookie: cookieyes-consent=consentid:abc,consent:yes,action:yes; __Host-sid=SEKRETcy01",
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "pomerado",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Pomerado's integration minter and Guardian with local compute and native Playwright",
5
5
  "type": "module",
6
- "license": "AGPL-3.0-only",
6
+ "license": "MIT",
7
7
  "homepage": "https://pomerado.ai",
8
8
  "repository": {
9
9
  "type": "git",
@@ -595,8 +595,11 @@
595
595
  },
596
596
  "files": [
597
597
  "dist",
598
+ "CHANGELOG.md",
598
599
  "LICENSE",
599
- "README.md"
600
+ "README.md",
601
+ "third-party/codex/LICENSE",
602
+ "third-party/codex/NOTICE"
600
603
  ],
601
604
  "scripts": {
602
605
  "build": "rm -rf dist && tsc -p tsconfig.json && node tools/copy-assets.ts",