@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.
- package/dist/dispatch-adapter.d.ts +11 -3
- package/dist/dispatch-adapter.d.ts.map +1 -1
- package/dist/dispatch-inactivity-deadline.d.ts +18 -0
- package/dist/dispatch-inactivity-deadline.d.ts.map +1 -0
- package/dist/dispatch-inactivity-deadline.js +49 -0
- package/dist/gateway-base.d.ts +4 -3
- package/dist/gateway-base.d.ts.map +1 -1
- package/dist/gateway-base.js +18 -16
- package/dist/generated/codex-message-delivery.d.ts +3 -0
- package/dist/generated/codex-message-delivery.d.ts.map +1 -0
- package/dist/generated/codex-message-delivery.js +5 -0
- package/dist/platform-instructions.d.ts +11 -5
- package/dist/platform-instructions.d.ts.map +1 -1
- package/dist/platform-instructions.js +21 -9
- package/dist/skills/parall-clip-authoring.d.ts +1 -1
- package/dist/skills/parall-clip-authoring.d.ts.map +1 -1
- package/dist/skills/parall-clip-authoring.js +54 -7
- package/package.json +2 -2
- package/src/dispatch-adapter.ts +11 -3
- package/src/dispatch-inactivity-deadline.ts +47 -0
- package/src/gateway-base.ts +23 -17
- package/src/generated/codex-message-delivery.ts +6 -0
- package/src/platform-instructions.ts +47 -9
- package/src/skills/parall-clip-authoring.ts +54 -7
|
@@ -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
|
|
216
|
-
* `dispatch()` generator (e.g. close a turn sink, end stdin,
|
|
217
|
-
* so the `for await` loop in `runDispatch` exits naturally.
|
|
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
|
+
}
|
package/dist/gateway-base.d.ts
CHANGED
|
@@ -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):
|
|
170
|
-
*
|
|
171
|
-
*
|
|
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":"AAIA,OAAO,EAAgC,YAAY,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACnF,OAAO,KAAK,EACV,qBAAqB,EAkBtB,MAAM,aAAa,CAAC;AAYrB,OAAO,EAGL,KAAK,eAAe,EAGpB,KAAK,aAAa,EAElB,KAAK,kBAAkB,EAExB,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"gateway-base.d.ts","sourceRoot":"","sources":["../src/gateway-base.ts"],"names":[],"mappings":"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"}
|
package/dist/gateway-base.js
CHANGED
|
@@ -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):
|
|
550
|
-
*
|
|
551
|
-
*
|
|
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
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
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
|
-
|
|
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
|
|
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":"
|
|
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
|
|
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,
|
|
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
|
|
227
|
-
"
|
|
228
|
-
|
|
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
|
-
-
|
|
236
|
-
|
|
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.
|
|
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.
|
|
39
|
+
"@parall/sdk": "1.55.2"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@types/node": "^22.0.0",
|
package/src/dispatch-adapter.ts
CHANGED
|
@@ -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
|
|
246
|
-
* `dispatch()` generator (e.g. close a turn sink, end stdin,
|
|
247
|
-
* so the `for await` loop in `runDispatch` exits naturally.
|
|
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
|
+
}
|
package/src/gateway-base.ts
CHANGED
|
@@ -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):
|
|
821
|
-
*
|
|
822
|
-
*
|
|
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
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
227
|
-
"
|
|
228
|
-
|
|
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
|
-
-
|
|
236
|
-
|
|
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
|