@parall/agent-core 1.55.0 → 1.55.2

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.
@@ -136,6 +136,13 @@ export type DispatchOpts = {
136
136
  sessionKey: string;
137
137
  context: DispatchContext;
138
138
  inputLifecycle?: DispatchInputLifecycle;
139
+ /**
140
+ * Refresh the dispatch inactivity deadline when the runtime makes progress
141
+ * that does not become a RuntimeEvent (for example, a streaming delta that
142
+ * the bridge intentionally suppresses). RuntimeEvents refresh it in the
143
+ * gateway automatically.
144
+ */
145
+ noteActivity?: () => void;
139
146
  };
140
147
  export type RuntimeInputState = 'started' | 'completed' | 'failed';
141
148
  export type RuntimeInputUpdateResult = {
@@ -212,9 +219,10 @@ export interface DispatchAdapter {
212
219
  getSessionHistoryPath?(sessionKey: string): string | undefined;
213
220
  /**
214
221
  * Abort the in-flight dispatch for the given session. Called by the gateway
215
- * when the dispatch deadline is exceeded. Implementations should unblock the
216
- * `dispatch()` generator (e.g. close a turn sink, end stdin, fail a stream)
217
- * so the `for await` loop in `runDispatch` exits naturally. Must be idempotent.
222
+ * when the dispatch inactivity deadline is exceeded. Implementations should
223
+ * unblock the `dispatch()` generator (e.g. close a turn sink, end stdin,
224
+ * fail a stream) so the `for await` loop in `runDispatch` exits naturally.
225
+ * Must be idempotent.
218
226
  */
219
227
  abortDispatch?(sessionKey: string): void;
220
228
  }
@@ -1 +1 @@
1
- {"version":3,"file":"dispatch-adapter.d.ts","sourceRoot":"","sources":["../src/dispatch-adapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dispatch-adapter.d.ts","sourceRoot":"","sources":["../src/dispatch-adapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ export declare class DispatchInactivityDeadline {
2
+ private readonly timeoutMs;
3
+ private readonly onExpire;
4
+ private readonly onDispose;
5
+ private timer;
6
+ private expired;
7
+ private disposed;
8
+ constructor(timeoutMs: number, onExpire: () => void, onDispose: () => void);
9
+ readonly touch: () => void;
10
+ dispose(): void;
11
+ }
12
+ /** Session-keyed registry for activity signals that arrive outside dispatch(). */
13
+ export declare class DispatchInactivityDeadlines {
14
+ private readonly active;
15
+ start(sessionKey: string, timeoutMs: number, onExpire: () => void): DispatchInactivityDeadline;
16
+ touch(sessionKey: string): void;
17
+ }
18
+ //# sourceMappingURL=dispatch-inactivity-deadline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatch-inactivity-deadline.d.ts","sourceRoot":"","sources":["../src/dispatch-inactivity-deadline.ts"],"names":[],"mappings":"AAAA,qBAAa,0BAA0B;IAMnC,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAP5B,OAAO,CAAC,KAAK,CAA8C;IAC3D,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAS;gBAGN,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,IAAI,EACpB,SAAS,EAAE,MAAM,IAAI;IAGxC,QAAQ,CAAC,KAAK,QAAO,IAAI,CAQvB;IAEF,OAAO,IAAI,IAAI;CAOhB;AAED,kFAAkF;AAClF,qBAAa,2BAA2B;IACtC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiD;IAExE,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,0BAA0B;IAS9F,KAAK,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;CAGhC"}
@@ -0,0 +1,49 @@
1
+ export class DispatchInactivityDeadline {
2
+ timeoutMs;
3
+ onExpire;
4
+ onDispose;
5
+ timer = null;
6
+ expired = false;
7
+ disposed = false;
8
+ constructor(timeoutMs, onExpire, onDispose) {
9
+ this.timeoutMs = timeoutMs;
10
+ this.onExpire = onExpire;
11
+ this.onDispose = onDispose;
12
+ }
13
+ touch = () => {
14
+ if (this.timeoutMs <= 0 || this.expired || this.disposed)
15
+ return;
16
+ if (this.timer)
17
+ clearTimeout(this.timer);
18
+ this.timer = setTimeout(() => {
19
+ this.timer = null;
20
+ this.expired = true;
21
+ this.onExpire();
22
+ }, this.timeoutMs);
23
+ };
24
+ dispose() {
25
+ if (this.disposed)
26
+ return;
27
+ this.disposed = true;
28
+ if (this.timer)
29
+ clearTimeout(this.timer);
30
+ this.timer = null;
31
+ this.onDispose();
32
+ }
33
+ }
34
+ /** Session-keyed registry for activity signals that arrive outside dispatch(). */
35
+ export class DispatchInactivityDeadlines {
36
+ active = new Map();
37
+ start(sessionKey, timeoutMs, onExpire) {
38
+ const deadline = new DispatchInactivityDeadline(timeoutMs, onExpire, () => {
39
+ if (this.active.get(sessionKey) === deadline)
40
+ this.active.delete(sessionKey);
41
+ });
42
+ this.active.set(sessionKey, deadline);
43
+ deadline.touch();
44
+ return deadline;
45
+ }
46
+ touch(sessionKey) {
47
+ this.active.get(sessionKey)?.touch();
48
+ }
49
+ }
@@ -162,13 +162,14 @@ export declare class ParallAgentGateway {
162
162
  * an unrelated stalled fork's lane leased forever).
163
163
  */
164
164
  private sessionActiveLanes;
165
+ private dispatchInactivityDeadlines;
165
166
  noteSessionLane(sessionKey: string, laneKey: string | null): void;
166
167
  /**
167
168
  * External runtime-activity signal for adapters whose tool activity does
168
169
  * not flow through the RuntimeEvent stream (openclaw hooks call this from
169
- * the tool-call lifecycle): renews the session's OWN active ledger lane so
170
- * a long tool call cannot outlive the lease and get dethroned mid-turn.
171
- * No-op without an active ledger lane for the session.
170
+ * the tool-call lifecycle): refreshes the dispatch inactivity deadline and
171
+ * renews the session's OWN active ledger lane, when present, so a long tool
172
+ * call cannot time out or get dethroned mid-turn.
172
173
  */
173
174
  touchRuntimeActivity(sessionKey: string): void;
174
175
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"gateway-base.d.ts","sourceRoot":"","sources":["../src/gateway-base.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gateway-base.d.ts","sourceRoot":"","sources":["../src/gateway-base.ts"],"names":[],"mappings":"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"}
@@ -8,6 +8,7 @@ import { CAPABILITY_SLACK_SEND, CAPABILITY_WECHAT_SEND, channelCapabilityKeyFor,
8
8
  import { buildErrorStepContent, } from './dispatch-adapter.js';
9
9
  import { clearTypedDedupeForEvent, consumeMessageWorkItem, consumeTypedDispatch, dispatchLaneGroup, resolveDispatchByID, settleDrainedTypedGroup, shiftMainBufferGroup, steerLaneMessage, typedLedgerEventIds, } from './gateway-lane-flow.js';
10
10
  import { LaneLedger, bindLaneSession, releaseLocalMessageClaims } from './lane-ledger.js';
11
+ import { DispatchInactivityDeadlines } from './dispatch-inactivity-deadline.js';
11
12
  import { routeTrigger } from './routing.js';
12
13
  import { redactTurnOutcome } from './redact.js';
13
14
  import { StepPersister, isRetryableStepError } from './step-persister.js';
@@ -537,6 +538,7 @@ export class ParallAgentGateway {
537
538
  * an unrelated stalled fork's lane leased forever).
538
539
  */
539
540
  sessionActiveLanes = new Map();
541
+ dispatchInactivityDeadlines = new DispatchInactivityDeadlines();
540
542
  noteSessionLane(sessionKey, laneKey) {
541
543
  if (laneKey == null)
542
544
  this.sessionActiveLanes.delete(sessionKey);
@@ -546,11 +548,12 @@ export class ParallAgentGateway {
546
548
  /**
547
549
  * External runtime-activity signal for adapters whose tool activity does
548
550
  * not flow through the RuntimeEvent stream (openclaw hooks call this from
549
- * the tool-call lifecycle): renews the session's OWN active ledger lane so
550
- * a long tool call cannot outlive the lease and get dethroned mid-turn.
551
- * No-op without an active ledger lane for the session.
551
+ * the tool-call lifecycle): refreshes the dispatch inactivity deadline and
552
+ * renews the session's OWN active ledger lane, when present, so a long tool
553
+ * call cannot time out or get dethroned mid-turn.
552
554
  */
553
555
  touchRuntimeActivity(sessionKey) {
556
+ this.dispatchInactivityDeadlines.touch(sessionKey);
554
557
  if (this.ledgerDisabled)
555
558
  return;
556
559
  const laneKey = this.sessionActiveLanes.get(sessionKey);
@@ -1100,17 +1103,15 @@ export class ParallAgentGateway {
1100
1103
  // — JS event loop is single-threaded, so shutdown() cannot interleave
1101
1104
  // here and miss our in-flight count.
1102
1105
  this.inFlightDispatches++;
1103
- const deadlineTimer = this.DISPATCH_DEADLINE_MS > 0
1104
- ? setTimeout(() => {
1105
- this.opts.log?.warn(`dispatch deadline exceeded (${this.DISPATCH_DEADLINE_MS}ms) for ${event.messageId} on ${sessionKey}; aborting`);
1106
- try {
1107
- this.opts.dispatchAdapter.abortDispatch?.(sessionKey);
1108
- }
1109
- catch (err) {
1110
- this.opts.log?.warn(`abortDispatch threw for ${sessionKey}: ${String(err)}`);
1111
- }
1112
- }, this.DISPATCH_DEADLINE_MS)
1113
- : null;
1106
+ const dispatchDeadline = this.dispatchInactivityDeadlines.start(sessionKey, this.DISPATCH_DEADLINE_MS, () => {
1107
+ this.opts.log?.warn(`dispatch inactivity deadline exceeded (${this.DISPATCH_DEADLINE_MS}ms) for ${event.messageId} on ${sessionKey}; aborting`);
1108
+ try {
1109
+ this.opts.dispatchAdapter.abortDispatch?.(sessionKey);
1110
+ }
1111
+ catch (err) {
1112
+ this.opts.log?.warn(`abortDispatch threw for ${sessionKey}: ${String(err)}`);
1113
+ }
1114
+ });
1114
1115
  let binding = this.sessionBindings.get(sessionKey);
1115
1116
  let inputStepsCreated = false;
1116
1117
  let turnHandle;
@@ -1138,7 +1139,9 @@ export class ParallAgentGateway {
1138
1139
  sessionKey,
1139
1140
  context: dispatchContext,
1140
1141
  inputLifecycle,
1142
+ noteActivity: dispatchDeadline.touch,
1141
1143
  })) {
1144
+ dispatchDeadline.touch();
1142
1145
  if (runtimeEvent.type === 'runtime_session') {
1143
1146
  const priorAgentSessionId = binding?.agentSessionId;
1144
1147
  binding = await this.bindRuntimeSession(sessionKey, runtimeEvent, contextFilePath, laneContextFilePath);
@@ -1323,8 +1326,7 @@ export class ParallAgentGateway {
1323
1326
  throw err;
1324
1327
  }
1325
1328
  finally {
1326
- if (deadlineTimer)
1327
- clearTimeout(deadlineTimer);
1329
+ dispatchDeadline.dispose();
1328
1330
  const metricsSnapshot = getDispatchMetrics(sessionKey);
1329
1331
  const durationMs = metricsSnapshot ? Date.now() - metricsSnapshot.started_at : 0;
1330
1332
  // Effective outcome for telemetry: the bridge's turn_outcome wins;
@@ -0,0 +1,3 @@
1
+ export declare const PLATFORM_CODEX_MESSAGE_DELIVERY_SOURCE_SHA256 = "a5a00b5777de3921e1c8d2731cee855bc0688c75f32c9f8c90f0067abb128cbc";
2
+ export declare const PLATFORM_CODEX_MESSAGE_DELIVERY_INSTRUCTIONS = "### Codex message delivery\n\nCodex's native `commentary` and `final` prose is not a Parall message. The\nbridge records that prose only as a suppressed session step; nobody in the chat\nsees it. Tool calls made from `commentary` still execute, so use the shell/exec\ntool there to run the Parall CLI.\n\nThroughout these Parall instructions, \u201Creply\u201D, \u201Csay\u201D, \u201Cask\u201D, \u201Creport\u201D,\n\u201Cupdate\u201D, and \u201Ccommunicate\u201D mean creating a collaborator-visible Parall\nartifact through the CLI, never emitting runtime-native assistant prose.\n\nWhen the current event warrants a visible response, deliver acknowledgements,\nmeaningful progress, blockers or questions, requested status, and final results\nwith `parall messages send` or `parall dm`. Native `commentary` or `final` prose\ndoes not satisfy that communication requirement. A reply is delivered only\nafter the CLI command succeeds.\n\nDo not mirror every routine runtime commentary heartbeat into the chat. After a\nsuccessful send, native `final` output is only an audit record and does not need\nto duplicate the collaborator-visible message.";
3
+ //# sourceMappingURL=codex-message-delivery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-message-delivery.d.ts","sourceRoot":"","sources":["../../src/generated/codex-message-delivery.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,6CAA6C,qEAAqE,CAAC;AAChI,eAAO,MAAM,4CAA4C,ioCAAqkC,CAAC"}
@@ -0,0 +1,5 @@
1
+ // Code generated by scripts/generate-platform-instructions.mjs; DO NOT EDIT.
2
+ // Source: prompt-source/codex-message-delivery.md
3
+ // Source SHA-256: a5a00b5777de3921e1c8d2731cee855bc0688c75f32c9f8c90f0067abb128cbc
4
+ export const PLATFORM_CODEX_MESSAGE_DELIVERY_SOURCE_SHA256 = 'a5a00b5777de3921e1c8d2731cee855bc0688c75f32c9f8c90f0067abb128cbc';
5
+ export const PLATFORM_CODEX_MESSAGE_DELIVERY_INSTRUCTIONS = "### Codex message delivery\n\nCodex's native `commentary` and `final` prose is not a Parall message. The\nbridge records that prose only as a suppressed session step; nobody in the chat\nsees it. Tool calls made from `commentary` still execute, so use the shell/exec\ntool there to run the Parall CLI.\n\nThroughout these Parall instructions, “reply”, “say”, “ask”, “report”,\n“update”, and “communicate” mean creating a collaborator-visible Parall\nartifact through the CLI, never emitting runtime-native assistant prose.\n\nWhen the current event warrants a visible response, deliver acknowledgements,\nmeaningful progress, blockers or questions, requested status, and final results\nwith `parall messages send` or `parall dm`. Native `commentary` or `final` prose\ndoes not satisfy that communication requirement. A reply is delivered only\nafter the CLI command succeeds.\n\nDo not mirror every routine runtime commentary heartbeat into the chat. After a\nsuccessful send, native `final` output is only an audit record and does not need\nto duplicate the collaborator-visible message.";
@@ -1,12 +1,18 @@
1
1
  import type { PlatformInstructionIdentity } from './generated/platform-instructions.js';
2
2
  /**
3
- * Assemble the canonical standing instructions for bridge runtimes.
4
- *
5
- * Claude Code and Codex use different delivery channels, but their visible
6
- * content and section order are identical. Keeping that order here prevents
7
- * either adapter from silently missing a future shared section.
3
+ * Assemble the canonical standing instructions shared by bridge runtimes.
8
4
  */
9
5
  export declare function buildBridgePlatformInstructions(workspaceDir: string, identity?: PlatformInstructionIdentity, capabilityFragments?: readonly string[]): string;
6
+ /**
7
+ * Assemble Codex instructions with its native-channel delivery adapter.
8
+ *
9
+ * Codex names `commentary` and `final` as collaborator-facing channels in its
10
+ * built-in prompt, while the Parall bridge suppresses their text. Keeping this
11
+ * adapter in the runtime appendix makes the transport boundary explicit
12
+ * without leaking Codex terminology into Claude Code or Parel.
13
+ */
14
+ export declare function buildCodexPlatformInstructions(workspaceDir: string, identity?: PlatformInstructionIdentity, capabilityFragments?: readonly string[]): string;
15
+ export { PLATFORM_CODEX_MESSAGE_DELIVERY_INSTRUCTIONS, PLATFORM_CODEX_MESSAGE_DELIVERY_SOURCE_SHA256, } from './generated/codex-message-delivery.js';
10
16
  export type { PlatformInstructionIdentity, PlatformInstructionRenderOptions, } from './generated/platform-instructions.js';
11
17
  export { PLATFORM_INSTRUCTIONS_SOURCE_SHA256, renderPlatformIdentity, renderPlatformInstructions, } from './generated/platform-instructions.js';
12
18
  //# sourceMappingURL=platform-instructions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"platform-instructions.d.ts","sourceRoot":"","sources":["../src/platform-instructions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,sCAAsC,CAAC;AAOxF;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAC7C,YAAY,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE,2BAA2B,EACtC,mBAAmB,CAAC,EAAE,SAAS,MAAM,EAAE,GACtC,MAAM,CAOR;AAED,YAAY,EACV,2BAA2B,EAC3B,gCAAgC,GACjC,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EACL,mCAAmC,EACnC,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,sCAAsC,CAAC"}
1
+ {"version":3,"file":"platform-instructions.d.ts","sourceRoot":"","sources":["../src/platform-instructions.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,sCAAsC,CAAC;AAqBxF;;GAEG;AACH,wBAAgB,+BAA+B,CAC7C,YAAY,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE,2BAA2B,EACtC,mBAAmB,CAAC,EAAE,SAAS,MAAM,EAAE,GACtC,MAAM,CAOR;AAED;;;;;;;GAOG;AACH,wBAAgB,8BAA8B,CAC5C,YAAY,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE,2BAA2B,EACtC,mBAAmB,CAAC,EAAE,SAAS,MAAM,EAAE,GACtC,MAAM,CASR;AAED,OAAO,EACL,4CAA4C,EAC5C,6CAA6C,GAC9C,MAAM,uCAAuC,CAAC;AAC/C,YAAY,EACV,2BAA2B,EAC3B,gCAAgC,GACjC,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EACL,mCAAmC,EACnC,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,sCAAsC,CAAC"}
@@ -1,18 +1,30 @@
1
+ import { PLATFORM_CODEX_MESSAGE_DELIVERY_INSTRUCTIONS } from './generated/codex-message-delivery.js';
1
2
  import { PLATFORM_BRIDGE_WORKSPACE_INSTRUCTIONS, renderPlatformInstructions, } from './generated/platform-instructions.js';
2
3
  import { buildSkillReferences } from './skills/index.js';
3
- /**
4
- * Assemble the canonical standing instructions for bridge runtimes.
5
- *
6
- * Claude Code and Codex use different delivery channels, but their visible
7
- * content and section order are identical. Keeping that order here prevents
8
- * either adapter from silently missing a future shared section.
9
- */
10
- export function buildBridgePlatformInstructions(workspaceDir, identity, capabilityFragments) {
4
+ function buildPlatformInstructions(workspaceDir, runtimeAppendix, identity, capabilityFragments) {
11
5
  return renderPlatformInstructions({
12
6
  identity,
13
- runtimeAppendix: PLATFORM_BRIDGE_WORKSPACE_INSTRUCTIONS,
7
+ runtimeAppendix,
14
8
  capabilityFragments,
15
9
  skillReferences: buildSkillReferences(workspaceDir),
16
10
  });
17
11
  }
12
+ /**
13
+ * Assemble the canonical standing instructions shared by bridge runtimes.
14
+ */
15
+ export function buildBridgePlatformInstructions(workspaceDir, identity, capabilityFragments) {
16
+ return buildPlatformInstructions(workspaceDir, PLATFORM_BRIDGE_WORKSPACE_INSTRUCTIONS, identity, capabilityFragments);
17
+ }
18
+ /**
19
+ * Assemble Codex instructions with its native-channel delivery adapter.
20
+ *
21
+ * Codex names `commentary` and `final` as collaborator-facing channels in its
22
+ * built-in prompt, while the Parall bridge suppresses their text. Keeping this
23
+ * adapter in the runtime appendix makes the transport boundary explicit
24
+ * without leaking Codex terminology into Claude Code or Parel.
25
+ */
26
+ export function buildCodexPlatformInstructions(workspaceDir, identity, capabilityFragments) {
27
+ return buildPlatformInstructions(workspaceDir, [PLATFORM_BRIDGE_WORKSPACE_INSTRUCTIONS, PLATFORM_CODEX_MESSAGE_DELIVERY_INSTRUCTIONS].join('\n\n'), identity, capabilityFragments);
28
+ }
29
+ export { PLATFORM_CODEX_MESSAGE_DELIVERY_INSTRUCTIONS, PLATFORM_CODEX_MESSAGE_DELIVERY_SOURCE_SHA256, } from './generated/codex-message-delivery.js';
18
30
  export { PLATFORM_INSTRUCTIONS_SOURCE_SHA256, renderPlatformIdentity, renderPlatformInstructions, } from './generated/platform-instructions.js';
@@ -1,2 +1,2 @@
1
- export declare const PARALL_CLIP_AUTHORING_SKILL = "# Authoring a Parall Clip (v3)\n\nWrite a **registry (v3) clip**. This skill is for CREATING clips; to DISCOVER\nand CALL clips that are already installed, use the Parall Clips skill instead.\n\nTwo kinds of clip exist, and this document covers authoring both:\n\n- A **browser clip** \u2014 the main subject here \u2014 is a folder of named commands\n that run on an Edge (a member's desktop or an org-shared cloud profile) and\n drive a real browser session: one manifest (`manifest.json` or `site.json`,\n either name works) + one `.js` file per command + optional `_`-prefixed\n helpers. The Edge runs each command's JS in a sandbox with a browser handle\n bound to the target profile.\n- An **MCP clip** is a manifest-ONLY declaration pointing at a remote MCP tool\n server \u2014 no `.js` files; its tools come live from that server's own\n `tools/list`, never from the manifest. See \"MCP clips\" below.\n\n## Project layout\n\n```\nmy-clip/\n\u251C\u2500\u2500 manifest.json # name, description, version, command params\n\u251C\u2500\u2500 _helpers.js # OPTIONAL \u2014 any _-prefixed file is auto-injected into every command\n\u251C\u2500\u2500 search.js # one command = one file; filename (minus .js) IS the command name\n\u2514\u2500\u2500 profile.js # another command\n```\n\n## manifest.json\n\n```json\n{\n \"name\": \"twitter\",\n \"description\": \"Twitter / X\",\n \"version\": \"1.0.0\",\n \"commands\": {\n \"search\": {\n \"description\": \"Search tweets\",\n \"params\": {\n \"query\": { \"type\": \"string\", \"required\": true },\n \"count\": { \"type\": \"number\", \"required\": false }\n }\n },\n \"profile\": {\n \"description\": \"Fetch a user profile\",\n \"params\": { \"handle\": { \"type\": \"string\", \"required\": true } }\n }\n }\n}\n```\n\n- `commands` keys MUST match the `.js` filenames (`search` \u2194 `search.js`).\n- `params` is the input contract the caller sees in `clip info`; validate them in code too.\n- **No top-level `\"type\"` needed for a browser clip** \u2014 omitting it means\n browser. The only accepted values are `\"browser\"` and `\"mcp\"` (for the\n latter, see \"MCP clips\" below). **`\"type\": \"clip\"` is the legacy Pinix v2\n package value and is refused at publish** \u2014 don't copy it in from an older\n clip.\n\n## A command file\n\n```js\n// search.js \u2014 a command is a single async function of (args).\n// The code does NOT know or pick the profile; the Edge binds it per invocation.\n//\n// SHAPE, not a runnable Twitter client: the \"...\" parts (the GraphQL path,\n// parseTweets' body) are what you fill in per target site. Deliberately not\n// pinned to a real X endpoint \u2014 a site's internal API paths rotate, and a\n// stale one baked into this skill would teach a URL that 404s.\nmodule.exports = async function (args) {\n if (!args.query) return { error: \"Missing argument: query\" };\n\n const tab = await browser.open(\"https://x.com\");\n // Everything after open() goes in try/finally: an early return or a thrown\n // fetch would otherwise leak the tab, and the Edge is long-lived.\n try {\n const ct0 = await tab.cookie(\"ct0\");\n if (!ct0) return { error: \"Not logged in\" };\n\n const data = await tab.fetch(\"/i/api/graphql/.../SearchTimeline?...\", {\n headers: twitterHeaders(ct0), // from _helpers.js, auto-injected\n });\n return { query: args.query, tweets: parseTweets(data) };\n } finally {\n await tab.close();\n }\n};\n```\n\nReturn a plain JSON-serializable object. A thrown error surfaces to the caller as\n`SCRIPT_ERROR`; a returned `{ error: \"...\" }` is your own typed failure \u2014 prefer it\nfor expected cases (not logged in, missing arg).\n\n## Helpers (`_`-prefixed)\n\nAny file whose name starts with `_` is NOT a command. Its top-level functions are\ninjected into every command's scope \u2014 no import/require needed:\n\n```js\n// _helpers.js\nfunction twitterHeaders(ct0) {\n return { \"X-Csrf-Token\": ct0, \"X-Twitter-Auth-Type\": \"OAuth2Session\" };\n}\nfunction parseTweets(data) { /* ... */ }\n```\n\n## Runtime API (globals available in every command)\n\n- `browser.open(url)` \u2192 tab handle \u00B7 `browser.tabs()` \u2192 open tabs\n- `tab.cookie(name)` \u00B7 `tab.fetch(url, opts)` (in-browser fetch, carries the session)\n- `tab.eval(expr)` (escape hatch) \u00B7 `tab.click(sel)` \u00B7 `tab.fill(sel, text)` \u00B7 `tab.navigate(url)`\n- `tab.waitForSelector(sel)` \u00B7 `tab.getTitle()` \u00B7 `tab.getURL()` \u00B7 `tab.screenshot()` \u00B7 `tab.close()`\n- `fetch` \u2014 runtime-side HTTP, does NOT go through the browser (no session)\n- `console` \u2014 logs \u00B7 `args` \u2014 the invocation input\n\n**Prefer `tab.fetch` over `tab.eval`**: fetch reuses the logged-in session and\nreturns structured data; eval is the last resort. Always `tab.close()` what you\nopen, and do it in a `finally` \u2014 an early return or a thrown fetch is exactly\nwhen the tab leaks.\n\n## Develop \u2192 publish \u2192 iterate\n\nUse the platform `parall clip` subcommands \u2014 they reuse the credentials you\nalready have (`PRLL_API_KEY` / `PRLL_ORG_ID`), so there is nothing to install\nor configure.\n\n> A separate **standalone `parall-clip`** binary also exists (the Edge-side\n> authoring tool). It takes the SAME operations but a DIFFERENT argument shape \u2014\n> `parall-clip exec <clip> <cmd> --query \"AI\" --count 10` passes one flag per\n> param, while `parall clip exec` takes a single JSON blob. Do not mix the two\n> forms; everything below is the platform CLI.\n\n1. **Publish** the clip directory to the org registry. Publishing is not a\n release step here \u2014 it is the edit loop's SAVE button, because exec only\n ever sees published files:\n\n ```sh\n parall clip publish ./my-clip/\n ```\n\n It packages the directory (the manifest plus the directory's top-level\n `.js` files; an MCP clip is manifest-only) and POSTs it for you (5 MB cap).\n `name` is the org-wide upsert key \u2014 manifest fields win, else the directory\n name / `0.0.1` / `private` fill the gaps. Re-publishing an existing name is\n **author-only** and REPLACES the file set. Publishing into YOUR org makes it\n usable there immediately (same-org self-reference, no review);\n `\"visibility\": \"public\"` additionally submits the version for platform\n review before it can spread cross-org.\n\n Programmatic equivalent (what `publish` calls under the hood \u2014 use only if\n you can't run the CLI). Send it verbatim-shaped: `visibility` is exactly\n one of `\"private\"` / `\"public\"`, and `files` maps each filename to its\n source as a string:\n\n ```\n POST /api/v1/orgs/{orgId}/clip-registry/publish\n {\n \"name\": \"twitter\",\n \"description\": \"Twitter / X\",\n \"version\": \"1.0.0\",\n \"visibility\": \"private\",\n \"manifest\": {\n \"name\": \"twitter\",\n \"version\": \"1.0.0\",\n \"commands\": { \"search\": { \"description\": \"Search tweets\" } }\n },\n \"files\": { \"search.js\": \"module.exports = async function (args) { return {}; };\" }\n }\n ```\n\n2. **Exec** a command against a real target. Args are ONE argument \u2014 a JSON\n string (or plain text for a single-value command), not per-param flags:\n\n ```sh\n parall clip exec <clip> <command> '{\"query\":\"AI\",\"count\":10}' --connection <ccn_id|alias>\n # or route to a desktop (BYOC) device you own: --edge <edge-id>\n # --connection and --edge are mutually exclusive; --timeout <ms> defaults to 30000\n ```\n\n A **cloud (hosted) profile is reachable ONLY via `--connection`** \u2014 the\n binding its maintainer created IS the authorization. With neither flag the\n server resolves only your own online desktop device, never a cloud profile.\n Discover the bindings with `parall clip connections <clip>`.\n\n3. **Iterate**: edit locally \u2192 `parall clip publish` again \u2192 re-exec. Exec\n resolves the file set from the REGISTRY, server-side \u2014 your own org always\n runs the live working copy, i.e. the latest publish. It NEVER reads your\n local directory: an edit you did not re-publish silently runs the previous\n version.\n\n## Install model & self-development (v3)\n\n- Installing a clip is a **reference**, not a copy \u2014 the JS lives once in the Market DB.\n- **Your own org** always executes its **live working copy** (latest published files),\n so re-publishing is your edit loop.\n- **Other orgs** installing your `public` clip execute only the **approved snapshot**\n (`approved_version_id`); unreviewed public edits are invisible/unexecutable cross-org.\n- To customize someone else's public clip: install \u2192 **fetch its effective\n file set** \u2192 modify \u2192 **publish into your OWN org** (a derived private\n entry). You cannot edit a published clip in place. The CLI has no files\n subcommand (`clip info` returns only the manifest) \u2014 read the source via\n the API:\n\n ```\n GET /api/v1/orgs/{orgId}/clip-registry/{clipId}/files\n ```\n\n It returns exactly what you may read and execute: your own clip \u2192 the live\n working copy; an installed public clip \u2192 the approved snapshot.\n\n## MCP clips (manifest-only)\n\nAn MCP clip declares a remote MCP tool server. There is nothing to code: no\n`.js` files (the no-scripts refusal at publish applies to browser clips only),\nand the folder is just a manifest:\n\n```json\n{\n \"name\": \"linear\",\n \"description\": \"Linear (MCP)\",\n \"version\": \"1.0.0\",\n \"type\": \"mcp\",\n \"mcp\": { \"server_url\": \"https://mcp.linear.app/mcp\", \"auth\": \"oauth\" }\n}\n```\n\n- The `mcp` block takes ONLY `server_url` and `auth` (`\"none\" | \"bearer\" |\n \"api_key\" | \"oauth\"`). Any other key is refused at publish \u2014 a credential\n belongs to the installing org's own configuration, NEVER to the clip\n definition.\n- `server_url` must be an absolute **https** URL with no embedded credentials,\n query, or fragment. It is review material, frozen with the approved version.\n- Do NOT put the server in the top-level `server` / `auth` manifest keys \u2014\n those are legacy Edge-manifest fields nothing reads. Only the `mcp` block\n declares the server.\n- Both fields are optional, but what you declare is LOCKED: the installing\n org's config must match it, and changing the URL or auth mode means\n republishing.\n- Entering the credential / completing OAuth is a HUMAN step in the Clip\n Console (the config-write endpoints are session-only \u2014 an API key cannot\n call them). An org can add SEVERAL connections to one MCP clip \u2014 one\n credential slot per account of the same service \u2014 all sharing the declared\n `server_url`/`auth`. Once configured, discover the live tool schemas with\n `parall clip tools <clip> [--connection <ref>]` and exec like any other\n clip (`--connection` picks the account).\n- Do NOT declare anything about OAuth client registration in the manifest \u2014\n there is no such field. Whether the provider needs a manually registered\n OAuth app (Google-style) is probed by the server at connect time; the\n Console walks the admin through it when required.\n- Publish is the same command: `parall clip publish ./my-clip/`.\n\n## Cloud (hosted) vs desktop (BYOC) Edge\n\nThe same command JS runs on either. Hosted profiles are org-shared cloud browsers\nreachable ONLY via an explicit `--connection`; cloud state lives in S3 and is\nhydrated per pod. Your code never touches this \u2014 it just gets a `browser`/`tab`\nbound to whatever profile the connection selected.\n\n## Constraints\n\n- Browser clips: one command = one file; keep a command's work self-contained\n (open what you need, close it, return). The Edge is stateless about your code\n between calls.\n- Never embed credentials in the clip source \u2014 rely on the profile's logged-in\n session (`tab.cookie` / `tab.fetch`). Published source is visible to installers.\n- Exec has a timeout (default 30s, caller-set up to 120s). Long scrapes should page,\n not block.\n";
1
+ export declare const PARALL_CLIP_AUTHORING_SKILL = "# Authoring a Parall Clip (v3)\n\nWrite a **registry (v3) clip**. This skill is for CREATING clips; to DISCOVER\nand CALL clips that are already installed, use the Parall Clips skill instead.\n\nTwo kinds of clip exist, and this document covers authoring both:\n\n- A **browser clip** \u2014 the main subject here \u2014 is a folder of named commands\n that run on an Edge (a member's desktop or an org-shared cloud profile) and\n drive a real browser session: one manifest (`manifest.json` or `site.json`,\n either name works) + one `.js` file per command + optional `_`-prefixed\n helpers. The Edge runs each command's JS in a sandbox with a browser handle\n bound to the target profile.\n- An **MCP clip** is a manifest-ONLY declaration pointing at a remote MCP tool\n server \u2014 no `.js` files; its tools come live from that server's own\n `tools/list`, never from the manifest. See \"MCP clips\" below.\n\n## Project layout\n\n```\nmy-clip/\n\u251C\u2500\u2500 manifest.json # name, description, version, command params\n\u251C\u2500\u2500 _helpers.js # OPTIONAL \u2014 any _-prefixed file is auto-injected into every command\n\u251C\u2500\u2500 search.js # one command = one file; filename (minus .js) IS the command name\n\u2514\u2500\u2500 profile.js # another command\n```\n\n## manifest.json\n\n```json\n{\n \"name\": \"twitter\",\n \"description\": \"Twitter / X\",\n \"version\": \"1.0.0\",\n \"commands\": {\n \"search\": {\n \"description\": \"Search tweets\",\n \"params\": {\n \"query\": { \"type\": \"string\", \"required\": true },\n \"count\": { \"type\": \"number\", \"required\": false }\n }\n },\n \"profile\": {\n \"description\": \"Fetch a user profile\",\n \"params\": { \"handle\": { \"type\": \"string\", \"required\": true } }\n }\n }\n}\n```\n\n- `commands` keys MUST match the `.js` filenames (`search` \u2194 `search.js`).\n- `params` is the input contract the caller sees in `clip info`; validate them in code too.\n- **No top-level `\"type\"` needed for a browser clip** \u2014 omitting it means\n browser. The only accepted values are `\"browser\"` and `\"mcp\"` (for the\n latter, see \"MCP clips\" below). **`\"type\": \"clip\"` is the legacy Pinix v2\n package value and is refused at publish** \u2014 don't copy it in from an older\n clip.\n\n## A command file\n\n```js\n// search.js \u2014 a command is a single async function of (args).\n// The code does NOT know or pick the profile; the Edge binds it per invocation.\n//\n// SHAPE, not a runnable Twitter client: the \"...\" parts (the GraphQL path,\n// parseTweets' body) are what you fill in per target site. Deliberately not\n// pinned to a real X endpoint \u2014 a site's internal API paths rotate, and a\n// stale one baked into this skill would teach a URL that 404s.\nmodule.exports = async function (args) {\n if (!args.query) return { error: \"Missing argument: query\" };\n\n const tab = await browser.open(\"https://x.com\");\n // Everything after open() goes in try/finally: an early return or a thrown\n // fetch would otherwise leak the tab, and the Edge is long-lived.\n try {\n const ct0 = await tab.cookie(\"ct0\");\n if (!ct0) return { error: \"Not logged in\" };\n\n const data = await tab.fetch(\"/i/api/graphql/.../SearchTimeline?...\", {\n headers: twitterHeaders(ct0), // from _helpers.js, auto-injected\n });\n return { query: args.query, tweets: parseTweets(data) };\n } finally {\n await tab.close();\n }\n};\n```\n\nReturn a plain JSON-serializable object. A thrown error surfaces to the caller as\n`SCRIPT_ERROR`; a returned `{ error: \"...\" }` is your own typed failure \u2014 prefer it\nfor expected cases (not logged in, missing arg).\n\n## Helpers (`_`-prefixed)\n\nAny file whose name starts with `_` is NOT a command. Its top-level functions are\ninjected into every command's scope \u2014 no import/require needed:\n\n```js\n// _helpers.js\nfunction twitterHeaders(ct0) {\n return { \"X-Csrf-Token\": ct0, \"X-Twitter-Auth-Type\": \"OAuth2Session\" };\n}\nfunction parseTweets(data) { /* ... */ }\n```\n\n## Runtime API (globals available in every command)\n\n- `browser.open(url)` \u2192 tab handle \u00B7 `browser.tabs()` \u2192 open tabs\n- `tab.cookie(name)` \u00B7 `tab.fetch(url, opts)` (in-browser fetch, carries the session)\n- `tab.eval(expr)` (escape hatch) \u00B7 `tab.click(sel)` \u00B7 `tab.fill(sel, text)` \u00B7 `tab.navigate(url)`\n- `tab.waitForSelector(sel)` \u00B7 `tab.getTitle()` \u00B7 `tab.getURL()` \u00B7 `tab.screenshot()` \u00B7 `tab.close()`\n- `tab.setFileInput(sel, url, opts?)` \u2014 upload a file into an `<input type=file>`\n- `fetch` \u2014 runtime-side HTTP, does NOT go through the browser (no session)\n- `console` \u2014 logs \u00B7 `args` \u2014 the invocation input\n\n**Prefer `tab.fetch` over `tab.eval`**: fetch reuses the logged-in session and\nreturns structured data; eval is the last resort. Always `tab.close()` what you\nopen, and do it in a `finally` \u2014 an early return or a thrown fetch is exactly\nwhen the tab leaks.\n\n### Uploading a file\n\n`tab.setFileInput` takes a URL, never a path, and the runtime \u2014 not the page \u2014\nfetches the bytes. That is what makes it work where an in-page `fetch` +\n`DataTransfer` cannot: upload targets ship a Content-Security-Policy that\nforbids the page from fetching an arbitrary file host (Instagram's `default-src`\nallows only its own domains), and you cannot change a header on their site. The\nruntime is not a page, so no CSP applies to it \u2014 and a large video never has to\npass through the page's memory.\n\n```js\nawait tab.setFileInput(\"input[type=file]\", videoUrl, { filename: \"clip.mp4\" });\nawait tab.click(\"button[type=submit]\");\nawait tab.waitForSelector(\".upload-complete\"); // \u2190 do not skip this\n```\n\nFour rules that decide whether your clip works:\n\n1. **The URL must be fetchable with no credentials** \u2014 a public direct link or a\n signed temporary one. The runtime sends no cookies, so a Drive or Feishu link\n copied from the address bar will not work: those are HTML pages behind a\n login. If the file needs a session, use a logged-in tab to obtain a signed\n direct link first, then pass THAT here. An HTML answer is refused with\n `EDGE_FILE_SOURCE_NOT_A_FILE` rather than uploaded as if it were a file.\n2. **Finish the upload inside the same command.** The browser reads the file when\n the page submits, and the runtime deletes it when your command ends. Injecting\n and returning immediately uploads nothing \u2014 wait for the site to confirm.\n3. **Main-document inputs only.** An input inside an iframe or a shadow root is\n not addressable and returns `EDGE_FILE_SELECTOR_MISS`. If the page rebuilds\n the input after you inject (navigation, re-render), inject again.\n4. **`opts.filename` is what the page sees**, and many sites validate by\n extension \u2014 set it when the URL has none. `opts.timeoutMs` bounds the\n download. One file per call; there is no multi-file form yet.\n\n## Develop \u2192 publish \u2192 iterate\n\nUse the platform `parall clip` subcommands \u2014 they reuse the credentials you\nalready have (`PRLL_API_KEY` / `PRLL_ORG_ID`), so there is nothing to install\nor configure.\n\n> A separate **standalone `parall-clip`** binary also exists (the Edge-side\n> authoring tool). It takes the SAME operations but a DIFFERENT argument shape \u2014\n> `parall-clip exec <clip> <cmd> --query \"AI\" --count 10` passes one flag per\n> param, while `parall clip exec` takes a single JSON blob. Do not mix the two\n> forms; everything below is the platform CLI.\n\n1. **Publish** the clip directory to the org registry. Publishing is not a\n release step here \u2014 it is the edit loop's SAVE button, because exec only\n ever sees published files:\n\n ```sh\n parall clip publish ./my-clip/\n ```\n\n It packages the directory (the manifest plus the directory's top-level\n `.js` files; an MCP clip is manifest-only) and POSTs it for you (5 MB cap).\n `name` is the org-wide upsert key \u2014 manifest fields win, else the directory\n name / `0.0.1` / `private` fill the gaps. Re-publishing an existing name is\n **author-only** and REPLACES the file set. Publishing into YOUR org makes it\n usable there immediately (same-org self-reference, no review);\n `\"visibility\": \"public\"` additionally submits the version for platform\n review before it can spread cross-org.\n\n Programmatic equivalent (what `publish` calls under the hood \u2014 use only if\n you can't run the CLI). Send it verbatim-shaped: `visibility` is exactly\n one of `\"private\"` / `\"public\"`, and `files` maps each filename to its\n source as a string:\n\n ```\n POST /api/v1/orgs/{orgId}/clip-registry/publish\n {\n \"name\": \"twitter\",\n \"description\": \"Twitter / X\",\n \"version\": \"1.0.0\",\n \"visibility\": \"private\",\n \"manifest\": {\n \"name\": \"twitter\",\n \"version\": \"1.0.0\",\n \"commands\": { \"search\": { \"description\": \"Search tweets\" } }\n },\n \"files\": { \"search.js\": \"module.exports = async function (args) { return {}; };\" }\n }\n ```\n\n2. **Exec** a command against a real target. Args are ONE argument \u2014 a JSON\n string (or plain text for a single-value command), not per-param flags:\n\n ```sh\n parall clip exec <clip> <command> '{\"query\":\"AI\",\"count\":10}' --connection <ccn_id|alias>\n # or route to a desktop (BYOC) device you own: --edge <edge-id>\n # --connection and --edge are mutually exclusive; --timeout <ms> defaults to 30000\n ```\n\n A **cloud (hosted) profile is reachable ONLY via `--connection`** \u2014 the\n binding its maintainer created IS the authorization. With neither flag the\n server resolves only your own online desktop device, never a cloud profile.\n Discover the bindings with `parall clip connections <clip>`.\n\n3. **Iterate**: edit locally \u2192 `parall clip publish` again \u2192 re-exec. Exec\n resolves the file set from the REGISTRY, server-side \u2014 your own org always\n runs the live working copy, i.e. the latest publish. It NEVER reads your\n local directory: an edit you did not re-publish silently runs the previous\n version.\n\n## Install model & self-development (v3)\n\n- Installing a clip is a **reference**, not a copy \u2014 the JS lives once in the Market DB.\n- **Your own org** always executes its **live working copy** (latest published files),\n so re-publishing is your edit loop.\n- **Other orgs** installing your `public` clip execute only the **approved snapshot**\n (`approved_version_id`); unreviewed public edits are invisible/unexecutable cross-org.\n- To customize someone else's public clip: install \u2192 **fetch its effective\n file set** \u2192 modify \u2192 **publish into your OWN org** (a derived private\n entry). You cannot edit a published clip in place. The CLI has no files\n subcommand (`clip info` returns only the manifest) \u2014 read the source via\n the API:\n\n ```\n GET /api/v1/orgs/{orgId}/clip-registry/{clipId}/files\n ```\n\n It returns exactly what you may read and execute: your own clip \u2192 the live\n working copy; an installed public clip \u2192 the approved snapshot.\n\n## MCP clips (manifest-only)\n\nAn MCP clip declares a remote MCP tool server. There is nothing to code: no\n`.js` files (the no-scripts refusal at publish applies to browser clips only),\nand the folder is just a manifest:\n\n```json\n{\n \"name\": \"linear\",\n \"description\": \"Linear (MCP)\",\n \"version\": \"1.0.0\",\n \"type\": \"mcp\",\n \"mcp\": { \"server_url\": \"https://mcp.linear.app/mcp\", \"auth\": \"oauth\" }\n}\n```\n\n- The `mcp` block takes ONLY `server_url`, `auth` (`\"none\" | \"api_key\" |\n \"basic\" | \"oauth\"`, legacy `\"bearer\"` accepted) and `auth_headers`. Any\n other key is refused at publish \u2014 a credential belongs to the installing\n org's own configuration, NEVER to the clip definition.\n- `auth` is REQUIRED at publish: you know what your server speaks, and this\n one word decides what the install form asks for (`none` = zero input,\n `api_key` = key field(s), `basic` = username + password, `oauth` = a\n Connect button).\n- `api_key` delivers as a single `X-API-Key` header by default. When the\n server wants a different shape, declare `auth_headers` (max 4 slots, one\n admin-supplied value each): `[{\"name\": \"Authorization\", \"scheme\":\n \"Bearer\"}]` for Bearer tokens, `[{\"name\": \"api-key\"}]` for a custom\n header, or a pair like `[{\"name\": \"CF-Access-Client-Id\"}, {\"name\":\n \"CF-Access-Client-Secret\"}]`. Framing/platform headers (Host, Cookie,\n X-Prll-*, \u2026) are refused.\n- `server_url` must be an absolute **https** URL with no embedded credentials,\n query, or fragment. It is review material, frozen with the approved version.\n It stays OPTIONAL for self-hosted products where each org connects its own\n instance URL.\n- Do NOT put the server in the top-level `server` / `auth` manifest keys \u2014\n those are legacy Edge-manifest fields nothing reads. Only the `mcp` block\n declares the server.\n- What you declare is LOCKED: the installing org's config must match it, and\n changing the URL, auth mode, or header shape means republishing.\n- Entering the credential / completing OAuth is a HUMAN step in the Clip\n Console (the config-write endpoints are session-only \u2014 an API key cannot\n call them). An org can add SEVERAL connections to one MCP clip \u2014 one\n credential slot per account of the same service \u2014 all sharing the declared\n `server_url`/`auth`. Once configured, discover the live tool schemas with\n `parall clip tools <clip> [--connection <ref>]` and exec like any other\n clip (`--connection` picks the account).\n- Do NOT declare anything about OAuth client registration in the manifest \u2014\n there is no such field. Whether the provider needs a manually registered\n OAuth app (Google-style) is probed by the server at connect time; the\n Console walks the admin through it when required.\n- Publish is the same command: `parall clip publish ./my-clip/`.\n\n## Cloud (hosted) vs desktop (BYOC) Edge\n\nThe same command JS runs on either. Hosted profiles are org-shared cloud browsers\nreachable ONLY via an explicit `--connection`; cloud state lives in S3 and is\nhydrated per pod. Your code never touches this \u2014 it just gets a `browser`/`tab`\nbound to whatever profile the connection selected.\n\n## Constraints\n\n- Browser clips: one command = one file; keep a command's work self-contained\n (open what you need, close it, return). The Edge is stateless about your code\n between calls.\n- Never embed credentials in the clip source \u2014 rely on the profile's logged-in\n session (`tab.cookie` / `tab.fetch`). Published source is visible to installers.\n- Exec has a timeout (default 30s, caller-set up to 120s). Long scrapes should page,\n not block.\n";
2
2
  //# sourceMappingURL=parall-clip-authoring.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"parall-clip-authoring.d.ts","sourceRoot":"","sources":["../../src/skills/parall-clip-authoring.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,2BAA2B,06XA0QvC,CAAC"}
1
+ {"version":3,"file":"parall-clip-authoring.d.ts","sourceRoot":"","sources":["../../src/skills/parall-clip-authoring.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,2BAA2B,kydAyTvC,CAAC"}
@@ -109,6 +109,7 @@ function parseTweets(data) { /* ... */ }
109
109
  - \`tab.cookie(name)\` · \`tab.fetch(url, opts)\` (in-browser fetch, carries the session)
110
110
  - \`tab.eval(expr)\` (escape hatch) · \`tab.click(sel)\` · \`tab.fill(sel, text)\` · \`tab.navigate(url)\`
111
111
  - \`tab.waitForSelector(sel)\` · \`tab.getTitle()\` · \`tab.getURL()\` · \`tab.screenshot()\` · \`tab.close()\`
112
+ - \`tab.setFileInput(sel, url, opts?)\` — upload a file into an \`<input type=file>\`
112
113
  - \`fetch\` — runtime-side HTTP, does NOT go through the browser (no session)
113
114
  - \`console\` — logs · \`args\` — the invocation input
114
115
 
@@ -117,6 +118,40 @@ returns structured data; eval is the last resort. Always \`tab.close()\` what yo
117
118
  open, and do it in a \`finally\` — an early return or a thrown fetch is exactly
118
119
  when the tab leaks.
119
120
 
121
+ ### Uploading a file
122
+
123
+ \`tab.setFileInput\` takes a URL, never a path, and the runtime — not the page —
124
+ fetches the bytes. That is what makes it work where an in-page \`fetch\` +
125
+ \`DataTransfer\` cannot: upload targets ship a Content-Security-Policy that
126
+ forbids the page from fetching an arbitrary file host (Instagram's \`default-src\`
127
+ allows only its own domains), and you cannot change a header on their site. The
128
+ runtime is not a page, so no CSP applies to it — and a large video never has to
129
+ pass through the page's memory.
130
+
131
+ \`\`\`js
132
+ await tab.setFileInput("input[type=file]", videoUrl, { filename: "clip.mp4" });
133
+ await tab.click("button[type=submit]");
134
+ await tab.waitForSelector(".upload-complete"); // ← do not skip this
135
+ \`\`\`
136
+
137
+ Four rules that decide whether your clip works:
138
+
139
+ 1. **The URL must be fetchable with no credentials** — a public direct link or a
140
+ signed temporary one. The runtime sends no cookies, so a Drive or Feishu link
141
+ copied from the address bar will not work: those are HTML pages behind a
142
+ login. If the file needs a session, use a logged-in tab to obtain a signed
143
+ direct link first, then pass THAT here. An HTML answer is refused with
144
+ \`EDGE_FILE_SOURCE_NOT_A_FILE\` rather than uploaded as if it were a file.
145
+ 2. **Finish the upload inside the same command.** The browser reads the file when
146
+ the page submits, and the runtime deletes it when your command ends. Injecting
147
+ and returning immediately uploads nothing — wait for the site to confirm.
148
+ 3. **Main-document inputs only.** An input inside an iframe or a shadow root is
149
+ not addressable and returns \`EDGE_FILE_SELECTOR_MISS\`. If the page rebuilds
150
+ the input after you inject (navigation, re-render), inject again.
151
+ 4. **\`opts.filename\` is what the page sees**, and many sites validate by
152
+ extension — set it when the URL has none. \`opts.timeoutMs\` bounds the
153
+ download. One file per call; there is no multi-file form yet.
154
+
120
155
  ## Develop → publish → iterate
121
156
 
122
157
  Use the platform \`parall clip\` subcommands — they reuse the credentials you
@@ -223,18 +258,30 @@ and the folder is just a manifest:
223
258
  }
224
259
  \`\`\`
225
260
 
226
- - The \`mcp\` block takes ONLY \`server_url\` and \`auth\` (\`"none" | "bearer" |
227
- "api_key" | "oauth"\`). Any other key is refused at publish — a credential
228
- belongs to the installing org's own configuration, NEVER to the clip
229
- definition.
261
+ - The \`mcp\` block takes ONLY \`server_url\`, \`auth\` (\`"none" | "api_key" |
262
+ "basic" | "oauth"\`, legacy \`"bearer"\` accepted) and \`auth_headers\`. Any
263
+ other key is refused at publish a credential belongs to the installing
264
+ org's own configuration, NEVER to the clip definition.
265
+ - \`auth\` is REQUIRED at publish: you know what your server speaks, and this
266
+ one word decides what the install form asks for (\`none\` = zero input,
267
+ \`api_key\` = key field(s), \`basic\` = username + password, \`oauth\` = a
268
+ Connect button).
269
+ - \`api_key\` delivers as a single \`X-API-Key\` header by default. When the
270
+ server wants a different shape, declare \`auth_headers\` (max 4 slots, one
271
+ admin-supplied value each): \`[{"name": "Authorization", "scheme":
272
+ "Bearer"}]\` for Bearer tokens, \`[{"name": "api-key"}]\` for a custom
273
+ header, or a pair like \`[{"name": "CF-Access-Client-Id"}, {"name":
274
+ "CF-Access-Client-Secret"}]\`. Framing/platform headers (Host, Cookie,
275
+ X-Prll-*, …) are refused.
230
276
  - \`server_url\` must be an absolute **https** URL with no embedded credentials,
231
277
  query, or fragment. It is review material, frozen with the approved version.
278
+ It stays OPTIONAL for self-hosted products where each org connects its own
279
+ instance URL.
232
280
  - Do NOT put the server in the top-level \`server\` / \`auth\` manifest keys —
233
281
  those are legacy Edge-manifest fields nothing reads. Only the \`mcp\` block
234
282
  declares the server.
235
- - Both fields are optional, but what you declare is LOCKED: the installing
236
- org's config must match it, and changing the URL or auth mode means
237
- republishing.
283
+ - What you declare is LOCKED: the installing org's config must match it, and
284
+ changing the URL, auth mode, or header shape means republishing.
238
285
  - Entering the credential / completing OAuth is a HUMAN step in the Clip
239
286
  Console (the config-write endpoints are session-only — an API key cannot
240
287
  call them). An org can add SEVERAL connections to one MCP clip — one
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parall/agent-core",
3
- "version": "1.55.0",
3
+ "version": "1.55.2",
4
4
  "description": "Shared agent runtime orchestration helpers for Parall",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -36,7 +36,7 @@
36
36
  "@opentelemetry/sdk-metrics": "^1.30.0",
37
37
  "@opentelemetry/sdk-trace-node": "^1.30.0",
38
38
  "undici": "^7.24.8",
39
- "@parall/sdk": "1.55.0"
39
+ "@parall/sdk": "1.55.2"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/node": "^22.0.0",
@@ -146,6 +146,13 @@ export type DispatchOpts = {
146
146
  sessionKey: string;
147
147
  context: DispatchContext;
148
148
  inputLifecycle?: DispatchInputLifecycle;
149
+ /**
150
+ * Refresh the dispatch inactivity deadline when the runtime makes progress
151
+ * that does not become a RuntimeEvent (for example, a streaming delta that
152
+ * the bridge intentionally suppresses). RuntimeEvents refresh it in the
153
+ * gateway automatically.
154
+ */
155
+ noteActivity?: () => void;
149
156
  };
150
157
 
151
158
  export type RuntimeInputState = 'started' | 'completed' | 'failed';
@@ -242,9 +249,10 @@ export interface DispatchAdapter {
242
249
 
243
250
  /**
244
251
  * Abort the in-flight dispatch for the given session. Called by the gateway
245
- * when the dispatch deadline is exceeded. Implementations should unblock the
246
- * `dispatch()` generator (e.g. close a turn sink, end stdin, fail a stream)
247
- * so the `for await` loop in `runDispatch` exits naturally. Must be idempotent.
252
+ * when the dispatch inactivity deadline is exceeded. Implementations should
253
+ * unblock the `dispatch()` generator (e.g. close a turn sink, end stdin,
254
+ * fail a stream) so the `for await` loop in `runDispatch` exits naturally.
255
+ * Must be idempotent.
248
256
  */
249
257
  abortDispatch?(sessionKey: string): void;
250
258
  }
@@ -0,0 +1,47 @@
1
+ export class DispatchInactivityDeadline {
2
+ private timer: ReturnType<typeof setTimeout> | null = null;
3
+ private expired = false;
4
+ private disposed = false;
5
+
6
+ constructor(
7
+ private readonly timeoutMs: number,
8
+ private readonly onExpire: () => void,
9
+ private readonly onDispose: () => void,
10
+ ) {}
11
+
12
+ readonly touch = (): void => {
13
+ if (this.timeoutMs <= 0 || this.expired || this.disposed) return;
14
+ if (this.timer) clearTimeout(this.timer);
15
+ this.timer = setTimeout(() => {
16
+ this.timer = null;
17
+ this.expired = true;
18
+ this.onExpire();
19
+ }, this.timeoutMs);
20
+ };
21
+
22
+ dispose(): void {
23
+ if (this.disposed) return;
24
+ this.disposed = true;
25
+ if (this.timer) clearTimeout(this.timer);
26
+ this.timer = null;
27
+ this.onDispose();
28
+ }
29
+ }
30
+
31
+ /** Session-keyed registry for activity signals that arrive outside dispatch(). */
32
+ export class DispatchInactivityDeadlines {
33
+ private readonly active = new Map<string, DispatchInactivityDeadline>();
34
+
35
+ start(sessionKey: string, timeoutMs: number, onExpire: () => void): DispatchInactivityDeadline {
36
+ const deadline = new DispatchInactivityDeadline(timeoutMs, onExpire, () => {
37
+ if (this.active.get(sessionKey) === deadline) this.active.delete(sessionKey);
38
+ });
39
+ this.active.set(sessionKey, deadline);
40
+ deadline.touch();
41
+ return deadline;
42
+ }
43
+
44
+ touch(sessionKey: string): void {
45
+ this.active.get(sessionKey)?.touch();
46
+ }
47
+ }
@@ -58,6 +58,7 @@ import {
58
58
  } from './gateway-lane-flow.js';
59
59
  import type { LaneFlowHost, TypedConsumeHooks } from './gateway-lane-flow.js';
60
60
  import { LaneLedger, bindLaneSession, releaseLocalMessageClaims } from './lane-ledger.js';
61
+ import { DispatchInactivityDeadlines } from './dispatch-inactivity-deadline.js';
61
62
  import { routeTrigger } from './routing.js';
62
63
  import { redactTurnOutcome } from './redact.js';
63
64
  import { StepPersister, isRetryableStepError } from './step-persister.js';
@@ -808,6 +809,7 @@ export class ParallAgentGateway {
808
809
  * an unrelated stalled fork's lane leased forever).
809
810
  */
810
811
  private sessionActiveLanes = new Map<string, string>();
812
+ private dispatchInactivityDeadlines = new DispatchInactivityDeadlines();
811
813
 
812
814
  noteSessionLane(sessionKey: string, laneKey: string | null): void {
813
815
  if (laneKey == null) this.sessionActiveLanes.delete(sessionKey);
@@ -817,11 +819,12 @@ export class ParallAgentGateway {
817
819
  /**
818
820
  * External runtime-activity signal for adapters whose tool activity does
819
821
  * not flow through the RuntimeEvent stream (openclaw hooks call this from
820
- * the tool-call lifecycle): renews the session's OWN active ledger lane so
821
- * a long tool call cannot outlive the lease and get dethroned mid-turn.
822
- * No-op without an active ledger lane for the session.
822
+ * the tool-call lifecycle): refreshes the dispatch inactivity deadline and
823
+ * renews the session's OWN active ledger lane, when present, so a long tool
824
+ * call cannot time out or get dethroned mid-turn.
823
825
  */
824
826
  touchRuntimeActivity(sessionKey: string): void {
827
+ this.dispatchInactivityDeadlines.touch(sessionKey);
825
828
  if (this.ledgerDisabled) return;
826
829
  const laneKey = this.sessionActiveLanes.get(sessionKey);
827
830
  if (laneKey) this.laneLedger?.renewByKey(laneKey);
@@ -1457,19 +1460,20 @@ export class ParallAgentGateway {
1457
1460
  // here and miss our in-flight count.
1458
1461
  this.inFlightDispatches++;
1459
1462
 
1460
- const deadlineTimer =
1461
- this.DISPATCH_DEADLINE_MS > 0
1462
- ? setTimeout(() => {
1463
- this.opts.log?.warn(
1464
- `dispatch deadline exceeded (${this.DISPATCH_DEADLINE_MS}ms) for ${event.messageId} on ${sessionKey}; aborting`,
1465
- );
1466
- try {
1467
- this.opts.dispatchAdapter.abortDispatch?.(sessionKey);
1468
- } catch (err) {
1469
- this.opts.log?.warn(`abortDispatch threw for ${sessionKey}: ${String(err)}`);
1470
- }
1471
- }, this.DISPATCH_DEADLINE_MS)
1472
- : null;
1463
+ const dispatchDeadline = this.dispatchInactivityDeadlines.start(
1464
+ sessionKey,
1465
+ this.DISPATCH_DEADLINE_MS,
1466
+ () => {
1467
+ this.opts.log?.warn(
1468
+ `dispatch inactivity deadline exceeded (${this.DISPATCH_DEADLINE_MS}ms) for ${event.messageId} on ${sessionKey}; aborting`,
1469
+ );
1470
+ try {
1471
+ this.opts.dispatchAdapter.abortDispatch?.(sessionKey);
1472
+ } catch (err) {
1473
+ this.opts.log?.warn(`abortDispatch threw for ${sessionKey}: ${String(err)}`);
1474
+ }
1475
+ },
1476
+ );
1473
1477
 
1474
1478
  let binding = this.sessionBindings.get(sessionKey);
1475
1479
  let inputStepsCreated = false;
@@ -1497,7 +1501,9 @@ export class ParallAgentGateway {
1497
1501
  sessionKey,
1498
1502
  context: dispatchContext,
1499
1503
  inputLifecycle,
1504
+ noteActivity: dispatchDeadline.touch,
1500
1505
  })) {
1506
+ dispatchDeadline.touch();
1501
1507
  if (runtimeEvent.type === 'runtime_session') {
1502
1508
  const priorAgentSessionId = binding?.agentSessionId;
1503
1509
  binding = await this.bindRuntimeSession(
@@ -1723,7 +1729,7 @@ export class ParallAgentGateway {
1723
1729
  }
1724
1730
  throw err;
1725
1731
  } finally {
1726
- if (deadlineTimer) clearTimeout(deadlineTimer);
1732
+ dispatchDeadline.dispose();
1727
1733
  const metricsSnapshot = getDispatchMetrics(sessionKey);
1728
1734
  const durationMs = metricsSnapshot ? Date.now() - metricsSnapshot.started_at : 0;
1729
1735
 
@@ -0,0 +1,6 @@
1
+ // Code generated by scripts/generate-platform-instructions.mjs; DO NOT EDIT.
2
+ // Source: prompt-source/codex-message-delivery.md
3
+ // Source SHA-256: a5a00b5777de3921e1c8d2731cee855bc0688c75f32c9f8c90f0067abb128cbc
4
+
5
+ export const PLATFORM_CODEX_MESSAGE_DELIVERY_SOURCE_SHA256 = 'a5a00b5777de3921e1c8d2731cee855bc0688c75f32c9f8c90f0067abb128cbc';
6
+ export const PLATFORM_CODEX_MESSAGE_DELIVERY_INSTRUCTIONS = "### Codex message delivery\n\nCodex's native `commentary` and `final` prose is not a Parall message. The\nbridge records that prose only as a suppressed session step; nobody in the chat\nsees it. Tool calls made from `commentary` still execute, so use the shell/exec\ntool there to run the Parall CLI.\n\nThroughout these Parall instructions, “reply”, “say”, “ask”, “report”,\n“update”, and “communicate” mean creating a collaborator-visible Parall\nartifact through the CLI, never emitting runtime-native assistant prose.\n\nWhen the current event warrants a visible response, deliver acknowledgements,\nmeaningful progress, blockers or questions, requested status, and final results\nwith `parall messages send` or `parall dm`. Native `commentary` or `final` prose\ndoes not satisfy that communication requirement. A reply is delivered only\nafter the CLI command succeeds.\n\nDo not mirror every routine runtime commentary heartbeat into the chat. After a\nsuccessful send, native `final` output is only an audit record and does not need\nto duplicate the collaborator-visible message.";
@@ -1,3 +1,4 @@
1
+ import { PLATFORM_CODEX_MESSAGE_DELIVERY_INSTRUCTIONS } from './generated/codex-message-delivery.js';
1
2
  import type { PlatformInstructionIdentity } from './generated/platform-instructions.js';
2
3
  import {
3
4
  PLATFORM_BRIDGE_WORKSPACE_INSTRUCTIONS,
@@ -5,26 +6,63 @@ import {
5
6
  } from './generated/platform-instructions.js';
6
7
  import { buildSkillReferences } from './skills/index.js';
7
8
 
8
- /**
9
- * Assemble the canonical standing instructions for bridge runtimes.
10
- *
11
- * Claude Code and Codex use different delivery channels, but their visible
12
- * content and section order are identical. Keeping that order here prevents
13
- * either adapter from silently missing a future shared section.
14
- */
15
- export function buildBridgePlatformInstructions(
9
+ function buildPlatformInstructions(
16
10
  workspaceDir: string,
11
+ runtimeAppendix: string,
17
12
  identity?: PlatformInstructionIdentity,
18
13
  capabilityFragments?: readonly string[],
19
14
  ): string {
20
15
  return renderPlatformInstructions({
21
16
  identity,
22
- runtimeAppendix: PLATFORM_BRIDGE_WORKSPACE_INSTRUCTIONS,
17
+ runtimeAppendix,
23
18
  capabilityFragments,
24
19
  skillReferences: buildSkillReferences(workspaceDir),
25
20
  });
26
21
  }
27
22
 
23
+ /**
24
+ * Assemble the canonical standing instructions shared by bridge runtimes.
25
+ */
26
+ export function buildBridgePlatformInstructions(
27
+ workspaceDir: string,
28
+ identity?: PlatformInstructionIdentity,
29
+ capabilityFragments?: readonly string[],
30
+ ): string {
31
+ return buildPlatformInstructions(
32
+ workspaceDir,
33
+ PLATFORM_BRIDGE_WORKSPACE_INSTRUCTIONS,
34
+ identity,
35
+ capabilityFragments,
36
+ );
37
+ }
38
+
39
+ /**
40
+ * Assemble Codex instructions with its native-channel delivery adapter.
41
+ *
42
+ * Codex names `commentary` and `final` as collaborator-facing channels in its
43
+ * built-in prompt, while the Parall bridge suppresses their text. Keeping this
44
+ * adapter in the runtime appendix makes the transport boundary explicit
45
+ * without leaking Codex terminology into Claude Code or Parel.
46
+ */
47
+ export function buildCodexPlatformInstructions(
48
+ workspaceDir: string,
49
+ identity?: PlatformInstructionIdentity,
50
+ capabilityFragments?: readonly string[],
51
+ ): string {
52
+ return buildPlatformInstructions(
53
+ workspaceDir,
54
+ [PLATFORM_BRIDGE_WORKSPACE_INSTRUCTIONS, PLATFORM_CODEX_MESSAGE_DELIVERY_INSTRUCTIONS].join(
55
+ '\n\n',
56
+ ),
57
+ identity,
58
+ capabilityFragments,
59
+ );
60
+ }
61
+
62
+ export {
63
+ PLATFORM_CODEX_MESSAGE_DELIVERY_INSTRUCTIONS,
64
+ PLATFORM_CODEX_MESSAGE_DELIVERY_SOURCE_SHA256,
65
+ } from './generated/codex-message-delivery.js';
28
66
  export type {
29
67
  PlatformInstructionIdentity,
30
68
  PlatformInstructionRenderOptions,
@@ -109,6 +109,7 @@ function parseTweets(data) { /* ... */ }
109
109
  - \`tab.cookie(name)\` · \`tab.fetch(url, opts)\` (in-browser fetch, carries the session)
110
110
  - \`tab.eval(expr)\` (escape hatch) · \`tab.click(sel)\` · \`tab.fill(sel, text)\` · \`tab.navigate(url)\`
111
111
  - \`tab.waitForSelector(sel)\` · \`tab.getTitle()\` · \`tab.getURL()\` · \`tab.screenshot()\` · \`tab.close()\`
112
+ - \`tab.setFileInput(sel, url, opts?)\` — upload a file into an \`<input type=file>\`
112
113
  - \`fetch\` — runtime-side HTTP, does NOT go through the browser (no session)
113
114
  - \`console\` — logs · \`args\` — the invocation input
114
115
 
@@ -117,6 +118,40 @@ returns structured data; eval is the last resort. Always \`tab.close()\` what yo
117
118
  open, and do it in a \`finally\` — an early return or a thrown fetch is exactly
118
119
  when the tab leaks.
119
120
 
121
+ ### Uploading a file
122
+
123
+ \`tab.setFileInput\` takes a URL, never a path, and the runtime — not the page —
124
+ fetches the bytes. That is what makes it work where an in-page \`fetch\` +
125
+ \`DataTransfer\` cannot: upload targets ship a Content-Security-Policy that
126
+ forbids the page from fetching an arbitrary file host (Instagram's \`default-src\`
127
+ allows only its own domains), and you cannot change a header on their site. The
128
+ runtime is not a page, so no CSP applies to it — and a large video never has to
129
+ pass through the page's memory.
130
+
131
+ \`\`\`js
132
+ await tab.setFileInput("input[type=file]", videoUrl, { filename: "clip.mp4" });
133
+ await tab.click("button[type=submit]");
134
+ await tab.waitForSelector(".upload-complete"); // ← do not skip this
135
+ \`\`\`
136
+
137
+ Four rules that decide whether your clip works:
138
+
139
+ 1. **The URL must be fetchable with no credentials** — a public direct link or a
140
+ signed temporary one. The runtime sends no cookies, so a Drive or Feishu link
141
+ copied from the address bar will not work: those are HTML pages behind a
142
+ login. If the file needs a session, use a logged-in tab to obtain a signed
143
+ direct link first, then pass THAT here. An HTML answer is refused with
144
+ \`EDGE_FILE_SOURCE_NOT_A_FILE\` rather than uploaded as if it were a file.
145
+ 2. **Finish the upload inside the same command.** The browser reads the file when
146
+ the page submits, and the runtime deletes it when your command ends. Injecting
147
+ and returning immediately uploads nothing — wait for the site to confirm.
148
+ 3. **Main-document inputs only.** An input inside an iframe or a shadow root is
149
+ not addressable and returns \`EDGE_FILE_SELECTOR_MISS\`. If the page rebuilds
150
+ the input after you inject (navigation, re-render), inject again.
151
+ 4. **\`opts.filename\` is what the page sees**, and many sites validate by
152
+ extension — set it when the URL has none. \`opts.timeoutMs\` bounds the
153
+ download. One file per call; there is no multi-file form yet.
154
+
120
155
  ## Develop → publish → iterate
121
156
 
122
157
  Use the platform \`parall clip\` subcommands — they reuse the credentials you
@@ -223,18 +258,30 @@ and the folder is just a manifest:
223
258
  }
224
259
  \`\`\`
225
260
 
226
- - The \`mcp\` block takes ONLY \`server_url\` and \`auth\` (\`"none" | "bearer" |
227
- "api_key" | "oauth"\`). Any other key is refused at publish — a credential
228
- belongs to the installing org's own configuration, NEVER to the clip
229
- definition.
261
+ - The \`mcp\` block takes ONLY \`server_url\`, \`auth\` (\`"none" | "api_key" |
262
+ "basic" | "oauth"\`, legacy \`"bearer"\` accepted) and \`auth_headers\`. Any
263
+ other key is refused at publish a credential belongs to the installing
264
+ org's own configuration, NEVER to the clip definition.
265
+ - \`auth\` is REQUIRED at publish: you know what your server speaks, and this
266
+ one word decides what the install form asks for (\`none\` = zero input,
267
+ \`api_key\` = key field(s), \`basic\` = username + password, \`oauth\` = a
268
+ Connect button).
269
+ - \`api_key\` delivers as a single \`X-API-Key\` header by default. When the
270
+ server wants a different shape, declare \`auth_headers\` (max 4 slots, one
271
+ admin-supplied value each): \`[{"name": "Authorization", "scheme":
272
+ "Bearer"}]\` for Bearer tokens, \`[{"name": "api-key"}]\` for a custom
273
+ header, or a pair like \`[{"name": "CF-Access-Client-Id"}, {"name":
274
+ "CF-Access-Client-Secret"}]\`. Framing/platform headers (Host, Cookie,
275
+ X-Prll-*, …) are refused.
230
276
  - \`server_url\` must be an absolute **https** URL with no embedded credentials,
231
277
  query, or fragment. It is review material, frozen with the approved version.
278
+ It stays OPTIONAL for self-hosted products where each org connects its own
279
+ instance URL.
232
280
  - Do NOT put the server in the top-level \`server\` / \`auth\` manifest keys —
233
281
  those are legacy Edge-manifest fields nothing reads. Only the \`mcp\` block
234
282
  declares the server.
235
- - Both fields are optional, but what you declare is LOCKED: the installing
236
- org's config must match it, and changing the URL or auth mode means
237
- republishing.
283
+ - What you declare is LOCKED: the installing org's config must match it, and
284
+ changing the URL, auth mode, or header shape means republishing.
238
285
  - Entering the credential / completing OAuth is a HUMAN step in the Clip
239
286
  Console (the config-write endpoints are session-only — an API key cannot
240
287
  call them). An org can add SEVERAL connections to one MCP clip — one