ticlawk 0.1.17-dev.11 → 0.1.17-dev.13
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/README.md +2 -2
- package/bin/ticlawk.mjs +2 -12
- package/package.json +1 -1
- package/src/adapters/ticlawk/api.mjs +0 -25
- package/src/cli/agent-commands.mjs +8 -55
- package/src/core/agent-cli-handlers.mjs +0 -45
- package/src/core/http.mjs +0 -14
- package/src/runtimes/_shared/goal-step-prompt.mjs +6 -10
- package/src/runtimes/_shared/handbook/BASICS.md +2 -2
- package/src/runtimes/_shared/handbook/GOAL_AUTHORITY.md +16 -11
- package/src/runtimes/_shared/handbook/GOAL_TASK_CORE.md +1 -1
- package/src/runtimes/_shared/standing-prompt.mjs +4 -4
- package/src/runtimes/_shared/wake-prompt.mjs +17 -8
package/README.md
CHANGED
|
@@ -230,8 +230,8 @@ Commands:
|
|
|
230
230
|
profile list or switch saved local identities
|
|
231
231
|
message send/read chat messages (agent CLI surface)
|
|
232
232
|
task claim/update/list tasks (agent CLI surface)
|
|
233
|
-
goal report FSM transitions
|
|
234
|
-
approval
|
|
233
|
+
goal report FSM transitions (agent CLI surface)
|
|
234
|
+
approval resolve/list goal-loop approvals (agent CLI surface)
|
|
235
235
|
charter get/set conversation goal and role spec (agent CLI surface)
|
|
236
236
|
group create/list/delete groups, manage charters/members (agent CLI surface)
|
|
237
237
|
dashboard set/get conversation dashboards (agent CLI surface)
|
package/bin/ticlawk.mjs
CHANGED
|
@@ -57,9 +57,7 @@ import {
|
|
|
57
57
|
runMessageCheckCommand,
|
|
58
58
|
runMessageReactCommand,
|
|
59
59
|
runMessageReadCommand,
|
|
60
|
-
runGoalChangedCommand,
|
|
61
60
|
runGoalReportCommand,
|
|
62
|
-
runApprovalRequestCommand,
|
|
63
61
|
runApprovalResolveCommand,
|
|
64
62
|
runApprovalListCommand,
|
|
65
63
|
runMessageSearchCommand,
|
|
@@ -134,8 +132,8 @@ Commands:
|
|
|
134
132
|
profile list or switch saved local identities
|
|
135
133
|
message send/read chat messages (agent CLI surface)
|
|
136
134
|
task claim/update/list tasks (agent CLI surface)
|
|
137
|
-
goal report FSM transitions
|
|
138
|
-
approval
|
|
135
|
+
goal report FSM transitions (agent CLI surface)
|
|
136
|
+
approval resolve/list goal-loop approvals (agent CLI surface)
|
|
139
137
|
charter get/set conversation goal and role spec (agent CLI surface)
|
|
140
138
|
group create/list/delete groups, manage charters/members (agent CLI surface)
|
|
141
139
|
dashboard set/get conversation dashboards (agent CLI surface)
|
|
@@ -487,10 +485,6 @@ async function main() {
|
|
|
487
485
|
process.exitCode = await runGoalReportCommand(args);
|
|
488
486
|
return;
|
|
489
487
|
}
|
|
490
|
-
if (sub === 'changed') {
|
|
491
|
-
process.exitCode = await runGoalChangedCommand(args);
|
|
492
|
-
return;
|
|
493
|
-
}
|
|
494
488
|
console.error(`unknown goal subcommand: ${sub}`);
|
|
495
489
|
process.exit(1);
|
|
496
490
|
}
|
|
@@ -501,10 +495,6 @@ async function main() {
|
|
|
501
495
|
console.log(AGENT_COMMAND_HELP.approval);
|
|
502
496
|
return;
|
|
503
497
|
}
|
|
504
|
-
if (sub === 'request') {
|
|
505
|
-
process.exitCode = await runApprovalRequestCommand(args);
|
|
506
|
-
return;
|
|
507
|
-
}
|
|
508
498
|
if (sub === 'resolve') {
|
|
509
499
|
process.exitCode = await runApprovalResolveCommand(args);
|
|
510
500
|
return;
|
package/package.json
CHANGED
|
@@ -302,31 +302,6 @@ export async function reportGoalTransition({
|
|
|
302
302
|
});
|
|
303
303
|
}
|
|
304
304
|
|
|
305
|
-
export async function noteGoalChanged({ actingAgentId, conversationId }) {
|
|
306
|
-
return apiFetch('/api/agent/goal/changed', {
|
|
307
|
-
method: 'POST',
|
|
308
|
-
body: JSON.stringify({
|
|
309
|
-
acting_as_agent_id: actingAgentId,
|
|
310
|
-
conversation_id: conversationId,
|
|
311
|
-
}),
|
|
312
|
-
});
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
export async function requestGoalApproval({
|
|
316
|
-
actingAgentId, conversationId, title, detail, ttlSeconds,
|
|
317
|
-
}) {
|
|
318
|
-
return apiFetch('/api/agent/approval/request', {
|
|
319
|
-
method: 'POST',
|
|
320
|
-
body: JSON.stringify({
|
|
321
|
-
acting_as_agent_id: actingAgentId,
|
|
322
|
-
conversation_id: conversationId,
|
|
323
|
-
title,
|
|
324
|
-
detail: detail ?? null,
|
|
325
|
-
ttl_seconds: ttlSeconds ?? null,
|
|
326
|
-
}),
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
|
|
330
305
|
export async function listGoalApprovals({ actingAgentId, conversationId }) {
|
|
331
306
|
const params = new URLSearchParams();
|
|
332
307
|
params.set('acting_as_agent_id', actingAgentId);
|
|
@@ -383,51 +383,6 @@ export async function runGoalReportCommand(args) {
|
|
|
383
383
|
return exitFromStatus(res.statusCode);
|
|
384
384
|
}
|
|
385
385
|
|
|
386
|
-
export async function runGoalChangedCommand(args) {
|
|
387
|
-
const env = requireAgentEnv();
|
|
388
|
-
const conversationId = getArg(args, 'conversation') || getArg(args, 'conversation-id') || env.currentConversationId;
|
|
389
|
-
if (!conversationId) {
|
|
390
|
-
console.error('--conversation is required');
|
|
391
|
-
return 2;
|
|
392
|
-
}
|
|
393
|
-
const res = await daemonRequest({
|
|
394
|
-
method: 'POST',
|
|
395
|
-
path: '/agent/goal/changed',
|
|
396
|
-
headers: commonHeaders(env),
|
|
397
|
-
body: { conversation_id: conversationId },
|
|
398
|
-
});
|
|
399
|
-
printJson(res.body);
|
|
400
|
-
return exitFromStatus(res.statusCode);
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
export async function runApprovalRequestCommand(args) {
|
|
404
|
-
const env = requireAgentEnv();
|
|
405
|
-
const conversationId = getArg(args, 'conversation') || getArg(args, 'conversation-id') || env.currentConversationId;
|
|
406
|
-
const title = getArg(args, 'title');
|
|
407
|
-
if (!conversationId) {
|
|
408
|
-
console.error('--conversation is required');
|
|
409
|
-
return 2;
|
|
410
|
-
}
|
|
411
|
-
if (!title) {
|
|
412
|
-
console.error('--title is required');
|
|
413
|
-
return 2;
|
|
414
|
-
}
|
|
415
|
-
const ttlRaw = getArg(args, 'ttl-seconds');
|
|
416
|
-
const res = await daemonRequest({
|
|
417
|
-
method: 'POST',
|
|
418
|
-
path: '/agent/approval/request',
|
|
419
|
-
headers: commonHeaders(env),
|
|
420
|
-
body: {
|
|
421
|
-
conversation_id: conversationId,
|
|
422
|
-
title,
|
|
423
|
-
detail: getArg(args, 'detail'),
|
|
424
|
-
ttl_seconds: ttlRaw != null ? Number(ttlRaw) : undefined,
|
|
425
|
-
},
|
|
426
|
-
});
|
|
427
|
-
printJson(res.body);
|
|
428
|
-
return exitFromStatus(res.statusCode);
|
|
429
|
-
}
|
|
430
|
-
|
|
431
386
|
export async function runApprovalResolveCommand(args) {
|
|
432
387
|
const env = requireAgentEnv();
|
|
433
388
|
const requestId = getArg(args, 'request') || getArg(args, 'request-id');
|
|
@@ -1481,7 +1436,7 @@ export const AGENT_COMMAND_HELP = {
|
|
|
1481
1436
|
Compatibility alias for group admin commands. Prefer \`ticlawk group ...\`
|
|
1482
1437
|
for groups and \`ticlawk charter ...\` for conversation charters.
|
|
1483
1438
|
`,
|
|
1484
|
-
goal: `ticlawk goal <report
|
|
1439
|
+
goal: `ticlawk goal <report>
|
|
1485
1440
|
Goal-lane (FSM) control. Normally invoked from a goal-step turn, not by hand.
|
|
1486
1441
|
ticlawk goal report --transition <id> --outcome <outcome> [--conversation <id>] [--detail <text>] [--current-task <task-id>]
|
|
1487
1442
|
Report the outcome of the current FSM step and advance the state machine.
|
|
@@ -1490,16 +1445,14 @@ export const AGENT_COMMAND_HELP = {
|
|
|
1490
1445
|
gap_analysis: gap | no_gap | wait
|
|
1491
1446
|
execute: task_completed | needs_approval | blocked
|
|
1492
1447
|
review: accepted | rejected
|
|
1493
|
-
|
|
1494
|
-
Signal that the conversation's goal changed; wakes the FSM into gap analysis.
|
|
1448
|
+
(To change a goal, write the charter with \`ticlawk charter set\` — that re-aims the goal lane.)
|
|
1495
1449
|
`,
|
|
1496
|
-
approval: `ticlawk approval <
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
--conversation defaults to the current goal-turn conversation.
|
|
1450
|
+
approval: `ticlawk approval <resolve|list>
|
|
1451
|
+
Goal-loop approval resolution (chat lane). The goal lane PARKS an approval by
|
|
1452
|
+
publishing an approval briefing (\`ticlawk briefing publish --mode approval\`),
|
|
1453
|
+
which both asks the owner and suspends the loop on it. The owner answers by
|
|
1454
|
+
tapping approve (which posts an "approved" message) or replying in plain
|
|
1455
|
+
language — either way the chat lane resolves it here, idempotently.
|
|
1503
1456
|
ticlawk approval list [--conversation <id> | --target "<target>"]
|
|
1504
1457
|
List pending approval requests in the conversation. Use this from the chat
|
|
1505
1458
|
lane to find which request an owner's natural-language approval refers to.
|
|
@@ -416,51 +416,6 @@ export async function handleGoalReport(req, body, ctx) {
|
|
|
416
416
|
}
|
|
417
417
|
}
|
|
418
418
|
|
|
419
|
-
export async function handleGoalChanged(req, body, ctx) {
|
|
420
|
-
const actingAgentId = getActingAgentId(req, body);
|
|
421
|
-
const v = validateActingAgent(actingAgentId, ctx);
|
|
422
|
-
if (!v.ok) return { status: v.status, body: { error: v.error } };
|
|
423
|
-
const conversationId = body?.conversation_id || getCurrentConversationId(req, body);
|
|
424
|
-
if (!conversationId) return { status: 400, body: { error: 'conversation_id is required' } };
|
|
425
|
-
try {
|
|
426
|
-
const data = await api.noteGoalChanged({ actingAgentId, conversationId });
|
|
427
|
-
debugLog('agent-cli', 'goal.changed', {
|
|
428
|
-
actingAgentId,
|
|
429
|
-
conversationId,
|
|
430
|
-
goalVersion: data?.goal_version,
|
|
431
|
-
});
|
|
432
|
-
return { status: data?.ok ? 200 : 400, body: data };
|
|
433
|
-
} catch (err) {
|
|
434
|
-
return { status: err?.status || 500, body: { error: err?.message || 'goal changed failed' } };
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
export async function handleApprovalRequest(req, body, ctx) {
|
|
439
|
-
const actingAgentId = getActingAgentId(req, body);
|
|
440
|
-
const v = validateActingAgent(actingAgentId, ctx);
|
|
441
|
-
if (!v.ok) return { status: v.status, body: { error: v.error } };
|
|
442
|
-
const conversationId = body?.conversation_id || getCurrentConversationId(req, body);
|
|
443
|
-
if (!conversationId) return { status: 400, body: { error: 'conversation_id is required' } };
|
|
444
|
-
if (!body?.title) return { status: 400, body: { error: 'title is required' } };
|
|
445
|
-
try {
|
|
446
|
-
const data = await api.requestGoalApproval({
|
|
447
|
-
actingAgentId,
|
|
448
|
-
conversationId,
|
|
449
|
-
title: body.title,
|
|
450
|
-
detail: body.detail || null,
|
|
451
|
-
ttlSeconds: body.ttl_seconds || null,
|
|
452
|
-
});
|
|
453
|
-
debugLog('agent-cli', 'approval.request', {
|
|
454
|
-
actingAgentId,
|
|
455
|
-
conversationId,
|
|
456
|
-
requestId: data?.request_id,
|
|
457
|
-
});
|
|
458
|
-
return { status: data?.ok ? 200 : 400, body: data };
|
|
459
|
-
} catch (err) {
|
|
460
|
-
return { status: err?.status || 500, body: { error: err?.message || 'approval request failed' } };
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
|
|
464
419
|
export async function handleApprovalList(req, query, ctx) {
|
|
465
420
|
const actingAgentId = getActingAgentId(req, query);
|
|
466
421
|
const v = validateActingAgent(actingAgentId, ctx);
|
package/src/core/http.mjs
CHANGED
|
@@ -39,9 +39,7 @@ import {
|
|
|
39
39
|
handleReminderSchedule,
|
|
40
40
|
handleReminderSnooze,
|
|
41
41
|
handleReminderUpdate,
|
|
42
|
-
handleGoalChanged,
|
|
43
42
|
handleGoalReport,
|
|
44
|
-
handleApprovalRequest,
|
|
45
43
|
handleApprovalResolve,
|
|
46
44
|
handleApprovalList,
|
|
47
45
|
handleServerInfo,
|
|
@@ -155,18 +153,6 @@ export function startLocalHttpServer({ port, adapter, ctx }) {
|
|
|
155
153
|
const r = await handleGoalReport(req, body, cliCtx);
|
|
156
154
|
return writeJson(res, r.status, r.body);
|
|
157
155
|
}
|
|
158
|
-
if (urlNoQuery === '/agent/goal/changed' && method === 'POST') {
|
|
159
|
-
const body = await readJsonBody(req);
|
|
160
|
-
if (body === null) return writeJson(res, 400, { error: 'invalid json body' });
|
|
161
|
-
const r = await handleGoalChanged(req, body, cliCtx);
|
|
162
|
-
return writeJson(res, r.status, r.body);
|
|
163
|
-
}
|
|
164
|
-
if (urlNoQuery === '/agent/approval/request' && method === 'POST') {
|
|
165
|
-
const body = await readJsonBody(req);
|
|
166
|
-
if (body === null) return writeJson(res, 400, { error: 'invalid json body' });
|
|
167
|
-
const r = await handleApprovalRequest(req, body, cliCtx);
|
|
168
|
-
return writeJson(res, r.status, r.body);
|
|
169
|
-
}
|
|
170
156
|
if (urlNoQuery === '/agent/approval/resolve' && method === 'POST') {
|
|
171
157
|
const body = await readJsonBody(req);
|
|
172
158
|
if (body === null) return writeJson(res, 400, { error: 'invalid json body' });
|
|
@@ -24,10 +24,10 @@ const STEP_GUIDES = {
|
|
|
24
24
|
},
|
|
25
25
|
execute: {
|
|
26
26
|
title: 'EXECUTE',
|
|
27
|
-
body: `Do the next concrete unit of work toward the goal (or drive the current task to completion).
|
|
27
|
+
body: `Do the next concrete unit of work toward the goal (or drive the current task to completion). Keep progress on the dashboard (\`ticlawk dashboard set\`); the goal lane reaches the owner only through the dashboard and briefings — never \`message send\`.
|
|
28
28
|
- When the unit of work is finished and ready to be checked, report outcome=task_completed.
|
|
29
|
-
- If you cannot proceed without
|
|
30
|
-
- If you are blocked by something
|
|
29
|
+
- If you cannot proceed without something only the owner can grant — a decision, a permission, or approval to spend money or use a resource — ask for it with ONE approval briefing: \`ticlawk briefing publish --mode approval\` (short: what you need and why). That single call both asks the owner and suspends the loop on the approval. Then report outcome=needs_approval. Their answer (tapping approve, or a natural-language reply) resumes you automatically.
|
|
30
|
+
- If you are blocked by something an owner decision cannot fix — an external failure, or a missing input no decision of theirs unblocks — surface it with \`ticlawk briefing publish --mode info\` only if they would be wrong not to know, then report outcome=blocked.`,
|
|
31
31
|
outcomes: ['task_completed', 'needs_approval', 'blocked'],
|
|
32
32
|
},
|
|
33
33
|
review: {
|
|
@@ -100,22 +100,18 @@ export function buildGoalStepPrompt(msg) {
|
|
|
100
100
|
|
|
101
101
|
if (guide) {
|
|
102
102
|
sections.push([
|
|
103
|
-
`[goal_step] You are running the goal loop for this conversation
|
|
103
|
+
`[goal_step] You are running one step of the goal loop for this conversation — a backend FSM step. Act on it and report the outcome; there is no message to reply to.`,
|
|
104
104
|
``,
|
|
105
105
|
`Current step: ${guide.title}`,
|
|
106
106
|
guide.body,
|
|
107
107
|
``,
|
|
108
|
-
`
|
|
109
|
-
` (a) the owner must act or decide — e.g. an approval you parked (\`--mode approval\`);`,
|
|
110
|
-
` (b) the owner asked to be told about this — a standing request, a scheduled time, or a threshold they set (\`--mode info\`);`,
|
|
111
|
-
` (c) something happened the owner would be wrong not to know now — goal done, blocked, materially off-track, or a result they are waiting on (\`--mode info\`).`,
|
|
112
|
-
`If you are unsure, it is NOT a briefing — put it on the dashboard. The dashboard is the always-current pull surface; briefings are scarce pushes — over-notifying trains the owner to ignore them.`,
|
|
108
|
+
`Briefings are scarce pushes that interrupt the owner; routine progress belongs on the dashboard (the owner pulls it). Send one (\`ticlawk briefing publish\`) only when the owner must act or decide (\`--mode approval\`), asked to be told, or would be wrong not to know now — goal done, blocked, off-track, or a result they are waiting on (\`--mode info\`). The exact bar is in SURFACES.md "Briefing"; if unsure, it is not a briefing.`,
|
|
113
109
|
``,
|
|
114
110
|
`When the step is done, advance the state machine by running EXACTLY ONE report:`,
|
|
115
111
|
` ${reportCmd}`,
|
|
116
112
|
``,
|
|
117
113
|
`Reporting the outcome continues the loop: a running next state comes back as a fresh step; with no gap or a wait, the loop parks itself. Report exactly once — do not loop inside this turn.`,
|
|
118
|
-
`Reach the owner only through
|
|
114
|
+
`Reach the owner only through two surfaces: \`ticlawk dashboard set\` (the goal report — routine progress, the owner pulls it) and \`ticlawk briefing publish\` (a scarce push, per the rule above). The goal lane never uses \`ticlawk message send\` — chat is the chat lane's. Write owner-facing text for someone reading cold who has never seen your plan or task board: what changed, why it matters, and what (if anything) they must do — naming things by what they are, never by a private code, run name, or task number.`,
|
|
119
115
|
`[/goal_step]`,
|
|
120
116
|
].join('\n'));
|
|
121
117
|
} else {
|
|
@@ -14,6 +14,6 @@ DO NOT EDIT.
|
|
|
14
14
|
|
|
15
15
|
`MEMORY.md` is your recovery entry point — read it before acting, and keep it durable, not a log.
|
|
16
16
|
|
|
17
|
-
- Record only what stays useful across turns: your role, stable owner/project/domain facts,
|
|
18
|
-
- Do not record what is already recoverable from the task board, dashboard, briefings, or recent chat — or any secret.
|
|
17
|
+
- Record only what stays useful across turns: your role, stable owner/project/domain facts, open blockers, standing decisions, key group context, and preferences.
|
|
18
|
+
- Do not record what is already recoverable from the charter, task board, dashboard, briefings, or recent chat — or any secret. The goal lives in the charter, not here.
|
|
19
19
|
- Keep it short; put long-lived detail in `notes/<topic>.md` and link those notes from `MEMORY.md`.
|
|
@@ -4,27 +4,32 @@ DO NOT EDIT.
|
|
|
4
4
|
|
|
5
5
|
Use this in a DM, or in a group where you are admin or owner.
|
|
6
6
|
|
|
7
|
-
The goal
|
|
7
|
+
The goal loop runs in the backend **goal lane**. Your chat-side job is to handle each incoming message and keep the charter — the goal lane's target — correct. Writing the charter with `ticlawk charter set` is what hands a new or changed goal to the lane; there is no separate signal.
|
|
8
8
|
|
|
9
9
|
## Handling a message
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
-
|
|
11
|
+
The goal lane does the goal's actual work — it runs the loop and carries out the goal's tasks (experiments, builds, and the like). Your chat-side job is the charter, approvals, and talking to the owner; you do not carry out the goal's work yourself in a chat turn. Read the incoming message as one of:
|
|
12
|
+
|
|
13
|
+
- **Approving or rejecting** something ("go ahead", "approved", "no, hold off") — the goal lane is waiting on an approval. Resolve it (see Owner approvals); resolving resumes the lane and it does the work. Don't do the work yourself.
|
|
14
|
+
- **Setting, clarifying, or changing the goal** — write it into the charter with `ticlawk charter set` (the per-turn goal block shows the current charter and how to edit it). That re-aims the lane.
|
|
15
|
+
- **Telling you to start or continue the goal's work** ("get going", "run it") — that work belongs to the goal lane. Don't run experiments, builds, or its tasks here. Acknowledge, make sure the charter is right and any pending approval is resolved (`ticlawk approval list`), and let the lane carry it out.
|
|
16
|
+
- **A genuine one-off aside**, not part of pursuing the chartered goal — just answer it.
|
|
17
|
+
|
|
18
|
+
Scope: in a DM you own the goal and the charter; in a group as admin/owner you also own the task board, membership, and the owner-facing surfaces.
|
|
14
19
|
|
|
15
20
|
## Owner approvals
|
|
16
21
|
|
|
17
|
-
When the goal lane needs the owner to approve or decide, it
|
|
22
|
+
When the goal lane needs the owner to approve or decide, it publishes one approval briefing, which both asks the owner and suspends the loop on it. The owner answers by tapping approve (the backend posts an "approved" message) or by replying here in plain language — either way the reply is yours to resolve.
|
|
18
23
|
|
|
19
24
|
- When a message reads as approving or rejecting ("go ahead", "approved", "no, hold off"), run `ticlawk approval list` to see what is pending in this conversation.
|
|
20
|
-
- Resolve only when you can bind the reply to exactly one request — it quotes a specific request, or there is exactly one pending: `ticlawk approval resolve --request <id> (--grant | --reject) --original-text "<owner's words>" --source-message-id <id>`. If several are pending and the reply fits none uniquely, ask which one
|
|
21
|
-
- Resolving is idempotent and backend-owned: a button tap and your resolve collapse to one decision,
|
|
25
|
+
- Resolve only when you can bind the reply to exactly one request — it quotes a specific request, or there is exactly one pending: `ticlawk approval resolve --request <id> (--grant | --reject) --original-text "<owner's words>" --source-message-id <id>`. If several are pending and the reply fits none uniquely, ask which one. If it is already decided or expired, say so instead of resolving again.
|
|
26
|
+
- Resolving is idempotent and backend-owned: a button tap and your resolve collapse to one decision, which resumes the lane automatically.
|
|
22
27
|
|
|
23
28
|
## Goal Setup When No Specific Goal Exists
|
|
24
29
|
|
|
25
|
-
- When the conversation has no goal,
|
|
30
|
+
- When the conversation has no goal, judge whether this message is a one-off or is starting an ongoing goal. Use that only to choose your next action; don't say it out loud.
|
|
26
31
|
- A one-off request: just answer. A goal statement, a goal discussion, or a "what should we pursue" question: treat as goal setup — ask naturally whether to set a goal (for this DM, an existing group, or a new group) before writing anything.
|
|
27
32
|
- Clarify only what you need to proceed: the goal, what "done" means, scope and boundaries, the rough approach, who is responsible for what, and any resources required.
|
|
28
|
-
- Summarize the proposed charter and get confirmation before writing it. Keep the charter to the goal, roles,
|
|
29
|
-
- After confirmation
|
|
30
|
-
- Goals are revisable: if the owner later changes the goal, scope, or boundaries, summarize and confirm the change,
|
|
33
|
+
- Summarize the proposed charter and get confirmation before writing it. Keep the charter to the goal (including what "done" looks like), roles, and boundaries — no workflow rules, task status, or playbooks.
|
|
34
|
+
- After confirmation, write the charter with `ticlawk charter set` (body on stdin). That starts the goal lane, which drives the work from there — building the dashboard and any tasks as it goes.
|
|
35
|
+
- Goals are revisable: if the owner later changes the goal, scope, or boundaries, summarize and confirm the change, then write the updated charter with `ticlawk charter set`.
|
|
@@ -9,7 +9,7 @@ Read this when a conversation has, or might get, a durable goal, a task board, d
|
|
|
9
9
|
A Ticlawk conversation can be an ordinary chat, or it can carry a durable **goal**. When it has a goal, work happens on two independent lanes:
|
|
10
10
|
|
|
11
11
|
- **Chat lane** — you, handling each incoming message and replying. This is where you discuss the goal, set or change the charter, and resolve owner approvals.
|
|
12
|
-
- **Goal lane** — a backend state machine that pursues the goal on its own (gap analysis → execute → review), looping until there is no gap and then parking until something changes. It wakes you only to carry out one step.
|
|
12
|
+
- **Goal lane** — a backend state machine that pursues the goal on its own (gap analysis → execute → review), looping until there is no gap and then parking until something changes. It wakes you only to carry out one step. From the chat lane you keep its target — the charter — correct; writing the charter (`ticlawk charter set`) is what re-aims the lane when the goal changes.
|
|
13
13
|
|
|
14
14
|
The goal-authority agent — the DM's agent, or a group's admin/owner — owns the charter and the owner-facing surfaces. Other group members only execute tasks.
|
|
15
15
|
|
|
@@ -22,7 +22,7 @@ Read other local files only when the current work clearly needs them.`;
|
|
|
22
22
|
|
|
23
23
|
// A goal-lane turn is a single backend FSM step, not a chat message. Its
|
|
24
24
|
// per-step instructions and exact CLI commands (`goal report`, `task`,
|
|
25
|
-
// `
|
|
25
|
+
// `dashboard set`, `briefing publish`) live in the goal-step (user) prompt
|
|
26
26
|
// built by goal-step-prompt.mjs. The system prompt here therefore stays
|
|
27
27
|
// minimal: identity + the one reply invariant. It deliberately omits the
|
|
28
28
|
// chat-lane role framing and handbook read-list, which would compete with
|
|
@@ -31,7 +31,7 @@ Read other local files only when the current work clearly needs them.`;
|
|
|
31
31
|
function buildGoalLaneStandingPrompt(_ctx = {}) {
|
|
32
32
|
return `You are executing one backend goal-loop step for this conversation, dispatched by the system — not a message from a person. Do only what this step requires; leave work for other steps to those steps.
|
|
33
33
|
|
|
34
|
-
Your normal output is private and reaches no one. The owner is reached only through
|
|
34
|
+
Your normal output is private and reaches no one. The owner is reached only through two surfaces: \`ticlawk dashboard set\` (the goal-level report — where routine progress lives, the owner pulls it) and \`ticlawk briefing publish\` (a scarce push — a decision to make, or something they would be wrong not to know). You do not use \`ticlawk message send\`: chat is the chat lane's, not the goal loop's. The step tells you which surface to use; \`SURFACES.md\` holds the rules. Read \`SURFACES.md\` or \`MEMORY.md\` only if the step needs them.`;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
function isGoalLane(ctx = {}) {
|
|
@@ -106,7 +106,7 @@ const FILE_DESCRIPTIONS = {
|
|
|
106
106
|
'COMMUNICATION.md': 'replying via the ticlawk CLI.',
|
|
107
107
|
'COLLABORATION.md': 'DM/group conduct.',
|
|
108
108
|
'GOAL_TASK_CORE.md': 'the goal system, the two lanes, and core vocabulary.',
|
|
109
|
-
'GOAL_AUTHORITY.md': 'your goal-side job: keep the charter right
|
|
109
|
+
'GOAL_AUTHORITY.md': 'your goal-side job: keep the charter right (the goal lane runs the loop).',
|
|
110
110
|
'TASK_WORKER.md': 'executing assigned tasks as a group member.',
|
|
111
111
|
'SURFACES.md': 'dashboards, briefings, reminders, and shared tools.',
|
|
112
112
|
};
|
|
@@ -119,7 +119,7 @@ function describeFile(name) {
|
|
|
119
119
|
function buildReadInstructions(ctx = {}) {
|
|
120
120
|
const files = buildReadFileNames(ctx);
|
|
121
121
|
const list = files.map((name, index) => `${index + 1}. ${describeFile(name)}`).join('\n');
|
|
122
|
-
return `To reply, use \`ticlawk message send --target <target> --phase progress|final\`. Normal assistant output is private and reaches no one. Whether the owner is asking you something or you are reaching out, talk to them as a person who has never seen your plan, your task board, or the names you gave things: say what is going on and why it matters in plain language, and refer to things by what they are and do — never by a private
|
|
122
|
+
return `To reply, use \`ticlawk message send --target <target> --phase progress|final\`. Normal assistant output is private and reaches no one. Whether the owner is asking you something or you are reaching out, talk to them as a person who has never seen your plan, your task board, or the names you gave things: say what is going on and why it matters in plain language, and refer to things by what they are and do — never by a private code, run name, or task number, which means nothing to them. Details are in \`COMMUNICATION.md\`.
|
|
123
123
|
|
|
124
124
|
Read these once if you haven't this session, then only when the current work needs them:
|
|
125
125
|
${list}`;
|
|
@@ -95,20 +95,29 @@ export function buildCharterBlock(msg) {
|
|
|
95
95
|
if (msg.reason === 'transition' || !hasGoalAuthority(msg)) return '';
|
|
96
96
|
return promptBlock(`
|
|
97
97
|
[conversation_goal]
|
|
98
|
-
This conversation has no goal charter yet
|
|
99
|
-
If this message sets a goal
|
|
98
|
+
This conversation has no goal charter yet.
|
|
99
|
+
If this message sets a goal, write it into the charter — the goal and what "done" looks like, in the owner's words — with \`ticlawk charter set --conversation ${conversationId}\` (body on stdin). That starts the goal lane. Otherwise handle the message normally. See GOAL_AUTHORITY.md.
|
|
100
100
|
[/conversation_goal]
|
|
101
101
|
`);
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
//
|
|
105
|
-
//
|
|
106
|
-
//
|
|
107
|
-
//
|
|
104
|
+
// For the goal-authority chat lane, lead with what work on the goal is doing
|
|
105
|
+
// right now (from goal_lane_state, attached by the claim) — in plain terms,
|
|
106
|
+
// not internal state names — so the agent acts on fact: resolve a pending
|
|
107
|
+
// approval, don't redo work already running, and use charter set for a goal
|
|
108
|
+
// change. Transition deliveries (the goal loop's own steps) get the spec only.
|
|
109
|
+
const gl = msg.goal_lane_state && typeof msg.goal_lane_state === 'object' ? msg.goal_lane_state : null;
|
|
110
|
+
const stateLine = !gl
|
|
111
|
+
? ''
|
|
112
|
+
: gl.pending_approval
|
|
113
|
+
? `Work on this goal is paused, waiting for you to approve: "${gl.pending_approval.title}". A go-ahead from you here is that approval — resolve it; the work then resumes on its own, you don't redo it.`
|
|
114
|
+
: (gl.state && gl.state !== 'suspended')
|
|
115
|
+
? `Work on this goal is already running on its own in the background — don't redo it in your reply.`
|
|
116
|
+
: `No work on this goal is running right now.`;
|
|
108
117
|
const authorityLine = msg.reason === 'transition'
|
|
109
118
|
? 'This is the goal and success spec for this conversation. Run the current step against it.'
|
|
110
119
|
: hasGoalAuthority(msg)
|
|
111
|
-
?
|
|
120
|
+
? `${stateLine ? stateLine + ' ' : ''}Handle this message and reply. If it changes the goal, edit the charter shown above and write it back in full with \`ticlawk charter set --conversation ${conversationId}\` (body on stdin) — change only what actually changed and keep the rest verbatim. That re-aims the work. See GOAL_AUTHORITY.md.`
|
|
112
121
|
: 'Use it as current group goal and role context. The group admin owns charter and dashboard changes unless they explicitly delegate them.';
|
|
113
122
|
return promptBlock(`
|
|
114
123
|
[conversation_goal]
|
|
@@ -125,7 +134,7 @@ export function buildGoalStateBlock(msg) {
|
|
|
125
134
|
const convType = msg.conversation_type || 'dm';
|
|
126
135
|
const subject = convType === 'group' ? 'This group' : 'This conversation';
|
|
127
136
|
const authorityLine = hasGoalAuthority(msg)
|
|
128
|
-
? 'If this message may be starting, clarifying, or changing an ongoing goal, read GOAL_AUTHORITY.md "Goal Setup When No Specific Goal Exists" and follow it to propose/confirm the goal before writing charter
|
|
137
|
+
? 'If this message may be starting, clarifying, or changing an ongoing goal, read GOAL_AUTHORITY.md "Goal Setup When No Specific Goal Exists" and follow it to propose/confirm the goal before writing the charter. If it is clearly one-off, answer normally.'
|
|
129
138
|
: 'The group admin owns goal setup; follow direct mentions or assigned tasks normally.';
|
|
130
139
|
return promptBlock(`
|
|
131
140
|
[conversation_goal]
|