@parall/daemon 1.52.1 → 1.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundle/manifest.json +11 -11
- package/bundle/parall-browser-pod.js +66 -12
- package/bundle/parall-claude-agent.js +428 -60
- package/bundle/parall-codex-agent.js +431 -62
- package/bundle/parall-daemon.js +75 -12
- package/package.json +6 -6
|
@@ -51511,6 +51511,8 @@ var ENDPOINTS = {
|
|
|
51511
51511
|
SLACK_HISTORY: (orgId) => `${API_BASE}/orgs/${orgId}/agents/me/slack/history`,
|
|
51512
51512
|
SLACK_MEMBERS: (orgId) => `${API_BASE}/orgs/${orgId}/agents/me/slack/members`,
|
|
51513
51513
|
SLACK_STATUS: (orgId) => `${API_BASE}/orgs/${orgId}/agents/me/slack/status`,
|
|
51514
|
+
// WeChat tier-B read verb (agent-only; internal research preview).
|
|
51515
|
+
WECHAT_CONTACTS: (orgId) => `${API_BASE}/orgs/${orgId}/agents/me/wechat/contacts`,
|
|
51514
51516
|
// Invitations (org-scoped, admin)
|
|
51515
51517
|
ORG_INVITATIONS: (orgId) => `${API_BASE}/orgs/${orgId}/invitations`,
|
|
51516
51518
|
ORG_INVITATION: (orgId, invId) => `${API_BASE}/orgs/${orgId}/invitations/${invId}`,
|
|
@@ -51672,8 +51674,6 @@ var ENDPOINTS = {
|
|
|
51672
51674
|
// Live viewer command — on api-server (API_BASE), not clip-service: it rides
|
|
51673
51675
|
// the machine control-plane request/reply bridge that lives in api-server.
|
|
51674
51676
|
BROWSER_PROFILE_VIEWER_COMMAND: (orgId, profileId) => `${API_BASE}/orgs/${orgId}/browser-profiles/${profileId}/viewer/command`,
|
|
51675
|
-
// Clip registry (global, served by clip-service → Pinix Hub proxy)
|
|
51676
|
-
CLIP_REGISTRY: () => `${CLIP_BASE}/registry/clips`,
|
|
51677
51677
|
// Edge device endpoints
|
|
51678
51678
|
ORG_EDGE: (orgId) => `/api/v1/orgs/${orgId}/edge`,
|
|
51679
51679
|
ORG_EDGE_DEVICES: (orgId) => `/api/v1/orgs/${orgId}/edge/devices`,
|
|
@@ -51690,15 +51690,20 @@ var ENDPOINTS = {
|
|
|
51690
51690
|
ORG_EDGE_VIEWER_COMMAND: (orgId, edgeId) => `/api/v1/orgs/${orgId}/edge/${edgeId}/viewer/command`,
|
|
51691
51691
|
CLIP_CONNECTIONS: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}/connections`,
|
|
51692
51692
|
CLIP_CONNECTION: (orgId, connId) => `/api/v1/orgs/${orgId}/clip-connections/${connId}`,
|
|
51693
|
-
// Clip registry (v3, org-scoped, served by api-server — `crg_` entries
|
|
51694
|
-
// Pinix Hub catalog proxy above is a different, id-less surface)
|
|
51693
|
+
// Clip registry (v3, org-scoped, served by api-server — `crg_` entries)
|
|
51695
51694
|
ORG_CLIP_REGISTRY: (orgId) => `/api/v1/orgs/${orgId}/clip-registry`,
|
|
51695
|
+
ORG_CLIP_REGISTRY_CLIP: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}`,
|
|
51696
|
+
ORG_CLIP_REGISTRY_PUBLISH: (orgId) => `/api/v1/orgs/${orgId}/clip-registry/publish`,
|
|
51696
51697
|
ORG_CLIP_INSTALL: (orgId) => `/api/v1/orgs/${orgId}/clips/install`,
|
|
51697
51698
|
ORG_CLIPS_INSTALLED: (orgId) => `/api/v1/orgs/${orgId}/clips/installed`,
|
|
51699
|
+
ORG_CLIP_UNINSTALL: (orgId, clipId) => `/api/v1/orgs/${orgId}/clips/${clipId}/uninstall`,
|
|
51700
|
+
// Per-org agent exec access for one clip (default all agents; PUT human-only)
|
|
51701
|
+
ORG_CLIP_EXEC_ACCESS: (orgId, clipId) => `/api/v1/orgs/${orgId}/clips/${clipId}/exec-access`,
|
|
51698
51702
|
// MCP clip server config (cap:clip-mcp; publisher-org only — cross-org gets
|
|
51699
51703
|
// 403 MCP_CROSS_ORG_DISABLED on the whole family, reads included)
|
|
51700
51704
|
ORG_CLIP_MCP_CONFIG: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}/mcp-config`,
|
|
51701
|
-
ORG_CLIP_MCP_TOOLS_REFRESH: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}/mcp-config/tools/refresh
|
|
51705
|
+
ORG_CLIP_MCP_TOOLS_REFRESH: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}/mcp-config/tools/refresh`,
|
|
51706
|
+
ORG_CLIP_MCP_OAUTH_DISCONNECT: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}/mcp-config/oauth/disconnect`
|
|
51702
51707
|
};
|
|
51703
51708
|
var WS_EVENTS = {
|
|
51704
51709
|
// Client -> Server
|
|
@@ -53194,6 +53199,14 @@ var ParallClient = class _ParallClient {
|
|
|
53194
53199
|
async setSlackStatus(orgId, input) {
|
|
53195
53200
|
await this.request("POST", ENDPOINTS.SLACK_STATUS(orgId), input);
|
|
53196
53201
|
}
|
|
53202
|
+
/**
|
|
53203
|
+
* WeChat tier-B read verb (agent-only; internal research preview): the
|
|
53204
|
+
* account's address book — friend wxids, saved group room ids, followed
|
|
53205
|
+
* official accounts. Same live gate as the send verb.
|
|
53206
|
+
*/
|
|
53207
|
+
async listWechatContacts(orgId) {
|
|
53208
|
+
return this.request("GET", ENDPOINTS.WECHAT_CONTACTS(orgId));
|
|
53209
|
+
}
|
|
53197
53210
|
async listChannelConversations(orgId, connectionId) {
|
|
53198
53211
|
return this.request("GET", ENDPOINTS.CHANNEL_CONNECTION_CONVERSATIONS(orgId, connectionId));
|
|
53199
53212
|
}
|
|
@@ -53732,17 +53745,11 @@ var ParallClient = class _ParallClient {
|
|
|
53732
53745
|
async revokeBrowserProfileConsent(orgId, profileId, clipId) {
|
|
53733
53746
|
await this.request("DELETE", ENDPOINTS.BROWSER_PROFILE_CONSENT(orgId, profileId, clipId));
|
|
53734
53747
|
}
|
|
53735
|
-
async listRegistryClips(q) {
|
|
53736
|
-
const url = ENDPOINTS.CLIP_REGISTRY() + (q ? `?q=${encodeURIComponent(q)}` : "");
|
|
53737
|
-
const resp = await this.request("GET", url);
|
|
53738
|
-
return resp.data;
|
|
53739
|
-
}
|
|
53740
53748
|
// ---- Clip registry (v3, api-server org registry — `crg_` entries) ----
|
|
53741
53749
|
/**
|
|
53742
53750
|
* List registry clips visible to the org: its own plus public+approved
|
|
53743
53751
|
* cross-org entries. This is the surface `installRegistryClip` and clip
|
|
53744
|
-
* connections operate on
|
|
53745
|
-
* ({@link listRegistryClips}), whose entries carry no `crg_` id.
|
|
53752
|
+
* connections operate on.
|
|
53746
53753
|
*/
|
|
53747
53754
|
async listOrgRegistryClips(orgId) {
|
|
53748
53755
|
const resp = await this.request("GET", `${ENDPOINTS.ORG_CLIP_REGISTRY(orgId)}?limit=100`);
|
|
@@ -53757,11 +53764,58 @@ var ParallClient = class _ParallClient {
|
|
|
53757
53764
|
async installRegistryClip(orgId, clipId) {
|
|
53758
53765
|
return this.request("POST", ENDPOINTS.ORG_CLIP_INSTALL(orgId), { clip_id: clipId });
|
|
53759
53766
|
}
|
|
53767
|
+
/**
|
|
53768
|
+
* Remove an org's install of a registry clip. Keyed by the REGISTRY id
|
|
53769
|
+
* (`crg_`), the same id {@link installRegistryClip} takes — the v2
|
|
53770
|
+
* `deleteClip` operates on a different table and cannot reach these.
|
|
53771
|
+
*/
|
|
53772
|
+
async uninstallRegistryClip(orgId, clipId) {
|
|
53773
|
+
await this.request("DELETE", ENDPOINTS.ORG_CLIP_UNINSTALL(orgId, clipId));
|
|
53774
|
+
}
|
|
53760
53775
|
/** List the org's installed registry clips (full entries). */
|
|
53761
53776
|
async listInstalledRegistryClips(orgId) {
|
|
53762
53777
|
const resp = await this.request("GET", ENDPOINTS.ORG_CLIPS_INSTALLED(orgId));
|
|
53763
53778
|
return resp ?? [];
|
|
53764
53779
|
}
|
|
53780
|
+
/**
|
|
53781
|
+
* Fetch one registry clip by its `crg_` id. For a clip OWNED by the calling
|
|
53782
|
+
* org the entry additionally carries `review_status`/`review_note` — the
|
|
53783
|
+
* state of the latest submitted version and the reviewer's rejection
|
|
53784
|
+
* feedback. This is the polling surface for "did platform review approve my
|
|
53785
|
+
* public publish yet"; cross-org callers see only public+approved entries
|
|
53786
|
+
* and never any review-state fields.
|
|
53787
|
+
*/
|
|
53788
|
+
async getRegistryClip(orgId, clipId) {
|
|
53789
|
+
return this.request("GET", ENDPOINTS.ORG_CLIP_REGISTRY_CLIP(orgId, clipId));
|
|
53790
|
+
}
|
|
53791
|
+
/**
|
|
53792
|
+
* Publish (create, or author-only update) a clip in the org registry,
|
|
53793
|
+
* replacing its whole file set. See {@link PublishRegistryClipRequest} for
|
|
53794
|
+
* the visibility/review semantics; the returned entry carries
|
|
53795
|
+
* `review_status` when a public publish queued a review submission — poll
|
|
53796
|
+
* {@link getRegistryClip} for the outcome.
|
|
53797
|
+
*/
|
|
53798
|
+
async publishRegistryClip(orgId, req) {
|
|
53799
|
+
return this.request("POST", ENDPOINTS.ORG_CLIP_REGISTRY_PUBLISH(orgId), req, void 0, false, {
|
|
53800
|
+
timeoutMs: 6e4
|
|
53801
|
+
});
|
|
53802
|
+
}
|
|
53803
|
+
/**
|
|
53804
|
+
* Read a clip's per-org agent exec access. Org-member readable (agents
|
|
53805
|
+
* included, so a denied agent can learn why exec answered
|
|
53806
|
+
* `CLIP_AGENT_NOT_ALLOWED`).
|
|
53807
|
+
*/
|
|
53808
|
+
async getClipAgentExecAccess(orgId, clipId) {
|
|
53809
|
+
return this.request("GET", ENDPOINTS.ORG_CLIP_EXEC_ACCESS(orgId, clipId));
|
|
53810
|
+
}
|
|
53811
|
+
/**
|
|
53812
|
+
* Replace a clip's per-org agent exec access. Human-only (agent principals
|
|
53813
|
+
* get 403); under `all_agents` the `agent_ids` must be empty. Every granted
|
|
53814
|
+
* id must be an active agent of this org (else `400 INVALID_INPUT`).
|
|
53815
|
+
*/
|
|
53816
|
+
async putClipAgentExecAccess(orgId, clipId, access) {
|
|
53817
|
+
return this.request("PUT", ENDPOINTS.ORG_CLIP_EXEC_ACCESS(orgId, clipId), access);
|
|
53818
|
+
}
|
|
53765
53819
|
// ---- Edge devices ----
|
|
53766
53820
|
async listEdgeDevices(orgId) {
|
|
53767
53821
|
return this.request("GET", ENDPOINTS.ORG_EDGE_DEVICES(orgId));
|
|
@@ -54511,10 +54565,16 @@ function buildSendMessageHint(event) {
|
|
|
54511
54565
|
const replyTo = event.channelExternalMessageId ? ` --reply-to "${event.channelExternalMessageId}"` : "";
|
|
54512
54566
|
return `
|
|
54513
54567
|
<system-reminder>To reply, use the platform verb: \`parall slack send${channelArg}${replyTo} --text <your reply>\`. In channels --reply-to is REQUIRED (the reply lands in that message's thread); in DMs it is optional (DMs are linear). \`parall slack send\` is the ONLY outbound path \u2014 your plain text output is NOT delivered to the external conversation.</system-reminder>`;
|
|
54568
|
+
}
|
|
54569
|
+
if (event.channelProvider === "wechat") {
|
|
54570
|
+
const toArg = event.channelExternalConversationId ? ` --to "${event.channelExternalConversationId}"` : " --to <wxid from this event>";
|
|
54571
|
+
const atArg = event.channelConversationType === "group" ? event.senderId && event.senderId !== "external" ? ` --at "${event.senderId}"` : " --at <wxid of the person you are answering>" : "";
|
|
54572
|
+
return `
|
|
54573
|
+
<system-reminder>To reply, use the platform verb: \`parall wechat send${toArg}${atArg} --text <your reply>\`. In group chats, --at @-mentions the person you are answering. \`parall wechat send\` is the ONLY outbound path \u2014 your plain text output is NOT delivered to the external conversation.</system-reminder>`;
|
|
54514
54574
|
}
|
|
54515
54575
|
if (!event.channelProvider) {
|
|
54516
54576
|
return `
|
|
54517
|
-
<system-reminder>To reply, use the channel capability granted in your system prompt \u2014 for Feishu conversations that is \`lark-cli im\`, for Slack it is \`parall slack send\` (pass the message id from this event as --reply-to)
|
|
54577
|
+
<system-reminder>To reply, use the channel capability granted in your system prompt \u2014 for Feishu conversations that is \`lark-cli im\`, for Slack it is \`parall slack send\` (pass the message id from this event as --reply-to), for WeChat it is \`parall wechat send\`. That capability is the ONLY outbound path \u2014 your plain text output is NOT delivered to the external conversation.</system-reminder>`;
|
|
54518
54578
|
}
|
|
54519
54579
|
const convRef = event.channelExternalConversationId ? `chat_id "${event.channelExternalConversationId}"` : "the conversation id named in this event";
|
|
54520
54580
|
const threadAlt = event.channelExternalMessageId ? ` To reply threaded to this specific message, reference message_id "${event.channelExternalMessageId}".` : "";
|
|
@@ -54993,13 +55053,33 @@ var LaneLedger = class {
|
|
|
54993
55053
|
};
|
|
54994
55054
|
|
|
54995
55055
|
// ts/agent-core/dist/gateway-lane-flow.js
|
|
55056
|
+
function shiftMainBufferGroup(host, groupKey, preserveInjectedFrameBoundary) {
|
|
55057
|
+
const events = [];
|
|
55058
|
+
while (host.dispatchState.mainBuffer[0] && host.dispatchGroupKey(host.dispatchState.mainBuffer[0]) === groupKey && (!preserveInjectedFrameBoundary || events.length === 0)) {
|
|
55059
|
+
events.push(host.dispatchState.mainBuffer.shift());
|
|
55060
|
+
}
|
|
55061
|
+
return events;
|
|
55062
|
+
}
|
|
55063
|
+
function hasUninjectedSameLaneEventAhead(host, event) {
|
|
55064
|
+
const eventIndex = host.dispatchState.mainBuffer.lastIndexOf(event);
|
|
55065
|
+
if (eventIndex <= 0)
|
|
55066
|
+
return false;
|
|
55067
|
+
const groupKey = host.dispatchGroupKey(event);
|
|
55068
|
+
return host.dispatchState.mainBuffer.slice(0, eventIndex).some((buffered) => host.dispatchGroupKey(buffered) === groupKey && !host.injectedMainBufferEvents.has(buffered));
|
|
55069
|
+
}
|
|
54996
55070
|
async function steerLaneMessage(host, event) {
|
|
54997
55071
|
const { laneLedger: ledger, opts } = host;
|
|
54998
55072
|
const adapter = opts.dispatchAdapter;
|
|
54999
55073
|
if (!ledger || !adapter.enqueueDuringDispatch)
|
|
55000
55074
|
return;
|
|
55075
|
+
if (hasUninjectedSameLaneEventAhead(host, event))
|
|
55076
|
+
return;
|
|
55001
55077
|
const folded = await ledger.steerLive(event);
|
|
55002
|
-
if (folded
|
|
55078
|
+
if (!folded)
|
|
55079
|
+
return;
|
|
55080
|
+
const injected = await adapter.enqueueDuringDispatch(opts.runtimeKey, buildEventBody(event), folded.inputLifecycle);
|
|
55081
|
+
if (injected) {
|
|
55082
|
+
host.injectedMainBufferEvents.add(event);
|
|
55003
55083
|
opts.log?.info(`steer folded+injected for ${event.messageId} (will drain for bookkeeping)`);
|
|
55004
55084
|
}
|
|
55005
55085
|
}
|
|
@@ -55935,9 +56015,12 @@ import * as path2 from "node:path";
|
|
|
55935
56015
|
import { fileURLToPath } from "node:url";
|
|
55936
56016
|
var CAPABILITY_FEISHU_CLI = "feishu-cli";
|
|
55937
56017
|
var CAPABILITY_SLACK_SEND = "slack-send";
|
|
56018
|
+
var CAPABILITY_WECHAT_SEND = "wechat-send";
|
|
55938
56019
|
function channelCapabilityKeyFor(provider) {
|
|
55939
56020
|
if (provider === "slack")
|
|
55940
56021
|
return CAPABILITY_SLACK_SEND;
|
|
56022
|
+
if (provider === "wechat")
|
|
56023
|
+
return CAPABILITY_WECHAT_SEND;
|
|
55941
56024
|
return `${provider}-cli`;
|
|
55942
56025
|
}
|
|
55943
56026
|
var CHANNEL_POINTER_MAGIC = "parall channel capability pointer";
|
|
@@ -57120,6 +57203,7 @@ var ParallAgentGateway = class {
|
|
|
57120
57203
|
// (stable mapping; avoids one connection fetch per inbound message).
|
|
57121
57204
|
channelConnectionProviders = /* @__PURE__ */ new Map();
|
|
57122
57205
|
dispatchedMessages = /* @__PURE__ */ new Set();
|
|
57206
|
+
injectedMainBufferEvents = /* @__PURE__ */ new WeakSet();
|
|
57123
57207
|
// Per-WorkItem failure backoff for typed dispatch consumption — see
|
|
57124
57208
|
// LaneFlowHost.typedRedriveBackoff in gateway-lane-flow.ts.
|
|
57125
57209
|
typedRedriveBackoff = /* @__PURE__ */ new Map();
|
|
@@ -58332,13 +58416,10 @@ var ParallAgentGateway = class {
|
|
|
58332
58416
|
break;
|
|
58333
58417
|
}
|
|
58334
58418
|
const groupKey = this.dispatchGroupKey(this.dispatchState.mainBuffer[0]);
|
|
58335
|
-
const
|
|
58336
|
-
|
|
58337
|
-
events.push(this.dispatchState.mainBuffer.shift());
|
|
58338
|
-
}
|
|
58419
|
+
const hasPendingInjections = this.opts.dispatchAdapter.hasPendingInjections?.(this.opts.runtimeKey) ?? false;
|
|
58420
|
+
const events = shiftMainBufferGroup(this.laneFlowHost(), groupKey, hasPendingInjections && this.opts.dispatchAdapter.inputLifecycleMode === "explicit");
|
|
58339
58421
|
const event = events[events.length - 1];
|
|
58340
58422
|
const earlier = events.slice(0, -1);
|
|
58341
|
-
const hasPendingInjections = this.opts.dispatchAdapter.hasPendingInjections?.(this.opts.runtimeKey) ?? false;
|
|
58342
58423
|
const pendingFork = hasPendingInjections ? [] : this.dispatchState.pendingForkResults.splice(0);
|
|
58343
58424
|
const forkPrefix = buildForkResultPrefix(pendingFork);
|
|
58344
58425
|
this.dispatchState.mainCurrentTargetId = event.targetId;
|
|
@@ -59051,7 +59132,7 @@ var ParallAgentGateway = class {
|
|
|
59051
59132
|
}
|
|
59052
59133
|
}
|
|
59053
59134
|
const keys = this.opts.getCapabilityKeys?.() ?? [];
|
|
59054
|
-
const cliCapable = provider ? keys.includes(channelCapabilityKeyFor(provider)) : keys.some((k) => k.endsWith("-cli") || k === CAPABILITY_SLACK_SEND);
|
|
59135
|
+
const cliCapable = provider ? keys.includes(channelCapabilityKeyFor(provider)) : keys.some((k) => k.endsWith("-cli") || k === CAPABILITY_SLACK_SEND || k === CAPABILITY_WECHAT_SEND);
|
|
59055
59136
|
const event = {
|
|
59056
59137
|
type: "channel_message",
|
|
59057
59138
|
targetId: conv.id,
|
|
@@ -60377,41 +60458,55 @@ CLI command results are JSON on stdout; mutation commands may emit auxiliary hin
|
|
|
60377
60458
|
var PARALL_CLIPS_SKILL = `# Parall Clips
|
|
60378
60459
|
|
|
60379
60460
|
Clips are packaged capabilities that let agents operate external systems \u2014
|
|
60380
|
-
APIs
|
|
60461
|
+
APIs, websites, remote tools \u2014 through named commands installed in the org.
|
|
60381
60462
|
|
|
60382
60463
|
## Discover
|
|
60383
60464
|
|
|
60384
60465
|
\`\`\`bash
|
|
60385
|
-
parall clip list
|
|
60386
|
-
parall clip info <
|
|
60466
|
+
parall clip list # installed clips + each clip's connections
|
|
60467
|
+
parall clip info <clip> # commands + per-command params (manifest)
|
|
60468
|
+
parall clip connections <clip> # one clip's connections, full rows
|
|
60469
|
+
parall clip tools <clip> # MCP clips only: live tool schemas
|
|
60387
60470
|
\`\`\`
|
|
60388
60471
|
|
|
60389
|
-
|
|
60472
|
+
\`clip list\` answers both discovery questions at once: WHICH clip (name,
|
|
60473
|
+
description, version) and WHERE it can run \u2014 every connection with its
|
|
60474
|
+
\`ccn_\u2026\` id, alias, and target kind:
|
|
60390
60475
|
|
|
60391
|
-
|
|
60392
|
-
|
|
60393
|
-
|
|
60394
|
-
|
|
60395
|
-
|
|
60396
|
-
|
|
60397
|
-
|
|
60476
|
+
- \`cloud\` \u2014 an org-shared cloud profile (a maintainer's signed-in browser)
|
|
60477
|
+
- \`desktop\` \u2014 a member's own device (only its owner can exec through it)
|
|
60478
|
+
- \`mcp\` \u2014 a remote MCP tool server
|
|
60479
|
+
- \`device\` \u2014 a device whose placement could not be resolved just now (the
|
|
60480
|
+
device list was unavailable); don't guess which kind it is \u2014 re-run
|
|
60481
|
+
discovery, and treat a persistent \`device\` like an unverified target
|
|
60482
|
+
- \`orphaned\` \u2014 the target device is gone; the connection is unusable
|
|
60398
60483
|
|
|
60399
|
-
|
|
60484
|
+
Pick the connection whose alias names the account/device the task needs
|
|
60485
|
+
(e.g. \`ins-nyc\` vs \`ins-boston\`). A row without an alias can only be
|
|
60486
|
+
referenced by \`ccn_\` id \u2014 when aliases are missing and several connections
|
|
60487
|
+
could match, ask a human to name them in the Clip Console rather than
|
|
60488
|
+
guessing which signed-in account you are about to act through.
|
|
60400
60489
|
|
|
60401
|
-
|
|
60402
|
-
profile. **Name the target explicitly.** A cloud profile has NO implicit
|
|
60403
|
-
route; omitting the target entirely is a desktop-only legacy form that
|
|
60404
|
-
reaches just YOUR OWN online desktop device \u2014 never a shared cloud profile.
|
|
60490
|
+
## Execute
|
|
60405
60491
|
|
|
60406
60492
|
\`\`\`bash
|
|
60407
|
-
parall clip exec <clip> <command> [args] --connection <
|
|
60493
|
+
parall clip exec <clip> <command> [args] --connection <ccn_|alias> # the normal form
|
|
60408
60494
|
parall clip exec browser-tools screenshot '{"url":"\u2026"}' --connection cloud-main
|
|
60409
60495
|
\`\`\`
|
|
60410
60496
|
|
|
60497
|
+
Build \`args\` as JSON per the command's params from \`clip info\` (or the
|
|
60498
|
+
tool's \`inputSchema\` from \`clip tools\` for MCP clips \u2014 tool names are NOT
|
|
60499
|
+
frozen in the manifest, so never guess a tool name or its argument shape).
|
|
60500
|
+
Results are JSON on stdout; failures print a typed error.
|
|
60501
|
+
|
|
60502
|
+
**Name the target explicitly.** A cloud profile has NO implicit route;
|
|
60503
|
+
omitting the target entirely is a desktop-only legacy form that reaches just
|
|
60504
|
+
YOUR OWN online desktop device \u2014 never a shared cloud profile.
|
|
60505
|
+
|
|
60411
60506
|
- A cloud (hosted) profile is reachable ONLY via \`--connection\` \u2014 the clip
|
|
60412
|
-
connection its maintainer bound
|
|
60413
|
-
|
|
60414
|
-
|
|
60507
|
+
connection its maintainer bound. That binding IS your authorization;
|
|
60508
|
+
without one the server answers \`HOSTED_CONNECTION_REQUIRED\` and the fix
|
|
60509
|
+
is to ask an owner/admin to bind the clip, never to retry.
|
|
60415
60510
|
- \`--edge <edgeId>\` targets only a desktop device YOU own.
|
|
60416
60511
|
- Waiting on a cloud profile is handled by the CLI: \`EDGE_ACTIVATING\` (cold
|
|
60417
60512
|
start), \`EDGE_BUSY\` (another exec is running) and
|
|
@@ -60423,19 +60518,10 @@ parall clip exec browser-tools screenshot '{"url":"\u2026"}' --connection cloud-
|
|
|
60423
60518
|
|
|
60424
60519
|
## MCP clips (remote tool servers)
|
|
60425
60520
|
|
|
60426
|
-
|
|
60427
|
-
|
|
60428
|
-
|
|
60429
|
-
|
|
60430
|
-
find the connection AND the tool schemas:
|
|
60431
|
-
|
|
60432
|
-
\`\`\`bash
|
|
60433
|
-
parall clip connections <alias> # the ccn_ id / alias to pass to --connection
|
|
60434
|
-
parall clip tools <alias> # tool names + descriptions + inputSchema (JSON)
|
|
60435
|
-
\`\`\`
|
|
60436
|
-
|
|
60437
|
-
Read each tool's \`inputSchema\` from \`clip tools\` to build valid args, then
|
|
60438
|
-
exec against that explicit target \u2014 same form as an Edge clip:
|
|
60521
|
+
A connection with target \`mcp\` routes to a remote MCP server; the command
|
|
60522
|
+
is an MCP tool name and the args are that tool's JSON arguments. Read each
|
|
60523
|
+
tool's \`inputSchema\` from \`clip tools\` first, then exec against the
|
|
60524
|
+
explicit target \u2014 the same form as any other clip:
|
|
60439
60525
|
|
|
60440
60526
|
\`\`\`bash
|
|
60441
60527
|
parall clip exec <clip> <tool> [json-args] --connection <ccn_|alias>
|
|
@@ -60453,13 +60539,11 @@ parall clip exec <clip> <tool> [json-args] --connection <ccn_|alias>
|
|
|
60453
60539
|
|
|
60454
60540
|
## Behavior rules
|
|
60455
60541
|
|
|
60456
|
-
- An authorization error (
|
|
60457
|
-
clip's
|
|
60458
|
-
|
|
60542
|
+
- An authorization error (\`HOSTED_CONNECTION_REQUIRED\`, \`FORBIDDEN\`) is a
|
|
60543
|
+
fail-fast: ask the clip's maintainer or an org admin to bind the clip or
|
|
60544
|
+
grant the connection \u2014 do not retry or work around it.
|
|
60545
|
+
- If the target device is offline or the call times out, report that
|
|
60459
60546
|
plainly; do not queue, and never fabricate a result for a run that errored.
|
|
60460
|
-
- Hosted browser activation is handled by the CLI: it waits (bounded) while a
|
|
60461
|
-
cold hosted browser starts, so if the invoke still fails, report the error \u2014
|
|
60462
|
-
do not blind-retry in a loop.
|
|
60463
60547
|
- **\`OUTCOME_UNKNOWN\` is never retryable.** It means the command was
|
|
60464
60548
|
dispatched and MAY HAVE EXECUTED even though no result came back. Retrying
|
|
60465
60549
|
could post, order or delete twice. Verify the effect through the system you
|
|
@@ -60475,10 +60559,274 @@ parall clip exec <clip> <tool> [json-args] --connection <ccn_|alias>
|
|
|
60475
60559
|
- A clip may act through a person's real logged-in account \u2014 outward,
|
|
60476
60560
|
irreversible, or spending actions (post, order, delete, pay) get the same
|
|
60477
60561
|
caution as any shared-state change: confirm when intent isn't explicit.
|
|
60562
|
+
The connection's alias/target tells you WHICH account you are acting as \u2014
|
|
60563
|
+
if that is ambiguous, resolve it with a human before acting, not after.
|
|
60478
60564
|
- Reach for \`parall clip list\` whenever a task needs capabilities beyond
|
|
60479
60565
|
built-in tools.
|
|
60480
60566
|
`;
|
|
60481
60567
|
|
|
60568
|
+
// ts/agent-core/dist/skills/parall-clip-authoring.js
|
|
60569
|
+
var PARALL_CLIP_AUTHORING_SKILL = `# Authoring a Parall Clip (v3)
|
|
60570
|
+
|
|
60571
|
+
Write a **registry (v3) clip**. This skill is for CREATING clips; to DISCOVER
|
|
60572
|
+
and CALL clips that are already installed, use the Parall Clips skill instead.
|
|
60573
|
+
|
|
60574
|
+
Two kinds of clip exist, and this document covers authoring both:
|
|
60575
|
+
|
|
60576
|
+
- A **browser clip** \u2014 the main subject here \u2014 is a folder of named commands
|
|
60577
|
+
that run on an Edge (a member's desktop or an org-shared cloud profile) and
|
|
60578
|
+
drive a real browser session: one manifest (\`manifest.json\` or \`site.json\`,
|
|
60579
|
+
either name works) + one \`.js\` file per command + optional \`_\`-prefixed
|
|
60580
|
+
helpers. The Edge runs each command's JS in a sandbox with a browser handle
|
|
60581
|
+
bound to the target profile.
|
|
60582
|
+
- An **MCP clip** is a manifest-ONLY declaration pointing at a remote MCP tool
|
|
60583
|
+
server \u2014 no \`.js\` files; its tools come live from that server's own
|
|
60584
|
+
\`tools/list\`, never from the manifest. See "MCP clips" below.
|
|
60585
|
+
|
|
60586
|
+
## Project layout
|
|
60587
|
+
|
|
60588
|
+
\`\`\`
|
|
60589
|
+
my-clip/
|
|
60590
|
+
\u251C\u2500\u2500 manifest.json # name, description, version, command params
|
|
60591
|
+
\u251C\u2500\u2500 _helpers.js # OPTIONAL \u2014 any _-prefixed file is auto-injected into every command
|
|
60592
|
+
\u251C\u2500\u2500 search.js # one command = one file; filename (minus .js) IS the command name
|
|
60593
|
+
\u2514\u2500\u2500 profile.js # another command
|
|
60594
|
+
\`\`\`
|
|
60595
|
+
|
|
60596
|
+
## manifest.json
|
|
60597
|
+
|
|
60598
|
+
\`\`\`json
|
|
60599
|
+
{
|
|
60600
|
+
"name": "twitter",
|
|
60601
|
+
"description": "Twitter / X",
|
|
60602
|
+
"version": "1.0.0",
|
|
60603
|
+
"commands": {
|
|
60604
|
+
"search": {
|
|
60605
|
+
"description": "Search tweets",
|
|
60606
|
+
"params": {
|
|
60607
|
+
"query": { "type": "string", "required": true },
|
|
60608
|
+
"count": { "type": "number", "required": false }
|
|
60609
|
+
}
|
|
60610
|
+
},
|
|
60611
|
+
"profile": {
|
|
60612
|
+
"description": "Fetch a user profile",
|
|
60613
|
+
"params": { "handle": { "type": "string", "required": true } }
|
|
60614
|
+
}
|
|
60615
|
+
}
|
|
60616
|
+
}
|
|
60617
|
+
\`\`\`
|
|
60618
|
+
|
|
60619
|
+
- \`commands\` keys MUST match the \`.js\` filenames (\`search\` \u2194 \`search.js\`).
|
|
60620
|
+
- \`params\` is the input contract the caller sees in \`clip info\`; validate them in code too.
|
|
60621
|
+
- **No top-level \`"type"\` needed for a browser clip** \u2014 omitting it means
|
|
60622
|
+
browser. The only accepted values are \`"browser"\` and \`"mcp"\` (for the
|
|
60623
|
+
latter, see "MCP clips" below). **\`"type": "clip"\` is the legacy Pinix v2
|
|
60624
|
+
package value and is refused at publish** \u2014 don't copy it in from an older
|
|
60625
|
+
clip.
|
|
60626
|
+
|
|
60627
|
+
## A command file
|
|
60628
|
+
|
|
60629
|
+
\`\`\`js
|
|
60630
|
+
// search.js \u2014 a command is a single async function of (args).
|
|
60631
|
+
// The code does NOT know or pick the profile; the Edge binds it per invocation.
|
|
60632
|
+
//
|
|
60633
|
+
// SHAPE, not a runnable Twitter client: the "..." parts (the GraphQL path,
|
|
60634
|
+
// parseTweets' body) are what you fill in per target site. Deliberately not
|
|
60635
|
+
// pinned to a real X endpoint \u2014 a site's internal API paths rotate, and a
|
|
60636
|
+
// stale one baked into this skill would teach a URL that 404s.
|
|
60637
|
+
module.exports = async function (args) {
|
|
60638
|
+
if (!args.query) return { error: "Missing argument: query" };
|
|
60639
|
+
|
|
60640
|
+
const tab = await browser.open("https://x.com");
|
|
60641
|
+
// Everything after open() goes in try/finally: an early return or a thrown
|
|
60642
|
+
// fetch would otherwise leak the tab, and the Edge is long-lived.
|
|
60643
|
+
try {
|
|
60644
|
+
const ct0 = await tab.cookie("ct0");
|
|
60645
|
+
if (!ct0) return { error: "Not logged in" };
|
|
60646
|
+
|
|
60647
|
+
const data = await tab.fetch("/i/api/graphql/.../SearchTimeline?...", {
|
|
60648
|
+
headers: twitterHeaders(ct0), // from _helpers.js, auto-injected
|
|
60649
|
+
});
|
|
60650
|
+
return { query: args.query, tweets: parseTweets(data) };
|
|
60651
|
+
} finally {
|
|
60652
|
+
await tab.close();
|
|
60653
|
+
}
|
|
60654
|
+
};
|
|
60655
|
+
\`\`\`
|
|
60656
|
+
|
|
60657
|
+
Return a plain JSON-serializable object. A thrown error surfaces to the caller as
|
|
60658
|
+
\`SCRIPT_ERROR\`; a returned \`{ error: "..." }\` is your own typed failure \u2014 prefer it
|
|
60659
|
+
for expected cases (not logged in, missing arg).
|
|
60660
|
+
|
|
60661
|
+
## Helpers (\`_\`-prefixed)
|
|
60662
|
+
|
|
60663
|
+
Any file whose name starts with \`_\` is NOT a command. Its top-level functions are
|
|
60664
|
+
injected into every command's scope \u2014 no import/require needed:
|
|
60665
|
+
|
|
60666
|
+
\`\`\`js
|
|
60667
|
+
// _helpers.js
|
|
60668
|
+
function twitterHeaders(ct0) {
|
|
60669
|
+
return { "X-Csrf-Token": ct0, "X-Twitter-Auth-Type": "OAuth2Session" };
|
|
60670
|
+
}
|
|
60671
|
+
function parseTweets(data) { /* ... */ }
|
|
60672
|
+
\`\`\`
|
|
60673
|
+
|
|
60674
|
+
## Runtime API (globals available in every command)
|
|
60675
|
+
|
|
60676
|
+
- \`browser.open(url)\` \u2192 tab handle \xB7 \`browser.tabs()\` \u2192 open tabs
|
|
60677
|
+
- \`tab.cookie(name)\` \xB7 \`tab.fetch(url, opts)\` (in-browser fetch, carries the session)
|
|
60678
|
+
- \`tab.eval(expr)\` (escape hatch) \xB7 \`tab.click(sel)\` \xB7 \`tab.fill(sel, text)\` \xB7 \`tab.navigate(url)\`
|
|
60679
|
+
- \`tab.waitForSelector(sel)\` \xB7 \`tab.getTitle()\` \xB7 \`tab.getURL()\` \xB7 \`tab.screenshot()\` \xB7 \`tab.close()\`
|
|
60680
|
+
- \`fetch\` \u2014 runtime-side HTTP, does NOT go through the browser (no session)
|
|
60681
|
+
- \`console\` \u2014 logs \xB7 \`args\` \u2014 the invocation input
|
|
60682
|
+
|
|
60683
|
+
**Prefer \`tab.fetch\` over \`tab.eval\`**: fetch reuses the logged-in session and
|
|
60684
|
+
returns structured data; eval is the last resort. Always \`tab.close()\` what you
|
|
60685
|
+
open, and do it in a \`finally\` \u2014 an early return or a thrown fetch is exactly
|
|
60686
|
+
when the tab leaks.
|
|
60687
|
+
|
|
60688
|
+
## Develop \u2192 publish \u2192 iterate
|
|
60689
|
+
|
|
60690
|
+
Use the platform \`parall clip\` subcommands \u2014 they reuse the credentials you
|
|
60691
|
+
already have (\`PRLL_API_KEY\` / \`PRLL_ORG_ID\`), so there is nothing to install
|
|
60692
|
+
or configure.
|
|
60693
|
+
|
|
60694
|
+
> A separate **standalone \`parall-clip\`** binary also exists (the Edge-side
|
|
60695
|
+
> authoring tool). It takes the SAME operations but a DIFFERENT argument shape \u2014
|
|
60696
|
+
> \`parall-clip exec <clip> <cmd> --query "AI" --count 10\` passes one flag per
|
|
60697
|
+
> param, while \`parall clip exec\` takes a single JSON blob. Do not mix the two
|
|
60698
|
+
> forms; everything below is the platform CLI.
|
|
60699
|
+
|
|
60700
|
+
1. **Publish** the clip directory to the org registry. Publishing is not a
|
|
60701
|
+
release step here \u2014 it is the edit loop's SAVE button, because exec only
|
|
60702
|
+
ever sees published files:
|
|
60703
|
+
|
|
60704
|
+
\`\`\`sh
|
|
60705
|
+
parall clip publish ./my-clip/
|
|
60706
|
+
\`\`\`
|
|
60707
|
+
|
|
60708
|
+
It packages the directory (the manifest plus the directory's top-level
|
|
60709
|
+
\`.js\` files; an MCP clip is manifest-only) and POSTs it for you (5 MB cap).
|
|
60710
|
+
\`name\` is the org-wide upsert key \u2014 manifest fields win, else the directory
|
|
60711
|
+
name / \`0.0.1\` / \`private\` fill the gaps. Re-publishing an existing name is
|
|
60712
|
+
**author-only** and REPLACES the file set. Publishing into YOUR org makes it
|
|
60713
|
+
usable there immediately (same-org self-reference, no review);
|
|
60714
|
+
\`"visibility": "public"\` additionally submits the version for platform
|
|
60715
|
+
review before it can spread cross-org.
|
|
60716
|
+
|
|
60717
|
+
Programmatic equivalent (what \`publish\` calls under the hood \u2014 use only if
|
|
60718
|
+
you can't run the CLI). Send it verbatim-shaped: \`visibility\` is exactly
|
|
60719
|
+
one of \`"private"\` / \`"public"\`, and \`files\` maps each filename to its
|
|
60720
|
+
source as a string:
|
|
60721
|
+
|
|
60722
|
+
\`\`\`
|
|
60723
|
+
POST /api/v1/orgs/{orgId}/clip-registry/publish
|
|
60724
|
+
{
|
|
60725
|
+
"name": "twitter",
|
|
60726
|
+
"description": "Twitter / X",
|
|
60727
|
+
"version": "1.0.0",
|
|
60728
|
+
"visibility": "private",
|
|
60729
|
+
"manifest": {
|
|
60730
|
+
"name": "twitter",
|
|
60731
|
+
"version": "1.0.0",
|
|
60732
|
+
"commands": { "search": { "description": "Search tweets" } }
|
|
60733
|
+
},
|
|
60734
|
+
"files": { "search.js": "module.exports = async function (args) { return {}; };" }
|
|
60735
|
+
}
|
|
60736
|
+
\`\`\`
|
|
60737
|
+
|
|
60738
|
+
2. **Exec** a command against a real target. Args are ONE argument \u2014 a JSON
|
|
60739
|
+
string (or plain text for a single-value command), not per-param flags:
|
|
60740
|
+
|
|
60741
|
+
\`\`\`sh
|
|
60742
|
+
parall clip exec <clip> <command> '{"query":"AI","count":10}' --connection <ccn_id|alias>
|
|
60743
|
+
# or route to a desktop (BYOC) device you own: --edge <edge-id>
|
|
60744
|
+
# --connection and --edge are mutually exclusive; --timeout <ms> defaults to 30000
|
|
60745
|
+
\`\`\`
|
|
60746
|
+
|
|
60747
|
+
A **cloud (hosted) profile is reachable ONLY via \`--connection\`** \u2014 the
|
|
60748
|
+
binding its maintainer created IS the authorization. With neither flag the
|
|
60749
|
+
server resolves only your own online desktop device, never a cloud profile.
|
|
60750
|
+
Discover the bindings with \`parall clip connections <clip>\`.
|
|
60751
|
+
|
|
60752
|
+
3. **Iterate**: edit locally \u2192 \`parall clip publish\` again \u2192 re-exec. Exec
|
|
60753
|
+
resolves the file set from the REGISTRY, server-side \u2014 your own org always
|
|
60754
|
+
runs the live working copy, i.e. the latest publish. It NEVER reads your
|
|
60755
|
+
local directory: an edit you did not re-publish silently runs the previous
|
|
60756
|
+
version.
|
|
60757
|
+
|
|
60758
|
+
## Install model & self-development (v3)
|
|
60759
|
+
|
|
60760
|
+
- Installing a clip is a **reference**, not a copy \u2014 the JS lives once in the Market DB.
|
|
60761
|
+
- **Your own org** always executes its **live working copy** (latest published files),
|
|
60762
|
+
so re-publishing is your edit loop.
|
|
60763
|
+
- **Other orgs** installing your \`public\` clip execute only the **approved snapshot**
|
|
60764
|
+
(\`approved_version_id\`); unreviewed public edits are invisible/unexecutable cross-org.
|
|
60765
|
+
- To customize someone else's public clip: install \u2192 **fetch its effective
|
|
60766
|
+
file set** \u2192 modify \u2192 **publish into your OWN org** (a derived private
|
|
60767
|
+
entry). You cannot edit a published clip in place. The CLI has no files
|
|
60768
|
+
subcommand (\`clip info\` returns only the manifest) \u2014 read the source via
|
|
60769
|
+
the API:
|
|
60770
|
+
|
|
60771
|
+
\`\`\`
|
|
60772
|
+
GET /api/v1/orgs/{orgId}/clip-registry/{clipId}/files
|
|
60773
|
+
\`\`\`
|
|
60774
|
+
|
|
60775
|
+
It returns exactly what you may read and execute: your own clip \u2192 the live
|
|
60776
|
+
working copy; an installed public clip \u2192 the approved snapshot.
|
|
60777
|
+
|
|
60778
|
+
## MCP clips (manifest-only)
|
|
60779
|
+
|
|
60780
|
+
An MCP clip declares a remote MCP tool server. There is nothing to code: no
|
|
60781
|
+
\`.js\` files (the no-scripts refusal at publish applies to browser clips only),
|
|
60782
|
+
and the folder is just a manifest:
|
|
60783
|
+
|
|
60784
|
+
\`\`\`json
|
|
60785
|
+
{
|
|
60786
|
+
"name": "linear",
|
|
60787
|
+
"description": "Linear (MCP)",
|
|
60788
|
+
"version": "1.0.0",
|
|
60789
|
+
"type": "mcp",
|
|
60790
|
+
"mcp": { "server_url": "https://mcp.linear.app/mcp", "auth": "oauth" }
|
|
60791
|
+
}
|
|
60792
|
+
\`\`\`
|
|
60793
|
+
|
|
60794
|
+
- The \`mcp\` block takes ONLY \`server_url\` and \`auth\` (\`"none" | "bearer" |
|
|
60795
|
+
"api_key" | "oauth"\`). Any other key is refused at publish \u2014 a credential
|
|
60796
|
+
belongs to the installing org's own configuration, NEVER to the clip
|
|
60797
|
+
definition.
|
|
60798
|
+
- \`server_url\` must be an absolute **https** URL with no embedded credentials,
|
|
60799
|
+
query, or fragment. It is review material, frozen with the approved version.
|
|
60800
|
+
- Do NOT put the server in the top-level \`server\` / \`auth\` manifest keys \u2014
|
|
60801
|
+
those are legacy Edge-manifest fields nothing reads. Only the \`mcp\` block
|
|
60802
|
+
declares the server.
|
|
60803
|
+
- Both fields are optional, but what you declare is LOCKED: the installing
|
|
60804
|
+
org's config must match it, and changing the URL or auth mode means
|
|
60805
|
+
republishing.
|
|
60806
|
+
- Entering the credential / completing OAuth is a HUMAN step in the Clip
|
|
60807
|
+
Console (the config-write endpoints are session-only \u2014 an API key cannot
|
|
60808
|
+
call them). Once configured, discover the live tool schemas with
|
|
60809
|
+
\`parall clip tools <clip>\` and exec like any other clip.
|
|
60810
|
+
- Publish is the same command: \`parall clip publish ./my-clip/\`.
|
|
60811
|
+
|
|
60812
|
+
## Cloud (hosted) vs desktop (BYOC) Edge
|
|
60813
|
+
|
|
60814
|
+
The same command JS runs on either. Hosted profiles are org-shared cloud browsers
|
|
60815
|
+
reachable ONLY via an explicit \`--connection\`; cloud state lives in S3 and is
|
|
60816
|
+
hydrated per pod. Your code never touches this \u2014 it just gets a \`browser\`/\`tab\`
|
|
60817
|
+
bound to whatever profile the connection selected.
|
|
60818
|
+
|
|
60819
|
+
## Constraints
|
|
60820
|
+
|
|
60821
|
+
- Browser clips: one command = one file; keep a command's work self-contained
|
|
60822
|
+
(open what you need, close it, return). The Edge is stateless about your code
|
|
60823
|
+
between calls.
|
|
60824
|
+
- Never embed credentials in the clip source \u2014 rely on the profile's logged-in
|
|
60825
|
+
session (\`tab.cookie\` / \`tab.fetch\`). Published source is visible to installers.
|
|
60826
|
+
- Exec has a timeout (default 30s, caller-set up to 120s). Long scrapes should page,
|
|
60827
|
+
not block.
|
|
60828
|
+
`;
|
|
60829
|
+
|
|
60482
60830
|
// ts/agent-core/dist/skills/index.js
|
|
60483
60831
|
var SKILLS = [
|
|
60484
60832
|
{
|
|
@@ -60508,8 +60856,13 @@ var SKILLS = [
|
|
|
60508
60856
|
},
|
|
60509
60857
|
{
|
|
60510
60858
|
name: "parall-clips",
|
|
60511
|
-
description: "Parall clip operations: list installed clips,
|
|
60859
|
+
description: "Parall clip operations: list installed clips and their connections, inspect clip commands/tools, execute clip commands on an explicit connection. Use when: the task requires external capabilities (GitHub, web search, etc.), user asks about available tools/clips, or you need to call a clip command. For WRITING a new clip, use parall-clip-authoring instead.",
|
|
60512
60860
|
content: PARALL_CLIPS_SKILL
|
|
60861
|
+
},
|
|
60862
|
+
{
|
|
60863
|
+
name: "parall-clip-authoring",
|
|
60864
|
+
description: "Authoring a v3 registry clip: write manifest.json + command .js files + _helpers, drive the browser/tab runtime API, publish the directory to the org registry with `parall clip publish` \u2014 or declare a manifest-only MCP clip pointing at a remote tool server. Use when: user asks to write/create/build a new clip, author a clip command, package a browser automation as a clip, declare an MCP clip, or publish a clip. For CALLING clips that already exist, use parall-clips instead.",
|
|
60865
|
+
content: PARALL_CLIP_AUTHORING_SKILL
|
|
60513
60866
|
}
|
|
60514
60867
|
];
|
|
60515
60868
|
function writeSkillFiles(targetDir) {
|
|
@@ -61189,6 +61542,15 @@ var ClaudeInputRegistry = class {
|
|
|
61189
61542
|
getByCommand(commandUuid) {
|
|
61190
61543
|
return this.byCommand.get(commandUuid);
|
|
61191
61544
|
}
|
|
61545
|
+
findOverlapping(dispatchEventIds) {
|
|
61546
|
+
const ids = new Set(dispatchEventIds);
|
|
61547
|
+
for (const delivery of this.byKey.values()) {
|
|
61548
|
+
if (delivery.lifecycle?.dispatchEventIds.some((id) => ids.has(id))) {
|
|
61549
|
+
return delivery;
|
|
61550
|
+
}
|
|
61551
|
+
}
|
|
61552
|
+
return void 0;
|
|
61553
|
+
}
|
|
61192
61554
|
values() {
|
|
61193
61555
|
return this.byKey.values();
|
|
61194
61556
|
}
|
|
@@ -61815,6 +62177,12 @@ var ClaudeCodeAdapter = class {
|
|
|
61815
62177
|
return;
|
|
61816
62178
|
}
|
|
61817
62179
|
}
|
|
62180
|
+
if (existingState && inputLifecycle) {
|
|
62181
|
+
const overlap = existingState.inputs.findOverlapping(inputLifecycle.dispatchEventIds);
|
|
62182
|
+
if (overlap) {
|
|
62183
|
+
throw new Error(`buffered Claude delivery ${deliveryKey} overlaps active input ${overlap.deliveryKey}; refusing duplicate stdin write`);
|
|
62184
|
+
}
|
|
62185
|
+
}
|
|
61818
62186
|
let promptBody = bodyForAgent;
|
|
61819
62187
|
let releasePreparedAttachments = () => {
|
|
61820
62188
|
};
|