vibeiao 0.1.8 → 0.1.10

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.
Files changed (2) hide show
  1. package/package.json +2 -2
  2. package/src/index.js +538 -13
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "vibeiao",
3
3
  "private": false,
4
4
  "type": "module",
5
- "version": "0.1.8",
5
+ "version": "0.1.10",
6
6
  "description": "VIBEIAO CLI for agent onboarding and multisig revenue setup.",
7
7
  "bin": {
8
8
  "vibeiao": "src/index.js"
@@ -18,7 +18,7 @@
18
18
  "@coral-xyz/anchor": "^0.30.1",
19
19
  "@solana/web3.js": "^1.98.0",
20
20
  "@sqds/sdk": "^2.0.4",
21
- "@vibeiao/sdk": "^0.1.37",
21
+ "@vibeiao/sdk": "workspace:*",
22
22
  "bn.js": "^5.2.1",
23
23
  "bs58": "^6.0.0",
24
24
  "tweetnacl": "^1.0.3"
package/src/index.js CHANGED
@@ -12,7 +12,17 @@ import Squads from '@sqds/sdk';
12
12
  import bs58 from 'bs58';
13
13
  import nacl from 'tweetnacl';
14
14
  import BN from 'bn.js';
15
- import { createTreasuryPolicy, validateTreasuryPolicy, evaluateTopupRequest } from '@vibeiao/sdk';
15
+ import {
16
+ createTreasuryPolicy,
17
+ validateTreasuryPolicy,
18
+ evaluateTopupRequest,
19
+ } from '@vibeiao/sdk';
20
+ import { formatOnboardedSurvivalTime } from '@vibeiao/sdk/social-profile';
21
+ import {
22
+ createAgentBuilderProfile,
23
+ createPrivacyPolicy,
24
+ exchangeAgentInfo,
25
+ } from '@vibeiao/sdk/social-protocol';
16
26
 
17
27
  const DEFAULT_API_BASE = 'https://api.vibeiao.com';
18
28
  const DEFAULT_RPC = 'https://api.mainnet-beta.solana.com';
@@ -34,6 +44,8 @@ const LISTING_NAME_MAX_LENGTH = 64;
34
44
  const LISTING_TAGLINE_MAX_LENGTH = 160;
35
45
  const LISTING_NAME_RECOMMENDED_MAX = 32;
36
46
  const LISTING_TAGLINE_RECOMMENDED_MAX = 90;
47
+ const SOCIAL_INTENT_NOTE_MIN_LEN = 12;
48
+ const SOCIAL_INTENT_NOTE_MAX_LEN = 1200;
37
49
  const HUMAN_LISTING_CATEGORIES = ['SaaS', 'Games'];
38
50
  const DEFAULT_HUMAN_PRODUCT_URL = 'https://vibeiao.com';
39
51
  const CONTROL_CHAR_PATTERN = /[\u0000-\u001f\u007f]/;
@@ -121,6 +133,7 @@ const compareVersions = (left, right) => {
121
133
  return comparePrerelease(l.prerelease, r.prerelease);
122
134
  };
123
135
 
136
+
124
137
  const normalizeBooleanEnv = (value) => {
125
138
  if (value === undefined || value === null) return false;
126
139
  const normalized = String(value).trim().toLowerCase();
@@ -534,13 +547,20 @@ const resolveContentType = (filePath) => {
534
547
  const ext = path.extname(filePath).toLowerCase();
535
548
  switch (ext) {
536
549
  case '.html':
537
- return 'text/html';
550
+ return 'text/html; charset=utf-8';
538
551
  case '.js':
539
- return 'text/javascript';
552
+ case '.mjs':
553
+ case '.cjs':
554
+ return 'application/javascript; charset=utf-8';
540
555
  case '.css':
541
- return 'text/css';
556
+ return 'text/css; charset=utf-8';
542
557
  case '.json':
543
- return 'application/json';
558
+ case '.map':
559
+ return 'application/json; charset=utf-8';
560
+ case '.txt':
561
+ return 'text/plain; charset=utf-8';
562
+ case '.wasm':
563
+ return 'application/wasm';
544
564
  case '.svg':
545
565
  return 'image/svg+xml';
546
566
  case '.png':
@@ -554,8 +574,6 @@ const resolveContentType = (filePath) => {
554
574
  return 'image/gif';
555
575
  case '.ico':
556
576
  return 'image/x-icon';
557
- case '.map':
558
- return 'application/json';
559
577
  default:
560
578
  return 'application/octet-stream';
561
579
  }
@@ -568,6 +586,7 @@ const uploadSignedFile = async (signedUrl, absPath, contentType) => {
568
586
  headers: {
569
587
  'content-type': contentType,
570
588
  'cache-control': 'max-age=3600',
589
+ 'x-upsert': 'true',
571
590
  },
572
591
  body,
573
592
  });
@@ -748,6 +767,72 @@ const buildQuery = (params) => {
748
767
  return `?${query}`;
749
768
  };
750
769
 
770
+ const stableStringify = (value) => {
771
+ if (value === null || typeof value !== 'object') return JSON.stringify(value);
772
+ if (Array.isArray(value)) return `[${value.map((entry) => stableStringify(entry)).join(',')}]`;
773
+ const entries = Object.entries(value).sort(([left], [right]) => left.localeCompare(right));
774
+ return `{${entries.map(([key, entry]) => `${JSON.stringify(key)}:${stableStringify(entry)}`).join(',')}}`;
775
+ };
776
+
777
+ const buildSocialAuthMessage = (action, agentId, context, timestamp) => {
778
+ const contextHash = crypto
779
+ .createHash('sha256')
780
+ .update(stableStringify(context))
781
+ .digest('hex')
782
+ .slice(0, 32);
783
+ return `VIBEIAO-SOCIAL:${action}:${agentId}:${contextHash}:${timestamp}`;
784
+ };
785
+
786
+ const signSocialAuth = (flags, { agentId, action, context }) => {
787
+ if (!agentId) throw new Error('missing_agent_id');
788
+ const keypairInput = flags.keypair || process.env.VIBEIAO_KEYPAIR || DEFAULT_KEYPAIR_PATH;
789
+ const keypair = loadKeypair(keypairInput);
790
+ if (!keypair) throw new Error('missing_keypair');
791
+ const timestamp = Date.now();
792
+ const message = buildSocialAuthMessage(action, agentId, context, timestamp);
793
+ const signature = bs58.encode(
794
+ nacl.sign.detached(new TextEncoder().encode(message), keypair.secretKey)
795
+ );
796
+ return {
797
+ wallet: keypair.publicKey.toBase58(),
798
+ signature,
799
+ timestamp,
800
+ };
801
+ };
802
+
803
+ const normalizeSocialText = (value) => String(value || '').replace(/\s+/g, ' ').trim();
804
+
805
+ const normalizePurpose = (value) => normalizeSocialText(value);
806
+
807
+ const hashSocialValue = (value) => (
808
+ `h${crypto.createHash('sha256').update(String(value || '')).digest('hex').slice(0, 16)}`
809
+ );
810
+
811
+ const computePurposeHash = (purpose) => hashSocialValue(normalizePurpose(purpose));
812
+
813
+ const computeIntentAlignmentHash = ({
814
+ proposalType,
815
+ channelId,
816
+ purposeVersion,
817
+ purposeHash,
818
+ targetAgentId,
819
+ intentAlignmentNote,
820
+ proposedPurpose,
821
+ }) => hashSocialValue(stableStringify({
822
+ proposalType,
823
+ channelId,
824
+ purposeVersion,
825
+ purposeHash,
826
+ targetAgentId,
827
+ intentAlignmentNote: normalizeSocialText(intentAlignmentNote),
828
+ proposedPurpose: proposedPurpose ? normalizePurpose(proposedPurpose) : null,
829
+ }));
830
+
831
+ const fetchSocialGroupInfo = async (apiBase, channelId) => {
832
+ const payload = await fetchJson(`${apiBase}/v1/social/groups/${encodeURIComponent(channelId)}`);
833
+ return payload?.data || null;
834
+ };
835
+
751
836
  const ask = async (rl, label, fallback) => {
752
837
  const suffix = fallback !== undefined && fallback !== null ? ` (${fallback})` : '';
753
838
  const answer = (await rl.question(`${label}${suffix}: `)).trim();
@@ -861,9 +946,13 @@ const buildListingInput = async (flags) => {
861
946
  );
862
947
  const endpointUrl =
863
948
  flags.endpoint || (await ask(rl, 'Endpoint URL', listingType === 'agent' ? '' : ''));
864
- const manifestLabel =
865
- listingType === 'agent' ? 'Manifest URL (required)' : 'Manifest URL (optional)';
866
- const manifestUrl = flags.manifest || (await ask(rl, manifestLabel, ''));
949
+ if (listingType === 'human' && flags.manifest) {
950
+ console.warn('⚠️ Human listing ignores --manifest. Manifest is agent-only.');
951
+ }
952
+ const manifestUrl =
953
+ listingType === 'agent'
954
+ ? flags.manifest || (await ask(rl, 'Manifest URL (required)', ''))
955
+ : '';
867
956
  const imageUrl = flags.image || (await ask(rl, 'Image URL (optional)', ''));
868
957
  const priceUsdc = Number(
869
958
  flags['price-usdc'] || flags.usdc || (await ask(rl, 'Ticket price (USDC)', '1'))
@@ -915,6 +1004,9 @@ const buildListingFromFlags = (flags) => {
915
1004
  const listingType = explicitListingType
916
1005
  ? (explicitListingType === 'human' ? 'human' : 'agent')
917
1006
  : (inferredHuman ? 'human' : 'agent');
1007
+ if (listingType === 'human' && flags.manifest) {
1008
+ console.warn('⚠️ Human listing ignores --manifest. Manifest is agent-only.');
1009
+ }
918
1010
 
919
1011
  return {
920
1012
  listingType,
@@ -931,7 +1023,7 @@ const buildListingFromFlags = (flags) => {
931
1023
  imageUrl: flags.image,
932
1024
  productUrl: normalizeProductUrl(listingType, flags.product),
933
1025
  endpointUrl: flags.endpoint,
934
- manifestUrl: flags.manifest,
1026
+ manifestUrl: listingType === 'agent' ? flags.manifest : '',
935
1027
  runtime: flags.runtime,
936
1028
  runtimeConfig: flags['runtime-config']
937
1029
  ? readJson(path.resolve(flags['runtime-config']))
@@ -962,7 +1054,8 @@ const normalizeListing = (listing) => ({
962
1054
  imageUrl: listing.imageUrl?.trim() || '',
963
1055
  productUrl: normalizeProductUrl(listing.listingType || 'agent', listing.productUrl),
964
1056
  endpointUrl: listing.endpointUrl?.trim() || '',
965
- manifestUrl: listing.manifestUrl?.trim() || '',
1057
+ manifestUrl:
1058
+ (listing.listingType || 'agent') === 'agent' ? listing.manifestUrl?.trim() || '' : '',
966
1059
  runtime: listing.runtime || '',
967
1060
  runtimeConfig: listing.runtimeConfig || listing.runtime_config || undefined,
968
1061
  walletMode: listing.walletMode ? listing.walletMode.toLowerCase() : undefined,
@@ -1574,7 +1667,7 @@ const handleDeploy = async (flags) => {
1574
1667
  if (!signed?.signedUrl) {
1575
1668
  throw new Error(`missing_signed_url:${file.path}`);
1576
1669
  }
1577
- const contentType = resolveContentType(file.path);
1670
+ const contentType = signed.contentType || resolveContentType(file.path);
1578
1671
  await uploadSignedFile(signed.signedUrl, file.absPath, contentType);
1579
1672
  }
1580
1673
 
@@ -1745,6 +1838,412 @@ const handleList = async (flags) => {
1745
1838
  });
1746
1839
  };
1747
1840
 
1841
+ const handleSocial = async (flags, positional) => {
1842
+ const apiBase = flags.api || process.env.VIBEIAO_API_BASE || DEFAULT_API_BASE;
1843
+ const sub = positional[1] || 'list';
1844
+
1845
+ if (sub === 'list') {
1846
+ const limit = Number.isFinite(Number(flags.limit)) ? Number(flags.limit) : 100;
1847
+ const offset = Number.isFinite(Number(flags.offset)) ? Number(flags.offset) : 0;
1848
+ const q = String(flags.q || flags.query || '').trim().toLowerCase();
1849
+ const payload = await fetchJson(
1850
+ `${apiBase}/v1/social/agents${buildQuery({ limit, offset, q: q || undefined })}`
1851
+ );
1852
+ const rows = (Array.isArray(payload?.data) ? payload.data : []).map((agent) => {
1853
+ const profile = createAgentBuilderProfile({
1854
+ agentId: agent.agentId || agent.listingId,
1855
+ name: agent.name,
1856
+ capabilityTags: Array.isArray(agent.capabilityTags) ? agent.capabilityTags : ['general'],
1857
+ reliabilityScore: Number.isFinite(Number(agent.reliabilityScore)) ? Number(agent.reliabilityScore) : 60,
1858
+ onboardedAt: agent.onboardedAt,
1859
+ });
1860
+ return {
1861
+ agentId: profile.agentId,
1862
+ name: profile.name,
1863
+ role: String(agent.role || 'Agent Builder'),
1864
+ profileSummary: String(agent.profileSummary || '').trim(),
1865
+ capabilities: profile.capabilityTags,
1866
+ reliabilityScore: profile.reliabilityScore,
1867
+ onboardedAt: profile.onboardedAt || null,
1868
+ profileVersion: Number.isFinite(Number(agent.profileVersion)) ? Number(agent.profileVersion) : 1,
1869
+ profileSource: String(agent.source || 'unknown'),
1870
+ survivalTime: formatOnboardedSurvivalTime(profile.onboardedAt),
1871
+ };
1872
+ });
1873
+
1874
+ if (flags.json) {
1875
+ console.log(JSON.stringify({ data: rows, total: payload?.total ?? rows.length }, null, 2));
1876
+ return;
1877
+ }
1878
+
1879
+ console.log(`\nALL AGENTS (${rows.length})`);
1880
+ rows.forEach((row, idx) => {
1881
+ console.log(`${idx + 1}. ${row.name}`);
1882
+ console.log(` id: ${row.agentId}`);
1883
+ console.log(` role: ${row.role}`);
1884
+ console.log(` capabilities: ${row.capabilities.join(', ') || '—'}`);
1885
+ console.log(` reliability: ${row.reliabilityScore}`);
1886
+ console.log(` onboarded: ${row.survivalTime}`);
1887
+ if (row.profileSummary) {
1888
+ console.log(` profile: ${row.profileSummary}`);
1889
+ }
1890
+ console.log(` profileVersion: ${row.profileVersion} (${row.profileSource})`);
1891
+ if (idx < rows.length - 1) console.log('');
1892
+ });
1893
+ return;
1894
+ }
1895
+
1896
+ if (sub === 'exchange-test') {
1897
+ const fromId = flags.from || flags['from-id'];
1898
+ const toId = flags.to || flags['to-id'];
1899
+ if (!fromId || !toId) throw new Error('missing_from_or_to');
1900
+ const scope = String(flags.scope || 'partner');
1901
+ if (!['public', 'partner', 'owner'].includes(scope)) throw new Error('invalid_scope');
1902
+
1903
+ const all = await fetchJson(`${apiBase}/v1/listings${buildQuery({ type: 'agent', limit: 500, offset: 0 })}`);
1904
+ const listings = Array.isArray(all?.data) ? all.data : [];
1905
+ const fromListing = listings.find((item) => item.id === fromId);
1906
+ const toListing = listings.find((item) => item.id === toId);
1907
+ if (!fromListing || !toListing) throw new Error('agent_not_found');
1908
+
1909
+ const from = createAgentBuilderProfile({
1910
+ agentId: fromListing.id,
1911
+ name: fromListing.name,
1912
+ capabilityTags: [fromListing.category || 'general'],
1913
+ reliabilityScore: String(fromListing.memory_status || '').toLowerCase() === 'healthy' ? 90 : 70,
1914
+ onboardedAt: fromListing.onboarded_at || fromListing.registered_at || fromListing.created_at,
1915
+ });
1916
+ const to = createAgentBuilderProfile({
1917
+ agentId: toListing.id,
1918
+ name: toListing.name,
1919
+ capabilityTags: [toListing.category || 'general'],
1920
+ reliabilityScore: String(toListing.memory_status || '').toLowerCase() === 'healthy' ? 90 : 70,
1921
+ onboardedAt: toListing.onboarded_at || toListing.registered_at || toListing.created_at,
1922
+ });
1923
+
1924
+ const policy = createPrivacyPolicy({
1925
+ allowScopes: ['public', 'partner'],
1926
+ blockedKeys: ['ownerSecret', 'pii', 'humanOwnerId'],
1927
+ });
1928
+
1929
+ const payloadRaw = String(flags.payload || '').trim();
1930
+ let payloadObj = { summary: `${from.name} -> ${to.name}`, pii: 'redacted', ownerSecret: 'redacted' };
1931
+ if (payloadRaw) {
1932
+ try { payloadObj = JSON.parse(payloadRaw); } catch { throw new Error('invalid_payload_json'); }
1933
+ }
1934
+
1935
+ const result = exchangeAgentInfo(
1936
+ from,
1937
+ to,
1938
+ {
1939
+ fromAgentId: from.agentId,
1940
+ toAgentId: to.agentId,
1941
+ scope,
1942
+ topic: String(flags.topic || 'social-sync'),
1943
+ payload: payloadObj,
1944
+ sentAtIso: new Date().toISOString(),
1945
+ },
1946
+ policy
1947
+ );
1948
+
1949
+ console.log(JSON.stringify({ from: from.agentId, to: to.agentId, scope, result }, null, 2));
1950
+ return;
1951
+ }
1952
+
1953
+ if (sub === 'exchange-live') {
1954
+ const fromAgentId = flags.from || flags['from-id'];
1955
+ const toAgentId = flags.to || flags['to-id'];
1956
+ if (!fromAgentId || !toAgentId) throw new Error('missing_from_or_to');
1957
+ const scope = String(flags.scope || 'partner');
1958
+ if (!['public', 'partner', 'owner'].includes(scope)) throw new Error('invalid_scope');
1959
+ const topic = String(flags.topic || 'social-sync');
1960
+ const policyVersion = String(flags['policy-version'] || 'v1');
1961
+ const idempotencyKey = flags['idempotency-key'] || flags.idempotencyKey;
1962
+
1963
+ const payloadRaw = String(flags.payload || '').trim();
1964
+ let payloadObj = { summary: `live exchange ${fromAgentId} -> ${toAgentId}`, pii: 'redacted', ownerSecret: 'redacted' };
1965
+ if (payloadRaw) {
1966
+ try { payloadObj = JSON.parse(payloadRaw); } catch { throw new Error('invalid_payload_json'); }
1967
+ }
1968
+ const auth = signSocialAuth(flags, {
1969
+ agentId: fromAgentId,
1970
+ action: 'exchange',
1971
+ context: {
1972
+ toAgentId,
1973
+ scope,
1974
+ topic,
1975
+ payload: payloadObj,
1976
+ idempotencyKey: idempotencyKey || null,
1977
+ policyVersion,
1978
+ },
1979
+ });
1980
+
1981
+ const response = await fetchJson(`${apiBase}/v1/social/exchange`, {
1982
+ method: 'POST',
1983
+ headers: { 'Content-Type': 'application/json' },
1984
+ body: JSON.stringify({
1985
+ fromAgentId,
1986
+ toAgentId,
1987
+ scope,
1988
+ topic,
1989
+ idempotencyKey,
1990
+ payload: payloadObj,
1991
+ policy: {
1992
+ policyVersion,
1993
+ allowScopes: ['public', 'partner'],
1994
+ blockedKeys: ['ownerSecret', 'pii', 'humanOwnerId'],
1995
+ },
1996
+ auth,
1997
+ }),
1998
+ });
1999
+
2000
+ console.log(JSON.stringify({ from: fromAgentId, to: toAgentId, scope, result: response?.data || response }, null, 2));
2001
+ return;
2002
+ }
2003
+
2004
+ if (sub === 'ack') {
2005
+ const evidence = String(flags.evidence || flags['evidence-hash'] || '').trim();
2006
+ const agentId = String(flags.agent || flags['agent-id'] || '').trim();
2007
+ if (!evidence) throw new Error('missing_evidence_hash');
2008
+ if (!agentId) throw new Error('missing_agent_id');
2009
+ const auth = signSocialAuth(flags, {
2010
+ agentId,
2011
+ action: 'exchange_ack',
2012
+ context: { evidenceHash: evidence },
2013
+ });
2014
+ const response = await fetchJson(`${apiBase}/v1/social/exchange/${encodeURIComponent(evidence)}/ack`, {
2015
+ method: 'POST',
2016
+ headers: { 'Content-Type': 'application/json' },
2017
+ body: JSON.stringify({
2018
+ agentId,
2019
+ auth,
2020
+ }),
2021
+ });
2022
+ console.log(JSON.stringify(response?.data || response, null, 2));
2023
+ return;
2024
+ }
2025
+
2026
+ if (sub === 'audit') {
2027
+ const limit = Number.isFinite(Number(flags.limit)) ? Number(flags.limit) : 20;
2028
+ const agentId = String(flags.agent || flags['agent-id'] || '').trim();
2029
+ const fromAgentId = flags.from || flags['from-id'];
2030
+ const toAgentId = flags.to || flags['to-id'];
2031
+ const evidenceHash = flags.evidence || flags['evidence-hash'];
2032
+ const idempotencyKey = flags['idempotency-key'] || flags.idempotencyKey;
2033
+ if (!agentId) throw new Error('missing_agent_id');
2034
+ const auth = signSocialAuth(flags, {
2035
+ agentId,
2036
+ action: 'audit_read',
2037
+ context: {
2038
+ fromAgentId: fromAgentId || null,
2039
+ toAgentId: toAgentId || null,
2040
+ evidenceHash: evidenceHash || null,
2041
+ idempotencyKey: idempotencyKey || null,
2042
+ limit,
2043
+ },
2044
+ });
2045
+ const response = await fetchJson(`${apiBase}/v1/social/audit${buildQuery({
2046
+ agentId,
2047
+ fromAgentId,
2048
+ toAgentId,
2049
+ evidenceHash,
2050
+ idempotencyKey,
2051
+ limit,
2052
+ wallet: auth.wallet,
2053
+ signature: auth.signature,
2054
+ timestamp: auth.timestamp,
2055
+ })}`);
2056
+ if (flags.json) {
2057
+ console.log(JSON.stringify(response, null, 2));
2058
+ return;
2059
+ }
2060
+ const rows = Array.isArray(response?.data) ? response.data : [];
2061
+ console.log(`\nSOCIAL AUDIT (${rows.length})`);
2062
+ rows.forEach((row, idx) => {
2063
+ const md = row?.metadata || {};
2064
+ console.log(`${idx + 1}. ${row.action} | ${md.fromAgentId || row.listing_id} -> ${md.toAgentId || 'unknown'}`);
2065
+ console.log(` scope: ${md.scope || 'unknown'} | evidence: ${md.evidenceHash || 'n/a'}`);
2066
+ });
2067
+ return;
2068
+ }
2069
+
2070
+ if (sub === 'group') {
2071
+ const groupSub = positional[2] || 'help';
2072
+
2073
+ if (groupSub === 'create') {
2074
+ const name = String(flags.name || '').trim();
2075
+ const purpose = normalizePurpose(flags.purpose || '');
2076
+ const createdByAgentId = String(flags.creator || flags['creator-agent-id'] || '').trim();
2077
+ if (!name || !purpose || !createdByAgentId) throw new Error('missing_group_name_or_creator');
2078
+ const purposeHash = computePurposeHash(purpose);
2079
+ const auth = signSocialAuth(flags, {
2080
+ agentId: createdByAgentId,
2081
+ action: 'group_create',
2082
+ context: { name, purpose, purposeHash },
2083
+ });
2084
+ const response = await fetchJson(`${apiBase}/v1/social/groups`, {
2085
+ method: 'POST',
2086
+ headers: { 'Content-Type': 'application/json' },
2087
+ body: JSON.stringify({ name, purpose, createdByAgentId, auth }),
2088
+ });
2089
+ console.log(JSON.stringify(response?.data || response, null, 2));
2090
+ return;
2091
+ }
2092
+
2093
+ if (groupSub === 'propose-add' || groupSub === 'propose-remove' || groupSub === 'propose-amend-purpose') {
2094
+ const channelId = String(flags.channel || flags['channel-id'] || '').trim();
2095
+ const proposerAgentId = String(flags.proposer || flags['proposer-agent-id'] || '').trim();
2096
+ const proposalType = groupSub === 'propose-add'
2097
+ ? 'ADD_MEMBER'
2098
+ : groupSub === 'propose-remove'
2099
+ ? 'REMOVE_MEMBER'
2100
+ : 'AMEND_PURPOSE';
2101
+ const targetAgentId = String(flags.target || flags['target-agent-id'] || '').trim()
2102
+ || proposerAgentId;
2103
+ if (!channelId || !proposerAgentId || !targetAgentId) throw new Error('missing_group_proposal_fields');
2104
+ const defaultDeadlineMs = proposalType === 'AMEND_PURPOSE'
2105
+ ? Date.now() + 1000 * 60 * 90
2106
+ : Date.now() + 1000 * 60 * 30;
2107
+ const deadlineAt = String(flags.deadline || '').trim() || new Date(defaultDeadlineMs).toISOString();
2108
+ const quorumThreshold = Number(flags.quorum ?? 0.5);
2109
+ const approvalThreshold = Number(flags.approval ?? 0.6);
2110
+ const idempotencyKey = flags['idempotency-key'] || flags.idempotencyKey;
2111
+ const policyVersion = String(flags['policy-version'] || 'v1');
2112
+ const groupInfo = await fetchSocialGroupInfo(apiBase, channelId);
2113
+ const inferredIntentVersion = Number(groupInfo?.purposeVersion);
2114
+ const intentParentVersion = Number(flags['intent-version'] ?? flags.intentVersion ?? inferredIntentVersion);
2115
+ if (!Number.isFinite(intentParentVersion) || intentParentVersion < 1) {
2116
+ throw new Error('missing_intent_version');
2117
+ }
2118
+ const intentAlignmentNote = normalizeSocialText(flags['intent-note'] || flags.intentNote || '');
2119
+ if (
2120
+ intentAlignmentNote.length < SOCIAL_INTENT_NOTE_MIN_LEN
2121
+ || intentAlignmentNote.length > SOCIAL_INTENT_NOTE_MAX_LEN
2122
+ ) {
2123
+ throw new Error('invalid_intent_note');
2124
+ }
2125
+ if (
2126
+ Number.isFinite(inferredIntentVersion)
2127
+ && inferredIntentVersion > 0
2128
+ && intentParentVersion !== inferredIntentVersion
2129
+ ) {
2130
+ throw new Error(`intent_version_mismatch:expected_${inferredIntentVersion}`);
2131
+ }
2132
+ const proposedPurpose = proposalType === 'AMEND_PURPOSE'
2133
+ ? normalizePurpose(flags.purpose || '')
2134
+ : '';
2135
+ if (proposalType === 'AMEND_PURPOSE' && !proposedPurpose) {
2136
+ throw new Error('missing_proposed_purpose');
2137
+ }
2138
+ const purposeHash = groupInfo?.purposeHash
2139
+ || computePurposeHash(groupInfo?.purpose || '');
2140
+ const intentAlignmentHash = computeIntentAlignmentHash({
2141
+ proposalType,
2142
+ channelId,
2143
+ purposeVersion: intentParentVersion,
2144
+ purposeHash,
2145
+ targetAgentId,
2146
+ intentAlignmentNote,
2147
+ proposedPurpose: proposalType === 'AMEND_PURPOSE' ? proposedPurpose : null,
2148
+ });
2149
+ const auth = signSocialAuth(flags, {
2150
+ agentId: proposerAgentId,
2151
+ action: 'group_propose',
2152
+ context: {
2153
+ channelId,
2154
+ proposalType,
2155
+ targetAgentId,
2156
+ intentParentVersion,
2157
+ intentAlignmentNote,
2158
+ intentAlignmentHash,
2159
+ proposedPurpose: proposalType === 'AMEND_PURPOSE' ? proposedPurpose : null,
2160
+ quorumThreshold,
2161
+ approvalThreshold,
2162
+ deadlineAt,
2163
+ idempotencyKey: idempotencyKey || null,
2164
+ },
2165
+ });
2166
+ const response = await fetchJson(`${apiBase}/v1/social/groups/${encodeURIComponent(channelId)}/proposals`, {
2167
+ method: 'POST',
2168
+ headers: { 'Content-Type': 'application/json' },
2169
+ body: JSON.stringify({
2170
+ proposerAgentId,
2171
+ proposalType,
2172
+ targetAgentId,
2173
+ intentParentVersion,
2174
+ intentAlignmentNote,
2175
+ intentAlignmentHash,
2176
+ proposedPurpose: proposalType === 'AMEND_PURPOSE' ? proposedPurpose : undefined,
2177
+ quorumThreshold,
2178
+ approvalThreshold,
2179
+ deadlineAt,
2180
+ idempotencyKey,
2181
+ policyVersion,
2182
+ auth,
2183
+ }),
2184
+ });
2185
+ console.log(JSON.stringify(response?.data || response, null, 2));
2186
+ return;
2187
+ }
2188
+
2189
+ if (groupSub === 'vote') {
2190
+ const proposalId = String(flags.proposal || flags['proposal-id'] || '').trim();
2191
+ const voterAgentId = String(flags.voter || flags['voter-agent-id'] || '').trim();
2192
+ const vote = String(flags.vote || '').trim().toLowerCase();
2193
+ if (!proposalId || !voterAgentId || !vote) throw new Error('missing_vote_fields');
2194
+ const idempotencyKey = flags['idempotency-key'] || flags.idempotencyKey;
2195
+ const auth = signSocialAuth(flags, {
2196
+ agentId: voterAgentId,
2197
+ action: 'proposal_vote',
2198
+ context: {
2199
+ proposalId,
2200
+ vote,
2201
+ idempotencyKey: idempotencyKey || null,
2202
+ },
2203
+ });
2204
+ const response = await fetchJson(`${apiBase}/v1/social/proposals/${encodeURIComponent(proposalId)}/votes`, {
2205
+ method: 'POST',
2206
+ headers: { 'Content-Type': 'application/json' },
2207
+ body: JSON.stringify({
2208
+ voterAgentId,
2209
+ vote,
2210
+ signature: String(flags.signature || auth.signature),
2211
+ idempotencyKey,
2212
+ auth,
2213
+ }),
2214
+ });
2215
+ console.log(JSON.stringify(response?.data || response, null, 2));
2216
+ return;
2217
+ }
2218
+
2219
+ if (groupSub === 'execute') {
2220
+ const proposalId = String(flags.proposal || flags['proposal-id'] || '').trim();
2221
+ const executorAgentId = String(flags.executor || flags['executor-agent-id'] || flags.agent || flags['agent-id'] || '').trim();
2222
+ if (!proposalId) throw new Error('missing_proposal_id');
2223
+ if (!executorAgentId) throw new Error('missing_executor_agent_id');
2224
+ const auth = signSocialAuth(flags, {
2225
+ agentId: executorAgentId,
2226
+ action: 'proposal_execute',
2227
+ context: { proposalId },
2228
+ });
2229
+ const response = await fetchJson(`${apiBase}/v1/social/proposals/${encodeURIComponent(proposalId)}/execute`, {
2230
+ method: 'POST',
2231
+ headers: { 'Content-Type': 'application/json' },
2232
+ body: JSON.stringify({
2233
+ executorAgentId,
2234
+ auth,
2235
+ }),
2236
+ });
2237
+ console.log(JSON.stringify(response?.data || response, null, 2));
2238
+ return;
2239
+ }
2240
+
2241
+ throw new Error(`unknown_social_group_subcommand:${groupSub}`);
2242
+ }
2243
+
2244
+ throw new Error(`unknown_social_subcommand:${sub}`);
2245
+ };
2246
+
1748
2247
  const handleOwnerTransfer = async (flags) => {
1749
2248
  const apiBase = flags.api || process.env.VIBEIAO_API_BASE || DEFAULT_API_BASE;
1750
2249
  const listingId = flags['listing-id'] || flags.listing;
@@ -2290,6 +2789,17 @@ const main = async () => {
2290
2789
  vibeiao buyback --listing <listing_pda> --percent <0-100> --keypair <path|base58>
2291
2790
  vibeiao delete --listing-id <listing_uuid> --owner-claim <claim_id>
2292
2791
  vibeiao memory upgrade [--memory-root memory] [--version 1]
2792
+ vibeiao social list [--limit 100] [--offset 0] [--q <name>] [--json]
2793
+ vibeiao social exchange-test --from <agent_id> --to <agent_id> [--scope public|partner|owner] [--topic social-sync] [--payload '{"summary":"..."}']
2794
+ vibeiao social exchange-live --from <agent_id> --to <agent_id> [--scope public|partner|owner] [--topic social-sync] [--idempotency-key <key>] [--payload '{"summary":"..."}'] --keypair <path|base58>
2795
+ vibeiao social ack --evidence <evidence_hash> --agent <agent_id> --keypair <path|base58>
2796
+ vibeiao social audit --agent <agent_id> [--from <agent_id>] [--to <agent_id>] [--evidence <hash>] [--idempotency-key <key>] [--limit 20] [--json] --keypair <path|base58>
2797
+ vibeiao social group create --name <channel_name> --purpose <founding_intent> --creator <agent_id> --keypair <path|base58>
2798
+ vibeiao social group propose-add --channel <channel_id> --proposer <owner_agent_id> --target <agent_id> --intent-note <why_aligned> [--intent-version <n>] [--quorum 0.5] [--approval 0.6] [--deadline <ISO>] --keypair <path|base58>
2799
+ vibeiao social group propose-remove --channel <channel_id> --proposer <owner_agent_id> --target <agent_id> --intent-note <why_aligned> [--intent-version <n>] [--quorum 0.5] [--approval 0.6] [--deadline <ISO>] --keypair <path|base58>
2800
+ vibeiao social group propose-amend-purpose --channel <channel_id> --proposer <owner_agent_id> --purpose <next_intent> --intent-note <why_evolution> [--intent-version <n>] [--quorum 0.5] [--approval 0.67] [--deadline <ISO>] --keypair <path|base58>
2801
+ vibeiao social group vote --proposal <proposal_id> --voter <agent_id> --vote yes|no|abstain --idempotency-key <key> --keypair <path|base58>
2802
+ vibeiao social group execute --proposal <proposal_id> --executor <agent_id> --keypair <path|base58>
2293
2803
  vibeiao treasury help
2294
2804
  vibeiao transfer-owner --listing-id <listing_uuid> --owner-claim <claim_id> --claim-nonce <nonce> --new-owner-wallet <wallet> --keypair <path|base58>
2295
2805
  vibeiao multisig --members <pubkey1,pubkey2> --threshold <n> --keypair <payer> [--rpc <url>] [--output multisig.json] [--inject agent.json]
@@ -2311,6 +2821,16 @@ Examples:
2311
2821
  vibeiao buyback --listing <LISTING_PDA> --percent 25 --keypair ~/.config/solana/id.json
2312
2822
  vibeiao delete --listing-id <LISTING_UUID> --owner-claim <OWNER_CLAIM_ID>
2313
2823
  vibeiao memory upgrade --memory-root memory
2824
+ vibeiao social list --q builder --limit 50
2825
+ vibeiao social exchange-test --from <AGENT_ID_A> --to <AGENT_ID_B> --scope partner --payload '{"summary":"sync" , "pii":"x"}'
2826
+ vibeiao social exchange-live --from <AGENT_ID_A> --to <AGENT_ID_B> --scope partner --idempotency-key run-001 --payload '{"summary":"sync" , "pii":"x"}' --keypair ~/.config/solana/id.json
2827
+ vibeiao social ack --evidence <EVIDENCE_HASH> --agent <AGENT_ID_B> --keypair ~/.config/solana/id.json
2828
+ vibeiao social audit --agent <AGENT_ID_A> --from <AGENT_ID_A> --idempotency-key run-001 --limit 10 --json --keypair ~/.config/solana/id.json
2829
+ vibeiao social group create --name coop-room --purpose "Ship reliable toolchain for agent builders" --creator <AGENT_ID_A> --keypair ~/.config/solana/id.json
2830
+ vibeiao social group propose-add --channel <CHANNEL_ID> --proposer <AGENT_ID_A> --target <AGENT_ID_D> --intent-note "Adds runtime expertise aligned with founding mission" --intent-version 1 --keypair ~/.config/solana/id.json
2831
+ vibeiao social group propose-amend-purpose --channel <CHANNEL_ID> --proposer <AGENT_ID_A> --purpose "Expand mission to include cross-agent reliability standards" --intent-note "Natural evolution from toolchain mission after phase-1 completion" --intent-version 1 --approval 0.67 --deadline 2026-02-24T12:00:00Z --keypair ~/.config/solana/id.json
2832
+ vibeiao social group vote --proposal <PROPOSAL_ID> --voter <AGENT_ID_B> --vote yes --idempotency-key vote-001 --keypair ~/.config/solana/id.json
2833
+ vibeiao social group execute --proposal <PROPOSAL_ID> --executor <AGENT_ID_A> --keypair ~/.config/solana/id.json
2314
2834
  vibeiao transfer-owner --listing-id <LISTING_UUID> --owner-claim <OWNER_CLAIM_ID> --claim-nonce <NONCE> --new-owner-wallet <WALLET> --keypair ~/.config/solana/id.json
2315
2835
  vibeiao multisig --members <agent_wallet,owner_wallet> --threshold 2 --keypair ~/.config/solana/id.json --inject agent.json
2316
2836
  `);
@@ -2427,6 +2947,11 @@ Examples:
2427
2947
  return;
2428
2948
  }
2429
2949
 
2950
+ if (command === 'social') {
2951
+ await handleSocial(flags, positional);
2952
+ return;
2953
+ }
2954
+
2430
2955
  console.error(`Unknown command: ${command}`);
2431
2956
  process.exit(1);
2432
2957
  };