@parall/agent-core 1.31.0 → 1.32.1
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/bridge-workspace.d.ts +1 -1
- package/dist/bridge-workspace.d.ts.map +1 -1
- package/dist/bridge-workspace.js +13 -13
- package/dist/dispatch-adapter.d.ts +15 -8
- package/dist/dispatch-adapter.d.ts.map +1 -1
- package/dist/event-format.d.ts +1 -1
- package/dist/event-format.d.ts.map +1 -1
- package/dist/event-format.js +68 -25
- package/dist/gateway-base.d.ts +14 -13
- package/dist/gateway-base.d.ts.map +1 -1
- package/dist/gateway-base.js +650 -313
- package/dist/index.d.ts +15 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -12
- package/dist/internal/attachment-input.d.ts +3 -3
- package/dist/internal/attachment-input.d.ts.map +1 -1
- package/dist/internal/attachment-input.js +61 -58
- package/dist/logger.d.ts +1 -1
- package/dist/platform-config.d.ts +28 -2
- package/dist/platform-config.d.ts.map +1 -1
- package/dist/platform-config.js +42 -11
- package/dist/prompt-fragments.d.ts +2 -2
- package/dist/prompt-fragments.d.ts.map +1 -1
- package/dist/prompt-fragments.js +37 -14
- package/dist/provider-config.d.ts +9 -0
- package/dist/provider-config.d.ts.map +1 -1
- package/dist/provider-config.js +13 -2
- package/dist/routing.d.ts +5 -5
- package/dist/routing.js +6 -6
- package/dist/session-state.d.ts +16 -0
- package/dist/session-state.d.ts.map +1 -1
- package/dist/session-state.js +45 -0
- package/dist/skills/index.d.ts +5 -4
- package/dist/skills/index.d.ts.map +1 -1
- package/dist/skills/index.js +28 -21
- package/dist/skills/parall-clips.d.ts +2 -0
- package/dist/skills/parall-clips.d.ts.map +1 -0
- package/dist/skills/parall-clips.js +44 -0
- package/dist/skills/parall-platform.d.ts +1 -1
- package/dist/skills/parall-platform.d.ts.map +1 -1
- package/dist/skills/parall-platform.js +6 -2
- package/dist/skills/parall-tasks.d.ts +1 -1
- package/dist/skills/parall-tasks.d.ts.map +1 -1
- package/dist/skills/parall-tasks.js +1 -1
- package/dist/skills/parall-wiki.d.ts +1 -1
- package/dist/skills/parall-wiki.d.ts.map +1 -1
- package/dist/skills/parall-wiki.js +1 -1
- package/dist/telemetry.d.ts +27 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +205 -0
- package/dist/types.d.ts +18 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +11 -2
- package/src/bridge-workspace.ts +13 -13
- package/src/dispatch-adapter.ts +31 -8
- package/src/event-format.ts +80 -30
- package/src/gateway-base.ts +988 -445
- package/src/index.ts +23 -13
- package/src/internal/attachment-input.ts +127 -100
- package/src/logger.ts +1 -1
- package/src/platform-config.ts +61 -16
- package/src/prompt-fragments.ts +37 -14
- package/src/provider-config.ts +14 -2
- package/src/routing.ts +11 -11
- package/src/session-state.ts +62 -0
- package/src/skills/index.ts +34 -23
- package/src/skills/parall-clips.ts +44 -0
- package/src/skills/parall-platform.ts +6 -2
- package/src/skills/parall-tasks.ts +1 -1
- package/src/skills/parall-wiki.ts +1 -1
- package/src/telemetry.ts +252 -0
- package/src/types.ts +18 -2
package/src/prompt-fragments.ts
CHANGED
|
@@ -27,29 +27,32 @@ be direct, and care about the outcome of the work — not just the request in fr
|
|
|
27
27
|
of you.`;
|
|
28
28
|
|
|
29
29
|
function sanitizeProfileField(value: string): string {
|
|
30
|
-
return value
|
|
30
|
+
return value
|
|
31
|
+
.replace(/[\r\n]+/g, ' ')
|
|
32
|
+
.replace(/`/g, "'")
|
|
33
|
+
.trim();
|
|
31
34
|
}
|
|
32
35
|
|
|
33
36
|
function sanitizeProfileBlock(value: string): string {
|
|
34
|
-
return value.replace(/\r\n?/g,
|
|
37
|
+
return value.replace(/\r\n?/g, '\n').trim();
|
|
35
38
|
}
|
|
36
39
|
|
|
37
40
|
export function buildIdentity(agent?: AgentIdentity): string {
|
|
38
41
|
if (!agent) return PRLL_IDENTITY_BASE;
|
|
39
42
|
const name = sanitizeProfileField(agent.displayName);
|
|
40
|
-
const lines = [PRLL_IDENTITY_BASE,
|
|
43
|
+
const lines = [PRLL_IDENTITY_BASE, '', '### Your Parall Identity', ''];
|
|
41
44
|
lines.push(`You are **${name}** (\`prll://${agent.userId}\`).`);
|
|
42
45
|
if (agent.description) {
|
|
43
46
|
const description = sanitizeProfileBlock(agent.description);
|
|
44
47
|
if (description) {
|
|
45
|
-
lines.push(
|
|
48
|
+
lines.push('', '### Your Agent Profile', '', description);
|
|
46
49
|
}
|
|
47
50
|
}
|
|
48
51
|
lines.push(
|
|
49
|
-
|
|
52
|
+
'',
|
|
50
53
|
`When you see \`${agent.userId}\` or \`prll://${agent.userId}\` in messages, mentions, or events — that's you.`,
|
|
51
54
|
);
|
|
52
|
-
return lines.join(
|
|
55
|
+
return lines.join('\n');
|
|
53
56
|
}
|
|
54
57
|
|
|
55
58
|
/** @deprecated Use buildIdentity() instead. Kept for backward compat during migration. */
|
|
@@ -90,10 +93,15 @@ Other agents share this workspace. Before starting work, check whether someone
|
|
|
90
93
|
### Permissions and approvals
|
|
91
94
|
You have real permissions based on your roles (chat member/admin, org member).
|
|
92
95
|
If you lack permission for an action, the API returns PERMISSION_DENIED with the
|
|
93
|
-
\`action\` and \`resource_uri\` that were denied.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
96
|
+
\`action\` and \`resource_uri\` that were denied. The server decides whether that
|
|
97
|
+
action is approvable: if it is, the CLI prints an \`approvals request\` command —
|
|
98
|
+
fill in the placeholders it shows (\`--chat\`, \`--title\`, \`--reason\`) and run
|
|
99
|
+
it to ask someone with permission. If it is NOT approvable, the output says so;
|
|
100
|
+
ask a human with permission instead of requesting approval. A
|
|
101
|
+
\`INVALID_TARGET\` error instead means you addressed the wrong kind of thing
|
|
102
|
+
(e.g. a \`usr_\` id where a chat is expected) — follow the message (e.g. use
|
|
103
|
+
\`dm\` for a user). Don't retry or work around a denial; only request approval
|
|
104
|
+
after an actual denial, never preemptively.
|
|
97
105
|
|
|
98
106
|
### When in doubt
|
|
99
107
|
Prefer asking over guessing. Prefer "I don't know" over fabricating. Your
|
|
@@ -143,6 +151,18 @@ resolves and renders the entity title automatically.
|
|
|
143
151
|
|
|
144
152
|
prll://tsk_xxx/description#Implementation heading within task description
|
|
145
153
|
|
|
154
|
+
### Unread context
|
|
155
|
+
|
|
156
|
+
When dispatched to a chat, you may see \`[Unread: N messages | since: prll://msg_xxx]\`.
|
|
157
|
+
This shows messages since your last interaction — your read cursor advances after each
|
|
158
|
+
dispatch, so context you skip now won't appear as unread next time. Use
|
|
159
|
+
\`parall messages list <chat> --limit 20\` to fetch recent context. For large unread
|
|
160
|
+
counts (50+), fetch only recent messages rather than everything.
|
|
161
|
+
|
|
162
|
+
Thread dispatches may show \`[Thread: prll://msg_root | N replies | M unread | since: prll://msg_r]\`.
|
|
163
|
+
Same semantics — use \`parall messages list <chat> --thread-root-id <thread_root> --limit 20\` to
|
|
164
|
+
catch up on the thread.
|
|
165
|
+
|
|
146
166
|
### Reading context on demand
|
|
147
167
|
|
|
148
168
|
An event only carries the single triggering message. If you're mentioned in a
|
|
@@ -206,7 +226,7 @@ export type LocalAttachmentResult = {
|
|
|
206
226
|
};
|
|
207
227
|
|
|
208
228
|
export function renderLocalAttachmentSection(section: LocalAttachmentResult): string {
|
|
209
|
-
if (section.images.length === 0 && section.notes.length === 0) return
|
|
229
|
+
if (section.images.length === 0 && section.notes.length === 0) return '';
|
|
210
230
|
|
|
211
231
|
// Each image renders as a metadata header line plus the absolute path on
|
|
212
232
|
// its own line. The path stands alone (no quoting / escaping) so file-
|
|
@@ -215,7 +235,7 @@ export function renderLocalAttachmentSection(section: LocalAttachmentResult): st
|
|
|
215
235
|
// the bridge operator. A pathological workspace dir with embedded newlines
|
|
216
236
|
// would split across lines; that's exotic enough not to warrant scrubbing
|
|
217
237
|
// every consumer's path through escape rules.
|
|
218
|
-
const lines = [
|
|
238
|
+
const lines = ['[Local attachment files]'];
|
|
219
239
|
for (const image of section.images) {
|
|
220
240
|
lines.push(
|
|
221
241
|
`- prll://${image.attachmentId} (${sanitizePromptMeta(image.mimeType)}, ${formatBytes(image.fileSize)}, ${sanitizePromptMeta(image.fileName)})`,
|
|
@@ -223,11 +243,14 @@ export function renderLocalAttachmentSection(section: LocalAttachmentResult): st
|
|
|
223
243
|
);
|
|
224
244
|
}
|
|
225
245
|
lines.push(...section.notes);
|
|
226
|
-
return lines.join(
|
|
246
|
+
return lines.join('\n');
|
|
227
247
|
}
|
|
228
248
|
|
|
229
249
|
function sanitizePromptMeta(value: string): string {
|
|
230
|
-
return value
|
|
250
|
+
return value
|
|
251
|
+
.replace(/[\r\n]+/g, ' ')
|
|
252
|
+
.replace(/[()]/g, ' ')
|
|
253
|
+
.trim();
|
|
231
254
|
}
|
|
232
255
|
|
|
233
256
|
export function formatBytes(bytes: number): string {
|
package/src/provider-config.ts
CHANGED
|
@@ -7,6 +7,18 @@ export interface ProviderConfig {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export function llmSource(pc?: ProviderConfig): string {
|
|
10
|
+
return effectiveLLMSourceExplicit(pc) || 'parall';
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Returns the explicitly-configured LLM source — the llm_source value when
|
|
15
|
+
* set, or "custom" when BYO provider credentials are present — or "" when the
|
|
16
|
+
* provider_config carries no source signal at all (e.g. the empty `{}` default
|
|
17
|
+
* that self-hosted agents always have). The daemon supervisor uses the empty
|
|
18
|
+
* case to defer to the machine-level llm_source instead of letting `{}` shadow
|
|
19
|
+
* it as "parall". Mirrors the Go `ProviderConfig.EffectiveLLMSourceExplicit`.
|
|
20
|
+
*/
|
|
21
|
+
export function effectiveLLMSourceExplicit(pc?: ProviderConfig): string {
|
|
10
22
|
if (pc?.llm_source) return pc.llm_source;
|
|
11
23
|
if (
|
|
12
24
|
pc?.openai_api_key ||
|
|
@@ -14,9 +26,9 @@ export function llmSource(pc?: ProviderConfig): string {
|
|
|
14
26
|
pc?.anthropic_auth_token ||
|
|
15
27
|
pc?.anthropic_base_url
|
|
16
28
|
) {
|
|
17
|
-
return
|
|
29
|
+
return 'custom';
|
|
18
30
|
}
|
|
19
|
-
return
|
|
31
|
+
return '';
|
|
20
32
|
}
|
|
21
33
|
|
|
22
34
|
export function clearAllProviderCreds(env: NodeJS.ProcessEnv): void {
|
package/src/routing.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type { DispatchState, ParallEvent } from
|
|
1
|
+
import type { DispatchState, ParallEvent } from './types.js';
|
|
2
2
|
|
|
3
3
|
/** Where an inbound event should be routed. */
|
|
4
4
|
export type TriggerDisposition =
|
|
5
|
-
| { action:
|
|
6
|
-
| { action:
|
|
7
|
-
| { action:
|
|
8
|
-
| { action:
|
|
5
|
+
| { action: 'main' }
|
|
6
|
+
| { action: 'buffer-main' }
|
|
7
|
+
| { action: 'buffer-fork'; forkKey: string }
|
|
8
|
+
| { action: 'new-fork' };
|
|
9
9
|
|
|
10
10
|
/** Pluggable strategy for routing triggers when main session is busy. */
|
|
11
11
|
export type RoutingStrategy = (event: ParallEvent, state: DispatchState) => TriggerDisposition;
|
|
@@ -21,17 +21,17 @@ const MAX_CONCURRENT_FORKS = 20;
|
|
|
21
21
|
*/
|
|
22
22
|
export const defaultRoutingStrategy: RoutingStrategy = (event, state) => {
|
|
23
23
|
if (state.mainCurrentTargetId === event.targetId) {
|
|
24
|
-
return { action:
|
|
24
|
+
return { action: 'buffer-main' };
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
const existingForkKey = state.activeForks.get(event.targetId);
|
|
28
|
-
if (existingForkKey) return { action:
|
|
28
|
+
if (existingForkKey) return { action: 'buffer-fork', forkKey: existingForkKey };
|
|
29
29
|
|
|
30
30
|
if (state.activeForks.size >= MAX_CONCURRENT_FORKS) {
|
|
31
|
-
return { action:
|
|
31
|
+
return { action: 'buffer-main' };
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
return { action:
|
|
34
|
+
return { action: 'new-fork' };
|
|
35
35
|
};
|
|
36
36
|
|
|
37
37
|
/** Route an inbound event based on current dispatch state. */
|
|
@@ -41,7 +41,7 @@ export function routeTrigger(
|
|
|
41
41
|
strategy: RoutingStrategy = defaultRoutingStrategy,
|
|
42
42
|
): TriggerDisposition {
|
|
43
43
|
const existingForkKey = state.activeForks.get(event.targetId);
|
|
44
|
-
if (existingForkKey) return { action:
|
|
45
|
-
if (!state.mainDispatching) return { action:
|
|
44
|
+
if (existingForkKey) return { action: 'buffer-fork', forkKey: existingForkKey };
|
|
45
|
+
if (!state.mainDispatching) return { action: 'main' };
|
|
46
46
|
return strategy(event, state);
|
|
47
47
|
}
|
package/src/session-state.ts
CHANGED
|
@@ -67,3 +67,65 @@ export function getDispatchNoReply(sessionKey: string): boolean {
|
|
|
67
67
|
export function clearDispatchNoReply(sessionKey: string) {
|
|
68
68
|
dispatchNoReplyMap.delete(normalizeSessionKey(sessionKey));
|
|
69
69
|
}
|
|
70
|
+
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
// Per-dispatch metrics — populated by runtime adapters, reported by gateway
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
|
|
75
|
+
export type DispatchMetrics = {
|
|
76
|
+
deliver_text_chunks: number;
|
|
77
|
+
deliver_text_chars: number;
|
|
78
|
+
message_send_attempts: number;
|
|
79
|
+
message_send_successes: number;
|
|
80
|
+
no_reply_called: boolean;
|
|
81
|
+
tool_call_count: number;
|
|
82
|
+
started_at: number;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const dispatchMetricsMap = new Map<string, DispatchMetrics>();
|
|
86
|
+
|
|
87
|
+
export function resetDispatchMetrics(sessionKey: string): void {
|
|
88
|
+
dispatchMetricsMap.set(normalizeSessionKey(sessionKey), {
|
|
89
|
+
deliver_text_chunks: 0,
|
|
90
|
+
deliver_text_chars: 0,
|
|
91
|
+
message_send_attempts: 0,
|
|
92
|
+
message_send_successes: 0,
|
|
93
|
+
no_reply_called: false,
|
|
94
|
+
tool_call_count: 0,
|
|
95
|
+
started_at: Date.now(),
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function getDispatchMetrics(sessionKey: string): DispatchMetrics | undefined {
|
|
100
|
+
return dispatchMetricsMap.get(normalizeSessionKey(sessionKey));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function clearDispatchMetrics(sessionKey: string): void {
|
|
104
|
+
dispatchMetricsMap.delete(normalizeSessionKey(sessionKey));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function recordDeliverText(sessionKey: string, charCount: number): void {
|
|
108
|
+
const m = dispatchMetricsMap.get(normalizeSessionKey(sessionKey));
|
|
109
|
+
if (!m) return;
|
|
110
|
+
m.deliver_text_chunks++;
|
|
111
|
+
m.deliver_text_chars += charCount;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function recordMessageSend(sessionKey: string, success: boolean): void {
|
|
115
|
+
const m = dispatchMetricsMap.get(normalizeSessionKey(sessionKey));
|
|
116
|
+
if (!m) return;
|
|
117
|
+
m.message_send_attempts++;
|
|
118
|
+
if (success) m.message_send_successes++;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function recordNoReply(sessionKey: string): void {
|
|
122
|
+
const m = dispatchMetricsMap.get(normalizeSessionKey(sessionKey));
|
|
123
|
+
if (!m) return;
|
|
124
|
+
m.no_reply_called = true;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function recordToolCall(sessionKey: string): void {
|
|
128
|
+
const m = dispatchMetricsMap.get(normalizeSessionKey(sessionKey));
|
|
129
|
+
if (!m) return;
|
|
130
|
+
m.tool_call_count++;
|
|
131
|
+
}
|
package/src/skills/index.ts
CHANGED
|
@@ -1,52 +1,63 @@
|
|
|
1
|
-
import * as fs from
|
|
2
|
-
import * as path from
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
3
|
|
|
4
|
-
export { PARALL_PLATFORM_SKILL } from
|
|
5
|
-
export { PARALL_TASKS_SKILL } from
|
|
6
|
-
export { PARALL_WIKI_SKILL } from
|
|
7
|
-
export { PARALL_SCHEDULES_SKILL } from
|
|
4
|
+
export { PARALL_PLATFORM_SKILL } from './parall-platform.js';
|
|
5
|
+
export { PARALL_TASKS_SKILL } from './parall-tasks.js';
|
|
6
|
+
export { PARALL_WIKI_SKILL } from './parall-wiki.js';
|
|
7
|
+
export { PARALL_SCHEDULES_SKILL } from './parall-schedules.js';
|
|
8
|
+
export { PARALL_CLIPS_SKILL } from './parall-clips.js';
|
|
8
9
|
|
|
9
|
-
import { PARALL_PLATFORM_SKILL } from
|
|
10
|
-
import { PARALL_TASKS_SKILL } from
|
|
11
|
-
import { PARALL_WIKI_SKILL } from
|
|
12
|
-
import { PARALL_SCHEDULES_SKILL } from
|
|
10
|
+
import { PARALL_PLATFORM_SKILL } from './parall-platform.js';
|
|
11
|
+
import { PARALL_TASKS_SKILL } from './parall-tasks.js';
|
|
12
|
+
import { PARALL_WIKI_SKILL } from './parall-wiki.js';
|
|
13
|
+
import { PARALL_SCHEDULES_SKILL } from './parall-schedules.js';
|
|
14
|
+
import { PARALL_CLIPS_SKILL } from './parall-clips.js';
|
|
13
15
|
|
|
14
16
|
export type SkillMeta = { name: string; description: string; content: string };
|
|
15
17
|
|
|
16
18
|
export const SKILLS: SkillMeta[] = [
|
|
17
19
|
{
|
|
18
|
-
name:
|
|
19
|
-
description:
|
|
20
|
+
name: 'parall-platform',
|
|
21
|
+
description:
|
|
22
|
+
"Parall platform queries and lightweight agent provisioning: list org members, agents, chats, read message history, check identity, or create another agent. Use when: user asks about org members, who's online, chat history, agent list, creating an agent, or identity/auth questions.",
|
|
20
23
|
content: PARALL_PLATFORM_SKILL,
|
|
21
24
|
},
|
|
22
25
|
{
|
|
23
|
-
name:
|
|
24
|
-
description:
|
|
26
|
+
name: 'parall-tasks',
|
|
27
|
+
description:
|
|
28
|
+
'Parall task operations: create, update, comment on, and query tasks and projects. Use when: user asks to create a task, update task status, add comments, list tasks, or manage projects.',
|
|
25
29
|
content: PARALL_TASKS_SKILL,
|
|
26
30
|
},
|
|
27
31
|
{
|
|
28
|
-
name:
|
|
29
|
-
description:
|
|
32
|
+
name: 'parall-wiki',
|
|
33
|
+
description:
|
|
34
|
+
'Parall wiki operations: read, search, edit, and propose changes to organization knowledge bases. Use when: user asks to read/write docs, edit wiki pages, search knowledge base, propose changes, or review changesets.',
|
|
30
35
|
content: PARALL_WIKI_SKILL,
|
|
31
36
|
},
|
|
32
37
|
{
|
|
33
|
-
name:
|
|
34
|
-
description:
|
|
38
|
+
name: 'parall-schedules',
|
|
39
|
+
description:
|
|
40
|
+
'Parall schedule operations: create / pause / resume / cancel recurring or one-shot time triggers; respond to schedule fire events. Use when: user asks to set up a recurring reminder, schedule a delayed prompt, run cron-like work, or when the agent receives an `[Event: schedule.fired]` dispatch.',
|
|
35
41
|
content: PARALL_SCHEDULES_SKILL,
|
|
36
42
|
},
|
|
43
|
+
{
|
|
44
|
+
name: 'parall-clips',
|
|
45
|
+
description:
|
|
46
|
+
'Parall clip operations: list installed clips, invoke clip commands, inspect clip details. 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.',
|
|
47
|
+
content: PARALL_CLIPS_SKILL,
|
|
48
|
+
},
|
|
37
49
|
];
|
|
38
50
|
|
|
39
51
|
/** Write plain skill markdown files to a target directory (CC/Codex). */
|
|
40
52
|
export function writeSkillFiles(targetDir: string): void {
|
|
41
53
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
42
54
|
for (const skill of SKILLS) {
|
|
43
|
-
fs.writeFileSync(path.join(targetDir, `${skill.name}.md`), skill.content,
|
|
55
|
+
fs.writeFileSync(path.join(targetDir, `${skill.name}.md`), skill.content, 'utf8');
|
|
44
56
|
}
|
|
45
57
|
}
|
|
46
58
|
|
|
47
|
-
|
|
48
59
|
export function buildSkillReferences(workspaceDir: string): string {
|
|
49
|
-
const dir = path.join(workspaceDir,
|
|
50
|
-
const lines = SKILLS.map((s) => `- ${s.description.split(
|
|
51
|
-
return `## Platform Skills (read on demand)\n\n${lines.join(
|
|
60
|
+
const dir = path.join(workspaceDir, '.parall', 'skills');
|
|
61
|
+
const lines = SKILLS.map((s) => `- ${s.description.split(':')[0]}: \`${dir}/${s.name}.md\``);
|
|
62
|
+
return `## Platform Skills (read on demand)\n\n${lines.join('\n')}\n`;
|
|
52
63
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export const PARALL_CLIPS_SKILL = `# Parall Clips
|
|
2
|
+
|
|
3
|
+
Clips are capability extensions — packaged toolkits that give you extra commands (e.g. GitHub operations, web search, code analysis). Clips installed in the org are available for any agent to invoke via the CLI.
|
|
4
|
+
|
|
5
|
+
## Discovering available clips
|
|
6
|
+
|
|
7
|
+
\`\`\`bash
|
|
8
|
+
# List all clips installed in the org
|
|
9
|
+
parall clip list
|
|
10
|
+
|
|
11
|
+
# Show detailed info about a clip (manifest, commands, version)
|
|
12
|
+
parall clip info <alias>
|
|
13
|
+
\`\`\`
|
|
14
|
+
|
|
15
|
+
## Invoking a clip command
|
|
16
|
+
|
|
17
|
+
\`\`\`bash
|
|
18
|
+
# Invoke a command on a clip by alias
|
|
19
|
+
parall clip invoke <alias> <command> [input]
|
|
20
|
+
|
|
21
|
+
# input is optional — when provided, it can be a JSON string or plain text
|
|
22
|
+
parall clip invoke github-tools list-repos '{"org": "acme"}'
|
|
23
|
+
parall clip invoke web-search search "latest Node.js LTS version"
|
|
24
|
+
|
|
25
|
+
# Custom timeout (default 30s)
|
|
26
|
+
parall clip invoke github-tools create-issue '{"title": "Bug report"}' --timeout 60000
|
|
27
|
+
\`\`\`
|
|
28
|
+
|
|
29
|
+
## How clips work
|
|
30
|
+
|
|
31
|
+
1. An org admin installs a clip from the Pinix registry or creates a custom one
|
|
32
|
+
2. \`parall clip list\` shows every clip installed in the org
|
|
33
|
+
3. You can only **invoke** clips that an admin has **bound to you** — invoking an unbound clip returns a "not bound" error. Ask an admin to bind the clip if you need it.
|
|
34
|
+
4. Each clip exposes one or more named commands with typed input/output
|
|
35
|
+
|
|
36
|
+
## When to use clips
|
|
37
|
+
|
|
38
|
+
- Check \`parall clip list\` when a task requires capabilities beyond your built-in tools (e.g. GitHub API, external services, specialized analysis)
|
|
39
|
+
- Use \`parall clip info <alias>\` to discover available commands and their expected input format
|
|
40
|
+
- If \`parall clip invoke\` reports the clip isn't bound to you, that clip exists in the org but hasn't been granted to you — ask an admin to bind it
|
|
41
|
+
- Clip invocations return JSON output on success or an error message on failure
|
|
42
|
+
|
|
43
|
+
CLI command results are JSON on stdout.
|
|
44
|
+
`;
|
|
@@ -115,7 +115,11 @@ parall dm "Alice" --file /tmp/report.pdf --text "Report attached"
|
|
|
115
115
|
|
|
116
116
|
## Approvals
|
|
117
117
|
|
|
118
|
-
When a CLI command returns a \`PERMISSION_DENIED\` error, the output includes
|
|
118
|
+
When a CLI command returns a \`PERMISSION_DENIED\` error, the output includes the denied \`action\` and \`resource_uri\`. Whether that action can be approved is decided by the server (there is no fixed allowlist):
|
|
119
|
+
- If it IS approvable, a \`Request approval:\` line with a \`parall approvals request\` command follows — fill in the placeholders it shows (\`--chat\`, \`--title\`, \`--reason\`) and run it.
|
|
120
|
+
- If it is NOT approvable, the output says so — ask a human with permission instead of requesting approval.
|
|
121
|
+
|
|
122
|
+
A different \`INVALID_TARGET\` error means you addressed the wrong kind of thing (e.g. a \`usr_\` id where a chat is expected). Follow the message (e.g. use \`parall dm\` to message a user) — do not request approval for it.
|
|
119
123
|
|
|
120
124
|
\`\`\`bash
|
|
121
125
|
# Request approval (use action and resource_uri from the error)
|
|
@@ -157,5 +161,5 @@ Every entity is addressable with a \`prll://\` URI. Common prefixes you'll see i
|
|
|
157
161
|
|
|
158
162
|
When a message or event references \`prll://sch_xxx\` or \`prll://srn_xxx\`, or when you receive \`[Event: schedule.fired]\`, switch to the **parall-schedules** skill for the CLI commands (create / list / pause / resume / cancel / runs).
|
|
159
163
|
|
|
160
|
-
|
|
164
|
+
CLI success output is JSON. Errors print a JSON line (\`{"error","status","code",...}\`) and, on a \`PERMISSION_DENIED\`, may add a plain-text \`Request approval:\` line — read both.
|
|
161
165
|
`;
|
|
@@ -61,5 +61,5 @@ When you receive \`[Event: task.comment.created]\`, someone commented on a task
|
|
|
61
61
|
2. Reply via comment: \`tasks comments add prll://tsk_xxx --body "Response..."\`
|
|
62
62
|
3. If the comment requests status changes, update accordingly
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
CLI success output is JSON; errors print a JSON line plus, on a \`PERMISSION_DENIED\`, an optional plain-text \`Request approval:\` line — read both.
|
|
65
65
|
`;
|
|
@@ -127,5 +127,5 @@ parall wiki reset <slug> # Restore all files to last synced state
|
|
|
127
127
|
parall wiki log <slug> # Recent wiki operations
|
|
128
128
|
\`\`\`
|
|
129
129
|
|
|
130
|
-
|
|
130
|
+
CLI success output is JSON; errors may add a plain-text \`Request approval:\` line on a \`PERMISSION_DENIED\` — read both. Run \`parall wiki --help\` for full options.
|
|
131
131
|
`;
|