@canonmsg/agent-sdk 13.2.0 → 13.3.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/dist/chunks/{chunk-5YKGDUJX.js → chunk-AVR7QCHK.js} +369 -416
- package/dist/chunks/{chunk-DTAWEQCM.js → chunk-BQGXOAIW.js} +2 -2
- package/dist/chunks/{chunk-5WPAB5MB.js → chunk-CUPSYLBV.js} +2041 -2049
- package/dist/chunks/{chunk-YJUPKU47.js → chunk-HFT75BNB.js} +2 -2
- package/dist/chunks/{chunk-YHDDOADK.js → chunk-NIORQKUI.js} +1 -1
- package/dist/chunks/{chunk-6FD6KFQN.js → chunk-ZT3TJZAV.js} +400 -270
- package/dist/chunks/{mls-crypto-NZVMSUFU.js → mls-crypto-AW7TY744.js} +2 -2
- package/dist/cli.js +141 -26
- package/dist/index.js +6 -6
- package/dist/internal/agent-tools/communication-tool.d.ts +80 -0
- package/dist/internal/agent-tools/index.d.ts +1 -0
- package/dist/internal/backend-contracts/endpoint.d.ts +25 -0
- package/dist/internal/backend-contracts/mlsDevKeyPackages.d.ts +4 -0
- package/dist/internal/core/client.d.ts +0 -4
- package/dist/internal/core/endpoint-client.d.ts +8 -0
- package/dist/internal/core/index.d.ts +1 -1
- package/dist/internal/core/registration-profile.d.ts +4 -1
- package/dist/internal/core/types.d.ts +3 -1
- package/dist/internal/endpoint/engine.d.ts +34 -9
- package/dist/internal/endpoint/events.d.ts +1 -2
- package/dist/internal/endpoint/index.d.ts +0 -5
- package/dist/internal/endpoint/media.d.ts +4 -63
- package/dist/internal/endpoint/mls-dev-groups.d.ts +9 -1
- package/dist/internal/endpoint/queue.d.ts +1 -3
- package/dist/media.js +4 -4
- package/dist/mls-wasm-host.js +3 -3
- package/dist/runner/mls.d.ts +12 -2
- package/dist/runner/runner.d.ts +5 -2
- package/package.json +2 -3
- package/dist/internal/endpoint/artifact-images.d.ts +0 -60
- package/dist/internal/endpoint/artifact-reducer.d.ts +0 -35
- package/dist/internal/endpoint/artifact-views.d.ts +0 -113
- package/dist/internal/endpoint/artifacts.d.ts +0 -75
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
wasmMlsCustody,
|
|
9
9
|
windowsAclProblems,
|
|
10
10
|
writeDurable
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-BQGXOAIW.js";
|
|
12
12
|
import {
|
|
13
13
|
BoundedFileReadError,
|
|
14
14
|
CanonApiError,
|
|
@@ -30,17 +30,19 @@ import {
|
|
|
30
30
|
renderCanonHostInboundContent,
|
|
31
31
|
resolveCanonRuntimeConnection,
|
|
32
32
|
splitTextWithinBytes,
|
|
33
|
+
utf8ByteLength,
|
|
33
34
|
verifyCanonRuntimeConnection
|
|
34
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-AVR7QCHK.js";
|
|
35
36
|
import {
|
|
36
37
|
NodeMlsDevCustodyError,
|
|
37
38
|
createNodeMlsDevCustody,
|
|
38
39
|
createPortableNodeMlsDevCustody
|
|
39
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-HFT75BNB.js";
|
|
40
41
|
import {
|
|
41
42
|
parseMlsDevClientConfiguration
|
|
42
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-NIORQKUI.js";
|
|
43
44
|
import {
|
|
45
|
+
AGENT_HOME_ELSEWHERE,
|
|
44
46
|
CONNECTOR_RELAY_ANSWER_WITHIN_MS,
|
|
45
47
|
CONNECTOR_RELAY_INSTALLATION_PATTERN,
|
|
46
48
|
CONNECTOR_RELAY_INSTALLATION_PREFIX,
|
|
@@ -54,10 +56,13 @@ import {
|
|
|
54
56
|
MLS_DEV_TEXT_PROTOCOL_V3,
|
|
55
57
|
NO_REPLY_ACK_NOTE,
|
|
56
58
|
PARTICIPATION_HISTORY_FETCH_LIMIT,
|
|
59
|
+
VERB_LIMITS,
|
|
57
60
|
__name,
|
|
61
|
+
agentHomeElsewhereMessage,
|
|
58
62
|
buildParticipationHistorySnapshot,
|
|
59
63
|
connectorRelayResponseBytes,
|
|
60
64
|
evaluateParticipationPolicy,
|
|
65
|
+
isCanonClientVersion,
|
|
61
66
|
isConnectorRelayCredential,
|
|
62
67
|
isConnectorRelayInstallation,
|
|
63
68
|
isMlsDevTextProtocol,
|
|
@@ -70,7 +75,7 @@ import {
|
|
|
70
75
|
parseMlsDevGroupRoster,
|
|
71
76
|
resolveAllowedUploadMimeType,
|
|
72
77
|
resolveConversationPolicyScope
|
|
73
|
-
} from "./chunk-
|
|
78
|
+
} from "./chunk-CUPSYLBV.js";
|
|
74
79
|
|
|
75
80
|
// src/runner/types.ts
|
|
76
81
|
var DriverSessionError = class extends Error {
|
|
@@ -1636,6 +1641,282 @@ import { createHash as createHash4, createHmac } from "node:crypto";
|
|
|
1636
1641
|
import { constants as constants3 } from "node:fs";
|
|
1637
1642
|
import { open as open2, realpath as realpath2 } from "node:fs/promises";
|
|
1638
1643
|
import { isAbsolute, resolve } from "node:path";
|
|
1644
|
+
|
|
1645
|
+
// ../agent-tools/src/communication-tool.ts
|
|
1646
|
+
var CANON_COMMUNICATE_DISCOVERY_MAX_RESULTS = 10;
|
|
1647
|
+
var NON_EMPTY_ID = { type: "string", minLength: 1, maxLength: 160 };
|
|
1648
|
+
var ACTION_FIELDS = {
|
|
1649
|
+
list_conversations: { required: [], optional: ["limit"] },
|
|
1650
|
+
discover_agents: { required: [], optional: ["query", "limit", "cursor"] },
|
|
1651
|
+
message_existing: { required: ["conversationId", "text"], optional: ["messageId"] },
|
|
1652
|
+
start_conversation: { required: ["principalId", "text"], optional: ["selection", "messageId"] },
|
|
1653
|
+
create_conversation: { required: ["memberIds"], optional: ["name"] },
|
|
1654
|
+
manage_participants: { required: ["conversationId", "userId", "operation"], optional: [] },
|
|
1655
|
+
leave_conversation: { required: ["conversationId"], optional: [] },
|
|
1656
|
+
forward_message: { required: ["sourceConversationId", "targetConversationId", "messageId"], optional: ["text"] },
|
|
1657
|
+
share_contact: { required: ["conversationId", "contactUserId"], optional: ["text", "messageId"] }
|
|
1658
|
+
};
|
|
1659
|
+
var ALIASES = {
|
|
1660
|
+
start_direct: "start_conversation",
|
|
1661
|
+
create_group: "create_conversation",
|
|
1662
|
+
manage_group_members: "manage_participants"
|
|
1663
|
+
};
|
|
1664
|
+
var CANON_COMMUNICATE_SHAPE = Object.fromEntries(Object.entries(ACTION_FIELDS).map(([action, { required, optional }]) => [action, [...required, ...optional]]));
|
|
1665
|
+
var FIELD_SCHEMAS = {
|
|
1666
|
+
limit: { type: "integer", minimum: 1, maximum: 100 },
|
|
1667
|
+
query: { type: "string", maxLength: MAX_DISCOVER_AGENTS_QUERY_LENGTH },
|
|
1668
|
+
cursor: { type: "string", minLength: 1 },
|
|
1669
|
+
conversationId: NON_EMPTY_ID,
|
|
1670
|
+
text: { type: "string" },
|
|
1671
|
+
messageId: NON_EMPTY_ID,
|
|
1672
|
+
principalId: NON_EMPTY_ID,
|
|
1673
|
+
selection: {
|
|
1674
|
+
type: "object",
|
|
1675
|
+
required: ["mode"],
|
|
1676
|
+
additionalProperties: false,
|
|
1677
|
+
properties: { mode: { type: "string", enum: ["new", "latest_or_new", "specific"] }, conversationId: NON_EMPTY_ID }
|
|
1678
|
+
},
|
|
1679
|
+
memberIds: { type: "array", minItems: 1, maxItems: VERB_LIMITS.groupMembers - 1, uniqueItems: true, items: NON_EMPTY_ID },
|
|
1680
|
+
name: { type: "string", minLength: 1, maxLength: VERB_LIMITS.groupNameChars },
|
|
1681
|
+
userId: NON_EMPTY_ID,
|
|
1682
|
+
operation: { type: "string", enum: ["add", "remove"] },
|
|
1683
|
+
sourceConversationId: NON_EMPTY_ID,
|
|
1684
|
+
targetConversationId: NON_EMPTY_ID,
|
|
1685
|
+
contactUserId: NON_EMPTY_ID
|
|
1686
|
+
};
|
|
1687
|
+
var SEND_ID = "a stable id for this message; reuse it when retrying";
|
|
1688
|
+
var FIELD_NOTES = {
|
|
1689
|
+
limit: { list_conversations: "how many conversations, 1 to 100", discover_agents: `how many agents, 1 to ${CANON_COMMUNICATE_DISCOVERY_MAX_RESULTS} (default ${CANON_COMMUNICATE_DISCOVERY_MAX_RESULTS})` },
|
|
1690
|
+
query: { discover_agents: `a name or description prefix of at most ${MAX_DISCOVER_AGENTS_QUERY_LENGTH} characters; leave it out to list the directory` },
|
|
1691
|
+
cursor: { discover_agents: "the cursor a previous discover_agents call with the same query returned" },
|
|
1692
|
+
conversationId: {
|
|
1693
|
+
message_existing: "the conversation to post in",
|
|
1694
|
+
share_contact: "the conversation to share the contact in",
|
|
1695
|
+
manage_participants: "the conversation",
|
|
1696
|
+
leave_conversation: "the conversation to leave"
|
|
1697
|
+
},
|
|
1698
|
+
text: {
|
|
1699
|
+
message_existing: "the message",
|
|
1700
|
+
start_conversation: `the opening message, never split; at most ${MLS_DEV_TEXT_MAX_BYTES} UTF-8 bytes when encrypted. Use message_existing for longer follow-ups`,
|
|
1701
|
+
forward_message: "a caption",
|
|
1702
|
+
share_contact: "a caption"
|
|
1703
|
+
},
|
|
1704
|
+
messageId: { message_existing: SEND_ID, start_conversation: SEND_ID, share_contact: SEND_ID, forward_message: "the message to forward" },
|
|
1705
|
+
principalId: { start_conversation: "the person or agent to talk to" },
|
|
1706
|
+
selection: { start_conversation: "which direct conversation: mode new (the default), latest_or_new, or specific with its conversationId" },
|
|
1707
|
+
memberIds: { create_conversation: "the people and agents to add besides you" },
|
|
1708
|
+
name: { create_conversation: "a title" },
|
|
1709
|
+
userId: { manage_participants: "the person or agent to add or remove" },
|
|
1710
|
+
operation: { manage_participants: "add or remove" },
|
|
1711
|
+
sourceConversationId: { forward_message: "the conversation the message is in" },
|
|
1712
|
+
targetConversationId: { forward_message: "the conversation to forward it to" },
|
|
1713
|
+
contactUserId: { share_contact: "whose contact card to share" }
|
|
1714
|
+
};
|
|
1715
|
+
function communicateInputSchema(shape, textMaxLength = VERB_LIMITS.messageTextBytes) {
|
|
1716
|
+
const actions = Object.keys(shape);
|
|
1717
|
+
const properties = {
|
|
1718
|
+
action: { type: "string", enum: actions, description: "What to do. Each other field says which actions take it." }
|
|
1719
|
+
};
|
|
1720
|
+
for (const [field, schema] of Object.entries(FIELD_SCHEMAS)) {
|
|
1721
|
+
const uses = actions.filter((action) => shape[action].includes(field) || ACTION_FIELDS[action].required.includes(field));
|
|
1722
|
+
if (!uses.length) continue;
|
|
1723
|
+
const notes = uses.map((action) => `${action} (${ACTION_FIELDS[action].required.includes(field) ? "required" : "optional"}): ${FIELD_NOTES[field][action]}`);
|
|
1724
|
+
properties[field] = { ...schema, ...field === "text" && textMaxLength !== null ? { maxLength: textMaxLength } : {}, description: `${notes.join("; ")}.` };
|
|
1725
|
+
}
|
|
1726
|
+
return { type: "object", required: ["action"], additionalProperties: false, properties };
|
|
1727
|
+
}
|
|
1728
|
+
__name(communicateInputSchema, "communicateInputSchema");
|
|
1729
|
+
function isRecord3(value) {
|
|
1730
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
1731
|
+
}
|
|
1732
|
+
__name(isRecord3, "isRecord");
|
|
1733
|
+
function readRequiredString(value) {
|
|
1734
|
+
return typeof value === "string" && value.trim() ? value : null;
|
|
1735
|
+
}
|
|
1736
|
+
__name(readRequiredString, "readRequiredString");
|
|
1737
|
+
var listed = /* @__PURE__ */ __name((names) => names.length < 2 ? names.join("") : `${names.slice(0, -1).join(", ")} and ${names.at(-1)}`, "listed");
|
|
1738
|
+
function parseCommunicateToolInput(value, shape = CANON_COMMUNICATE_SHAPE) {
|
|
1739
|
+
if (!isRecord3(value)) throw new Error("communicate arguments must be an object");
|
|
1740
|
+
const action = typeof value.action === "string" ? ALIASES[value.action] ?? value.action : "";
|
|
1741
|
+
if (!Object.hasOwn(shape, action)) {
|
|
1742
|
+
throw new Error(`communicate.action is invalid: use ${listed(Object.keys(shape))}`);
|
|
1743
|
+
}
|
|
1744
|
+
const fields = ACTION_FIELDS[action];
|
|
1745
|
+
const missing = fields.required.filter((field) => value[field] === void 0 || value[field] === null || typeof value[field] === "string" && !value[field].trim());
|
|
1746
|
+
if (missing.length) throw new Error(`${String(value.action)} needs ${listed(missing)}`);
|
|
1747
|
+
const taken = ["action", ...fields.required, ...fields.optional];
|
|
1748
|
+
const extra = Object.keys(value).filter((field) => !taken.includes(field));
|
|
1749
|
+
if (extra.length) throw new Error(`${String(value.action)} does not take ${listed(extra)}`);
|
|
1750
|
+
if (value.action === "list_conversations") {
|
|
1751
|
+
if (value.limit !== void 0 && (!Number.isInteger(value.limit) || value.limit < 1 || value.limit > 100)) throw new Error("list_conversations requires an optional limit from 1 to 100");
|
|
1752
|
+
return { action: "list_conversations", ...typeof value.limit === "number" ? { limit: value.limit } : {} };
|
|
1753
|
+
}
|
|
1754
|
+
if (value.action === "leave_conversation") {
|
|
1755
|
+
const conversationId = readRequiredString(value.conversationId);
|
|
1756
|
+
if (!conversationId) throw new Error("leave_conversation requires conversationId");
|
|
1757
|
+
return { action: "leave_conversation", conversationId };
|
|
1758
|
+
}
|
|
1759
|
+
if (value.action === "discover_agents") {
|
|
1760
|
+
if (value.query !== void 0 && typeof value.query !== "string") {
|
|
1761
|
+
throw new Error("communicate.query must be a string");
|
|
1762
|
+
}
|
|
1763
|
+
if (typeof value.query === "string" && value.query.length > MAX_DISCOVER_AGENTS_QUERY_LENGTH) {
|
|
1764
|
+
throw new Error(
|
|
1765
|
+
`communicate.query must be at most ${MAX_DISCOVER_AGENTS_QUERY_LENGTH} characters`
|
|
1766
|
+
);
|
|
1767
|
+
}
|
|
1768
|
+
if (value.limit !== void 0 && (!Number.isInteger(value.limit) || value.limit < 1 || value.limit > CANON_COMMUNICATE_DISCOVERY_MAX_RESULTS)) {
|
|
1769
|
+
throw new Error(
|
|
1770
|
+
`communicate.limit must be an integer from 1 to ${CANON_COMMUNICATE_DISCOVERY_MAX_RESULTS}`
|
|
1771
|
+
);
|
|
1772
|
+
}
|
|
1773
|
+
if (value.cursor !== void 0 && (typeof value.cursor !== "string" || value.cursor.length === 0)) {
|
|
1774
|
+
throw new Error("communicate.cursor must be a non-empty string");
|
|
1775
|
+
}
|
|
1776
|
+
return {
|
|
1777
|
+
action: "discover_agents",
|
|
1778
|
+
...typeof value.query === "string" ? { query: value.query } : {},
|
|
1779
|
+
limit: typeof value.limit === "number" ? value.limit : CANON_COMMUNICATE_DISCOVERY_MAX_RESULTS,
|
|
1780
|
+
...typeof value.cursor === "string" ? { cursor: value.cursor } : {}
|
|
1781
|
+
};
|
|
1782
|
+
}
|
|
1783
|
+
if (value.action === "create_group" || value.action === "create_conversation") {
|
|
1784
|
+
const name = value.name === void 0 ? void 0 : readRequiredString(value.name);
|
|
1785
|
+
if (name === null) throw new Error("communicate.name must be a non-empty string when supplied");
|
|
1786
|
+
if (!Array.isArray(value.memberIds) || value.memberIds.length === 0 || !value.memberIds.every((entry) => readRequiredString(entry) !== null)) {
|
|
1787
|
+
throw new Error("communicate.memberIds must contain at least one principal id");
|
|
1788
|
+
}
|
|
1789
|
+
return {
|
|
1790
|
+
action: value.action,
|
|
1791
|
+
...name ? { name } : {},
|
|
1792
|
+
memberIds: [...new Set(value.memberIds)]
|
|
1793
|
+
};
|
|
1794
|
+
}
|
|
1795
|
+
if (value.action === "forward_message") {
|
|
1796
|
+
const sourceConversationId = readRequiredString(value.sourceConversationId);
|
|
1797
|
+
const targetConversationId = readRequiredString(value.targetConversationId);
|
|
1798
|
+
const messageId2 = readRequiredString(value.messageId);
|
|
1799
|
+
if (!sourceConversationId) throw new Error("communicate.sourceConversationId is required");
|
|
1800
|
+
if (!targetConversationId) throw new Error("communicate.targetConversationId is required");
|
|
1801
|
+
if (!messageId2) throw new Error("communicate.messageId is required");
|
|
1802
|
+
if (value.text !== void 0 && typeof value.text !== "string") {
|
|
1803
|
+
throw new Error("communicate.text must be a string");
|
|
1804
|
+
}
|
|
1805
|
+
return {
|
|
1806
|
+
action: "forward_message",
|
|
1807
|
+
sourceConversationId,
|
|
1808
|
+
targetConversationId,
|
|
1809
|
+
messageId: messageId2,
|
|
1810
|
+
...value.text !== void 0 ? { text: value.text } : {}
|
|
1811
|
+
};
|
|
1812
|
+
}
|
|
1813
|
+
if (value.action === "share_contact") {
|
|
1814
|
+
const conversationId = readRequiredString(value.conversationId);
|
|
1815
|
+
const contactUserId = readRequiredString(value.contactUserId);
|
|
1816
|
+
const messageId2 = value.messageId === void 0 ? void 0 : readRequiredString(value.messageId);
|
|
1817
|
+
if (!conversationId) throw new Error("communicate.conversationId is required");
|
|
1818
|
+
if (!contactUserId) throw new Error("communicate.contactUserId is required");
|
|
1819
|
+
if (value.text !== void 0 && typeof value.text !== "string") {
|
|
1820
|
+
throw new Error("communicate.text must be a string");
|
|
1821
|
+
}
|
|
1822
|
+
if (value.messageId !== void 0 && !messageId2) {
|
|
1823
|
+
throw new Error("communicate.messageId must be a non-empty string");
|
|
1824
|
+
}
|
|
1825
|
+
return {
|
|
1826
|
+
action: "share_contact",
|
|
1827
|
+
conversationId,
|
|
1828
|
+
contactUserId,
|
|
1829
|
+
...value.text !== void 0 ? { text: value.text } : {},
|
|
1830
|
+
...messageId2 ? { messageId: messageId2 } : {}
|
|
1831
|
+
};
|
|
1832
|
+
}
|
|
1833
|
+
if (value.action === "manage_group_members" || value.action === "manage_participants") {
|
|
1834
|
+
const conversationId = readRequiredString(value.conversationId);
|
|
1835
|
+
const userId = readRequiredString(value.userId);
|
|
1836
|
+
if (!conversationId) throw new Error("communicate.conversationId is required");
|
|
1837
|
+
if (!userId) throw new Error("communicate.userId is required");
|
|
1838
|
+
if (value.operation !== "add" && value.operation !== "remove") {
|
|
1839
|
+
throw new Error("communicate.operation must be add or remove");
|
|
1840
|
+
}
|
|
1841
|
+
return {
|
|
1842
|
+
action: value.action,
|
|
1843
|
+
conversationId,
|
|
1844
|
+
userId,
|
|
1845
|
+
operation: value.operation
|
|
1846
|
+
};
|
|
1847
|
+
}
|
|
1848
|
+
const text3 = readRequiredString(value.text);
|
|
1849
|
+
const messageId = value.messageId === void 0 ? void 0 : readRequiredString(value.messageId);
|
|
1850
|
+
if (!text3) throw new Error("communicate.text is required");
|
|
1851
|
+
if (value.messageId !== void 0 && !messageId) {
|
|
1852
|
+
throw new Error("communicate.messageId must be a non-empty string");
|
|
1853
|
+
}
|
|
1854
|
+
if (value.action === "message_existing") {
|
|
1855
|
+
const conversationId = readRequiredString(value.conversationId);
|
|
1856
|
+
if (!conversationId) throw new Error("communicate.conversationId is required");
|
|
1857
|
+
return {
|
|
1858
|
+
action: "message_existing",
|
|
1859
|
+
conversationId,
|
|
1860
|
+
text: text3,
|
|
1861
|
+
...messageId ? { messageId } : {}
|
|
1862
|
+
};
|
|
1863
|
+
}
|
|
1864
|
+
if (value.action === "start_direct" || value.action === "start_conversation") {
|
|
1865
|
+
const principalId = readRequiredString(value.principalId);
|
|
1866
|
+
if (!principalId) throw new Error("communicate.principalId is required");
|
|
1867
|
+
if (utf8ByteLength(text3) > MLS_DEV_TEXT_MAX_BYTES) {
|
|
1868
|
+
throw new Error(`${value.action} opening text must be at most ${MLS_DEV_TEXT_MAX_BYTES} UTF-8 bytes; use message_existing for longer follow-ups`);
|
|
1869
|
+
}
|
|
1870
|
+
const selection = value.selection === void 0 ? value.action === "start_conversation" ? { mode: "new" } : void 0 : parseDirectConversationSelection(value.selection);
|
|
1871
|
+
return {
|
|
1872
|
+
action: value.action,
|
|
1873
|
+
principalId,
|
|
1874
|
+
text: text3,
|
|
1875
|
+
...selection ? { selection } : {},
|
|
1876
|
+
...messageId ? { messageId } : {}
|
|
1877
|
+
};
|
|
1878
|
+
}
|
|
1879
|
+
throw new Error("communicate.action is invalid");
|
|
1880
|
+
}
|
|
1881
|
+
__name(parseCommunicateToolInput, "parseCommunicateToolInput");
|
|
1882
|
+
function renderResult(result) {
|
|
1883
|
+
let id;
|
|
1884
|
+
if (result.status === "messaged" || result.status === "forwarded" || result.status === "shared") {
|
|
1885
|
+
id = result.messageId;
|
|
1886
|
+
} else if (result.status === "added" || result.status === "removed") {
|
|
1887
|
+
id = result.userId;
|
|
1888
|
+
} else if (result.status === "created") {
|
|
1889
|
+
id = result.conversationId;
|
|
1890
|
+
} else if (result.status === "requested" || result.status === "pending") {
|
|
1891
|
+
id = result.requestId;
|
|
1892
|
+
}
|
|
1893
|
+
return {
|
|
1894
|
+
content: [{
|
|
1895
|
+
type: "text",
|
|
1896
|
+
text: `communicate: ${result.status}${id ? ` (${id})` : ""}
|
|
1897
|
+
${JSON.stringify(result, null, 2)}`
|
|
1898
|
+
}]
|
|
1899
|
+
};
|
|
1900
|
+
}
|
|
1901
|
+
__name(renderResult, "renderResult");
|
|
1902
|
+
async function executeCanonCommunicateTool(client, args, shape = CANON_COMMUNICATE_SHAPE) {
|
|
1903
|
+
let input;
|
|
1904
|
+
try {
|
|
1905
|
+
input = parseCommunicateToolInput(args, shape);
|
|
1906
|
+
} catch (error) {
|
|
1907
|
+
return {
|
|
1908
|
+
content: [{
|
|
1909
|
+
type: "text",
|
|
1910
|
+
text: `Invalid communicate arguments: ${error.message}`
|
|
1911
|
+
}],
|
|
1912
|
+
isError: true
|
|
1913
|
+
};
|
|
1914
|
+
}
|
|
1915
|
+
return renderResult(await client.communicate(input));
|
|
1916
|
+
}
|
|
1917
|
+
__name(executeCanonCommunicateTool, "executeCanonCommunicateTool");
|
|
1918
|
+
|
|
1919
|
+
// src/runner/mls.ts
|
|
1639
1920
|
var fail = /* @__PURE__ */ __name(() => {
|
|
1640
1921
|
throw new Error("MLS_AUTHORITY_REQUIRED");
|
|
1641
1922
|
}, "fail");
|
|
@@ -1712,30 +1993,22 @@ function mentionsAgent(text3, names) {
|
|
|
1712
1993
|
});
|
|
1713
1994
|
}
|
|
1714
1995
|
__name(mentionsAgent, "mentionsAgent");
|
|
1996
|
+
var ENCRYPTED_COMMUNICATE_SHAPE = {
|
|
1997
|
+
list_conversations: ["limit"],
|
|
1998
|
+
discover_agents: ["limit"],
|
|
1999
|
+
message_existing: ["conversationId", "text"]
|
|
2000
|
+
};
|
|
1715
2001
|
var ENCRYPTED_COMMUNICATE = {
|
|
1716
2002
|
name: "communicate",
|
|
1717
2003
|
description: "List Canon conversations or agents, or send text to the current encrypted conversation. Other Canon outputs are not yet supported.",
|
|
1718
|
-
inputSchema:
|
|
1719
|
-
{
|
|
1720
|
-
type: "object",
|
|
1721
|
-
required: ["action"],
|
|
1722
|
-
additionalProperties: false,
|
|
1723
|
-
properties: { action: { const: "list_conversations" }, limit: { type: "integer", minimum: 1, maximum: 100 } }
|
|
1724
|
-
},
|
|
1725
|
-
{
|
|
1726
|
-
type: "object",
|
|
1727
|
-
required: ["action"],
|
|
1728
|
-
additionalProperties: false,
|
|
1729
|
-
properties: { action: { const: "discover_agents" }, limit: { type: "integer", minimum: 1, maximum: 10 } }
|
|
1730
|
-
},
|
|
1731
|
-
{
|
|
1732
|
-
type: "object",
|
|
1733
|
-
required: ["action", "conversationId", "text"],
|
|
1734
|
-
additionalProperties: false,
|
|
1735
|
-
properties: { action: { const: "message_existing" }, conversationId: { type: "string", minLength: 1 }, text: { type: "string", minLength: 1 } }
|
|
1736
|
-
}
|
|
1737
|
-
] }
|
|
2004
|
+
inputSchema: communicateInputSchema(ENCRYPTED_COMMUNICATE_SHAPE, null)
|
|
1738
2005
|
};
|
|
2006
|
+
var errandShape = /* @__PURE__ */ __name((contact) => ({ ...ENCRYPTED_COMMUNICATE_SHAPE, ...contact ? { start_conversation: ["principalId", "text"] } : {} }), "errandShape");
|
|
2007
|
+
var errandCommunicate = /* @__PURE__ */ __name((contact) => ({
|
|
2008
|
+
name: "communicate",
|
|
2009
|
+
description: "List Canon conversations or agents, or send text to your errand's recipient or to the conversation it came from" + (contact ? ". Start the conversation with the recipient first, with start_conversation." : "."),
|
|
2010
|
+
inputSchema: communicateInputSchema(errandShape(contact), null)
|
|
2011
|
+
}), "errandCommunicate");
|
|
1739
2012
|
var MAX_CONFIGURATION_BYTES = 64 * 1024;
|
|
1740
2013
|
function invalid() {
|
|
1741
2014
|
throw new Error("MLS_DEV_CONFIGURATION_INVALID");
|
|
@@ -2412,232 +2685,16 @@ function relayRunnerHome(home, installationId) {
|
|
|
2412
2685
|
}
|
|
2413
2686
|
__name(relayRunnerHome, "relayRunnerHome");
|
|
2414
2687
|
|
|
2688
|
+
// src/version.ts
|
|
2689
|
+
var AGENT_SDK_VERSION = true ? "13.3.0" : void 0;
|
|
2690
|
+
var RUNNER_CLIENT = isCanonClientVersion(AGENT_SDK_VERSION) ? Object.freeze({ kind: "runner", version: AGENT_SDK_VERSION }) : void 0;
|
|
2691
|
+
|
|
2415
2692
|
// src/runner/runner.ts
|
|
2416
2693
|
import { createHash as createHash9, randomUUID as randomUUID2 } from "node:crypto";
|
|
2417
2694
|
import { readFileSync as readFileSync7, rmSync as rmSync2 } from "node:fs";
|
|
2418
2695
|
import { realpath as realpath4, stat as stat2 } from "node:fs/promises";
|
|
2419
2696
|
import { basename as basename4, isAbsolute as isAbsolute3, join as join8, sep } from "node:path";
|
|
2420
2697
|
|
|
2421
|
-
// ../agent-tools/src/communication-tool.ts
|
|
2422
|
-
var CANON_COMMUNICATE_DISCOVERY_MAX_RESULTS = 10;
|
|
2423
|
-
function isRecord3(value) {
|
|
2424
|
-
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
2425
|
-
}
|
|
2426
|
-
__name(isRecord3, "isRecord");
|
|
2427
|
-
function readRequiredString(value) {
|
|
2428
|
-
return typeof value === "string" && value.trim() ? value : null;
|
|
2429
|
-
}
|
|
2430
|
-
__name(readRequiredString, "readRequiredString");
|
|
2431
|
-
function hasOnlyKeys(record, allowed) {
|
|
2432
|
-
return Object.keys(record).every((key) => allowed.has(key));
|
|
2433
|
-
}
|
|
2434
|
-
__name(hasOnlyKeys, "hasOnlyKeys");
|
|
2435
|
-
function parseCommunicateToolInput(value) {
|
|
2436
|
-
if (!isRecord3(value)) throw new Error("communicate arguments must be an object");
|
|
2437
|
-
if (value.action === "list_conversations") {
|
|
2438
|
-
if (!hasOnlyKeys(value, /* @__PURE__ */ new Set(["action", "limit"])) || value.limit !== void 0 && (!Number.isInteger(value.limit) || value.limit < 1 || value.limit > 100)) throw new Error("list_conversations requires an optional limit from 1 to 100");
|
|
2439
|
-
return { action: "list_conversations", ...typeof value.limit === "number" ? { limit: value.limit } : {} };
|
|
2440
|
-
}
|
|
2441
|
-
if (value.action === "leave_conversation") {
|
|
2442
|
-
const conversationId = readRequiredString(value.conversationId);
|
|
2443
|
-
if (!hasOnlyKeys(value, /* @__PURE__ */ new Set(["action", "conversationId"])) || !conversationId) throw new Error("leave_conversation requires conversationId");
|
|
2444
|
-
return { action: "leave_conversation", conversationId };
|
|
2445
|
-
}
|
|
2446
|
-
if (value.action === "discover_agents") {
|
|
2447
|
-
if (!hasOnlyKeys(value, /* @__PURE__ */ new Set(["action", "query", "limit", "cursor"]))) {
|
|
2448
|
-
throw new Error("discover_agents contains unsupported fields");
|
|
2449
|
-
}
|
|
2450
|
-
if (value.query !== void 0 && typeof value.query !== "string") {
|
|
2451
|
-
throw new Error("communicate.query must be a string");
|
|
2452
|
-
}
|
|
2453
|
-
if (typeof value.query === "string" && value.query.length > MAX_DISCOVER_AGENTS_QUERY_LENGTH) {
|
|
2454
|
-
throw new Error(
|
|
2455
|
-
`communicate.query must be at most ${MAX_DISCOVER_AGENTS_QUERY_LENGTH} characters`
|
|
2456
|
-
);
|
|
2457
|
-
}
|
|
2458
|
-
if (value.limit !== void 0 && (!Number.isInteger(value.limit) || value.limit < 1 || value.limit > CANON_COMMUNICATE_DISCOVERY_MAX_RESULTS)) {
|
|
2459
|
-
throw new Error(
|
|
2460
|
-
`communicate.limit must be an integer from 1 to ${CANON_COMMUNICATE_DISCOVERY_MAX_RESULTS}`
|
|
2461
|
-
);
|
|
2462
|
-
}
|
|
2463
|
-
if (value.cursor !== void 0 && (typeof value.cursor !== "string" || value.cursor.length === 0)) {
|
|
2464
|
-
throw new Error("communicate.cursor must be a non-empty string");
|
|
2465
|
-
}
|
|
2466
|
-
return {
|
|
2467
|
-
action: "discover_agents",
|
|
2468
|
-
...typeof value.query === "string" ? { query: value.query } : {},
|
|
2469
|
-
limit: typeof value.limit === "number" ? value.limit : CANON_COMMUNICATE_DISCOVERY_MAX_RESULTS,
|
|
2470
|
-
...typeof value.cursor === "string" ? { cursor: value.cursor } : {}
|
|
2471
|
-
};
|
|
2472
|
-
}
|
|
2473
|
-
if (value.action === "create_group" || value.action === "create_conversation") {
|
|
2474
|
-
if (!hasOnlyKeys(value, /* @__PURE__ */ new Set(["action", "name", "memberIds"]))) {
|
|
2475
|
-
throw new Error("create_group contains unsupported fields");
|
|
2476
|
-
}
|
|
2477
|
-
const name = value.name === void 0 ? void 0 : readRequiredString(value.name);
|
|
2478
|
-
if (name === null) throw new Error("communicate.name must be a non-empty string when supplied");
|
|
2479
|
-
if (!Array.isArray(value.memberIds) || value.memberIds.length === 0 || !value.memberIds.every((entry) => readRequiredString(entry) !== null)) {
|
|
2480
|
-
throw new Error("communicate.memberIds must contain at least one principal id");
|
|
2481
|
-
}
|
|
2482
|
-
return {
|
|
2483
|
-
action: value.action,
|
|
2484
|
-
...name ? { name } : {},
|
|
2485
|
-
memberIds: [...new Set(value.memberIds)]
|
|
2486
|
-
};
|
|
2487
|
-
}
|
|
2488
|
-
if (value.action === "forward_message") {
|
|
2489
|
-
if (!hasOnlyKeys(value, /* @__PURE__ */ new Set([
|
|
2490
|
-
"action",
|
|
2491
|
-
"sourceConversationId",
|
|
2492
|
-
"targetConversationId",
|
|
2493
|
-
"messageId",
|
|
2494
|
-
"text"
|
|
2495
|
-
]))) {
|
|
2496
|
-
throw new Error("forward_message contains unsupported fields");
|
|
2497
|
-
}
|
|
2498
|
-
const sourceConversationId = readRequiredString(value.sourceConversationId);
|
|
2499
|
-
const targetConversationId = readRequiredString(value.targetConversationId);
|
|
2500
|
-
const messageId2 = readRequiredString(value.messageId);
|
|
2501
|
-
if (!sourceConversationId) throw new Error("communicate.sourceConversationId is required");
|
|
2502
|
-
if (!targetConversationId) throw new Error("communicate.targetConversationId is required");
|
|
2503
|
-
if (!messageId2) throw new Error("communicate.messageId is required");
|
|
2504
|
-
if (value.text !== void 0 && typeof value.text !== "string") {
|
|
2505
|
-
throw new Error("communicate.text must be a string");
|
|
2506
|
-
}
|
|
2507
|
-
return {
|
|
2508
|
-
action: "forward_message",
|
|
2509
|
-
sourceConversationId,
|
|
2510
|
-
targetConversationId,
|
|
2511
|
-
messageId: messageId2,
|
|
2512
|
-
...value.text !== void 0 ? { text: value.text } : {}
|
|
2513
|
-
};
|
|
2514
|
-
}
|
|
2515
|
-
if (value.action === "share_contact") {
|
|
2516
|
-
if (!hasOnlyKeys(value, /* @__PURE__ */ new Set([
|
|
2517
|
-
"action",
|
|
2518
|
-
"conversationId",
|
|
2519
|
-
"contactUserId",
|
|
2520
|
-
"text",
|
|
2521
|
-
"messageId"
|
|
2522
|
-
]))) {
|
|
2523
|
-
throw new Error("share_contact contains unsupported fields");
|
|
2524
|
-
}
|
|
2525
|
-
const conversationId = readRequiredString(value.conversationId);
|
|
2526
|
-
const contactUserId = readRequiredString(value.contactUserId);
|
|
2527
|
-
const messageId2 = value.messageId === void 0 ? void 0 : readRequiredString(value.messageId);
|
|
2528
|
-
if (!conversationId) throw new Error("communicate.conversationId is required");
|
|
2529
|
-
if (!contactUserId) throw new Error("communicate.contactUserId is required");
|
|
2530
|
-
if (value.text !== void 0 && typeof value.text !== "string") {
|
|
2531
|
-
throw new Error("communicate.text must be a string");
|
|
2532
|
-
}
|
|
2533
|
-
if (value.messageId !== void 0 && !messageId2) {
|
|
2534
|
-
throw new Error("communicate.messageId must be a non-empty string");
|
|
2535
|
-
}
|
|
2536
|
-
return {
|
|
2537
|
-
action: "share_contact",
|
|
2538
|
-
conversationId,
|
|
2539
|
-
contactUserId,
|
|
2540
|
-
...value.text !== void 0 ? { text: value.text } : {},
|
|
2541
|
-
...messageId2 ? { messageId: messageId2 } : {}
|
|
2542
|
-
};
|
|
2543
|
-
}
|
|
2544
|
-
if (value.action === "manage_group_members" || value.action === "manage_participants") {
|
|
2545
|
-
if (!hasOnlyKeys(value, /* @__PURE__ */ new Set([
|
|
2546
|
-
"action",
|
|
2547
|
-
"conversationId",
|
|
2548
|
-
"userId",
|
|
2549
|
-
"operation"
|
|
2550
|
-
]))) {
|
|
2551
|
-
throw new Error("manage_group_members contains unsupported fields");
|
|
2552
|
-
}
|
|
2553
|
-
const conversationId = readRequiredString(value.conversationId);
|
|
2554
|
-
const userId = readRequiredString(value.userId);
|
|
2555
|
-
if (!conversationId) throw new Error("communicate.conversationId is required");
|
|
2556
|
-
if (!userId) throw new Error("communicate.userId is required");
|
|
2557
|
-
if (value.operation !== "add" && value.operation !== "remove") {
|
|
2558
|
-
throw new Error("communicate.operation must be add or remove");
|
|
2559
|
-
}
|
|
2560
|
-
return {
|
|
2561
|
-
action: value.action,
|
|
2562
|
-
conversationId,
|
|
2563
|
-
userId,
|
|
2564
|
-
operation: value.operation
|
|
2565
|
-
};
|
|
2566
|
-
}
|
|
2567
|
-
const text3 = readRequiredString(value.text);
|
|
2568
|
-
const messageId = value.messageId === void 0 ? void 0 : readRequiredString(value.messageId);
|
|
2569
|
-
if (!text3) throw new Error("communicate.text is required");
|
|
2570
|
-
if (value.messageId !== void 0 && !messageId) {
|
|
2571
|
-
throw new Error("communicate.messageId must be a non-empty string");
|
|
2572
|
-
}
|
|
2573
|
-
if (value.action === "message_existing") {
|
|
2574
|
-
if (!hasOnlyKeys(value, /* @__PURE__ */ new Set(["action", "conversationId", "text", "messageId"]))) {
|
|
2575
|
-
throw new Error("message_existing contains unsupported fields");
|
|
2576
|
-
}
|
|
2577
|
-
const conversationId = readRequiredString(value.conversationId);
|
|
2578
|
-
if (!conversationId) throw new Error("communicate.conversationId is required");
|
|
2579
|
-
return {
|
|
2580
|
-
action: "message_existing",
|
|
2581
|
-
conversationId,
|
|
2582
|
-
text: text3,
|
|
2583
|
-
...messageId ? { messageId } : {}
|
|
2584
|
-
};
|
|
2585
|
-
}
|
|
2586
|
-
if (value.action === "start_direct" || value.action === "start_conversation") {
|
|
2587
|
-
if (!hasOnlyKeys(value, /* @__PURE__ */ new Set(["action", "principalId", "text", "selection", "messageId"]))) {
|
|
2588
|
-
throw new Error("start_direct contains unsupported fields");
|
|
2589
|
-
}
|
|
2590
|
-
const principalId = readRequiredString(value.principalId);
|
|
2591
|
-
if (!principalId) throw new Error("communicate.principalId is required");
|
|
2592
|
-
const selection = value.selection === void 0 ? value.action === "start_conversation" ? { mode: "new" } : void 0 : parseDirectConversationSelection(value.selection);
|
|
2593
|
-
return {
|
|
2594
|
-
action: value.action,
|
|
2595
|
-
principalId,
|
|
2596
|
-
text: text3,
|
|
2597
|
-
...selection ? { selection } : {},
|
|
2598
|
-
...messageId ? { messageId } : {}
|
|
2599
|
-
};
|
|
2600
|
-
}
|
|
2601
|
-
throw new Error("communicate.action is invalid");
|
|
2602
|
-
}
|
|
2603
|
-
__name(parseCommunicateToolInput, "parseCommunicateToolInput");
|
|
2604
|
-
function renderResult(result) {
|
|
2605
|
-
let id;
|
|
2606
|
-
if (result.status === "messaged" || result.status === "forwarded" || result.status === "shared") {
|
|
2607
|
-
id = result.messageId;
|
|
2608
|
-
} else if (result.status === "added" || result.status === "removed") {
|
|
2609
|
-
id = result.userId;
|
|
2610
|
-
} else if (result.status === "created") {
|
|
2611
|
-
id = result.conversationId;
|
|
2612
|
-
} else if (result.status === "requested" || result.status === "pending") {
|
|
2613
|
-
id = result.requestId;
|
|
2614
|
-
}
|
|
2615
|
-
return {
|
|
2616
|
-
content: [{
|
|
2617
|
-
type: "text",
|
|
2618
|
-
text: `communicate: ${result.status}${id ? ` (${id})` : ""}
|
|
2619
|
-
${JSON.stringify(result, null, 2)}`
|
|
2620
|
-
}]
|
|
2621
|
-
};
|
|
2622
|
-
}
|
|
2623
|
-
__name(renderResult, "renderResult");
|
|
2624
|
-
async function executeCanonCommunicateTool(client, args) {
|
|
2625
|
-
let input;
|
|
2626
|
-
try {
|
|
2627
|
-
input = parseCommunicateToolInput(args);
|
|
2628
|
-
} catch (error) {
|
|
2629
|
-
return {
|
|
2630
|
-
content: [{
|
|
2631
|
-
type: "text",
|
|
2632
|
-
text: `Invalid communicate arguments: ${error.message}`
|
|
2633
|
-
}],
|
|
2634
|
-
isError: true
|
|
2635
|
-
};
|
|
2636
|
-
}
|
|
2637
|
-
return renderResult(await client.communicate(input));
|
|
2638
|
-
}
|
|
2639
|
-
__name(executeCanonCommunicateTool, "executeCanonCommunicateTool");
|
|
2640
|
-
|
|
2641
2698
|
// src/runner/finals.ts
|
|
2642
2699
|
import { createHash as createHash7 } from "node:crypto";
|
|
2643
2700
|
import { readdirSync as readdirSync3, readFileSync as readFileSync5, rmdirSync, unlinkSync as unlinkSync2 } from "node:fs";
|
|
@@ -3371,7 +3428,21 @@ var STYLE_RULES = {
|
|
|
3371
3428
|
observer: "Stay quiet unless a reply is necessary, and end your turn with no_reply."
|
|
3372
3429
|
};
|
|
3373
3430
|
var factText = /* @__PURE__ */ __name((value, limit = MAX_FACT_NAME) => speaker(value).slice(0, limit).trimEnd(), "factText");
|
|
3374
|
-
|
|
3431
|
+
var mentionKey = /* @__PURE__ */ __name((value) => value.normalize("NFC").trim().replace(/^@+/, "").replace(/\s+/g, " ").toLowerCase(), "mentionKey");
|
|
3432
|
+
var mentionsToo = /* @__PURE__ */ __name((name, other) => other.length > 0 && name.startsWith(other) && !/^[\p{L}\p{N}\p{M}_]/u.test(name.slice(other.length)), "mentionsToo");
|
|
3433
|
+
function mentionOf(peer, agents) {
|
|
3434
|
+
const name = peer.name?.trim();
|
|
3435
|
+
const key = name ? mentionKey(name) : "";
|
|
3436
|
+
if (name && key && !name.startsWith("@") && factText(name) === name && !agents.some((other) => other !== peer && !other.isYou && [other.name, other.id].some((word) => word !== void 0 && mentionsToo(key, mentionKey(word))))) return name;
|
|
3437
|
+
return ENDPOINT_ID_PATTERN.test(peer.id) ? peer.id : void 0;
|
|
3438
|
+
}
|
|
3439
|
+
__name(mentionOf, "mentionOf");
|
|
3440
|
+
function factsPrompt({ name, topic, owner, members, rules }, kind, self) {
|
|
3441
|
+
const agents = members.filter((member) => member.type === "ai_agent");
|
|
3442
|
+
const reach = rules && !rules.allowAgentToAgent ? [] : members.slice(0, MAX_FACT_MEMBERS).filter((member) => member.type === "ai_agent" && !member.isYou).flatMap((peer) => {
|
|
3443
|
+
const mention = mentionOf(peer, agents), seen = factText(peer.name ?? peer.id);
|
|
3444
|
+
return !mention ? [] : [mention === seen ? `@${mention}` : `@${mention} (the id of ${seen})`];
|
|
3445
|
+
});
|
|
3375
3446
|
const shown = members.slice(0, MAX_FACT_MEMBERS).map((member) => member.isYou ? "you" : factText(member.name ?? member.id) + (member.isOwner ? OWNER_MARK : member.type === "ai_agent" ? " [agent]" : ""));
|
|
3376
3447
|
const lines = !rules ? [] : [
|
|
3377
3448
|
STYLE_RULES[rules.style],
|
|
@@ -3382,10 +3453,12 @@ function factsPrompt({ name, topic, owner, members, rules }, kind) {
|
|
|
3382
3453
|
].filter((line) => line !== void 0);
|
|
3383
3454
|
return [
|
|
3384
3455
|
FACTS_OPEN,
|
|
3456
|
+
...self ? [`Your name: ${factText(self)}`] : [],
|
|
3385
3457
|
`Your owner: ${factText(owner.name ?? owner.id)}`,
|
|
3386
3458
|
...name ? [`Conversation: ${factText(name)}`] : [],
|
|
3387
3459
|
...topic ? [`Topic: ${factText(topic, MAX_FACT_TOPIC)}`] : [],
|
|
3388
3460
|
`Members: ${shown.join(", ")}${members.length > shown.length ? `, and ${members.length - shown.length} more` : ""}`,
|
|
3461
|
+
...reach.length ? [`${reach.length > 1 ? "Other agents here answer only a message that mentions them" : "Another agent here answers only a message that mentions it"}: ${reach.join(", ")}.`] : [],
|
|
3389
3462
|
...lines.length ? [
|
|
3390
3463
|
"This conversation's rules, which your owner or any of its admins can set. They say how you take part here, not what you may do:",
|
|
3391
3464
|
...lines.map((line) => `- ${line}`)
|
|
@@ -3394,16 +3467,6 @@ function factsPrompt({ name, topic, owner, members, rules }, kind) {
|
|
|
3394
3467
|
];
|
|
3395
3468
|
}
|
|
3396
3469
|
__name(factsPrompt, "factsPrompt");
|
|
3397
|
-
var errandCommunicate = /* @__PURE__ */ __name((contact) => ({
|
|
3398
|
-
name: "communicate",
|
|
3399
|
-
description: "List Canon conversations or agents, or send text to your errand's recipient or to the conversation it came from" + (contact ? ". Start the conversation with the recipient first, with start_conversation." : "."),
|
|
3400
|
-
inputSchema: { type: "object", oneOf: [...ENCRYPTED_COMMUNICATE.inputSchema.oneOf, ...contact ? [{
|
|
3401
|
-
type: "object",
|
|
3402
|
-
required: ["action", "principalId", "text"],
|
|
3403
|
-
additionalProperties: false,
|
|
3404
|
-
properties: { action: { const: "start_conversation" }, principalId: { type: "string", minLength: 1 }, text: { type: "string", minLength: 1 } }
|
|
3405
|
-
}] : []] }
|
|
3406
|
-
}), "errandCommunicate");
|
|
3407
3470
|
var promptLine = /* @__PURE__ */ __name((message2, name, media, mark = "") => [
|
|
3408
3471
|
`${speaker(name)}${mark}: ${defused(renderCanonHostInboundContent(message2, media?.files))}`,
|
|
3409
3472
|
...(media?.failed ?? []).map((label) => `[Attachment "${defused(inert(label))}" could not be downloaded]`)
|
|
@@ -3553,6 +3616,7 @@ async function createRunner(options) {
|
|
|
3553
3616
|
}, "keyed");
|
|
3554
3617
|
let outboundClosed = false;
|
|
3555
3618
|
let ownershipVersion;
|
|
3619
|
+
let installationId;
|
|
3556
3620
|
let displayName;
|
|
3557
3621
|
let ownerOf;
|
|
3558
3622
|
const refused = /* @__PURE__ */ new Set();
|
|
@@ -3686,11 +3750,18 @@ async function createRunner(options) {
|
|
|
3686
3750
|
log(why === "plaintext" ? `Refusing room ${roomId}: it is not encrypted, and this runner takes part only in encrypted rooms. Its messages are not delivered to this agent.` : `Refusing encrypted room ${roomId}: ${why === "no-device" ? "this runner has no MLS device (run it with --mls-dev-config)" : why === "remote" ? `the ${driver.name} driver runs off this host, which an encrypted room allows only in relay mode` : "its encryption protocol is not supported"}. Its messages are not delivered to this agent.`);
|
|
3687
3751
|
}
|
|
3688
3752
|
__name(refuse, "refuse");
|
|
3753
|
+
let homeSince = 0;
|
|
3689
3754
|
async function authorize() {
|
|
3690
3755
|
await endpoint.mlsDevDevice.ensureReady();
|
|
3691
3756
|
const me = await client.getAgentMe();
|
|
3692
3757
|
requireMlsSupport({ environmentId: options.environmentId });
|
|
3693
3758
|
if (me.agentId !== agentId || me.ownershipVersion !== ownershipVersion) throw new Error("MLS_AUTHORITY_REQUIRED");
|
|
3759
|
+
if (!options.relay && me.home && (me.home.kind === "connector" || me.home.installationId !== void 0 && me.home.installationId !== installationId)) {
|
|
3760
|
+
const error = Object.assign(new Error(agentHomeElsewhereMessage(me.displayName, me.home)), { code: AGENT_HOME_ELSEWHERE });
|
|
3761
|
+
options.onFatal?.(error);
|
|
3762
|
+
throw error;
|
|
3763
|
+
}
|
|
3764
|
+
homeSince = !options.relay && me.home?.kind === "runner" ? me.home.since ?? 0 : 0;
|
|
3694
3765
|
outboundClosed = me.outboundPolicy === "closed";
|
|
3695
3766
|
if (outboundClosed && options.relay) throw new Error("MLS_AUTHORITY_REQUIRED");
|
|
3696
3767
|
displayName = me.displayName;
|
|
@@ -3733,6 +3804,50 @@ ${error instanceof Error ? error.message : String(error)}`;
|
|
|
3733
3804
|
turn.timer.unref?.();
|
|
3734
3805
|
}
|
|
3735
3806
|
__name(progress, "progress");
|
|
3807
|
+
const waitingRooms = /* @__PURE__ */ new Map(), noting = /* @__PURE__ */ new Map();
|
|
3808
|
+
let ownerAsk;
|
|
3809
|
+
function noteAdmission(roomId, known) {
|
|
3810
|
+
const next = (noting.get(roomId) ?? Promise.resolve()).then(() => lookAtAdmission(roomId, known));
|
|
3811
|
+
noting.set(roomId, next.catch(() => void 0));
|
|
3812
|
+
return next;
|
|
3813
|
+
}
|
|
3814
|
+
__name(noteAdmission, "noteAdmission");
|
|
3815
|
+
async function lookAtAdmission(roomId, known) {
|
|
3816
|
+
if (!mlsOn || stopping) return;
|
|
3817
|
+
const inGroup = /* @__PURE__ */ __name(async () => {
|
|
3818
|
+
const view = await endpoint.mlsDevGroups.read(roomId);
|
|
3819
|
+
return !!view && view.status !== "removed";
|
|
3820
|
+
}, "inGroup");
|
|
3821
|
+
const read = /* @__PURE__ */ __name(async () => await endpoint.getConversation(roomId).catch(() => null) ?? void 0, "read");
|
|
3822
|
+
let joined = await inGroup(), room2 = known;
|
|
3823
|
+
if (!joined && known && !waitingRooms.has(roomId)) {
|
|
3824
|
+
room2 = await read() ?? room2;
|
|
3825
|
+
joined = await inGroup();
|
|
3826
|
+
}
|
|
3827
|
+
if (joined) {
|
|
3828
|
+
if (waitingRooms.has(roomId)) log(`Joined ${waitingRooms.get(roomId)}.`);
|
|
3829
|
+
waitingRooms.delete(roomId);
|
|
3830
|
+
return;
|
|
3831
|
+
}
|
|
3832
|
+
if (waitingRooms.has(roomId) || (await endpoint.mlsDevGroups.readAdmissionHold(roomId)).outside !== true) return;
|
|
3833
|
+
room2 ??= await read();
|
|
3834
|
+
const members = Array.isArray(room2?.memberIds) ? room2.memberIds : [];
|
|
3835
|
+
if (!members.includes(agentId)) return;
|
|
3836
|
+
const label = typeof room2?.name === "string" && room2.name.trim() ? `"${room2.name.trim()}"` : `room ${roomId}`;
|
|
3837
|
+
waitingRooms.set(roomId, label);
|
|
3838
|
+
ownerAsk ??= client.getAgentMe().then((me) => ({ id: me.ownerId, ...me.ownerName ? { name: me.ownerName } : {} }));
|
|
3839
|
+
const owner2 = await ownerAsk.catch(() => void 0);
|
|
3840
|
+
const ask2 = owner2 && members.includes(owner2.id) ? `Ask ${owner2.name ?? "its owner"} to open Canon.` : "Ask one of its members to open Canon.";
|
|
3841
|
+
log(`Waiting to join ${label}: only a device already in it can add this agent's device. ${ask2}`);
|
|
3842
|
+
}
|
|
3843
|
+
__name(lookAtAdmission, "lookAtAdmission");
|
|
3844
|
+
async function noteAdmissions() {
|
|
3845
|
+
const { conversations = [] } = await endpoint.query("list_conversations", { limit: 100 });
|
|
3846
|
+
for (const room2 of conversations) {
|
|
3847
|
+
if (typeof room2.id === "string" && ["mls", "mlsRequired", "mlsProtocol"].some((key) => Object.hasOwn(room2, key))) await noteAdmission(room2.id, room2);
|
|
3848
|
+
}
|
|
3849
|
+
}
|
|
3850
|
+
__name(noteAdmissions, "noteAdmissions");
|
|
3736
3851
|
async function offer(event) {
|
|
3737
3852
|
offering += 1;
|
|
3738
3853
|
touch();
|
|
@@ -3783,6 +3898,7 @@ ${error instanceof Error ? error.message : String(error)}`;
|
|
|
3783
3898
|
await authorize();
|
|
3784
3899
|
const binding = await groups.readVerifiedMessageBinding({ conversationId, messageId: payload.message.id });
|
|
3785
3900
|
if (!binding) throw new Error("MLS_INPUT_NOT_VERIFIED");
|
|
3901
|
+
if (binding.acceptedAtMs < homeSince) return void await handle.settle("mls-before-home");
|
|
3786
3902
|
const { messages } = await endpoint.query("get_messages", { conversationId, messageIds: [binding.messageId] });
|
|
3787
3903
|
const [message2] = messages;
|
|
3788
3904
|
if (messages.length !== 1 || message2.id !== binding.messageId || message2.senderId !== binding.senderId || message2.senderType !== binding.senderPrincipal.type || !Number.isSafeInteger(message2.mlsFeedOrdinal) || typeof message2.text !== "string" && !(message2.text === null && (message2.attachments?.length ?? 0) === 1)) throw new Error("MLS_INPUT_NOT_VERIFIED");
|
|
@@ -4166,6 +4282,8 @@ ${decision.reasonCode}`;
|
|
|
4166
4282
|
function bindTools(current, turn) {
|
|
4167
4283
|
const questions = /* @__PURE__ */ new Map();
|
|
4168
4284
|
let asked = 0;
|
|
4285
|
+
const errandContact = Boolean(turn.errand && "contact" in turn.errand.target);
|
|
4286
|
+
const shape = turn.errand ? errandShape(errandContact) : ENCRYPTED_COMMUNICATE_SHAPE;
|
|
4169
4287
|
const handlers = {
|
|
4170
4288
|
no_reply: /* @__PURE__ */ __name(async ({ reason }) => {
|
|
4171
4289
|
turn.silenced = typeof reason === "string" && reason.trim() ? { reason } : {};
|
|
@@ -4186,7 +4304,7 @@ ${decision.reasonCode}`;
|
|
|
4186
4304
|
turn.outbox.push(file.path);
|
|
4187
4305
|
return { result: toolText(`${basename4(file.path)} will be sent with your reply.`) };
|
|
4188
4306
|
}, "send_file"),
|
|
4189
|
-
communicate: /* @__PURE__ */ __name(async (args) => ({ result: await communicate(current, turn, args) }), "communicate"),
|
|
4307
|
+
communicate: /* @__PURE__ */ __name(async (args) => ({ result: await communicate(current, turn, args, shape) }), "communicate"),
|
|
4190
4308
|
// RUN-25 through a tool: the same `ask`, so the same responder, card, expiry, waiting_input and paused deadline;
|
|
4191
4309
|
// the turn's end or an abort withdraws it, and the call then fails.
|
|
4192
4310
|
ask_question: /* @__PURE__ */ __name(async (args) => {
|
|
@@ -4213,7 +4331,7 @@ ${decision.reasonCode}`;
|
|
|
4213
4331
|
// §12.6: an errand turn reaches its recipient and its origin, and asks only in an origin its report goes to.
|
|
4214
4332
|
definitions: errand ? [
|
|
4215
4333
|
TOOLS.no_reply,
|
|
4216
|
-
errandCommunicate(
|
|
4334
|
+
errandCommunicate(errandContact),
|
|
4217
4335
|
...errand.reports && cards && offersQuestions ? [TOOLS.ask_question] : []
|
|
4218
4336
|
] : [
|
|
4219
4337
|
TOOLS.no_reply,
|
|
@@ -4250,12 +4368,12 @@ ${decision.reasonCode}`;
|
|
|
4250
4368
|
}, "call") } };
|
|
4251
4369
|
}
|
|
4252
4370
|
__name(bindTools, "bindTools");
|
|
4253
|
-
async function communicate(current, turn, args) {
|
|
4371
|
+
async function communicate(current, turn, args, shape) {
|
|
4254
4372
|
const errand = turn.errand;
|
|
4255
4373
|
const origins = errand ? errand.origin ? [errand.origin.room] : [] : turn.contexts.flatMap((record) => record.origin ? [record.origin] : []);
|
|
4256
4374
|
let command;
|
|
4257
4375
|
try {
|
|
4258
|
-
command = parseCommunicateToolInput(args);
|
|
4376
|
+
command = parseCommunicateToolInput(args, shape);
|
|
4259
4377
|
} catch (error) {
|
|
4260
4378
|
return toolText(`Invalid communicate arguments: ${error instanceof Error ? error.message : String(error)}`, true);
|
|
4261
4379
|
}
|
|
@@ -4462,7 +4580,9 @@ ${JSON.stringify(result, null, 2)}`);
|
|
|
4462
4580
|
async function stopOutput(record) {
|
|
4463
4581
|
let queued2 = 0;
|
|
4464
4582
|
for (const [index, { messageId }] of encryptedMessages(record.mls, record.text, record.options, record).entries()) {
|
|
4465
|
-
|
|
4583
|
+
let operation = await endpoint.getOperation(encryptedOperation(record.mls, messageId));
|
|
4584
|
+
while (operation?.resentAs) operation = await endpoint.getOperation(operation.resentAs);
|
|
4585
|
+
const status = operation?.status;
|
|
4466
4586
|
if (status && status !== "succeeded" && status !== "rejected") queued2 = index + 1;
|
|
4467
4587
|
}
|
|
4468
4588
|
const { items: _items, errand: _errand, ...rest } = record;
|
|
@@ -4470,13 +4590,14 @@ ${JSON.stringify(result, null, 2)}`);
|
|
|
4470
4590
|
else await endpoint.finishCompletion(record.turnId);
|
|
4471
4591
|
}
|
|
4472
4592
|
__name(stopOutput, "stopOutput");
|
|
4593
|
+
const publicationGuard = /* @__PURE__ */ __name((audience) => ({ automaticResend }) => (automaticResend ? authorize() : assertAudience(audience)).then(() => true, (error) => {
|
|
4594
|
+
if (audienceChanged(error)) throw error;
|
|
4595
|
+
return false;
|
|
4596
|
+
}), "publicationGuard");
|
|
4473
4597
|
async function sendEncrypted(audience, messageId, text3, signal, replyAuthority) {
|
|
4474
4598
|
const operationId = encryptedOperation(audience, messageId);
|
|
4475
4599
|
if (!guards.has(operationId)) {
|
|
4476
|
-
guards.set(operationId, endpoint.registerDeliveryGuard(operationId, (
|
|
4477
|
-
if (audienceChanged(error)) throw error;
|
|
4478
|
-
return false;
|
|
4479
|
-
})));
|
|
4600
|
+
guards.set(operationId, endpoint.registerDeliveryGuard(operationId, publicationGuard(audience)));
|
|
4480
4601
|
}
|
|
4481
4602
|
if (!await endpoint.getOperation(operationId)) await assertAudience(audience);
|
|
4482
4603
|
signal?.throwIfAborted();
|
|
@@ -4516,7 +4637,7 @@ ${JSON.stringify(result, null, 2)}`);
|
|
|
4516
4637
|
async function sendEncryptedItems(audience, items, signal) {
|
|
4517
4638
|
if (!items.length) return;
|
|
4518
4639
|
const media = await client.getFileMediaPipeline(options.fetch);
|
|
4519
|
-
const sendOptions = { signal, beforePublish:
|
|
4640
|
+
const sendOptions = { signal, beforePublish: publicationGuard(audience) };
|
|
4520
4641
|
for (const item of items) {
|
|
4521
4642
|
signal?.throwIfAborted();
|
|
4522
4643
|
try {
|
|
@@ -4599,7 +4720,7 @@ ${JSON.stringify(result, null, 2)}`);
|
|
|
4599
4720
|
session: turn.session,
|
|
4600
4721
|
// §12.6: an errand turn's prompt is its errand; the owner's message is only its input's record.
|
|
4601
4722
|
prompt: turn.errand ? errandPrompt(turn.errand) : [
|
|
4602
|
-
...turn.facts ? [...factsPrompt(turn.facts, kind), ""] : [],
|
|
4723
|
+
...turn.facts ? [...factsPrompt(turn.facts, kind, displayName), ""] : [],
|
|
4603
4724
|
...turn.notes.flatMap((note) => [NOTE_OPEN, defused(note.text), NOTE_CLOSE, ""]),
|
|
4604
4725
|
...turn.contexts.flatMap((record) => [...contextPrompt(record), ""]),
|
|
4605
4726
|
...history.length ? ["Earlier in this conversation (context, not new requests):", ...history.map((message2) => line(message2)), "", "New messages:"] : turn.facts || turn.notes.length || turn.contexts.length ? ["New messages:"] : [],
|
|
@@ -5073,7 +5194,7 @@ ${JSON.stringify(result, null, 2)}`);
|
|
|
5073
5194
|
...record.origin ? { origin: record.origin } : {}
|
|
5074
5195
|
}), "errandFacts");
|
|
5075
5196
|
function errandPrompt({ record, target, origin, reports }) {
|
|
5076
|
-
const recipient = "contact" in target ? `${target.contact}. Start a new conversation with them: communicate start_conversation.` : `the encrypted conversation ${target.room}. Send to it with communicate message_existing.`;
|
|
5197
|
+
const recipient = "contact" in target ? `${target.contact}. Start a new conversation with them: communicate start_conversation. If they are an agent, address them with @${target.contact} (their exact agent id): an agent's message reaches another only if it mentions it.` : `the encrypted conversation ${target.room}. Send to it with communicate message_existing. An agent's message reaches another agent there only if it mentions it, as @ and its exact agent id.`;
|
|
5077
5198
|
const report = reports ? `your final answer is your report to your owner, posted in the conversation ${origin.room}.` : "your final answer is your report to your owner, and it stays on their machine.";
|
|
5078
5199
|
return [
|
|
5079
5200
|
`${ERRAND_OPEN} Your owner sends you on an errand, from the machine you run on. It is not a message in any conversation, and no one else sees it.`,
|
|
@@ -5283,7 +5404,10 @@ ${JSON.stringify(result, null, 2)}`);
|
|
|
5283
5404
|
const known = rooms.get(data.conversationId);
|
|
5284
5405
|
if (known && Array.isArray(merged.memberIds) && !merged.memberIds.includes(agentId)) loseMembership(known);
|
|
5285
5406
|
}
|
|
5286
|
-
if (mlsOn && (encrypted || Array.isArray(changes?.memberIds)))
|
|
5407
|
+
if (mlsOn && (encrypted || Array.isArray(changes?.memberIds))) {
|
|
5408
|
+
await checkAudience(rooms.get(data.conversationId));
|
|
5409
|
+
await noteAdmission(data.conversationId).catch(warn("room admission"));
|
|
5410
|
+
}
|
|
5287
5411
|
if (encrypted) mlsAsks?.wake(data.conversationId);
|
|
5288
5412
|
}
|
|
5289
5413
|
__name(onRoomChanged, "onRoomChanged");
|
|
@@ -5393,9 +5517,12 @@ ${JSON.stringify(result, null, 2)}`);
|
|
|
5393
5517
|
environmentId: connection.environmentId,
|
|
5394
5518
|
streamUrl: connection.streamUrl,
|
|
5395
5519
|
stateDirectory: join8(home, "endpoint"),
|
|
5396
|
-
...options.client ? { client: options.client } : {},
|
|
5520
|
+
...options.client ?? RUNNER_CLIENT ? { client: options.client ?? RUNNER_CLIENT } : {},
|
|
5397
5521
|
// Below Canon's client floor nothing can work, wherever the refusal surfaces: a query, an operation or the stream.
|
|
5398
5522
|
...options.onFatal ? { onClientUpdateRequired: /* @__PURE__ */ __name((error) => options.onFatal(error), "onClientUpdateRequired") } : {},
|
|
5523
|
+
// The runner is the agent's home; Canon's relay is the connector home under its own installation.
|
|
5524
|
+
claimHome: true,
|
|
5525
|
+
...options.takeOver ? { takeOver: true } : {},
|
|
5399
5526
|
...options.installationId ? { installationId: options.installationId } : {},
|
|
5400
5527
|
...mls ? { mlsDev: { configuration: mls.configuration, custody: mls.custody } } : {},
|
|
5401
5528
|
...options.endpoint ? { endpoint: options.endpoint } : {},
|
|
@@ -5410,6 +5537,7 @@ ${JSON.stringify(result, null, 2)}`);
|
|
|
5410
5537
|
}
|
|
5411
5538
|
agentId = identity.principalId;
|
|
5412
5539
|
ownershipVersion = identity.ownershipVersion;
|
|
5540
|
+
installationId = identity.installationId;
|
|
5413
5541
|
if (options.wake !== void 0) await client.configureWake(options.wake);
|
|
5414
5542
|
mlsOn = !!(endpoint.mlsDevDevice && endpoint.mlsDevGroups);
|
|
5415
5543
|
if (!mlsOn && (await endpoint.capabilities().catch(() => null))?.mls?.roomMode === "default") {
|
|
@@ -5488,6 +5616,7 @@ ${JSON.stringify(result, null, 2)}`);
|
|
|
5488
5616
|
families: ["messages", "contacts", "runtime_turn"],
|
|
5489
5617
|
onConnected: /* @__PURE__ */ __name(() => {
|
|
5490
5618
|
heartbeat?.connect();
|
|
5619
|
+
if (mlsOn) void noteAdmissions().catch(warn("room admission"));
|
|
5491
5620
|
if (reconnect) {
|
|
5492
5621
|
for (const target of rooms.values()) if (target.turn) scheduleControl(target);
|
|
5493
5622
|
}
|
|
@@ -5761,6 +5890,7 @@ export {
|
|
|
5761
5890
|
readRelayCredential,
|
|
5762
5891
|
writeRelayCredential,
|
|
5763
5892
|
relayRunnerHome,
|
|
5893
|
+
RUNNER_CLIENT,
|
|
5764
5894
|
runtimeDescriptor,
|
|
5765
5895
|
createRunner,
|
|
5766
5896
|
parseWakeConfiguration,
|