relay-companion 0.1.509 → 0.1.511
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/bootstrap/relay-setup.cjs +3 -0
- package/bootstrap/relay-skill.cjs +56 -12
- package/package.json +1 -1
- package/skill/SKILL.src.md +127 -116
- package/skill/manifest.json +5 -5
- package/skill/relay/SKILL.md +188 -152
- package/skill/relay/scripts/relay-protocol.mjs +75 -1
- package/skill/variants/SKILL.dev.md +201 -161
|
@@ -1073,6 +1073,9 @@ async function setup(argv = []) {
|
|
|
1073
1073
|
console.log(`Relay ${version} is installed and paired. The Relay pill is open.`);
|
|
1074
1074
|
} else {
|
|
1075
1075
|
console.log(`Relay ${version} is installed. The Relay pill is open; sign in there to finish.`);
|
|
1076
|
+
// A person set up from sendrelays.com has no invitation document telling
|
|
1077
|
+
// their agent what comes after sign-in; the public guide does.
|
|
1078
|
+
console.log("Once the pill shows the account is paired, read https://sendrelays.com/llm_guide.md and follow \"Set up without an invitation\" to offer the first-Relay tutorial from this conversation.");
|
|
1076
1079
|
}
|
|
1077
1080
|
if (activated?.cliLauncher?.ok && activated.cliLauncher.shimPath && !activated.cliLauncher.pathAvailable) {
|
|
1078
1081
|
console.log(`Relay's command is installed at ${activated.cliLauncher.shimPath}. Open a new login session to add ~/.local/bin to PATH.`);
|
|
@@ -114,6 +114,37 @@ function pathInside(parent, child) {
|
|
|
114
114
|
return relative !== "" && !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
+
// Where the previous skill tree is kept for `relay skill rollback`. It used to
|
|
118
|
+
// be a sibling of the installed skill (`.relay-rollback` inside the host's
|
|
119
|
+
// skills folder), and Claude Code and Codex load any directory there that has
|
|
120
|
+
// a SKILL.md, so every session saw the Relay skill twice. It now lives under
|
|
121
|
+
// Relay's own state directory, keyed by the target it backs.
|
|
122
|
+
function rollbackRoot({ homeDir = os.homedir(), env = process.env } = {}) {
|
|
123
|
+
return path.join(env.RELAY_CONFIG_DIR || path.join(homeDir, ".relay"), "skill-rollback");
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function rollbackPathFor(directory, options = {}) {
|
|
127
|
+
return path.join(rollbackRoot(options), sha256(path.resolve(directory)).slice(0, 16));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function legacyRollbackPath(directory) {
|
|
131
|
+
return path.join(path.dirname(directory), `.${SKILL_NAME}-rollback`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// A rename when both paths share a volume; a copy and remove when they do not
|
|
135
|
+
// (CODEX_HOME or RELAY_CONFIG_DIR may point at another drive).
|
|
136
|
+
function moveTree(from, to) {
|
|
137
|
+
fs.mkdirSync(path.dirname(to), { recursive: true, mode: 0o700 });
|
|
138
|
+
try {
|
|
139
|
+
fs.renameSync(from, to);
|
|
140
|
+
return;
|
|
141
|
+
} catch (error) {
|
|
142
|
+
if (error.code !== "EXDEV") throw error;
|
|
143
|
+
}
|
|
144
|
+
fs.cpSync(from, to, { recursive: true, errorOnExist: true, force: false });
|
|
145
|
+
fs.rmSync(from, { recursive: true, force: true });
|
|
146
|
+
}
|
|
147
|
+
|
|
117
148
|
function fileHash(file) {
|
|
118
149
|
return sha256(fs.readFileSync(file));
|
|
119
150
|
}
|
|
@@ -244,10 +275,12 @@ function removeSkillArtifact(directory) {
|
|
|
244
275
|
: { ok: true, status: managed ? "removed" : "empty_debris_removed", directory };
|
|
245
276
|
}
|
|
246
277
|
|
|
247
|
-
function skillArtifacts(directory) {
|
|
278
|
+
function skillArtifacts(directory, options = {}) {
|
|
248
279
|
const parent = path.dirname(directory);
|
|
249
280
|
const name = path.basename(directory);
|
|
250
281
|
const artifacts = [directory];
|
|
282
|
+
const rollback = rollbackPathFor(directory, options);
|
|
283
|
+
if (fs.existsSync(rollback)) artifacts.push(rollback);
|
|
251
284
|
let entries = [];
|
|
252
285
|
try { entries = fs.readdirSync(parent, { withFileTypes: true }); }
|
|
253
286
|
catch (error) {
|
|
@@ -270,7 +303,7 @@ function uninstallManaged(options = {}) {
|
|
|
270
303
|
const seen = new Set();
|
|
271
304
|
for (const target of targets) {
|
|
272
305
|
let artifacts;
|
|
273
|
-
try { artifacts = skillArtifacts(target.directory); }
|
|
306
|
+
try { artifacts = skillArtifacts(target.directory, options); }
|
|
274
307
|
catch (error) {
|
|
275
308
|
results.push({ host: target.host, ok: false, status: "failed", directory: target.directory, error: error?.message || String(error) });
|
|
276
309
|
continue;
|
|
@@ -345,16 +378,23 @@ async function installOne(directory, manifest, readFile, options = {}) {
|
|
|
345
378
|
|
|
346
379
|
fs.mkdirSync(parent, { recursive: true, mode: 0o700 });
|
|
347
380
|
const staging = fs.mkdtempSync(path.join(parent, `.${SKILL_NAME}-staging-`));
|
|
348
|
-
const rollback =
|
|
349
|
-
|
|
381
|
+
const rollback = rollbackPathFor(directory, options);
|
|
382
|
+
const legacyRollback = legacyRollbackPath(directory);
|
|
383
|
+
if (!pathInside(parent, staging) || !pathInside(rollbackRoot(options), rollback) || !pathInside(parent, legacyRollback)) {
|
|
384
|
+
throw new Error("Relay refused an unsafe skill update location.");
|
|
385
|
+
}
|
|
350
386
|
try {
|
|
351
387
|
await materialize(manifest, staging, readFile, options, existing);
|
|
352
|
-
|
|
353
|
-
|
|
388
|
+
// One rollback copy per target. A copy an earlier installer left beside
|
|
389
|
+
// the skill goes too: hosts load it as a second skill.
|
|
390
|
+
for (const stale of [rollback, legacyRollback]) {
|
|
391
|
+
if (fs.existsSync(stale)) fs.rmSync(stale, { recursive: true, force: true });
|
|
392
|
+
}
|
|
393
|
+
if (fs.existsSync(directory)) moveTree(directory, rollback);
|
|
354
394
|
try {
|
|
355
395
|
fs.renameSync(staging, directory);
|
|
356
396
|
} catch (error) {
|
|
357
|
-
if (!fs.existsSync(directory) && fs.existsSync(rollback))
|
|
397
|
+
if (!fs.existsSync(directory) && fs.existsSync(rollback)) moveTree(rollback, directory);
|
|
358
398
|
throw error;
|
|
359
399
|
}
|
|
360
400
|
return { ok: true, status: existing ? "updated" : "installed", directory, version: manifest.version, rollback: fs.existsSync(rollback) ? rollback : null };
|
|
@@ -396,10 +436,13 @@ async function updateFromRemote(options = {}) {
|
|
|
396
436
|
return installManifest(manifest, (entry) => fetchBytes(`${manifest.baseUrl}/${entry.path.split("/").map(encodeURIComponent).join("/")}`, options), options);
|
|
397
437
|
}
|
|
398
438
|
|
|
399
|
-
function rollbackOne(directory) {
|
|
439
|
+
function rollbackOne(directory, options = {}) {
|
|
400
440
|
const parent = path.dirname(directory);
|
|
401
|
-
|
|
402
|
-
|
|
441
|
+
// The current location first; a copy an earlier installer left beside the
|
|
442
|
+
// skill still rolls back once.
|
|
443
|
+
const rollback = [rollbackPathFor(directory, options), legacyRollbackPath(directory)].find((candidate) => fs.existsSync(candidate));
|
|
444
|
+
if (!rollback) return { ok: false, status: "no_rollback", directory };
|
|
445
|
+
if (!pathInside(rollbackRoot(options), rollback) && !pathInside(parent, rollback)) throw new Error("Relay refused an unsafe rollback location.");
|
|
403
446
|
const currentChanges = localChanges(directory);
|
|
404
447
|
if (currentChanges.length) return { ok: false, status: readState(directory) ? "modified" : "unmanaged", directory, changedFiles: currentChanges };
|
|
405
448
|
const rollbackChanges = localChanges(rollback);
|
|
@@ -408,7 +451,7 @@ function rollbackOne(directory) {
|
|
|
408
451
|
if (!pathInside(parent, current)) throw new Error("Relay refused an unsafe rollback location.");
|
|
409
452
|
if (fs.existsSync(directory)) fs.renameSync(directory, current);
|
|
410
453
|
try {
|
|
411
|
-
|
|
454
|
+
moveTree(rollback, directory);
|
|
412
455
|
if (fs.existsSync(current)) fs.rmSync(current, { recursive: true, force: true });
|
|
413
456
|
return { ok: true, status: "rolled_back", directory, version: readState(directory)?.version || "" };
|
|
414
457
|
} catch (error) {
|
|
@@ -430,7 +473,7 @@ async function runCli(argv = process.argv.slice(2), options = {}) {
|
|
|
430
473
|
if (command === "install") return installBundled(common);
|
|
431
474
|
if (command === "update") return updateFromRemote(common);
|
|
432
475
|
if (command === "rollback") {
|
|
433
|
-
const results = defaultTargets(common).map((target) => ({ host: target.host, ...rollbackOne(target.directory) }));
|
|
476
|
+
const results = defaultTargets(common).map((target) => ({ host: target.host, ...rollbackOne(target.directory, common) }));
|
|
434
477
|
return { ok: results.every((item) => item.ok), results };
|
|
435
478
|
}
|
|
436
479
|
if (command === "status") {
|
|
@@ -470,6 +513,7 @@ module.exports = {
|
|
|
470
513
|
parseManifest,
|
|
471
514
|
readState,
|
|
472
515
|
rollbackOne,
|
|
516
|
+
rollbackPathFor,
|
|
473
517
|
runCli,
|
|
474
518
|
sha256,
|
|
475
519
|
uninstallManaged,
|
package/package.json
CHANGED
package/skill/SKILL.src.md
CHANGED
|
@@ -29,6 +29,120 @@ Hosted/headless agents can use the authenticated HTTPS protocol directly.
|
|
|
29
29
|
<!-- BEGIN GENERATED RELAY VALUE -->
|
|
30
30
|
<!-- END GENERATED RELAY VALUE -->
|
|
31
31
|
|
|
32
|
+
<!-- BEGIN GENERATED RELAY WRITING -->
|
|
33
|
+
## Writing a Relay
|
|
34
|
+
|
|
35
|
+
Every regular Relay has two documents for two readers. The person is switching
|
|
36
|
+
contexts and needs to understand what this is about and what it means for them.
|
|
37
|
+
Their agent needs enough context to understand the whole matter and help them
|
|
38
|
+
continue without making them reconstruct the sender's work. Compose the complete
|
|
39
|
+
`forAgent` first, then write `forHuman`. A short human message must not mean a
|
|
40
|
+
thin agent handoff. Apply these rules to drafts and previews as well as sends.
|
|
41
|
+
|
|
42
|
+
### Preserve the sender's intent and voice
|
|
43
|
+
|
|
44
|
+
The human's informal instructions tell you what to communicate; they are not
|
|
45
|
+
usually a draft to lightly edit. Write what this person would naturally say to
|
|
46
|
+
this recipient. Supply the words, never additional meaning. Preserve every ask,
|
|
47
|
+
question, commitment, permission, deadline, urgency, opinion, evaluation and
|
|
48
|
+
next step without adding, removing, strengthening or softening any of them.
|
|
49
|
+
Keep suggestions tentative when the sender made them tentative. Include thanks
|
|
50
|
+
or other sentiments the sender explicitly requested. Preserve exact text when
|
|
51
|
+
the person requests a verbatim payload.
|
|
52
|
+
|
|
53
|
+
Sending information or attaching a file does not imply "please review",
|
|
54
|
+
"thoughts?", "let me know", or another request for a response. Do not invent a
|
|
55
|
+
closing ask, "nothing needed", or implementation assignment to complete a
|
|
56
|
+
template. Make ordinary wording choices yourself; ask only when a critical
|
|
57
|
+
uncertainty would materially change the meaning or commitment.
|
|
58
|
+
|
|
59
|
+
Use the sender's recipient-specific vocabulary, rhythm, directness, formality,
|
|
60
|
+
warmth, emphasis and sign-off. When relationship context matters, `sent` and
|
|
61
|
+
`chat <id>` can supply facts, referents and examples of messages the human typed.
|
|
62
|
+
Learn their voice from those, not earlier agent-written messages. History cannot
|
|
63
|
+
revive superseded intent. Do not copy the brevity or shorthand of messages they
|
|
64
|
+
wrote while already in a conversation; this reader may need fresh orientation.
|
|
65
|
+
|
|
66
|
+
### The agent document: carry the complete useful context
|
|
67
|
+
|
|
68
|
+
`forAgent` is required and non-empty for a regular Relay. Write a self-contained
|
|
69
|
+
document, as long and detailed as the authorized subject requires. Under-sending
|
|
70
|
+
here is worse than over-sending. Preserve the useful conclusions, constraints,
|
|
71
|
+
rejected options, failures, preferences, questions, next steps, sources,
|
|
72
|
+
mechanisms, evidence, code, paths, logs, reproduction steps, chronology, data
|
|
73
|
+
and verification guidance that are available and relevant. Use Markdown when
|
|
74
|
+
it helps. Do not invent missing evidence or include unrelated private context.
|
|
75
|
+
|
|
76
|
+
Retain each distinct point from the sender, its rationale when supplied, and
|
|
77
|
+
the qualifications needed to interpret it. Separate observations, suggestions,
|
|
78
|
+
open questions and authorized actions. Technical detail does not itself make a
|
|
79
|
+
message an assignment. Give the recipient's agent substantive context, not an
|
|
80
|
+
instruction to "capture these points" followed by a shorter paraphrase of the
|
|
81
|
+
human message. Avoid repeating `forHuman` verbatim; enough shared context to
|
|
82
|
+
make the agent document understandable is appropriate.
|
|
83
|
+
|
|
84
|
+
### The human document: write for someone arriving fresh
|
|
85
|
+
|
|
86
|
+
Start by plainly saying what this is about. Assume the reader has done a dozen
|
|
87
|
+
other things since it last came up. Give the minimum background needed before
|
|
88
|
+
the news; preserve that orientation when cutting. Do not refer to "the new
|
|
89
|
+
rule", "what we settled", an unexplained thread, or a coined term. Retell the
|
|
90
|
+
relevant thing in familiar words. A follow-up to an issue the recipient raised
|
|
91
|
+
within the last day may need only a sentence about the result and the closing
|
|
92
|
+
state the sender intended.
|
|
93
|
+
|
|
94
|
+
Then convey what happened, why it matters, and any decision, opinion or action
|
|
95
|
+
the sender actually wants. Keep the news to three sentences where possible;
|
|
96
|
+
use a fourth only when needed to preserve the intended meaning. Stay under 95
|
|
97
|
+
words by default. That is a ceiling, never a target: a small update is usually
|
|
98
|
+
a line or two. There is no required length ratio between the two documents;
|
|
99
|
+
their readers' needs determine the length.
|
|
100
|
+
|
|
101
|
+
Use complete, spoken sentences and plain words. Read it aloud: would the sender
|
|
102
|
+
say this to the recipient's face, and would the recipient understand it without
|
|
103
|
+
doing the work? Put one idea at a time. Avoid fragments, clipped shorthand,
|
|
104
|
+
clever lines, figures of speech, flourishes, or balanced rhetorical halves.
|
|
105
|
+
Use the sender's names for things. Words the recipient encounters in the
|
|
106
|
+
product or their own work are fine; avoid vocabulary learned only while doing
|
|
107
|
+
the underlying investigation. Say what happened to someone: "A supplier
|
|
108
|
+
charged us more than we agreed" or "People who opened the invite saw a blank
|
|
109
|
+
page."
|
|
110
|
+
|
|
111
|
+
Keep mechanisms, evidence, paths, commands, logs, versions, internal identifiers,
|
|
112
|
+
work chronology and implementation detail in `forAgent`, unless the sender
|
|
113
|
+
explicitly wants the person to read them or they change the person's decision.
|
|
114
|
+
Do not pack four findings into one sentence, squeeze a checklist into prose, or
|
|
115
|
+
turn the human document into an inventory of the agent document. Cut details
|
|
116
|
+
the person need not read before cutting meaning or necessary background.
|
|
117
|
+
Never add text just because space remains. No headings, lists, tables, code
|
|
118
|
+
blocks or repetition of the title in `forHuman`.
|
|
119
|
+
|
|
120
|
+
If Relay rejects an overlong draft, review it from the recipient's perspective,
|
|
121
|
+
remove repetition and move supporting detail to `forAgent`. Use any supported
|
|
122
|
+
length-review override only after rejection and only when the exact draft's
|
|
123
|
+
extra length is necessary to preserve intent; never preemptively or merely
|
|
124
|
+
because more detail is available.
|
|
125
|
+
|
|
126
|
+
### Title, message kind and final review
|
|
127
|
+
|
|
128
|
+
For a titled Relay, use a natural 3–6 word gist in the sender's register. Name
|
|
129
|
+
the single ask, outcome, update or decision someone should recognize at a
|
|
130
|
+
glance. Do not concatenate every finding or write a report headline.
|
|
131
|
+
|
|
132
|
+
Classify the requested outcome: `kind: "message"` is human correspondence,
|
|
133
|
+
including technical notes, suggestions, opinions and decisions. Use
|
|
134
|
+
`kind: "task"` only for requested external work by the recipient's agent, such
|
|
135
|
+
as inspecting, retrieving, changing, testing or verifying something. A small
|
|
136
|
+
operation or one addressed as "you" is still work; dense agent context alone
|
|
137
|
+
is not. Respect the account's available capabilities.
|
|
138
|
+
|
|
139
|
+
Before presenting or sending, check both documents against the user's request:
|
|
140
|
+
every intended point is preserved; no ask or commitment was invented; the person
|
|
141
|
+
can understand the message on its own; the agent has the complete useful context;
|
|
142
|
+
and the human message sounds like the sender speaking. If either document fails,
|
|
143
|
+
revise it before sending or requesting any required approval.
|
|
144
|
+
<!-- END GENERATED RELAY WRITING -->
|
|
145
|
+
|
|
32
146
|
## Agent transport
|
|
33
147
|
|
|
34
148
|
<!-- BEGIN GENERATED RELAY TRANSPORT -->
|
|
@@ -327,8 +441,9 @@ result is ambiguous. Never invent an address or recipient identifier. Always
|
|
|
327
441
|
show the proposed human and agent payloads and obtain the person's approval for
|
|
328
442
|
a representational send.
|
|
329
443
|
|
|
330
|
-
Before composing any Relay, apply the complete writing contract
|
|
331
|
-
part of this skill for every send path; no MCP tool
|
|
444
|
+
Before composing any Relay, apply the complete writing contract in Writing a
|
|
445
|
+
Relay above. It is part of this skill for every send path; no MCP tool
|
|
446
|
+
description is needed.
|
|
332
447
|
|
|
333
448
|
Reading or summarizing an unread Relay should mark only the surfaced message as
|
|
334
449
|
read. The sequence is: run `inbox`; choose the intended Relay id; run `read`
|
|
@@ -344,120 +459,6 @@ same body and key; never generate a replacement key for a retry.
|
|
|
344
459
|
|
|
345
460
|
Use the absolute helper path with `help` for the exact local command surface.
|
|
346
461
|
|
|
347
|
-
<!-- BEGIN GENERATED RELAY WRITING -->
|
|
348
|
-
## Writing a Relay
|
|
349
|
-
|
|
350
|
-
Every regular Relay has two documents for two readers. The person is switching
|
|
351
|
-
contexts and needs to understand what this is about and what it means for them.
|
|
352
|
-
Their agent needs enough context to understand the whole matter and help them
|
|
353
|
-
continue without making them reconstruct the sender's work. Compose the complete
|
|
354
|
-
`forAgent` first, then write `forHuman`. A short human message must not mean a
|
|
355
|
-
thin agent handoff. Apply these rules to drafts and previews as well as sends.
|
|
356
|
-
|
|
357
|
-
### Preserve the sender's intent and voice
|
|
358
|
-
|
|
359
|
-
The human's informal instructions tell you what to communicate; they are not
|
|
360
|
-
usually a draft to lightly edit. Write what this person would naturally say to
|
|
361
|
-
this recipient. Supply the words, never additional meaning. Preserve every ask,
|
|
362
|
-
question, commitment, permission, deadline, urgency, opinion, evaluation and
|
|
363
|
-
next step without adding, removing, strengthening or softening any of them.
|
|
364
|
-
Keep suggestions tentative when the sender made them tentative. Include thanks
|
|
365
|
-
or other sentiments the sender explicitly requested. Preserve exact text when
|
|
366
|
-
the person requests a verbatim payload.
|
|
367
|
-
|
|
368
|
-
Sending information or attaching a file does not imply "please review",
|
|
369
|
-
"thoughts?", "let me know", or another request for a response. Do not invent a
|
|
370
|
-
closing ask, "nothing needed", or implementation assignment to complete a
|
|
371
|
-
template. Make ordinary wording choices yourself; ask only when a critical
|
|
372
|
-
uncertainty would materially change the meaning or commitment.
|
|
373
|
-
|
|
374
|
-
Use the sender's recipient-specific vocabulary, rhythm, directness, formality,
|
|
375
|
-
warmth, emphasis and sign-off. When relationship context matters, `sent` and
|
|
376
|
-
`chat <id>` can supply facts, referents and examples of messages the human typed.
|
|
377
|
-
Learn their voice from those, not earlier agent-written messages. History cannot
|
|
378
|
-
revive superseded intent. Do not copy the brevity or shorthand of messages they
|
|
379
|
-
wrote while already in a conversation; this reader may need fresh orientation.
|
|
380
|
-
|
|
381
|
-
### The agent document: carry the complete useful context
|
|
382
|
-
|
|
383
|
-
`forAgent` is required and non-empty for a regular Relay. Write a self-contained
|
|
384
|
-
document, as long and detailed as the authorized subject requires. Under-sending
|
|
385
|
-
here is worse than over-sending. Preserve the useful conclusions, constraints,
|
|
386
|
-
rejected options, failures, preferences, questions, next steps, sources,
|
|
387
|
-
mechanisms, evidence, code, paths, logs, reproduction steps, chronology, data
|
|
388
|
-
and verification guidance that are available and relevant. Use Markdown when
|
|
389
|
-
it helps. Do not invent missing evidence or include unrelated private context.
|
|
390
|
-
|
|
391
|
-
Retain each distinct point from the sender, its rationale when supplied, and
|
|
392
|
-
the qualifications needed to interpret it. Separate observations, suggestions,
|
|
393
|
-
open questions and authorized actions. Technical detail does not itself make a
|
|
394
|
-
message an assignment. Give the recipient's agent substantive context, not an
|
|
395
|
-
instruction to "capture these points" followed by a shorter paraphrase of the
|
|
396
|
-
human message. Avoid repeating `forHuman` verbatim; enough shared context to
|
|
397
|
-
make the agent document understandable is appropriate.
|
|
398
|
-
|
|
399
|
-
### The human document: write for someone arriving fresh
|
|
400
|
-
|
|
401
|
-
Start by plainly saying what this is about. Assume the reader has done a dozen
|
|
402
|
-
other things since it last came up. Give the minimum background needed before
|
|
403
|
-
the news; preserve that orientation when cutting. Do not refer to "the new
|
|
404
|
-
rule", "what we settled", an unexplained thread, or a coined term. Retell the
|
|
405
|
-
relevant thing in familiar words. A follow-up to an issue the recipient raised
|
|
406
|
-
within the last day may need only a sentence about the result and the closing
|
|
407
|
-
state the sender intended.
|
|
408
|
-
|
|
409
|
-
Then convey what happened, why it matters, and any decision, opinion or action
|
|
410
|
-
the sender actually wants. Keep the news to three sentences where possible;
|
|
411
|
-
use a fourth only when needed to preserve the intended meaning. Stay under 95
|
|
412
|
-
words by default. That is a ceiling, never a target: a small update is usually
|
|
413
|
-
a line or two. There is no required length ratio between the two documents;
|
|
414
|
-
their readers' needs determine the length.
|
|
415
|
-
|
|
416
|
-
Use complete, spoken sentences and plain words. Read it aloud: would the sender
|
|
417
|
-
say this to the recipient's face, and would the recipient understand it without
|
|
418
|
-
doing the work? Put one idea at a time. Avoid fragments, clipped shorthand,
|
|
419
|
-
clever lines, figures of speech, flourishes, or balanced rhetorical halves.
|
|
420
|
-
Use the sender's names for things. Words the recipient encounters in the
|
|
421
|
-
product or their own work are fine; avoid vocabulary learned only while doing
|
|
422
|
-
the underlying investigation. Say what happened to someone: "A supplier
|
|
423
|
-
charged us more than we agreed" or "People who opened the invite saw a blank
|
|
424
|
-
page."
|
|
425
|
-
|
|
426
|
-
Keep mechanisms, evidence, paths, commands, logs, versions, internal identifiers,
|
|
427
|
-
work chronology and implementation detail in `forAgent`, unless the sender
|
|
428
|
-
explicitly wants the person to read them or they change the person's decision.
|
|
429
|
-
Do not pack four findings into one sentence, squeeze a checklist into prose, or
|
|
430
|
-
turn the human document into an inventory of the agent document. Cut details
|
|
431
|
-
the person need not read before cutting meaning or necessary background.
|
|
432
|
-
Never add text just because space remains. No headings, lists, tables, code
|
|
433
|
-
blocks or repetition of the title in `forHuman`.
|
|
434
|
-
|
|
435
|
-
If Relay rejects an overlong draft, review it from the recipient's perspective,
|
|
436
|
-
remove repetition and move supporting detail to `forAgent`. Use any supported
|
|
437
|
-
length-review override only after rejection and only when the exact draft's
|
|
438
|
-
extra length is necessary to preserve intent; never preemptively or merely
|
|
439
|
-
because more detail is available.
|
|
440
|
-
|
|
441
|
-
### Title, message kind and final review
|
|
442
|
-
|
|
443
|
-
For a titled Relay, use a natural 3–6 word gist in the sender's register. Name
|
|
444
|
-
the single ask, outcome, update or decision someone should recognize at a
|
|
445
|
-
glance. Do not concatenate every finding or write a report headline.
|
|
446
|
-
|
|
447
|
-
Classify the requested outcome: `kind: "message"` is human correspondence,
|
|
448
|
-
including technical notes, suggestions, opinions and decisions. Use
|
|
449
|
-
`kind: "task"` only for requested external work by the recipient's agent, such
|
|
450
|
-
as inspecting, retrieving, changing, testing or verifying something. A small
|
|
451
|
-
operation or one addressed as "you" is still work; dense agent context alone
|
|
452
|
-
is not. Respect the account's available capabilities.
|
|
453
|
-
|
|
454
|
-
Before presenting or sending, check both documents against the user's request:
|
|
455
|
-
every intended point is preserved; no ask or commitment was invented; the person
|
|
456
|
-
can understand the message on its own; the agent has the complete useful context;
|
|
457
|
-
and the human message sounds like the sender speaking. If either document fails,
|
|
458
|
-
revise it before sending or requesting any required approval.
|
|
459
|
-
<!-- END GENERATED RELAY WRITING -->
|
|
460
|
-
|
|
461
462
|
## Attachments, channels and conversations
|
|
462
463
|
|
|
463
464
|
Use `groups` to find a channel, `chats` to find a conversation, and `chat <id>`
|
|
@@ -468,6 +469,16 @@ and the same `idempotencyKey` for every retry. [[dev]]`kind: "task"` and an opti
|
|
|
468
469
|
`title` are[[/dev]][[prod]]An optional `title` is[[/prod]] also supported under the existing send contract. Set
|
|
469
470
|
`inReplyToRelayId` only for an explicitly selected message.
|
|
470
471
|
|
|
472
|
+
To forward a Relay the person sent or received, use `relay_forward` (or the
|
|
473
|
+
helper's `forward <relay-id>` with JSON on stdin) with the exact relay id, one
|
|
474
|
+
exact recipient identifier, an optional `note` in the person's own words to the
|
|
475
|
+
new recipient, and a stable `idempotencyKey`. Relay copies the original's
|
|
476
|
+
title, both documents and attachments itself and marks the new Relay as
|
|
477
|
+
forwarded from its original sender by name; do not restate the original in the
|
|
478
|
+
note. The original sender is not notified and does not join the new
|
|
479
|
+
conversation, so treat forwarding as disclosure: confirm who is receiving it.
|
|
480
|
+
Encrypted messages cannot be forwarded. Ask for approval as for any send.
|
|
481
|
+
|
|
471
482
|
To attach a local file, add `files: ["<absolute path>"]` to the JSON passed on
|
|
472
483
|
stdin to `send`, or `attachments: [{path: "<absolute path>", name: "report.pdf"}]`.
|
|
473
484
|
The helper reads and hashes files before sending. Companion encrypts them when
|
package/skill/manifest.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"name": "relay",
|
|
4
|
-
"version": "1.1.
|
|
4
|
+
"version": "1.1.42",
|
|
5
5
|
"consentVersion": 2,
|
|
6
|
-
"baseUrl": "https://sendrelays.com/skills/relay/v1.1.
|
|
6
|
+
"baseUrl": "https://sendrelays.com/skills/relay/v1.1.42",
|
|
7
7
|
"files": [
|
|
8
8
|
{
|
|
9
9
|
"path": "SKILL.md",
|
|
10
|
-
"sha256": "
|
|
10
|
+
"sha256": "f014ce9fea1cc6bf12a70ada1dd0524cb08b7dc4aa92e0d95c391670f2592557",
|
|
11
11
|
"variants": {
|
|
12
12
|
"dev": {
|
|
13
13
|
"source": "SKILL.dev.md",
|
|
14
|
-
"sha256": "
|
|
14
|
+
"sha256": "0728baf3e1729f7a09b160e0ac4fada66545c03f007e454302aa7359fa9f15f6"
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
},
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
},
|
|
22
22
|
{
|
|
23
23
|
"path": "scripts/relay-protocol.mjs",
|
|
24
|
-
"sha256": "
|
|
24
|
+
"sha256": "66550ebe11c82cacf4d2a22d05aa9e803d5b3731de55d03ad7e45737b2f95661"
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
27
|
"path": "scripts/relay-local.mjs",
|