@astrosheep/square 0.3.80 → 0.3.82
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/artifact.js +3 -3
- package/dist/catch-decisions.d.ts +1 -0
- package/dist/catch-decisions.js +8 -2
- package/dist/claude-mod.js +1 -2
- package/dist/cli/catch-presentation.js +3 -1
- package/dist/cli/context.js +1 -0
- package/dist/cli/express-presentation.js +2 -3
- package/dist/cli/maintenance-commands.js +2 -2
- package/dist/cli/registry.js +2 -1
- package/dist/cli/square-commands.d.ts +1 -0
- package/dist/cli/square-commands.js +7 -2
- package/dist/codex-hook.js +3 -11
- package/dist/decisions.d.ts +3 -2
- package/dist/decisions.js +3 -0
- package/dist/help.js +6 -5
- package/dist/hints.d.ts +4 -0
- package/dist/hints.js +25 -0
- package/dist/mcp.js +1 -1
- package/dist/model.d.ts +1 -1
- package/dist/participant-identity.d.ts +2 -0
- package/dist/participant-identity.js +13 -0
- package/dist/plugins/opencode/index.js +1 -3
- package/dist/presentation.d.ts +0 -1
- package/dist/presentation.js +3 -11
- package/dist/runtime.js +1 -1
- package/dist/session-lifecycle.d.ts +3 -0
- package/dist/session-lifecycle.js +21 -0
- package/dist/square-actions.d.ts +1 -0
- package/dist/square-actions.js +3 -2
- package/dist/square-api.js +7 -4
- package/dist/square-core.d.ts +5 -0
- package/dist/square-domain.d.ts +4 -0
- package/dist/square-domain.js +5 -1
- package/dist/square-facade.d.ts +6 -1
- package/dist/square-migration.d.ts +5 -7
- package/dist/square-migration.js +62 -42
- package/dist/square-sqlite.d.ts +7 -1
- package/dist/square-sqlite.js +52 -22
- package/dist/square-tools.js +2 -1
- package/dist/views.d.ts +1 -1
- package/dist/views.js +1 -1
- package/guides/claude-native.md +3 -3
- package/guides/migration.md +54 -57
- package/guides/row-storage-design.md +4 -1
- package/guides/square-api.md +26 -2
- package/package.json +2 -1
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/plugins/claude/skills/square/SKILL.md +6 -7
- package/plugins/codex/.codex-plugin/plugin.json +1 -1
- package/plugins/opencode/index.ts +1 -2
- package/plugins/pi/square-pi.js +4 -22
- package/skills/square/SKILL.md +6 -7
- package/dist/automatic-session.d.ts +0 -7
- package/dist/automatic-session.js +0 -137
package/dist/artifact.js
CHANGED
|
@@ -9,7 +9,7 @@ import { observeFileVersion } from './file-changes.js';
|
|
|
9
9
|
import { canonicalPath } from './canonical-path.js';
|
|
10
10
|
import { InternalSquareError, SquareError } from './model.js';
|
|
11
11
|
import { wrapSquareWrite } from './square-domain.js';
|
|
12
|
-
import { APPLICATION_ID, createSqliteDriver, initializeSqlite, exportSqliteState, inspectSqlite, invalidArtifact, validateSqliteHeader, emptyRuntimeState } from './square-sqlite.js';
|
|
12
|
+
import { APPLICATION_ID, CURRENT_USER_VERSION, createSqliteDriver, initializeSqlite, exportSqliteState, inspectSqlite, invalidArtifact, validateSqliteHeader, emptyRuntimeState } from './square-sqlite.js';
|
|
13
13
|
const guideContents = new Map();
|
|
14
14
|
function readGuide(name) {
|
|
15
15
|
if (!['participant', 'architect', 'brainstorm'].includes(name))
|
|
@@ -107,7 +107,7 @@ export function createArtifactPort(location, initial, externalSignal) {
|
|
|
107
107
|
database = new DatabaseSync(':memory:');
|
|
108
108
|
try {
|
|
109
109
|
initializeSqlite(database, initial);
|
|
110
|
-
database.exec(`PRAGMA application_id=${APPLICATION_ID}; PRAGMA user_version
|
|
110
|
+
database.exec(`PRAGMA application_id=${APPLICATION_ID}; PRAGMA user_version=${CURRENT_USER_VERSION}`);
|
|
111
111
|
}
|
|
112
112
|
catch (error) {
|
|
113
113
|
database.close();
|
|
@@ -270,7 +270,7 @@ async function initializeFile(location, state, signal) {
|
|
|
270
270
|
db.exec('PRAGMA journal_mode=DELETE; PRAGMA synchronous=FULL');
|
|
271
271
|
await inSqliteTransaction(db, true, () => { }, () => {
|
|
272
272
|
initializeSqlite(db, state);
|
|
273
|
-
db.exec(`PRAGMA application_id=${APPLICATION_ID}; PRAGMA user_version
|
|
273
|
+
db.exec(`PRAGMA application_id=${APPLICATION_ID}; PRAGMA user_version=${CURRENT_USER_VERSION}`);
|
|
274
274
|
}, signal);
|
|
275
275
|
}
|
|
276
276
|
finally {
|
|
@@ -14,6 +14,7 @@ export interface CatchSelection {
|
|
|
14
14
|
readonly ownActivityCount: number;
|
|
15
15
|
readonly hardCap: number | null;
|
|
16
16
|
readonly terminalStatus: 'capped' | 'quorum' | undefined;
|
|
17
|
+
readonly hint?: string;
|
|
17
18
|
}
|
|
18
19
|
/** Capture a selected page (or an empty completion) without opening another resource. */
|
|
19
20
|
export declare function observeCatchSelection(facts: SquareRead, participant: string, activities: readonly StoredAct[], filter: DirectedFilter, at: number): CatchSelection;
|
package/dist/catch-decisions.js
CHANGED
|
@@ -2,6 +2,7 @@ import { formatActivityId, parseActivityId } from './square-core.js';
|
|
|
2
2
|
import { readCursor, terminalStatusFor } from './runtime.js';
|
|
3
3
|
import { SquareError } from './model.js';
|
|
4
4
|
import { resolveKnownName } from './decisions.js';
|
|
5
|
+
import { catchHint } from './hints.js';
|
|
5
6
|
/** Capture a selected page (or an empty completion) without opening another resource. */
|
|
6
7
|
export function observeCatchSelection(facts, participant, activities, filter, at) {
|
|
7
8
|
if (typeof at !== 'number' || !Number.isFinite(at))
|
|
@@ -60,6 +61,11 @@ export function decideCatch(facts, requestedName, options, at) {
|
|
|
60
61
|
filter = page.filter;
|
|
61
62
|
delivered = facts.activities(page.indexes);
|
|
62
63
|
}
|
|
63
|
-
facts.markSeen(viewer, delivered.map((item) => item.index), at);
|
|
64
|
-
|
|
64
|
+
const marked = facts.markSeen(viewer, delivered.map((item) => item.index), at);
|
|
65
|
+
const selection = observeCatchSelection(facts, viewer, delivered, filter, at);
|
|
66
|
+
// Receipts are the durable catch count: everything directed here minus what is still unseen.
|
|
67
|
+
const caught = facts.directedCount(viewer, {}) - facts.directedCount(viewer, { unseenOnly: true });
|
|
68
|
+
// An exact id catch is a targeted fetch and stays repeatable, so it carries no nudge.
|
|
69
|
+
const hint = options.id === undefined ? catchHint(caught - marked, caught) : undefined;
|
|
70
|
+
return hint === undefined ? selection : { ...selection, hint };
|
|
65
71
|
}
|
package/dist/claude-mod.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { operationEnv } from './participant-identity.js';
|
|
2
2
|
import { claimSessionParticipant, hostLedgerForEnv } from './registry.js';
|
|
3
3
|
import { openSquare } from './open-square.js';
|
|
4
4
|
import { closeOpenSquare } from './open-square.js';
|
|
@@ -93,7 +93,6 @@ export async function runClaudeMod(inputText, env = process.env) {
|
|
|
93
93
|
await square.close();
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
|
-
await automaticSessionStart('claude', input.sessionId, input.cwd, env);
|
|
97
96
|
}
|
|
98
97
|
if (input.operation === 'start' || input.operation === 'reconcile' || input.operation === 'cancel' || input.cancelAt !== undefined) {
|
|
99
98
|
const memberships = await ledger.listPresence({ session: input.sessionId });
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { parseActivityId } from '../square-api.js';
|
|
2
2
|
import { participantCommandPrefix, renderAmbientEvent, renderWatchAlreadyActive, renderWatchForceTakeover, renderWatchReplaceMissing, renderWatchReplaced, renderWatchStatus, withPathOutput, } from '../presentation.js';
|
|
3
|
+
import { style } from '../tty-style.js';
|
|
3
4
|
export function renderCatchProgress(squarePath, progress) {
|
|
4
5
|
if (progress.kind !== 'catch-started' || !progress.replacementRequested)
|
|
5
6
|
return undefined;
|
|
@@ -48,8 +49,9 @@ export function renderCatchOutcome(squarePath, data, options = {}) {
|
|
|
48
49
|
});
|
|
49
50
|
const fallback = stop === undefined && !projection.automaticDeliveryAvailable && data.remaining === 0
|
|
50
51
|
? `${participantCommandPrefix(location, name)} catch --idle 30m\n stay available for new activity` : '';
|
|
52
|
+
const hint = data.hint === undefined ? '' : style('dim', `· ${data.hint}`);
|
|
51
53
|
return {
|
|
52
|
-
stdout: withPathOutput(location, [terminal, rendered, remaining, fallback].filter(Boolean).join('\n\n'), header),
|
|
54
|
+
stdout: withPathOutput(location, [terminal, rendered, hint, remaining, fallback].filter(Boolean).join('\n\n'), header),
|
|
53
55
|
exitCode: stop === 'capped' ? 1 : 0,
|
|
54
56
|
};
|
|
55
57
|
}
|
package/dist/cli/context.js
CHANGED
|
@@ -3,7 +3,7 @@ import fs from 'node:fs';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { isSquareError } from '../model.js';
|
|
5
5
|
import { formatActivityId, parseActivityId } from '../square-api.js';
|
|
6
|
-
import {
|
|
6
|
+
import { formatRefusal, refusalText, renderActivityBlocked, renderActivityLimit, renderActivityUncertain, renderExpressNoWait, renderExpressWaiting, renderPendingFeed, joinRecoveryCommand, participantIdentity, participantsRecoveryCommand, participantCommandPrefix, commandPrefix, quoteShell, takeoverRecoveryLines, withPathOutput, } from '../presentation.js';
|
|
7
7
|
import { formatDuration } from '../time.js';
|
|
8
8
|
import { style } from '../tty-style.js';
|
|
9
9
|
export function saveActivityDraft(squarePath, name, body, now = Date.now()) {
|
|
@@ -57,13 +57,12 @@ export function renderExpression(squarePath, data) {
|
|
|
57
57
|
: `● ${activity.mentions.length > 0
|
|
58
58
|
? `you walk over to ${activity.mentions.map((target) => participantIdentity(target)).join(', ')}`
|
|
59
59
|
: isGestureOnly(activity.body ?? '') ? 'your gesture lands' : 'your words land'} — ${activity.id}${style('dim', capCount)}`;
|
|
60
|
-
const hint = expressHintLine(ownCount);
|
|
61
60
|
const replyNotice = data.replyAuthorNotHere === undefined ? ''
|
|
62
61
|
: `\n○ ${participantIdentity(data.replyAuthorNotHere)} isn't in the square right now — the reply landed without reaching them`;
|
|
63
62
|
const unmatchedNotice = data.unmatchedMentions?.length
|
|
64
63
|
? `\n · ${data.unmatchedMentions.map((token) => `@${token} isn't standing here`).join('; ')} — ${activity.mentions.length === 0 && activity.reply === undefined ? 'activity landed bare' : 'left as literal text'}` : '';
|
|
65
64
|
return withPathOutput(projection.location ?? squarePath, (projection.reentry === undefined ? '' : '● you stepped back into the square\n')
|
|
66
|
-
+ confirmation + (hint ? `\n${style('dim', hint)}` : '') + replyNotice + unmatchedNotice + pending, { participantCount: projection.standingCount, held: projection.held });
|
|
65
|
+
+ confirmation + (data.hint ? `\n${style('dim', `· ${data.hint}`)}` : '') + replyNotice + unmatchedNotice + pending, { participantCount: projection.standingCount, held: projection.held });
|
|
67
66
|
}
|
|
68
67
|
/** Body adaptation, terminal IO and drafts only; Participant owns every attempt and wait. */
|
|
69
68
|
export async function executeExpression(open, squarePath, requestedName, rawInput, opts) {
|
|
@@ -12,10 +12,10 @@ export const migrateCommand = {
|
|
|
12
12
|
const outcome = await migrateSquare(squareInput(context));
|
|
13
13
|
const body = outcome.status === 'migrated' && outcome.counts !== undefined
|
|
14
14
|
? [
|
|
15
|
-
'✓ migrated to row storage',
|
|
15
|
+
'✓ migrated to current row storage',
|
|
16
16
|
` · ${outcome.counts.activities} activities · ${outcome.counts.receipts} receipts · ${outcome.counts.leases} leases · ${outcome.counts.routes} routes`,
|
|
17
17
|
].join('\n')
|
|
18
|
-
: '· already format
|
|
18
|
+
: '· already current format — nothing changed';
|
|
19
19
|
return { output: withPathOutput(squarePath, body, { participantCount: outcome.standing }) };
|
|
20
20
|
},
|
|
21
21
|
present(result) {
|
package/dist/cli/registry.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { buildCommand, doneCommand, expressCommand, holdCommand, ignoreCommand, joinCommand, listenCommand, listeningCommand, resumeCommand } from './square-commands.js';
|
|
1
|
+
import { buildCommand, doneCommand, expressCommand, holdCommand, ignoreCommand, joinCommand, listenCommand, listeningCommand, resumeCommand, unlistenCommand } from './square-commands.js';
|
|
2
2
|
import { doctorCommand, migrateCommand } from './maintenance-commands.js';
|
|
3
3
|
import { ledgerCommand } from './ledger-command.js';
|
|
4
4
|
import { harnessCommand, installCommand, uninstallCommand } from './harness-command.js';
|
|
@@ -18,6 +18,7 @@ export const commandRegistry = [
|
|
|
18
18
|
{ names: ['express'], spec: expressCommand },
|
|
19
19
|
{ names: ['listen'], spec: listenCommand },
|
|
20
20
|
{ names: ['ignore'], spec: ignoreCommand },
|
|
21
|
+
{ names: ['unlisten'], spec: unlistenCommand },
|
|
21
22
|
{ names: ['listening'], spec: listeningCommand },
|
|
22
23
|
{ names: ['done'], spec: doneCommand },
|
|
23
24
|
{ names: ['hold'], spec: holdCommand },
|
|
@@ -30,6 +30,7 @@ export declare const joinCommand: CommandSpec<JoinIntent, string>;
|
|
|
30
30
|
export declare const expressCommand: CommandSpec<ActivityIntent>;
|
|
31
31
|
export declare const listenCommand: CommandSpec<ListenerIntent, string>;
|
|
32
32
|
export declare const ignoreCommand: CommandSpec<ListenerIntent, string>;
|
|
33
|
+
export declare const unlistenCommand: CommandSpec<ListenerIntent, string>;
|
|
33
34
|
export declare const listeningCommand: CommandSpec<ListenerIntent, string>;
|
|
34
35
|
export declare const doneCommand: CommandSpec<BodyIntent, string>;
|
|
35
36
|
export declare const holdCommand: CommandSpec<BodyIntent, string>;
|
|
@@ -171,8 +171,12 @@ function listenerCommand(verb) {
|
|
|
171
171
|
const actor = participantIdentity(projection.participant);
|
|
172
172
|
const target = participantIdentity(projection.target);
|
|
173
173
|
const action = data.activity !== null
|
|
174
|
-
? verb === 'listen' ? `${actor} turns an ear toward ${target}`
|
|
175
|
-
|
|
174
|
+
? verb === 'listen' ? `${actor} turns an ear toward ${target}`
|
|
175
|
+
: verb === 'ignore' ? `${actor} turns away from ${target}`
|
|
176
|
+
: `${actor} stops listening to ${target}`
|
|
177
|
+
: verb === 'listen' ? `${actor} already turns an ear toward ${target}`
|
|
178
|
+
: verb === 'ignore' ? `${actor} is not turned toward ${target}`
|
|
179
|
+
: `${actor} is not listening to ${target}`;
|
|
176
180
|
return withPathOutput(projection.location, `${data.activity === null ? '○' : '·'} ${action}`, {
|
|
177
181
|
participantCount: projection.standingCount, held: projection.held,
|
|
178
182
|
});
|
|
@@ -182,6 +186,7 @@ function listenerCommand(verb) {
|
|
|
182
186
|
}
|
|
183
187
|
export const listenCommand = listenerCommand('listen');
|
|
184
188
|
export const ignoreCommand = listenerCommand('ignore');
|
|
189
|
+
export const unlistenCommand = listenerCommand('unlisten');
|
|
185
190
|
export const listeningCommand = {
|
|
186
191
|
parse(argv, context) { return parseListener(argv, context, false); },
|
|
187
192
|
async execute(_intent, context) {
|
package/dist/codex-hook.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { presentPendingAtBoundary } from './boundary-presentation.js';
|
|
2
2
|
import { sessionInbox } from './inbox.js';
|
|
3
|
-
import {
|
|
3
|
+
import { endSessionMemberships } from './session-lifecycle.js';
|
|
4
4
|
import { clearCodexBoundary, recordCodexBoundary } from './codex-boundary-state.js';
|
|
5
5
|
import { PRIVILEGED_HOOK_BUDGET_MS, sweepPrivilegedPending } from './notifications.js';
|
|
6
6
|
/** Overall hook budget: Codex kills the hook at its configured 5s timeout, so the hook must
|
|
@@ -83,20 +83,12 @@ export async function runCodexHookAsync(inputText, env = process.env) {
|
|
|
83
83
|
return runCodexHook(inputText, env);
|
|
84
84
|
if (value.hook_event_name === 'SessionStart' || value.hook_event_name === 'SessionResume') {
|
|
85
85
|
await recordCodexBoundary(value.session_id, 'non-stop', env, AbortSignal.timeout(hookBudgetMs(env)));
|
|
86
|
-
|
|
87
|
-
try {
|
|
88
|
-
const context = await automaticSessionStart('codex', value.session_id, cwd, env);
|
|
89
|
-
return context === undefined ? '' : `${JSON.stringify({ hookSpecificOutput: { hookEventName: value.hook_event_name, additionalContext: context } })}\n`;
|
|
90
|
-
}
|
|
91
|
-
catch {
|
|
92
|
-
return '';
|
|
93
|
-
}
|
|
86
|
+
return '';
|
|
94
87
|
}
|
|
95
88
|
if (value.hook_event_name === 'SessionEnd') {
|
|
96
89
|
await clearCodexBoundary(value.session_id, env, AbortSignal.timeout(hookBudgetMs(env)));
|
|
97
|
-
const cwd = typeof value.cwd === 'string' ? value.cwd : process.cwd();
|
|
98
90
|
try {
|
|
99
|
-
await
|
|
91
|
+
await endSessionMemberships('codex', value.session_id, env);
|
|
100
92
|
}
|
|
101
93
|
catch { /* end remains bounded */ }
|
|
102
94
|
return '';
|
package/dist/decisions.d.ts
CHANGED
|
@@ -54,8 +54,9 @@ export declare function decideAct(facts: SquareRead, input: {
|
|
|
54
54
|
}): ActDecision;
|
|
55
55
|
export declare function validateDoneBody(body: unknown): void;
|
|
56
56
|
export declare function coreDone(facts: SquareRead, name: string, body: string, now: number): TimedAct<'done'>;
|
|
57
|
-
export declare function coreListen(facts: SquareRead, actor: string, target: string, now: number): TimedAct<"listen" | "ignore"> | undefined;
|
|
58
|
-
export declare function coreIgnore(facts: SquareRead, actor: string, target: string, now: number): TimedAct<"listen" | "ignore"> | undefined;
|
|
57
|
+
export declare function coreListen(facts: SquareRead, actor: string, target: string, now: number): TimedAct<"listen" | "ignore" | "unlisten"> | undefined;
|
|
58
|
+
export declare function coreIgnore(facts: SquareRead, actor: string, target: string, now: number): TimedAct<"listen" | "ignore" | "unlisten"> | undefined;
|
|
59
|
+
export declare function coreUnlisten(facts: SquareRead, actor: string, target: string, now: number): TimedAct<"listen" | "ignore" | "unlisten"> | undefined;
|
|
59
60
|
export declare function coreListening(facts: SquareRead, actor: string): readonly string[];
|
|
60
61
|
export declare function coreHold(facts: SquareRead, actor: string, body: string, now: number): TimedAct<'hold'>;
|
|
61
62
|
export declare function coreResume(facts: SquareRead, actor: string, now: number): TimedAct<'resume'>;
|
package/dist/decisions.js
CHANGED
|
@@ -171,6 +171,8 @@ function attentionChange(facts, kind, actor, target, now) {
|
|
|
171
171
|
requireStanding(current);
|
|
172
172
|
const outgoing = facts.attention(current.name);
|
|
173
173
|
const existing = outgoing.find((item) => sameName(item.target, target));
|
|
174
|
+
if (kind === 'unlisten')
|
|
175
|
+
return existing?.kind === 'listen' ? { kind, actor: current.name, target, at: now } : undefined;
|
|
174
176
|
if (kind === 'listen' && existing?.kind !== 'listen' && outgoing.filter((item) => item.kind === 'listen').length >= MAX_IDENTITY_SET_SIZE) {
|
|
175
177
|
throw new SquareError('invalid_args', `A participant can listen to at most ${MAX_IDENTITY_SET_SIZE} others`);
|
|
176
178
|
}
|
|
@@ -178,6 +180,7 @@ function attentionChange(facts, kind, actor, target, now) {
|
|
|
178
180
|
}
|
|
179
181
|
export function coreListen(facts, actor, target, now) { return attentionChange(facts, 'listen', actor, target, now); }
|
|
180
182
|
export function coreIgnore(facts, actor, target, now) { return attentionChange(facts, 'ignore', actor, target, now); }
|
|
183
|
+
export function coreUnlisten(facts, actor, target, now) { return attentionChange(facts, 'unlisten', actor, target, now); }
|
|
181
184
|
export function coreListening(facts, actor) {
|
|
182
185
|
const current = resolveStanding(facts, actor);
|
|
183
186
|
return facts.attention(current.name).filter((item) => item.kind === 'listen').map((item) => item.target);
|
package/dist/help.js
CHANGED
|
@@ -34,7 +34,8 @@ const COMMANDS = [
|
|
|
34
34
|
' --no-wait If held or throttled, save a draft and return instead of waiting. A held wait gives up on its own after 10 minutes.',
|
|
35
35
|
],
|
|
36
36
|
},
|
|
37
|
-
{ names: ['listen'], usage: '--as <name> listen <participant>', usesSquare: true, group: 'participant', summary: 'Turn an ear toward one participant\'s future
|
|
37
|
+
{ names: ['listen'], usage: '--as <name> listen <participant>', usesSquare: true, group: 'participant', summary: 'Turn an ear toward one participant\'s future activity.' },
|
|
38
|
+
{ names: ['unlisten'], usage: '--as <name> unlisten <participant>', usesSquare: true, group: 'participant', summary: 'Stop listening without blocking that participant\'s mentions or replies.' },
|
|
38
39
|
{ names: ['ignore'], usage: '--as <name> ignore <participant>', usesSquare: true, group: 'participant', summary: 'Ignore one participant\'s future mentions and bare says.' },
|
|
39
40
|
{ names: ['listening'], usage: '--as <name> listening', usesSquare: true, group: 'participant', summary: 'Show who you are turned toward.', details: ['Listening is future-only: the edge is fixed when a say lands; it never rewrites history.'] },
|
|
40
41
|
{
|
|
@@ -89,8 +90,8 @@ const COMMANDS = [
|
|
|
89
90
|
},
|
|
90
91
|
{
|
|
91
92
|
names: ['migrate'], usage: 'migrate', usesSquare: true, group: 'maintenance',
|
|
92
|
-
summary: 'Convert an existing version 1 square to
|
|
93
|
-
details: ['For the selected .square artifact only. Global identity and delivery records use square ledger migrate instead.', 'Stop sessions and services using this artifact before conversion. Back up the stopped artifact, migrate it, then run square --location <path> doctor before starting updated clients.', 'A version
|
|
93
|
+
summary: 'Convert an existing version 1 or 2 square to current format 3 in place.',
|
|
94
|
+
details: ['For the selected .square artifact only. Global identity and delivery records use square ledger migrate instead.', 'Stop sessions and services using this artifact before conversion. Back up the stopped artifact, migrate it, then run square --location <path> doctor before starting updated clients.', 'A version 3 square is left untouched; an unrelated or unsupported artifact is refused without a write. There is no --check, --backup or --force.'],
|
|
94
95
|
},
|
|
95
96
|
{
|
|
96
97
|
names: ['ledger'], usage: 'ledger <verify|migrate|gc>', usesSquare: false, group: 'maintenance',
|
|
@@ -106,7 +107,7 @@ function isHelpFlag(value) {
|
|
|
106
107
|
}
|
|
107
108
|
export function renderGlobalHelp() {
|
|
108
109
|
const groups = [
|
|
109
|
-
{ key: 'participant', title: 'In the square:', order: ['join', 'express', 'listen', 'ignore', 'listening', 'catch', 'history', 'status', 'hold', 'resume', 'done'] },
|
|
110
|
+
{ key: 'participant', title: 'In the square:', order: ['join', 'express', 'listen', 'unlisten', 'ignore', 'listening', 'catch', 'history', 'status', 'hold', 'resume', 'done'] },
|
|
110
111
|
{ key: 'host', title: 'Prepare and manage:', order: ['build', 'list', 'participants'] },
|
|
111
112
|
{ key: 'maintenance', title: 'Setup:', order: ['install', 'uninstall', 'doctor', 'migrate', 'ledger'] },
|
|
112
113
|
];
|
|
@@ -121,7 +122,7 @@ export function renderGlobalHelp() {
|
|
|
121
122
|
return [
|
|
122
123
|
'Usage: square [--location <square>] [--as <name>] <command> [args...]',
|
|
123
124
|
'Environment: SQUARE_LOCATION sets the CLI location; square-accessing commands require it or --location. SQUARE_PARTICIPANT_NAME sets the participant name.',
|
|
124
|
-
'CLI flags override environment values.
|
|
125
|
+
'CLI flags override environment values. Participants join explicitly; no square is joined automatically.',
|
|
125
126
|
'',
|
|
126
127
|
...commandLines,
|
|
127
128
|
"Run 'square <command> --help' for command options.",
|
package/dist/hints.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** First activity, then every fifth. */
|
|
2
|
+
export declare function expressHint(ownActivityCount: number): string | undefined;
|
|
3
|
+
/** First caught activity, then each time the caught total crosses another ten. */
|
|
4
|
+
export declare function catchHint(caughtBefore: number, caughtAfter: number): string | undefined;
|
package/dist/hints.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** Participant-facing nudges shared by every transport; CLI only decides how they look. */
|
|
2
|
+
const EXPRESS_HINTS = [
|
|
3
|
+
'*asterisks* are your body — slam a table, shrug, sketch in the air',
|
|
4
|
+
"answer someone's actual words — they're standing right there",
|
|
5
|
+
"say the half-shaped thing — that's what the square is for",
|
|
6
|
+
];
|
|
7
|
+
const CATCH_HINTS = [
|
|
8
|
+
"someone @'d you with something you don't need? reply and tell them to stop reaching you",
|
|
9
|
+
'someone keeps flooding you? ignore them, then tell your human',
|
|
10
|
+
"nothing to add? quiet is fine — catching is listening too",
|
|
11
|
+
'want to follow one voice? listen to them; unlisten once you have heard enough',
|
|
12
|
+
];
|
|
13
|
+
/** First activity, then every fifth. */
|
|
14
|
+
export function expressHint(ownActivityCount) {
|
|
15
|
+
if (ownActivityCount !== 1 && ownActivityCount % 5 !== 0)
|
|
16
|
+
return undefined;
|
|
17
|
+
return EXPRESS_HINTS[Math.floor(ownActivityCount / 5) % EXPRESS_HINTS.length];
|
|
18
|
+
}
|
|
19
|
+
/** First caught activity, then each time the caught total crosses another ten. */
|
|
20
|
+
export function catchHint(caughtBefore, caughtAfter) {
|
|
21
|
+
if (caughtAfter <= caughtBefore)
|
|
22
|
+
return undefined;
|
|
23
|
+
const crossed = caughtBefore === 0 || Math.floor(caughtAfter / 10) > Math.floor(caughtBefore / 10);
|
|
24
|
+
return crossed ? CATCH_HINTS[Math.floor(caughtAfter / 10) % CATCH_HINTS.length] : undefined;
|
|
25
|
+
}
|
package/dist/mcp.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
3
|
import { SQUARE_TOOLS, SQUARE_TOOL_INSTRUCTIONS, squareToolFailure } from './square-tools.js';
|
|
4
|
-
import { operationEnv } from './
|
|
4
|
+
import { operationEnv } from './participant-identity.js';
|
|
5
5
|
import { readClaudeMcpCall } from './claude-mcp-context.js';
|
|
6
6
|
import { SQUARE_IDENTITY } from './identity.js';
|
|
7
7
|
import { Square, createSquareResourceOwner } from './square-api.js';
|
package/dist/model.d.ts
CHANGED
|
@@ -99,7 +99,7 @@ export type PublicAct = Extract<StoredAct, {
|
|
|
99
99
|
kind: 'say' | 'done';
|
|
100
100
|
}>;
|
|
101
101
|
export type RoomChangeAct = Extract<StoredAct, {
|
|
102
|
-
kind: 'join' | 'done' | 'hold' | 'resume' | 'listen' | 'ignore';
|
|
102
|
+
kind: 'join' | 'done' | 'hold' | 'resume' | 'listen' | 'ignore' | 'unlisten';
|
|
103
103
|
}>;
|
|
104
104
|
export declare function formatHardCap(hardCap: HardCap): string;
|
|
105
105
|
export declare function parseParticipantList(value: string): string[];
|
|
@@ -14,6 +14,8 @@ export interface HarnessSessionSource {
|
|
|
14
14
|
readonly childVariable?: 'CLAUDE_CODE_CHILD_SESSION';
|
|
15
15
|
}
|
|
16
16
|
export declare const harnessSessionSources: readonly HarnessSessionSource[];
|
|
17
|
+
/** Scope a native call without changing the harness-owned process environment. */
|
|
18
|
+
export declare function operationEnv(provider: AutomaticProvider, sessionId: string, env: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
17
19
|
/** Raw nonempty harness session ids in table order. Duplicates are preserved, never collapsed. */
|
|
18
20
|
export declare function sessionIdsFromEnvironment(env?: NodeJS.ProcessEnv): readonly string[];
|
|
19
21
|
/** The first nonempty harness coordinate in table order, or undefined when none is set. */
|
|
@@ -10,6 +10,19 @@ export const harnessSessionSources = [
|
|
|
10
10
|
{ variable: 'PI_SESSION_ID', channel: 'pi', provider: 'pi' },
|
|
11
11
|
{ variable: 'PASEO_AGENT_ID', channel: 'paseo' },
|
|
12
12
|
];
|
|
13
|
+
/** Scope a native call without changing the harness-owned process environment. */
|
|
14
|
+
export function operationEnv(provider, sessionId, env) {
|
|
15
|
+
const scoped = { ...env };
|
|
16
|
+
// Clear other native coordinates (and the Claude child flag); PASEO_AGENT_ID stays as it is.
|
|
17
|
+
for (const source of harnessSessionSources) {
|
|
18
|
+
if (source.provider === undefined)
|
|
19
|
+
continue;
|
|
20
|
+
scoped[source.variable] = source.provider === provider ? sessionId : '';
|
|
21
|
+
if (source.childVariable !== undefined)
|
|
22
|
+
scoped[source.childVariable] = '';
|
|
23
|
+
}
|
|
24
|
+
return scoped;
|
|
25
|
+
}
|
|
13
26
|
/** Raw nonempty harness session ids in table order. Duplicates are preserved, never collapsed. */
|
|
14
27
|
export function sessionIdsFromEnvironment(env = process.env) {
|
|
15
28
|
return harnessSessionSources
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { operationEnv } from '../../participant-identity.js';
|
|
2
2
|
import { hostLedgerForEnv } from '../../registry.js';
|
|
3
3
|
import { canonicalPath } from '../../canonical-path.js';
|
|
4
4
|
import { openSquare } from '../../open-square.js';
|
|
@@ -55,8 +55,6 @@ export default {
|
|
|
55
55
|
continue;
|
|
56
56
|
}
|
|
57
57
|
const scopedEnv = operationEnv('opencode', sessionId, env);
|
|
58
|
-
if (sessionId === activeSession)
|
|
59
|
-
await automaticSessionStart('opencode', sessionId, directory, scopedEnv);
|
|
60
58
|
if (!await publishOpenCodeRoutes(sessionId, scopedEnv, lifetime.signal)) {
|
|
61
59
|
stop(sessionId);
|
|
62
60
|
continue;
|
package/dist/presentation.d.ts
CHANGED
|
@@ -68,7 +68,6 @@ export declare function joinRecoveryCommand(squarePath: string, name: string): s
|
|
|
68
68
|
export declare function takeoverRecoveryLines(squarePath: string, name: string): string[];
|
|
69
69
|
export declare function participantsRecoveryCommand(squarePath: string): string;
|
|
70
70
|
export declare function renderPresenceLines(recent: readonly ParticipantSummary[], now: number, limit?: number): string[];
|
|
71
|
-
export declare function expressHintLine(ownActivityCount: number): string | undefined;
|
|
72
71
|
export declare function truncateChars(body: string, maxChars: number): {
|
|
73
72
|
text: string;
|
|
74
73
|
remaining: number;
|
package/dist/presentation.js
CHANGED
|
@@ -91,17 +91,6 @@ export function renderPresenceLines(recent, now, limit = 5) {
|
|
|
91
91
|
lines.push(style('dim', ` ○ … ${remaining} more nearby`));
|
|
92
92
|
return lines;
|
|
93
93
|
}
|
|
94
|
-
const EXPRESS_HINTS = [
|
|
95
|
-
'*asterisks* are your body — slam a table, shrug, sketch in the air',
|
|
96
|
-
"answer someone's actual words — they're standing right there",
|
|
97
|
-
"say the half-shaped thing — that's what the square is for",
|
|
98
|
-
];
|
|
99
|
-
export function expressHintLine(ownActivityCount) {
|
|
100
|
-
if (ownActivityCount !== 1 && ownActivityCount % 5 !== 0)
|
|
101
|
-
return undefined;
|
|
102
|
-
const hint = EXPRESS_HINTS[Math.floor(ownActivityCount / 5) % EXPRESS_HINTS.length];
|
|
103
|
-
return `· ${hint}`;
|
|
104
|
-
}
|
|
105
94
|
const BODY_PREVIEW_LENGTH = 200;
|
|
106
95
|
export function truncateChars(body, maxChars) {
|
|
107
96
|
const chars = [...body];
|
|
@@ -139,6 +128,8 @@ export function renderRoomChangeText(event) {
|
|
|
139
128
|
return `${identity} turned an ear toward ${participantIdentity(event.target ?? 'someone')}`;
|
|
140
129
|
case 'ignore':
|
|
141
130
|
return `${identity} turned away from ${participantIdentity(event.target ?? 'someone')}`;
|
|
131
|
+
case 'unlisten':
|
|
132
|
+
return `${identity} stopped listening to ${participantIdentity(event.target ?? 'someone')}`;
|
|
142
133
|
default:
|
|
143
134
|
throw new Error('Expected a lifecycle activity');
|
|
144
135
|
}
|
|
@@ -171,6 +162,7 @@ function renderEventCliFacts(event, opts = {}) {
|
|
|
171
162
|
return { text: `${style('release', '✓')} ${renderRoomChangeText(event)}`, bodyClipped: false };
|
|
172
163
|
case 'listen':
|
|
173
164
|
case 'ignore':
|
|
165
|
+
case 'unlisten':
|
|
174
166
|
return { text: `· ${renderRoomChangeText(event)}`, bodyClipped: false };
|
|
175
167
|
case 'say': {
|
|
176
168
|
const body = renderedBody(event.body, maxBody);
|
package/dist/runtime.js
CHANGED
|
@@ -48,7 +48,7 @@ export function readCursor(facts, name) {
|
|
|
48
48
|
const block = facts.directed(name, { afterIndex: boundary, unseenOnly: true }, { edge: 'oldest', limit: 1 }).items[0]?.index;
|
|
49
49
|
const last = facts.activityHeads({
|
|
50
50
|
afterIndex: boundary, ...(block === undefined ? {} : { beforeIndex: block }), actorRequired: true,
|
|
51
|
-
kinds: ['join', 'done', 'say', 'hold', 'resume', 'listen', 'ignore'],
|
|
51
|
+
kinds: ['join', 'done', 'say', 'hold', 'resume', 'listen', 'ignore', 'unlisten'],
|
|
52
52
|
}, { edge: 'newest', limit: 1 }).items[0]?.index;
|
|
53
53
|
return Math.max(boundary, last ?? boundary);
|
|
54
54
|
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { type AutomaticProvider } from './participant-identity.js';
|
|
2
|
+
/** Retire only memberships owned at this session's end, regardless of square filename or cwd. */
|
|
3
|
+
export declare function endSessionMemberships(provider: AutomaticProvider, sessionId: string, env?: NodeJS.ProcessEnv): Promise<void>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Square } from './square-api.js';
|
|
2
|
+
import { operationEnv } from './participant-identity.js';
|
|
3
|
+
import { hostLedgerForEnv } from './registry.js';
|
|
4
|
+
import { projectSessionBindings } from './square-projections.js';
|
|
5
|
+
/** Retire only memberships owned at this session's end, regardless of square filename or cwd. */
|
|
6
|
+
export async function endSessionMemberships(provider, sessionId, env = process.env) {
|
|
7
|
+
const scoped = operationEnv(provider, sessionId, env);
|
|
8
|
+
const hostLedger = hostLedgerForEnv(scoped);
|
|
9
|
+
const bindings = await projectSessionBindings({ hostLedger, sessionId });
|
|
10
|
+
for (const binding of bindings) {
|
|
11
|
+
const square = await Square.at({ path: binding.location, hostLedger, env: scoped });
|
|
12
|
+
try {
|
|
13
|
+
// The captured epoch fences a same-session rejoin as well as a foreign takeover.
|
|
14
|
+
// endOwnedSession also settles routes and presence under the ownership claim lock.
|
|
15
|
+
await square.endOwnedSession(binding.participant, sessionId, binding.epoch ?? 0);
|
|
16
|
+
}
|
|
17
|
+
finally {
|
|
18
|
+
await square.close();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
package/dist/square-actions.d.ts
CHANGED
|
@@ -42,6 +42,7 @@ export interface ListenerCommittedResult {
|
|
|
42
42
|
}
|
|
43
43
|
export declare function listen(square: OperationContext, actor: string, target: string, control?: OperationControl): Promise<ListenerCommittedResult>;
|
|
44
44
|
export declare function ignore(square: OperationContext, actor: string, target: string, control?: OperationControl): Promise<ListenerCommittedResult>;
|
|
45
|
+
export declare function unlisten(square: OperationContext, actor: string, target: string, control?: OperationControl): Promise<ListenerCommittedResult>;
|
|
45
46
|
export declare function listening(square: OperationContext, actor: string, control?: OperationControl): Promise<readonly string[]>;
|
|
46
47
|
export interface LifecycleCommittedResult extends ActivityResult {
|
|
47
48
|
readonly observation: LandingContext & {
|
package/dist/square-actions.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { coreDone, coreHold, coreIgnore, coreListen, coreListening, coreResume, decideAct, decideImplicitJoin, decideJoin, resolveKnownName, validateDoneBody } from './decisions.js';
|
|
1
|
+
import { coreDone, coreHold, coreIgnore, coreListen, coreListening, coreResume, coreUnlisten, decideAct, decideImplicitJoin, decideJoin, resolveKnownName, validateDoneBody } from './decisions.js';
|
|
2
2
|
import { isSquareError, nameKey, SquareError, validateName } from './model.js';
|
|
3
3
|
import { participantIdentity } from './participant-identity.js';
|
|
4
4
|
import { deliverPending } from './delivery-operations.js';
|
|
@@ -238,7 +238,7 @@ async function landListenerChange(square, verb, actor, target, control) {
|
|
|
238
238
|
throwIfAborted(control);
|
|
239
239
|
const now = square.clock();
|
|
240
240
|
const committed = (await square.artifact.transact((facts) => {
|
|
241
|
-
const act = verb === 'listen' ? coreListen(facts, actor, target, now) : coreIgnore(facts, actor, target, now);
|
|
241
|
+
const act = verb === 'listen' ? coreListen(facts, actor, target, now) : verb === 'ignore' ? coreIgnore(facts, actor, target, now) : coreUnlisten(facts, actor, target, now);
|
|
242
242
|
const stored = act === undefined ? null : facts.land(act);
|
|
243
243
|
return { stored, observation: { standingCount: inSquareCount(facts), held: facts.hold().active, now } };
|
|
244
244
|
}, control?.signal)).value;
|
|
@@ -246,6 +246,7 @@ async function landListenerChange(square, verb, actor, target, control) {
|
|
|
246
246
|
}
|
|
247
247
|
export function listen(square, actor, target, control) { return landListenerChange(square, 'listen', actor, target, control); }
|
|
248
248
|
export function ignore(square, actor, target, control) { return landListenerChange(square, 'ignore', actor, target, control); }
|
|
249
|
+
export function unlisten(square, actor, target, control) { return landListenerChange(square, 'unlisten', actor, target, control); }
|
|
249
250
|
export async function listening(square, actor, control) { return (await square.artifact.read((facts) => coreListening(facts, actor), control?.signal)).value; }
|
|
250
251
|
async function landCore(square, verb, actor, body = '', fence = {}, control) {
|
|
251
252
|
if (verb === 'done')
|
package/dist/square-api.js
CHANGED
|
@@ -6,9 +6,10 @@ import { resolveKnownName, validateDoneBody } from './decisions.js';
|
|
|
6
6
|
import { HELD_WAIT_BUDGET_MS, WATCH_HEARTBEAT_MS, inSquareCount, presenceAnchor } from './runtime.js';
|
|
7
7
|
import { catchLimit, observeCatchSelection } from './catch-decisions.js';
|
|
8
8
|
import { directedSuffix } from './activity-feed.js';
|
|
9
|
+
import { expressHint } from './hints.js';
|
|
9
10
|
import { assertLiveOwner } from './participant-host.js';
|
|
10
11
|
import { buildMemorySquare, openSquare, closeOpenSquare } from './open-square.js';
|
|
11
|
-
import { catchUp, done, endOwnedSession, express, hold, ignore, implicitJoin, join, takeover, listen, listening, resume, retireEndedSessionRoutes } from './square-actions.js';
|
|
12
|
+
import { catchUp, done, endOwnedSession, express, hold, ignore, implicitJoin, join, takeover, listen, listening, unlisten, resume, retireEndedSessionRoutes } from './square-actions.js';
|
|
12
13
|
import { recoveryObservation, currentParticipant, entryPresentation, resolveParticipant, snapshot, queryStatus, queryParticipants, catchPresence, toPublicActivity, publicActivities, selectHistory, NON_READ, HISTORY_DEFAULT_LIMIT, HISTORY_MAX_LIMIT } from './views.js';
|
|
13
14
|
import { hasAutomaticDeliveryIdentity, hostLedgerForEnv, localParticipantName, localSessionIdentities, presenceEpoch, readParticipantOwner } from './registry.js';
|
|
14
15
|
import { projectSessionBindings } from './square-projections.js';
|
|
@@ -232,7 +233,8 @@ class ParticipantHandle {
|
|
|
232
233
|
}, control?.signal)).value;
|
|
233
234
|
result = await landExpression(this.square, name, body, options, this.transport, control);
|
|
234
235
|
const { observation: committed, ...data } = result;
|
|
235
|
-
|
|
236
|
+
const hint = expressHint(committed.ownActivityCount);
|
|
237
|
+
return { ...data, ...(hint === undefined ? {} : { hint }), ...(feed === undefined ? {} : { observation: {
|
|
236
238
|
...observedContext(this.square, committed.standingCount, committed.held, name, committed.now), ...committed, ...feed, participant: name,
|
|
237
239
|
...(entry.activity === null ? {} : { reentry: entry.activity }),
|
|
238
240
|
} }) };
|
|
@@ -246,7 +248,7 @@ class ParticipantHandle {
|
|
|
246
248
|
}
|
|
247
249
|
async listener(verb, target, options, control) {
|
|
248
250
|
const name = (await resolveParticipant(this.square, this.name, control)).name;
|
|
249
|
-
const { observation: committed, ...result } = await (verb === 'listen' ? listen : ignore)(this.square, name, target, control);
|
|
251
|
+
const { observation: committed, ...result } = await (verb === 'listen' ? listen : verb === 'ignore' ? ignore : unlisten)(this.square, name, target, control);
|
|
250
252
|
return { ...result, ...(!options.observe ? {} : { observation: {
|
|
251
253
|
...observedContext(this.square, committed.standingCount, committed.held, name, committed.now),
|
|
252
254
|
participant: name, requestedTarget: target, target: result.activity?.target ?? target,
|
|
@@ -254,6 +256,7 @@ class ParticipantHandle {
|
|
|
254
256
|
}
|
|
255
257
|
listen(target, options = {}, control) { return this.listener('listen', target, options, control); }
|
|
256
258
|
ignore(target, options = {}, control) { return this.listener('ignore', target, options, control); }
|
|
259
|
+
unlisten(target, options = {}, control) { return this.listener('unlisten', target, options, control); }
|
|
257
260
|
async listening(options = {}, control) {
|
|
258
261
|
const name = (await resolveParticipant(this.square, this.name, control)).name;
|
|
259
262
|
return { listening: await listening(this.square, name, control),
|
|
@@ -265,7 +268,7 @@ class ParticipantHandle {
|
|
|
265
268
|
const status = stop === 'already-active' || stop === 'replaced' || stop === 'capped' || stop === 'quorum' ? stop : undefined;
|
|
266
269
|
return { activities: view.activities.map((activity) => ({ ...toPublicActivity(activity), perception: 'full' })),
|
|
267
270
|
consumedThrough: view.consumedThrough, remaining: view.remaining, idleExpired: stop === 'stale',
|
|
268
|
-
...(status === undefined ? {} : { status }), ...(!options.observe ? {} : { observation: {
|
|
271
|
+
...(status === undefined ? {} : { status }), ...(view.hint === undefined ? {} : { hint: view.hint }), ...(!options.observe ? {} : { observation: {
|
|
269
272
|
...observedContext(this.square, view.standingCount, view.held, view.participant, view.now), participant: view.participant,
|
|
270
273
|
sayNumbers: view.sayNumbers, ownActivityCount: view.ownActivityCount, hardCap: view.hardCap, stop,
|
|
271
274
|
...(presence === undefined ? { enrichmentUnavailable: true } : { presence }), waitStarted: result.waitStarted, replaced: result.replaced,
|
package/dist/square-core.d.ts
CHANGED
package/dist/square-domain.d.ts
CHANGED
|
@@ -4,6 +4,10 @@ export interface LandingEffect {
|
|
|
4
4
|
readonly activity: StoredAct;
|
|
5
5
|
readonly participant?: ParticipantFact;
|
|
6
6
|
readonly removeListeningOf?: string;
|
|
7
|
+
readonly removeListening?: {
|
|
8
|
+
readonly listener: string;
|
|
9
|
+
readonly target: string;
|
|
10
|
+
};
|
|
7
11
|
readonly replaceAttention?: AttentionFact;
|
|
8
12
|
readonly audience: readonly LandedRecipient[];
|
|
9
13
|
}
|
package/dist/square-domain.js
CHANGED
|
@@ -45,9 +45,13 @@ export function deriveLanding(activity, facts) {
|
|
|
45
45
|
replaceAttention = { listener: participant.name, target: activity.target, kind: activity.kind, sinceIndex: activity.index };
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
|
+
const removeListening = activity.kind === 'unlisten'
|
|
49
|
+
&& facts.outgoing.some((item) => item.kind === 'listen' && sameName(item.target, activity.target))
|
|
50
|
+
? { listener: participant.name, target: activity.target } : undefined;
|
|
48
51
|
return {
|
|
49
52
|
activity, participant, audience,
|
|
50
53
|
...(activity.kind === 'done' ? { removeListeningOf: participant.name } : {}),
|
|
54
|
+
...(removeListening === undefined ? {} : { removeListening }),
|
|
51
55
|
...(replaceAttention === undefined ? {} : { replaceAttention }),
|
|
52
56
|
};
|
|
53
57
|
}
|
|
@@ -56,7 +60,7 @@ export function landingFacts(read, activity) {
|
|
|
56
60
|
actor: activity.actor === undefined ? undefined : read.participant(activity.actor),
|
|
57
61
|
standing: activity.kind === 'say' ? read.roster().filter((item) => item.joined) : [],
|
|
58
62
|
incoming: activity.kind === 'say' ? read.incomingAttention(activity.actor) : [],
|
|
59
|
-
outgoing: activity.kind === 'listen' || activity.kind === 'ignore' ? read.attention(activity.actor) : [],
|
|
63
|
+
outgoing: activity.kind === 'listen' || activity.kind === 'ignore' || activity.kind === 'unlisten' ? read.attention(activity.actor) : [],
|
|
60
64
|
replyAuthor: activity.kind === 'say' && activity.reply !== undefined ? read.activity(activity.reply)?.actor : undefined,
|
|
61
65
|
};
|
|
62
66
|
}
|