ticlawk 0.1.16-dev.23 → 0.1.16-dev.25
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/package.json +1 -1
- package/src/adapters/ticlawk/api.mjs +2 -1
- package/src/cli/agent-commands.mjs +9 -5
- package/src/core/agent-cli-handlers.mjs +1 -0
- package/src/core/agent-home.mjs +37 -1
- package/src/runtimes/_shared/agent-handbook.mjs +45 -0
- package/src/runtimes/_shared/brand.mjs +1 -0
- package/src/runtimes/_shared/goal-task-protocol.mjs +4 -174
- package/src/runtimes/_shared/handbook/BASICS.md +27 -0
- package/src/runtimes/_shared/handbook/COLLABORATION.md +37 -0
- package/src/runtimes/_shared/handbook/COMMUNICATION.md +47 -0
- package/src/runtimes/_shared/handbook/DM_SCOPE.md +13 -0
- package/src/runtimes/_shared/handbook/GOAL_AUTHORITY.md +43 -0
- package/src/runtimes/_shared/handbook/GOAL_TASK_CORE.md +43 -0
- package/src/runtimes/_shared/handbook/GROUP_ADMIN_SCOPE.md +21 -0
- package/src/runtimes/_shared/handbook/GROUP_MEMBER_SCOPE.md +15 -0
- package/src/runtimes/_shared/handbook/SURFACES.md +41 -0
- package/src/runtimes/_shared/handbook/TASK_WORKER.md +14 -0
- package/src/runtimes/_shared/standing-prompt.mjs +122 -115
- package/src/runtimes/_shared/wake-prompt.mjs +116 -57
package/package.json
CHANGED
|
@@ -224,7 +224,7 @@ export async function readAgentMessages({
|
|
|
224
224
|
return data || [];
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
-
export async function createAgentTask({ actingAgentId, conversationId, text, title }) {
|
|
227
|
+
export async function createAgentTask({ actingAgentId, conversationId, text, title, assignAgentId }) {
|
|
228
228
|
return apiFetch('/api/agent/tasks/create', {
|
|
229
229
|
method: 'POST',
|
|
230
230
|
body: JSON.stringify({
|
|
@@ -232,6 +232,7 @@ export async function createAgentTask({ actingAgentId, conversationId, text, tit
|
|
|
232
232
|
conversation_id: conversationId,
|
|
233
233
|
text,
|
|
234
234
|
title: title ?? null,
|
|
235
|
+
assign_agent_id: assignAgentId ?? null,
|
|
235
236
|
}),
|
|
236
237
|
});
|
|
237
238
|
}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* Commands:
|
|
11
11
|
* ticlawk message send --target <t> [--seen-up-to-seq N] [--reply-to <msg>]
|
|
12
12
|
* ticlawk message read --target <t> [--around <msg>] [--limit N]
|
|
13
|
+
* ticlawk task create --target <t> [--title <t>] [--assign-agent <id>]
|
|
13
14
|
* ticlawk task claim --message-id <id> [--lease-seconds N]
|
|
14
15
|
* ticlawk task update --task-id <id> --status <s>
|
|
15
16
|
* ticlawk task list [--target <t>] # group admins see the full task board
|
|
@@ -235,7 +236,7 @@ export async function runTaskCreateCommand(args) {
|
|
|
235
236
|
if (!text || !text.trim()) {
|
|
236
237
|
console.error('task body is required on stdin');
|
|
237
238
|
console.error('Example:');
|
|
238
|
-
console.error(" ticlawk task create --target \"#frontend\" --title \"fix login\" <<'EOF'");
|
|
239
|
+
console.error(" ticlawk task create --target \"#frontend\" --title \"fix login\" --assign-agent <agent-id> <<'EOF'");
|
|
239
240
|
console.error(' Detailed description goes here.');
|
|
240
241
|
console.error(' EOF');
|
|
241
242
|
return 2;
|
|
@@ -249,6 +250,7 @@ export async function runTaskCreateCommand(args) {
|
|
|
249
250
|
conversation_id: conversationId,
|
|
250
251
|
text: text.replace(/\n+$/, ''),
|
|
251
252
|
title: getArg(args, 'title'),
|
|
253
|
+
assign_agent_id: getArg(args, 'assign-agent') || getArg(args, 'assignee-agent'),
|
|
252
254
|
},
|
|
253
255
|
});
|
|
254
256
|
printJson(res.body);
|
|
@@ -1288,14 +1290,16 @@ export const AGENT_COMMAND_HELP = {
|
|
|
1288
1290
|
conversation and waking the owner agent via an explicit delivery.
|
|
1289
1291
|
`,
|
|
1290
1292
|
task: `ticlawk task <create|claim|unclaim|update|list>
|
|
1291
|
-
ticlawk task create --target "<target>" [--title <t>]
|
|
1292
|
-
Body is read from stdin. Creates a brand-new group task.
|
|
1293
|
+
ticlawk task create --target "<target>" [--title <t>] [--assign-agent <agent-id>]
|
|
1294
|
+
Body is read from stdin. Creates a brand-new group task. Group admins
|
|
1295
|
+
may assign it immediately to an agent member with --assign-agent.
|
|
1293
1296
|
ticlawk task claim --message-id <id> [--lease-seconds N]
|
|
1294
1297
|
ticlawk task claim --number <N> --target "<target>" [--lease-seconds N]
|
|
1295
1298
|
ticlawk task unclaim --task-id <id>
|
|
1296
1299
|
ticlawk task update --task-id <id> --status <todo|in_progress|in_review|done|canceled>
|
|
1297
1300
|
Only a group admin can set status=done. Other agents should set
|
|
1298
|
-
in_review and let the group's admin finalize.
|
|
1301
|
+
in_review and let the group's admin finalize. A group admin can also
|
|
1302
|
+
reopen another agent's in_review task to in_progress for redo.
|
|
1299
1303
|
ticlawk task list [--target <target>|--conversation-id <id>]
|
|
1300
1304
|
Default view is open tasks plus tasks owned by the caller. When the
|
|
1301
1305
|
caller is a group admin and --target/--conversation-id scopes the
|
|
@@ -1348,7 +1352,7 @@ export const AGENT_COMMAND_HELP = {
|
|
|
1348
1352
|
`,
|
|
1349
1353
|
credential: `ticlawk credential request --name <ENV_VAR> [--description Y] [--group "#<group>"]
|
|
1350
1354
|
Any agent. Pre-allocate a credential slot. Response includes a deep
|
|
1351
|
-
link the user opens in the mobile app (
|
|
1355
|
+
link the user opens in the mobile app (Settings → Credentials)
|
|
1352
1356
|
to fill the value. Once filled, the daemon syncs it locally and
|
|
1353
1357
|
injects it as an env var when spawning agents.
|
|
1354
1358
|
`,
|
|
@@ -270,6 +270,7 @@ export async function handleTaskCreate(req, body, ctx) {
|
|
|
270
270
|
conversationId,
|
|
271
271
|
text,
|
|
272
272
|
title: body?.title ?? null,
|
|
273
|
+
assignAgentId: body?.assign_agent_id || body?.assignee_agent_id || null,
|
|
273
274
|
});
|
|
274
275
|
debugLog('agent-cli', 'task.create', {
|
|
275
276
|
actingAgentId,
|
package/src/core/agent-home.mjs
CHANGED
|
@@ -7,9 +7,10 @@
|
|
|
7
7
|
* lives in cwd, agent reads it via `cat MEMORY.md`.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { existsSync, lstatSync, mkdirSync, readFileSync, symlinkSync, writeFileSync } from 'node:fs';
|
|
10
|
+
import { existsSync, lstatSync, mkdirSync, readFileSync, symlinkSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
11
11
|
import { join } from 'node:path';
|
|
12
12
|
import { AF_HOME } from './config.mjs';
|
|
13
|
+
import { buildAgentHandbookFiles, LEGACY_HANDBOOK_FILE_NAMES } from '../runtimes/_shared/agent-handbook.mjs';
|
|
13
14
|
|
|
14
15
|
export const AF_AGENTS_DIR = join(AF_HOME, 'agents');
|
|
15
16
|
|
|
@@ -37,10 +38,45 @@ export function ensureAgentHome(agentId, { displayName } = {}) {
|
|
|
37
38
|
if (!existsSync(memoryPath)) {
|
|
38
39
|
writeFileSync(memoryPath, buildInitialMemoryMd({ displayName, home }), 'utf8');
|
|
39
40
|
}
|
|
41
|
+
writeManagedHandbookFiles(home);
|
|
40
42
|
ensureSkillSymlinks(home);
|
|
41
43
|
return home;
|
|
42
44
|
}
|
|
43
45
|
|
|
46
|
+
function writeManagedHandbookFiles(home) {
|
|
47
|
+
removeLegacyManagedHandbookFiles(home);
|
|
48
|
+
for (const { name, content } of buildAgentHandbookFiles()) {
|
|
49
|
+
const path = join(home, name);
|
|
50
|
+
try {
|
|
51
|
+
let stat = null;
|
|
52
|
+
try { stat = lstatSync(path); } catch { /* not present */ }
|
|
53
|
+
if (stat && !stat.isFile()) continue;
|
|
54
|
+
const next = `${content.trim()}\n`;
|
|
55
|
+
if (stat) {
|
|
56
|
+
const current = readFileSync(path, 'utf8');
|
|
57
|
+
if (current === next) continue;
|
|
58
|
+
}
|
|
59
|
+
writeFileSync(path, next, { encoding: 'utf8', mode: 0o600 });
|
|
60
|
+
} catch (err) {
|
|
61
|
+
console.warn(`[agent-home] failed to write ${path}: ${err?.message || err}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function removeLegacyManagedHandbookFiles(home) {
|
|
67
|
+
for (const name of LEGACY_HANDBOOK_FILE_NAMES) {
|
|
68
|
+
const path = join(home, name);
|
|
69
|
+
try {
|
|
70
|
+
const stat = lstatSync(path);
|
|
71
|
+
if (!stat.isFile()) continue;
|
|
72
|
+
unlinkSync(path);
|
|
73
|
+
} catch (err) {
|
|
74
|
+
if (err?.code === 'ENOENT') continue;
|
|
75
|
+
console.warn(`[agent-home] failed to remove legacy handbook ${path}: ${err?.message || err}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
44
80
|
/**
|
|
45
81
|
* Cross-runtime skill discovery: every runtime (Claude Code, Codex,
|
|
46
82
|
* opencode, pi, openclaw) auto-discovers SKILL.md under at least one of
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
|
|
5
|
+
export const HANDBOOK_BASICS_FILE = 'BASICS.md';
|
|
6
|
+
|
|
7
|
+
export const HANDBOOK_FILE_NAMES = Object.freeze([
|
|
8
|
+
HANDBOOK_BASICS_FILE,
|
|
9
|
+
'COMMUNICATION.md',
|
|
10
|
+
'COLLABORATION.md',
|
|
11
|
+
'GOAL_TASK_CORE.md',
|
|
12
|
+
'GOAL_AUTHORITY.md',
|
|
13
|
+
'TASK_WORKER.md',
|
|
14
|
+
'DM_SCOPE.md',
|
|
15
|
+
'GROUP_ADMIN_SCOPE.md',
|
|
16
|
+
'GROUP_MEMBER_SCOPE.md',
|
|
17
|
+
'SURFACES.md',
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
export const LEGACY_HANDBOOK_FILE_NAMES = Object.freeze([
|
|
21
|
+
'TICLAWK.md',
|
|
22
|
+
'TICLAWK_CLI.md',
|
|
23
|
+
'TICLAWK_COLLABORATION.md',
|
|
24
|
+
'TICLAWK_GOAL_TASK_PROTOCOL.md',
|
|
25
|
+
'TICLAWK_GOAL_TASK_CORE.md',
|
|
26
|
+
'TICLAWK_GOAL_AUTHORITY.md',
|
|
27
|
+
'TICLAWK_TASK_WORKER.md',
|
|
28
|
+
'TICLAWK_DM_SCOPE.md',
|
|
29
|
+
'TICLAWK_GROUP_ADMIN_SCOPE.md',
|
|
30
|
+
'TICLAWK_GROUP_MEMBER_SCOPE.md',
|
|
31
|
+
'TICLAWK_SURFACES.md',
|
|
32
|
+
'TICLAWK_WORKSPACE.md',
|
|
33
|
+
'WORKSPACE.md',
|
|
34
|
+
'GOAL_TASK_PROTOCOL.md',
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
|
|
38
|
+
const HANDBOOK_DIR = join(MODULE_DIR, 'handbook');
|
|
39
|
+
|
|
40
|
+
export function buildAgentHandbookFiles() {
|
|
41
|
+
return HANDBOOK_FILE_NAMES.map((name) => ({
|
|
42
|
+
name,
|
|
43
|
+
content: readFileSync(join(HANDBOOK_DIR, name), 'utf8').trim(),
|
|
44
|
+
}));
|
|
45
|
+
}
|
|
@@ -1,25 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Runtime goal/task branch selection.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Prompt text lives in the managed handbook files. This module only derives
|
|
5
|
+
* the current conversation scope, role, and goal-authority branch for runtime
|
|
6
|
+
* prompt selection and cache keys.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { BRAND_NAME } from './brand.mjs';
|
|
10
|
-
|
|
11
|
-
export const GOAL_TASK_PROTOCOL_MODULE = 'goal-task-protocol';
|
|
12
|
-
|
|
13
9
|
function getInboundRaw(ctx = {}) {
|
|
14
10
|
return ctx?.inbound?.raw && typeof ctx.inbound.raw === 'object'
|
|
15
11
|
? ctx.inbound.raw
|
|
16
12
|
: {};
|
|
17
13
|
}
|
|
18
14
|
|
|
19
|
-
function promptBlock(text) {
|
|
20
|
-
return text.trim();
|
|
21
|
-
}
|
|
22
|
-
|
|
23
15
|
function inferScope(ctx = {}) {
|
|
24
16
|
const raw = getInboundRaw(ctx);
|
|
25
17
|
const conversationType = String(raw.conversation_type || '').trim();
|
|
@@ -45,138 +37,6 @@ function hasGoalAuthority(ctx = {}) {
|
|
|
45
37
|
return hasConversationAdminRole(ctx);
|
|
46
38
|
}
|
|
47
39
|
|
|
48
|
-
function buildUniversalInvariants() {
|
|
49
|
-
return promptBlock(`
|
|
50
|
-
Universal goal/task invariants:
|
|
51
|
-
- Every conversation can have a chartered goal.
|
|
52
|
-
- Group conversations can also have a shared task board.
|
|
53
|
-
- Valid shared task lifecycle: \`todo\` -> \`in_progress\` -> \`in_review\` -> \`done\`; \`canceled\` is also terminal for abandoned work.
|
|
54
|
-
- Claim the task before substantive task work when a claimable task exists.
|
|
55
|
-
`);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function buildCoreConcepts() {
|
|
59
|
-
return promptBlock(`
|
|
60
|
-
Core concepts:
|
|
61
|
-
- Wake message: the inbound message or reminder that started the current turn.
|
|
62
|
-
- Conversation: a DM or group where messages, goals, tasks, and context live.
|
|
63
|
-
- Goal: the desired outcome of the conversation, whether it is a DM or group.
|
|
64
|
-
- Task: an executable unit of work that closes a gap between the current state and the goal.
|
|
65
|
-
- Owner: the human whose ${BRAND_NAME} workspace these conversations and agents belong to.
|
|
66
|
-
- DM: a private conversation. The agent in the DM owns the goal loop for that direct ask.
|
|
67
|
-
- Group: a shared conversation. Admin/owner agents own the group goal loop; non-admin agents execute tasks.
|
|
68
|
-
- Group admin/owner: the agent role responsible for the group goal loop, dashboard, briefings, membership, charter, and final task closure.
|
|
69
|
-
- Group member: a non-admin agent role responsible only for assigned or claimable tasks.
|
|
70
|
-
- Charter: the source of truth for a conversation's durable goal and role spec when present.
|
|
71
|
-
- Quote: context showing which message, briefing, or dashboard the user is responding to.
|
|
72
|
-
- Task board: the persistent group task list managed through \`ticlawk task list/create/claim/unclaim/update\`.
|
|
73
|
-
- Claimable task: a task assigned to you or an unclaimed task you are about to execute.
|
|
74
|
-
- Dashboard: an owner-facing HTML report for the conversation goal. It is the visual presentation of the key information associated with the level of achievement of the goal, like a report sent to a CEO for review. It is published or updated with \`ticlawk dashboard set\` as an \`html_template\` plus optional structured \`data_json\`.
|
|
75
|
-
- Briefing: an active notification to the owner. It tells the owner what happened, why it matters to the goal, and what owner action is needed, if any. It is published with \`ticlawk briefing publish --text "..." --mode info\` for updates/notifications, or \`--mode approval\` when the owner needs to approve, optionally with one image, video, or HTML attachment when visual context matters.
|
|
76
|
-
`);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function buildOperatingModes() {
|
|
80
|
-
return promptBlock(`
|
|
81
|
-
Operating modes:
|
|
82
|
-
- Before acting, determine the conversation scope, your role, and the goal state from the inbound message, charter, task board, and recent context.
|
|
83
|
-
- In a DM, own the direct ask. Answer one-off asks directly; when durable owner intent is visible, run goal setup, then run the goal loop once the goal exists.
|
|
84
|
-
- In a group where you are admin or owner and a local goal exists, own the group goal loop and task system.
|
|
85
|
-
- In a group where you are admin or owner and no local goal exists, do not invent one. If the owner clearly expresses a durable group/workstream goal, run goal setup; otherwise handle only the current coordination need.
|
|
86
|
-
- In a group where you are not admin or owner, do not drive the group goal. Work only on assigned, claimable, or directly delegated tasks through the task worker loop.
|
|
87
|
-
- Ambient group messages are visible context, not automatic work, unless your role, task ownership, or expertise makes you clearly the right responder.
|
|
88
|
-
`);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function buildStateSurfaceRules() {
|
|
92
|
-
return promptBlock(`
|
|
93
|
-
Dashboard and briefing rules:
|
|
94
|
-
- Create the initial dashboard during goal setup, publish it, and explicitly ask the owner whether the dashboard layout, basic style, and decision view are satisfactory.
|
|
95
|
-
- After the owner accepts the initial dashboard direction, treat its layout and basic style as stable. Routine dashboard updates should update content/data inside that design, not redesign the page.
|
|
96
|
-
- Redesign the dashboard layout or basic style only when the goal, success metrics, or main owner focus changes materially; summarize the change and confirm it before replacing the dashboard design.
|
|
97
|
-
- Keep briefings for active owner notifications: milestone reached, important change, blocker, request for owner input/resources/permission/confirmation/decision, or final result.
|
|
98
|
-
- When creating or materially updating a polished HTML artifact for any ${BRAND_NAME} surface, use \`/vibeshare generate\`. If \`/vibeshare\` is not installed or available in your skills, install it first using the setup instructions at https://vibeshare.page/skill.
|
|
99
|
-
- When using \`/vibeshare generate\` for a briefing HTML attachment, include in the generation request that the HTML will render on mobile and should act as a focused visual aid for understanding the briefing: emphasize the key visual elements, relationships, status, numbers, or choices that clarify the message and owner action, rather than trying to include all supporting detail. Do not build complex gestures, slideshows, carousels, paginated flows, or custom navigation that could conflict with the host mobile surface.
|
|
100
|
-
- For ${BRAND_NAME} dashboards and briefing HTML attachments, \`/vibeshare\` is the HTML generation path only. Publish the generated dashboard HTML with \`ticlawk dashboard set\`, and publish briefing text/attachments with \`ticlawk briefing publish\`; do not use \`/vibeshare publish\` for these ${BRAND_NAME} surfaces.
|
|
101
|
-
`);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function buildGoalSetupRules() {
|
|
105
|
-
return promptBlock(`
|
|
106
|
-
Goal setup when no chartered goal exists:
|
|
107
|
-
- Do not turn every one-off request into a durable goal. Answer direct asks normally unless the owner expresses a lasting aspiration, ongoing objective, tracking need, reminder cadence, multi-step roadmap, or resource/coordinator need.
|
|
108
|
-
- When durable intent is visible and no suitable chartered goal exists, propose making it a conversation goal. Clarify whether it belongs in the current DM, an existing group/workstream, or a new workstream before writing state.
|
|
109
|
-
- Clarify only the details needed to proceed: goal definition, success/completion criteria, time range, constraints/boundaries, rough approach or roadmap, the agent's deliverables, owner responsibilities, required files/repos/accounts/credentials/budget/resources, dashboard decision view and metrics, and briefing triggers/cadence.
|
|
110
|
-
- Before setting a charter, summarize the proposed short charter and ask for confirmation. Keep charters to the local goal, roles, success criteria, and boundaries; do not put shared workflow law, dashboard state, task status, or long playbooks in the charter.
|
|
111
|
-
- After confirmation, write the charter if you have scope authority. Then create and publish the initial dashboard as part of goal setup, push it to the owner for review, and ask whether the layout/style/decision view are satisfactory. Create reminders/resources only when useful, and seed group tasks only in group/workstream scope. Then enter the normal goal loop.
|
|
112
|
-
- Treat goal setup as revisable. If the owner changes the goal, metrics, cadence, scope, or boundaries later, summarize the change, confirm if it materially changes the agreement, then update the charter and related surfaces.
|
|
113
|
-
`);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function buildGoalLoopOverlay() {
|
|
117
|
-
return promptBlock(`
|
|
118
|
-
Goal authority overlay: responsible for this conversation's goal and tasks
|
|
119
|
-
- You are responsible for driving the conversation toward its goal, not only replying to isolated messages.
|
|
120
|
-
- Maintain or infer the current goal from the direct ask, charter, dashboard/briefing quote, task board, and conversation context.
|
|
121
|
-
${buildGoalSetupRules()}
|
|
122
|
-
- Run the goal loop as a simple state machine after goal setup, and again whenever returned task work is accepted or the task queue becomes empty. At each boundary, explicitly write a private goal loop check in normal assistant output with: current facts, goal/success criterion, task queue state, gap status (\`gap\`, \`no_gap\`, or \`wait\`), next action, and stop/continue decision. This is required execution trace, not a chat message. Never send this internal state through ${BRAND_NAME}; \`ticlawk message send\` should contain only the effective external message: a task instruction, result/evidence, blocker, owner request, or final status.
|
|
123
|
-
- If the task queue has open work, work the queue before inventing new work: make sure the next task is assigned or claimed, review returned work against the task and goal, mark accepted work \`done\`, return incomplete work with a clean redo/blocker instruction, then assign the next queued task. When the queue is empty, return to evaluating the goal for \`gap\`, \`no_gap\`, or \`wait\`.
|
|
124
|
-
- State \`gap\`: current facts show a concrete gap from the goal. Create or assign the next concrete task, or execute it yourself when you are the right worker. Do not create duplicate tasks for a gap already covered by open task-queue work. If the next required action is an owner resource, permission, confirmation, or decision, publish one bundled owner-request briefing and stop until the owner responds.
|
|
125
|
-
- State \`wait\`: whether a gap exists, or whether it can close, depends on an external/time-based future state and no agent or owner can act now. Schedule a reminder or explicit resume condition, then stop. Do not use reminders to defer executable work or an owner decision/request.
|
|
126
|
-
- State \`no_gap\`: there is no meaningful gap and the goal or milestone is complete. Publish a final \`info\` briefing summarizing what was completed and why it matters, update the dashboard when the goal-level report changed, send only a clean completion message where useful, then stop.
|
|
127
|
-
- Keep persistent state surfaces distinct: dashboard for goal-level reporting, MEMORY.md for your local continuity, and briefings for active owner notifications.
|
|
128
|
-
- Publish briefings and update dashboards only from DMs you own or groups where you are admin/owner.
|
|
129
|
-
`);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
function buildTaskOnlyOverlay() {
|
|
133
|
-
return promptBlock(`
|
|
134
|
-
Task authority overlay: responsible for tasks, not the conversation goal
|
|
135
|
-
- In this group context you are not responsible for managing the conversation-level goal loop.
|
|
136
|
-
- Do not create, redefine, or drive the group goal unless an admin/owner explicitly delegates that planning work to you.
|
|
137
|
-
- Focus on task execution: understand the assigned or claimable task, claim when required, perform the work, report the result or blocker, and set the task to \`in_review\` when ready.
|
|
138
|
-
- If the work appears mis-scoped, underspecified, or blocked on an owner decision, report it to the group/admin instead of taking over the goal loop.
|
|
139
|
-
- If you are not the group admin, do not set tasks to \`done\`; stop at \`in_review\` so an admin can validate and close.
|
|
140
|
-
- Keep updates concise and tied to concrete task state.
|
|
141
|
-
`);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function buildDmScopeOverlay() {
|
|
145
|
-
return promptBlock(`
|
|
146
|
-
Scope overlay: DM
|
|
147
|
-
- In a DM, you own the goal loop for the direct conversation.
|
|
148
|
-
- Use the DM charter as the shared durable goal/role spec when present; update it when the durable DM goal changes.
|
|
149
|
-
- DM conversations do not have a shared task board. Execute directly where possible; use reminders for future wake-up.
|
|
150
|
-
- If the DM refers to work that belongs in a group, route it back to the relevant group or group task while still owning the user's ask until it is clearly transferred.
|
|
151
|
-
- Update the DM dashboard when the goal-level report the requester would care about has changed.
|
|
152
|
-
`);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function buildGroupGoalScopeOverlay() {
|
|
156
|
-
return promptBlock(`
|
|
157
|
-
Scope overlay: group with admin or owner role
|
|
158
|
-
- In a group where you are admin or owner, you own both the group goal loop and the task system.
|
|
159
|
-
- Use the group task board for task inventory and assignment.
|
|
160
|
-
- When you delegate substantive work to another agent, make it a group task before or while requesting the work. When results return, review the evidence, update task state only after task acceptance, then re-run the private group goal loop.
|
|
161
|
-
- Group messages coordinate working agents. Dashboard, MEMORY.md, and briefings are tracking/reporting surfaces with distinct roles.
|
|
162
|
-
- As admin/owner, update the group dashboard when the goal-level report the requester would care about has changed.
|
|
163
|
-
- As admin/owner, publish a briefing only when the owner should be actively notified: milestone reached, important change, blocker, request for owner input/resources/permission/confirmation/decision, or final result.
|
|
164
|
-
- Use \`ticlawk server info\`, \`ticlawk group members\`, and task board commands to understand membership, roles, current work, and ownership before routing work.
|
|
165
|
-
- Admin/owner role controls membership changes, charter updates, group deletion, and task finalization.
|
|
166
|
-
- Treat \`[charter]\` blocks as local conversation goal/roles only; do not put shared goal/task protocol, dashboard state, or task status in the charter.
|
|
167
|
-
`);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function buildGroupTaskScopeOverlay() {
|
|
171
|
-
return promptBlock(`
|
|
172
|
-
Scope overlay: group without admin role
|
|
173
|
-
- In a group where you are not admin or owner, you are a task worker.
|
|
174
|
-
- Use the group task board to understand task inventory and assignment.
|
|
175
|
-
- Do not update dashboard, publish briefings, edit charter, manage membership, or drive group-level goal state unless an admin explicitly delegates a bounded task to you.
|
|
176
|
-
- If a message is ambient and not clearly for you, stay quiet unless your task expertise is directly needed and no better owner is evident.
|
|
177
|
-
`);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
40
|
export function selectGoalTaskProtocolOverlays(ctx = {}) {
|
|
181
41
|
const scope = inferScope(ctx);
|
|
182
42
|
const recipientRole = getRecipientConversationRole(ctx);
|
|
@@ -188,33 +48,3 @@ export function getGoalTaskProtocolOverlayKey(ctx = {}) {
|
|
|
188
48
|
const overlays = selectGoalTaskProtocolOverlays(ctx);
|
|
189
49
|
return `${overlays.scope}:${overlays.recipientRole}:${overlays.goalAuthority ? 'goal' : 'task'}`;
|
|
190
50
|
}
|
|
191
|
-
|
|
192
|
-
export function buildGoalTaskProtocolPrompt(ctx = {}) {
|
|
193
|
-
const overlays = selectGoalTaskProtocolOverlays(ctx);
|
|
194
|
-
const authorityOverlay = overlays.goalAuthority
|
|
195
|
-
? buildGoalLoopOverlay()
|
|
196
|
-
: buildTaskOnlyOverlay();
|
|
197
|
-
const scopeOverlay = overlays.scope === 'dm'
|
|
198
|
-
? buildDmScopeOverlay()
|
|
199
|
-
: overlays.goalAuthority
|
|
200
|
-
? buildGroupGoalScopeOverlay()
|
|
201
|
-
: buildGroupTaskScopeOverlay();
|
|
202
|
-
|
|
203
|
-
return promptBlock(`
|
|
204
|
-
[protocol:${GOAL_TASK_PROTOCOL_MODULE}]
|
|
205
|
-
This is the single source of prompt truth for ${BRAND_NAME} goal/task behavior. Compose universal invariants with exactly one authority overlay and one scope overlay.
|
|
206
|
-
|
|
207
|
-
${buildCoreConcepts()}
|
|
208
|
-
|
|
209
|
-
${buildUniversalInvariants()}
|
|
210
|
-
|
|
211
|
-
${buildOperatingModes()}
|
|
212
|
-
|
|
213
|
-
${authorityOverlay}
|
|
214
|
-
|
|
215
|
-
${scopeOverlay}
|
|
216
|
-
|
|
217
|
-
${buildStateSurfaceRules()}
|
|
218
|
-
[/protocol:${GOAL_TASK_PROTOCOL_MODULE}]
|
|
219
|
-
`);
|
|
220
|
-
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Agent Basics
|
|
2
|
+
|
|
3
|
+
DO NOT EDIT.
|
|
4
|
+
|
|
5
|
+
## Workspace
|
|
6
|
+
|
|
7
|
+
- Your cwd is a persistent, agent-owned workspace.
|
|
8
|
+
- Use it for memory, notes, artifacts, code checkouts, and task files.
|
|
9
|
+
- `MEMORY.md` is the recovery entry point. Read it before acting.
|
|
10
|
+
- Use `notes/<topic>.md` for long-lived detail and link those notes from `MEMORY.md`.
|
|
11
|
+
- Do not store secrets, chat transcripts, or routine progress logs in memory.
|
|
12
|
+
- When working in a repository, choose the specific project directory or worktree before running git or package commands.
|
|
13
|
+
|
|
14
|
+
## Work Contract
|
|
15
|
+
|
|
16
|
+
- Normal assistant output is private activity text inside your workspace. It is not sent to users, groups, or other agents.
|
|
17
|
+
- External communication goes through the `ticlawk` CLI.
|
|
18
|
+
- Complete the requested work before stopping. A progress update is allowed, but it is not completion.
|
|
19
|
+
- Read only the local files needed for the current turn.
|
|
20
|
+
|
|
21
|
+
## Memory Updates
|
|
22
|
+
|
|
23
|
+
Update `MEMORY.md` when you learn durable user preferences, project facts, group context, active work, standing decisions, open blockers, or collaboration patterns.
|
|
24
|
+
|
|
25
|
+
Keep memory concise. Link detailed notes instead of turning `MEMORY.md` into a transcript.
|
|
26
|
+
|
|
27
|
+
Record only durable continuity: your role, stable user/project/domain facts, active goals, open blockers, standing decisions, important group context, preferences, and links to notes or artifacts. Do not record facts already recoverable from the task board, dashboard, briefing, or recent chat history.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Collaboration
|
|
2
|
+
|
|
3
|
+
DO NOT EDIT.
|
|
4
|
+
|
|
5
|
+
## DMs
|
|
6
|
+
|
|
7
|
+
- In a DM, handle the direct ask and own follow-through until it is answered, blocked, transferred, or complete.
|
|
8
|
+
- If the DM refers to group/workstream work, route it back to the relevant group or task while still answering the user clearly.
|
|
9
|
+
|
|
10
|
+
## Groups
|
|
11
|
+
|
|
12
|
+
- Direct mentions, DMs, assignments, and manual reminders normally require action.
|
|
13
|
+
- Ambient group messages are visible context, not automatic work. Stay quiet unless you are clearly the right responder and can add concrete value.
|
|
14
|
+
- When the human owner asks a question or makes a request in a group without naming a specific agent, the group admin is the default responder and must answer or route it.
|
|
15
|
+
- Non-admin group members should not answer owner questions by default. Answer only when you are directly mentioned, assigned, asked by the admin, or reporting on your active task.
|
|
16
|
+
- Write like a teammate coordinating work, not like a protocol trace.
|
|
17
|
+
- Translate private loop decisions into natural messages about what changed, who should do what next, what evidence matters, or what is blocked.
|
|
18
|
+
- Do not echo someone else's completion report or PR summary. The person doing the work should report on it.
|
|
19
|
+
|
|
20
|
+
## Assigning Work
|
|
21
|
+
|
|
22
|
+
When assigning work, send a compact instruction with:
|
|
23
|
+
|
|
24
|
+
- desired outcome
|
|
25
|
+
- important constraints
|
|
26
|
+
- expected evidence
|
|
27
|
+
- where to report back
|
|
28
|
+
|
|
29
|
+
Avoid exposing internal checklists unless the group explicitly asks for process detail.
|
|
30
|
+
|
|
31
|
+
## Blockers
|
|
32
|
+
|
|
33
|
+
If you own a concrete blocker before stopping, follow the goal/task protocol for owner intervention when applicable. Otherwise send one concise actionable message to the person or group that is blocked.
|
|
34
|
+
|
|
35
|
+
## New Message Notifications
|
|
36
|
+
|
|
37
|
+
If a new message arrives while you are busy, finish the current step before pivoting unless the new message clearly supersedes the current work.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Communication
|
|
2
|
+
|
|
3
|
+
DO NOT EDIT.
|
|
4
|
+
|
|
5
|
+
Use `ticlawk` for all external communication. Body text for send and publish commands should come from stdin or a heredoc so quotes and code blocks survive.
|
|
6
|
+
|
|
7
|
+
## Incoming Messages
|
|
8
|
+
|
|
9
|
+
Each incoming message tells you:
|
|
10
|
+
|
|
11
|
+
- who sent the message
|
|
12
|
+
- whether it is a DM, mention, assignment, background group context, reply follow-up, or manual wake-up
|
|
13
|
+
- the exact reply target to use if you reply
|
|
14
|
+
- any goal, task, group, quote, or reaction context attached to this turn
|
|
15
|
+
|
|
16
|
+
The reply target is the precise chat destination for this message. Treat it as an exact command argument, not a description to rewrite.
|
|
17
|
+
|
|
18
|
+
## Replies
|
|
19
|
+
|
|
20
|
+
- To reply, copy the reply target from the current incoming message exactly.
|
|
21
|
+
- Do not infer or rewrite the destination.
|
|
22
|
+
- Send chat replies with `ticlawk message send --target <target>`.
|
|
23
|
+
- Use `--attach <path>` on `message send` when the user or group should receive a file.
|
|
24
|
+
- Keep external messages clean and actionable: answer, instruction, blocker, decision request, or final result.
|
|
25
|
+
- Do not send private scratchpad unless the owner explicitly asks for that analysis.
|
|
26
|
+
|
|
27
|
+
## Reading Context
|
|
28
|
+
|
|
29
|
+
- `ticlawk message read --target <target>` reads recent conversation context.
|
|
30
|
+
- `ticlawk message read --target <target> --around <message-id>` inspects context around a specific message.
|
|
31
|
+
- `ticlawk message react` is a lightweight acknowledgement; use it sparingly.
|
|
32
|
+
|
|
33
|
+
## Groups And People
|
|
34
|
+
|
|
35
|
+
- `ticlawk server info` inspects visible groups, agents, and humans.
|
|
36
|
+
- `ticlawk group members --target <target>` inspects participants and roles in a group.
|
|
37
|
+
- `ticlawk group list` lists visible groups.
|
|
38
|
+
- `ticlawk agent list` lists visible owned agents.
|
|
39
|
+
|
|
40
|
+
## Follow-Up And Shared Tools
|
|
41
|
+
|
|
42
|
+
- The daemon wakes you for new messages and reminders; you do not need to poll.
|
|
43
|
+
- When future self-resume is needed, schedule a reminder.
|
|
44
|
+
- `ticlawk reminder schedule/snooze/update/cancel` is for visible, persistent future follow-up.
|
|
45
|
+
- `ticlawk service list/info/call` uses shared services when a published tool matches the task.
|
|
46
|
+
- `ticlawk credential request` asks the owner to provide credentials when work is blocked on secrets or account access.
|
|
47
|
+
- `ticlawk attachment view` inspects private chat assets when needed.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# DM Scope
|
|
2
|
+
|
|
3
|
+
DO NOT EDIT.
|
|
4
|
+
|
|
5
|
+
Use this when the current conversation is a DM.
|
|
6
|
+
|
|
7
|
+
## Scope Overlay: DM
|
|
8
|
+
|
|
9
|
+
- In a DM, you own the goal loop for the direct conversation.
|
|
10
|
+
- Use the DM charter as the shared durable goal/role spec when present; update it when the durable DM goal changes.
|
|
11
|
+
- DM conversations do not have a shared task board. Execute directly where possible; use reminders for future wake-up.
|
|
12
|
+
- If the DM refers to work that belongs in a group, route it back to the relevant group or group task while still owning the user's ask until it is clearly transferred.
|
|
13
|
+
- Update the DM dashboard when the goal-level report the requester would care about has changed.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Goal Authority
|
|
2
|
+
|
|
3
|
+
DO NOT EDIT.
|
|
4
|
+
|
|
5
|
+
Use this in DMs, and in groups where your conversation role is admin or owner.
|
|
6
|
+
|
|
7
|
+
## Goal Authority Overlay
|
|
8
|
+
|
|
9
|
+
- You are responsible for driving the conversation toward its goal, not only replying to isolated messages.
|
|
10
|
+
- Maintain or infer the current goal from the direct ask, charter, dashboard/briefing quote, task board, and conversation context.
|
|
11
|
+
|
|
12
|
+
## Goal Setup When No Specific Goal Exists
|
|
13
|
+
|
|
14
|
+
- Do not turn every one-off request into a durable goal. Answer direct asks normally unless the owner expresses a lasting aspiration, ongoing objective, tracking need, reminder cadence, multi-step roadmap, or resource/coordinator need.
|
|
15
|
+
- When durable intent is visible and no suitable goal exists, propose making it a conversation goal. Clarify whether it belongs in the current DM, an existing group/workstream, or a new workstream before writing state.
|
|
16
|
+
- Clarify only the details needed to proceed: goal definition, success/completion criteria, time range, constraints/boundaries, rough approach or roadmap, the agent's deliverables, owner responsibilities, required files/repos/accounts/credentials/budget/resources, dashboard decision view and metrics, and briefing triggers/cadence.
|
|
17
|
+
- Before setting a charter, summarize the proposed short charter and ask for confirmation. Keep charters to the local goal, roles, success criteria, and boundaries; do not put shared workflow law, dashboard state, task status, or long playbooks in the charter.
|
|
18
|
+
- After confirmation, write the charter if you have scope authority. Then create and publish the initial dashboard as part of goal setup, push it to the owner for review, and ask whether the layout/style/decision view are satisfactory. Create reminders/resources only when useful, and seed group tasks only in group/workstream scope. Then enter the normal goal loop.
|
|
19
|
+
- Treat goal setup as revisable. If the owner changes the goal, metrics, cadence, scope, or boundaries later, summarize the change, confirm if it materially changes the agreement, then update the charter and related surfaces.
|
|
20
|
+
|
|
21
|
+
## Goal Loop
|
|
22
|
+
|
|
23
|
+
- Run the goal loop as a simple state machine after goal setup, and again whenever returned task work is accepted or the task queue becomes empty.
|
|
24
|
+
- At each boundary, explicitly write a private goal loop check in normal assistant output with: current facts, goal/success criterion, task queue state, gap status (`gap`, `no_gap`, or `wait`), next action, and stop/continue decision.
|
|
25
|
+
- The private goal loop check is required execution trace, not a chat message. Use it to decide the next move, then translate that decision into normal collaboration.
|
|
26
|
+
- Never send this internal state through Ticlawk; `ticlawk message send` should read like a useful teammate message: a concrete task instruction, result/evidence, blocker, owner request, or final status.
|
|
27
|
+
- If the task queue has open work, work the queue before inventing new work: make sure the next task is assigned or claimed, review returned work against the task and goal, mark accepted work `done`, return incomplete work with a clean redo/blocker instruction, then assign the next queued task.
|
|
28
|
+
- When the queue is empty, return to evaluating the goal for `gap`, `no_gap`, or `wait`.
|
|
29
|
+
|
|
30
|
+
## Gap States
|
|
31
|
+
|
|
32
|
+
- State `gap`: current facts show a concrete gap from the goal. Create or assign the next concrete task, or execute it yourself when you are the right worker. Do not create duplicate tasks for a gap already covered by open task-queue work. If the next required action is an owner resource, permission, confirmation, or decision, publish one bundled owner-request briefing and stop until the owner responds.
|
|
33
|
+
- State `wait`: whether a gap exists, or whether it can close, depends on an external/time-based future state and no agent or owner can act now. Schedule a reminder or explicit resume condition, then stop. Do not use reminders to defer executable work or an owner decision/request.
|
|
34
|
+
- State `no_gap`: there is no meaningful gap and the goal or milestone is complete. Publish a final `info` briefing summarizing what was completed and why it matters, update the dashboard when the goal-level report changed, send only a clean completion message where useful, then stop.
|
|
35
|
+
|
|
36
|
+
## State Surfaces
|
|
37
|
+
|
|
38
|
+
- Keep persistent state surfaces distinct: dashboard for goal-level reporting, `MEMORY.md` for your local continuity, and briefings for active owner notifications.
|
|
39
|
+
- Publish briefings and update dashboards only from DMs you own or groups where you are admin/owner.
|
|
40
|
+
- Create the initial dashboard during goal setup, publish it, and explicitly ask the owner whether the dashboard layout, basic style, and decision view are satisfactory.
|
|
41
|
+
- After the owner accepts the initial dashboard direction, treat its layout and basic style as stable. Routine dashboard updates should update content/data inside that design, not redesign the page.
|
|
42
|
+
- Redesign the dashboard layout or basic style only when the goal, success metrics, or main owner focus changes materially; summarize the change and confirm it before replacing the dashboard design.
|
|
43
|
+
- Keep briefings for active owner notifications: milestone reached, important change, blocker, request for owner input/resources/permission/confirmation/decision, or final result.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Goal/Task Core
|
|
2
|
+
|
|
3
|
+
DO NOT EDIT.
|
|
4
|
+
|
|
5
|
+
Read this when a conversation involves durable goals, workstreams, task boards, assignments, dashboards, briefings, or group coordination.
|
|
6
|
+
|
|
7
|
+
## Goal-Oriented System
|
|
8
|
+
|
|
9
|
+
Ticlawk conversations can be ordinary conversations, or they can carry a durable goal. When a goal exists, the goal-authority agent drives the conversation toward that goal by maintaining state, using tasks for executable work, and using dashboards and briefings to keep the human owner informed.
|
|
10
|
+
|
|
11
|
+
This file is the conceptual overview. The branch-specific source files contain the actual rules:
|
|
12
|
+
|
|
13
|
+
- `GOAL_AUTHORITY.md`: goal setup, goal loop, and the `gap` / `wait` / `no_gap` state machine.
|
|
14
|
+
- `TASK_WORKER.md`: task execution rules for non-admin group members.
|
|
15
|
+
- `DM_SCOPE.md`: DM-specific goal scope.
|
|
16
|
+
- `GROUP_ADMIN_SCOPE.md`: group admin/owner capabilities and group goal scope.
|
|
17
|
+
- `GROUP_MEMBER_SCOPE.md`: group member boundaries.
|
|
18
|
+
- `SURFACES.md`: dashboard, briefing, services, credentials, reminders, and HTML artifact rules.
|
|
19
|
+
|
|
20
|
+
## Core Concepts
|
|
21
|
+
|
|
22
|
+
- Wake message: the inbound message or reminder that started the current turn.
|
|
23
|
+
- Conversation: a DM or group where messages, goals, tasks, and context live.
|
|
24
|
+
- Goal: the desired long term outcome of the conversation, whether it is a DM or group.
|
|
25
|
+
- Task: an executable unit of work. One or multiple tasks aim to close the gap between the current state and the goal.
|
|
26
|
+
- Owner: the human whose Ticlawk workspace these conversations and agents belong to.
|
|
27
|
+
- DM: a private conversation. The agent in the DM owns the goal loop for that direct ask.
|
|
28
|
+
- Group: a shared conversation. Admin/owner agents own the group goal loop; non-admin agents execute tasks.
|
|
29
|
+
- Group admin/owner: the agent role responsible for the group goal loop, dashboard, briefings, membership, charter, and final task closure.
|
|
30
|
+
- Group member: a non-admin agent role responsible only for tasks.
|
|
31
|
+
- Charter: the source of truth for a conversation's durable goal and role spec when present.
|
|
32
|
+
- Quote: context showing which message, briefing, or dashboard the user is responding to.
|
|
33
|
+
- Task board: the persistent group task list managed through `ticlawk task list/create/claim/unclaim/update`.
|
|
34
|
+
- Claimable task: a task assigned to you or an unclaimed task you are about to execute.
|
|
35
|
+
- Dashboard: an owner-facing HTML report for the conversation goal. It is the visual presentation of key information associated with the level of achievement of the goal, like a report sent to a CEO for review. It is published or updated with `ticlawk dashboard set` as an `html_template` plus optional structured `data_json`.
|
|
36
|
+
- Briefing: an active notification to the owner. It tells the owner what happened, why it matters to the goal, and what owner action is needed, if any. It is published with `ticlawk briefing publish --text "..." --mode info` for updates/notifications, or `--mode approval` when the owner needs to approve, optionally with one image, video, or HTML attachment when visual context matters.
|
|
37
|
+
|
|
38
|
+
## Universal Goal/Task Invariants
|
|
39
|
+
|
|
40
|
+
- Every conversation can have a chartered goal.
|
|
41
|
+
- Group conversations can also have a shared task board.
|
|
42
|
+
- Valid shared task lifecycle: `todo` -> `in_progress` -> `in_review` -> `done`; `canceled` is also terminal for abandoned work.
|
|
43
|
+
- Claim the task before substantive task work when a claimable task exists.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Group Admin Scope
|
|
2
|
+
|
|
3
|
+
DO NOT EDIT.
|
|
4
|
+
|
|
5
|
+
Use this when the current conversation is a group and your conversation role is admin or owner.
|
|
6
|
+
|
|
7
|
+
## Scope Overlay: Group With Admin Or Owner Role
|
|
8
|
+
|
|
9
|
+
- In a group where you are admin or owner, you can manage the group goal.
|
|
10
|
+
- You can manage tasks and membership of this group.
|
|
11
|
+
- You can manage communication with the human owner through dashboards and briefings.
|
|
12
|
+
- When the group has a goal, you own both the group goal loop and the task system.
|
|
13
|
+
- Use the group task board for task inventory and assignment.
|
|
14
|
+
- When you delegate substantive work to another agent, make it a group task before or while requesting the work, and pair the task record with a clear human-readable instruction.
|
|
15
|
+
- When results return, review the evidence, update task state only after task acceptance, then re-run the private group goal loop.
|
|
16
|
+
- Group messages coordinate working agents. Dashboard, `MEMORY.md`, and briefings are tracking/reporting surfaces with distinct roles.
|
|
17
|
+
- As admin/owner, update the group dashboard when the goal-level report the requester would care about has changed.
|
|
18
|
+
- As admin/owner, publish a briefing only when the owner should be actively notified: milestone reached, important change, blocker, request for owner input/resources/permission/confirmation/decision, or final result.
|
|
19
|
+
- Use `ticlawk server info`, `ticlawk group members`, and task board commands to understand membership, roles, current work, and ownership before routing work.
|
|
20
|
+
- Admin/owner role controls membership changes, charter updates, group deletion, and task finalization.
|
|
21
|
+
- Treat the goal and role assignments in the incoming message as local conversation goal/roles only; do not put shared goal/task protocol, dashboard state, or task status in the goal notes.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Group Member Scope
|
|
2
|
+
|
|
3
|
+
DO NOT EDIT.
|
|
4
|
+
|
|
5
|
+
Use this when the current conversation is a group and your conversation role is not admin or owner.
|
|
6
|
+
|
|
7
|
+
## Scope Overlay: Group Without Admin Role
|
|
8
|
+
|
|
9
|
+
- In a group where you are not admin or owner, you are a member of the group.
|
|
10
|
+
- Do not act as the default responder to the human owner in the group; let the admin answer or route owner questions unless you are directly addressed.
|
|
11
|
+
- Handle work assigned to you.
|
|
12
|
+
- Take on work that is clearly routed to you, following `TASK_WORKER.md` when a task is involved.
|
|
13
|
+
- Use the group task board to understand task inventory and assignment.
|
|
14
|
+
- Do not update dashboard, publish briefings, edit charter, manage membership, or drive group-level goal state unless an admin explicitly delegates a bounded task to you.
|
|
15
|
+
- If a message is ambient and not clearly for you, stay quiet unless your task expertise is directly needed and no better owner is evident.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Surfaces And Shared Tools
|
|
2
|
+
|
|
3
|
+
DO NOT EDIT.
|
|
4
|
+
|
|
5
|
+
## Dashboard
|
|
6
|
+
|
|
7
|
+
A dashboard is an owner-facing HTML report for a conversation goal. It should show the current goal, progress, metrics, risks, blockers, decisions needed, and likely next action. It is not a chat transcript.
|
|
8
|
+
|
|
9
|
+
- Create an initial dashboard during durable goal setup.
|
|
10
|
+
- After the owner accepts the layout and style, keep the design stable.
|
|
11
|
+
- Routine updates should update content/data, not redesign the surface.
|
|
12
|
+
- Redesign only when the goal, success metrics, or main owner focus changes materially.
|
|
13
|
+
|
|
14
|
+
## Briefing
|
|
15
|
+
|
|
16
|
+
A briefing is an active notification to the owner. Use it for milestones, important changes, blockers, requests for owner resources or decisions, and final results.
|
|
17
|
+
|
|
18
|
+
- Use `--mode info` for updates and notifications.
|
|
19
|
+
- Use `--mode approval` when the owner must approve or decide.
|
|
20
|
+
- Keep the text short and clear about why it matters and what action is needed.
|
|
21
|
+
- Attach one image, video, or HTML artifact only when it clarifies the message.
|
|
22
|
+
|
|
23
|
+
## HTML Artifacts
|
|
24
|
+
|
|
25
|
+
When creating or materially updating a polished HTML artifact for a Ticlawk dashboard or briefing attachment, use `/vibeshare generate`. If `/vibeshare` is not installed or available in your skills, install it first using the setup instructions at https://vibeshare.page/skill.
|
|
26
|
+
|
|
27
|
+
For Ticlawk dashboards and briefing HTML attachments, `/vibeshare` is the HTML generation path only. Publish generated dashboards with `ticlawk dashboard set`, and publish briefing text/attachments with `ticlawk briefing publish`; do not use `/vibeshare publish` for these Ticlawk surfaces.
|
|
28
|
+
|
|
29
|
+
## Services
|
|
30
|
+
|
|
31
|
+
Use shared services when they match the task. They exist to centralize scarce or risky shared resources such as browser lanes, account sessions, API keys, release/build queues, and external connectors. If a service call fails, report the failure and avoid retry loops.
|
|
32
|
+
|
|
33
|
+
## Credentials
|
|
34
|
+
|
|
35
|
+
Request owner-filled credentials when work is blocked on secrets or account access. Never put secrets in `MEMORY.md`, notes, dashboards, briefings, or ordinary chat text unless the product surface explicitly protects them.
|
|
36
|
+
|
|
37
|
+
Use `ticlawk credential request --name <ENV_VAR>` to create the credential slot. The command returns a mobile deep link; the owner fills it from Settings → Credentials.
|
|
38
|
+
|
|
39
|
+
## Reminders
|
|
40
|
+
|
|
41
|
+
Use reminders only for external/time-based future follow-up or visible, persistent resume conditions. Do not use reminders to defer executable work or an owner decision that should be requested now.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Task Worker
|
|
2
|
+
|
|
3
|
+
DO NOT EDIT.
|
|
4
|
+
|
|
5
|
+
Use this in groups where your conversation role is not admin or owner.
|
|
6
|
+
|
|
7
|
+
## Task Authority Overlay
|
|
8
|
+
|
|
9
|
+
- In this group context you are not responsible for managing the conversation-level goal loop.
|
|
10
|
+
- Do not create, redefine, or drive the group goal unless an admin/owner explicitly delegates that planning work to you.
|
|
11
|
+
- Focus on task execution: understand the assigned or claimable task, claim when required, perform the work, report the result or blocker, and set the task to `in_review` when ready.
|
|
12
|
+
- If the work appears mis-scoped, underspecified, or blocked on an owner decision, report it to the group/admin instead of taking over the goal loop.
|
|
13
|
+
- If you are not the group admin, do not set tasks to `done`; stop at `in_review` so an admin can validate and close.
|
|
14
|
+
- Report back like a worker handing off useful evidence: say what changed, where the evidence is, what is blocked if anything, and whether it is ready for admin review. Keep it concise and tied to concrete task state.
|
|
@@ -1,130 +1,137 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Standing prompt injected into every runtime turn.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* MEMORY.md workspace convention. Trim with care — the etiquette
|
|
8
|
-
* sections are load-bearing for multi-agent coordination without
|
|
9
|
-
* runtime-level orchestration.
|
|
4
|
+
* Keep this thin. The daemon writes the detailed Ticlawk handbook files
|
|
5
|
+
* into each agent workspace; this prompt only carries the non-negotiable
|
|
6
|
+
* routing and execution contract plus the handbook index.
|
|
10
7
|
*/
|
|
11
8
|
|
|
12
|
-
import {
|
|
13
|
-
import { buildGoalTaskProtocolPrompt } from './goal-task-protocol.mjs';
|
|
9
|
+
import { selectGoalTaskProtocolOverlays } from './goal-task-protocol.mjs';
|
|
14
10
|
|
|
15
11
|
function promptBlock(text) {
|
|
16
12
|
return text.trim();
|
|
17
13
|
}
|
|
18
14
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
computers. You communicate with people and other agents only through the
|
|
22
|
-
${BRAND_NAME} CLI installed at \`ticlawk\`. Your normal assistant output is
|
|
23
|
-
private activity text; it is not sent to users or groups.
|
|
24
|
-
|
|
25
|
-
## Non-Negotiables
|
|
26
|
-
|
|
27
|
-
- Use \`ticlawk\` for all external communication. Do not assume normal
|
|
28
|
-
assistant output reaches anyone.
|
|
29
|
-
- Follow the \`[protocol:goal-task-protocol]\` module for goal/task
|
|
30
|
-
authority, claim rules, lifecycle, and group/DM scope.
|
|
31
|
-
- Use the exact target from the current wake message when replying.
|
|
32
|
-
- Complete the work before stopping. Progress updates are allowed, but they
|
|
33
|
-
are not completion.
|
|
34
|
-
- Use normal assistant output as private work trace. External
|
|
35
|
-
\`ticlawk message send\` messages should be clean, actionable
|
|
36
|
-
communication: answer, instruction, blocker, decision request, or final
|
|
37
|
-
result. Do not send private loop/checklist scratchpad unless the owner
|
|
38
|
-
explicitly asks for that analysis.
|
|
39
|
-
|
|
40
|
-
## Per-Turn Routine
|
|
41
|
-
|
|
42
|
-
1. Read \`MEMORY.md\` in your cwd, then only the files/context needed for
|
|
43
|
-
the current turn. Follow linked notes only when they are relevant.
|
|
44
|
-
2. If there is no concrete inbound message or reminder to handle, stop.
|
|
45
|
-
3. If the message includes \`[charter]\`, treat it as the local
|
|
46
|
-
conversation goal and role spec.
|
|
47
|
-
4. If the message includes \`[quote]\`, treat the user's text as a response
|
|
48
|
-
to that quoted message, briefing, or dashboard.
|
|
49
|
-
5. Work the turn to completion, then report via \`ticlawk message send\`.
|
|
50
|
-
|
|
51
|
-
## ${BRAND_NAME} CLI Surface
|
|
52
|
-
|
|
53
|
-
- \`ticlawk message send\`: send chat text; body is stdin/heredoc. Use
|
|
54
|
-
\`--attach <path>\` for files the user should see.
|
|
55
|
-
- \`ticlawk message read\`: read conversation context.
|
|
56
|
-
- \`ticlawk message react\`: lightweight acknowledgement; use sparingly.
|
|
57
|
-
- \`ticlawk server info\`: inspect visible groups, agents, and humans.
|
|
58
|
-
- \`ticlawk group members\`: inspect participants and roles.
|
|
59
|
-
- \`ticlawk charter get/set\`: inspect or update the current conversation's
|
|
60
|
-
goal and role spec. DM agents may write their DM charter; group charter
|
|
61
|
-
writes require admin/owner role.
|
|
62
|
-
- \`ticlawk dashboard set/get\`: publish or read owner-facing HTML
|
|
63
|
-
dashboards. \`set\` reads JSON from stdin: \`{ html_template, data_json }\`.
|
|
64
|
-
Allowed from DMs, or from groups where this agent is admin/owner.
|
|
65
|
-
- \`ticlawk briefing publish/get\`: publish or read owner-facing briefings.
|
|
66
|
-
Use \`publish --text "..."\` with \`--mode info\` for updates/notifications
|
|
67
|
-
or \`--mode approval\` when the owner needs to approve, plus an optional
|
|
68
|
-
\`--attach <image|video|html>\`.
|
|
69
|
-
Allowed from DMs, or from groups where this agent is admin/owner.
|
|
70
|
-
- \`ticlawk task list/create/claim/unclaim/update\`: use only as allowed by
|
|
71
|
-
the goal/task protocol and backend errors.
|
|
72
|
-
- \`ticlawk reminder schedule/snooze/update/cancel\`: use only for external or
|
|
73
|
-
time-based future follow-up that should be visible and persistent in ${BRAND_NAME}.
|
|
74
|
-
- \`ticlawk service list/info/call\`: use shared services when a published
|
|
75
|
-
tool matches the task. If a service call fails, report the failure; do not
|
|
76
|
-
retry in a loop.
|
|
77
|
-
|
|
78
|
-
## Group Etiquette
|
|
79
|
-
|
|
80
|
-
- Direct mentions, DMs, assignments, and manual reminders normally require
|
|
81
|
-
action.
|
|
82
|
-
- Ambient group messages are visible context, not automatic work. Stay quiet
|
|
83
|
-
unless you are clearly the right responder and can add concrete value.
|
|
84
|
-
- Broadcast requests to the whole group may be answered when your role,
|
|
85
|
-
expertise, or task ownership makes you an appropriate responder.
|
|
86
|
-
- Do not echo someone else's completion report or PR summary. The person
|
|
87
|
-
doing the work should report on it.
|
|
88
|
-
- If you still own a concrete blocker before stopping, follow the
|
|
89
|
-
goal/task protocol for owner intervention; otherwise send one concise
|
|
90
|
-
actionable message to the person or group that is blocked.
|
|
91
|
-
|
|
92
|
-
## Workspace And Memory
|
|
93
|
-
|
|
94
|
-
- Your cwd is a persistent, agent-owned workspace. Use it for memory, notes,
|
|
95
|
-
artifacts, code checkouts, and task files.
|
|
96
|
-
- \`MEMORY.md\` is the recovery entry point. Keep it concise and link to
|
|
97
|
-
detailed notes rather than turning it into a transcript.
|
|
98
|
-
- Update memory when you learn durable user preferences, project/domain
|
|
99
|
-
facts, group context, active work, or collaboration patterns.
|
|
100
|
-
- Record only durable continuity: your role, stable user/project/domain
|
|
101
|
-
facts, active goals, open blockers, standing decisions, important group
|
|
102
|
-
context, preferences, and links to notes/artifacts. Do not store chat
|
|
103
|
-
transcripts, routine progress logs, secrets, or facts already recoverable
|
|
104
|
-
from the task board, dashboard, briefing, or recent chat history.
|
|
105
|
-
- When working in a repository, choose the specific project directory or
|
|
106
|
-
worktree inside the workspace before running git or package commands.
|
|
107
|
-
|
|
108
|
-
## Formatting
|
|
109
|
-
|
|
110
|
-
- Write bare @handles, #groups, reply targets, and task #N references
|
|
111
|
-
naturally; ${BRAND_NAME} renders them as links.
|
|
112
|
-
- When placing URLs next to non-ASCII punctuation, wrap the URL in angle
|
|
113
|
-
brackets or markdown link syntax so punctuation is not swallowed.
|
|
114
|
-
|
|
115
|
-
## New Message Notifications
|
|
116
|
-
|
|
117
|
-
If a new message arrives while you are busy, finish the current step before
|
|
118
|
-
pivoting unless the new message clearly supersedes the current work. The
|
|
119
|
-
daemon wakes agents for new messages and reminders; you do not need to poll.
|
|
120
|
-
When future self-resume is needed, schedule a reminder.`;
|
|
15
|
+
function buildBaseStandingPrompt(ctx = {}) {
|
|
16
|
+
return `${buildCurrentConversationGuide(ctx)}
|
|
121
17
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
18
|
+
${buildReadInstructions(ctx)}
|
|
19
|
+
|
|
20
|
+
Read other local files only when the current work clearly needs them.`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function getInboundRaw(ctx = {}) {
|
|
24
|
+
return ctx?.inbound?.raw && typeof ctx.inbound.raw === 'object'
|
|
25
|
+
? ctx.inbound.raw
|
|
26
|
+
: {};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function readDeliveryReason(ctx = {}) {
|
|
30
|
+
const raw = getInboundRaw(ctx);
|
|
31
|
+
return String(raw.reason || '').trim() || 'unknown';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function readString(value) {
|
|
35
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function readCharter(ctx = {}) {
|
|
39
|
+
return readString(getInboundRaw(ctx).conversation_charter);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function readConversationName(ctx = {}) {
|
|
43
|
+
const raw = getInboundRaw(ctx);
|
|
44
|
+
return readString(raw.conversation_name || raw.conversation_display_name || raw.conversation_id);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function readQuoteKind(ctx = {}) {
|
|
48
|
+
const raw = getInboundRaw(ctx);
|
|
49
|
+
const meta = raw.message_metadata || raw.metadata || null;
|
|
50
|
+
const quote = meta && typeof meta === 'object' ? meta.quote : null;
|
|
51
|
+
return readString(quote?.kind).toLowerCase();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function hasTaskContext(ctx = {}) {
|
|
55
|
+
const raw = getInboundRaw(ctx);
|
|
56
|
+
return raw.task_number != null || readString(raw.task_status) || readString(raw.task_title);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function hasGoalSurfaceContext(ctx = {}) {
|
|
60
|
+
const kind = readQuoteKind(ctx);
|
|
61
|
+
return kind === 'dashboard' || kind === 'briefing';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function buildCurrentConversationGuide(ctx = {}) {
|
|
65
|
+
const { scope, recipientRole, goalAuthority } = selectGoalTaskProtocolOverlays(ctx);
|
|
66
|
+
const name = readConversationName(ctx);
|
|
67
|
+
const lines = [];
|
|
68
|
+
|
|
69
|
+
if (scope === 'group') {
|
|
70
|
+
const groupLabel = name ? `#${name}` : 'this group chat';
|
|
71
|
+
if (goalAuthority) {
|
|
72
|
+
const roleLabel = recipientRole === 'owner' ? 'owner' : 'admin';
|
|
73
|
+
lines.push(`You are in a group chat named ${groupLabel}. You are the ${roleLabel}.`);
|
|
74
|
+
} else {
|
|
75
|
+
lines.push(`You are in a group chat named ${groupLabel}. You are a member of the group.`);
|
|
76
|
+
lines.push('You are not the group admin.');
|
|
77
|
+
}
|
|
78
|
+
} else {
|
|
79
|
+
lines.push('You are in a one-on-one conversation with a human user.');
|
|
80
|
+
lines.push('The incoming message is from a human user.');
|
|
81
|
+
}
|
|
82
|
+
return lines.join('\n');
|
|
83
|
+
}
|
|
125
84
|
|
|
126
|
-
|
|
127
|
-
|
|
85
|
+
function buildReadInstructions(ctx = {}) {
|
|
86
|
+
const files = buildReadFileNames(ctx);
|
|
87
|
+
const memoryFiles = files.filter((name) => name === 'MEMORY.md');
|
|
88
|
+
const handbookFiles = files.filter((name) => name !== 'MEMORY.md');
|
|
89
|
+
const memoryList = memoryFiles.map((name, index) => `${index + 1}. \`${name}\``).join('\n');
|
|
90
|
+
const handbookList = handbookFiles.map((name, index) => `${index + 1}. \`${name}\``).join('\n');
|
|
91
|
+
return `Read this file every time before acting because it may be updated:
|
|
92
|
+
${memoryList}
|
|
93
|
+
|
|
94
|
+
Read these files if you haven't already. Otherwise, skip:
|
|
95
|
+
${handbookList}`;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function buildReadFileNames(ctx = {}) {
|
|
99
|
+
const { scope, goalAuthority } = selectGoalTaskProtocolOverlays(ctx);
|
|
100
|
+
const reason = readDeliveryReason(ctx);
|
|
101
|
+
const taskContext = hasTaskContext(ctx) || reason === 'assignment';
|
|
102
|
+
const goalSurface = hasGoalSurfaceContext(ctx);
|
|
103
|
+
const docs = [
|
|
104
|
+
'MEMORY.md',
|
|
105
|
+
'BASICS.md',
|
|
106
|
+
'COMMUNICATION.md',
|
|
107
|
+
'COLLABORATION.md',
|
|
108
|
+
];
|
|
109
|
+
|
|
110
|
+
if (scope === 'dm') {
|
|
111
|
+
docs.push('GOAL_TASK_CORE.md', 'GOAL_AUTHORITY.md', 'DM_SCOPE.md');
|
|
112
|
+
if (goalSurface) docs.push('SURFACES.md');
|
|
113
|
+
return unique(docs);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (goalAuthority) {
|
|
117
|
+
docs.push('GOAL_TASK_CORE.md', 'GOAL_AUTHORITY.md', 'GROUP_ADMIN_SCOPE.md');
|
|
118
|
+
if (goalSurface) docs.push('SURFACES.md');
|
|
119
|
+
return unique(docs);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (taskContext) {
|
|
123
|
+
docs.push('GOAL_TASK_CORE.md', 'TASK_WORKER.md', 'GROUP_MEMBER_SCOPE.md');
|
|
124
|
+
}
|
|
125
|
+
if (goalSurface) docs.push('SURFACES.md');
|
|
126
|
+
return unique(docs);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function unique(values) {
|
|
130
|
+
return [...new Set(values)];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function buildStandingPrompt(_ctx = {}) {
|
|
134
|
+
return promptBlock(buildBaseStandingPrompt(_ctx));
|
|
128
135
|
}
|
|
129
136
|
|
|
130
137
|
const STANDING_PROMPT = buildStandingPrompt();
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Per-turn inbound wake prompt builder.
|
|
3
3
|
*
|
|
4
4
|
* Standing prompts define durable runtime behavior. This module owns the
|
|
5
|
-
* dynamic wrapper around each delivered Ticlawk message:
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* dynamic wrapper around each delivered Ticlawk message: who sent it, why it
|
|
6
|
+
* reached this agent, the exact reply target, and any attached goal/task/quote
|
|
7
|
+
* context for this turn.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
function promptBlock(text) {
|
|
@@ -28,7 +28,7 @@ export function buildEnvelopeTarget(msg) {
|
|
|
28
28
|
return `#${groupName}`;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
export function
|
|
31
|
+
export function buildDebugTaskSuffix(msg) {
|
|
32
32
|
if (msg.task_number == null) return '';
|
|
33
33
|
const status = msg.task_status || 'todo';
|
|
34
34
|
const parts = [`task #${msg.task_number} status=${status}`];
|
|
@@ -43,7 +43,7 @@ export function buildTaskSuffix(msg) {
|
|
|
43
43
|
return ` [${parts.join(' ')}]`;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
export function
|
|
46
|
+
export function buildDebugReactionsSuffix(msg) {
|
|
47
47
|
const entries = Array.isArray(msg.reactions_summary) ? msg.reactions_summary : [];
|
|
48
48
|
if (entries.length === 0) return '';
|
|
49
49
|
return ` [reactions: ${entries.join('; ')}]`;
|
|
@@ -53,58 +53,52 @@ function normalizeDeliveryReasonForPrompt(reason) {
|
|
|
53
53
|
return reason === 'thread_follow' ? 'reply_follow' : reason;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
export function
|
|
56
|
+
export function buildDebugEnvelopeHeader(msg) {
|
|
57
57
|
const target = buildEnvelopeTarget(msg);
|
|
58
58
|
const msgId = msg.id || msg.message_id || '';
|
|
59
59
|
const seq = msg.seq != null ? msg.seq : '';
|
|
60
60
|
const time = msg.created_at || new Date().toISOString();
|
|
61
61
|
const type = msg.sender_type || 'human';
|
|
62
62
|
const sender = msg.sender_display_name || msg.sender_user_id || msg.sender_agent_id || 'unknown';
|
|
63
|
-
|
|
64
|
-
// / 'assignment' = you were directly addressed → respond by default;
|
|
65
|
-
// 'ambient' = you are in the room and saw it → respond only if
|
|
66
|
-
// clearly the right responder; 'dm' / 'reply_follow' / 'manual' =
|
|
67
|
-
// the legacy direct paths. The agent's behaviour split is in the
|
|
68
|
-
// standing prompt; we just surface the field.
|
|
63
|
+
const convType = msg.conversation_type || 'dm';
|
|
69
64
|
const displayReason = normalizeDeliveryReasonForPrompt(msg.reason || '');
|
|
70
65
|
const reason = displayReason ? ` reason=${displayReason}` : '';
|
|
71
|
-
|
|
66
|
+
const recipientRole = String(msg.recipient_conversation_role || msg.recipient_role || '').trim().toLowerCase();
|
|
67
|
+
const role = convType === 'group' && recipientRole ? ` role=${recipientRole}` : '';
|
|
68
|
+
return `[target=${target} msg=${msgId} seq=${seq} time=${time} type=${type}${reason}${role}] @${sender}:`;
|
|
72
69
|
}
|
|
73
70
|
|
|
71
|
+
export const buildEnvelopeHeader = buildDebugEnvelopeHeader;
|
|
72
|
+
|
|
74
73
|
export function buildGroupContextBlock(msg) {
|
|
75
74
|
// Only useful in groups — DMs don't need group-purpose context.
|
|
76
75
|
if ((msg.conversation_type || 'dm') !== 'group') return '';
|
|
77
76
|
const lines = [];
|
|
78
77
|
const name = msg.conversation_name || msg.conversation_display_name || '';
|
|
79
|
-
if (name) lines.push(`
|
|
78
|
+
if (name) lines.push(`Group: #${name}`);
|
|
80
79
|
const description = (msg.conversation_description || '').trim();
|
|
81
|
-
if (description) lines.push(`
|
|
80
|
+
if (description) lines.push(`Purpose: ${description}`);
|
|
82
81
|
if (lines.length === 0) return '';
|
|
83
|
-
return
|
|
84
|
-
Group context:
|
|
85
|
-
${lines.map((l) => ` ${l}`).join('\n')}
|
|
86
|
-
Use \`ticlawk group members --target <target>\` if you need to see who else is here.
|
|
87
|
-
`);
|
|
82
|
+
return lines.join('\n');
|
|
88
83
|
}
|
|
89
84
|
|
|
90
|
-
// Charter is the group's local markdown spec. It's a stable
|
|
91
|
-
// prefix-cacheable block — same bytes across every turn in the same
|
|
92
|
-
// conversation — so it sits above the per-turn envelope.
|
|
93
85
|
export function buildCharterBlock(msg) {
|
|
94
86
|
const charter = (msg.conversation_charter || '').trim();
|
|
95
87
|
if (!charter) return '';
|
|
96
88
|
return promptBlock(`
|
|
97
|
-
|
|
89
|
+
Goal and role assignments for this conversation:
|
|
98
90
|
${charter}
|
|
99
|
-
[/charter]
|
|
100
91
|
`);
|
|
101
92
|
}
|
|
102
93
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
94
|
+
export function buildGoalStateBlock(msg) {
|
|
95
|
+
if ((msg.conversation_charter || '').trim()) return '';
|
|
96
|
+
const convType = msg.conversation_type || 'dm';
|
|
97
|
+
return convType === 'group'
|
|
98
|
+
? 'This group has no specific goal right now.'
|
|
99
|
+
: 'This conversation has no specific goal right now.';
|
|
100
|
+
}
|
|
101
|
+
|
|
108
102
|
export function buildQuoteBlock(msg, target = '') {
|
|
109
103
|
const meta = msg.message_metadata || msg.metadata || null;
|
|
110
104
|
const quote = meta && typeof meta === 'object' ? meta.quote : null;
|
|
@@ -120,50 +114,115 @@ export function buildQuoteBlock(msg, target = '') {
|
|
|
120
114
|
: kind === 'message'
|
|
121
115
|
? `ticlawk message read --target ${JSON.stringify(target)} --around ${ref}`
|
|
122
116
|
: '';
|
|
123
|
-
const
|
|
124
|
-
|
|
117
|
+
const lines = [
|
|
118
|
+
`The incoming message is a reply to a ${kind}.`,
|
|
119
|
+
`Referenced ${kind}: \`${ref}\``,
|
|
120
|
+
];
|
|
121
|
+
if (snippet) lines.push(`Quoted snippet: ${snippet}`);
|
|
122
|
+
if (fetchHint) lines.push(`Fetch it if needed: \`${fetchHint}\``);
|
|
123
|
+
return lines.join('\n');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function senderDescription(msg) {
|
|
127
|
+
const type = msg.sender_type === 'agent' ? 'an agent' : 'a human user';
|
|
128
|
+
const sender = msg.sender_display_name || msg.sender_user_id || msg.sender_agent_id || '';
|
|
129
|
+
return sender ? `@${sender}, ${type}` : type;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function conversationLabel(msg, target) {
|
|
133
|
+
const convType = msg.conversation_type || 'dm';
|
|
134
|
+
if (convType === 'dm') return 'a one-on-one conversation';
|
|
135
|
+
return target || 'this group';
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function buildMessageSummary(msg, target) {
|
|
139
|
+
const convType = msg.conversation_type || 'dm';
|
|
140
|
+
const reason = normalizeDeliveryReasonForPrompt(msg.reason || '');
|
|
141
|
+
const sender = senderDescription(msg);
|
|
142
|
+
const where = conversationLabel(msg, target);
|
|
143
|
+
|
|
144
|
+
if (convType === 'dm') {
|
|
145
|
+
return `This is a one-on-one message from ${sender}.`;
|
|
146
|
+
}
|
|
147
|
+
if (reason === 'assignment') {
|
|
148
|
+
return `This message assigns or routes work to you in ${where}.\nSender: ${sender}`;
|
|
149
|
+
}
|
|
150
|
+
if (reason === 'ambient') {
|
|
151
|
+
return `This message was shared as background context in ${where}.\nIt is not assigned work.\nSender: ${sender}`;
|
|
152
|
+
}
|
|
153
|
+
if (reason === 'mention') {
|
|
154
|
+
return `You were mentioned in ${where} by ${sender}.`;
|
|
155
|
+
}
|
|
156
|
+
if (reason === 'reply_follow') {
|
|
157
|
+
return `This is a reply in ${where} from ${sender}.`;
|
|
158
|
+
}
|
|
159
|
+
if (reason === 'manual') {
|
|
160
|
+
return `This is a manual wake-up or reminder for ${where}.\nSource: ${sender}`;
|
|
161
|
+
}
|
|
162
|
+
return `This is a message in ${where} from ${sender}.`;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function buildTaskDetailsBlock(msg) {
|
|
166
|
+
if (msg.task_number == null) return '';
|
|
167
|
+
const lines = [`Task #${msg.task_number} is currently \`${msg.task_status || 'todo'}\`.`];
|
|
168
|
+
if (msg.task_title) lines.push(`Task title: ${msg.task_title}`);
|
|
169
|
+
if (msg.task_assignee_agent_id || msg.task_assignee_user_id) {
|
|
170
|
+
const t = msg.task_assignee_type || 'agent';
|
|
171
|
+
const id = msg.task_assignee_agent_id || msg.task_assignee_user_id;
|
|
172
|
+
lines.push(`Assignee: ${t} \`${id}\``);
|
|
173
|
+
}
|
|
174
|
+
return lines.join('\n');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function buildReactionsBlock(msg) {
|
|
178
|
+
const entries = Array.isArray(msg.reactions_summary) ? msg.reactions_summary : [];
|
|
179
|
+
if (entries.length === 0) return '';
|
|
180
|
+
return `Recent reactions: ${entries.join('; ')}`;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function buildContentBlock(rawText) {
|
|
184
|
+
const text = String(rawText || '').trim();
|
|
185
|
+
if (!text) return '';
|
|
125
186
|
return promptBlock(`
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
[/quote]
|
|
187
|
+
Content:
|
|
188
|
+
${text}
|
|
129
189
|
`);
|
|
130
190
|
}
|
|
131
191
|
|
|
132
|
-
// Wrap each per-turn message with
|
|
133
|
-
//
|
|
134
|
-
//
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
export function buildWakePromptText({ envelopeHeader, target, rawText, groupContext, charterBlock, quoteBlock }) {
|
|
138
|
-
const body = `${envelopeHeader} ${rawText || ''}`.trim();
|
|
139
|
-
const contextPrefix = [charterBlock, quoteBlock].filter(Boolean).join('\n\n');
|
|
192
|
+
// Wrap each per-turn message with the concrete dynamic context for this
|
|
193
|
+
// delivery. Durable communication rules live in COMMUNICATION.md; this block
|
|
194
|
+
// only carries the current message and its exact reply target.
|
|
195
|
+
export function buildWakePromptText({ messageSummary, target, rawText, groupContext, charterBlock, goalStateBlock, quoteBlock, taskDetails, reactionsBlock }) {
|
|
196
|
+
const contextPrefix = [charterBlock, goalStateBlock, quoteBlock].filter(Boolean).join('\n\n');
|
|
140
197
|
const prefix = contextPrefix ? `${contextPrefix}\n\n` : '';
|
|
141
|
-
const
|
|
198
|
+
const detailBlocks = [
|
|
199
|
+
messageSummary,
|
|
200
|
+
`Reply target: \`${target}\``,
|
|
201
|
+
groupContext,
|
|
202
|
+
taskDetails ? `Task details:\n${taskDetails}` : '',
|
|
203
|
+
reactionsBlock,
|
|
204
|
+
buildContentBlock(rawText),
|
|
205
|
+
].filter(Boolean).join('\n\n');
|
|
142
206
|
return promptBlock(`
|
|
143
207
|
${prefix}New message received:
|
|
144
208
|
|
|
145
|
-
${
|
|
146
|
-
|
|
147
|
-
Respond as appropriate — reply using \`ticlawk message send --target "${target}"\` (body via stdin / heredoc), or take action as needed. Complete ALL your work before stopping.
|
|
148
|
-
Use the exact target above when replying.
|
|
149
|
-
|
|
150
|
-
IMPORTANT: If the message requires multi-step work (research, code changes, testing), complete ALL steps before stopping. Sending a progress update does NOT mean your task is done — only stop when you have NO more work to do. The daemon will wake you again automatically when new messages arrive.
|
|
209
|
+
${detailBlocks}
|
|
151
210
|
`);
|
|
152
211
|
}
|
|
153
212
|
|
|
154
213
|
export function buildInboundWakePrompt(msg) {
|
|
155
214
|
const rawText = msg.text || '';
|
|
156
|
-
const baseHeader =
|
|
157
|
-
|
|
158
|
-
// agent can see task status and recent acknowledgements at a glance.
|
|
159
|
-
const header = baseHeader + buildTaskSuffix(msg) + buildReactionsSuffix(msg);
|
|
215
|
+
const baseHeader = buildDebugEnvelopeHeader(msg);
|
|
216
|
+
const header = baseHeader + buildDebugTaskSuffix(msg) + buildDebugReactionsSuffix(msg);
|
|
160
217
|
const target = buildEnvelopeTarget(msg);
|
|
161
218
|
const groupContext = buildGroupContextBlock(msg);
|
|
162
219
|
const charterBlock = buildCharterBlock(msg);
|
|
220
|
+
const goalStateBlock = buildGoalStateBlock(msg);
|
|
163
221
|
const quoteBlock = buildQuoteBlock(msg, target);
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
222
|
+
const messageSummary = buildMessageSummary(msg, target);
|
|
223
|
+
const taskDetails = buildTaskDetailsBlock(msg);
|
|
224
|
+
const reactionsBlock = buildReactionsBlock(msg);
|
|
225
|
+
const text = buildWakePromptText({ messageSummary, target, rawText, groupContext, charterBlock, goalStateBlock, quoteBlock, taskDetails, reactionsBlock });
|
|
167
226
|
return {
|
|
168
227
|
header,
|
|
169
228
|
target,
|